diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..58b0fc0985950fcdca36d06fdef8a6cad200d344 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,60 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +dist/ +build/ +*.egg-info/ +*.egg + +# Virtual environments +.env +.env.sh +venv/ +ENV/ + +# IDE-specific files +.vscode/ +.idea/ + +# Compiled Python modules +*.pyc +*.pyo +*.pyd + +# Python testing +.pytest_cache/ +.ruff_cache/ +.coverage +.mypy_cache/ + +# macOS specific files +.DS_Store + +# Windows specific files +Thumbs.db + +# this application's specific files +archive + +# any log file +*log.txt +todo +scratchpad + +# Ignore GPT Engineer files +projects +!projects/example + +# Pyenv +.python-version + +# Benchmark files +benchmark +!benchmark/*/prompt + +.gpte_consent diff --git a/.env.template b/.env.template new file mode 100644 index 0000000000000000000000000000000000000000..ffdc5bb6e270c24e6b8dee8b720f6b6ace3ccedd --- /dev/null +++ b/.env.template @@ -0,0 +1,5 @@ +### OpenAI Setup ### + +# OPENAI_API_KEY=Your personal OpenAI API key from https://platform.openai.com/account/api-keys +OPENAI_API_KEY=... +ANTHROPIC_API_KEY=... diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..82cfada6afe93ff6369598beaea0504df23c2002 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +tests/test_data/mona_lisa.jpg filter=lfs diff=lfs merge=lfs -text diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000000000000000000000000000000000..55dfea91d989f15755ad0e7aecd0dbd890760a89 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +.github/workflows/ @ATheorell diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000000000000000000000000000000..7713a6b3df86b57a46987b4350ffd0a0831155ba --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,131 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity or expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting using an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of reporters of incidents. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..dbc4b4dec11ea15852d1587a246b8c3ccd88964c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,135 @@ +# Contributing to gpt-engineer + +The gpt-engineer is a community project and lives from your contributions - they are warmly appreciated. The main contribution avenues are: +- Pull request: implement code and have it reviewed and potentially merged by the maintainers. Implementations of existing feature requests or fixes to bug reports are likely to be merged. +- Bug report: report when something in gpt-engineer doesn't work. Do not report errors in programs written _by_ gpt-engineer. +- Feature request: provide a detailed sketch about something you want to have implemented in gpt-engineer. There is no guarantee that features will be implemented. +- Discussion: raise awareness of a potential improvement. This is often a good starting point before making a detailed feature request. + +By participating in this project, you agree to abide by the [code of conduct](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/.github/CODE_OF_CONDUCT.md). + +## Merge Policy for Pull Requests +Code that is likely to introduce breaking changes, or significantly change the user experience for users and developers, require [board approval](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/GOVERNANCE.md) to be merged. Smaller code changes can be merged directly. +As a rule, cosmetic pull requests, for example rephrasing the readme or introducing more compact syntax, that do not yield clear practical improvements are not merged. Such pull requests are generally discouraged, both to save time for the maintainers and to establish a lower bar for becoming a contributor. + +## Getting Started with Pull Requests to gpt-engineer + +To get started with contributing, please follow these steps: + +1. Fork the repository and clone it to your local machine. +2. Install any necessary dependencies. +3. Create a new branch for your changes: `git checkout -b my-branch-name`. +4. Make your desired changes or additions. +5. Run the tests to ensure everything is working as expected. +6. Commit your changes: `git commit -m "Descriptive commit message"`. +7. Push to the branch: `git push origin my-branch-name`. +8. Submit a pull request to the `main` branch of the original repository. + +## Code Style + +Please make sure to follow the established code style guidelines for this project. Consistent code style helps maintain readability and makes it easier for others to contribute to the project. + +To enforce this we use [`pre-commit`](https://pre-commit.com/) to run [`black`](https://black.readthedocs.io/en/stable/index.html) and [`ruff`](https://beta.ruff.rs/docs/) on every commit. + +To install gpt-engineer as a developer, clone the repository and install the dependencies with: + +```bash +$ poetry install +$ poetry shell +``` + +And then install the `pre-commit` hooks with: + +```bash +$ pre-commit install + +# output: +pre-commit installed at .git/hooks/pre-commit +``` + +If you are not familiar with the concept of [git hooks](https://git-scm.com/docs/githooks) and/or [`pre-commit`](https://pre-commit.com/) please read the documentation to understand how they work. + +As an introduction of the actual workflow, here is an example of the process you will encounter when you make a commit: + +Let's add a file we have modified with some errors, see how the pre-commit hooks run `black` and fails. +`black` is set to automatically fix the issues it finds: + +```bash +$ git add chat_to_files.py +$ git commit -m "commit message" +black....................................................................Failed +- hook id: black +- files were modified by this hook + +reformatted chat_to_files.py + +All done! ✨ 🍰 ✨ +1 file reformatted. +``` + +You can see that `chat_to_files.py` is both staged and not staged for commit. This is because `black` has formatted it and now it is different from the version you have in your working directory. To fix this you can simply run `git add chat_to_files.py` again and now you can commit your changes. + +```bash +$ git status +On branch pre-commit-setup +Changes to be committed: + (use "git restore --staged ..." to unstage) + modified: chat_to_files.py + +Changes not staged for commit: + (use "git add ..." to update what will be committed) + (use "git restore ..." to discard changes in working directory) + modified: chat_to_files.py +``` + +Now let's add the file again to include the latest commits and see how `ruff` fails. + +```bash +$ git add chat_to_files.py +$ git commit -m "commit message" +black....................................................................Passed +ruff.....................................................................Failed +- hook id: ruff +- exit code: 1 +- files were modified by this hook + +Found 2 errors (2 fixed, 0 remaining). +``` + +Same as before, you can see that `chat_to_files.py` is both staged and not staged for commit. This is because `ruff` has formatted it and now it is different from the version you have in your working directory. To fix this you can simply run `git add chat_to_files.py` again and now you can commit your changes. + +```bash +$ git add chat_to_files.py +$ git commit -m "commit message" +black....................................................................Passed +ruff.....................................................................Passed +fix end of files.........................................................Passed +[pre-commit-setup f00c0ce] testing + 1 file changed, 1 insertion(+), 1 deletion(-) +``` + +Now your file has been committed and you can push your changes. + +At the beginning this might seem like a tedious process (having to add the file again after `black` and `ruff` have modified it) but it is actually very useful. It allows you to see what changes `black` and `ruff` have made to your files and make sure that they are correct before you commit them. + +### Important Note When `pre-commit` Fails in the Build Pipeline +Sometimes `pre-commit` will seemingly run successfully, as follows: + +```bash +black................................................(no files to check)Skipped +ruff.................................................(no files to check)Skipped +check toml...........................................(no files to check)Skipped +check yaml...........................................(no files to check)Skipped +detect private key...................................(no files to check)Skipped +fix end of files.....................................(no files to check)Skipped +trim trailing whitespace.............................(no files to check)Skipped +``` + +However, you may see `pre-commit` fail in the build pipeline upon submitting a PR. The solution to this is to run `pre-commit run --all-files` to force `pre-commit` to execute these checks, and make any necessary file modifications, to all files. + + +## Licensing + +By contributing to gpt-engineer, you agree that your contributions will be licensed under the [LICENSE](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/LICENSE) file of the project. + +Thank you for your interest in contributing to gpt-engineer! We appreciate your support and look forward to your contributions. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..e7deadaff83f467525342d47e4f53d05139d7265 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [antonosika] +patreon: gpt_eng diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000000000000000000000000000000000000..579ffe532eb1b96b69d427790bafaddb19b76da2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug, triage +assignees: '' + +--- + +## Policy and info + - Maintainers will close issues that have been stale for 14 days if they contain relevant answers. + - Adding the label "sweep" will automatically turn the issue into a coded pull request. Works best for mechanical tasks. More info/syntax at: https://docs.sweep.dev/ + +## Expected Behavior + +Please describe the behavior you are expecting. + +## Current Behavior + +What is the current behavior? + +## Failure Information + +Information about the failure, including environment details, such as LLM used. + +### Failure Logs + +If your project includes a debug_log_file.txt, kindly upload it from your_project/.gpteng/memory/ directory. This file encompasses all the necessary logs. Should the file prove extensive, consider utilizing GitHub's "add files" functionality. + +## System Information + +Please copy and paste the output of the `gpte --sysinfo` command as part of your bug report. diff --git a/.github/ISSUE_TEMPLATE/documentation-clarification.md b/.github/ISSUE_TEMPLATE/documentation-clarification.md new file mode 100644 index 0000000000000000000000000000000000000000..66de382e5df2d9ec2a1882aaad322e5522e071ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-clarification.md @@ -0,0 +1,19 @@ +--- +name: Documentation improvement +about: Inaccuracies, inadequacies in the docs pages +title: '' +labels: documentation, triage +assignees: '' + +--- + +## Policy and info + - Maintainers will close issues that have been stale for 14 days if they contain relevant answers. + - Adding the label "sweep" will automatically turn the issue into a coded pull request. Works best for mechanical tasks. More info/syntax at: https://docs.sweep.dev/ + + +## Description +A clear and concise description of how the documentation at https://gpt-engineer.readthedocs.io/en/latest/ is providing wrong/insufficient information. + +## Suggestion +How can it be improved diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000000000000000000000000000000000..31e6ea2776a7f0e905945a1af5bdf5eac550ebf8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,19 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement, triage +assignees: '' + +--- + +## Policy and info + - Maintainers will close issues that have been stale for 14 days if they contain relevant answers. + - Adding the label "sweep" will automatically turn the issue into a coded pull request. Works best for mechanical tasks. More info/syntax at: https://docs.sweep.dev/ + - Consider adding the label "good first issue" for interesting, but easy features. + +## Feature description +A clear and concise description of what you would like to have + +## Motivation/Application +Why is this feature useful? diff --git a/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000000000000000000000000000000000..8343ddb0d409247e321e8be7f357b5d4a2322593 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +**YOU MAY DELETE THE ENTIRE TEMPLATE BELOW.** + +## How Has This Been Tested? + +Please describe if you have either: + +- Generated the "example" project +- Ran the entire benchmark suite +- Something else diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml new file mode 100644 index 0000000000000000000000000000000000000000..1f232730d0bf7f31d802a1e162f4cd4de2cc3c7f --- /dev/null +++ b/.github/workflows/automation.yml @@ -0,0 +1,29 @@ +name: Automation Workflow + +on: + schedule: + - cron: '0 0 * * *' + issues: + types: [opened, edited, reopened] + pull_request: + types: [opened, edited, reopened] + +jobs: + mark-stale-issues: + runs-on: ubuntu-latest + steps: + - name: Mark stale issues + uses: actions/stale@v4 + with: + stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.' + days-before-stale: 60 + + # Add additional jobs as needed + # job-name: + # runs-on: ubuntu-latest + # steps: + # - name: Job step name + # uses: action-name@version + # with: + # parameter1: value1 + # diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000000000000000000000000000000000000..94fae70786f6f27dfff651a2cb3c33d2a67e8d58 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,49 @@ +name: Tox pytest all python versions + +on: + push: + branches: [ main ] + paths: + - gpt_engineer/** + - tests/** + pull_request: + branches: [ main ] + +concurrency: + group: ${{ github.workflow }} - ${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.11', '3.12'] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version == '3.12' && '3.12.3' || matrix.python-version }} # Using 3.12.3 to resolve Pydantic ForwardRef issue + cache: 'pip' # Note that pip is for the tox level. Poetry is still used for installing the specific environments (tox.ini) + + - name: Check Python Version + run: python --version + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox==4.15.0 poetry + + - name: Run tox + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: tox + + # Temporarily disabling codecov until we resolve codecov rate limiting issue + # - name: Report coverage + # run: | + # bash <(curl -s https://codecov.io/bash) diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1ef45cc277bf929c66820673a2dc1f38cd5e7d34 --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,22 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [main] + +jobs: + pre-commit: + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + + - uses: pre-commit/action@v3.0.0 + with: + extra_args: --all-files diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5044c1f4da82bc1a48ec4ad83e553a97fdd5bc7b --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,73 @@ +name: Build and publish Python packages to PyPI + +on: + workflow_dispatch: + release: + types: + - published + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: + - "3.10" + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + # Removed the cache line that was here + + # Install Poetry + - name: Install Poetry + run: | + curl -sSL https://install.python-poetry.org | python3 - + + # Add Poetry to PATH + - name: Add Poetry to PATH + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + + # Cache Poetry's dependencies based on the lock file + - name: Set up Poetry cache + uses: actions/cache@v3 + with: + path: ~/.cache/pypoetry + key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }} + restore-keys: | + ${{ runner.os }}-poetry- + + # Install dependencies using Poetry (if any) + - name: Install dependencies + run: poetry install + + # Build package using Poetry + - name: Build package + run: poetry build --format sdist + + # Upload package as build artifact + - uses: actions/upload-artifact@v3 + with: + name: package + path: dist/ + + publish: + runs-on: ubuntu-latest + needs: build + environment: + name: pypi + url: https://pypi.org/p/gpt-engineer + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v3 + with: + name: package + path: dist/ + + - name: Publish packages to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c0c793b88ea93b29b224b33379830d3fe6fbfd87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,98 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +.history/ + +# Distribution / packaging +dist/ +build/ +*.egg-info/ +*.egg + +# Virtual environments +.env +.env.sh +venv/ +ENV/ +venv_test_installation/ + +# IDE-specific files +.vscode/ +.idea/ + +# Compiled Python modules +*.pyc +*.pyo +*.pyd + +# Python testing +.pytest_cache/ +.ruff_cache/ +.mypy_cache/ +.coverage +coverage.* + +# macOS specific files +.DS_Store + +# Windows specific files +Thumbs.db + +# this application's specific files +archive + +# any log file +*log.txt +todo +scratchpad + +# Pyenv +.python-version + +.gpte_consent + +# projects folder apart from default prompt + +projects/* +!projects/example/prompt +!projects/example-improve +!projects/example-vision + +# docs + +docs/_build +docs/applications +docs/benchmark +docs/cli +docs/core +docs/intro +docs/tools + +# coding assistants +.aider* +.gpteng + +# webapp specific +webapp/node_modules +webapp/package-lock.json + +webapp/.next/ + +.langchain.db + +# TODO files +/!todo* + +#ignore tox files +.tox + +# locally saved datasets +gpt_engineer/benchmark/benchmarks/apps/dataset +gpt_engineer/benchmark/benchmarks/mbpp/dataset + +gpt_engineer/benchmark/minimal_bench_config.toml + +test.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3fe0daa264ea673f44dd0e2dbb5ce270281ec9a3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +fail_fast: true +default_stages: [commit] + +repos: + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black + args: [--config, pyproject.toml] + types: [python] + + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.0.272" + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-toml + - id: check-yaml + - id: detect-private-key + - id: end-of-file-fixer + - id: trailing-whitespace diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..995eb517ecfdf443149b03e2cbbb46d7b6e44297 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,39 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # You can also specify other tool versions: + # nodejs: "19" + # rust: "1.64" + # golang: "1.19" + jobs: + post_create_environment: + - pip install poetry + post_install: + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --with docs + pre_build: + - python docs/create_api_rst.py + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +#python: +# install: +# - requirements: docs/requirements.txt diff --git a/Acknowledgements.md b/Acknowledgements.md new file mode 100644 index 0000000000000000000000000000000000000000..4f1a2cb73000745f6eafb2158d73b88da4fcbf55 --- /dev/null +++ b/Acknowledgements.md @@ -0,0 +1,5 @@ +# We thank the following people for inspiration + +| Person | Content | File(s) | Source | +|----|---|---|---| +| Paul Gauthier | The prompt for the `improve code` step is strongly based on Paul's prompt in Aider | /preprompts/improve.txt | https://github.com/paul-gauthier/aider/blob/main/aider/coders/editblock_coder.py diff --git a/DISCLAIMER.md b/DISCLAIMER.md new file mode 100644 index 0000000000000000000000000000000000000000..17fd588fb692369a24f6c3d185b555a88eb731ca --- /dev/null +++ b/DISCLAIMER.md @@ -0,0 +1,11 @@ +# Disclaimer + +gpt-engineer is an experimental application and is provided "as-is" without any warranty, express or implied. By using this software, you agree to assume all risks associated with its use, including but not limited to data loss, system failure, or any other issues that may arise. + +The developers and contributors of this project do not accept any responsibility or liability for any losses, damages, or other consequences that may occur as a result of using this software. You are solely responsible for any decisions and actions taken based on the information provided by gpt-engineer. + +Please note that the use of the GPT-4 language model can be expensive due to its token usage. By utilizing this project, you acknowledge that you are responsible for monitoring and managing your own token usage and the associated costs. It is highly recommended to check your OpenAI API usage regularly and set up any necessary limits or alerts to prevent unexpected charges. + +As an autonomous experiment, gpt-engineer may generate code or take actions that are not in line with real-world business practices or legal requirements. It is your responsibility to ensure that any actions or decisions made by the generated code comply with all applicable laws, regulations, and ethical standards. The developers and contributors of this project shall not be held responsible for any consequences arising from the use of this software. + +By using gpt-engineer, you agree to indemnify, defend, and hold harmless the developers, contributors, and any affiliated parties from and against any and all claims, damages, losses, liabilities, costs, and expenses (including reasonable attorneys' fees) arising from your use of this software or your violation of these terms. diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000000000000000000000000000000000000..f621698a9520857f9378dd2bb99ccdcb9d1067bd --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,76 @@ +# Governance Model of GPT-Engineer + +## I. Project Board Structure + +### Project Board + +The Project Board is the central decision-making body for the project, overseeing both strategic and technical decisions of the open source project GPT-Engineer. + +#### Composition: +- The Board consists of the project's founder, Anton Osika, and representatives from each significant contributing entity, including individual contributors and commercial partners. +- The board is restricted to a maximum of 7 seats. +- New board members are admitted by majority vote. +- Board members may be expelled by majority vote. + +## II. Roles and Responsibilities + +### Veto due to Ethical Considerations +- The founder has veto right over any decisions made by the Board. +- This veto power is a safeguard to ensure the project's direction remains true to its original vision and ethos. + +### Contribution-Conditioned Decision Making +- Each board member has one vote as long as they qualify as active contributors. +- To qualify as an active contributor, a board member or the entity they represent, must have made 6 significant contributions on the GPT-Engineer GitHub page over the past 90 days. +- A significant contribution is: + - A merged pull request with at least 3 lines of code. + - Engagement in a GitHub/Discord bug report, where the board members' input leads to the confirmed resolution of the bug. If the solution is in terms of a merged pull request, the bug resolution together with the merged pull request counts as one significant contribution. + - A non-code, but necessary, community activity agreed on by the board, such as administration, corporate design, workflow design etc, deemed to take more than 1 hour. Participation in meetings or discussions does not count as a significant contribution. +- A board member may retain its seat on the board without voting right. + +## III. Decision-Making Process + +### Majority Voting +- Decisions are made based on a simple majority vote. Majority means more than half of board members with voting rights agree on one decision, regardless of the number of choices. +- The founder's veto can override the majority decision if exercised. + +### Regular Meetings and Reporting +- The Board will convene regularly, with the frequency of meetings decided by the Board members. +- Decisions, discussion points, and contributions will be transparently documented and shared within the project community. + +## IV. Data Access and Confidentiality + +### Board Members' Right to Access Data +- Any confidential data collected by GPT-Engineer is accessible to the board members after signing a relevant non-disclosure agreement (NDA). +- A relevant NDA requires a board member to erase any copies of confidential data obtained by the time of leaving the board. + +## V. Scope of Voting + +### Essential Topics +- Board voting is restricted to essential topics. +- Essential topics include essential technical topics and essential community topics. +- An essential technical topic is a change in the GPT-engineer code base that is likely to introduce breaking changes, or significantly change the user experience for users and developers. +- Essential community topics are changes to the community's governance or other central policy documents such as the readme or license. +- Day-to-day tasks such as bug fixes or implementation of new features outside the core module do not require voting. + +## VI. Transparency + +### Commitment to Transparency +- The governance process will be transparent, with key decisions, meeting minutes, and voting results publicly available, except for sensitive or confidential matters. + +## VII. Amendments + +### Changes to Governance Structure +- The governance model can be revised as the project evolves. Proposals for changes can be made by any Board member and will require a majority vote for adoption. + +## VIII. The GPT-Engineer Brand + +### Copyright and Stewardship +- The creator of GPT-engineer (Anton Osika) will be the steward of the GPT-engineer brand to decide when and how it can be used, and is committed to never jeopardizing the interest of the open source community in this stewardship. +- Anton Osika possesses the exclusive intellectual property rights for the trademark 'GPT-engineer,' encompassing all case variations such as 'gpt-engineer,' 'GPT-engineer,' and 'GPTE.' This ownership extends to the exclusive legal authority to utilize the 'GPT-engineer' trademark in the establishment and branding of both commercial and non-profit entities. It includes, but is not limited to, the use of the trademark in business names, logos, marketing materials, and other forms of corporate identity. Any use of the 'GPT-engineer' trademark, in any of its case variations, by other parties for commercial or non-commercial purposes requires express permission or a license agreement from Anton Osika. + +# Current Board Members +- Anton Osika +- Axel Theorell +- Corey Gallon +- Peter Harrington +- Theo McCabe diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..a56867b9bdba937f0a9cb2656cc999d86aab2eaf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Anton Osika + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..d575b3d2438e549e45a37362920cd79551bffb23 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +recursive-include gpt_engineer/preprompts * diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c66a67e63ad8c350d9b84fb20b24aaf99b926f1f --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +#Sets the default shell for executing commands as /bin/bash and specifies command should be executed in a Bash shell. +SHELL := /bin/bash + +# Color codes for terminal output +COLOR_RESET=\033[0m +COLOR_CYAN=\033[1;36m +COLOR_GREEN=\033[1;32m + +# Defines the targets help, install, dev-install, and run as phony targets. +.PHONY: help install run + +#sets the default goal to help when no target is specified on the command line. +.DEFAULT_GOAL := help + +#Disables echoing of commands. +.SILENT: + +#Sets the variable name to the second word from the MAKECMDGOALS. +name := $(word 2,$(MAKECMDGOALS)) + +#Defines a target named help. +help: + @echo "Please use 'make ' where is one of the following:" + @echo " help Return this message with usage instructions." + @echo " install Will install the dependencies using Poetry." + @echo " run Runs GPT Engineer on the folder with the given name." + +#Defines a target named install. This target will install the project using Poetry. +install: poetry-install install-pre-commit farewell + +#Defines a target named poetry-install. This target will install the project dependencies using Poetry. +poetry-install: + @echo -e "$(COLOR_CYAN)Installing project with Poetry...$(COLOR_RESET)" && \ + poetry install + +#Defines a target named install-pre-commit. This target will install the pre-commit hooks. +install-pre-commit: + @echo -e "$(COLOR_CYAN)Installing pre-commit hooks...$(COLOR_RESET)" && \ + poetry run pre-commit install + +#Defines a target named farewell. This target will print a farewell message. +farewell: + @echo -e "$(COLOR_GREEN)All done!$(COLOR_RESET)" + +#Defines a target named run. This target will run GPT Engineer on the folder with the given name. +run: + @echo -e "$(COLOR_CYAN)Running GPT Engineer on $(COLOR_GREEN)$(name)$(COLOR_CYAN) folder...$(COLOR_RESET)" && \ + poetry run gpt-engineer projects/$(name) + +# Counts the lines of code in the project +cloc: + cloc . --exclude-dir=node_modules,dist,build,.mypy_cache,benchmark --exclude-list-file=.gitignore --fullpath --not-match-d='docs/_build' --by-file diff --git a/README.md b/README.md index dd766132797256b40c42c716b68e075757be9820..fa07e0fcc1b2b0286208bed5ca24007bb285c9da 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,112 @@ ---- -title: Gpt Eng -emoji: 👁 -colorFrom: yellow -colorTo: green -sdk: gradio -sdk_version: 4.38.1 -app_file: app.py -pinned: false ---- - -Check out the configuration reference at https://huggingface.co./docs/hub/spaces-config-reference +# gpt-engineer + +[![GitHub Repo stars](https://img.shields.io/github/stars/gpt-engineer-org/gpt-engineer?style=social)](https://github.com/gpt-engineer-org/gpt-engineer) +[![Discord Follow](https://dcbadge.vercel.app/api/server/8tcDQ89Ej2?style=flat)](https://discord.gg/8tcDQ89Ej2) +[![License](https://img.shields.io/github/license/gpt-engineer-org/gpt-engineer)](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/LICENSE) +[![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/gpt-engineer-org/gpt-engineer)](https://github.com/gpt-engineer-org/gpt-engineer/issues) +![GitHub Release](https://img.shields.io/github/v/release/gpt-engineer-org/gpt-engineer) +[![Twitter Follow](https://img.shields.io/twitter/follow/antonosika?style=social)](https://twitter.com/antonosika) + +gpt-engineer lets you: +- Specify software in natural language +- Sit back and watch as an AI writes and executes the code +- Ask the AI to implement improvements + +## Getting Started + +### Install gpt-engineer + +For **stable** release: + +- `python -m pip install gpt-engineer` + +For **development**: +- `git clone https://github.com/gpt-engineer-org/gpt-engineer.git` +- `cd gpt-engineer` +- `poetry install` +- `poetry shell` to activate the virtual environment + +We actively support Python 3.10 - 3.12. The last version to support Python 3.8 - 3.9 was [0.2.6](https://pypi.org/project/gpt-engineer/0.2.6/). + +### Setup API key + +Choose **one** of: +- Export env variable (you can add this to .bashrc so that you don't have to do it each time you start the terminal) + - `export OPENAI_API_KEY=[your api key]` +- .env file: + - Create a copy of `.env.template` named `.env` + - Add your OPENAI_API_KEY in .env +- Custom model: + - See [docs](https://gpt-engineer.readthedocs.io/en/latest/open_models.html), supports local model, azure, etc. + +Check the [Windows README](./WINDOWS_README.md) for Windows usage. + +**Other ways to run:** +- Use Docker ([instructions](docker/README.md)) +- Do everything in your browser: +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/gpt-engineer-org/gpt-engineer/codespaces) + +### Create new code (default usage) +- Create an empty folder for your project anywhere on your computer +- Create a file called `prompt` (no extension) inside your new folder and fill it with instructions +- Run `gpte ` with a relative path to your folder + - For example: `gpte projects/my-new-project` from the gpt-engineer directory root with your new folder in `projects/` + +### Improve existing code +- Locate a folder with code which you want to improve anywhere on your computer +- Create a file called `prompt` (no extension) inside your new folder and fill it with instructions for how you want to improve the code +- Run `gpte -i` with a relative path to your folder + - For example: `gpte projects/my-old-project -i` from the gpt-engineer directory root with your folder in `projects/` + +### Benchmark custom agents +- gpt-engineer installs the binary 'bench', which gives you a simple interface for benchmarking your own agent implementations against popular public datasets. +- The easiest way to get started with benchmarking is by checking out the [template](https://github.com/gpt-engineer-org/gpte-bench-template) repo, which contains detailed instructions and an agent template. +- Currently supported benchmark: + - [APPS](https://github.com/hendrycks/apps) + - [MBPP](https://github.com/google-research/google-research/tree/master/mbpp) + +By running gpt-engineer, you agree to our [terms](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/TERMS_OF_USE.md). + + +## Relation to gptengineer.app (GPT Engineer) +[gptengineer.app](https://gptengineer.app/) is a commercial project for the automatic generation of web apps. +It features a UI for non-technical users connected to a git-controlled codebase. +The gptengineer.app team is actively supporting the open source community. + + +## Features + +### Pre Prompts +You can specify the "identity" of the AI agent by overriding the `preprompts` folder with your own version of the `preprompts`. You can do so via the `--use-custom-preprompts` argument. + +Editing the `preprompts` is how you make the agent remember things between projects. + +### Vision + +By default, gpt-engineer expects text input via a `prompt` file. It can also accept image inputs for vision-capable models. This can be useful for adding UX or architecture diagrams as additional context for GPT Engineer. You can do this by specifying an image directory with the `—-image_directory` flag and setting a vision-capable model in the second CLI argument. + +E.g. `gpte projects/example-vision gpt-4-vision-preview --prompt_file prompt/text --image_directory prompt/images -i` + +### Open source, local and alternative models + +By default, gpt-engineer supports OpenAI Models via the OpenAI API or Azure OpenAI API, as well as Anthropic models. + +With a little extra setup, you can also run with open source models like WizardCoder. See the [documentation](https://gpt-engineer.readthedocs.io/en/latest/open_models.html) for example instructions. + +## Mission + +The gpt-engineer community mission is to **maintain tools that coding agent builders can use and facilitate collaboration in the open source community**. + +If you are interested in contributing to this, we are interested in having you. + +If you want to see our broader ambitions, check out the [roadmap](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/ROADMAP.md), and join +[discord](https://discord.gg/8tcDQ89Ej2) +to learn how you can [contribute](.github/CONTRIBUTING.md) to it. + +gpt-engineer is [governed](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/GOVERNANCE.md) by a board of long-term contributors. If you contribute routinely and have an interest in shaping the future of gpt-engineer, you will be considered for the board. + +## Example + + + +https://github.com/gpt-engineer-org/gpt-engineer/assets/4467025/40d0a9a8-82d0-4432-9376-136df0d57c99 diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000000000000000000000000000000000000..f3759edbf3ebe7919a5abf70782b5548d9271cea --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,31 @@ +# Roadmap + +image + + +This document is a general roadmap guide to the gpt-engineer project's strategic direction. +Our goal is to continually improve by focusing on three main pillars: +- User Experience, +- Technical Features, and +- Performance Tracking/Testing. + +Each pillar is supported by a set of epics, reflecting our major goals and initiatives. + + +## Tracking Progress with GitHub Projects + +We are using [GitHub Projects](https://github.com/orgs/gpt-engineer-org/projects/3) to track the progress of our roadmap. + +Each issue within our project is categorized under one of the main pillars and, in most cases, associated epics. You can check our [Project's README](https://github.com/orgs/gpt-engineer-org/projects/3?pane=info) section to understand better our logic and organization. + + + +# How you can help out + +You can: + +- Post a "design" as a Google Doc in our [Discord](https://discord.com/channels/1119885301872070706/1120698764445880350), and ask for feedback to address one of the items in the roadmap +- Submit PRs to address one of the items in the roadmap +- Do a review of someone else's PR and propose next steps (further review, merge, close) + +🙌 Volunteer work in any of these will get acknowledged.🙌 diff --git a/TERMS_OF_USE.md b/TERMS_OF_USE.md new file mode 100644 index 0000000000000000000000000000000000000000..91e97b86a28860f110da84a152a3b1ac749c96dd --- /dev/null +++ b/TERMS_OF_USE.md @@ -0,0 +1,13 @@ +# Terms of Use + +Welcome to gpt-engineer! By utilizing this powerful tool, you acknowledge and agree to the following comprehensive Terms of Use. We also encourage you to review the linked [disclaimer of warranty](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/DISCLAIMER.md) for additional information. + +Both OpenAI, L.L.C. and the dedicated creators behind the remarkable gpt-engineer have implemented a data collection process focused on enhancing the product's capabilities. This endeavor is undertaken with utmost care and dedication to safeguarding user privacy. Rest assured that no information that could be directly attributed to any individual is stored. + +It's important to be aware that the utilization of natural text inputs, including the 'prompt' and 'feedback' files, may be subject to storage. While it's theoretically possible to establish connections between a person's writing style or content within these files and their real-life identity, please note that the creators of gpt-engineer explicitly assure that such attempts will never be made. + +For a deeper understanding of OpenAI's overarching terms of use, we encourage you to explore the details available [here](https://openai.com/policies/terms-of-use). + +Optionally, gpt-engineer collects usage data for the purpose of improving gpt-engineer. Data collection only happens when a consent file called .gpte_consent is present in the gpt-engineer directory. Note that gpt-engineer cannot prevent that data streams passing through gpt-engineer to a third party may be stored by that third party (for example OpenAI). + +Your engagement with gpt-engineer is an acknowledgment and acceptance of these terms, demonstrating your commitment to using this tool responsibly and within the bounds of ethical conduct. We appreciate your trust and look forward to the exciting possibilities that gpt-engineer can offer in your endeavors. diff --git a/WINDOWS_README.md b/WINDOWS_README.md new file mode 100644 index 0000000000000000000000000000000000000000..1553d542dd235f32278c4bc9b92c7b3ab96d6027 --- /dev/null +++ b/WINDOWS_README.md @@ -0,0 +1,68 @@ +# Windows Setup +## Short version + +On Windows, follow the standard [README.md](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/README.md), but to set API key do one of: +- `set OPENAI_API_KEY=[your api key]` on cmd +- `$env:OPENAI_API_KEY="[your api key]"` on powershell + +## Full setup guide + +Choose either **stable** or **development**. + +For **stable** release: + +Run `pip install gpt-engineer` in the command line as an administrator + +Or: + + 1. Open your web browser and navigate to the Python Package Index (PyPI) website: . + 2. On the PyPI page for the gpt-engineer package, locate the "Download files" section. Here you'll find a list of available versions and their corresponding download links. + 3. Identify the version of gpt-engineer you want to install and click on the associated download link. This will download the package file (usually a .tar.gz or .whl file) to your computer. + 4. Once the package file is downloaded, open your Python development environment or IDE. + 5. In your Python development environment, look for an option to install packages or manage dependencies. The exact location and terminology may vary depending on your IDE. For example, in PyCharm, you can go to "File" > "Settings" > "Project: \" > "Python Interpreter" to manage packages. + 6. In the package management interface, you should see a list of installed packages. Look for an option to add or install a new package. + 7. Click on the "Add Package" or "Install Package" button. + 8. In the package installation dialog, choose the option to install from a file or from a local source. + 9. Browse and select the downloaded gpt-engineer package file from your computer. + +For **development**: + +- `git clone git@github.com:gpt-engineer-org/gpt-engineer.git` +- `cd gpt-engineer` +- `poetry install` +- `poetry shell` to activate the virtual environment + +### Setup + +With an api key from OpenAI: + +Run `set OPENAI_API_KEY=[your API key]` in the command line + +Or: + + 1. In the Start Menu, type to search for "Environment Variables" and click on "Edit the system environment variables". + 2. In the System Properties window, click on the "Environment Variables" button. + 3. In the Environment Variables window, you'll see two sections: User variables and System variables. + 4. To set a user-specific environment variable, select the "New" button under the User variables section. + 5. To set a system-wide environment variable, select the "New" button under the System variables section. + 6. Enter the variable name "OPENAI_API_KEY" in the "Variable name" field. + 7. Enter the variable value (e.g., your API key) in the "Variable value" field. + 8. Click "OK" to save the changes. + 9. Close any open command prompt or application windows and reopen them for the changes to take effect. + +Now you can use `%OPENAI_API_KEY%` when prompted to input your key. + +### Run + +- Create an empty folder. If inside the repo, you can: + - Run `xcopy /E projects\example projects\my-new-project` in the command line + - Or hold CTRL and drag the folder down to create a copy, then rename to fit your project +- Fill in the `prompt` file in your new folder +- `gpt-engineer projects/my-new-project` + - (Note, `gpt-engineer --help` lets you see all available options. For example `--steps use_feedback` lets you improve/fix code in a project) + +By running gpt-engineer you agree to our [ToS](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/TERMS_OF_USE.md). + +### Results + +- Check the generated files in `projects/my-new-project/workspace` diff --git a/citation.cff b/citation.cff new file mode 100644 index 0000000000000000000000000000000000000000..62dd0edafd02d65c2c3e000a167b09d8e839406d --- /dev/null +++ b/citation.cff @@ -0,0 +1,10 @@ +cff-version: 1.0.0 +message: "If you use this software, please cite it as below." +authors: + - family-names: Osika + given-names: Anton +title: gpt-engineer +version: 0.1.0 +date-released: 2023-04-23 +repository-code: https://github.com/gpt-engineer-org/gpt-engineer +url: https://gpt-engineer.readthedocs.io diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..dc96935b66575fcad157920e2f51bcbe3a877a86 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +services: + gpt-engineer: + build: + context: . + dockerfile: docker/Dockerfile + stdin_open: true + tty: true + # Set the API key from the .env file + env_file: + - .env + ## OR set the API key directly + # environment: + # - OPENAI_API_KEY=YOUR_API_KEY + image: gpt-engineer + volumes: + - ./projects/example:/project diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..50e3fe1811634cc18ea53e858f0191fa09426b0b --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,29 @@ +# Stage 1: Builder stage +FROM python:3.11-slim AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + tk \ + tcl \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY . . + +RUN pip install --no-cache-dir -e . + +# Stage 2: Final stage +FROM python:3.11-slim + +WORKDIR /app + +COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages +COPY --from=builder /usr/local/bin /usr/local/bin +COPY --from=builder /usr/bin /usr/bin +COPY --from=builder /app . + +COPY docker/entrypoint.sh . + +ENTRYPOINT ["bash", "/app/entrypoint.sh"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000000000000000000000000000000000..89d53b5e8c53c43bdabacd2c83050ed6e553a04d --- /dev/null +++ b/docker/README.md @@ -0,0 +1,69 @@ +# Getting Started Using Docker + +This guide provides step-by-step instructions on how to set up and run the Docker environment for your GPT-Engineer project. + +## Prerequisites + +- Docker installed on your machine. +- Git (for cloning the repository). + +## Setup Instructions + +### Using Docker CLI + +1. **Clone the Repository** + + ```bash + git clone https://github.com/gpt-engineer-org/gpt-engineer.git + cd gpt-engineer + ``` + +2. **Build the Docker Image** + + ```bash + docker build --rm -t gpt-engineer -f docker/Dockerfile . + ``` + +3. **Run the Docker Container** + + ```bash + docker run -it --rm -e OPENAI_API_KEY="YOUR_OPENAI_KEY" -v ./your-project:/project gpt-engineer + ``` + + Replace `YOUR_OPENAI_KEY` with your actual OpenAI API key. The `-v` flag mounts your local `your-project` directory inside the container. Replace this with your actual project directory. Ensure this directory contains all necessary files, including the `prompt` file. + +### Using Docker Compose + +1. **Clone the Repository** (if not already done) + + ```bash + git clone https://github.com/gpt-engineer-org/gpt-engineer.git + cd gpt-engineer + ``` + +2. **Build and Run using Docker Compose** + + ```bash + docker-compose -f docker-compose.yml build + docker-compose run --rm gpt-engineer + ``` + + Set the `OPENAI_API_KEY` in the `docker/docker-compose.yml` using an `.env` file or as an environment variable. Mount your project directory to the container using volumes, e.g., `"./projects/example:/project"` where `./projects/example` is the path to your project directory. + +3. **Another alternative using Docker Compose** + + Since there is only one `docker-compose.yml` file, you could run it without the -f option. + - `docker compose up -d --build` - To build and start the containers defined in your `docker-compose.yml` file in detached mode + - `docker compose up -d` - To start the containers defined in your `docker-compose.yml` file in detached mode + - `docker compose down` - To stop and remove all containers, networks, and volumes associated with the `docker-compose.yml` + - `docker compose restart` - To restart the containers defined in the `docker-compose.yml` file + +## Debugging + +To facilitate debugging, you can run a shell inside the built Docker image: + +```bash +docker run -it --entrypoint /bin/bash gpt-engineer +``` + +This opens a shell inside the Docker container, allowing you to execute commands and inspect the environment manually. diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..138ab50255824c599dd1acb60e270b95ced628dc --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# -*- coding: utf-8 -*- + +project_dir="/project" + +# Run the gpt engineer script +gpt-engineer $project_dir "$@" + +# Patch the permissions of the generated files to be owned by nobody except prompt file +find "$project_dir" -mindepth 1 -maxdepth 1 ! -path "$project_dir/prompt" -exec chown -R nobody:nogroup {} + -exec chmod -R 777 {} + diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b7e237fa0362ca1fa39c4b2333b4c0700d307c6a --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = python -msphinx +SPHINXPROJ = gpt_engineer +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/api_reference.rst b/docs/api_reference.rst new file mode 100644 index 0000000000000000000000000000000000000000..1f0912e09d35602d4d9cbe7874fdb32ebbb93056 --- /dev/null +++ b/docs/api_reference.rst @@ -0,0 +1,144 @@ +.. _api_reference: + +============= +API Reference +============= + +:mod:`gpt_engineer.applications`: Applications +=============================================== + +.. automodule:: gpt_engineer.applications + :no-members: + :no-inherited-members: + +Classes +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: applications + :template: class.rst + + applications.cli.cli_agent.CliAgent + applications.cli.file_selector.DisplayablePath + +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: applications + + applications.cli.collect.collect_and_send_human_review + applications.cli.collect.collect_learnings + applications.cli.collect.send_learning + applications.cli.learning.ask_collection_consent + applications.cli.learning.ask_for_valid_input + applications.cli.learning.check_collection_consent + applications.cli.learning.extract_learning + applications.cli.learning.get_session + applications.cli.learning.human_review_input + applications.cli.main.get_preprompts_path + applications.cli.main.load_env_if_needed + applications.cli.main.load_prompt + applications.cli.main.main + +:mod:`gpt_engineer.benchmark`: Benchmark +========================================= + +.. automodule:: gpt_engineer.benchmark + :no-members: + :no-inherited-members: + +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: benchmark + + benchmark.__main__.get_agent + benchmark.__main__.main + benchmark.benchmarks.gpteng.eval_tools.assert_exists_in_source_code + benchmark.benchmarks.gpteng.eval_tools.check_evaluation_component + benchmark.benchmarks.gpteng.eval_tools.check_language + benchmark.benchmarks.gpteng.eval_tools.run_code_class_has_property + benchmark.benchmarks.gpteng.eval_tools.run_code_class_has_property_w_value + benchmark.benchmarks.gpteng.eval_tools.run_code_eval_function + benchmark.benchmarks.gpteng.load.eval_to_task + benchmark.benchmarks.gpteng.load.expect_to_assertion + benchmark.benchmarks.gpteng.load.load_gpteng + benchmark.benchmarks.gptme.load.load_gptme + benchmark.benchmarks.load.get_benchmark + benchmark.run.print_results + benchmark.run.run + +:mod:`gpt_engineer.core`: Core +=============================== + +.. automodule:: gpt_engineer.core + :no-members: + :no-inherited-members: + +Classes +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: core + :template: class.rst + + core.base_agent.BaseAgent + core.base_execution_env.BaseExecutionEnv + core.default.disk_execution_env.DiskExecutionEnv + core.default.disk_memory.DiskMemory + core.default.simple_agent.SimpleAgent + core.files_dict.FilesDict + core.version_manager.BaseVersionManager + +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: core + + core.ai.serialize_messages + core.chat_to_files.apply_diffs + core.chat_to_files.chat_to_files_dict + core.chat_to_files.parse_diff_block + core.chat_to_files.parse_diffs + core.chat_to_files.parse_hunk_header + core.default.paths.memory_path + core.default.paths.metadata_path + core.default.simple_agent.default_config_agent + core.default.steps.curr_fn + core.default.steps.execute_entrypoint + core.default.steps.gen_code + core.default.steps.gen_entrypoint + core.default.steps.improve + core.default.steps.salvage_correct_hunks + core.default.steps.setup_sys_prompt + core.default.steps.setup_sys_prompt_existing_code + core.diff.count_ratio + core.diff.is_similar + core.files_dict.file_to_lines_dict + +:mod:`gpt_engineer.tools`: Tools +================================= + +.. automodule:: gpt_engineer.tools + :no-members: + :no-inherited-members: + +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: tools + + tools.custom_steps.clarified_gen + tools.custom_steps.get_platform_info + tools.custom_steps.lite_gen + tools.custom_steps.self_heal diff --git a/docs/code_conduct_link.rst b/docs/code_conduct_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..58cb2e4fdaa68ea16e545d7a36b949ec382d303e --- /dev/null +++ b/docs/code_conduct_link.rst @@ -0,0 +1,2 @@ +.. include:: ../.github/CODE_OF_CONDUCT.md + :parser: myst_parser.sphinx_ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..235b36c8cf27849d9367726034714ac303cff264 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# +# file_processor documentation build configuration file, created by +# sphinx-quickstart on Fri Jun 9 13:47:02 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another +# directory, add these directories to sys.path here. If the directory is +# relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +# +import os +import sys + +from pathlib import Path + +import toml + +sys.path.insert(0, os.path.abspath("..")) + +ROOT_DIR = Path(__file__).parents[1].absolute() + +with open("../pyproject.toml") as f: + data = toml.load(f) + + +# The master toctree document. +master_doc = "index" + +# General information about the project. +project = data["tool"]["poetry"]["name"] +copyright = "2023 Anton Osika" +author = " Anton Osika & Contributors" + +# The version info for the project you're documenting, acts as replacement +# for |version| and |release|, also used in various other places throughout +# the built documents. +# +# The short X.Y version. +version = data["tool"]["poetry"]["version"] +# The full version, including alpha/beta/rc tags. +release = data["tool"]["poetry"]["version"] + + +# -- General configuration --------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autodoc.typehints", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", + "sphinx_copybutton", + "myst_parser", + "IPython.sphinxext.ipython_console_highlighting", +] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: + +source_suffix = [".rst", ".md"] + +autodoc_pydantic_model_show_json = False +autodoc_pydantic_field_list_validators = False +autodoc_pydantic_config_members = False +autodoc_pydantic_model_show_config_summary = False +autodoc_pydantic_model_show_validator_members = False +autodoc_pydantic_model_show_validator_summary = False +autodoc_pydantic_model_signature_prefix = "class" +autodoc_pydantic_field_signature_prefix = "param" +autodoc_member_order = "groupwise" +autoclass_content = "both" +autodoc_typehints_format = "short" + +autodoc_default_options = { + "members": True, + "show-inheritance": True, + "inherited-members": "BaseModel", + "undoc-members": False, +} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + + +# source_suffix = '.rst' + + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "en" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = 'alabaster' +html_theme = "sphinx_rtd_theme" + +# Theme options are theme-specific and customize the look and feel of a +# theme further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ["_static"] + + +# -- Options for HTMLHelp output --------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = "gpt_engineerdoc" + + +# -- Options for LaTeX output ------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass +# [howto, manual, or own class]). +latex_documents = [ + (master_doc, "gpt_engineer.tex", "GPT-ENgineer Documentation", "manual"), +] + + +# -- Options for manual page output ------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, "gpt_engineer", "GPT-Engineer Documentation", [author], 1)] + + +# -- Options for Texinfo output ---------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + "gpt_engineer", + "GPT-Engineer Documentation", + author, + "gpt_engineer", + "One line description of project.", + "Miscellaneous", + ), +] + +# generate autosummary even if no references +autosummary_generate = True + +myst_enable_extensions = [ + "colon_fence", +] + +myst_all_links_external = True diff --git a/docs/contributing_link.rst b/docs/contributing_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..4d4c123bdfdf4bcd211f9034994fca7201f6f9fb --- /dev/null +++ b/docs/contributing_link.rst @@ -0,0 +1,2 @@ +.. include:: ../.github/CONTRIBUTING.md + :parser: myst_parser.sphinx_ diff --git a/docs/create_api_rst.py b/docs/create_api_rst.py new file mode 100644 index 0000000000000000000000000000000000000000..6b443c484aa8680ec289cdf000a7f71e4b73ef83 --- /dev/null +++ b/docs/create_api_rst.py @@ -0,0 +1,98 @@ +"""Script for auto-generating api_reference.rst""" +import glob +import re + +from pathlib import Path + +ROOT_DIR = Path(__file__).parents[1].absolute() +print(ROOT_DIR) +PKG_DIR = ROOT_DIR / "gpt_engineer" +WRITE_FILE = Path(__file__).parent / "api_reference.rst" + + +def load_members() -> dict: + members: dict = {} + for py in glob.glob(str(PKG_DIR) + "/**/*.py", recursive=True): + module = py[len(str(PKG_DIR)) + 1 :].replace(".py", "").replace("/", ".") + top_level = module.split(".")[0] + if top_level not in members: + members[top_level] = {"classes": [], "functions": []} + with open(py, "r") as f: + for line in f.readlines(): + cls = re.findall(r"^class ([^_].*)\(", line) + members[top_level]["classes"].extend([module + "." + c for c in cls]) + func = re.findall(r"^def ([^_].*)\(", line) + afunc = re.findall(r"^async def ([^_].*)\(", line) + func_strings = [module + "." + f for f in func + afunc] + members[top_level]["functions"].extend(func_strings) + return members + + +def construct_doc(members: dict) -> str: + full_doc = """\ +.. _api_reference: + +============= +API Reference +============= + +""" + for module, _members in sorted(members.items(), key=lambda kv: kv[0]): + classes = _members["classes"] + functions = _members["functions"] + if not (classes or functions): + continue + + module_title = module.replace("_", " ").title() + if module_title == "Llms": + module_title = "LLMs" + section = f":mod:`gpt_engineer.{module}`: {module_title}" + full_doc += f"""\ +{section} +{'=' * (len(section) + 1)} + +.. automodule:: gpt_engineer.{module} + :no-members: + :no-inherited-members: + +""" + + if classes: + cstring = "\n ".join(sorted(classes)) + full_doc += f"""\ +Classes +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: {module} + :template: class.rst + + {cstring} + +""" + if functions: + fstring = "\n ".join(sorted(functions)) + full_doc += f"""\ +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: {module} + + {fstring} + +""" + return full_doc + + +def main() -> None: + members = load_members() + full_doc = construct_doc(members) + with open(WRITE_FILE, "w") as f: + f.write(full_doc) + + +if __name__ == "__main__": + main() diff --git a/docs/disclaimer_link.rst b/docs/disclaimer_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..d0ed67e71dd2033b02e13197893c451447db0050 --- /dev/null +++ b/docs/disclaimer_link.rst @@ -0,0 +1,2 @@ +.. include:: ../DISCLAIMER.md + :parser: myst_parser.sphinx_ diff --git a/docs/docs_building.md b/docs/docs_building.md new file mode 100644 index 0000000000000000000000000000000000000000..52fc35a73ce1254d8d83bf5f11f0d3da6c10f004 --- /dev/null +++ b/docs/docs_building.md @@ -0,0 +1,64 @@ +Building Docs with Sphinx +========================= + +This example shows a basic Sphinx docs project with Read the Docs. This project is using `sphinx` with `readthedocs` +project template. + +Some useful links are given below to lear and contribute in the project. + +📚 [docs/](https://www.sphinx-doc.org/en/master/usage/quickstart.html)
+A basic Sphinx project lives in `docs/`, it was generated using Sphinx defaults. All the `*.rst` & `*.md` make up sections in the documentation. Both `.rst` and `.md` formats are supported in this project + +⚙️ [.readthedocs.yaml](https://docs.readthedocs.io/en/stable/config-file/v2.html)
+Read the Docs Build configuration is stored in `.readthedocs.yaml`. + + +Example Project usage +--------------------- + +``Poetry`` is the package manager for ``gpt-engineer``. In order to build documentation, we have to add docs requirements in +development environment. + +This project has a standard readthedocs layout which is built by Read the Docs almost the same way that you would build it +locally (on your own laptop!). + +You can build and view this documentation project locally - we recommend that you activate a ``poetry shell``. + +Update ``repository_stats.md`` file under ``docs/intro`` + +```console +# Install required Python dependencies (MkDocs etc.) +poetry install +cd docs/ + +# Create the `api_reference.rst` +python create_api_rst.py + +# Build the docs +make html + +## Alternatively, to rebuild the docs on changes with live-reload in the browser +sphinx-autobuild . _build/html +``` + +Project Docs Structure +---------------------- +If you are new to Read the Docs, you may want to refer to the [Read the Docs User documentation](https://docs.readthedocs.io/). + +Below is the rundown of documentation structure for `pandasai`, you need to know: + +1. place your `docs/` folder alongside your Python project. +2. copy `.readthedocs.yaml` and the `docs/` folder into your project root. +3. `docs/api_reference.rst` contains the API documentation created using `docstring`. Run the `create_api_rst.py` to update the API reference file. +4. Project is using standard Google Docstring Style. +5. Rebuild the documentation locally to see that it works. +6. Documentation are hosted on [Read the Docs tutorial](https://docs.readthedocs.io/en/stable/tutorial/) + + +Read the Docs tutorial +---------------------- + +To get started with Read the Docs, you may also refer to the +[Read the Docs tutorial](https://docs.readthedocs.io/en/stable/tutorial/). I + +With every release, build the documentation manually. diff --git a/docs/examples/open_llms/README.md b/docs/examples/open_llms/README.md new file mode 100644 index 0000000000000000000000000000000000000000..93e8f3300fd21ff6148970008dcfb4ed61d48a76 --- /dev/null +++ b/docs/examples/open_llms/README.md @@ -0,0 +1,56 @@ +# Test that the Open LLM is running + +First start the server by using only CPU: + +```bash +export model_path="TheBloke/CodeLlama-13B-GGUF/codellama-13b.Q8_0.gguf" +python -m llama_cpp.server --model $model_path +``` + +Or with GPU support (recommended): + +```bash +python -m llama_cpp.server --model TheBloke/CodeLlama-13B-GGUF/codellama-13b.Q8_0.gguf --n_gpu_layers 1 +``` + +If you have more `GPU` layers available set `--n_gpu_layers` to the higher number. + +To find the amount of available run the above command and look for `llm_load_tensors: offloaded 1/41 layers to GPU` in the output. + +## Test API call + +Set the environment variables: + +```bash +export OPENAI_API_BASE="http://localhost:8000/v1" +export OPENAI_API_KEY="sk-xxx" +export MODEL_NAME="CodeLlama" +```` + +Then ping the model via `python` using `OpenAI` API: + +```bash +python examples/open_llms/openai_api_interface.py +``` + +If you're not using `CodeLLama` make sure to change the `MODEL_NAME` parameter. + +Or using `curl`: + +```bash +curl --request POST \ + --url http://localhost:8000/v1/chat/completions \ + --header "Content-Type: application/json" \ + --data '{ "model": "CodeLlama", "prompt": "Who are you?", "max_tokens": 60}' +``` + +If this works also make sure that `langchain` interface works since that's how `gpte` interacts with LLMs. + +## Langchain test + +```bash +export MODEL_NAME="CodeLlama" +python examples/open_llms/langchain_interface.py +``` + +That's it 🤓 time to go back [to](/docs/open_models.md#running-the-example) and give `gpte` a try. diff --git a/docs/examples/open_llms/langchain_interface.py b/docs/examples/open_llms/langchain_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..05a7c2c269ba683bb6054b0533d0141a70ea5073 --- /dev/null +++ b/docs/examples/open_llms/langchain_interface.py @@ -0,0 +1,17 @@ +import os + +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler +from langchain_openai import ChatOpenAI + +model = ChatOpenAI( + model=os.getenv("MODEL_NAME"), + temperature=0.1, + callbacks=[StreamingStdOutCallbackHandler()], + streaming=True, +) + +prompt = ( + "Provide me with only the code for a simple python function that sums two numbers." +) + +model.invoke(prompt) diff --git a/docs/examples/open_llms/openai_api_interface.py b/docs/examples/open_llms/openai_api_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..21650b77c1fba2ebaf98980940b99fda12cb7d2c --- /dev/null +++ b/docs/examples/open_llms/openai_api_interface.py @@ -0,0 +1,21 @@ +import os + +from openai import OpenAI + +client = OpenAI( + base_url=os.getenv("OPENAI_API_BASE"), api_key=os.getenv("OPENAI_API_KEY") +) + +response = client.chat.completions.create( + model=os.getenv("MODEL_NAME"), + messages=[ + { + "role": "user", + "content": "Provide me with only the code for a simple python function that sums two numbers.", + }, + ], + temperature=0.7, + max_tokens=200, +) + +print(response.choices[0].message.content) diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..11a428de13faab383646ff9f81077024784bb61f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,41 @@ +Welcome to GPT-ENGINEER's Documentation +======================================= + +.. toctree:: + :maxdepth: 2 + :caption: GET STARTED: + + introduction.md + installation + quickstart + +.. toctree:: + :maxdepth: 2 + :caption: USER GUIDES: + + windows_readme_link + open_models.md + tracing_debugging.md + +.. toctree:: + :maxdepth: 2 + :caption: CONTRIBUTE: + + contributing_link + roadmap_link + code_conduct_link + disclaimer_link + docs_building.md + terms_link + +.. toctree:: + :maxdepth: 2 + :caption: PACKAGE API: + + api_reference + +Indices and tables +================== +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000000000000000000000000000000000000..6dc776bbcff31a18991f1676a19f87599ea7c52b --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,63 @@ +.. highlight:: shell + +============ +Installation +============ + + +Stable release +-------------- + +To install ``gpt-engineer``, run this command in your terminal: + +.. code-block:: console + + $ python -m pip install gpt-engineer + +This is the preferred method to install ``gpt-engineer``, as it will always install the most recent stable release. + +If you don't have `pip`_ installed, this `Python installation guide`_ can guide +you through the process. + +.. _pip: https://pip.pypa.io +.. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ + + +From sources +------------ + +The sources for ``gpt-engineer`` can be downloaded from the `Github repo`_. + +You can either clone the public repository: + +.. code-block:: console + + $ git clone https://github.com/gpt-engineer-org/gpt-engineer.git + +Once you have a copy of the source, you can install it with: + +.. code-block:: console + + $ cd gpt-engineer + $ poetry install + $ poetry shell + + +.. _Github repo: https://github.com/gpt-engineer-org/gpt-engineer.git + +Troubleshooting +--------------- + +For mac and linux system, there are sometimes slim python installations that do not include the ``gpt-engineer`` requirement tkinter, which is a standard library and thus not pip installable. + +To install tkinter on mac, you can for example use brew: + +.. code-block:: console + + $ brew install python-tk + +On debian-based linux systems you can use: + +.. code-block:: console + + $ sudo apt-get install python3-tk diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 0000000000000000000000000000000000000000..4cfdcf86a2190334e8875697ae750c6cbb377dfc --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,20 @@ +# Introduction +``gpt-engineer`` is a project that uses LLMs (such as GPT-4) to automate the process of software engineering. It includes several Python scripts that interact with the LLM to generate code, clarify requirements, generate specifications, and more. + +
+ +## Get started +[Here’s](/en/latest/installation.html) how to install ``gpt-engineer``, set up your environment, and start building. + +We recommend following our [Quickstart](/en/latest/quickstart.html) guide to familiarize yourself with the framework by building your first application with ``gpt-engineer``. + +
+ +## Example +You can find an example of the project in action here. + + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000000000000000000000000000000000000..d172000568e1e51226ac647fe6e80b36ffdbd7fa --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=python -msphinx +) +set SOURCEDIR=. +set BUILDDIR=_build +set SPHINXPROJ=file_processor + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The Sphinx module was not found. Make sure you have Sphinx installed, + echo.then set the SPHINXBUILD environment variable to point to the full + echo.path of the 'sphinx-build' executable. Alternatively you may add the + echo.Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/open_models.md b/docs/open_models.md new file mode 100644 index 0000000000000000000000000000000000000000..e1cc5a5294d29f2fd013ae2b34633733d7f79adb --- /dev/null +++ b/docs/open_models.md @@ -0,0 +1,148 @@ +Using with open/local models +============================ + +**Use `gpte` first with OpenAI models to get a feel for the `gpte` tool.** + +**Then go play with experimental Open LLMs 🐉 support and try not to get 🔥!!** + +At the moment the best option for coding is still the use of `gpt-4` models provided by OpenAI. But open models are catching up and are a good free and privacy-oriented alternative if you possess the proper hardware. + +You can integrate `gpt-engineer` with open-source models by leveraging an OpenAI-compatible API. + +We provide the minimal and cleanest solution below. What is described is not the only way to use open/local models, but the one we tested and would recommend to most users. + +More details on why the solution below is recommended in [this blog post](https://zigabrencic.com/blog/2024-02-21). + +Setup +----- + +For inference engine we recommend for the users to use [llama.cpp](https://github.com/ggerganov/llama.cpp) with its `python` bindings `llama-cpp-python`. + +We choose `llama.cpp` because: + +- 1.) It supports the largest amount of hardware acceleration backends. +- 2.) It supports the diverse set of open LLMs. +- 3.) Is written in `python` and directly on top of `llama.cpp` inference engine. +- 4.) Supports the `openAI` API and `langchain` interface. + +To install `llama-cpp-python` follow the official [installation docs](https://llama-cpp-python.readthedocs.io/en/latest/) and [those docs](https://llama-cpp-python.readthedocs.io/en/latest/install/macos/) for MacOS with Metal support. + +If you want to benefit from proper hardware acceleration on your machine make sure to set up the proper compiler flags before installing your package. + +- `linux`: `CMAKE_ARGS="-DLLAMA_BLAS=ON -DLLAMA_BLAS_VENDOR=OpenBLAS"` +- `macos` with Metal support: `CMAKE_ARGS="-DLLAMA_METAL=on"` +- `windows`: `$env:CMAKE_ARGS = "-DLLAMA_BLAS=ON -DLLAMA_BLAS_VENDOR=OpenBLAS"` + +This will enable the `pip` installer to compile the `llama.cpp` with the proper hardware acceleration backend. + +Then run: + +```bash +pip install llama-cpp-python +``` + +For our use case we also need to set up the web server that `llama-cpp-python` library provides. To install: + +```bash +pip install 'llama-cpp-python[server]' +``` + +For detailed use consult the [`llama-cpp-python` docs](https://llama-cpp-python.readthedocs.io/en/latest/server/). + +Before we proceed we need to obtain the model weights in the `gguf` format. That should be a single file on your disk. + +In case you have weights in other formats check the `llama-cpp-python` docs for conversion to `gguf` format. + +Models in other formats `ggml`, `.safetensors`, etc. won't work without prior conversion to `gguf` file format with the solution described below! + +Which open model to use? +================== + +Your best choice would be: + +- CodeLlama 70B +- Mixtral 8x7B + +We are still testing this part, but the larger the model you can run the better. Sure the responses might be slower in terms of (token/s), but code quality will be higher. + +For testing that the open LLM `gpte` setup works we recommend starting with a smaller model. You can download weights of [CodeLlama-13B-GGUF by the `TheBloke`](https://huggingface.co./TheBloke/CodeLlama-13B-GGUF) choose the largest model version you can run (for example `Q6_K`), since quantisation will degrade LLM performance. + +Feel free to try out larger models on your hardware and see what happens. + +Running the Example +================== + +To see that your setup works check [test open LLM setup](examples/test_open_llm/README.md). + +If above tests work proceed 😉 + +For checking that `gpte` works with the `CodeLLama` we recommend for you to create a project with `prompt` file content: + +``` +Write a python script that sums up two numbers. Provide only the `sum_two_numbers` function and nothing else. + +Provide two tests: + +assert(sum_two_numbers(100, 10) == 110) +assert(sum_two_numbers(10.1, 10) == 20.1) +``` + +Now run the LLM in separate terminal: + +```bash +python -m llama_cpp.server --model $model_path --n_batch 256 --n_gpu_layers 30 +``` + +Then in another terminal window set the following environment variables: + +```bash +export OPENAI_API_BASE="http://localhost:8000/v1" +export OPENAI_API_KEY="sk-xxx" +export MODEL_NAME="CodeLLama" +export LOCAL_MODEL=true +``` + +And run `gpt-engineer` with the following command: + +```bash +gpte $MODEL_NAME --lite --temperature 0.1 +``` + +The `--lite` mode is needed for now since open models for some reason behave worse with too many instructions at the moment. Temperature is set to `0.1` to get consistent best possible results. + +That's it. + +*If sth. doesn't work as expected, or you figure out how to improve the open LLM support please let us know.* + +Using Open Router models +================== + +In case you don't posses the hardware to run local LLM's yourself you can use the hosting on [Open Router](https://openrouter.ai) and pay as you go for the tokens. + +To set it up you need to Sign In and load purchase 💰 the LLM credits. Pricing per token is different for (each model](https://openrouter.ai/models), but mostly cheaper then Open AI. + +Then create the API key. + +To for example use [Meta: Llama 3 8B Instruct (extended)](https://openrouter.ai/models/meta-llama/llama-3-8b-instruct:extended) with `gpte` we need to set: + +```bash +export OPENAI_API_BASE="https://openrouter.ai/api/v1" +export OPENAI_API_KEY="sk-key-from-open-router" +export MODEL_NAME="meta-llama/llama-3-8b-instruct:extended" +export LOCAL_MODEL=true +``` + +```bash +gpte $MODEL_NAME --lite --temperature 0.1 +``` + +Using Azure models +================== + +You set your Azure OpenAI key: +- `export OPENAI_API_KEY=[your api key]` + +Then you call `gpt-engineer` with your service endpoint `--azure https://aoi-resource-name.openai.azure.com` and set your deployment name (which you created in the Azure AI Studio) as the model name (last `gpt-engineer` argument). + +Example: +`gpt-engineer --azure https://myairesource.openai.azure.com ./projects/example/ my-gpt4-project-name` diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 0000000000000000000000000000000000000000..ee264031927202e419cdcda20a033fb15ee75706 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,68 @@ +========== +Quickstart +========== + +Installation +============ + +To install LangChain run: + +.. code-block:: console + + $ python -m pip install gpt-engineer + +For more details, see our [Installation guide](/instllation.html). + +Setup API Key +============= + +Choose one of the following: + +- Export env variable (you can add this to ``.bashrc`` so that you don't have to do it each time you start the terminal) + + .. code-block:: console + + $ export OPENAI_API_KEY=[your api key] + +- Add it to the ``.env`` file: + + - Create a copy of ``.env.template`` named ``.env`` + - Add your ``OPENAI_API_KEY`` in .env + +- If you want to use a custom model, visit our docs on `using open models and azure models <./open_models.html>`_. + +- To set API key on windows check the `Windows README <./windows_readme_link.html>`_. + +Building with ``gpt-engineer`` +============================== + +Create new code (default usage) +------------------------------- + +- Create an empty folder for your project anywhere on your computer +- Create a file called ``prompt`` (no extension) inside your new folder and fill it with instructions +- Run ``gpte `` with a relative path to your folder +- For example, if you create a new project inside the gpt-engineer ``/projects`` directory: + + .. code-block:: console + + $ gpte projects/my-new-project + +Improve Existing Code +--------------------- + +- Locate a folder with code which you want to improve anywhere on your computer +- Create a file called ``prompt`` (no extension) inside your new folder and fill it with instructions for how you want to improve the code +- Run ``gpte -i`` with a relative path to your folder +- For example, if you want to run it against an existing project inside the gpt-engineer ``/projects`` directory: + + .. code-block:: console + + $ gpte projects/my-old-project -i + +By running ``gpt-engineer`` you agree to our `terms <./terms_link.html>`_. + +To **run in the browser** you can simply: + +.. image:: https://github.com/codespaces/badge.svg + :target: https://github.com/gpt-engineer-org/gpt-engineer/codespaces diff --git a/docs/roadmap_link.rst b/docs/roadmap_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..90647760eb8cf85b9849420f8254d67c3a4add48 --- /dev/null +++ b/docs/roadmap_link.rst @@ -0,0 +1,2 @@ +.. include:: ../ROADMAP.md + :parser: myst_parser.sphinx_ diff --git a/docs/terms_link.rst b/docs/terms_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..9738880560a33391acfc582b3a7d2f5bb7af95ab --- /dev/null +++ b/docs/terms_link.rst @@ -0,0 +1,2 @@ +.. include:: ../TERMS_OF_USE.md + :parser: myst_parser.sphinx_ diff --git a/docs/tracing_debugging.md b/docs/tracing_debugging.md new file mode 100644 index 0000000000000000000000000000000000000000..0f443925fabeb307602bfbce7b2610baa4281beb --- /dev/null +++ b/docs/tracing_debugging.md @@ -0,0 +1,21 @@ +Tracing and Debugging with Weights and Biases +============================ + +## How to store results in Weights & Biases + +W&B Prompts is a suite of LLMOps tools built for the development of LLM-powered applications. Use W&B Prompts to visualize and inspect the execution flow of your LLMs, analyze the inputs and outputs of your LLMs, view the intermediate results and securely store and manage your prompts and LLM chain configurations. Read more at https://docs.wandb.ai/guides/prompts + +```shell + $ export WANDB_API_KEY="YOUR-KEY" + $ export LANGCHAIN_WANDB_TRACING=true + ``` + +Sign up for free at https://wandb.ai + + +Debug and trace the execution of the AI generated code to compare across different experiments with `gpt-engineer` and related prompts +![](https://drive.google.com/uc?id=10wuLwyPbH00CoESsS2Q2q6mkdrtS91jd) + + +Automatically capture and save terminal `stdout` to one easily accessible and shareable webpage +![](https://drive.google.com/uc?id=1gVva7ZfpwbTSBsnNvId6iq09Gw5ETOks) diff --git a/docs/windows_readme_link.rst b/docs/windows_readme_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..b0427e4d185a88fee7545ff2d92f2d23ff310cc2 --- /dev/null +++ b/docs/windows_readme_link.rst @@ -0,0 +1,2 @@ +.. include:: ../WINDOWS_README.md + :parser: myst_parser.sphinx_ diff --git a/gpt_engineer/__init__.py b/gpt_engineer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..518f9f8cc0a779f1db5fa5baa6446561b39aff39 --- /dev/null +++ b/gpt_engineer/__init__.py @@ -0,0 +1,4 @@ +# Adding convenience imports to the package + +# from gpt_engineer.tools import code_vector_repository +# from gpt_engineer.core.default import on_disk_repository diff --git a/gpt_engineer/applications/__init__.py b/gpt_engineer/applications/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt_engineer/applications/cli/__init__.py b/gpt_engineer/applications/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt_engineer/applications/cli/cli_agent.py b/gpt_engineer/applications/cli/cli_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..85933378f3a4a3b86f98816335778fee07219672 --- /dev/null +++ b/gpt_engineer/applications/cli/cli_agent.py @@ -0,0 +1,225 @@ +""" +This module provides the CliAgent class which manages the lifecycle of code generation and improvement +using an AI model. It includes functionalities to initialize code generation, improve existing code, +and process the code through various steps defined in the step bundle. +""" + +from typing import Callable, Optional, TypeVar + +# from gpt_engineer.core.default.git_version_manager import GitVersionManager +from gpt_engineer.core.ai import AI +from gpt_engineer.core.base_agent import BaseAgent +from gpt_engineer.core.base_execution_env import BaseExecutionEnv +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.paths import PREPROMPTS_PATH +from gpt_engineer.core.default.steps import ( + execute_entrypoint, + gen_code, + gen_entrypoint, + improve_fn, +) +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.core.prompt import Prompt + +CodeGenType = TypeVar("CodeGenType", bound=Callable[[AI, str, BaseMemory], FilesDict]) +CodeProcessor = TypeVar( + "CodeProcessor", bound=Callable[[AI, BaseExecutionEnv, FilesDict], FilesDict] +) +ImproveType = TypeVar( + "ImproveType", bound=Callable[[AI, str, FilesDict, BaseMemory], FilesDict] +) + + +class CliAgent(BaseAgent): + """ + The `CliAgent` class is responsible for managing the lifecycle of code generation and improvement + using an AI model. It orchestrates the generation of new code and the improvement of existing code + based on given prompts and utilizes a memory system and execution environment for processing. + + Parameters + ---------- + memory : BaseMemory + An instance of a class that adheres to the BaseMemory interface, used for storing and retrieving + information during the code generation process. + execution_env : BaseExecutionEnv + An instance of a class that adheres to the BaseExecutionEnv interface, used for executing code + and managing the execution environment. + ai : AI, optional + An instance of the AI class that manages calls to the language model. If not provided, a default + instance is created. + code_gen_fn : CodeGenType, optional + A callable that takes an AI instance, a prompt, and a memory instance to generate code. Defaults + to the `gen_code` function. + improve_fn : ImproveType, optional + A callable that takes an AI instance, a prompt, a FilesDict instance, and a memory instance to + improve code. Defaults to the `improve` function. + process_code_fn : CodeProcessor, optional + A callable that takes an AI instance, an execution environment, and a FilesDict instance to + process code. Defaults to the `execute_entrypoint` function. + preprompts_holder : PrepromptsHolder, optional + An instance of PrepromptsHolder that manages preprompt templates. If not provided, a default + instance is created using the PREPROMPTS_PATH. + + Attributes + ---------- + memory : BaseMemory + The memory instance where the agent stores and retrieves information. + execution_env : BaseExecutionEnv + The execution environment instance where the agent executes and manages code. + ai : AI + The AI instance used for interacting with the language model. + code_gen_fn : CodeGenType + The function used for generating code. + improve_fn : ImproveType + The function used for improving code. + process_code_fn : CodeProcessor + The function used for processing code. + preprompts_holder : PrepromptsHolder + The holder for preprompt templates. + """ + + def __init__( + self, + memory: BaseMemory, + execution_env: BaseExecutionEnv, + ai: AI = None, + code_gen_fn: CodeGenType = gen_code, + improve_fn: ImproveType = improve_fn, + process_code_fn: CodeProcessor = execute_entrypoint, + preprompts_holder: PrepromptsHolder = None, + ): + self.memory = memory + self.execution_env = execution_env + self.ai = ai or AI() + self.code_gen_fn = code_gen_fn + self.process_code_fn = process_code_fn + self.improve_fn = improve_fn + self.preprompts_holder = preprompts_holder or PrepromptsHolder(PREPROMPTS_PATH) + + @classmethod + def with_default_config( + cls, + memory: DiskMemory, + execution_env: DiskExecutionEnv, + ai: AI = None, + code_gen_fn: CodeGenType = gen_code, + improve_fn: ImproveType = improve_fn, + process_code_fn: CodeProcessor = execute_entrypoint, + preprompts_holder: PrepromptsHolder = None, + ): + """ + Creates a new instance of CliAgent with default configurations for memory, execution environment, + AI, and other functional parameters. + + Parameters + ---------- + memory : DiskMemory + An instance of DiskMemory for storing and retrieving information. + execution_env : DiskExecutionEnv + An instance of DiskExecutionEnv for executing code. + ai : AI, optional + An instance of AI for interacting with the language model. Defaults to None, which will create + a new AI instance. + code_gen_fn : CodeGenType, optional + A function for generating code. Defaults to `gen_code`. + improve_fn : ImproveType, optional + A function for improving code. Defaults to `improve`. + process_code_fn : CodeProcessor, optional + A function for processing code. Defaults to `execute_entrypoint`. + preprompts_holder : PrepromptsHolder, optional + An instance of PrepromptsHolder for managing preprompt templates. Defaults to None, which will + create a new PrepromptsHolder instance using PREPROMPTS_PATH. + + Returns + ------- + CliAgent + An instance of CliAgent configured with the provided or default parameters. + """ + return cls( + memory=memory, + execution_env=execution_env, + ai=ai, + code_gen_fn=code_gen_fn, + process_code_fn=process_code_fn, + improve_fn=improve_fn, + preprompts_holder=preprompts_holder or PrepromptsHolder(PREPROMPTS_PATH), + ) + + def init(self, prompt: Prompt) -> FilesDict: + """ + Generates a new piece of code using the AI and step bundle based on the provided prompt. + + Parameters + ---------- + prompt : str + A string prompt that guides the code generation process. + + Returns + ------- + FilesDict + An instance of the `FilesDict` class containing the generated code. + """ + + files_dict = self.code_gen_fn( + self.ai, prompt, self.memory, self.preprompts_holder + ) + entrypoint = gen_entrypoint( + self.ai, prompt, files_dict, self.memory, self.preprompts_holder + ) + combined_dict = {**files_dict, **entrypoint} + files_dict = FilesDict(combined_dict) + files_dict = self.process_code_fn( + self.ai, + self.execution_env, + files_dict, + preprompts_holder=self.preprompts_holder, + prompt=prompt, + memory=self.memory, + ) + return files_dict + + def improve( + self, + files_dict: FilesDict, + prompt: Prompt, + execution_command: Optional[str] = None, + ) -> FilesDict: + """ + Improves an existing piece of code using the AI and step bundle based on the provided prompt. + + Parameters + ---------- + files_dict : FilesDict + An instance of `FilesDict` containing the code to be improved. + prompt : str + A string prompt that guides the code improvement process. + execution_command : str, optional + An optional command to execute the code. If not provided, the default execution command is used. + + Returns + ------- + FilesDict + An instance of the `FilesDict` class containing the improved code. + """ + + files_dict = self.improve_fn( + self.ai, prompt, files_dict, self.memory, self.preprompts_holder + ) + # entrypoint = gen_entrypoint( + # self.ai, prompt, files_dict, self.memory, self.preprompts_holder + # ) + # combined_dict = {**files_dict, **entrypoint} + # files_dict = FilesDict(combined_dict) + # files_dict = self.process_code_fn( + # self.ai, + # self.execution_env, + # files_dict, + # preprompts_holder=self.preprompts_holder, + # prompt=prompt, + # memory=self.memory, + # ) + + return files_dict diff --git a/gpt_engineer/applications/cli/collect.py b/gpt_engineer/applications/cli/collect.py new file mode 100644 index 0000000000000000000000000000000000000000..2f02774419ac9ec776ac9bca64277070444e7455 --- /dev/null +++ b/gpt_engineer/applications/cli/collect.py @@ -0,0 +1,177 @@ +""" +Module `collect` - Data Handling and RudderStack Integration + +This module provides functionalities to handle and send learning data to RudderStack +for the purpose of analysis and to improve the gpt-engineer system. The data is sent +only when the user gives consent to share. + +Functions: + send_learning(learning): Sends learning data to RudderStack. + collect_learnings(prompt, model, temperature, config, memory, review): Processes and sends learning data. + collect_and_send_human_review(prompt, model, temperature, config, memory): Collects human feedback and sends it. + +Dependencies: + hashlib: For generating SHA-256 hash. + typing: For type annotations. + gpt_engineer.core: Core functionalities of gpt-engineer. + gpt_engineer.cli.learning: Handles the extraction of learning data. + +Notes: + Data sent to RudderStack is not shared with third parties and is used solely to + improve gpt-engineer and allow it to handle a broader range of use cases. + Consent logic is in gpt_engineer/learning.py. +""" + +from typing import Tuple + +from gpt_engineer.applications.cli.learning import ( + Learning, + Review, + extract_learning, + human_review_input, +) +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.prompt import Prompt + + +def send_learning(learning: Learning): + """ + Send the learning data to RudderStack for analysis. + + Parameters + ---------- + learning : Learning + An instance of the Learning class containing the data to be sent. + + Notes + ----- + This function is only called if consent is given to share data. + Data is not shared to a third party. It is used with the sole purpose of + improving gpt-engineer, and letting it handle more use cases. + Consent logic is in gpt_engineer/learning.py. + """ + import rudderstack.analytics as rudder_analytics + + rudder_analytics.write_key = "2Re4kqwL61GDp7S8ewe6K5dbogG" + rudder_analytics.dataPlaneUrl = "https://gptengineerezm.dataplane.rudderstack.com" + + rudder_analytics.track( + user_id=learning.session, + event="learning", + properties=learning.to_dict(), # type: ignore + ) + + +def collect_learnings( + prompt: Prompt, + model: str, + temperature: float, + config: any, + memory: DiskMemory, + review: Review, +): + """ + Collect the learning data and send it to RudderStack for analysis. + + Parameters + ---------- + prompt : str + The initial prompt or question that was provided to the model. + model : str + The name of the model used for generating the response. + temperature : float + The temperature setting used in the model's response generation. + config : any + Configuration parameters used for the learning session. + memory : DiskMemory + An instance of DiskMemory for storing and retrieving data. + review : Review + An instance of Review containing human feedback on the model's response. + + Notes + ----- + This function attempts to send the learning data to RudderStack. If the data size exceeds + the maximum allowed size, it trims the data and retries sending it. + """ + learnings = extract_learning(prompt, model, temperature, config, memory, review) + try: + send_learning(learnings) + except RuntimeError: + # try to remove some parts of learning that might be too big + # rudderstack max event size is 32kb + max_size = 32 << 10 # 32KB in bytes + current_size = len(learnings.to_json().encode("utf-8")) # get size in bytes + + overflow = current_size - max_size + + # Add some extra characters for the "[REMOVED...]" string and for safety margin + remove_length = overflow + len(f"[REMOVED {overflow} CHARACTERS]") + 100 + + learnings.logs = ( + learnings.logs[:-remove_length] + + f"\n\n[REMOVED {remove_length} CHARACTERS]" + ) + + print( + "WARNING: learning too big, removing some parts. " + "Please report if this results in a crash." + ) + try: + send_learning(learnings) + except RuntimeError: + print( + "Sending learnings crashed despite truncation. Progressing without saving learnings." + ) + + +# def steps_file_hash(): +# """ +# Compute the SHA-256 hash of the steps file. +# +# Returns +# ------- +# str +# The SHA-256 hash of the steps file. +# """ +# with open(steps.__file__, "r") as f: +# content = f.read() +# return hashlib.sha256(content.encode("utf-8")).hexdigest() + + +def collect_and_send_human_review( + prompt: Prompt, + model: str, + temperature: float, + config: Tuple[str, ...], + memory: DiskMemory, +): + """ + Collects human feedback on the code and sends it for analysis. + + Parameters + ---------- + prompt : str + The initial prompt or question that was provided to the model. + model : str + The name of the model used for generating the response. + temperature : float + The temperature setting used in the model's response generation. + config : Tuple[str, ...] + Configuration parameters used for the learning session. + memory : DiskMemory + An instance of DiskMemory for storing and retrieving data. + + Returns + ------- + None + + Notes + ----- + This function prompts the user for a review of the generated or improved code using the + `human_review_input` function. If a valid review is provided, it's serialized to JSON format + and stored within the database's memory under the "review" key. + """ + + review = human_review_input() + if review: + collect_learnings(prompt, model, temperature, config, memory, review) diff --git a/gpt_engineer/applications/cli/file_selector.py b/gpt_engineer/applications/cli/file_selector.py new file mode 100644 index 0000000000000000000000000000000000000000..a80608620c4f261fb17750a65605a88c5dc0ff37 --- /dev/null +++ b/gpt_engineer/applications/cli/file_selector.py @@ -0,0 +1,539 @@ +""" +file_selector.py + +This module offers interactive file selection for projects. Leveraging a terminal-based, +tree-structured display, users can navigate and select files for editing or processing. +It integrates with system editors for direct file modification and supports saving +selections for later use. Designed for efficient workflow enhancement in file-intensive +environments, it offers customizable file filtering and seamless editor integration. + +Key Components: +- FileSelector: Manages file selection and interaction. +- DisplayablePath: Provides a structured view of file paths. + +Usage: +Typically used in project setup or management phases for selecting specific files. +It operates within the GPT-Engineer environment, relying on core functionalities for +file handling and persistence. +""" + +import fnmatch +import os +import subprocess + +from pathlib import Path +from typing import Any, Dict, Generator, List, Union + +import toml + +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.paths import metadata_path +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.git import filter_by_gitignore, is_git_repo + + +class FileSelector: + """ + Manages file selection and interaction within a project directory. + + This class provides methods to interactively select files from the terminal, + save selections for later use, and integrate with system editors for direct + file modification. + + Attributes + ---------- + IGNORE_FOLDERS : set + A set of directory names to ignore during file selection. + FILE_LIST_NAME : str + The name of the file that stores the selected files list. + COMMENT : str + The comment string to be added to the top of the file selection list. + """ + + IGNORE_FOLDERS = {"site-packages", "node_modules", "venv", "__pycache__"} + FILE_LIST_NAME = "file_selection.toml" + COMMENT = ( + "# Remove '#' to select a file or turn off linting.\n\n" + "# Linting with BLACK (Python) enhances code suggestions from LLMs. " + "To disable linting, uncomment the relevant option in the linting settings.\n\n" + "# gpt-engineer can only read selected files. " + "Including irrelevant files will degrade performance, " + "cost additional tokens and potentially overflow token limit.\n\n" + ) + LINTING_STRING = '[linting]\n# "linting" = "off"\n\n' + is_linting = True + + def __init__(self, project_path: Union[str, Path]): + """ + Initializes the FileSelector with a given project path. + + Parameters + ---------- + project_path : Union[str, Path] + The path to the project directory where file selection is to be performed. + """ + self.project_path = project_path + self.metadata_db = DiskMemory(metadata_path(self.project_path)) + self.toml_path = self.metadata_db.path / self.FILE_LIST_NAME + + def ask_for_files(self) -> tuple[FilesDict, bool]: + """ + Prompts the user to select files for context improvement. + + This method supports selection from the terminal or using a previously saved list. + In test mode, it retrieves files from a predefined TOML configuration. + + Returns + ------- + FilesDict + A dictionary with file paths as keys and file contents as values. + """ + + if os.getenv("GPTE_TEST_MODE"): + # In test mode, retrieve files from a predefined TOML configuration + assert self.FILE_LIST_NAME in self.metadata_db + selected_files = self.get_files_from_toml(self.project_path, self.toml_path) + else: + # Otherwise, use the editor file selector for interactive selection + if self.FILE_LIST_NAME in self.metadata_db: + print( + f"File list detected at {self.toml_path}. Edit or delete it if you want to select new files." + ) + selected_files = self.editor_file_selector(self.project_path, False) + else: + selected_files = self.editor_file_selector(self.project_path, True) + + content_dict = {} + for file_path in selected_files: + # selected files contains paths that are relative to the project path + try: + # to open the file we need the path from the cwd + with open( + Path(self.project_path) / file_path, "r", encoding="utf-8" + ) as content: + content_dict[str(file_path)] = content.read() + except FileNotFoundError: + print(f"Warning: File not found {file_path}") + except UnicodeDecodeError: + print(f"Warning: File not UTF-8 encoded {file_path}, skipping") + + return FilesDict(content_dict), self.is_linting + + def editor_file_selector( + self, input_path: Union[str, Path], init: bool = True + ) -> List[str]: + """ + Provides an interactive file selection interface using a .toml file. + + Parameters + ---------- + input_path : Union[str, Path] + The path where file selection is to be performed. + init : bool, optional + Indicates whether to initialize the .toml file with the file tree. + + Returns + ------- + List[str] + A list of strings representing the paths of selected files. + """ + + root_path = Path(input_path) + tree_dict = {} + toml_file = DiskMemory(metadata_path(input_path)).path / "file_selection.toml" + # Define the toml file path + + # Initialize .toml file with file tree if in initial state + if init: + tree_dict = {x: "selected" for x in self.get_current_files(root_path)} + + s = toml.dumps({"files": tree_dict}) + + # add comments on all lines that match = "selected" + s = "\n".join( + [ + "# " + line if line.endswith(' = "selected"') else line + for line in s.split("\n") + ] + ) + # Write to the toml file + with open(toml_file, "w") as f: + f.write(self.COMMENT) + f.write(self.LINTING_STRING) + f.write(s) + + else: + # Load existing files from the .toml configuration + all_files = self.get_current_files(root_path) + s = toml.dumps({"files": {x: "selected" for x in all_files}}) + + # get linting status from the toml file + with open(toml_file, "r") as file: + linting_status = toml.load(file) + if ( + "linting" in linting_status + and linting_status["linting"].get("linting", "").lower() == "off" + ): + self.is_linting = False + self.LINTING_STRING = '[linting]\n"linting" = "off"\n\n' + print("\nLinting is disabled") + + with open(toml_file, "r") as file: + selected_files = toml.load(file) + + lines = s.split("\n") + s = "\n".join( + lines[:1] + + [ + line + if line.split(" = ")[0].strip('"') in selected_files["files"] + else "# " + line + for line in lines[1:] + ] + ) + + # Write the merged list back to the .toml for user review and modification + with open(toml_file, "w") as file: + file.write(self.COMMENT) # Ensure to write the comment + file.write(self.LINTING_STRING) + file.write(s) + + print( + "Please select and deselect (add # in front) files, save it, and close it to continue..." + ) + self.open_with_default_editor( + toml_file + ) # Open the .toml file in the default editor for user modification + return self.get_files_from_toml( + input_path, toml_file + ) # Return the list of selected files after user edits + + def open_with_default_editor(self, file_path: Union[str, Path]): + """ + Opens a file with the system's default text editor. + + Parameters + ---------- + file_path : Union[str, Path] + The path to the file to be opened in the text editor. + """ + + editors = [ + "gedit", + "notepad", + "nvim", + "write", + "nano", + "vim", + "emacs", + ] # Putting the beginner-friendly text editor forward + chosen_editor = os.environ.get("EDITOR") + + # Try the preferred editor first, then fallback to common editors + if chosen_editor: + try: + subprocess.run([chosen_editor, file_path]) + return + except Exception: + pass + + for editor in editors: + try: + subprocess.run([editor, file_path]) + return + except Exception: + continue + print("No suitable text editor found. Please edit the file manually.") + + def is_utf8(self, file_path: Union[str, Path]) -> bool: + """ + Checks if the file at the given path is UTF-8 encoded. + + Parameters + ---------- + file_path : Union[str, Path] + The path to the file to be checked. + + Returns + ------- + bool + True if the file is UTF-8 encoded, False otherwise. + """ + + try: + with open(file_path, "rb") as file: + file.read().decode("utf-8") + return True + except UnicodeDecodeError: + return False + + def get_files_from_toml( + self, input_path: Union[str, Path], toml_file: Union[str, Path] + ) -> List[str]: + """ + Retrieves a list of selected files from a .toml configuration file. + + Parameters + ---------- + input_path : Union[str, Path] + The path where file selection was performed. + toml_file : Union[str, Path] + The path to the .toml file containing the file selection. + + Returns + ------- + List[str] + A list of strings representing the paths of selected files. + + Raises + ------ + Exception + If no files are selected in the .toml file. + """ + selected_files = [] + edited_tree = toml.load(toml_file) # Load the edited .toml file + + # check if users have disabled linting or not + if ( + "linting" in edited_tree + and edited_tree["linting"].get("linting", "").lower() == "off" + ): + self.is_linting = False + print("\nLinting is disabled") + else: + self.is_linting = True + + # Iterate through the files in the .toml and append selected files to the list + for file, _ in edited_tree["files"].items(): + selected_files.append(file) + + # Ensure that at least one file is selected, or raise an exception + if not selected_files: + raise Exception( + "No files were selected. Please select at least one file to proceed." + ) + + print(f"\nYou have selected the following files:\n{input_path}") + + project_path = Path(input_path).resolve() + selected_paths = set( + project_path.joinpath(file).resolve(strict=False) for file in selected_files + ) + + for displayable_path in DisplayablePath.make_tree(project_path): + if displayable_path.path in selected_paths: + p = displayable_path + while p.parent and p.parent.path not in selected_paths: + selected_paths.add(p.parent.path) + p = p.parent + + try: + for displayable_path in DisplayablePath.make_tree(project_path): + if displayable_path.path in selected_paths: + print(displayable_path.displayable()) + + except FileNotFoundError: + print("Specified path does not exist: ", project_path) + except Exception as e: + print("An error occurred while trying to display the file tree:", e) + + print("\n") + return selected_files + + def merge_file_lists( + self, existing_files: Dict[str, Any], new_files: Dict[str, Any] + ) -> Dict[str, Any]: + """ + Merges two lists of files, preserving the selection status. + + Parameters + ---------- + existing_files : Dict[str, Any] + The dictionary of existing files with their properties. + new_files : Dict[str, Any] + The dictionary of new files with their properties. + + Returns + ------- + Dict[str, Any] + The updated dictionary of files after merging. + """ + # Update the existing files with any new files or changes + for file, properties in new_files.items(): + if file not in existing_files: + existing_files[file] = properties # Add new files as unselected + # If you want to update other properties of existing files, you can do so here + + return existing_files + + def should_filter_file(self, file_path: Path, filters: List[str]) -> bool: + """ + Determines if a file should be ignored based on .gitignore rules. + """ + for f in filters: + if fnmatch.fnmatchcase(str(file_path), f): + return True + return False + + def get_current_files(self, project_path: Union[str, Path]) -> List[str]: + """ + Generates a list of all files in the project directory. Will use .gitignore files if project_path is a git repository. + + Parameters + ---------- + project_path : Union[str, Path] + The path to the project directory. + + Returns + ------- + List[str] + A list of strings representing the relative paths of all files in the project directory. + """ + all_files = [] + project_path = Path( + project_path + ).resolve() # Ensure path is absolute and resolved + + file_list = project_path.glob("**/*") + + for path in file_list: # Recursively list all files + if path.is_file(): + relpath = path.relative_to(project_path) + parts = relpath.parts + if any(part.startswith(".") for part in parts): + continue # Skip hidden files + if any(part in self.IGNORE_FOLDERS for part in parts): + continue + if relpath.name == "prompt": + continue # Skip files named 'prompt' + + all_files.append(str(relpath)) + + if is_git_repo(project_path) and "projects" not in project_path.parts: + all_files = filter_by_gitignore(project_path, all_files) + + return all_files + + +class DisplayablePath(object): + """ + Represents and displays a file system path in a tree-like structure. + + This class is used to visually represent the structure of directories and files + in a way that is similar to a file explorer's tree view. + """ + + display_filename_prefix_middle = "├── " + display_filename_prefix_last = "└── " + display_parent_prefix_middle = " " + display_parent_prefix_last = "│ " + + def __init__( + self, path: Union[str, Path], parent_path: "DisplayablePath", is_last: bool + ): + """ + Initializes a DisplayablePath object with a given path and parent. + + Parameters + ---------- + path : Union[str, Path] + The file system path to be displayed. + parent_path : DisplayablePath + The parent path in the tree structure. + is_last : bool + Indicates whether this is the last sibling in the tree structure. + """ + self.depth = 0 + self.path = Path(str(path)) + self.parent = parent_path + self.is_last = is_last + if self.parent: + self.depth = self.parent.depth + 1 # Increment depth if it has a parent + + @property + def display_name(self) -> str: + """ + Get the display name of the file or directory. + """ + if self.path.is_dir(): + return self.path.name + "/" + return self.path.name + + @classmethod + def make_tree( + cls, root: Union[str, Path], parent=None, is_last=False, criteria=None + ) -> Generator["DisplayablePath", None, None]: + """ + Creates a tree of DisplayablePath objects from a root directory. + + Parameters + ---------- + root : Union[str, Path] + The root directory from which to start creating the tree. + parent : DisplayablePath, optional + The parent path in the tree structure. + is_last : bool, optional + Indicates whether this is the last sibling in the tree structure. + criteria : callable, optional + A function to filter the paths included in the tree. + + Yields + ------ + DisplayablePath + The next DisplayablePath object in the tree. + """ + root = Path(str(root)) # Ensure root is a Path object + criteria = criteria or cls._default_criteria + displayable_root = cls(root, parent, is_last) + yield displayable_root + + if root.is_dir(): # Check if root is a directory before iterating + children = sorted( + list(path for path in root.iterdir() if criteria(path)), + key=lambda s: str(s).lower(), + ) + count = 1 + for path in children: + is_last = count == len(children) + yield from cls.make_tree( + path, parent=displayable_root, is_last=is_last, criteria=criteria + ) + count += 1 + + @classmethod + def _default_criteria(cls, path: Path) -> bool: + """ + The default criteria function to filter the paths. + """ + return True + + def displayable(self) -> str: + """ + Returns a string representation of the path for display in a tree-like structure. + + Returns + ------- + str + The displayable string representation of the file or directory. + """ + if self.parent is None: + return self.display_name + + _filename_prefix = ( + self.display_filename_prefix_last + if self.is_last + else self.display_filename_prefix_middle + ) + + parts = ["{!s} {!s}".format(_filename_prefix, self.display_name)] + + parent = self.parent + while parent and parent.parent is not None: + parts.append( + self.display_parent_prefix_middle + if parent.is_last + else self.display_parent_prefix_last + ) + parent = parent.parent + + return "".join(reversed(parts)) # Assemble the parts into the final string diff --git a/gpt_engineer/applications/cli/learning.py b/gpt_engineer/applications/cli/learning.py new file mode 100644 index 0000000000000000000000000000000000000000..d9a7cbc8aa37175e387bcb5a4965da90c21438d9 --- /dev/null +++ b/gpt_engineer/applications/cli/learning.py @@ -0,0 +1,301 @@ +""" +The `learning` module is designed to facilitate the collection and storage of user feedback on the outputs generated by the GPT Engineer tool. It provides mechanisms for obtaining user consent, capturing user reviews, and storing this information for future analysis and enhancement of the tool's performance. + +Classes +------- +Review : dataclass + Represents a user's review of the generated code, including whether it ran, was perfect, was useful, and any additional comments. +Learning : dataclass + Encapsulates the metadata and feedback collected during a session of using the GPT Engineer tool, including the prompt, model, temperature, configuration, logs, session identifier, user review, and timestamp. + +Functions +--------- +human_review_input() -> Optional[Review] + Interactively gathers feedback from the user regarding the performance of generated code and returns a Review instance. +check_collection_consent() -> bool + Checks if the user has previously given consent to store their data and, if not, asks for it. +ask_collection_consent() -> bool + Prompts the user for consent to store their data for the purpose of improving GPT Engineer. +extract_learning(prompt: Prompt, model: str, temperature: float, config: Tuple[str, ...], memory: DiskMemory, review: Review) -> Learning + Extracts feedback and session details to create a Learning instance based on the provided parameters. +get_session() -> str + Retrieves a unique identifier for the current user session, creating one if it does not exist. + +Constants +--------- +TERM_CHOICES : tuple + Terminal color choices for user interactive prompts, formatted with termcolor for readability. +""" + +import json +import random +import tempfile + +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path +from typing import Optional, Tuple + +from dataclasses_json import dataclass_json +from termcolor import colored + +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.prompt import Prompt + + +@dataclass_json +@dataclass +class Review: + """ + A dataclass that represents a user's review of the generated code. + + Attributes + ---------- + ran : Optional[bool] + Indicates whether the generated code ran without errors. + perfect : Optional[bool] + Indicates whether the generated code met all the user's requirements. + works : Optional[bool] + Indicates whether the generated code was useful, even if not perfect. + comments : str + Any additional comments provided by the user. + raw : str + A raw string representation of the user's responses. + """ + + ran: Optional[bool] + perfect: Optional[bool] + works: Optional[bool] + comments: str + raw: str + + +@dataclass_json +@dataclass +class Learning: + """ + A dataclass that encapsulates the learning data collected during a GPT Engineer session. + + Attributes + ---------- + prompt : str + A JSON string representing the prompt provided to GPT Engineer. + model : str + The name of the model used during the session. + temperature : float + The temperature setting used for the model's responses. + config : str + A JSON string representing the configuration settings for the session. + logs : str + A JSON string representing the logs of the session. + session : str + A unique identifier for the user session. + review : Optional[Review] + The user's review of the generated code. + timestamp : str + The UTC timestamp when the learning data was created. + version : str + The version of the learning data schema. + """ + + prompt: str + model: str + temperature: float + config: str + logs: str + session: str + review: Optional[Review] + timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat()) + version: str = "0.3" + + +TERM_CHOICES = ( + colored("y", "green") + + "/" + + colored("n", "red") + + "/" + + colored("u", "yellow") + + "(ncertain): " +) + + +def human_review_input() -> Optional[Review]: + """ + Interactively prompts the user to review the generated code and returns their feedback encapsulated in a Review object. + + This function will first check if the user has given consent to collect their feedback. If consent is given, it will ask the user a series of questions about the generated code's performance and capture their responses. + + Returns + ------- + Optional[Review] + A Review object containing the user's feedback, or None if consent is not given. + """ + print() + if not check_collection_consent(): + return None + print() + print( + colored("To help gpt-engineer learn, please answer 3 questions:", "light_green") + ) + print() + + ran = input("Did the generated code run at all? " + TERM_CHOICES) + ran = ask_for_valid_input(ran) + + if ran == "y": + perfect = input( + "Did the generated code do everything you wanted? " + TERM_CHOICES + ) + perfect = ask_for_valid_input(perfect) + + if perfect != "y": + useful = input("Did the generated code do anything useful? " + TERM_CHOICES) + useful = ask_for_valid_input(useful) + else: + useful = "" + else: + perfect = "" + useful = "" + + if perfect != "y": + comments = input( + "If you have time, please explain what was not working " + + colored("(ok to leave blank)\n", "light_green") + ) + else: + comments = "" + + return Review( + raw=", ".join([ran, perfect, useful]), + ran={"y": True, "n": False, "u": None, "": None}[ran], + works={"y": True, "n": False, "u": None, "": None}[useful], + perfect={"y": True, "n": False, "u": None, "": None}[perfect], + comments=comments, + ) + + +def ask_for_valid_input(ran): + while ran not in ("y", "n", "u"): + ran = input("Invalid input. Please enter y, n, or u: ") + return ran + + +def check_collection_consent() -> bool: + """ + Checks if the user has previously given consent to store their data for feedback collection. + + This function looks for a file that stores the user's consent status. If the file exists and contains 'true', consent is assumed. If the file does not exist or does not contain 'true', the function will prompt the user for consent. + + Returns + ------- + bool + True if the user has given consent, False otherwise. + """ + path = Path(".gpte_consent") + if path.exists() and path.read_text() == "true": + return True + else: + return ask_collection_consent() + + +def ask_collection_consent() -> bool: + """ + Asks the user for their consent to store their data for the purpose of improving the GPT Engineer tool. + + The user's response is recorded in a file for future reference. If the user consents, the function will write 'true' to the file. If the user does not consent, no data will be collected, and the function will not modify the file. + + Returns + ------- + bool + True if the user consents, False otherwise. + """ + answer = input( + "Is it ok if we store your prompts to help improve GPT Engineer? (y/n)" + ) + while answer.lower() not in ("y", "n"): + answer = input("Invalid input. Please enter y or n: ") + + if answer.lower() == "y": + path = Path(".gpte_consent") + path.write_text("true") + print(colored("Thank you️", "light_green")) + print() + print( + "(If you no longer wish to participate in data collection, delete the file .gpte_consent)" + ) + return True + else: + print( + colored( + "No worries! GPT Engineer will not collect your prompts. ❤️", + "light_green", + ) + ) + return False + + +def extract_learning( + prompt: Prompt, + model: str, + temperature: float, + config: Tuple[str, ...], + memory: DiskMemory, + review: Review, +) -> Learning: + """ + Constructs a Learning object containing the session's metadata and user feedback. + + Parameters + ---------- + prompt : str + The initial prompt provided to the GPT Engineer. + model : str + The name of the model used during the session. + temperature : float + The temperature setting used for the model's responses. + config : Tuple[str, ...] + A tuple representing the configuration settings for the session. + memory : DiskMemory + An object representing the disk memory used during the session. + review : Review + The user's review of the generated code. + + Returns + ------- + Learning + An instance of Learning containing all the session details and user feedback. + """ + return Learning( + prompt=prompt.to_json(), + model=model, + temperature=temperature, + config=json.dumps(config), + session=get_session(), + logs=memory.to_json(), + review=review, + ) + + +def get_session() -> str: + """ + Retrieves or generates a unique identifier for the current user session. + + This function attempts to read a unique user ID from a temporary file. If the file does not exist, it generates a new random ID, writes it to the file, and returns it. This ID is used to uniquely identify the user's session. + + Returns + ------- + str + A unique identifier for the user session. + """ + path = Path(tempfile.gettempdir()) / "gpt_engineer_user_id.txt" + + try: + if path.exists(): + user_id = path.read_text() + else: + # random uuid: + user_id = str(random.randint(0, 2**32)) + path.write_text(user_id) + return user_id + except IOError: + return "ephemeral_" + str(random.randint(0, 2**32)) diff --git a/gpt_engineer/applications/cli/main.py b/gpt_engineer/applications/cli/main.py new file mode 100644 index 0000000000000000000000000000000000000000..51b88b10c3d383b0be4f3f6a35a9dc5ff23dbd76 --- /dev/null +++ b/gpt_engineer/applications/cli/main.py @@ -0,0 +1,544 @@ +""" +Entrypoint for the CLI tool. + +This module serves as the entry point for a command-line interface (CLI) tool. +It is designed to interact with OpenAI's language models. +The module provides functionality to: +- Load necessary environment variables, +- Configure various parameters for the AI interaction, +- Manage the generation or improvement of code projects. + +Main Functionality +------------------ +- Load environment variables required for OpenAI API interaction. +- Parse user-specified parameters for project configuration and AI behavior. +- Facilitate interaction with AI models, databases, and archival processes. + +Parameters +---------- +None + +Notes +----- +- The `OPENAI_API_KEY` must be set in the environment or provided in a `.env` file within the working directory. +- The default project path is `projects/example`. +- When using the `azure_endpoint` parameter, provide the Azure OpenAI service endpoint URL. +""" + +import difflib +import json +import logging +import os +import platform +import subprocess +import sys + +from pathlib import Path + +import openai +import typer + +from dotenv import load_dotenv +from langchain.globals import set_llm_cache +from langchain_community.cache import SQLiteCache +from termcolor import colored + +from gpt_engineer.applications.cli.cli_agent import CliAgent +from gpt_engineer.applications.cli.collect import collect_and_send_human_review +from gpt_engineer.applications.cli.file_selector import FileSelector +from gpt_engineer.core.ai import AI, ClipboardAI +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.file_store import FileStore +from gpt_engineer.core.default.paths import PREPROMPTS_PATH, memory_path +from gpt_engineer.core.default.steps import ( + execute_entrypoint, + gen_code, + handle_improve_mode, + improve_fn as improve_fn, +) +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.git import stage_uncommitted_to_git +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.core.prompt import Prompt +from gpt_engineer.tools.custom_steps import clarified_gen, lite_gen, self_heal + +app = typer.Typer( + context_settings={"help_option_names": ["-h", "--help"]} +) # creates a CLI app + + +def load_env_if_needed(): + """ + Load environment variables if the OPENAI_API_KEY is not already set. + + This function checks if the OPENAI_API_KEY environment variable is set, + and if not, it attempts to load it from a .env file in the current working + directory. It then sets the openai.api_key for use in the application. + """ + # We have all these checks for legacy reasons... + if os.getenv("OPENAI_API_KEY") is None: + load_dotenv() + if os.getenv("OPENAI_API_KEY") is None: + load_dotenv(dotenv_path=os.path.join(os.getcwd(), ".env")) + + openai.api_key = os.getenv("OPENAI_API_KEY") + + if os.getenv("ANTHROPIC_API_KEY") is None: + load_dotenv() + if os.getenv("ANTHROPIC_API_KEY") is None: + load_dotenv(dotenv_path=os.path.join(os.getcwd(), ".env")) + + +def concatenate_paths(base_path, sub_path): + # Compute the relative path from base_path to sub_path + relative_path = os.path.relpath(sub_path, base_path) + + # If the relative path is not in the parent directory, use the original sub_path + if not relative_path.startswith(".."): + return sub_path + + # Otherwise, concatenate base_path and sub_path + return os.path.normpath(os.path.join(base_path, sub_path)) + + +def load_prompt( + input_repo: DiskMemory, + improve_mode: bool, + prompt_file: str, + image_directory: str, + entrypoint_prompt_file: str = "", +) -> Prompt: + """ + Load or request a prompt from the user based on the mode. + + Parameters + ---------- + input_repo : DiskMemory + The disk memory object where prompts and other data are stored. + improve_mode : bool + Flag indicating whether the application is in improve mode. + + Returns + ------- + str + The loaded or inputted prompt. + """ + + if os.path.isdir(prompt_file): + raise ValueError( + f"The path to the prompt, {prompt_file}, already exists as a directory. No prompt can be read from it. Please specify a prompt file using --prompt_file" + ) + prompt_str = input_repo.get(prompt_file) + if prompt_str: + print(colored("Using prompt from file:", "green"), prompt_file) + print(prompt_str) + else: + if not improve_mode: + prompt_str = input( + "\nWhat application do you want gpt-engineer to generate?\n" + ) + else: + prompt_str = input("\nHow do you want to improve the application?\n") + + if entrypoint_prompt_file == "": + entrypoint_prompt = "" + else: + full_entrypoint_prompt_file = concatenate_paths( + input_repo.path, entrypoint_prompt_file + ) + if os.path.isfile(full_entrypoint_prompt_file): + entrypoint_prompt = input_repo.get(full_entrypoint_prompt_file) + + else: + raise ValueError("The provided file at --entrypoint-prompt does not exist") + + if image_directory == "": + return Prompt(prompt_str, entrypoint_prompt=entrypoint_prompt) + + full_image_directory = concatenate_paths(input_repo.path, image_directory) + if os.path.isdir(full_image_directory): + if len(os.listdir(full_image_directory)) == 0: + raise ValueError("The provided --image_directory is empty.") + image_repo = DiskMemory(full_image_directory) + return Prompt( + prompt_str, + image_repo.get(".").to_dict(), + entrypoint_prompt=entrypoint_prompt, + ) + else: + raise ValueError("The provided --image_directory is not a directory.") + + +def get_preprompts_path(use_custom_preprompts: bool, input_path: Path) -> Path: + """ + Get the path to the preprompts, using custom ones if specified. + + Parameters + ---------- + use_custom_preprompts : bool + Flag indicating whether to use custom preprompts. + input_path : Path + The path to the project directory. + + Returns + ------- + Path + The path to the directory containing the preprompts. + """ + original_preprompts_path = PREPROMPTS_PATH + if not use_custom_preprompts: + return original_preprompts_path + + custom_preprompts_path = input_path / "preprompts" + if not custom_preprompts_path.exists(): + custom_preprompts_path.mkdir() + + for file in original_preprompts_path.glob("*"): + if not (custom_preprompts_path / file.name).exists(): + (custom_preprompts_path / file.name).write_text(file.read_text()) + return custom_preprompts_path + + +def compare(f1: FilesDict, f2: FilesDict): + def colored_diff(s1, s2): + lines1 = s1.splitlines() + lines2 = s2.splitlines() + + diff = difflib.unified_diff(lines1, lines2, lineterm="") + + RED = "\033[38;5;202m" + GREEN = "\033[92m" + RESET = "\033[0m" + + colored_lines = [] + for line in diff: + if line.startswith("+"): + colored_lines.append(GREEN + line + RESET) + elif line.startswith("-"): + colored_lines.append(RED + line + RESET) + else: + colored_lines.append(line) + + return "\n".join(colored_lines) + + for file in sorted(set(f1) | set(f2)): + diff = colored_diff(f1.get(file, ""), f2.get(file, "")) + if diff: + print(f"Changes to {file}:") + print(diff) + + +def prompt_yesno() -> bool: + TERM_CHOICES = colored("y", "green") + "/" + colored("n", "red") + " " + while True: + response = input(TERM_CHOICES).strip().lower() + if response in ["y", "yes"]: + return True + if response in ["n", "no"]: + break + print("Please respond with 'y' or 'n'") + + +def get_system_info(): + system_info = { + "os": platform.system(), + "os_version": platform.version(), + "architecture": platform.machine(), + "python_version": sys.version, + "packages": format_installed_packages(get_installed_packages()), + } + return system_info + + +def get_installed_packages(): + try: + result = subprocess.run( + [sys.executable, "-m", "pip", "list", "--format=json"], + capture_output=True, + text=True, + ) + packages = json.loads(result.stdout) + return {pkg["name"]: pkg["version"] for pkg in packages} + except Exception as e: + return str(e) + + +def format_installed_packages(packages): + return "\n".join([f"{name}: {version}" for name, version in packages.items()]) + + +@app.command( + help=""" + GPT-engineer lets you: + + \b + - Specify a software in natural language + - Sit back and watch as an AI writes and executes the code + - Ask the AI to implement improvements + """ +) +def main( + project_path: str = typer.Argument(".", help="path"), + model: str = typer.Option( + os.environ.get("MODEL_NAME", "gpt-4o"), "--model", "-m", help="model id string" + ), + temperature: float = typer.Option( + 0.1, + "--temperature", + "-t", + help="Controls randomness: lower values for more focused, deterministic outputs", + ), + improve_mode: bool = typer.Option( + False, + "--improve", + "-i", + help="Improve an existing project by modifying the files.", + ), + lite_mode: bool = typer.Option( + False, + "--lite", + "-l", + help="Lite mode: run a generation using only the main prompt.", + ), + clarify_mode: bool = typer.Option( + False, + "--clarify", + "-c", + help="Clarify mode - discuss specification with AI before implementation.", + ), + self_heal_mode: bool = typer.Option( + False, + "--self-heal", + "-sh", + help="Self-heal mode - fix the code by itself when it fails.", + ), + azure_endpoint: str = typer.Option( + "", + "--azure", + "-a", + help="""Endpoint for your Azure OpenAI Service (https://xx.openai.azure.com). + In that case, the given model is the deployment name chosen in the Azure AI Studio.""", + ), + use_custom_preprompts: bool = typer.Option( + False, + "--use-custom-preprompts", + help="""Use your project's custom preprompts instead of the default ones. + Copies all original preprompts to the project's workspace if they don't exist there.""", + ), + llm_via_clipboard: bool = typer.Option( + False, + "--llm-via-clipboard", + help="Use the clipboard to communicate with the AI.", + ), + verbose: bool = typer.Option( + False, "--verbose", "-v", help="Enable verbose logging for debugging." + ), + debug: bool = typer.Option( + False, "--debug", "-d", help="Enable debug mode for debugging." + ), + prompt_file: str = typer.Option( + "prompt", + "--prompt_file", + help="Relative path to a text file containing a prompt.", + ), + entrypoint_prompt_file: str = typer.Option( + "", + "--entrypoint_prompt", + help="Relative path to a text file containing a file that specifies requirements for you entrypoint.", + ), + image_directory: str = typer.Option( + "", + "--image_directory", + help="Relative path to a folder containing images.", + ), + use_cache: bool = typer.Option( + False, + "--use_cache", + help="Speeds up computations and saves tokens when running the same prompt multiple times by caching the LLM response.", + ), + no_execution: bool = typer.Option( + False, + "--no_execution", + help="Run setup but to not call LLM or write any code. For testing purposes.", + ), + sysinfo: bool = typer.Option( + False, + "--sysinfo", + help="Output system information for debugging", + ), +): + """ + The main entry point for the CLI tool that generates or improves a project. + + This function sets up the CLI tool, loads environment variables, initializes + the AI, and processes the user's request to generate or improve a project + based on the provided arguments. + + Parameters + ---------- + project_path : str + The file path to the project directory. + model : str + The model ID string for the AI. + temperature : float + The temperature setting for the AI's responses. + improve_mode : bool + Flag indicating whether to improve an existing project. + lite_mode : bool + Flag indicating whether to run in lite mode. + clarify_mode : bool + Flag indicating whether to discuss specifications with AI before implementation. + self_heal_mode : bool + Flag indicating whether to enable self-healing mode. + azure_endpoint : str + The endpoint for Azure OpenAI services. + use_custom_preprompts : bool + Flag indicating whether to use custom preprompts. + prompt_file : str + Relative path to a text file containing a prompt. + entrypoint_prompt_file: str + Relative path to a text file containing a file that specifies requirements for you entrypoint. + image_directory: str + Relative path to a folder containing images. + use_cache: bool + Speeds up computations and saves tokens when running the same prompt multiple times by caching the LLM response. + verbose : bool + Flag indicating whether to enable verbose logging. + no_execution: bool + Run setup but to not call LLM or write any code. For testing purposes. + sysinfo: bool + Flag indicating whether to output system information for debugging. + + Returns + ------- + None + """ + + if debug: + import pdb + + sys.excepthook = lambda *_: pdb.pm() + + if sysinfo: + sys_info = get_system_info() + for key, value in sys_info.items(): + print(f"{key}: {value}") + raise typer.Exit() + + # Validate arguments + if improve_mode and (clarify_mode or lite_mode): + typer.echo("Error: Clarify and lite mode are not compatible with improve mode.") + raise typer.Exit(code=1) + + # Set up logging + logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO) + if use_cache: + set_llm_cache(SQLiteCache(database_path=".langchain.db")) + if improve_mode: + assert not ( + clarify_mode or lite_mode + ), "Clarify and lite mode are not active for improve mode" + + load_env_if_needed() + + if llm_via_clipboard: + ai = ClipboardAI() + else: + ai = AI( + model_name=model, + temperature=temperature, + azure_endpoint=azure_endpoint, + ) + + path = Path(project_path) + print("Running gpt-engineer in", path.absolute(), "\n") + + prompt = load_prompt( + DiskMemory(path), + improve_mode, + prompt_file, + image_directory, + entrypoint_prompt_file, + ) + + # todo: if ai.vision is false and not llm_via_clipboard - ask if they would like to use gpt-4-vision-preview instead? If so recreate AI + if not ai.vision: + prompt.image_urls = None + + # configure generation function + if clarify_mode: + code_gen_fn = clarified_gen + elif lite_mode: + code_gen_fn = lite_gen + else: + code_gen_fn = gen_code + + # configure execution function + if self_heal_mode: + execution_fn = self_heal + else: + execution_fn = execute_entrypoint + + preprompts_holder = PrepromptsHolder( + get_preprompts_path(use_custom_preprompts, Path(project_path)) + ) + + memory = DiskMemory(memory_path(project_path)) + memory.archive_logs() + + execution_env = DiskExecutionEnv() + agent = CliAgent.with_default_config( + memory, + execution_env, + ai=ai, + code_gen_fn=code_gen_fn, + improve_fn=improve_fn, + process_code_fn=execution_fn, + preprompts_holder=preprompts_holder, + ) + + files = FileStore(project_path) + if not no_execution: + if improve_mode: + files_dict_before, is_linting = FileSelector(project_path).ask_for_files() + + # lint the code + if is_linting: + files_dict_before = files.linting(files_dict_before) + + files_dict = handle_improve_mode(prompt, agent, memory, files_dict_before) + if not files_dict or files_dict_before == files_dict: + print( + f"No changes applied. Could you please upload the debug_log_file.txt in {memory.path}/logs folder in a github issue?" + ) + + else: + print("\nChanges to be made:") + compare(files_dict_before, files_dict) + + print() + print(colored("Do you want to apply these changes?", "light_green")) + if not prompt_yesno(): + files_dict = files_dict_before + + else: + files_dict = agent.init(prompt) + # collect user feedback if user consents + config = (code_gen_fn.__name__, execution_fn.__name__) + collect_and_send_human_review(prompt, model, temperature, config, memory) + + stage_uncommitted_to_git(path, files_dict, improve_mode) + + files.push(files_dict) + + if ai.token_usage_log.is_openai_model(): + print("Total api cost: $ ", ai.token_usage_log.usage_cost()) + elif os.getenv("LOCAL_MODEL"): + print("Total api cost: $ 0.0 since we are using local LLM.") + else: + print("Total tokens used: ", ai.token_usage_log.total_tokens()) + + +if __name__ == "__main__": + app() diff --git a/gpt_engineer/benchmark/__init__.py b/gpt_engineer/benchmark/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt_engineer/benchmark/__main__.py b/gpt_engineer/benchmark/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..839fb87f4100e20f63a65c122297852534dce3f1 --- /dev/null +++ b/gpt_engineer/benchmark/__main__.py @@ -0,0 +1,154 @@ +""" +Main entry point for the benchmarking tool. + +This module provides a command-line interface for running benchmarks using Typer. +It allows users to specify the path to an agent, the benchmark(s) to run, and other +options such as verbosity. + +Functions +--------- +get_agent : function + Dynamically imports and returns the default configuration agent from the given path. + +main : function + The main function that runs the specified benchmarks with the given agent. + Outputs the results to the console. + +Attributes +---------- +__name__ : str + The standard boilerplate for invoking the main function when the script is executed. +""" +import importlib +import os.path +import sys + +from typing import Annotated, Optional + +import typer + +from langchain.globals import set_llm_cache +from langchain_community.cache import SQLiteCache + +from gpt_engineer.applications.cli.main import load_env_if_needed +from gpt_engineer.benchmark.bench_config import BenchConfig +from gpt_engineer.benchmark.benchmarks.load import get_benchmark +from gpt_engineer.benchmark.run import export_yaml_results, print_results, run + +app = typer.Typer( + context_settings={"help_option_names": ["-h", "--help"]} +) # creates a CLI app + + +def get_agent(path): + """ + Dynamically imports and returns the default configuration agent from the given path. + + Parameters + ---------- + path : str + The file path to the module containing the default configuration agent. + + Returns + ------- + BaseAgent + An instance of the imported default configuration agent. + """ + # Dynamically import the python module at path + sys.path.append(os.path.dirname(path)) + agent_module = importlib.import_module(path.replace("/", ".").replace(".py", "")) + return agent_module.default_config_agent() + + +@app.command( + help=""" + Run any benchmark(s) against the specified agent. + + \b + Currently available benchmarks are: apps and mbpp + """ +) +def main( + path_to_agent: Annotated[ + str, + typer.Argument( + help="python file that contains a function called 'default_config_agent'" + ), + ], + bench_config: Annotated[ + str, typer.Argument(help="optional task name in benchmark") + ] = os.path.join(os.path.dirname(__file__), "default_bench_config.toml"), + yaml_output: Annotated[ + Optional[str], + typer.Option(help="print results for each task", show_default=False), + ] = None, + verbose: Annotated[ + Optional[bool], + typer.Option(help="print results for each task", show_default=False), + ] = False, + use_cache: Annotated[ + Optional[bool], + typer.Option( + help="Speeds up computations and saves tokens when running the same prompt multiple times by caching the LLM response.", + show_default=False, + ), + ] = True, +): + """ + The main function that runs the specified benchmarks with the given agent and outputs the results to the console. + + Parameters + ---------- + path_to_agent : str + The file path to the Python module that contains a function called 'default_config_agent'. + bench_config : str, default=default_bench_config.toml + Configuration file for choosing which benchmark problems to run. See default config for more details. + yaml_output: Optional[str], default=None + Pass a path to a yaml file to have results written to file. + verbose : Optional[bool], default=False + A flag to indicate whether to print results for each task. + use_cache : Optional[bool], default=True + Speeds up computations and saves tokens when running the same prompt multiple times by caching the LLM response. + Returns + ------- + None + """ + if use_cache: + set_llm_cache(SQLiteCache(database_path=".langchain.db")) + load_env_if_needed() + config = BenchConfig.from_toml(bench_config) + print("using config file: " + bench_config) + benchmarks = list() + benchmark_results = dict() + for specific_config_name in vars(config): + specific_config = getattr(config, specific_config_name) + if hasattr(specific_config, "active"): + if specific_config.active: + benchmarks.append(specific_config_name) + + for benchmark_name in benchmarks: + benchmark = get_benchmark(benchmark_name, config) + if len(benchmark.tasks) == 0: + print( + benchmark_name + + " was skipped, since no tasks are specified. Increase the number of tasks in the config file at: " + + bench_config + ) + continue + agent = get_agent(path_to_agent) + + results = run(agent, benchmark, verbose=verbose) + print( + f"\n--- Results for agent {path_to_agent}, benchmark: {benchmark_name} ---" + ) + print_results(results) + print() + benchmark_results[benchmark_name] = { + "detailed": [result.to_dict() for result in results] + } + if yaml_output is not None: + export_yaml_results(yaml_output, benchmark_results, config.to_dict()) + + +if __name__ == "__main__": + typer.run(main) diff --git a/gpt_engineer/benchmark/bench_config.py b/gpt_engineer/benchmark/bench_config.py new file mode 100644 index 0000000000000000000000000000000000000000..526e997bb6661b582b7d51175c29cb3707477098 --- /dev/null +++ b/gpt_engineer/benchmark/bench_config.py @@ -0,0 +1,69 @@ +from dataclasses import dataclass, field +from pathlib import Path + +from tomlkit.items import Integer + +from gpt_engineer.core.project_config import read_config + + +@dataclass +class AppsConfig: + active: bool | None = True + test_start_index: int | None = 0 + test_end_index: int | None = 1 + train_start_index: int | None = 0 + train_end_index: int | None = 0 + examples_per_problem: int | None = 10 + + +@dataclass +class MbppConfig: + active: bool | None = True + test_len: int | None = 1 + train_len: int | None = 0 + + +@dataclass +class GptmeConfig: + active: bool | None = True + + +@dataclass +class BenchConfig: + """Configuration for the GPT Engineer CLI and gptengineer.app via `gpt-engineer.toml`.""" + + apps: AppsConfig = field(default_factory=AppsConfig) + mbpp: MbppConfig = field(default_factory=MbppConfig) + gptme: GptmeConfig = field(default_factory=GptmeConfig) + + @classmethod + def from_toml(cls, config_file: Path | str): + if isinstance(config_file, str): + config_file = Path(config_file) + config_dict = read_config(config_file) + return cls.from_dict(config_dict) + + @classmethod + def from_dict(cls, config_dict: dict): + return cls( + apps=AppsConfig(**config_dict.get("apps", {})), + mbpp=MbppConfig(**config_dict.get("mbpp", {})), + gptme=GptmeConfig(**config_dict.get("gptme", {})), + ) + + @staticmethod + def recursive_resolve(data_dict): + for key, value in data_dict.items(): + if isinstance(value, Integer): + data_dict[key] = int(value) + elif isinstance(value, dict): + BenchConfig.recursive_resolve(value) + + def to_dict(self): + dict_config = { + benchmark_name: {key: val for key, val in spec_config.__dict__.items()} + for benchmark_name, spec_config in self.__dict__.items() + } + BenchConfig.recursive_resolve(dict_config) + + return dict_config diff --git a/gpt_engineer/benchmark/benchmarks/apps/load.py b/gpt_engineer/benchmark/benchmarks/apps/load.py new file mode 100644 index 0000000000000000000000000000000000000000..4d0d99729a4d53f10549417c3a27f29beffbed4b --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/apps/load.py @@ -0,0 +1,118 @@ +""" +Module for loading APPS evaluation tasks. + +This module provides functionality to load tasks for evaluating GPT-based models +on smaller, more focused tasks. It defines a set of tasks with predefined prompts +and assertions to benchmark the performance of AI models. + +Functions +--------- +load_apps : function + Loads the APPS benchmark, which consists of a series coding problems. +""" +from pathlib import Path +from subprocess import TimeoutExpired +from typing import Union + +from datasets import Dataset, DatasetDict, load_dataset, load_from_disk + +from gpt_engineer.benchmark.bench_config import AppsConfig +from gpt_engineer.benchmark.benchmarks.apps.problem import Problem +from gpt_engineer.benchmark.types import Assertable, Benchmark, Task +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt + +DATASET_PATH = Path(__file__).parent / "dataset" + + +class AppsAssertion: + def __init__(self, expected: str, command: str): + self.expected_output = self._format(expected) + self.command = command + + def evaluate(self, assertable: Assertable) -> bool: + # Create new execution environment for every run to avoid side effects + env = DiskExecutionEnv() + env.upload(assertable.files) + pro = env.popen(self.command) + try: + stdout, stderr = pro.communicate(timeout=2) + stdout, stderr = stdout.decode("utf-8"), stderr.decode("utf-8") + except TimeoutExpired: + print("Execution Timeout") + return False + + return self.expected_output in self._format(stdout) + + def _format(self, string: str) -> str: + return string.replace(" ", "").replace("\n", "") + + +def _get_dataset() -> Union[Dataset, DatasetDict]: + try: + return load_from_disk(str(DATASET_PATH)) + except FileNotFoundError: + print("Dataset not found locally, downloading...") + + dataset = load_dataset("codeparrot/apps", trust_remote_code=True) + dataset.save_to_disk(str(DATASET_PATH)) + + return dataset + + +def load_apps(config: AppsConfig) -> Benchmark: + """ + Loads the APPS benchmark, which consists of a series coding problems. + + Returns + ------- + Benchmark + A Benchmark object containing a list of Task objects for the APPS evaluation. + """ + dataset = _get_dataset() + tasks = [] + problems = list() + for dataset_type in ["test", "train"]: + problems += [ + Problem( + id=problem["problem_id"], + question=problem["question"], + input_output=problem["input_output"], + starter_code=problem["starter_code"], + ) + for index, problem in enumerate(dataset[dataset_type]) + if (index < config.__getattribute__(dataset_type + "_end_index")) + and (index >= config.__getattribute__(dataset_type + "_start_index")) + ] + + for problem in problems: + prompt = Prompt( + problem.question + + "\nThe program, including its inputs, should be run from the command " + "line like 'python main \"input1 input2 etc \"', with all inputs inside " + "the quotation marks. The program should not read inputs from stdin." + ) + + tasks.append( + Task( + name=str(problem.id), + initial_code=FilesDict({"main.py": problem.starter_code}), + command=None, # Explicitly setting `None` because each assertion specifies its command + prompt=prompt, + assertions={ + f"correct output {i}": AppsAssertion( + expected=problem.outputs[i], + command="python main.py" + ' "' + problem.inputs[i] + '"', + ).evaluate + for i in range( + min(len(problem.outputs), config.examples_per_problem) + ) + }, + ) + ) + + return Benchmark( + name="apps", + tasks=tasks, + ) diff --git a/gpt_engineer/benchmark/benchmarks/apps/problem.py b/gpt_engineer/benchmark/benchmarks/apps/problem.py new file mode 100644 index 0000000000000000000000000000000000000000..b44567a42608f3f38f6a8c3f8547b1eeb9f1dd5d --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/apps/problem.py @@ -0,0 +1,25 @@ +import json + +from dataclasses import dataclass +from functools import cached_property +from typing import List + + +@dataclass(frozen=True) +class Problem: + id: int + question: str + input_output: str + starter_code: str + + @property + def inputs(self) -> List[str]: + return self._parsed_inputs_outputs["inputs"] + + @property + def outputs(self) -> List[str]: + return self._parsed_inputs_outputs["outputs"] + + @cached_property + def _parsed_inputs_outputs(self): + return json.loads(self.input_output.replace("\n", "")) diff --git a/gpt_engineer/benchmark/benchmarks/apps/problems.py b/gpt_engineer/benchmark/benchmarks/apps/problems.py new file mode 100644 index 0000000000000000000000000000000000000000..a4481104c6d4b5f1624dc574e461f7d829065750 --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/apps/problems.py @@ -0,0 +1,3 @@ +# TODO: Pick problems +# Temporary testing against these problems +PROBLEM_IDS = list(range(0, 50)) diff --git a/gpt_engineer/benchmark/benchmarks/gptme/load.py b/gpt_engineer/benchmark/benchmarks/gptme/load.py new file mode 100644 index 0000000000000000000000000000000000000000..a611ebb531c7501ae358b18a23f0c2fb12d9f04f --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/gptme/load.py @@ -0,0 +1,104 @@ +""" +Module for loading GPT-Me evaluation tasks. + +This module provides functionality to load tasks for evaluating GPT-based models +on smaller, more focused tasks. It defines a set of tasks with predefined prompts +and assertions to benchmark the performance of AI models. + +Functions +--------- +load_gptme : function + Loads the GPT-Me benchmark, which consists of a series of tasks for evaluation. +""" +from gpt_engineer.benchmark.bench_config import GptmeConfig +from gpt_engineer.benchmark.types import Benchmark, Task +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt + + +def load_gptme(config: GptmeConfig) -> Benchmark: + """ + Loads the GPT-Me benchmark, which consists of a series of tasks for evaluation. + + Returns + ------- + Benchmark + A Benchmark object containing a list of Task objects for the GPT-Me evaluation. + """ + return Benchmark( + name="gptme", + tasks=[ + Task( + name="hello", + initial_code=FilesDict({"hello.py": "print('Hello, world!')"}), + command="python hello.py", + prompt=Prompt("Change the code in hello.py to print 'Hello, human!'"), + assertions={ + "correct output": lambda assertable: assertable.stdout + == "Hello, human!\n", + "correct file": lambda assertable: assertable.files[ + "hello.py" + ].strip() + == "print('Hello, human!')", + }, + ), + Task( + name="hello-patch", + initial_code=FilesDict({"hello.py": "print('Hello, world!')"}), + command="python hello.py", + prompt=Prompt("Patch the code in hello.py to print 'Hello, human!'"), + assertions={ + "correct output": lambda assertable: assertable.stdout + == "Hello, human!\n", + "correct file": lambda assertable: assertable.files[ + "hello.py" + ].strip() + == "print('Hello, human!')", + }, + ), + Task( + name="hello-ask", + initial_code=FilesDict({"hello.py": "print('Hello, world!')"}), + command="echo 'Erik' | python hello.py", + prompt=Prompt( + "modify hello.py to ask the user for their name and print 'Hello, !'. don't try to execute it" + ), + assertions={ + "correct output": lambda assertable: "Hello, Erik!" + in assertable.stdout, + }, + ), + Task( + name="prime100", + initial_code=FilesDict( + {} + ), # Empty dictionary since no initial code is provided + command="python prime.py", + prompt=Prompt( + "write a script prime.py that computes and prints the 100th prime number" + ), + assertions={ + "correct output": lambda assertable: "541" + in assertable.stdout.split(), + }, + ), + Task( + name="init-git", + initial_code=FilesDict( + {} + ), # Empty dictionary since no initial code is provided + command="git status", + prompt=Prompt( + "initialize a git repository, write a main.py file, and commit it" + ), + assertions={ + "clean exit": lambda assertable: assertable.process.returncode == 0, + "clean working tree": lambda assertable: "nothing to commit, working tree clean" + in assertable.stdout, + "main.py exists": lambda assertable: "main.py" in assertable.files, + "we have a commit": lambda assertable: "No commits yet" + not in assertable.stdout, + }, + ), + ], + ) diff --git a/gpt_engineer/benchmark/benchmarks/load.py b/gpt_engineer/benchmark/benchmarks/load.py new file mode 100644 index 0000000000000000000000000000000000000000..dfbf7ba1c644da975c5058b78bc4bd1e4954ed43 --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/load.py @@ -0,0 +1,48 @@ +""" +Module for loading benchmarks. + +This module provides a central point to access different benchmarks by name. +It maps benchmark names to their respective loading functions. + +Functions +--------- +get_benchmark : function + Retrieves a Benchmark object by name. Raises ValueError if the benchmark is unknown. +""" +from gpt_engineer.benchmark.bench_config import BenchConfig +from gpt_engineer.benchmark.benchmarks.apps.load import load_apps +from gpt_engineer.benchmark.benchmarks.gptme.load import load_gptme +from gpt_engineer.benchmark.benchmarks.mbpp.load import load_mbpp +from gpt_engineer.benchmark.types import Benchmark + +BENCHMARKS = { + "gptme": load_gptme, + "apps": load_apps, + "mbpp": load_mbpp, +} + + +def get_benchmark(name: str, config: BenchConfig) -> Benchmark: + """ + Retrieves a Benchmark object by name. Raises ValueError if the benchmark is unknown. + + Parameters + ---------- + name : str + The name of the benchmark to retrieve. + config : BenchConfig + Configuration object for the benchmarks. + + Returns + ------- + Benchmark + The Benchmark object corresponding to the given name. + + Raises + ------ + ValueError + If the benchmark name is not found in the BENCHMARKS mapping. + """ + if name not in BENCHMARKS: + raise ValueError(f"Unknown benchmark {name}.") + return BENCHMARKS[name](config.__getattribute__(name)) diff --git a/gpt_engineer/benchmark/benchmarks/mbpp/load.py b/gpt_engineer/benchmark/benchmarks/mbpp/load.py new file mode 100644 index 0000000000000000000000000000000000000000..f0ed7282e45dbb1e5e43b5842448ad0cb7380e3f --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/mbpp/load.py @@ -0,0 +1,114 @@ +""" +Module for loading MBPP evaluation tasks. + +This module provides functionality to load tasks for evaluating GPT-based models +on smaller, more focused tasks. It defines a set of tasks with predefined prompts +and assertions to benchmark the performance of AI models. + +Functions +--------- +load_mbpp : function + Loads the MBPP benchmark, which consists of a series coding problems. +""" +from pathlib import Path +from subprocess import TimeoutExpired +from typing import Union + +from datasets import Dataset, DatasetDict, load_dataset, load_from_disk + +from gpt_engineer.benchmark.bench_config import MbppConfig +from gpt_engineer.benchmark.benchmarks.mbpp.problem import Problem +from gpt_engineer.benchmark.types import Assertable, Benchmark, Task +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt + +DATASET_PATH = Path(__file__).parent / "dataset" + + +class MbppAssertion: + def __init__(self, assertion: str): + self.assertion = assertion + + def evaluate(self, assertable: Assertable) -> bool: + generated_code = assertable.files["main.py"] + code_with_assertion = f"{generated_code}\n{self.assertion}" + + # Create new execution environment for every run to avoid side effects + env = DiskExecutionEnv() + env.upload(FilesDict({"main.py": code_with_assertion})) + pro = env.popen("python main.py") + + try: + stdout, stderr = pro.communicate(timeout=2) + stdout, stderr = stdout.decode("utf-8"), stderr.decode("utf-8") + except TimeoutExpired: + print("Execution Timeout") + return False + + return not stderr + + +def _get_dataset() -> Union[Dataset, DatasetDict]: + try: + return load_from_disk(str(DATASET_PATH)) + except FileNotFoundError: + print("Dataset not found locally, downloading...") + + dataset = load_dataset("mbpp", "sanitized", trust_remote_code=True) + dataset.save_to_disk(str(DATASET_PATH)) + + return dataset + + +def load_mbpp(config: MbppConfig) -> Benchmark: + """ + Loads the MBPP benchmark, which consists of a series coding problems. + + Returns + ------- + Benchmark + A Benchmark object containing a list of Task objects for the MBPP evaluation. + """ + dataset = _get_dataset() + tasks = [] + problems = [] + for dataset_type in ["test", "train"]: + problems += [ + Problem( + source_file=problem["source_file"], + task_id=problem["task_id"], + prompt=problem["prompt"], + code=problem["code"], + test_imports=problem["test_imports"], + test_list=problem["test_list"], + ) + for index, problem in enumerate(dataset[dataset_type]) + if index < config.__getattribute__(dataset_type + "_len") + ] + + for problem in problems: + prompt = Prompt( + problem.prompt + + "Please extend given function without changing it's declaration including arguments." + ) + + tasks.append( + Task( + name=str(problem.task_id), + initial_code=FilesDict({"main.py": problem.starting_code}), + command=None, # Explicitly setting `None` because each assertion runs code + prompt=prompt, + assertions={ + f"correct assertion {i}": MbppAssertion( + assertion=assertion + ).evaluate + for i, assertion in enumerate(problem.test_list) + }, + ) + ) + + return Benchmark( + name="mbpp", + tasks=tasks, + ) diff --git a/gpt_engineer/benchmark/benchmarks/mbpp/problem.py b/gpt_engineer/benchmark/benchmarks/mbpp/problem.py new file mode 100644 index 0000000000000000000000000000000000000000..ca3cd0ad4fdaea752bb1ccc11e9beed29231aee5 --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/mbpp/problem.py @@ -0,0 +1,25 @@ +from dataclasses import dataclass +from typing import List + + +@dataclass(frozen=True) +class Problem: + source_file: int + task_id: str + prompt: str + code: str + test_imports: str + test_list: List[str] + + @property + def starting_code(self) -> str: + lines: List[str] = [] + + for line in self.code.split("\n"): + lines.append(line) + + if line.startswith("def "): + lines.append("pass # TODO: Implement method\n") + break + + return "\n".join(lines) diff --git a/gpt_engineer/benchmark/benchmarks/mbpp/problems.py b/gpt_engineer/benchmark/benchmarks/mbpp/problems.py new file mode 100644 index 0000000000000000000000000000000000000000..c9dffd97a6fba8d83f11ed485c48a811d106dd6d --- /dev/null +++ b/gpt_engineer/benchmark/benchmarks/mbpp/problems.py @@ -0,0 +1,3 @@ +# TODO: Pick problems +# Temporary testing against these problems +PROBLEM_IDS = range(0, 100) diff --git a/gpt_engineer/benchmark/default_bench_config.toml b/gpt_engineer/benchmark/default_bench_config.toml new file mode 100644 index 0000000000000000000000000000000000000000..50205dc70568cc0079b0d842be6d1a8ef58819f5 --- /dev/null +++ b/gpt_engineer/benchmark/default_bench_config.toml @@ -0,0 +1,16 @@ +# For apps, the maximal range is 0:5000 for both train and test +[apps] +active = true +test_start_index = 0 +test_end_index = 2 +train_start_index = 0 +train_end_index = 2 + +# For mbpp, the maximal range is 0:47 +[mbpp] +active = true +test_len = 2 +train_len = 2 + +[gptme] +active = true diff --git a/gpt_engineer/benchmark/run.py b/gpt_engineer/benchmark/run.py new file mode 100644 index 0000000000000000000000000000000000000000..de5b979d4b14192b58e3315066c0302a52f4594e --- /dev/null +++ b/gpt_engineer/benchmark/run.py @@ -0,0 +1,150 @@ +""" +Module for running benchmarks. + +This module defines functions to run benchmarks using a given agent and to print +the results of the benchmark tasks. + +Functions +--------- +run : function + Runs the benchmark tasks using the provided agent and returns a list of TaskResult objects. + +print_results : function + Prints the results of the benchmark tasks to the console. +""" +import time + +from typing import List + +import yaml + +from gpt_engineer.benchmark.types import Assertable, Benchmark, TaskResult +from gpt_engineer.core.base_agent import BaseAgent +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv + + +def run( + agent: BaseAgent, + benchmark: Benchmark, + verbose=False, +) -> List[TaskResult]: + """ + Runs the benchmark tasks using the provided agent and returns a list of TaskResult objects. + + Parameters + ---------- + agent : BaseAgent + The agent to use for running the benchmark tasks. + benchmark : Benchmark + The benchmark containing the tasks to run. + verbose : bool, default=False + A flag to indicate whether to print verbose output during the benchmark. + + Returns + ------- + List[TaskResult] + A list of TaskResult objects representing the results of the benchmark tasks. + """ + task_results = [] + for task in benchmark.tasks: + print(f"--> Running task: {task.name}\n") + + t0 = time.time() + files_dict = agent.improve(task.initial_code, task.prompt) + t1 = time.time() + + env = DiskExecutionEnv() + env.upload(files_dict) + + if task.command: + p = env.popen(task.command) + stdout, stderr = p.communicate(benchmark.timeout) + stdout, stderr = stdout.decode("utf-8"), stderr.decode("utf-8") + else: + p, stdout, stderr = None, None, None + + exec_result = Assertable( + files=files_dict, + env=env, + process=p, + stdout=stdout, + stderr=stderr, + ) + + task_results.append( + TaskResult( + task_name=task.name, + assertion_results={ + assertion_name: assertion(exec_result) + for assertion_name, assertion in task.assertions.items() + }, + duration=t1 - t0, + ) + ) + + if verbose: + print_results(task_results) + return task_results + + +def print_results(results: list[TaskResult]): + """ + Prints the results of the benchmark tasks to the console. + + Parameters + ---------- + results : list[TaskResult] + A list of TaskResult objects representing the results of the benchmark tasks. + + Returns + ------- + None + """ + for task_result in results: + print(f"\n--- Results for {task_result.task_name} ---") + print(f"{task_result.task_name} ({task_result.duration:.2f}s)") + for assertion_name, assertion_result in task_result.assertion_results.items(): + checkmark = "✅" if assertion_result else "❌" + print(f" {checkmark} {assertion_name}") + print() + + success_rates = [task_result.success_rate for task_result in results] + avg_success_rate = sum(success_rates) / len(results) + + total_time = sum(task_result.duration for task_result in results) + + correct_assertions = sum( + sum( + assertion_result + for assertion_result in task_result.assertion_results.values() + ) + for task_result in results + ) + total_assertions = sum( + len(task_result.assertion_results) for task_result in results + ) + correct_tasks = [ + task_result for task_result in results if task_result.success_rate == 1 + ] + + print("--- Results ---") + print(f"Total time: {total_time:.2f}s") + print(f"Completely correct tasks: {len(correct_tasks)}/{len(results)}") + print(f"Total correct assertions: {correct_assertions}/{total_assertions}") + print(f"Average success rate: {avg_success_rate * 100}% on {len(results)} tasks") + print("--- Results ---") + print() + + +def export_yaml_results(yaml_path, complete_results, config): + for results in complete_results.values(): + correct_tasks = [ + task_result + for task_result in results["detailed"] + if task_result["solved"] == 1.0 + ] + fraction_correct = len(correct_tasks) / len(results["detailed"]) + results["fully_solved"] = fraction_correct + complete_results["config"] = config + with open(yaml_path, "w") as f: + yaml.dump(complete_results, f, indent=4) diff --git a/gpt_engineer/benchmark/types.py b/gpt_engineer/benchmark/types.py new file mode 100644 index 0000000000000000000000000000000000000000..f444b0efd6c4c742563633c96c44cf0d04582dc1 --- /dev/null +++ b/gpt_engineer/benchmark/types.py @@ -0,0 +1,94 @@ +""" +Module defining types used in benchmarking. + +This module contains dataclass definitions for various types used throughout the +benchmarking process, such as Assertable, Task, Benchmark, and TaskResult. + +Classes: + Assertable: + Represents an object that can be asserted against in a benchmark task. + + Assertion: + Type alias for a callable that takes an Assertable and returns a boolean. + + Task: + Represents a single task within a benchmark, including its assertions. + + Benchmark: + Represents a collection of tasks used to evaluate a model's performance. + + TaskResult: + Represents the result of running a single task within a benchmark. +""" +from dataclasses import dataclass +from subprocess import Popen +from typing import Callable, Dict, Optional + +from gpt_engineer.core.base_execution_env import BaseExecutionEnv +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt + + +@dataclass +class Assertable: + """ + A class representing an object which can be asserted against. + + Attributes: + files (FilesDict): The code files involved in the assertion. + env (BaseExecutionEnv): The execution environment in which the code is run. + process (Popen): The subprocess in which the code is run. + stdout (str): The standard output from the code execution. + stderr (str): The standard error from the code execution. + """ + + files: FilesDict + env: BaseExecutionEnv + process: Optional[Popen] + stdout: Optional[str] + stderr: Optional[str] + + +Assertion = Callable[[Assertable], bool] + + +@dataclass +class Task: + name: str + initial_code: Optional[FilesDict] + command: Optional[str] + prompt: Prompt + assertions: Optional[Dict[str, Assertion]] + + +@dataclass +class Benchmark: + """A benchmark is a collection of tasks that evaluate a model's performance.""" + + name: str + tasks: list[Task] + timeout: Optional[int] = None + + +@dataclass +class TaskResult: + task_name: str + assertion_results: dict[str, bool] + duration: float + + # Returns success rate from 0.00 up to 1.00 + @property + def success_rate(self) -> float: + if not self.assertion_results: + return 0.0 + + succeeded = len( + [result for result in self.assertion_results.values() if result is True] + ) + + return succeeded / len(self.assertion_results) + + def to_dict(self) -> dict: + out_dict = {key: value for key, value in self.__dict__.items()} + out_dict["solved"] = self.success_rate + return out_dict diff --git a/gpt_engineer/core/__init__.py b/gpt_engineer/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt_engineer/core/ai.py b/gpt_engineer/core/ai.py new file mode 100644 index 0000000000000000000000000000000000000000..ae86f6336471f93666059295325bdd634eb9a129 --- /dev/null +++ b/gpt_engineer/core/ai.py @@ -0,0 +1,437 @@ +""" +AI Module + +This module provides an AI class that interfaces with language models to perform various tasks such as +starting a conversation, advancing the conversation, and handling message serialization. It also includes +backoff strategies for handling rate limit errors from the OpenAI API. + +Classes: + AI: A class that interfaces with language models for conversation management and message serialization. + +Functions: + serialize_messages(messages: List[Message]) -> str + Serialize a list of messages to a JSON string. +""" + +from __future__ import annotations + +import json +import logging +import os + +from pathlib import Path +from typing import Any, List, Optional, Union + +import backoff +import openai +import pyperclip + +from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler +from langchain.chat_models.base import BaseChatModel +from langchain.schema import ( + AIMessage, + HumanMessage, + SystemMessage, + messages_from_dict, + messages_to_dict, +) +from langchain_anthropic import ChatAnthropic +from langchain_openai import AzureChatOpenAI, ChatOpenAI + +from gpt_engineer.core.token_usage import TokenUsageLog + +# Type hint for a chat message +Message = Union[AIMessage, HumanMessage, SystemMessage] + +# Set up logging +logger = logging.getLogger(__name__) + + +class AI: + """ + A class that interfaces with language models for conversation management and message serialization. + + This class provides methods to start and advance conversations, handle message serialization, + and implement backoff strategies for rate limit errors when interacting with the OpenAI API. + + Attributes + ---------- + temperature : float + The temperature setting for the language model. + azure_endpoint : str + The endpoint URL for the Azure-hosted language model. + model_name : str + The name of the language model to use. + streaming : bool + A flag indicating whether to use streaming for the language model. + llm : BaseChatModel + The language model instance for conversation management. + token_usage_log : TokenUsageLog + A log for tracking token usage during conversations. + + Methods + ------- + start(system: str, user: str, step_name: str) -> List[Message] + Start the conversation with a system message and a user message. + next(messages: List[Message], prompt: Optional[str], step_name: str) -> List[Message] + Advances the conversation by sending message history to LLM and updating with the response. + backoff_inference(messages: List[Message]) -> Any + Perform inference using the language model with an exponential backoff strategy. + serialize_messages(messages: List[Message]) -> str + Serialize a list of messages to a JSON string. + deserialize_messages(jsondictstr: str) -> List[Message] + Deserialize a JSON string to a list of messages. + _create_chat_model() -> BaseChatModel + Create a chat model with the specified model name and temperature. + """ + + def __init__( + self, + model_name="gpt-4-turbo", + temperature=0.1, + azure_endpoint=None, + streaming=True, + vision=False, + ): + """ + Initialize the AI class. + + Parameters + ---------- + model_name : str, optional + The name of the model to use, by default "gpt-4". + temperature : float, optional + The temperature to use for the model, by default 0.1. + """ + self.temperature = temperature + self.azure_endpoint = azure_endpoint + self.model_name = model_name + self.streaming = streaming + self.vision = ( + ("vision-preview" in model_name) + or ("gpt-4-turbo" in model_name and "preview" not in model_name) + or ("claude" in model_name) + ) + self.llm = self._create_chat_model() + self.token_usage_log = TokenUsageLog(model_name) + + logger.debug(f"Using model {self.model_name}") + + def start(self, system: str, user: Any, *, step_name: str) -> List[Message]: + """ + Start the conversation with a system message and a user message. + + Parameters + ---------- + system : str + The content of the system message. + user : str + The content of the user message. + step_name : str + The name of the step. + + Returns + ------- + List[Message] + The list of messages in the conversation. + """ + + messages: List[Message] = [ + SystemMessage(content=system), + HumanMessage(content=user), + ] + return self.next(messages, step_name=step_name) + + def _extract_content(self, content): + """ + Extracts text content from a message, supporting both string and list types. + Parameters + ---------- + content : Union[str, List[dict]] + The content of a message, which could be a string or a list. + Returns + ------- + str + The extracted text content. + """ + if isinstance(content, str): + return content + elif isinstance(content, list) and content and "text" in content[0]: + # Assuming the structure of list content is [{'type': 'text', 'text': 'Some text'}, ...] + return content[0]["text"] + else: + return "" + + def _collapse_text_messages(self, messages: List[Message]): + """ + Combine consecutive messages of the same type into a single message, where if the message content + is a list type, the first text element's content is taken. This method keeps `combined_content` as a string. + + This method iterates through the list of messages, combining consecutive messages of the same type + by joining their content with a newline character. If the content is a list, it extracts text from the first + text element's content. This reduces the number of messages and simplifies the conversation for processing. + + Parameters + ---------- + messages : List[Message] + The list of messages to collapse. + + Returns + ------- + List[Message] + The list of messages after collapsing consecutive messages of the same type. + """ + collapsed_messages = [] + if not messages: + return collapsed_messages + + previous_message = messages[0] + combined_content = self._extract_content(previous_message.content) + + for current_message in messages[1:]: + if current_message.type == previous_message.type: + combined_content += "\n\n" + self._extract_content( + current_message.content + ) + else: + collapsed_messages.append( + previous_message.__class__(content=combined_content) + ) + previous_message = current_message + combined_content = self._extract_content(current_message.content) + + collapsed_messages.append(previous_message.__class__(content=combined_content)) + return collapsed_messages + + def next( + self, + messages: List[Message], + prompt: Optional[str] = None, + *, + step_name: str, + ) -> List[Message]: + """ + Advances the conversation by sending message history + to LLM and updating with the response. + + Parameters + ---------- + messages : List[Message] + The list of messages in the conversation. + prompt : Optional[str], optional + The prompt to use, by default None. + step_name : str + The name of the step. + + Returns + ------- + List[Message] + The updated list of messages in the conversation. + """ + + if prompt: + messages.append(HumanMessage(content=prompt)) + + logger.debug( + "Creating a new chat completion: %s", + "\n".join([m.pretty_repr() for m in messages]), + ) + + if not self.vision: + messages = self._collapse_text_messages(messages) + + response = self.backoff_inference(messages) + + self.token_usage_log.update_log( + messages=messages, answer=response.content, step_name=step_name + ) + messages.append(response) + logger.debug(f"Chat completion finished: {messages}") + + return messages + + @backoff.on_exception(backoff.expo, openai.RateLimitError, max_tries=7, max_time=45) + def backoff_inference(self, messages): + """ + Perform inference using the language model while implementing an exponential backoff strategy. + + This function will retry the inference in case of a rate limit error from the OpenAI API. + It uses an exponential backoff strategy, meaning the wait time between retries increases + exponentially. The function will attempt to retry up to 7 times within a span of 45 seconds. + + Parameters + ---------- + messages : List[Message] + A list of chat messages which will be passed to the language model for processing. + + callbacks : List[Callable] + A list of callback functions that are triggered after each inference. These functions + can be used for logging, monitoring, or other auxiliary tasks. + + Returns + ------- + Any + The output from the language model after processing the provided messages. + + Raises + ------ + openai.error.RateLimitError + If the number of retries exceeds the maximum or if the rate limit persists beyond the + allotted time, the function will ultimately raise a RateLimitError. + + Example + ------- + >>> messages = [SystemMessage(content="Hello"), HumanMessage(content="How's the weather?")] + >>> response = backoff_inference(messages) + """ + return self.llm.invoke(messages) # type: ignore + + @staticmethod + def serialize_messages(messages: List[Message]) -> str: + """ + Serialize a list of messages to a JSON string. + + Parameters + ---------- + messages : List[Message] + The list of messages to serialize. + + Returns + ------- + str + The serialized messages as a JSON string. + """ + return json.dumps(messages_to_dict(messages)) + + @staticmethod + def deserialize_messages(jsondictstr: str) -> List[Message]: + """ + Deserialize a JSON string to a list of messages. + + Parameters + ---------- + jsondictstr : str + The JSON string to deserialize. + + Returns + ------- + List[Message] + The deserialized list of messages. + """ + data = json.loads(jsondictstr) + # Modify implicit is_chunk property to ALWAYS false + # since Langchain's Message schema is stricter + prevalidated_data = [ + {**item, "tools": {**item.get("tools", {}), "is_chunk": False}} + for item in data + ] + return list(messages_from_dict(prevalidated_data)) # type: ignore + + def _create_chat_model(self) -> BaseChatModel: + """ + Create a chat model with the specified model name and temperature. + + Parameters + ---------- + model : str + The name of the model to create. + temperature : float + The temperature to use for the model. + + Returns + ------- + BaseChatModel + The created chat model. + """ + if self.azure_endpoint: + return AzureChatOpenAI( + azure_endpoint=self.azure_endpoint, + openai_api_version=os.getenv( + "OPENAI_API_VERSION", "2024-05-01-preview" + ), + deployment_name=self.model_name, + openai_api_type="azure", + streaming=self.streaming, + callbacks=[StreamingStdOutCallbackHandler()], + ) + elif "claude" in self.model_name: + return ChatAnthropic( + model=self.model_name, + temperature=self.temperature, + callbacks=[StreamingStdOutCallbackHandler()], + streaming=self.streaming, + max_tokens_to_sample=4096, + ) + elif self.vision: + return ChatOpenAI( + model=self.model_name, + temperature=self.temperature, + streaming=self.streaming, + callbacks=[StreamingStdOutCallbackHandler()], + max_tokens=4096, # vision models default to low max token limits + ) + else: + return ChatOpenAI( + model=self.model_name, + temperature=self.temperature, + streaming=self.streaming, + callbacks=[StreamingStdOutCallbackHandler()], + ) + + +def serialize_messages(messages: List[Message]) -> str: + return AI.serialize_messages(messages) + + +class ClipboardAI(AI): + # Ignore not init superclass + def __init__(self, **_): # type: ignore + self.vision = False + self.token_usage_log = TokenUsageLog("clipboard_llm") + + @staticmethod + def serialize_messages(messages: List[Message]) -> str: + return "\n\n".join([f"{m.type}:\n{m.content}" for m in messages]) + + @staticmethod + def multiline_input(): + print("Enter/Paste your content. Ctrl-D or Ctrl-Z ( windows ) to save it.") + content = [] + while True: + try: + line = input() + except EOFError: + break + content.append(line) + return "\n".join(content) + + def next( + self, + messages: List[Message], + prompt: Optional[str] = None, + *, + step_name: str, + ) -> List[Message]: + """ + Not yet fully supported + """ + if prompt: + messages.append(HumanMessage(content=prompt)) + + logger.debug(f"Creating a new chat completion: {messages}") + + msgs = self.serialize_messages(messages) + pyperclip.copy(msgs) + Path("clipboard.txt").write_text(msgs) + print( + "Messages copied to clipboard and written to clipboard.txt,", + len(msgs), + "characters in total", + ) + + response = self.multiline_input() + + messages.append(AIMessage(content=response)) + logger.debug(f"Chat completion finished: {messages}") + + return messages diff --git a/gpt_engineer/core/base_agent.py b/gpt_engineer/core/base_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..9d70d0b2a3fc926935dfe902fa5111e539c13d39 --- /dev/null +++ b/gpt_engineer/core/base_agent.py @@ -0,0 +1,31 @@ +""" +Base Agent Module + +This module provides an abstract base class for an agent that interacts with code. It defines the interface +for agents capable of initializing and improving code based on a given prompt. Implementations of this class +are expected to provide concrete methods for these actions. + +Classes: + BaseAgent: Abstract base class for an agent that interacts with code. +""" +from abc import ABC, abstractmethod + +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt + + +class BaseAgent(ABC): + """ + Abstract base class for an agent that interacts with code. + + Defines the interface for agents capable of initializing and improving code based on a given prompt. + Implementations of this class are expected to provide concrete methods for these actions. + """ + + @abstractmethod + def init(self, prompt: Prompt) -> FilesDict: + pass + + @abstractmethod + def improve(self, files_dict: FilesDict, prompt: Prompt) -> FilesDict: + pass diff --git a/gpt_engineer/core/base_execution_env.py b/gpt_engineer/core/base_execution_env.py new file mode 100644 index 0000000000000000000000000000000000000000..e743f7831f51e6cf15ce8e45558b317236961f13 --- /dev/null +++ b/gpt_engineer/core/base_execution_env.py @@ -0,0 +1,42 @@ +from abc import ABC, abstractmethod +from subprocess import Popen +from typing import Optional, Tuple + +from gpt_engineer.core.files_dict import FilesDict + + +class BaseExecutionEnv(ABC): + """ + Abstract base class for an execution environment capable of running code. + + This class defines the interface for execution environments that can execute commands, + handle processes, and manage file uploads and downloads. + """ + + @abstractmethod + def run(self, command: str, timeout: Optional[int] = None) -> Tuple[str, str, int]: + """ + Runs a command in the execution environment. + """ + raise NotImplementedError + + @abstractmethod + def popen(self, command: str) -> Popen: + """ + Runs a command in the execution environment. + """ + raise NotImplementedError + + @abstractmethod + def upload(self, files: FilesDict) -> "BaseExecutionEnv": + """ + Uploads files to the execution environment. + """ + raise NotImplementedError + + @abstractmethod + def download(self) -> FilesDict: + """ + Downloads files from the execution environment. + """ + raise NotImplementedError diff --git a/gpt_engineer/core/base_memory.py b/gpt_engineer/core/base_memory.py new file mode 100644 index 0000000000000000000000000000000000000000..a491ae0aafa847553a95a3d4e08cf4574ee7637b --- /dev/null +++ b/gpt_engineer/core/base_memory.py @@ -0,0 +1,15 @@ +""" +Base Memory Module + +This module provides a type alias for a mutable mapping that represents the base memory structure +used in the GPT Engineer project. The base memory is a mapping from file names (as strings or Path objects) +to their corresponding code content (as strings). + +Type Aliases: + BaseMemory: A mutable mapping from file names to code content. +""" + +from pathlib import Path +from typing import MutableMapping, Union + +BaseMemory = MutableMapping[Union[str, Path], str] diff --git a/gpt_engineer/core/chat_to_files.py b/gpt_engineer/core/chat_to_files.py new file mode 100644 index 0000000000000000000000000000000000000000..7459bf3b38b3c07d7effeb889263d6dbb5e351b0 --- /dev/null +++ b/gpt_engineer/core/chat_to_files.py @@ -0,0 +1,238 @@ +""" +This Python script provides functionalities for parsing chat transcripts that contain file paths and code blocks, +applying diffs to these files, and parsing unified git diff format strings. The script is designed to work within +a larger system that involves processing and manipulating code files based on chat inputs and diff information. + +Key Components: +- chat_to_files_dict: Parses a chat transcript, extracting file paths and associated code blocks, and organizes + them into a FilesDict object, which is a custom dictionary format designed to hold file contents keyed by their paths. + +- apply_diffs: Takes a dictionary of Diff objects (which represent changes to be made to files) and a FilesDict + object containing the current state of files. It applies the changes described by the Diff objects to the + corresponding files in the FilesDict, updating the file contents as specified by the diffs. + +- parse_diffs: Parses a string containing diffs in the unified git diff format, extracting the changes described + in the diffs and organizing them into a dictionary of Diff objects, keyed by the filename to which each diff applies. + +- parse_diff_block: Parses a single block of text from a diff string, translating it into a Diff object that + represents the changes described in that block of text. + +This script is intended for use in environments where code collaboration or review is conducted through chat interfaces, +allowing for the dynamic application of changes to code bases and the efficient handling of file and diff information in chat transcripts. +""" + +import logging +import re + +from typing import Dict, Tuple + +from regex import regex + +from gpt_engineer.core.diff import ADD, REMOVE, RETAIN, Diff, Hunk +from gpt_engineer.core.files_dict import FilesDict, file_to_lines_dict + +# Initialize a logger for this module +logger = logging.getLogger(__name__) + + +def chat_to_files_dict(chat: str) -> FilesDict: + """ + Converts a chat string containing file paths and code blocks into a FilesDict object. + + Args: + - chat (str): The chat string containing file paths and code blocks. + + Returns: + - FilesDict: A dictionary with file paths as keys and code blocks as values. + """ + # Regex to match file paths and associated code blocks + regex = r"(\S+)\n\s*```[^\n]*\n(.+?)```" + matches = re.finditer(regex, chat, re.DOTALL) + + files_dict = FilesDict() + for match in matches: + # Clean and standardize the file path + path = re.sub(r'[\:<>"|?*]', "", match.group(1)) + path = re.sub(r"^\[(.*)\]$", r"\1", path) + path = re.sub(r"^`(.*)`$", r"\1", path) + path = re.sub(r"[\]\:]$", "", path) + + # Extract and clean the code content + content = match.group(2) + + # Add the cleaned path and content to the FilesDict + files_dict[path.strip()] = content.strip() + + return files_dict + + +def apply_diffs(diffs: Dict[str, Diff], files: FilesDict) -> FilesDict: + """ + Applies diffs to the provided files. + + Args: + - diffs (Dict[str, Diff]): A dictionary of diffs to apply, keyed by filename. + - files (FilesDict): The original files to which diffs will be applied. + + Returns: + - FilesDict: The updated files after applying diffs. + """ + files = FilesDict(files.copy()) + REMOVE_FLAG = "" # Placeholder to mark lines for removal + for diff in diffs.values(): + if diff.is_new_file(): + # If it's a new file, create it with the content from the diff + files[diff.filename_post] = "\n".join( + line[1] for hunk in diff.hunks for line in hunk.lines + ) + else: + # Convert the file content to a dictionary of lines + line_dict = file_to_lines_dict(files[diff.filename_pre]) + for hunk in diff.hunks: + current_line = hunk.start_line_pre_edit + for line in hunk.lines: + if line[0] == RETAIN: + current_line += 1 + elif line[0] == ADD: + # Handle added lines + current_line -= 1 + if ( + current_line in line_dict.keys() + and line_dict[current_line] != REMOVE_FLAG + ): + line_dict[current_line] += "\n" + line[1] + else: + line_dict[current_line] = line[1] + current_line += 1 + elif line[0] == REMOVE: + # Mark removed lines with REMOVE_FLAG + line_dict[current_line] = REMOVE_FLAG + current_line += 1 + + # Remove lines marked for removal + line_dict = { + key: line_content + for key, line_content in line_dict.items() + if REMOVE_FLAG not in line_content + } + # Reassemble the file content + files[diff.filename_post] = "\n".join(line_dict.values()) + return files + + +def parse_diffs(diff_string: str) -> dict: + """ + Parses a diff string in the unified git diff format. + + Args: + - diff_string (str): The diff string to parse. + + Returns: + - dict: A dictionary of Diff objects keyed by filename. + """ + # Regex to match individual diff blocks + diff_block_pattern = regex.compile( + r"```.*?\n\s*?--- .*?\n\s*?\+\+\+ .*?\n(?:@@ .*? @@\n(?:[-+ ].*?\n)*?)*?```", + re.DOTALL, + ) + + diffs = {} + try: + for block in diff_block_pattern.finditer(diff_string, timeout=1): + diff_block = block.group() + + # Parse individual diff blocks and update the diffs dictionary + diffs.update(parse_diff_block(diff_block)) + except TimeoutError: + print("gpt-engineer timed out while parsing git diff") + + if not diffs: + print( + "GPT did not provide any proposed changes. Please try to reselect the files for uploading and edit your prompt file." + ) + + return diffs + + +def parse_diff_block(diff_block: str) -> dict: + """ + Parses a block of diff text into a Diff object. + + Args: + - diff_block (str): A single block of diff text. + + Returns: + - dict: A dictionary containing a single Diff object keyed by the post-edit filename. + """ + lines = diff_block.strip().split("\n")[1:-1] # Exclude the opening and closing ``` + diffs = {} + current_diff = None + hunk_lines = [] + filename_pre = None + filename_post = None + hunk_header = None + + for line in lines: + if line.startswith("--- "): + # Pre-edit filename + filename_pre = line[4:] + elif line.startswith("+++ "): + # Post-edit filename and initiation of a new Diff object + if ( + filename_post is not None + and current_diff is not None + and hunk_header is not None + ): + current_diff.hunks.append(Hunk(*hunk_header, hunk_lines)) + hunk_lines = [] + filename_post = line[4:] + current_diff = Diff(filename_pre, filename_post) + diffs[filename_post] = current_diff + elif line.startswith("@@ "): + # Start of a new hunk in the diff + if hunk_lines and current_diff is not None and hunk_header is not None: + current_diff.hunks.append(Hunk(*hunk_header, hunk_lines)) + hunk_lines = [] + hunk_header = parse_hunk_header(line) + elif line.startswith("+"): + # Added line + hunk_lines.append((ADD, line[1:])) + elif line.startswith("-"): + # Removed line + hunk_lines.append((REMOVE, line[1:])) + else: + # Retained line + hunk_lines.append((RETAIN, line[1:])) + + # Append the last hunk if any + if current_diff is not None and hunk_lines and hunk_header is not None: + current_diff.hunks.append(Hunk(*hunk_header, hunk_lines)) + + return diffs + + +def parse_hunk_header(header_line) -> Tuple[int, int, int, int]: + """ + Parses the header of a hunk from a diff. + + Args: + - header_line (str): The header line of a hunk. + + Returns: + - tuple: A tuple containing start and length information for pre- and post-edit. + """ + pattern = re.compile(r"^@@ -\d{1,},\d{1,} \+\d{1,},\d{1,} @@$") + + if not pattern.match(header_line): + # Return a default value if the header does not match the expected format + return 0, 0, 0, 0 + + pre, post = header_line.split(" ")[1:3] + start_line_pre_edit, hunk_len_pre_edit = map(int, pre[1:].split(",")) + start_line_post_edit, hunk_len_post_edit = map(int, post[1:].split(",")) + return ( + start_line_pre_edit, + hunk_len_pre_edit, + start_line_post_edit, + hunk_len_post_edit, + ) diff --git a/gpt_engineer/core/default/__init__.py b/gpt_engineer/core/default/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt_engineer/core/default/constants.py b/gpt_engineer/core/default/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..1887d241193679cf4f626d62e89a34db6ae5c28f --- /dev/null +++ b/gpt_engineer/core/default/constants.py @@ -0,0 +1,12 @@ +""" +Module defining constants used throughout the application. + +This module contains definitions of constants that are used across various +components of the application to maintain consistency and ease of configuration. + +Constants +--------- +MAX_EDIT_REFINEMENT_STEPS : int + The maximum number of refinement steps allowed when generating edit blocks. +""" +MAX_EDIT_REFINEMENT_STEPS = 2 diff --git a/gpt_engineer/core/default/disk_execution_env.py b/gpt_engineer/core/default/disk_execution_env.py new file mode 100644 index 0000000000000000000000000000000000000000..8394de10a06de16ca2c40854f580bfb782ee4791 --- /dev/null +++ b/gpt_engineer/core/default/disk_execution_env.py @@ -0,0 +1,111 @@ +""" +Module for managing the execution environment on the local disk. + +This module provides a class that handles the execution of code stored on the local +file system. It includes methods for uploading files to the execution environment, +running commands, and capturing the output. + +Classes +------- +DiskExecutionEnv + An execution environment that runs code on the local file system and captures + the output of the execution. + +Imports +------- +- subprocess: For running shell commands. +- time: For timing the execution of commands. +- Path: For handling file system paths. +- Optional, Tuple, Union: For type annotations. +- BaseExecutionEnv: For inheriting the base execution environment interface. +- FileStore: For managing file storage. +- FilesDict: For handling collections of files. +""" + +import subprocess +import time + +from pathlib import Path +from typing import Optional, Tuple, Union + +from gpt_engineer.core.base_execution_env import BaseExecutionEnv +from gpt_engineer.core.default.file_store import FileStore +from gpt_engineer.core.files_dict import FilesDict + + +class DiskExecutionEnv(BaseExecutionEnv): + """ + An execution environment that runs code on the local file system and captures + the output of the execution. + + This class is responsible for executing code that is stored on disk. It ensures that + the necessary entrypoint file exists and then runs the code using a subprocess. If the + execution is interrupted by the user, it handles the interruption gracefully. + + Attributes + ---------- + store : FileStore + An instance of FileStore that manages the storage of files in the execution + environment. + """ + + def __init__(self, path: Union[str, Path, None] = None): + self.files = FileStore(path) + + def upload(self, files: FilesDict) -> "DiskExecutionEnv": + self.files.push(files) + return self + + def download(self) -> FilesDict: + return self.files.pull() + + def popen(self, command: str) -> subprocess.Popen: + p = subprocess.Popen( + command, + shell=True, + cwd=self.files.working_dir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + return p + + def run(self, command: str, timeout: Optional[int] = None) -> Tuple[str, str, int]: + start = time.time() + print("\n--- Start of run ---") + # while running, also print the stdout and stderr + p = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=self.files.working_dir, + text=True, + shell=True, + ) + print("$", command) + stdout_full, stderr_full = "", "" + + try: + while p.poll() is None: + assert p.stdout is not None + assert p.stderr is not None + stdout = p.stdout.readline() + stderr = p.stderr.readline() + if stdout: + print(stdout, end="") + stdout_full += stdout + if stderr: + print(stderr, end="") + stderr_full += stderr + if timeout and time.time() - start > timeout: + print("Timeout!") + p.kill() + raise TimeoutError() + except KeyboardInterrupt: + print() + print("Stopping execution.") + print("Execution stopped.") + p.kill() + print() + print("--- Finished run ---\n") + + return stdout_full, stderr_full, p.returncode diff --git a/gpt_engineer/core/default/disk_memory.py b/gpt_engineer/core/default/disk_memory.py new file mode 100644 index 0000000000000000000000000000000000000000..62c7daf32bb142cb1cdb1216d7985394c77aac93 --- /dev/null +++ b/gpt_engineer/core/default/disk_memory.py @@ -0,0 +1,326 @@ +""" +Disk Memory Module +================== + +This module provides a simple file-based key-value database system, where keys are +represented as filenames and values are the contents of these files. The `DiskMemory` class +is responsible for the CRUD operations on the database. + +Attributes +---------- +None + +Functions +--------- +None + +Classes +------- +DiskMemory + A file-based key-value store where keys correspond to filenames and values to file contents. +""" + +import base64 +import json +import shutil + +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, Iterator, Optional, Union + +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.tools.supported_languages import SUPPORTED_LANGUAGES + + +# This class represents a simple database that stores its tools as files in a directory. +class DiskMemory(BaseMemory): + """ + A file-based key-value store where keys correspond to filenames and values to file contents. + + This class provides an interface to a file-based database, leveraging file operations to + facilitate CRUD-like interactions. It allows for quick checks on the existence of keys, + retrieval of values based on keys, and setting new key-value pairs. + + Attributes + ---------- + path : Path + The directory path where the database files are stored. + """ + + def __init__(self, path: Union[str, Path]): + """ + Initialize the DiskMemory class with a specified path. + + Parameters + ---------- + path : str or Path + The path to the directory where the database files will be stored. + + """ + self.path: Path = Path(path).absolute() + + self.path.mkdir(parents=True, exist_ok=True) + + def __contains__(self, key: str) -> bool: + """ + Determine whether the database contains a file with the specified key. + + Parameters + ---------- + key : str + The key (filename) to check for existence in the database. + + Returns + ------- + bool + Returns True if the file exists, False otherwise. + + """ + return (self.path / key).is_file() + + def __getitem__(self, key: str) -> str: + """ + Retrieve the content of a file in the database corresponding to the given key. + If the file is an image with a .png or .jpeg extension, it returns the content + in Base64-encoded string format. + + Parameters + ---------- + key : str + The key (filename) whose content is to be retrieved. + + Returns + ------- + str + The content of the file associated with the key, or Base64-encoded string if it's a .png or .jpeg file. + + Raises + ------ + KeyError + If the file corresponding to the key does not exist in the database. + """ + full_path = self.path / key + + if not full_path.is_file(): + raise KeyError(f"File '{key}' could not be found in '{self.path}'") + + if full_path.suffix in [".png", ".jpeg", ".jpg"]: + with full_path.open("rb") as image_file: + encoded_string = base64.b64encode(image_file.read()).decode("utf-8") + mime_type = "image/png" if full_path.suffix == ".png" else "image/jpeg" + return f"data:{mime_type};base64,{encoded_string}" + else: + with full_path.open("r", encoding="utf-8") as f: + return f.read() + + def get(self, key: str, default: Optional[Any] = None) -> Any: + """ + Retrieve the content of a file in the database, or return a default value if not found. + + Parameters + ---------- + key : str + The key (filename) whose content is to be retrieved. + default : Any, optional + The default value to return if the file does not exist. Default is None. + + Returns + ------- + Any + The content of the file if it exists, a new DiskMemory instance if the key corresponds to a directory. + """ + + item_path = self.path / key + try: + if item_path.is_file(): + return self[key] + elif item_path.is_dir(): + return DiskMemory(item_path) + else: + return default + except: + return default + + def __setitem__(self, key: Union[str, Path], val: str) -> None: + """ + Set or update the content of a file in the database corresponding to the given key. + + Parameters + ---------- + key : str or Path + The key (filename) where the content is to be set. + val : str + The content to be written to the file. + + Raises + ------ + ValueError + If the key attempts to access a parent path. + TypeError + If the value is not a string. + + """ + if str(key).startswith("../"): + raise ValueError(f"File name {key} attempted to access parent path.") + + if not isinstance(val, str): + raise TypeError("val must be str") + + full_path = self.path / key + full_path.parent.mkdir(parents=True, exist_ok=True) + + full_path.write_text(val, encoding="utf-8") + + def __delitem__(self, key: Union[str, Path]) -> None: + """ + Delete a file or directory from the database corresponding to the given key. + + Parameters + ---------- + key : str or Path + The key (filename or directory name) to be deleted. + + Raises + ------ + KeyError + If the file or directory corresponding to the key does not exist in the database. + + """ + item_path = self.path / key + if not item_path.exists(): + raise KeyError(f"Item '{key}' could not be found in '{self.path}'") + + if item_path.is_file(): + item_path.unlink() + elif item_path.is_dir(): + shutil.rmtree(item_path) + + def __iter__(self) -> Iterator[str]: + """ + Iterate over the keys (filenames) in the database. + + Yields + ------ + Iterator[str] + An iterator over the sorted list of keys (filenames) in the database. + + """ + return iter( + sorted( + str(item.relative_to(self.path)) + for item in sorted(self.path.rglob("*")) + if item.is_file() + ) + ) + + def __len__(self) -> int: + """ + Get the number of files in the database. + + Returns + ------- + int + The number of files in the database. + + """ + return len(list(self.__iter__())) + + def _supported_files(self) -> str: + valid_extensions = { + ext for lang in SUPPORTED_LANGUAGES for ext in lang["extensions"] + } + file_paths = [ + str(item) + for item in self + if Path(item).is_file() and Path(item).suffix in valid_extensions + ] + return "\n".join(file_paths) + + def _all_files(self) -> str: + file_paths = [str(item) for item in self if Path(item).is_file()] + return "\n".join(file_paths) + + def to_path_list_string(self, supported_code_files_only: bool = False) -> str: + """ + Generate a string representation of the file paths in the database. + + Parameters + ---------- + supported_code_files_only : bool, optional + If True, filter the list to include only supported code file extensions. + Default is False. + + Returns + ------- + str + A newline-separated string of file paths. + + """ + if supported_code_files_only: + return self._supported_files() + else: + return self._all_files() + + def to_dict(self) -> Dict[Union[str, Path], str]: + """ + Convert the database contents to a dictionary. + + Returns + ------- + Dict[Union[str, Path], str] + A dictionary with keys as filenames and values as file contents. + + """ + return {file_path: self[file_path] for file_path in self} + + def to_json(self) -> str: + """ + Serialize the database contents to a JSON string. + + Returns + ------- + str + A JSON string representation of the database contents. + + """ + return json.dumps(self.to_dict()) + + def log(self, key: Union[str, Path], val: str) -> None: + """ + Append to a file or create and write to it if it doesn't exist. + + Parameters + ---------- + key : str or Path + The key (filename) where the content is to be appended. + val : str + The content to be appended to the file. + + """ + + if str(key).startswith("../"): + raise ValueError(f"File name {key} attempted to access parent path.") + + if not isinstance(val, str): + raise TypeError("val must be str") + + full_path = self.path / "logs" / key + full_path.parent.mkdir(parents=True, exist_ok=True) + + # Touch if it doesnt exist + if not full_path.exists(): + full_path.touch() + + with open(full_path, "a", encoding="utf-8") as file: + file.write(f"\n{datetime.now().isoformat()}\n") + file.write(val + "\n") + + def archive_logs(self): + """ + Moves all logs to archive directory based on current timestamp + """ + if "logs" in self: + archive_dir = ( + self.path / f"logs_{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}" + ) + shutil.move(self.path / "logs", archive_dir) diff --git a/gpt_engineer/core/default/file_store.py b/gpt_engineer/core/default/file_store.py new file mode 100644 index 0000000000000000000000000000000000000000..3700941b257915470acecdad20b2acd9ad57b469 --- /dev/null +++ b/gpt_engineer/core/default/file_store.py @@ -0,0 +1,62 @@ +import tempfile + +from pathlib import Path +from typing import Union + +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.linting import Linting + + +class FileStore: + """ + Module for managing file storage in a temporary directory. + + This module provides a class that manages the storage of files in a temporary directory. + It includes methods for uploading files to the directory and downloading them as a + collection of files. + + Classes + ------- + FileStore + Manages file storage in a temporary directory, allowing for upload and download of files. + + Imports + ------- + - tempfile: For creating temporary directories. + - Path: For handling file system paths. + - Union: For type annotations. + - FilesDict: For handling collections of files. + """ + + def __init__(self, path: Union[str, Path, None] = None): + if path is None: + path = Path(tempfile.mkdtemp(prefix="gpt-engineer-")) + + self.working_dir = Path(path) + self.working_dir.mkdir(parents=True, exist_ok=True) + self.id = self.working_dir.name.split("-")[-1] + + def push(self, files: FilesDict): + for name, content in files.items(): + path = self.working_dir / name + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "w") as f: + f.write(content) + return self + + def linting(self, files: FilesDict) -> FilesDict: + # lint the code + linting = Linting() + return linting.lint_files(files) + + def pull(self) -> FilesDict: + files = {} + for path in self.working_dir.glob("**/*"): + if path.is_file(): + with open(path, "r") as f: + try: + content = f.read() + except UnicodeDecodeError: + content = "binary file" + files[str(path.relative_to(self.working_dir))] = content + return FilesDict(files) diff --git a/gpt_engineer/core/default/paths.py b/gpt_engineer/core/default/paths.py new file mode 100644 index 0000000000000000000000000000000000000000..2c3b41de8aea0bfb4a17f609037550c024d2e117 --- /dev/null +++ b/gpt_engineer/core/default/paths.py @@ -0,0 +1,86 @@ +""" +Module defining file system paths used by the application. + +This module contains definitions of file system paths that are used throughout the +application to locate and manage various files and directories, such as logs, memory, +and preprompts. + +Constants +--------- +META_DATA_REL_PATH : str + The relative path to the directory where metadata is stored. + +MEMORY_REL_PATH : str + The relative path to the directory where memory-related files are stored. + +CODE_GEN_LOG_FILE : str + The filename for the log file that contains all output from code generation. + +DEBUG_LOG_FILE : str + The filename for the log file that contains debug information. + +ENTRYPOINT_FILE : str + The filename for the entrypoint script that is executed to run the application. + +ENTRYPOINT_LOG_FILE : str + The filename for the log file that contains the chat related to entrypoint generation. + +PREPROMPTS_PATH : Path + The file system path to the directory containing preprompt files. + +Functions +--------- +memory_path : function + Constructs the full path to the memory directory based on a given base path. + +metadata_path : function + Constructs the full path to the metadata directory based on a given base path. +""" +import os + +from pathlib import Path + +META_DATA_REL_PATH = ".gpteng" +MEMORY_REL_PATH = os.path.join(META_DATA_REL_PATH, "memory") +CODE_GEN_LOG_FILE = "all_output.txt" +IMPROVE_LOG_FILE = "improve.txt" +DIFF_LOG_FILE = "diff_errors.txt" +DEBUG_LOG_FILE = "debug_log_file.txt" +ENTRYPOINT_FILE = "run.sh" +ENTRYPOINT_LOG_FILE = "gen_entrypoint_chat.txt" +ENTRYPOINT_FILE = "run.sh" +PREPROMPTS_PATH = Path(__file__).parent.parent.parent / "preprompts" + + +def memory_path(path): + """ + Constructs the full path to the memory directory based on a given base path. + + Parameters + ---------- + path : str + The base path to append the memory directory to. + + Returns + ------- + str + The full path to the memory directory. + """ + return os.path.join(path, MEMORY_REL_PATH) + + +def metadata_path(path): + """ + Constructs the full path to the metadata directory based on a given base path. + + Parameters + ---------- + path : str + The base path to append the metadata directory to. + + Returns + ------- + str + The full path to the metadata directory. + """ + return os.path.join(path, META_DATA_REL_PATH) diff --git a/gpt_engineer/core/default/simple_agent.py b/gpt_engineer/core/default/simple_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..f2d330dc9eb7a98e596fe669d7c47acd126b1fd9 --- /dev/null +++ b/gpt_engineer/core/default/simple_agent.py @@ -0,0 +1,100 @@ +""" +Module for defining a simple agent that uses AI to manage code generation and improvement. + +This module provides a class that represents an agent capable of initializing and improving +a codebase using AI. It handles interactions with the AI model, memory, and execution +environment to generate and refine code based on user prompts. + +""" + +import tempfile + +from typing import Optional + +from gpt_engineer.core.ai import AI +from gpt_engineer.core.base_agent import BaseAgent +from gpt_engineer.core.base_execution_env import BaseExecutionEnv +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.paths import PREPROMPTS_PATH, memory_path +from gpt_engineer.core.default.steps import gen_code, gen_entrypoint, improve_fn +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.core.prompt import Prompt + + +class SimpleAgent(BaseAgent): + """ + An agent that uses AI to generate and improve code based on a given prompt. + + This agent is capable of initializing a codebase from a prompt and improving an existing + codebase based on user input. It uses an AI model to generate and refine code, and it + interacts with a repository and an execution environment to manage and execute the code. + + Attributes + ---------- + memory : BaseMemory + The memory interface where the code and related data are stored. + execution_env : BaseExecutionEnv + The execution environment in which the code is executed. + ai : AI + The AI model used for generating and improving code. + preprompts_holder : PrepromptsHolder + The holder for preprompt messages that guide the AI model. + """ + + def __init__( + self, + memory: BaseMemory, + execution_env: BaseExecutionEnv, + ai: AI = None, + preprompts_holder: PrepromptsHolder = None, + ): + self.preprompts_holder = preprompts_holder or PrepromptsHolder(PREPROMPTS_PATH) + self.memory = memory + self.execution_env = execution_env + self.ai = ai or AI() + + @classmethod + def with_default_config( + cls, path: str, ai: AI = None, preprompts_holder: PrepromptsHolder = None + ): + return cls( + memory=DiskMemory(memory_path(path)), + execution_env=DiskExecutionEnv(), + ai=ai, + preprompts_holder=preprompts_holder or PrepromptsHolder(PREPROMPTS_PATH), + ) + + def init(self, prompt: Prompt) -> FilesDict: + files_dict = gen_code(self.ai, prompt, self.memory, self.preprompts_holder) + entrypoint = gen_entrypoint( + self.ai, prompt, files_dict, self.memory, self.preprompts_holder + ) + combined_dict = {**files_dict, **entrypoint} + files_dict = FilesDict(combined_dict) + return files_dict + + def improve( + self, + files_dict: FilesDict, + prompt: Prompt, + execution_command: Optional[str] = None, + ) -> FilesDict: + files_dict = improve_fn( + self.ai, prompt, files_dict, self.memory, self.preprompts_holder + ) + return files_dict + + +def default_config_agent(): + """ + Creates an instance of SimpleAgent with default configuration. + + Returns + ------- + SimpleAgent + An instance of SimpleAgent with a temporary directory as its base path. + """ + return SimpleAgent.with_default_config(tempfile.mkdtemp()) diff --git a/gpt_engineer/core/default/steps.py b/gpt_engineer/core/default/steps.py new file mode 100644 index 0000000000000000000000000000000000000000..d778948b655be754fdc8c910b7f7e9d9a67e76fd --- /dev/null +++ b/gpt_engineer/core/default/steps.py @@ -0,0 +1,394 @@ +""" +Module for defining the steps involved in generating and improving code using AI. + +This module provides functions that represent different steps in the process of generating +and improving code using an AI model. These steps include generating code from a prompt, +creating an entrypoint for the codebase, executing the entrypoint, and refining code edits. + +Functions +--------- +curr_fn : function + Returns the name of the current function. + +setup_sys_prompt : function + Sets up the system prompt for generating code. + +gen_code : function + Generates code from a prompt using AI and returns the generated files. + +gen_entrypoint : function + Generates an entrypoint for the codebase and returns the entrypoint files. + +execute_entrypoint : function + Executes the entrypoint of the codebase. + +setup_sys_prompt_existing_code : function + Sets up the system prompt for improving existing code. + + +improve : function + Improves the code based on user input and returns the updated files. +""" + +import inspect +import io +import re +import sys +import traceback + +from pathlib import Path +from typing import List, MutableMapping, Union + +from langchain.schema import HumanMessage, SystemMessage +from termcolor import colored + +from gpt_engineer.core.ai import AI +from gpt_engineer.core.base_execution_env import BaseExecutionEnv +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.core.chat_to_files import apply_diffs, chat_to_files_dict, parse_diffs +from gpt_engineer.core.default.constants import MAX_EDIT_REFINEMENT_STEPS +from gpt_engineer.core.default.paths import ( + CODE_GEN_LOG_FILE, + DEBUG_LOG_FILE, + DIFF_LOG_FILE, + ENTRYPOINT_FILE, + ENTRYPOINT_LOG_FILE, + IMPROVE_LOG_FILE, +) +from gpt_engineer.core.files_dict import FilesDict, file_to_lines_dict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.core.prompt import Prompt + + +def curr_fn() -> str: + """ + Returns the name of the current function. + + Returns + ------- + str + The name of the function that called this function. + """ + return inspect.stack()[1].function + + +def setup_sys_prompt(preprompts: MutableMapping[Union[str, Path], str]) -> str: + """ + Sets up the system prompt for generating code. + + Parameters + ---------- + preprompts : MutableMapping[Union[str, Path], str] + A mapping of preprompt messages to guide the AI model. + + Returns + ------- + str + The system prompt message for the AI model. + """ + return ( + preprompts["roadmap"] + + preprompts["generate"].replace("FILE_FORMAT", preprompts["file_format"]) + + "\nUseful to know:\n" + + preprompts["philosophy"] + ) + + +def setup_sys_prompt_existing_code( + preprompts: MutableMapping[Union[str, Path], str] +) -> str: + """ + Sets up the system prompt for improving existing code. + + Parameters + ---------- + preprompts : MutableMapping[Union[str, Path], str] + A mapping of preprompt messages to guide the AI model. + + Returns + ------- + str + The system prompt message for the AI model to improve existing code. + """ + return ( + preprompts["roadmap"] + + preprompts["improve"].replace("FILE_FORMAT", preprompts["file_format_diff"]) + + "\nUseful to know:\n" + + preprompts["philosophy"] + ) + + +def gen_code( + ai: AI, prompt: Prompt, memory: BaseMemory, preprompts_holder: PrepromptsHolder +) -> FilesDict: + """ + Generates code from a prompt using AI and returns the generated files. + + Parameters + ---------- + ai : AI + The AI model used for generating code. + prompt : str + The user prompt to generate code from. + memory : BaseMemory + The memory interface where the code and related data are stored. + preprompts_holder : PrepromptsHolder + The holder for preprompt messages that guide the AI model. + + Returns + ------- + FilesDict + A dictionary of file names to their respective source code content. + """ + preprompts = preprompts_holder.get_preprompts() + messages = ai.start( + setup_sys_prompt(preprompts), prompt.to_langchain_content(), step_name=curr_fn() + ) + chat = messages[-1].content.strip() + memory.log(CODE_GEN_LOG_FILE, "\n\n".join(x.pretty_repr() for x in messages)) + files_dict = chat_to_files_dict(chat) + return files_dict + + +def gen_entrypoint( + ai: AI, + prompt: Prompt, + files_dict: FilesDict, + memory: BaseMemory, + preprompts_holder: PrepromptsHolder, +) -> FilesDict: + """ + Generates an entrypoint for the codebase and returns the entrypoint files. + + Parameters + ---------- + ai : AI + The AI model used for generating the entrypoint. + files_dict : FilesDict + The dictionary of file names to their respective source code content. + memory : BaseMemory + The memory interface where the code and related data are stored. + preprompts_holder : PrepromptsHolder + The holder for preprompt messages that guide the AI model. + + Returns + ------- + FilesDict + A dictionary containing the entrypoint file. + """ + user_prompt = prompt.entrypoint_prompt + if not user_prompt: + user_prompt = """ + Make a unix script that + a) installs dependencies + b) runs all necessary parts of the codebase (in parallel if necessary) + """ + preprompts = preprompts_holder.get_preprompts() + messages = ai.start( + system=(preprompts["entrypoint"]), + user=user_prompt + + "\nInformation about the codebase:\n\n" + + files_dict.to_chat(), + step_name=curr_fn(), + ) + print() + chat = messages[-1].content.strip() + regex = r"```\S*\n(.+?)```" + matches = re.finditer(regex, chat, re.DOTALL) + entrypoint_code = FilesDict( + {ENTRYPOINT_FILE: "\n".join(match.group(1) for match in matches)} + ) + memory.log(ENTRYPOINT_LOG_FILE, "\n\n".join(x.pretty_repr() for x in messages)) + return entrypoint_code + + +def execute_entrypoint( + ai: AI, + execution_env: BaseExecutionEnv, + files_dict: FilesDict, + prompt: Prompt = None, + preprompts_holder: PrepromptsHolder = None, + memory: BaseMemory = None, +) -> FilesDict: + """ + Executes the entrypoint of the codebase. + + Parameters + ---------- + ai : AI + The AI model used for generating the entrypoint. + execution_env : BaseExecutionEnv + The execution environment in which the code is executed. + files_dict : FilesDict + The dictionary of file names to their respective source code content. + preprompts_holder : PrepromptsHolder, optional + The holder for preprompt messages that guide the AI model. + + Returns + ------- + FilesDict + The dictionary of file names to their respective source code content after execution. + """ + if ENTRYPOINT_FILE not in files_dict: + raise FileNotFoundError( + "The required entrypoint " + + ENTRYPOINT_FILE + + " does not exist in the code." + ) + + command = files_dict[ENTRYPOINT_FILE] + + print() + print( + colored( + "Do you want to execute this code? (Y/n)", + "red", + ) + ) + print() + print(command) + print() + if input("").lower() not in ["", "y", "yes"]: + print("Ok, not executing the code.") + return files_dict + print("Executing the code...") + print() + print( + colored( + "Note: If it does not work as expected, consider running the code" + + " in another way than above.", + "green", + ) + ) + print() + print("You can press ctrl+c *once* to stop the execution.") + print() + + execution_env.upload(files_dict).run(f"bash {ENTRYPOINT_FILE}") + return files_dict + + +def improve_fn( + ai: AI, + prompt: Prompt, + files_dict: FilesDict, + memory: BaseMemory, + preprompts_holder: PrepromptsHolder, +) -> FilesDict: + """ + Improves the code based on user input and returns the updated files. + + Parameters + ---------- + ai : AI + The AI model used for improving code. + prompt :str + The user prompt to improve the code. + files_dict : FilesDict + The dictionary of file names to their respective source code content. + memory : BaseMemory + The memory interface where the code and related data are stored. + preprompts_holder : PrepromptsHolder + The holder for preprompt messages that guide the AI model. + + Returns + ------- + FilesDict + The dictionary of file names to their respective updated source code content. + """ + preprompts = preprompts_holder.get_preprompts() + messages = [ + SystemMessage(content=setup_sys_prompt_existing_code(preprompts)), + ] + + # Add files as input + messages.append(HumanMessage(content=f"{files_dict.to_chat()}")) + messages.append(HumanMessage(content=prompt.to_langchain_content())) + memory.log( + DEBUG_LOG_FILE, + "UPLOADED FILES:\n" + files_dict.to_log() + "\nPROMPT:\n" + prompt.text, + ) + return _improve_loop(ai, files_dict, memory, messages) + + +def _improve_loop( + ai: AI, files_dict: FilesDict, memory: BaseMemory, messages: List +) -> FilesDict: + messages = ai.next(messages, step_name=curr_fn()) + files_dict, errors = salvage_correct_hunks(messages, files_dict, memory) + + retries = 0 + while errors and retries < MAX_EDIT_REFINEMENT_STEPS: + messages.append( + HumanMessage( + content="Some previously produced diffs were not on the requested format, or the code part was not found in the code. Details:\n" + + "\n".join(errors) + + "\n Only rewrite the problematic diffs, making sure that the failing ones are now on the correct format and can be found in the code. Make sure to not repeat past mistakes. \n" + ) + ) + messages = ai.next(messages, step_name=curr_fn()) + files_dict, errors = salvage_correct_hunks(messages, files_dict, memory) + retries += 1 + + return files_dict + + +def salvage_correct_hunks( + messages: List, + files_dict: FilesDict, + memory: BaseMemory, +) -> tuple[FilesDict, List[str]]: + error_messages = [] + ai_response = messages[-1].content.strip() + + diffs = parse_diffs(ai_response) + # validate and correct diffs + + for _, diff in diffs.items(): + # if diff is a new file, validation and correction is unnecessary + if not diff.is_new_file(): + problems = diff.validate_and_correct( + file_to_lines_dict(files_dict[diff.filename_pre]) + ) + error_messages.extend(problems) + files_dict = apply_diffs(diffs, files_dict) + memory.log(IMPROVE_LOG_FILE, "\n\n".join(x.pretty_repr() for x in messages)) + memory.log(DIFF_LOG_FILE, "\n\n".join(error_messages)) + return files_dict, error_messages + + +class Tee(object): + def __init__(self, *files): + self.files = files + + def write(self, obj): + for file in self.files: + file.write(obj) + + def flush(self): + for file in self.files: + file.flush() + + +def handle_improve_mode(prompt, agent, memory, files_dict): + captured_output = io.StringIO() + old_stdout = sys.stdout + sys.stdout = Tee(sys.stdout, captured_output) + + try: + files_dict = agent.improve(files_dict, prompt) + except Exception as e: + print( + f"Error while improving the project: {e}\nCould you please upload the debug_log_file.txt in {memory.path}/logs folder to github?\nFULL STACK TRACE:\n" + ) + traceback.print_exc(file=sys.stdout) # Print the full stack trace + finally: + # Reset stdout + sys.stdout = old_stdout + + # Get the captured output + captured_string = captured_output.getvalue() + print(captured_string) + memory.log(DEBUG_LOG_FILE, "\nCONSOLE OUTPUT:\n" + captured_string) + + return files_dict diff --git a/gpt_engineer/core/diff.py b/gpt_engineer/core/diff.py new file mode 100644 index 0000000000000000000000000000000000000000..cdbfc32e38d2842d25ad211bc43e750a3d5f3094 --- /dev/null +++ b/gpt_engineer/core/diff.py @@ -0,0 +1,419 @@ +""" +File Overview: + +This Python module is designed for processing and analyzing diffs in source code files. Diffs represent the changes between two versions of a file, which are crucial in version control systems for tracking file modifications. The module focuses on the detailed examination of these diffs, enabling users to understand, validate, and correct changes between file versions. + +Key Features: + +1. The `Hunk` class encapsulates a contiguous block of changes within a file. It includes detailed information such as start lines before and after edits, lengths of change blocks, and specific line changes categorized as additions, deletions, or unchanged. + +2. The `Diff` class represents a complete set of changes across a file and may contain multiple `Hunk` objects. It facilitates operations like generating string representations of diffs, and validating and correcting hunks based on the original file content. + +3. Functions within the module allow for the validation of hunks against original files, identifying mismatches, and making necessary corrections. This feature ensures that diffs are accurate and reflect true changes. + +4. Utility functions `is_similar` and `count_ratio` offer the capability to compare strings for similarity, accounting for variations in spacing and case. This aids in the validation process by allowing a flexible comparison of code lines. + +Dependencies: + +- `logging`: Utilized for logging warnings and errors encountered during the validation and correction process. +- `collections.Counter`: Used for counting occurrences of characters in strings, supporting the string similarity assessment functions. + +Functions and Classes: + +1. `Hunk`: Class representing a block of changes within a file, with methods for managing and validating these changes. + +2. `Diff`: Class representing the entire set of changes in a file, containing multiple `Hunk` instances and methods for overall diff management. + +3. `is_similar(str1, str2, similarity_threshold)`: Function to compare two strings for similarity, useful in validating line changes in hunks. + +4. `count_ratio(str1, str2)`: Function that computes the ratio of common characters to the length of the longer string, aiding in the assessment of line similarity. + +This module is essential for developers and teams utilizing version control systems, providing tools for a deeper analysis and correction of diffs, ensuring the integrity and accuracy of code changes. + +""" +import logging + +from collections import Counter +from typing import List + +RETAIN = "retain" +ADD = "add" +REMOVE = "remove" + + +class Hunk: + """ + Represents a section of a file diff, containing changes made to that section. + + Attributes: + start_line_pre_edit (int): The starting line number in the original file. + hunk_len_pre_edit (int): The length of the hunk in the original file. + start_line_post_edit (int): The starting line number in the edited file. + hunk_len_post_edit (int): The length of the hunk in the edited file. + lines (list): A list of tuples representing the lines in the hunk and their types (RETAIN, ADD, REMOVE). + category_counts (dict): A count of lines by their type. + is_new_file (bool): Flag indicating if the hunk represents a new file. + """ + + def __init__( + self, + start_line_pre_edit, + hunk_len_pre_edit, + start_line_post_edit, + hunk_len_post_edit, + lines, + ) -> None: + self.start_line_pre_edit = start_line_pre_edit + self.hunk_len_pre_edit = hunk_len_pre_edit + self.start_line_post_edit = start_line_post_edit + self.hunk_len_post_edit = hunk_len_post_edit + self.category_counts = {RETAIN: 0, ADD: 0, REMOVE: 0} + self.lines = list() + self.add_lines(lines) + self.forward_block_len = 10 + # Note that this assumption should not be done on hunk level, however, if the below is true, no validation is possible anyway. + if self.category_counts[RETAIN] == 0 and self.category_counts[REMOVE] == 0: + self.is_new_file = True + else: + self.is_new_file = False + + def add_retained_line(self, line, index) -> None: + """Adds a retained line to the hunk at the specified index.""" + self.lines.insert(index, (RETAIN, line)) + self.category_counts[RETAIN] += 1 + + def relabel_line(self, index, new_label) -> None: + """Changes the label of a line at the specified index.""" + old_label = self.lines[index][0] + self.lines[index] = (new_label, self.lines[index][1]) + self.category_counts[old_label] -= 1 + self.category_counts[new_label] += 1 + + def pop_line(self, line, index) -> None: + """Removes a line from the hunk at the specified index.""" + self.lines.pop(index) + assert self.category_counts[line[0]] > 0 + self.category_counts[line[0]] -= 1 + + def add_lines(self, new_lines) -> None: + """Adds multiple lines to the hunk.""" + for line in new_lines: + self.lines.append(line) + self.category_counts[line[0]] += 1 + + def hunk_to_string(self) -> str: + """Converts the hunk to a string representation.""" + string = f"@@ -{self.start_line_pre_edit},{self.hunk_len_pre_edit} +{self.start_line_post_edit},{self.hunk_len_post_edit} @@\n" + for line_type, line_content in self.lines: + line_prefix = ( + " " if line_type == RETAIN else "+" if line_type == ADD else "-" + ) + string += f"{line_prefix}{line_content}\n" + return string + + def make_forward_block(self, hunk_ind: int, forward_block_len) -> str: + """Creates a block of lines for forward comparison.""" + forward_lines = [ + line[1] for line in self.lines[hunk_ind:] if not line[0] == ADD + ] + forward_block = "\n".join(forward_lines[0:forward_block_len]) + return forward_block + + def check_start_line(self, lines_dict: dict) -> bool: + """Check if the starting line of a hunk is present in the original code and returns a boolean value accordingly.""" + if self.is_new_file: + # this hunk cannot be falsified and is by definition true + return True + if self.start_line_pre_edit in lines_dict: + # check the location of the actual starting line: + is_similar(self.lines[0][1], lines_dict[self.start_line_pre_edit]) + else: + pass + + def find_start_line(self, lines_dict: dict, problems: list) -> bool: + """Finds the starting line of the hunk in the original code and returns a boolean value accordingly. If the starting line is not found, it appends a problem message to the problems list.""" + + # ToDo handle the case where the start line is 0 or 1 characters separately + if self.lines[0][0] == ADD: + # handle the case where the start line is an add + start_line = None + # find the first line that is not an add + for index, line in enumerate(self.lines): + if line[0] != ADD: + for line_number, line_content in lines_dict.items(): + # if the line is similar to a non-blank line in line_dict, we can pick the line prior to it + if is_similar(line[1], line_content) and line[1] != "": + start_line = line_number - 1 + break + # if the start line is not found, append a problem message + if start_line is None: + problems.append( + f"In {self.hunk_to_string()}:can not find the starting line of the diff" + ) + return False + + else: + # the line prior to the start line is found now we insert it to the first place as the start line + self.start_line_pre_edit = start_line + retain_line = lines_dict.get(start_line, "") + if retain_line: + self.add_retained_line(lines_dict[start_line], 0) + return self.validate_and_correct(lines_dict, problems) + else: + problems.append( + f"In {self.hunk_to_string()}:The starting line of the diff {self.hunk_to_string()} does not exist in the code" + ) + return False + pot_start_lines = { + key: is_similar(self.lines[0][1], line) for key, line in lines_dict.items() + } + sum_of_matches = sum(pot_start_lines.values()) + if sum_of_matches == 0: + # before we go any further, we should check if it's a comment from LLM + if self.lines[0][1].count("#") > 0: + # if it is, we can mark it as an ADD lines + self.relabel_line(0, ADD) + # and restart the validation at the next line + return self.validate_and_correct(lines_dict, problems) + + else: + problems.append( + f"In {self.hunk_to_string()}:The starting line of the diff {self.hunk_to_string()} does not exist in the code" + ) + return False + elif sum_of_matches == 1: + start_ind = list(pot_start_lines.keys())[ + list(pot_start_lines.values()).index(True) + ] # lines are one indexed + else: + logging.warning("multiple candidates for starting index") + # ToDo handle all the cases better again here. Smartest choice is that, for each candidate check match to the next line etc (recursively) + start_ind = list(pot_start_lines.keys())[ + list(pot_start_lines.values()).index(True) + ] + self.start_line_pre_edit = start_ind + + # This should now be fulfilled by default + assert is_similar(self.lines[0][1], lines_dict[self.start_line_pre_edit]) + return True + + def validate_lines(self, lines_dict: dict, problems: list) -> bool: + """Validates the lines of the hunk against the original file and returns a boolean value accordingly. If the lines do not match, it appends a problem message to the problems list.""" + hunk_ind = 0 + file_ind = self.start_line_pre_edit + # make an orig hunk lines for logging + # orig_hunk_lines = deepcopy(self.lines) + while hunk_ind < len(self.lines) and file_ind <= max(lines_dict): + if self.lines[hunk_ind][0] == ADD: + # this cannot be validated, jump one index + hunk_ind += 1 + elif not is_similar(self.lines[hunk_ind][1], lines_dict[file_ind]): + # before we go any further, we should relabel the comment from LLM + if self.lines[hunk_ind][1].count("#") > 0: + self.relabel_line(hunk_ind, ADD) + continue + + # make a forward block from the code for comparisons + forward_code = "\n".join( + [ + lines_dict[ind] + for ind in range( + file_ind, + min( + file_ind + self.forward_block_len, + max(lines_dict.keys()), + ), + ) + ] + ) + # make the original forward block for quantitative comparison + forward_block = self.make_forward_block( + hunk_ind, self.forward_block_len + ) + orig_count_ratio = count_ratio(forward_block, forward_code) + # Here we have 2 cases + # 1) some lines were simply skipped in the diff and we should add them to the diff + # If this is the case, adding the line to the diff, should give an improved forward diff + forward_block_missing_line = self.make_forward_block( + hunk_ind, self.forward_block_len - 1 + ) + # insert the missing line in front of the block + forward_block_missing_line = "\n".join( + [lines_dict[file_ind], forward_block_missing_line] + ) + missing_line_count_ratio = count_ratio( + forward_block_missing_line, forward_code + ) + # 2) Additional lines, not belonging to the code were added to the diff + forward_block_false_line = self.make_forward_block( + hunk_ind + 1, self.forward_block_len + ) + false_line_count_ratio = count_ratio( + forward_block_false_line, forward_code + ) + if ( + orig_count_ratio >= missing_line_count_ratio + and orig_count_ratio >= false_line_count_ratio + ): + problems.append( + f"In Hunk:{self.hunk_to_string()}, there was at least one mismatch." + ) + return False + + elif missing_line_count_ratio > false_line_count_ratio: + self.add_retained_line(lines_dict[file_ind], hunk_ind) + hunk_ind += 1 + file_ind += 1 + # NOTE: IF THE LLM SKIPS SOME LINES AND HAS ADDs ADJACENT TO THE SKIPPED BLOCK, + # WE CANNOT KNOW WHETHER THE ADDs SHOULD BE BEFORE OR AFTER THE BLOCK. WE OPT FOR PUTTING IT BEFORE. + # IF IT MATTERED, WE ASSUME THE LLM WOULD NOT SKIP THE BLOCK + else: + self.pop_line(self.lines[hunk_ind], hunk_ind) + + else: + hunk_ind += 1 + file_ind += 1 + # if we have not validated all lines, we have a problem + if hunk_ind < len(self.lines) - 1: + remaining_lines = "\n".join( + f"{line_type}: {line_content}" + for line_type, line_content in self.lines[file_ind + 1 :] + ) + problems.append( + f"In {self.hunk_to_string()}:Hunk validation stopped before the lines {remaining_lines} were validated. The diff is incorrect" + ) + return False + return True + + def validate_and_correct( + self, + lines_dict: dict, + problems: list, + ) -> bool: + """ + Validates and corrects the hunk based on the original lines. + + This function attempts to validate the hunk by comparing its lines to the original file and making corrections + where necessary. It also identifies problems such as non-matching lines or incorrect line types. + """ + start_true = self.check_start_line(lines_dict) + + if not start_true: + if not self.find_start_line(lines_dict, problems): + return False + + # Now we should be able to validate the hunk line by line and add missing line + if not self.validate_lines(lines_dict, problems): + return False + # Pass the validation + return True + + +class Diff: + """ + Represents a file diff, containing multiple hunks of changes. + + Attributes: + filename_pre (str): The name of the original file. + filename_post (str): The name of the edited file. + hunks (list): A list of Hunk objects representing the changes in the diff. + """ + + def __init__(self, filename_pre, filename_post) -> None: + self.filename_pre = filename_pre + self.filename_post = filename_post + self.hunks = [] + + def is_new_file(self) -> bool: + """Determines if the diff represents a new file.""" + if self.filename_pre == "/dev/null": + return True + return any(hunk.is_new_file for hunk in self.hunks) + + def diff_to_string(self) -> str: + """Converts the diff to a string representation.""" + string = f"--- {self.filename_pre}\n+++ {self.filename_post}\n" + for hunk in self.hunks: + string += hunk.hunk_to_string() + return string.strip() + + def validate_and_correct(self, lines_dict: dict) -> List[str]: + """Validates and corrects each hunk in the diff.""" + problems = [] + past_hunk = None + cut_lines_dict = lines_dict.copy() + for hunk in self.hunks: + if past_hunk is not None: + # make sure to not cut so much that the start_line gets out of range + cut_ind = min( + past_hunk.start_line_pre_edit + past_hunk.hunk_len_pre_edit, + hunk.start_line_pre_edit, + ) + cut_lines_dict = { + key: val for key, val in cut_lines_dict.items() if key >= (cut_ind) + } + is_valid = hunk.validate_and_correct(cut_lines_dict, problems) + if not is_valid and len(problems) > 0: + for idx, val in enumerate(problems): + print(f"\nInvalid Hunk NO.{idx}---\n{val}\n---") + self.hunks.remove(hunk) + # now correct the numbers, assuming the start line pre-edit has been fixed + hunk.hunk_len_pre_edit = ( + hunk.category_counts[RETAIN] + hunk.category_counts[REMOVE] + ) + hunk.hunk_len_post_edit = ( + hunk.category_counts[RETAIN] + hunk.category_counts[ADD] + ) + if past_hunk is not None: + hunk.start_line_post_edit = ( + hunk.start_line_pre_edit + + past_hunk.hunk_len_post_edit + - past_hunk.hunk_len_pre_edit + + past_hunk.start_line_post_edit + - past_hunk.start_line_pre_edit + ) + else: + hunk.start_line_post_edit = hunk.start_line_pre_edit + past_hunk = hunk + return problems + + +def is_similar(str1, str2, similarity_threshold=0.9) -> bool: + """ + Compares two strings for similarity, ignoring spaces and case. + + Parameters + ---------- + str1, str2 : str + The strings to compare. + similarity_threshold: float + How similar must the strings be + + Returns + ------- + bool + True if the strings are similar, False otherwise. + """ + + return count_ratio(str1, str2) >= similarity_threshold + + +def count_ratio(str1, str2) -> float: + """ + Computes the ratio of common characters to the length of the longer string, ignoring spaces and case. + + Parameters: + - str1, str2 (str): The strings to compare. + + Returns: + - float: The ratio of common characters to the length of the longer string. + """ + str1, str2 = str1.replace(" ", "").lower(), str2.replace(" ", "").lower() + + counter1, counter2 = Counter(str1), Counter(str2) + intersection = sum((counter1 & counter2).values()) + longer_length = max(len(str1), len(str2)) + if longer_length == 0: + return 1 + else: + return intersection / longer_length diff --git a/gpt_engineer/core/files_dict.py b/gpt_engineer/core/files_dict.py new file mode 100644 index 0000000000000000000000000000000000000000..3f26a3ff200326be4499c8fcbaa0761b9638266c --- /dev/null +++ b/gpt_engineer/core/files_dict.py @@ -0,0 +1,115 @@ +""" +FilesDict Module + +This module provides a FilesDict class which is a dictionary-based container for managing code files. +It extends the standard dictionary to enforce string keys and values, representing filenames and their +corresponding code content. It also provides methods to format its contents for chat-based interaction +with an AI agent and to enforce type checks on keys and values. + +Classes: + FilesDict: A dictionary-based container for managing code files. +""" +from collections import OrderedDict +from pathlib import Path +from typing import Union + + +# class Code(MutableMapping[str | Path, str]): +# ToDo: implement as mutable mapping, potentially holding a dict instead of being a dict. +class FilesDict(dict): + """ + A dictionary-based container for managing code files. + + This class extends the standard dictionary to enforce string keys and values, + representing filenames and their corresponding code content. It provides methods + to format its contents for chat-based interaction with an AI agent and to enforce + type checks on keys and values. + """ + + def __setitem__(self, key: Union[str, Path], value: str): + """ + Set the code content for the given filename, enforcing type checks on the key and value. + + Overrides the dictionary's __setitem__ to enforce type checks on the key and value. + The key must be a string or a Path object, and the value must be a string representing + the code content. + + Parameters + ---------- + key : Union[str, Path] + The filename as a key for the code content. + value : str + The code content to associate with the filename. + + Raises + ------ + TypeError + If the key is not a string or Path, or if the value is not a string. + """ + if not isinstance(key, (str, Path)): + raise TypeError("Keys must be strings or Path's") + if not isinstance(value, str): + raise TypeError("Values must be strings") + super().__setitem__(key, value) + + def to_chat(self): + """ + Formats the items of the object (assuming file name and content pairs) + into a string suitable for chat display. + + Returns + ------- + str + A string representation of the files. + """ + chat_str = "" + for file_name, file_content in self.items(): + lines_dict = file_to_lines_dict(file_content) + chat_str += f"File: {file_name}\n" + for line_number, line_content in lines_dict.items(): + chat_str += f"{line_number} {line_content}\n" + chat_str += "\n" + return f"```\n{chat_str}```" + + def to_log(self): + """ + Formats the items of the object (assuming file name and content pairs) + into a string suitable for log display. + + Returns + ------- + str + A string representation of the files. + """ + log_str = "" + for file_name, file_content in self.items(): + log_str += f"File: {file_name}\n" + log_str += file_content + log_str += "\n" + return log_str + + +def file_to_lines_dict(file_content: str) -> dict: + """ + Converts file content into a dictionary where each line number is a key + and the corresponding line content is the value. + + Parameters + ---------- + file_name : str + The name of the file. + file_content : str + The content of the file. + + Returns + ------- + dict + A dictionary with file names as keys and dictionaries (line numbers as keys and line contents as values) as values. + """ + lines_dict = OrderedDict( + { + line_number: line_content + for line_number, line_content in enumerate(file_content.split("\n"), 1) + } + ) + return lines_dict diff --git a/gpt_engineer/core/git.py b/gpt_engineer/core/git.py new file mode 100644 index 0000000000000000000000000000000000000000..0188cd1aad5f5467b33d4608d5ace9caeeee83df --- /dev/null +++ b/gpt_engineer/core/git.py @@ -0,0 +1,85 @@ +import shutil +import subprocess + +from pathlib import Path +from typing import List + +from gpt_engineer.core.files_dict import FilesDict + + +def is_git_installed(): + return shutil.which("git") is not None + + +def is_git_repo(path: Path): + return ( + subprocess.run( + ["git", "rev-parse", "--is-inside-work-tree"], + cwd=path, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).returncode + == 0 + ) + + +def init_git_repo(path: Path): + subprocess.run(["git", "init"], cwd=path) + + +def has_uncommitted_changes(path: Path): + return bool( + subprocess.run( + ["git", "diff", "--exit-code"], + cwd=path, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ).returncode + ) + + +def filter_files_with_uncommitted_changes( + basepath: Path, files_dict: FilesDict +) -> List[Path]: + files_with_diff = ( + subprocess.run( + ["git", "diff", "--name-only"], cwd=basepath, stdout=subprocess.PIPE + ) + .stdout.decode() + .splitlines() + ) + return [f for f in files_dict.keys() if f in files_with_diff] + + +def stage_files(path: Path, files: List[str]): + subprocess.run(["git", "add", *files], cwd=path) + + +def filter_by_gitignore(path: Path, file_list: List[str]) -> List[str]: + out = subprocess.run( + ["git", "-C", ".", "check-ignore", "--no-index", "--stdin"], + cwd=path, + input="\n".join(file_list).encode(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + paths = out.stdout.decode().splitlines() + # return file_list but filter out the results from git check-ignore + return [f for f in file_list if f not in paths] + + +def stage_uncommitted_to_git(path, files_dict, improve_mode): + # Check if there's a git repo and verify that there aren't any uncommitted changes + if is_git_installed() and not improve_mode: + if not is_git_repo(path): + print("\nInitializing an empty git repository") + init_git_repo(path) + + if is_git_repo(path): + modified_files = filter_files_with_uncommitted_changes(path, files_dict) + if modified_files: + print( + "Staging the following uncommitted files before overwriting: ", + ", ".join(modified_files), + ) + stage_files(path, modified_files) diff --git a/gpt_engineer/core/linting.py b/gpt_engineer/core/linting.py new file mode 100644 index 0000000000000000000000000000000000000000..37eb91c4de3cbf45b0af291585e1736c42db3d5f --- /dev/null +++ b/gpt_engineer/core/linting.py @@ -0,0 +1,64 @@ +import black + +from gpt_engineer.core.files_dict import FilesDict + + +class Linting: + def __init__(self): + # Dictionary to hold linting methods for different file types + self.linters = {".py": self.lint_python} + + import black + + def lint_python(self, content, config): + """Lint Python files using the `black` library, handling all exceptions silently and logging them. + This function attempts to format the code and returns the formatted code if successful. + If any error occurs during formatting, it logs the error and returns the original content. + """ + try: + # Try to format the content using black + linted_content = black.format_str(content, mode=black.FileMode(**config)) + except black.NothingChanged: + # If nothing changed, log the info and return the original content + print("\nInfo: No changes were made during formatting.\n") + linted_content = content + except Exception as error: + # If any other exception occurs, log the error and return the original content + print(f"\nError: Could not format due to {error}\n") + linted_content = content + return linted_content + + def lint_files(self, files_dict: FilesDict, config: dict = None) -> FilesDict: + """ + Lints files based on their extension using registered linting functions. + + Parameters + ---------- + files_dict : FilesDict + The dictionary of file names to their respective source code content. + config : dict, optional + A dictionary of configuration options for the linting tools. + + Returns + ------- + FilesDict + The dictionary of file names to their respective source code content after linting. + """ + if config is None: + config = {} + + for filename, content in files_dict.items(): + extension = filename[ + filename.rfind(".") : + ].lower() # Ensure case insensitivity + if extension in self.linters: + original_content = content + linted_content = self.linters[extension](content, config) + if linted_content != original_content: + print(f"Linted {filename}.") + else: + print(f"No changes made for {filename}.") + files_dict[filename] = linted_content + else: + print(f"No linter registered for {filename}.") + return files_dict diff --git a/gpt_engineer/core/preprompts_holder.py b/gpt_engineer/core/preprompts_holder.py new file mode 100644 index 0000000000000000000000000000000000000000..0f7d3d29ee4337d60f4c91c56017e6e2e831546a --- /dev/null +++ b/gpt_engineer/core/preprompts_holder.py @@ -0,0 +1,29 @@ +from pathlib import Path +from typing import Dict + +from gpt_engineer.core.default.disk_memory import DiskMemory + + +class PrepromptsHolder: + """ + A holder for preprompt texts that are stored on disk. + + This class provides methods to retrieve preprompt texts from a specified directory. + + Attributes + ---------- + preprompts_path : Path + The file path to the directory containing preprompt texts. + + Methods + ------- + get_preprompts() -> Dict[str, str] + Retrieve all preprompt texts from the directory and return them as a dictionary. + """ + + def __init__(self, preprompts_path: Path): + self.preprompts_path = preprompts_path + + def get_preprompts(self) -> Dict[str, str]: + preprompts_repo = DiskMemory(self.preprompts_path) + return {file_name: preprompts_repo[file_name] for file_name in preprompts_repo} diff --git a/gpt_engineer/core/project_config.py b/gpt_engineer/core/project_config.py new file mode 100644 index 0000000000000000000000000000000000000000..137a5558c869dabad04a01e5c1c00c9609672133 --- /dev/null +++ b/gpt_engineer/core/project_config.py @@ -0,0 +1,158 @@ +""" +Functions for reading and writing the `gpt-engineer.toml` configuration file. + +The `gpt-engineer.toml` file is a TOML file that contains project-specific configuration used by the GPT Engineer CLI and gptengineer.app. +""" +from dataclasses import asdict, dataclass, field +from pathlib import Path + +import tomlkit + +default_config_filename = "gpt-engineer.toml" + +example_config = """ +[run] +build = "npm run build" +test = "npm run test" +lint = "quick-lint-js" + +[paths] +base = "./frontend" # base directory to operate in (for monorepos) +src = "./src" # source directory (under the base directory) from which context will be retrieved + +[gptengineer-app] # this namespace is used for gptengineer.app, may be used for internal experiments +project_id = "..." + +# we support multiple OpenAPI schemas, used as context for the LLM +openapi = [ + { url = "https://api.gptengineer.app/openapi.json" }, + { url = "https://some-color-translating-api/openapi.json" }, +] +""" + + +@dataclass +class _PathsConfig: + base: str | None = None + src: str | None = None + + +@dataclass +class _RunConfig: + build: str | None = None + test: str | None = None + lint: str | None = None + format: str | None = None + + +@dataclass +class _OpenApiConfig: + url: str + + +@dataclass +class _GptEngineerAppConfig: + project_id: str + openapi: list[_OpenApiConfig] | None = None + + +def filter_none(d: dict) -> dict: + # Drop None values and empty dictionaries from a dictionary + return { + k: v + for k, v in ( + (k, filter_none(v) if isinstance(v, dict) else v) + for k, v in d.items() + if v is not None + ) + if not (isinstance(v, dict) and not v) # Check for non-empty after filtering + } + + +@dataclass +class Config: + """Configuration for the GPT Engineer CLI and gptengineer.app via `gpt-engineer.toml`.""" + + paths: _PathsConfig = field(default_factory=_PathsConfig) + run: _RunConfig = field(default_factory=_RunConfig) + gptengineer_app: _GptEngineerAppConfig | None = None + + @classmethod + def from_toml(cls, config_file: Path | str): + if isinstance(config_file, str): + config_file = Path(config_file) + config_dict = read_config(config_file) + return cls.from_dict(config_dict) + + @classmethod + def from_dict(cls, config_dict: dict): + run = _RunConfig(**config_dict.get("run", {})) + paths = _PathsConfig(**config_dict.get("paths", {})) + + # load optional gptengineer-app section + gptengineer_app_dict = config_dict.get("gptengineer-app", {}) + gptengineer_app = None + if gptengineer_app_dict: + assert ( + "project_id" in gptengineer_app_dict + ), "project_id is required in gptengineer-app section" + gptengineer_app = _GptEngineerAppConfig( + # required if gptengineer-app section is present + project_id=gptengineer_app_dict["project_id"], + openapi=[ + _OpenApiConfig(**openapi) + for openapi in gptengineer_app_dict.get("openapi", []) + ] + or None, + ) + + return cls(paths=paths, run=run, gptengineer_app=gptengineer_app) + + def to_dict(self) -> dict: + d = asdict(self) + d["gptengineer-app"] = d.pop("gptengineer_app", None) + + # Drop None values and empty dictionaries + # Needed because tomlkit.dumps() doesn't handle None values, + # and we don't want to write empty sections. + d = filter_none(d) + + return d + + def to_toml(self, config_file: Path | str, save=True) -> str: + """Write the configuration to a TOML file.""" + if isinstance(config_file, str): + config_file = Path(config_file) + + # Load the TOMLDocument and overwrite it with the new values + config = read_config(config_file) + default_config = Config().to_dict() + for k, v in self.to_dict().items(): + # only write values that are already explicitly set, or that differ from defaults + if k in config or v != default_config[k]: + if isinstance(v, dict): + config[k] = { + k2: v2 + for k2, v2 in v.items() + if ( + k2 in config[k] + or default_config.get(k) is None + or v2 != default_config[k].get(k2) + ) + } + else: + config[k] = v + + toml_str = tomlkit.dumps(config) + if save: + with open(config_file, "w") as f: + f.write(toml_str) + + return toml_str + + +def read_config(config_file: Path) -> tomlkit.TOMLDocument: + """Read the configuration file""" + assert config_file.exists(), f"Config file {config_file} does not exist" + with open(config_file, "r") as f: + return tomlkit.load(f) diff --git a/gpt_engineer/core/prompt.py b/gpt_engineer/core/prompt.py new file mode 100644 index 0000000000000000000000000000000000000000..4d8286343c52041ddc6a5c001169400a167f04fb --- /dev/null +++ b/gpt_engineer/core/prompt.py @@ -0,0 +1,44 @@ +import json + +from typing import Dict, Optional + + +class Prompt: + def __init__( + self, + text: str, + image_urls: Optional[Dict[str, str]] = None, + entrypoint_prompt: str = "", + ): + self.text = text + self.image_urls = image_urls + self.entrypoint_prompt = entrypoint_prompt + + def __repr__(self): + return f"Prompt(text={self.text!r}, image_urls={self.image_urls!r})" + + def to_langchain_content(self): + content = [{"type": "text", "text": f"Request: {self.text}"}] + + if self.image_urls: + for name, url in self.image_urls.items(): + image_content = { + "type": "image_url", + "image_url": { + "url": url, + "detail": "low", + }, + } + content.append(image_content) + + return content + + def to_dict(self): + return { + "text": self.text, + "image_urls": self.image_urls, + "entrypoint_prompt": self.entrypoint_prompt, + } + + def to_json(self): + return json.dumps(self.to_dict()) diff --git a/gpt_engineer/core/token_usage.py b/gpt_engineer/core/token_usage.py new file mode 100644 index 0000000000000000000000000000000000000000..b10fec9033e70e405882f0b72a57a31cab3d0a03 --- /dev/null +++ b/gpt_engineer/core/token_usage.py @@ -0,0 +1,297 @@ +import base64 +import io +import logging +import math + +from dataclasses import dataclass +from typing import List, Union + +import tiktoken + +from langchain.schema import AIMessage, HumanMessage, SystemMessage +from PIL import Image + +# workaround for function moved in: +# https://github.com/langchain-ai/langchain/blob/535db72607c4ae308566ede4af65295967bb33a8/libs/community/langchain_community/callbacks/openai_info.py +try: + from langchain.callbacks.openai_info import ( + get_openai_token_cost_for_model, # fmt: skip + ) +except ImportError: + from langchain_community.callbacks.openai_info import ( + get_openai_token_cost_for_model, # fmt: skip + ) + + +Message = Union[AIMessage, HumanMessage, SystemMessage] + +logger = logging.getLogger(__name__) + + +@dataclass +class TokenUsage: + """ + Dataclass representing token usage statistics for a conversation step. + + Attributes + ---------- + step_name : str + The name of the conversation step. + in_step_prompt_tokens : int + The number of prompt tokens used in the step. + in_step_completion_tokens : int + The number of completion tokens used in the step. + in_step_total_tokens : int + The total number of tokens used in the step. + total_prompt_tokens : int + The cumulative number of prompt tokens used up to this step. + total_completion_tokens : int + The cumulative number of completion tokens used up to this step. + total_tokens : int + The cumulative total number of tokens used up to this step. + """ + + """ + Represents token usage statistics for a conversation step. + """ + + step_name: str + in_step_prompt_tokens: int + in_step_completion_tokens: int + in_step_total_tokens: int + total_prompt_tokens: int + total_completion_tokens: int + total_tokens: int + + +class Tokenizer: + """ + Tokenizer for counting tokens in text. + """ + + def __init__(self, model_name): + self.model_name = model_name + self._tiktoken_tokenizer = ( + tiktoken.encoding_for_model(model_name) + if "gpt-4" in model_name or "gpt-3.5" in model_name + else tiktoken.get_encoding("cl100k_base") + ) + + def num_tokens(self, txt: str) -> int: + """ + Get the number of tokens in a text. + + Parameters + ---------- + txt : str + The text to count the tokens in. + + Returns + ------- + int + The number of tokens in the text. + """ + return len(self._tiktoken_tokenizer.encode(txt)) + + def num_tokens_for_base64_image( + self, image_base64: str, detail: str = "high" + ) -> int: + """ + Calculate the token size for a base64 encoded image based on OpenAI's token calculation rules. + + Parameters: + - image_base64 (str): The base64 encoded string of the image. + - detail (str): The detail level of the image, 'low' or 'high'. + + Returns: + - int: The token size of the image. + """ + + if detail == "low": + return 85 # Fixed cost for low detail images + + # Decode image from base64 + image_data = base64.b64decode(image_base64) + + # Convert byte data to image for size extraction + image = Image.open(io.BytesIO(image_data)) + + # Calculate the initial scale to fit within 2048 square while maintaining aspect ratio + max_dimension = max(image.size) + scale_factor = min(2048 / max_dimension, 1) # Ensure we don't scale up + new_width = int(image.size[0] * scale_factor) + new_height = int(image.size[1] * scale_factor) + + # Scale such that the shortest side is 768px + shortest_side = min(new_width, new_height) + if shortest_side > 768: + resize_factor = 768 / shortest_side + new_width = int(new_width * resize_factor) + new_height = int(new_height * resize_factor) + + # Calculate the number of 512px tiles needed + width_tiles = math.ceil(new_width / 512) + height_tiles = math.ceil(new_height / 512) + total_tiles = width_tiles * height_tiles + + # Each tile costs 170 tokens, plus a base cost of 85 tokens for high detail + token_cost = total_tiles * 170 + 85 + + return token_cost + + def num_tokens_from_messages(self, messages: List[Message]) -> int: + """ + Get the total number of tokens used by a list of messages, accounting for text and base64 encoded images. + + Parameters + ---------- + messages : List[Message] + The list of messages to count the tokens in. + + Returns + ------- + int + The total number of tokens used by the messages. + """ + n_tokens = 0 + for message in messages: + n_tokens += 4 # Account for message framing tokens + + if isinstance(message.content, str): + # Content is a simple string + n_tokens += self.num_tokens(message.content) + elif isinstance(message.content, list): + # Content is a list, potentially mixed with text and images + for item in message.content: + if item.get("type") == "text": + n_tokens += self.num_tokens(item["text"]) + elif item.get("type") == "image_url": + image_detail = item["image_url"].get("detail", "high") + image_base64 = item["image_url"].get("url") + n_tokens += self.num_tokens_for_base64_image( + image_base64, detail=image_detail + ) + + n_tokens += 2 # Account for assistant's reply framing tokens + + return n_tokens + + +class TokenUsageLog: + """ + Represents a log of token usage statistics for a conversation. + """ + + def __init__(self, model_name): + self.model_name = model_name + self._cumulative_prompt_tokens = 0 + self._cumulative_completion_tokens = 0 + self._cumulative_total_tokens = 0 + self._log = [] + self._tokenizer = Tokenizer(model_name) + + def update_log(self, messages: List[Message], answer: str, step_name: str) -> None: + """ + Update the token usage log with the number of tokens used in the current step. + + Parameters + ---------- + messages : List[Message] + The list of messages in the conversation. + answer : str + The answer from the AI. + step_name : str + The name of the step. + """ + prompt_tokens = self._tokenizer.num_tokens_from_messages(messages) + completion_tokens = self._tokenizer.num_tokens(answer) + total_tokens = prompt_tokens + completion_tokens + + self._cumulative_prompt_tokens += prompt_tokens + self._cumulative_completion_tokens += completion_tokens + self._cumulative_total_tokens += total_tokens + + self._log.append( + TokenUsage( + step_name=step_name, + in_step_prompt_tokens=prompt_tokens, + in_step_completion_tokens=completion_tokens, + in_step_total_tokens=total_tokens, + total_prompt_tokens=self._cumulative_prompt_tokens, + total_completion_tokens=self._cumulative_completion_tokens, + total_tokens=self._cumulative_total_tokens, + ) + ) + + def log(self) -> List[TokenUsage]: + """ + Get the token usage log. + + Returns + ------- + List[TokenUsage] + A log of token usage details per step in the conversation. + """ + return self._log + + def format_log(self) -> str: + """ + Format the token usage log as a CSV string. + + Returns + ------- + str + The token usage log formatted as a CSV string. + """ + result = "step_name,prompt_tokens_in_step,completion_tokens_in_step,total_tokens_in_step,total_prompt_tokens,total_completion_tokens,total_tokens\n" + for log in self._log: + result += f"{log.step_name},{log.in_step_prompt_tokens},{log.in_step_completion_tokens},{log.in_step_total_tokens},{log.total_prompt_tokens},{log.total_completion_tokens},{log.total_tokens}\n" + return result + + def is_openai_model(self) -> bool: + """ + Check if the model is an OpenAI model. + + Returns + ------- + bool + True if the model is an OpenAI model, False otherwise. + """ + return "gpt" in self.model_name.lower() + + def total_tokens(self) -> int: + """ + Return the total number of tokens used in the conversation. + + Returns + ------- + int + The total number of tokens used in the conversation. + """ + return self._cumulative_total_tokens + + def usage_cost(self) -> float | None: + """ + Return the total cost in USD of the API usage. + + Returns + ------- + float + Cost in USD. + """ + if not self.is_openai_model(): + return None + + try: + result = 0 + for log in self.log(): + result += get_openai_token_cost_for_model( + self.model_name, log.total_prompt_tokens, is_completion=False + ) + result += get_openai_token_cost_for_model( + self.model_name, log.total_completion_tokens, is_completion=True + ) + return result + except Exception as e: + print(f"Error calculating usage cost: {e}") + return None diff --git a/gpt_engineer/core/version_manager.py b/gpt_engineer/core/version_manager.py new file mode 100644 index 0000000000000000000000000000000000000000..d6e0b51d91eae942a7d5c5544f9a80c6ab894c56 --- /dev/null +++ b/gpt_engineer/core/version_manager.py @@ -0,0 +1,30 @@ +""" +Version Manager Module + +This module provides an abstract base class for a version manager that handles the creation of snapshots +for code. Implementations of this class are expected to provide methods to create a snapshot of the given +code and return a reference to it. +""" +from abc import ABC, abstractmethod +from pathlib import Path +from typing import Union + +from gpt_engineer.core.files_dict import FilesDict + + +class BaseVersionManager(ABC): + """ + Abstract base class for a version manager. + + Defines the interface for version managers that handle the creation of snapshots for code. + Implementations of this class are expected to provide methods to create a snapshot of the given + code and return a reference to it. + """ + + @abstractmethod + def __init__(self, path: Union[str, Path]): + pass + + @abstractmethod + def snapshot(self, files_dict: FilesDict) -> str: + pass diff --git a/gpt_engineer/preprompts/clarify b/gpt_engineer/preprompts/clarify new file mode 100644 index 0000000000000000000000000000000000000000..5fae0da39b8d010f5b29bc0e0401ef7c57d82dbc --- /dev/null +++ b/gpt_engineer/preprompts/clarify @@ -0,0 +1,3 @@ +Given some instructions, determine if anything needs to be clarified, do not carry them out. +You can make reasonable assumptions, but if you are unsure, ask a single clarification question. +Otherwise state: "Nothing to clarify" diff --git a/gpt_engineer/preprompts/entrypoint b/gpt_engineer/preprompts/entrypoint new file mode 100644 index 0000000000000000000000000000000000000000..5a952043af3bd842e2d9234055c66b664526d6f5 --- /dev/null +++ b/gpt_engineer/preprompts/entrypoint @@ -0,0 +1,6 @@ +You will get information about a codebase that is currently on disk in the current folder. +The user will ask you to write a script that runs the code in a specific way. +You will answer with code blocks that include all the necessary terminal commands. +Do not install globally. Do not use sudo. +Do not explain the code, just give the commands. +Do not use placeholders, use example values (like . for a folder argument) if necessary. diff --git a/gpt_engineer/preprompts/file_format b/gpt_engineer/preprompts/file_format new file mode 100644 index 0000000000000000000000000000000000000000..45a611855a98e117e058c602d3c2ea976efa98e5 --- /dev/null +++ b/gpt_engineer/preprompts/file_format @@ -0,0 +1,20 @@ +You will output the content of each file necessary to achieve the goal, including ALL code. +Represent files like so: + +FILENAME +``` +CODE +``` + +The following tokens must be replaced like so: +FILENAME is the lowercase combined path and file name including the file extension +CODE is the code in the file + +Example representation of a file: + +src/hello_world.py +``` +print("Hello World") +``` + +Do not comment on what every file does. Please note that the code should be fully functional. No placeholders. diff --git a/gpt_engineer/preprompts/file_format_diff b/gpt_engineer/preprompts/file_format_diff new file mode 100644 index 0000000000000000000000000000000000000000..bcf34e2921c408a2ea3d3e0de528b3f1961460e2 --- /dev/null +++ b/gpt_engineer/preprompts/file_format_diff @@ -0,0 +1,39 @@ +You will output the content of each file necessary to achieve the goal, including ALL code. +Output requested code changes and new code in the unified "git diff" syntax. Example: + +```diff +--- example.txt ++++ example.txt +@@ -6,3 +6,4 @@ + line content A + line content B ++ new line added +- original line X ++ modified line X with changes +@@ -26,4 +27,5 @@ + condition check: +- action for condition A ++ if certain condition is met: ++ alternative action for condition A + another condition check: +- action for condition B ++ modified action for condition B +``` + +Example of a git diff creating a new file: + +```diff +--- /dev/null ++++ new_file.txt +@@ -0,0 +1,3 @@ ++First example line ++ ++Last example line +``` +-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous! +-Every diff must be fenced with triple backtick ```. +-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff. +-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE. +-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS. +-AVOID STARTING A HUNK WITH AN EMPTY LINE. +-ALL DIFFS WILL BE APPLIED SIMULTANEOUSLY, AVOID SEQUENTIAL HUNKS. diff --git a/gpt_engineer/preprompts/file_format_fix b/gpt_engineer/preprompts/file_format_fix new file mode 100644 index 0000000000000000000000000000000000000000..69dade9182ab95b6a1c2c1e60ebd767029bc957e --- /dev/null +++ b/gpt_engineer/preprompts/file_format_fix @@ -0,0 +1,22 @@ +Please fix any errors in the code above. + +You will output the content of each new or changed. +Represent files like so: + +FILENAME +``` +CODE +``` + +The following tokens must be replaced like so: +FILENAME is the lowercase combined path and file name including the file extension +CODE is the code in the file + +Example representation of a file: + +src/hello_world.py +``` +print("Hello World") +``` + +Do not comment on what every file does. Please note that the code should be fully functional. No placeholders. diff --git a/gpt_engineer/preprompts/generate b/gpt_engineer/preprompts/generate new file mode 100644 index 0000000000000000000000000000000000000000..6e9725879ac99ae4e925af20c89484406122bdfd --- /dev/null +++ b/gpt_engineer/preprompts/generate @@ -0,0 +1,15 @@ +Think step by step and reason yourself to the correct decisions to make sure we get it right. +First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose. + +FILE_FORMAT + +You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. +Please note that the code should be fully functional. No placeholders. + +Follow a language and framework appropriate best practice file naming convention. +Make sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other. +Ensure to implement all code, if you are unsure, write a plausible implementation. +Include module dependency or package manager dependency definition file. +Before you finish, double check that all parts of the architecture is present in the files. + +When you are done, write finish with "this concludes a fully working implementation". diff --git a/gpt_engineer/preprompts/improve b/gpt_engineer/preprompts/improve new file mode 100644 index 0000000000000000000000000000000000000000..a810b426669190795c1af5e470816f3dc675b1b2 --- /dev/null +++ b/gpt_engineer/preprompts/improve @@ -0,0 +1,15 @@ +Think step by step and reason yourself to the correct decisions to make sure we get it right. +Make changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose. + +FILE_FORMAT + +As far as compatible with the user request, start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. +Please note that the code should be fully functional. No placeholders. + +Follow a language and framework appropriate best practice file naming convention. +Make sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other. +Ensure to implement all code, if you are unsure, write a plausible implementation. +Include module dependency or package manager dependency definition file. +Before you finish, double check that all parts of the architecture is present in the files. + +When you are done, write finish with "this concludes a fully working implementation". diff --git a/gpt_engineer/preprompts/philosophy b/gpt_engineer/preprompts/philosophy new file mode 100644 index 0000000000000000000000000000000000000000..74231021cc75aec9478f401f4910b7e8d2f5cb83 --- /dev/null +++ b/gpt_engineer/preprompts/philosophy @@ -0,0 +1,12 @@ +Almost always put different classes in different files. +Always use the programming language the user asks for. +For Python, you always create an appropriate requirements.txt file. +For NodeJS, you always create an appropriate package.json file. +Always add a comment briefly describing the purpose of the function definition. +Add comments explaining very complex bits of logic. +Always follow the best practices for the requested languages for folder/file structure and how to package the project. + + +Python toolbelt preferences: +- pytest +- dataclasses diff --git a/gpt_engineer/preprompts/roadmap b/gpt_engineer/preprompts/roadmap new file mode 100644 index 0000000000000000000000000000000000000000..177bfdb5a8499267fd7bf6408cc659945ce170d2 --- /dev/null +++ b/gpt_engineer/preprompts/roadmap @@ -0,0 +1,2 @@ +You will get instructions for code to write. +You will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code. diff --git a/gpt_engineer/tools/__init__.py b/gpt_engineer/tools/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt_engineer/tools/custom_steps.py b/gpt_engineer/tools/custom_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..827fbb685013551c35e57407ff5a6360d9c41ef7 --- /dev/null +++ b/gpt_engineer/tools/custom_steps.py @@ -0,0 +1,232 @@ +from platform import platform +from sys import version_info +from typing import List, Union + +from langchain.schema import AIMessage, HumanMessage, SystemMessage + +from gpt_engineer.core.ai import AI +from gpt_engineer.core.base_execution_env import BaseExecutionEnv +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.core.chat_to_files import chat_to_files_dict +from gpt_engineer.core.default.paths import CODE_GEN_LOG_FILE, ENTRYPOINT_FILE +from gpt_engineer.core.default.steps import curr_fn, improve_fn, setup_sys_prompt +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.core.prompt import Prompt + +# Type hint for chat messages +Message = Union[AIMessage, HumanMessage, SystemMessage] +MAX_SELF_HEAL_ATTEMPTS = 10 + + +def get_platform_info() -> str: + """ + Returns a string containing the OS and Python version information. + + This function is used for self-healing by providing information about the current + operating system and Python version. It assumes that the Python version in the + virtual environment is the one being used. + + Returns: + str: A string containing the OS and Python version information. + """ + + v = version_info + a = f"Python Version: {v.major}.{v.minor}.{v.micro}" + b = f"\nOS: {platform()}\n" + return a + b + + +def self_heal( + ai: AI, + execution_env: BaseExecutionEnv, + files_dict: FilesDict, + prompt: Prompt = None, + preprompts_holder: PrepromptsHolder = None, + memory: BaseMemory = None, +) -> FilesDict: + """ + Attempts to execute the code from the entrypoint and if it fails, sends the error output back to the AI with instructions to fix. + + Parameters + ---------- + ai : AI + An instance of the AI model. + execution_env : BaseExecutionEnv + The execution environment where the code is run. + files_dict : FilesDict + A dictionary of file names to their contents. + preprompts_holder : PrepromptsHolder, optional + A holder for preprompt messages. + + Returns + ------- + FilesDict + The updated files dictionary after self-healing attempts. + + Raises + ------ + FileNotFoundError + If the required entrypoint file does not exist in the code. + AssertionError + If the preprompts_holder is None. + + Notes + ----- + This code will make `MAX_SELF_HEAL_ATTEMPTS` to try and fix the code + before giving up. + This makes the assuption that the previous step was `gen_entrypoint`, + this code could work with `simple_gen`, or `gen_clarified_code` as well. + """ + + # step 1. execute the entrypoint + # log_path = dbs.workspace.path / "log.txt" + if ENTRYPOINT_FILE not in files_dict: + raise FileNotFoundError( + "The required entrypoint " + + ENTRYPOINT_FILE + + " does not exist in the code." + ) + + attempts = 0 + if preprompts_holder is None: + raise AssertionError("Prepromptsholder required for self-heal") + while attempts < MAX_SELF_HEAL_ATTEMPTS: + attempts += 1 + timed_out = False + + # Start the process + execution_env.upload(files_dict) + p = execution_env.popen(files_dict[ENTRYPOINT_FILE]) + + # Wait for the process to complete and get output + stdout_full, stderr_full = p.communicate() + + if (p.returncode != 0 and p.returncode != 2) and not timed_out: + print("run.sh failed. The log is:") + print(stdout_full.decode("utf-8")) + print(stderr_full.decode("utf-8")) + + new_prompt = Prompt( + f"A program with this specification was requested:\n{prompt}\n, but running it produced the following output:\n{stdout_full}\n and the following errors:\n{stderr_full}. Please change it so that it fulfills the requirements." + ) + files_dict = improve_fn( + ai, new_prompt, files_dict, memory, preprompts_holder + ) + else: + break + return files_dict + + +def clarified_gen( + ai: AI, prompt: Prompt, memory: BaseMemory, preprompts_holder: PrepromptsHolder +) -> FilesDict: + """ + Generates code based on clarifications obtained from the user and saves it to a specified workspace. + + Parameters + ---------- + ai : AI + An instance of the AI model, responsible for processing and generating the code. + prompt : str + The user's clarification prompt. + memory : BaseMemory + The memory instance where the generated code log is saved. + preprompts_holder : PrepromptsHolder + A holder for preprompt messages. + + Returns + ------- + FilesDict + A dictionary of file names to their contents generated by the AI. + """ + + preprompts = preprompts_holder.get_preprompts() + messages: List[Message] = [SystemMessage(content=preprompts["clarify"])] + user_input = prompt.text # clarify does not work with vision right now + while True: + messages = ai.next(messages, user_input, step_name=curr_fn()) + msg = messages[-1].content.strip() + + if "nothing to clarify" in msg.lower(): + break + + if msg.lower().startswith("no"): + print("Nothing to clarify.") + break + + print('(answer in text, or "c" to move on)\n') + user_input = input("") + print() + + if not user_input or user_input == "c": + print("(letting gpt-engineer make its own assumptions)") + print() + messages = ai.next( + messages, + "Make your own assumptions and state them explicitly before starting", + step_name=curr_fn(), + ) + print() + + user_input += """ + \n\n + Is anything else unclear? If yes, ask another question.\n + Otherwise state: "Nothing to clarify" + """ + + print() + + messages = [ + SystemMessage(content=setup_sys_prompt(preprompts)), + ] + messages[ + 1: + ] # skip the first clarify message, which was the original clarify priming prompt + messages = ai.next( + messages, + preprompts["generate"].replace("FILE_FORMAT", preprompts["file_format"]), + step_name=curr_fn(), + ) + print() + chat = messages[-1].content.strip() + memory.log(CODE_GEN_LOG_FILE, "\n\n".join(x.pretty_repr() for x in messages)) + files_dict = chat_to_files_dict(chat) + return files_dict + + +def lite_gen( + ai: AI, prompt: Prompt, memory: BaseMemory, preprompts_holder: PrepromptsHolder +) -> FilesDict: + """ + Executes the AI model using the main prompt and saves the generated results to the specified workspace. + + Parameters + ---------- + ai : AI + An instance of the AI model. + prompt : str + The main prompt to feed to the AI model. + memory : BaseMemory + The memory instance where the generated code log is saved. + preprompts_holder : PrepromptsHolder + A holder for preprompt messages. + + Returns + ------- + FilesDict + A dictionary of file names to their contents generated by the AI. + + Notes + ----- + The function assumes the `ai.start` method and the `to_files` utility to be correctly + set up and functional. Ensure these prerequisites before invoking `lite_gen`. + """ + + preprompts = preprompts_holder.get_preprompts() + messages = ai.start( + prompt.to_langchain_content(), preprompts["file_format"], step_name=curr_fn() + ) + chat = messages[-1].content.strip() + memory.log(CODE_GEN_LOG_FILE, "\n\n".join(x.pretty_repr() for x in messages)) + files_dict = chat_to_files_dict(chat) + return files_dict diff --git a/gpt_engineer/tools/supported_languages.py b/gpt_engineer/tools/supported_languages.py new file mode 100644 index 0000000000000000000000000000000000000000..f17536c852d0840e685398ec6f22fcfff8f54daa --- /dev/null +++ b/gpt_engineer/tools/supported_languages.py @@ -0,0 +1,56 @@ +""" +This module defines the supported programming languages for document chunking. + +Variables: + SUPPORTED_LANGUAGES (list): A list of dictionaries defining supported languages. +""" + +SUPPORTED_LANGUAGES = [ + {"name": "Python", "extensions": [".py"], "tree_sitter_name": "python"}, + { + "name": "JavaScript", + "extensions": [".js", ".mjs"], + "tree_sitter_name": "javascript", + }, + {"name": "HTML", "extensions": [".html", ".htm"], "tree_sitter_name": "html"}, + {"name": "CSS", "extensions": [".css"], "tree_sitter_name": "css"}, + {"name": "Java", "extensions": [".java"], "tree_sitter_name": "java"}, + {"name": "C#", "extensions": [".cs"], "tree_sitter_name": "c_sharp"}, + { + "name": "TypeScript", + "extensions": [".ts", ".tsx"], + "tree_sitter_name": "typescript", + }, + {"name": "Ruby", "extensions": [".rb", ".erb"], "tree_sitter_name": "ruby"}, + { + "name": "PHP", + "extensions": [ + ".php", + ".phtml", + ".php3", + ".php4", + ".php5", + ".php7", + ".phps", + ".php-s", + ".pht", + ".phar", + ], + "tree_sitter_name": "php", + }, + {"name": "Go", "extensions": [".go"], "tree_sitter_name": "go"}, + {"name": "Kotlin", "extensions": [".kt", ".kts"], "tree_sitter_name": "kotlin"}, + {"name": "Rust", "extensions": [".rs"], "tree_sitter_name": "rust"}, + { + "name": "C++", + "extensions": [".cpp", ".cc", ".cxx", ".h", ".hpp", ".hxx"], + "tree_sitter_name": "cpp", + }, + {"name": "C", "extensions": [".c", ".h"], "tree_sitter_name": "c"} + # ---- the following are not supported by the current code chunker implementation ---- + # { + # "name": "Swift", + # "extensions": [".swift"], + # "tree_sitter_name": "swift" + # }, +] diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000000000000000000000000000000000000..78e9447720b021a18b68eae654efc9c79faac32b --- /dev/null +++ b/poetry.lock @@ -0,0 +1,5004 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "accessible-pygments" +version = "0.0.4" +description = "A collection of accessible pygments styles" +optional = false +python-versions = "*" +files = [ + {file = "accessible-pygments-0.0.4.tar.gz", hash = "sha256:e7b57a9b15958e9601c7e9eb07a440c813283545a20973f2574a5f453d0e953e"}, + {file = "accessible_pygments-0.0.4-py2.py3-none-any.whl", hash = "sha256:416c6d8c1ea1c5ad8701903a20fcedf953c6e720d64f33dc47bfb2d3f2fa4e8d"}, +] + +[package.dependencies] +pygments = ">=1.5" + +[[package]] +name = "aiohttp" +version = "3.9.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[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 = "alabaster" +version = "0.7.16" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.9" +files = [ + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, +] + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "anthropic" +version = "0.26.1" +description = "The official Python library for the anthropic API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anthropic-0.26.1-py3-none-any.whl", hash = "sha256:2812b9b250b551ed8a1f0a7e6ae3f005654098994f45ebca5b5808bd154c9628"}, + {file = "anthropic-0.26.1.tar.gz", hash = "sha256:26680ff781a6f678a30a1dccd0743631e602b23a47719439ffdef5335fa167d8"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +jiter = ">=0.1.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tokenizers = ">=0.13.0" +typing-extensions = ">=4.7,<5" + +[package.extras] +bedrock = ["boto3 (>=1.28.57)", "botocore (>=1.31.57)"] +vertex = ["google-auth (>=2,<3)"] + +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[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 = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[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.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +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 = "autodoc-pydantic" +version = "2.2.0" +description = "Seamlessly integrate pydantic models in your Sphinx documentation." +optional = false +python-versions = "<4.0.0,>=3.8.1" +files = [ + {file = "autodoc_pydantic-2.2.0-py3-none-any.whl", hash = "sha256:8c6a36fbf6ed2700ea9c6d21ea76ad541b621fbdf16b5a80ee04673548af4d95"}, +] + +[package.dependencies] +pydantic = ">=2.0,<3.0.0" +pydantic-settings = ">=2.0,<3.0.0" +Sphinx = ">=4.0" + +[package.extras] +docs = ["myst-parser (>=3.0.0,<4.0.0)", "sphinx-copybutton (>=0.5.0,<0.6.0)", "sphinx-rtd-theme (>=2.0.0,<3.0.0)", "sphinx-tabs (>=3,<4)", "sphinxcontrib-mermaid (>=0.9.0,<0.10.0)"] +erdantic = ["erdantic (<2.0)"] +linting = ["ruff (>=0.4.0,<0.5.0)"] +security = ["pip-audit (>=2.7.2,<3.0.0)"] +test = ["coverage (>=7,<8)", "defusedxml (>=0.7.1)", "pytest (>=8.0.0,<9.0.0)", "pytest-sugar (>=1.0.0,<2.0.0)"] +type-checking = ["mypy (>=1.9,<2.0)", "types-docutils (>=0.20,<0.21)", "typing-extensions (>=4.11,<5.0)"] + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "23.3.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +] + +[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\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "bleach" +version = "6.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, + {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.3)"] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "certifi" +version = "2024.7.4" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "chardet" +version = "5.2.0" +description = "Universal encoding detector for Python 3" +optional = false +python-versions = ">=3.7" +files = [ + {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, + {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[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 = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "coverage" +version = "7.5.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, + {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, + {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, + {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, + {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, + {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, + {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, + {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, + {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, + {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, + {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, + {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, + {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, + {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, + {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, + {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, + {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, + {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, + {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, + {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, + {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, + {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, + {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, + {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, + {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, + {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, + {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, + {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dataclasses-json" +version = "0.5.7" +description = "Easily serialize dataclasses to and from JSON" +optional = false +python-versions = ">=3.6" +files = [ + {file = "dataclasses-json-0.5.7.tar.gz", hash = "sha256:c2c11bc8214fbf709ffc369d11446ff6945254a7f09128154a7620613d8fda90"}, + {file = "dataclasses_json-0.5.7-py3-none-any.whl", hash = "sha256:bc285b5f892094c3a53d558858a88553dd6a61a11ab1a8128a0e554385dcc5dd"}, +] + +[package.dependencies] +marshmallow = ">=3.3.0,<4.0.0" +marshmallow-enum = ">=1.5.1,<2.0.0" +typing-inspect = ">=0.4.0" + +[package.extras] +dev = ["flake8", "hypothesis", "ipython", "mypy (>=0.710)", "portray", "pytest (>=6.2.3)", "simplejson", "types-dataclasses"] + +[[package]] +name = "datasets" +version = "2.19.0" +description = "HuggingFace community-driven open-source library of datasets" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "datasets-2.19.0-py3-none-any.whl", hash = "sha256:f57c5316e123d4721b970c68c1cb856505f289cda58f5557ffe745b49c011a8e"}, + {file = "datasets-2.19.0.tar.gz", hash = "sha256:0b47e08cc7af2c6800a42cadc4657b22a0afc7197786c8986d703c08d90886a6"}, +] + +[package.dependencies] +aiohttp = "*" +dill = ">=0.3.0,<0.3.9" +filelock = "*" +fsspec = {version = ">=2023.1.0,<=2024.3.1", extras = ["http"]} +huggingface-hub = ">=0.21.2" +multiprocess = "*" +numpy = ">=1.17" +packaging = "*" +pandas = "*" +pyarrow = ">=12.0.0" +pyarrow-hotfix = "*" +pyyaml = ">=5.1" +requests = ">=2.19.0" +tqdm = ">=4.62.1" +xxhash = "*" + +[package.extras] +apache-beam = ["apache-beam (>=2.26.0)"] +audio = ["librosa", "soundfile (>=0.12.1)"] +benchmarks = ["tensorflow (==2.12.0)", "torch (==2.0.1)", "transformers (==4.30.1)"] +dev = ["Pillow (>=6.2.1)", "absl-py", "apache-beam (>=2.26.0)", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.6.4)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "ruff (>=0.3.0)", "s3fs", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tensorflow (>=2.6.0)", "tiktoken", "torch", "torch (>=2.0.0)", "transformers", "typing-extensions (>=4.6.1)", "zstandard"] +docs = ["s3fs", "tensorflow (>=2.6.0)", "torch", "transformers"] +jax = ["jax (>=0.3.14)", "jaxlib (>=0.3.14)"] +metrics-tests = ["Werkzeug (>=1.0.1)", "accelerate", "bert-score (>=0.3.6)", "jiwer", "langdetect", "mauve-text", "nltk", "requests-file (>=1.5.1)", "rouge-score", "sacrebleu", "sacremoses", "scikit-learn", "scipy", "sentencepiece", "seqeval", "six (>=1.15.0,<1.16.0)", "spacy (>=3.0.0)", "texttable (>=1.6.3)", "tldextract", "tldextract (>=3.1.0)", "toml (>=0.10.1)", "typer (<0.5.0)"] +quality = ["ruff (>=0.3.0)"] +s3 = ["s3fs"] +tensorflow = ["tensorflow (>=2.6.0)"] +tensorflow-gpu = ["tensorflow (>=2.6.0)"] +tests = ["Pillow (>=6.2.1)", "absl-py", "apache-beam (>=2.26.0)", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.6.4)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tensorflow (>=2.6.0)", "tiktoken", "torch (>=2.0.0)", "transformers", "typing-extensions (>=4.6.1)", "zstandard"] +torch = ["torch"] +vision = ["Pillow (>=6.2.1)"] + +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "deprecation" +version = "2.1.0" +description = "A library to handle automated deprecations" +optional = false +python-versions = "*" +files = [ + {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"}, + {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, +] + +[package.dependencies] +packaging = "*" + +[[package]] +name = "dill" +version = "0.3.8" +description = "serialize all of Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + +[[package]] +name = "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 = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, +] + +[[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 = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.13.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "fsspec" +version = "2024.3.1" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"}, + {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"}, +] + +[package.dependencies] +aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""} + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +devel = ["pytest", "pytest-cov"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +tqdm = ["tqdm"] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[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 = "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 = "huggingface-hub" +version = "0.22.2" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, + {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + +[[package]] +name = "identify" +version = "2.5.36" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, +] + +[package.extras] +license = ["ukkonen"] + +[[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 = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "7.1.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + +[[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 = "ipykernel" +version = "6.29.4" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, + {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.24.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.10" +files = [ + {file = "ipython-8.24.0-py3-none-any.whl", hash = "sha256:d7bf2f6c4314984e3e02393213bab8703cf163ede39672ce5918c51fe253a2a3"}, + {file = "ipython-8.24.0.tar.gz", hash = "sha256:010db3f8a728a578bb641fdd06c063b9fb8e96a9464c63aec6310fbcb5e80501"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5.13.0" +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} + +[package.extras] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] +kernel = ["ipykernel"] +matplotlib = ["matplotlib"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] + +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jiter" +version = "0.4.0" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jiter-0.4.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4aa6226d82a4a4505078c0bd5947bad65399635fc5cd4b226512e41753624edf"}, + {file = "jiter-0.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:947111ac906740a948e7b63799481acd3d5ef666ccb178d146e25718640b7408"}, + {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69572ffb4e84ae289a7422b9af4ea123cae2ce0772228859b37d4b26b4bc92ea"}, + {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba6046cbb5d1baa5a781b846f7e5438596a332f249a857d63f86ef5d1d9563b0"}, + {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4f346e54602782e66d07df0d1c7389384fd93680052ed6170da2c6dc758409e"}, + {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49110ce693f07e97d61089d894cea05a0b9894d5ccc6ac6fc583028726c8c8af"}, + {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e358df6fd129f3a4e087539f086355ad0107e5da16dbc8bc857d94222eaeed5"}, + {file = "jiter-0.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb852ca39a48f3c049def56f0d1771b32e948e4f429a782d14ef4cc64cfd26e"}, + {file = "jiter-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:44dc045decb2545bffe2da04ea4c36d9438d3f3d49fc47ed423ea75c352b712e"}, + {file = "jiter-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:413adb15372ac63db04373240f40925788e4282c997eeafc2040530049a0a599"}, + {file = "jiter-0.4.0-cp310-none-win32.whl", hash = "sha256:0b48ea71673a97b897e4b94bbc871e62495a5a85f836c9f90712a4c70aa3ef7e"}, + {file = "jiter-0.4.0-cp310-none-win_amd64.whl", hash = "sha256:6a1c84b44afafaf0ba6223679cf17af664b889da14da31d8af3595fd977d96fa"}, + {file = "jiter-0.4.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b2cc498345fa37ca23fbc20271a553aa46e6eb00924600f49b7dc4b2aa8952ee"}, + {file = "jiter-0.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69f7221ac09ab421abf04f89942026868297c568133998fb181bcf435760cbf3"}, + {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7d01c52f3e5a56ae73af36bd13797dd1a56711eb522748e5e84d15425b3f10"}, + {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:39be97d5ce0c4d0dae28c23c03a0af0501a725589427e99763f99c42e18aa402"}, + {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eac2ed1ec1e577b92b7ea2d4e6de8aec0c1164defd8af8affdc8ec0f0ec2904a"}, + {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6258837d184c92c9cb91c983c310ad7269d41afb49d34f00ca9246e073943a03"}, + {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c2a77b066bf17a4d021e238e8351058cfa56b90ac04f2522d120dc64ea055"}, + {file = "jiter-0.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2df939f792c7a40e55f36700417db551b9f6b84d348990fa0f2c608adeb1f11b"}, + {file = "jiter-0.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cb1b09b16d40cf9ba1d11ba11e5b96ad29286a6a1c4ad5e6a2aef5e352a89f5d"}, + {file = "jiter-0.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0efb4208889ebdbf933bf08dbcbc16e64ffd34c8e2b28044ee142789a9dc3a67"}, + {file = "jiter-0.4.0-cp311-none-win32.whl", hash = "sha256:20545ac1b68e7e5b066a1e8347840c9cebdd02ace65faae2e655fc02ec5c915c"}, + {file = "jiter-0.4.0-cp311-none-win_amd64.whl", hash = "sha256:6b300f9887c8e4431cd03a974ea3e4f9958885636003c3864220a9b2d2f8462b"}, + {file = "jiter-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:923432a0563bbae404ff25bb010e348514a69bfab979f2f8119b23b625dbf6d9"}, + {file = "jiter-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab8bb0ec8b97cec4422dc8b37b525442d969244488c805b834609ab0ccd788e2"}, + {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b857adb127b9c533907226791eafa79c5038c3eb5a477984994bf7c4715ba518"}, + {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2609cc0d1d8d470e921ff9a604afeb4c701bbe13e00bd9834d5aa6e7ea732a9b"}, + {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d39e99f8b7df46a119b6f84321f6ba01f16fa46abfa765d44c05c486d8e66829"}, + {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56de8b518ebfe76a70f856741f6de248ce396c50a87acef827b6e8388e3a502d"}, + {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488b7e777be47f67ce1a1f8f8eb907f9bbd81af5c03784a9bab09d025c250233"}, + {file = "jiter-0.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ea35e0ecbb5dadd457855eb980dcc548c14cf5341bcd22a43814cb56f2bcc79"}, + {file = "jiter-0.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e1a9e9ee69c80b63951c93226b68d0e955953f64fe758bad2afe7ef7f9016af9"}, + {file = "jiter-0.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:78e2f3cc2a32a21d43ccc5afcf66f5d17e827ccc4e6d21c0b353bdad2c7dcc9c"}, + {file = "jiter-0.4.0-cp312-none-win32.whl", hash = "sha256:eeaa7a2b47a99f4ebbb4142bb58b95617e09f24c87570f6a57d2770687c9ddbe"}, + {file = "jiter-0.4.0-cp312-none-win_amd64.whl", hash = "sha256:8d4a78b385b93ff59a67215d26000fcb4789a388fca3730d1b60fab17fc81e3c"}, + {file = "jiter-0.4.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ebf20a3fac1089ce26963bf04140da0f803d55332ec69d59c5a87cf1a87d29c4"}, + {file = "jiter-0.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d62244ffc6a168187452277adeefb7b2c30170689c6bf543a51e98e8c17ddab7"}, + {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40b2cde77446a41cec595739fd168be87edff2428eaf7c3438231224dd0ab7a5"}, + {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e51fc0a22021ec8905b9b00a2f7d25756f2ff7a653e35a790a2067ae126b51f6"}, + {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a56e6f980b89d7cfe5c43811dcf52d6f37b319428a4540511235dafda9ea7808"}, + {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fec16adab8d3d3d6d74e3711a1f380836ebeab2a20e3f88cfe2ec5094d8b84"}, + {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e3de515801c954e8f1dc1f575282a4a86df9e782d4993ea1ed2be9a8dedaa0"}, + {file = "jiter-0.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17e0ad8abf0bb04d81810eaeaab35d2c99b5da11fcd1058e0a389607ff6503b0"}, + {file = "jiter-0.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8dc0132b728f3b3e90ff0d1874504cd49c78f3553bf3745168a7fc0b4cf674e1"}, + {file = "jiter-0.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81a883104aa96e494d3d28eaf7070780d03ecee8ccfdfaf7e4899710340c47f1"}, + {file = "jiter-0.4.0-cp38-none-win32.whl", hash = "sha256:a044c53ab1aaa4af624ac9574181b5bad8e260aea7e03104738156511433deba"}, + {file = "jiter-0.4.0-cp38-none-win_amd64.whl", hash = "sha256:d920035c869053e3d9a0b3ff94384d16a8ef5fde3dea55f97bd29916f6e27554"}, + {file = "jiter-0.4.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:091e978f4e586a2f1c69bf940d45f4e6a23455877172a0ab7d6de04a3b119299"}, + {file = "jiter-0.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79134b2d601309bcbe3304a262d7d228ad61d53c80883231c637773000a6d683"}, + {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c471473e0b05058b5d729ff04271b6d45a575ac8bd9948563268c734b380ac7e"}, + {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb84b8930da8b32b0b1fdff9817e2c4b47e8981b5647ad11c4975403416e4112"}, + {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f2805e28941751ebfe0948596a64cde4cfb9b84bea5282affd020063e659c96"}, + {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42ef59f9e513bf081a8b5c5578933ea9c3a63e559e6e3501a3e72edcd456ff5e"}, + {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae12e3906f9e565120ab569de261b738e3a1ec50c40e30c67499e4f893e9a8c"}, + {file = "jiter-0.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:264dc1324f45a793bc89af4f653225229eb17bca9ec7107dce6c8fb4fe68d20f"}, + {file = "jiter-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9a1c172ec47d846e25881dfbd52438ddb690da4ea04d185e477abd3db6c32f8a"}, + {file = "jiter-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ccde31d0bc114aedad0dbd71b7f63ba0f0eecd7ec9ae1926a0ca01c1eb2854e7"}, + {file = "jiter-0.4.0-cp39-none-win32.whl", hash = "sha256:13139b05792fbc13a0f9a5b4c89823ea0874141decae1b8f693f12bb1d28e061"}, + {file = "jiter-0.4.0-cp39-none-win_amd64.whl", hash = "sha256:3a729b2631c6d5551a41069697415fee9659c3eadc9ab87369376ba51930cd00"}, + {file = "jiter-0.4.0.tar.gz", hash = "sha256:68203e02e0419bc3eca717c580c2d8f615aeee1150e2a1fb68d6600a7e52a37c"}, +] + +[[package]] +name = "jsonpatch" +version = "1.33" +description = "Apply JSON-Patches (RFC 6902)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, + {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, +] + +[package.dependencies] +jsonpointer = ">=1.9" + +[[package]] +name = "jsonpointer" +version = "2.4" +description = "Identify specific nodes in a JSON document (RFC 6901)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, + {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, +] + +[[package]] +name = "jsonschema" +version = "4.21.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, +] + +[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.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-cache" +version = "1.0.0" +description = "A defined interface for working with a cache of jupyter notebooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "jupyter_cache-1.0.0-py3-none-any.whl", hash = "sha256:594b1c4e29b488b36547e12477645f489dbdc62cc939b2408df5679f79245078"}, + {file = "jupyter_cache-1.0.0.tar.gz", hash = "sha256:d0fa7d7533cd5798198d8889318269a8c1382ed3b22f622c09a9356521f48687"}, +] + +[package.dependencies] +attrs = "*" +click = "*" +importlib-metadata = "*" +nbclient = ">=0.2" +nbformat = "*" +pyyaml = "*" +sqlalchemy = ">=1.3.12,<3" +tabulate = "*" + +[package.extras] +cli = ["click-log"] +code-style = ["pre-commit (>=2.12)"] +rtd = ["ipykernel", "jupytext", "myst-nb", "nbdime", "sphinx-book-theme", "sphinx-copybutton"] +testing = ["coverage", "ipykernel", "jupytext", "matplotlib", "nbdime", "nbformat (>=5.1)", "numpy", "pandas", "pytest (>=6,<8)", "pytest-cov", "pytest-regressions", "sympy"] + +[[package]] +name = "jupyter-client" +version = "8.6.1" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, + {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, +] + +[package.dependencies] +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +description = "Pygments theme using JupyterLab CSS variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, + {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, +] + +[[package]] +name = "langchain" +version = "0.2.1" +description = "Building applications with LLMs through composability" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain-0.2.1-py3-none-any.whl", hash = "sha256:3e13bf97c5717bce2c281f5117e8778823e8ccf62d949e73d3869448962b1c97"}, + {file = "langchain-0.2.1.tar.gz", hash = "sha256:5758a315e1ac92eb26dafec5ad0fafa03cafa686aba197d5bb0b1dd28cc03ebe"}, +] + +[package.dependencies] +aiohttp = ">=3.8.3,<4.0.0" +async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} +langchain-core = ">=0.2.0,<0.3.0" +langchain-text-splitters = ">=0.2.0,<0.3.0" +langsmith = ">=0.1.17,<0.2.0" +numpy = ">=1,<2" +pydantic = ">=1,<3" +PyYAML = ">=5.3" +requests = ">=2,<3" +SQLAlchemy = ">=1.4,<3" +tenacity = ">=8.1.0,<9.0.0" + +[package.extras] +azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"] +clarifai = ["clarifai (>=9.1.0)"] +cli = ["typer (>=0.9.0,<0.10.0)"] +cohere = ["cohere (>=4,<6)"] +docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"] +embeddings = ["sentence-transformers (>=2,<3)"] +extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.1,<0.2)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] +javascript = ["esprima (>=4.0.1,<5.0.0)"] +llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"] +openai = ["openai (<2)", "tiktoken (>=0.7,<1.0)"] +qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"] +text-helpers = ["chardet (>=5.1.0,<6.0.0)"] + +[[package]] +name = "langchain-anthropic" +version = "0.1.13" +description = "An integration package connecting AnthropicMessages and LangChain" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_anthropic-0.1.13-py3-none-any.whl", hash = "sha256:121f6f480da7685c239573d98322adb94fe486d40651ac341637f65da36881de"}, + {file = "langchain_anthropic-0.1.13.tar.gz", hash = "sha256:32e7ac51e1874c47e1a20493e75f5bfc88b0ffeaf5f1aed6091547e1ae44bb85"}, +] + +[package.dependencies] +anthropic = ">=0.26.0,<1" +defusedxml = ">=0.7.1,<0.8.0" +langchain-core = ">=0.1.43,<0.3" + +[[package]] +name = "langchain-community" +version = "0.2.1" +description = "Community contributed LangChain integrations." +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_community-0.2.1-py3-none-any.whl", hash = "sha256:b834e2c5ded6903b839fcaf566eee90a0ffae53405a0f7748202725e701d39cd"}, + {file = "langchain_community-0.2.1.tar.gz", hash = "sha256:079942e8f15da975769ccaae19042b7bba5481c42020bbbd7d8cad73a9393261"}, +] + +[package.dependencies] +aiohttp = ">=3.8.3,<4.0.0" +dataclasses-json = ">=0.5.7,<0.7" +langchain = ">=0.2.0,<0.3.0" +langchain-core = ">=0.2.0,<0.3.0" +langsmith = ">=0.1.0,<0.2.0" +numpy = ">=1,<2" +PyYAML = ">=5.3" +requests = ">=2,<3" +SQLAlchemy = ">=1.4,<3" +tenacity = ">=8.1.0,<9.0.0" + +[package.extras] +cli = ["typer (>=0.9.0,<0.10.0)"] +extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "azure-identity (>=1.15.0,<2.0.0)", "azure-search-documents (==11.4.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.6,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpathlib (>=0.18,<0.19)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "httpx-sse (>=0.4.0,<0.5.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "oracledb (>=2.2.0,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "premai (>=0.3.25,<0.4.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pyjwt (>=2.8.0,<3.0.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "vdms (>=0.0.20,<0.0.21)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] + +[[package]] +name = "langchain-core" +version = "0.2.1" +description = "Building applications with LLMs through composability" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_core-0.2.1-py3-none-any.whl", hash = "sha256:3521e1e573988c47399fca9739270c5d34f8ecec147253ad829eb9ff288f76d5"}, + {file = "langchain_core-0.2.1.tar.gz", hash = "sha256:49383126168d934559a543ce812c485048d9e6ac9b6798fbf3d4a72b6bba5b0c"}, +] + +[package.dependencies] +jsonpatch = ">=1.33,<2.0" +langsmith = ">=0.1.0,<0.2.0" +packaging = ">=23.2,<24.0" +pydantic = ">=1,<3" +PyYAML = ">=5.3" +tenacity = ">=8.1.0,<9.0.0" + +[package.extras] +extended-testing = ["jinja2 (>=3,<4)"] + +[[package]] +name = "langchain-openai" +version = "0.1.7" +description = "An integration package connecting OpenAI and LangChain" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_openai-0.1.7-py3-none-any.whl", hash = "sha256:39c3cb22bb739900ae8294d4d9939a6138c0ca7ad11198e57038eb14c08d04ec"}, + {file = "langchain_openai-0.1.7.tar.gz", hash = "sha256:fd7e1c33ba8e2cab4b2154f3a2fd4a0d9cc6518b41cf49bb87255f9f732a4896"}, +] + +[package.dependencies] +langchain-core = ">=0.1.46,<0.3" +openai = ">=1.24.0,<2.0.0" +tiktoken = ">=0.7,<1" + +[[package]] +name = "langchain-text-splitters" +version = "0.2.0" +description = "LangChain text splitting utilities" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_text_splitters-0.2.0-py3-none-any.whl", hash = "sha256:7b4c6a45f8471630a882b321e138329b6897102a5bc62f4c12be1c0b05bb9199"}, + {file = "langchain_text_splitters-0.2.0.tar.gz", hash = "sha256:b32ab4f7397f7d42c1fa3283fefc2547ba356bd63a68ee9092865e5ad83c82f9"}, +] + +[package.dependencies] +langchain-core = ">=0.2.0,<0.3.0" + +[package.extras] +extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"] + +[[package]] +name = "langsmith" +version = "0.1.51" +description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langsmith-0.1.51-py3-none-any.whl", hash = "sha256:1e7363a3f472ecf02a1d91f6dbacde25519554b98c490be71716fcffaab0ca6b"}, + {file = "langsmith-0.1.51.tar.gz", hash = "sha256:b99b40a8c00e66174540865caa61412622fa1dc4f02602965364919c90528f97"}, +] + +[package.dependencies] +orjson = ">=3.9.14,<4.0.0" +pydantic = ">=1,<3" +requests = ">=2,<3" + +[[package]] +name = "linkchecker" +version = "10.4.0" +description = "check links in web documents or full websites" +optional = false +python-versions = ">=3.9" +files = [ + {file = "LinkChecker-10.4.0-py3-none-any.whl", hash = "sha256:83931f071916ebc2da1b5ef9b25d65360881b148f27ad9308efe1afe569feb8d"}, + {file = "LinkChecker-10.4.0.tar.gz", hash = "sha256:6250dd29261ddd6df4be9e979bd47a759a6cfafbcaf0f7926afbe2a230aa761a"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.8.1" +dnspython = ">=2.0" +requests = ">=2.20" + +[[package]] +name = "markdown" +version = "3.6" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, + {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, +] + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markdown-include" +version = "0.8.1" +description = "A Python-Markdown extension which provides an 'include' function" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-include-0.8.1.tar.gz", hash = "sha256:1d0623e0fc2757c38d35df53752768356162284259d259c486b4ab6285cdbbe3"}, + {file = "markdown_include-0.8.1-py3-none-any.whl", hash = "sha256:32f0635b9cfef46997b307e2430022852529f7a5b87c0075c504283e7cc7db53"}, +] + +[package.dependencies] +markdown = ">=3.0" + +[package.extras] +tests = ["pytest"] + +[[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 = "marshmallow" +version = "3.21.1" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, + {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] +docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-enum" +version = "1.5.1" +description = "Enum field for Marshmallow" +optional = false +python-versions = "*" +files = [ + {file = "marshmallow-enum-1.5.1.tar.gz", hash = "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"}, + {file = "marshmallow_enum-1.5.1-py2.py3-none-any.whl", hash = "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072"}, +] + +[package.dependencies] +marshmallow = ">=2.0.0" + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[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 = "mistune" +version = "3.0.2" +description = "A sane and fast Markdown parser with useful plugins and renderers" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, + {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, +] + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +optional = false +python-versions = "*" +files = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +description = "better multiprocessing and multithreading in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"}, + {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"}, + {file = "multiprocess-0.70.16-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37b55f71c07e2d741374998c043b9520b626a8dddc8b3129222ca4f1a06ef67a"}, + {file = "multiprocess-0.70.16-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba8c31889abf4511c7308a8c52bb4a30b9d590e7f58523302ba00237702ca054"}, + {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"}, + {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"}, + {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"}, + {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"}, + {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"}, + {file = "multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435"}, + {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"}, + {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"}, +] + +[package.dependencies] +dill = ">=0.3.8" + +[[package]] +name = "mypy" +version = "1.3.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mypy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d"}, + {file = "mypy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85"}, + {file = "mypy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd"}, + {file = "mypy-1.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152"}, + {file = "mypy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228"}, + {file = "mypy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd"}, + {file = "mypy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c"}, + {file = "mypy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae"}, + {file = "mypy-1.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca"}, + {file = "mypy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf"}, + {file = "mypy-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409"}, + {file = "mypy-1.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929"}, + {file = "mypy-1.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a"}, + {file = "mypy-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee"}, + {file = "mypy-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f"}, + {file = "mypy-1.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb"}, + {file = "mypy-1.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4"}, + {file = "mypy-1.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305"}, + {file = "mypy-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf"}, + {file = "mypy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8"}, + {file = "mypy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703"}, + {file = "mypy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017"}, + {file = "mypy-1.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e"}, + {file = "mypy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a"}, + {file = "mypy-1.3.0-py3-none-any.whl", hash = "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897"}, + {file = "mypy-1.3.0.tar.gz", hash = "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "myst-nb" +version = "1.1.0" +description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." +optional = false +python-versions = ">=3.9" +files = [ + {file = "myst_nb-1.1.0-py3-none-any.whl", hash = "sha256:0ac29b2a346f9a1257edbfb5d6c47d528728a37e6b9438903c2821f69fda9235"}, + {file = "myst_nb-1.1.0.tar.gz", hash = "sha256:9278840e844f5d780b5acc5400cbf63d97caaccf8eb442a55ebd9a03e2522d5e"}, +] + +[package.dependencies] +importlib_metadata = "*" +ipykernel = "*" +ipython = "*" +jupyter-cache = ">=0.5" +myst-parser = ">=1.0.0" +nbclient = "*" +nbformat = ">=5.0" +pyyaml = "*" +sphinx = ">=5" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<3.1.0)", "ipykernel (>=5.5,<7.0)", "ipywidgets", "jupytext (>=1.11.2,<1.16.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3)", "sphinx-copybutton", "sphinx-design (>=0.4.0,<0.5.0)", "sphinxcontrib-bibtex", "sympy"] +testing = ["beautifulsoup4", "coverage (>=6.4,<8.0)", "ipykernel (>=5.5,<7.0)", "ipython (!=8.1.0,<8.17)", "ipywidgets (>=8)", "jupytext (>=1.11.2,<1.16.0)", "matplotlib (==3.7.*)", "nbdime", "numpy", "pandas (==1.5.*)", "pyarrow", "pytest (>=7.1,<8.0)", "pytest-cov (>=3,<5)", "pytest-param-files (>=0.3.3,<0.4.0)", "pytest-regressions", "sympy (>=1.10.1)"] + +[[package]] +name = "myst-parser" +version = "3.0.0" +description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," +optional = false +python-versions = ">=3.8" +files = [ + {file = "myst_parser-3.0.0-py3-none-any.whl", hash = "sha256:8ee926557b8e4c2940a1e62c5720e1667cfaf8480b94b1b9c77dc38e31d104aa"}, + {file = "myst_parser-3.0.0.tar.gz", hash = "sha256:0b4ae0b33a45800a748260cb40348c37089a8a456c35120609240bd1b32f9255"}, +] + +[package.dependencies] +docutils = ">=0.18,<0.22" +jinja2 = "*" +markdown-it-py = ">=3.0,<4.0" +mdit-py-plugins = ">=0.4,<1.0" +pyyaml = "*" +sphinx = ">=6,<8" + +[package.extras] +code-style = ["pre-commit (>=3.0,<4.0)"] +linkify = ["linkify-it-py (>=2.0,<3.0)"] +rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] +testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] + +[[package]] +name = "nbclient" +version = "0.10.0" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, + {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, +] + +[package.dependencies] +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +nbformat = ">=5.1" +traitlets = ">=5.4" + +[package.extras] +dev = ["pre-commit"] +docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbconvert" +version = "7.16.3" +description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"}, + {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +bleach = "!=5.0.0" +defusedxml = "*" +jinja2 = ">=3.0" +jupyter-core = ">=4.7" +jupyterlab-pygments = "*" +markupsafe = ">=2.0" +mistune = ">=2.0.3,<4" +nbclient = ">=0.5.0" +nbformat = ">=5.7" +packaging = "*" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +tinycss2 = "*" +traitlets = ">=5.1" + +[package.extras] +all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] +docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] +qtpdf = ["nbconvert[qtpng]"] +qtpng = ["pyqtwebengine (>=5.15)"] +serve = ["tornado (>=6.1)"] +test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] +webpdf = ["playwright"] + +[[package]] +name = "nbformat" +version = "5.10.4" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] + +[package.dependencies] +fastjsonschema = ">=2.15" +jsonschema = ">=2.6" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nbsphinx" +version = "0.9.3" +description = "Jupyter Notebook Tools for Sphinx" +optional = false +python-versions = ">=3.6" +files = [ + {file = "nbsphinx-0.9.3-py3-none-any.whl", hash = "sha256:6e805e9627f4a358bd5720d5cbf8bf48853989c79af557afd91a5f22e163029f"}, + {file = "nbsphinx-0.9.3.tar.gz", hash = "sha256:ec339c8691b688f8676104a367a4b8cf3ea01fd089dc28d24dec22d563b11562"}, +] + +[package.dependencies] +docutils = "*" +jinja2 = "*" +nbconvert = "!=5.4" +nbformat = "*" +sphinx = ">=1.8" +traitlets = ">=5" + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + +[[package]] +name = "nodeenv" +version = "1.8.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "openai" +version = "1.30.1" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.30.1-py3-none-any.whl", hash = "sha256:c9fb3c3545c118bbce8deb824397b9433a66d0d0ede6a96f7009c95b76de4a46"}, + {file = "openai-1.30.1.tar.gz", hash = "sha256:4f85190e577cba0b066e1950b8eb9b11d25bc7ebcc43a86b326ce1bfa564ec74"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.7,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + +[[package]] +name = "orjson" +version = "3.10.1" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8ec2fc456d53ea4a47768f622bb709be68acd455b0c6be57e91462259741c4f3"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e900863691d327758be14e2a491931605bd0aded3a21beb6ce133889830b659"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab6ecbd6fe57785ebc86ee49e183f37d45f91b46fc601380c67c5c5e9c0014a2"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af7c68b01b876335cccfb4eee0beef2b5b6eae1945d46a09a7c24c9faac7a77"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:915abfb2e528677b488a06eba173e9d7706a20fdfe9cdb15890b74ef9791b85e"}, + {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3fd4a36eff9c63d25503b439531d21828da9def0059c4f472e3845a081aa0b"}, + {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d229564e72cfc062e6481a91977a5165c5a0fdce11ddc19ced8471847a67c517"}, + {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e00495b18304173ac843b5c5fbea7b6f7968564d0d49bef06bfaeca4b656f4e"}, + {file = "orjson-3.10.1-cp310-none-win32.whl", hash = "sha256:fd78ec55179545c108174ba19c1795ced548d6cac4d80d014163033c047ca4ea"}, + {file = "orjson-3.10.1-cp310-none-win_amd64.whl", hash = "sha256:50ca42b40d5a442a9e22eece8cf42ba3d7cd4cd0f2f20184b4d7682894f05eec"}, + {file = "orjson-3.10.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b345a3d6953628df2f42502297f6c1e1b475cfbf6268013c94c5ac80e8abc04c"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caa7395ef51af4190d2c70a364e2f42138e0e5fcb4bc08bc9b76997659b27dab"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b01d701decd75ae092e5f36f7b88a1e7a1d3bb7c9b9d7694de850fb155578d5a"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5028981ba393f443d8fed9049211b979cadc9d0afecf162832f5a5b152c6297"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31ff6a222ea362b87bf21ff619598a4dc1106aaafaea32b1c4876d692891ec27"}, + {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e852a83d7803d3406135fb7a57cf0c1e4a3e73bac80ec621bd32f01c653849c5"}, + {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2567bc928ed3c3fcd90998009e8835de7c7dc59aabcf764b8374d36044864f3b"}, + {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4ce98cac60b7bb56457bdd2ed7f0d5d7f242d291fdc0ca566c83fa721b52e92d"}, + {file = "orjson-3.10.1-cp311-none-win32.whl", hash = "sha256:813905e111318acb356bb8029014c77b4c647f8b03f314e7b475bd9ce6d1a8ce"}, + {file = "orjson-3.10.1-cp311-none-win_amd64.whl", hash = "sha256:03a3ca0b3ed52bed1a869163a4284e8a7b0be6a0359d521e467cdef7e8e8a3ee"}, + {file = "orjson-3.10.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f02c06cee680b1b3a8727ec26c36f4b3c0c9e2b26339d64471034d16f74f4ef5"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1aa2f127ac546e123283e437cc90b5ecce754a22306c7700b11035dad4ccf85"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2cf29b4b74f585225196944dffdebd549ad2af6da9e80db7115984103fb18a96"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1b130c20b116f413caf6059c651ad32215c28500dce9cd029a334a2d84aa66f"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d31f9a709e6114492136e87c7c6da5e21dfedebefa03af85f3ad72656c493ae9"}, + {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1d169461726f271ab31633cf0e7e7353417e16fb69256a4f8ecb3246a78d6e"}, + {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57c294d73825c6b7f30d11c9e5900cfec9a814893af7f14efbe06b8d0f25fba9"}, + {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7f11dbacfa9265ec76b4019efffabaabba7a7ebf14078f6b4df9b51c3c9a8ea"}, + {file = "orjson-3.10.1-cp312-none-win32.whl", hash = "sha256:d89e5ed68593226c31c76ab4de3e0d35c760bfd3fbf0a74c4b2be1383a1bf123"}, + {file = "orjson-3.10.1-cp312-none-win_amd64.whl", hash = "sha256:aa76c4fe147fd162107ce1692c39f7189180cfd3a27cfbc2ab5643422812da8e"}, + {file = "orjson-3.10.1-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a2c6a85c92d0e494c1ae117befc93cf8e7bca2075f7fe52e32698da650b2c6d1"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9813f43da955197d36a7365eb99bed42b83680801729ab2487fef305b9ced866"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec917b768e2b34b7084cb6c68941f6de5812cc26c6f1a9fecb728e36a3deb9e8"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5252146b3172d75c8a6d27ebca59c9ee066ffc5a277050ccec24821e68742fdf"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:536429bb02791a199d976118b95014ad66f74c58b7644d21061c54ad284e00f4"}, + {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dfed3c3e9b9199fb9c3355b9c7e4649b65f639e50ddf50efdf86b45c6de04b5"}, + {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2b230ec35f188f003f5b543644ae486b2998f6afa74ee3a98fc8ed2e45960afc"}, + {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:01234249ba19c6ab1eb0b8be89f13ea21218b2d72d496ef085cfd37e1bae9dd8"}, + {file = "orjson-3.10.1-cp38-none-win32.whl", hash = "sha256:8a884fbf81a3cc22d264ba780920d4885442144e6acaa1411921260416ac9a54"}, + {file = "orjson-3.10.1-cp38-none-win_amd64.whl", hash = "sha256:dab5f802d52b182163f307d2b1f727d30b1762e1923c64c9c56dd853f9671a49"}, + {file = "orjson-3.10.1-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a51fd55d4486bc5293b7a400f9acd55a2dc3b5fc8420d5ffe9b1d6bb1a056a5e"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53521542a6db1411b3bfa1b24ddce18605a3abdc95a28a67b33f9145f26aa8f2"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27d610df96ac18ace4931411d489637d20ab3b8f63562b0531bba16011998db0"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79244b1456e5846d44e9846534bd9e3206712936d026ea8e6a55a7374d2c0694"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d751efaa8a49ae15cbebdda747a62a9ae521126e396fda8143858419f3b03610"}, + {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ff69c620a4fff33267df70cfd21e0097c2a14216e72943bd5414943e376d77"}, + {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebc58693464146506fde0c4eb1216ff6d4e40213e61f7d40e2f0dde9b2f21650"}, + {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5be608c3972ed902e0143a5b8776d81ac1059436915d42defe5c6ae97b3137a4"}, + {file = "orjson-3.10.1-cp39-none-win32.whl", hash = "sha256:4ae10753e7511d359405aadcbf96556c86e9dbf3a948d26c2c9f9a150c52b091"}, + {file = "orjson-3.10.1-cp39-none-win_amd64.whl", hash = "sha256:fb5bc4caa2c192077fdb02dce4e5ef8639e7f20bec4e3a834346693907362932"}, + {file = "orjson-3.10.1.tar.gz", hash = "sha256:a883b28d73370df23ed995c466b4f6c708c1f7a9bdc400fe89165c96c7603204"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pandas" +version = "2.2.2" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +description = "Utilities for writing pandoc filters in python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, + {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, +] + +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "platformdirs" +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[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 = "pre-commit" +version = "3.3.3" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, + {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "pyarrow" +version = "16.0.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyarrow-16.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:22a1fdb1254e5095d629e29cd1ea98ed04b4bbfd8e42cc670a6b639ccc208b60"}, + {file = "pyarrow-16.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:574a00260a4ed9d118a14770edbd440b848fcae5a3024128be9d0274dbcaf858"}, + {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0815d0ddb733b8c1b53a05827a91f1b8bde6240f3b20bf9ba5d650eb9b89cdf"}, + {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df0080339387b5d30de31e0a149c0c11a827a10c82f0c67d9afae3981d1aabb7"}, + {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:edf38cce0bf0dcf726e074159c60516447e4474904c0033f018c1f33d7dac6c5"}, + {file = "pyarrow-16.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:91d28f9a40f1264eab2af7905a4d95320ac2f287891e9c8b0035f264fe3c3a4b"}, + {file = "pyarrow-16.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:99af421ee451a78884d7faea23816c429e263bd3618b22d38e7992c9ce2a7ad9"}, + {file = "pyarrow-16.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:d22d0941e6c7bafddf5f4c0662e46f2075850f1c044bf1a03150dd9e189427ce"}, + {file = "pyarrow-16.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:266ddb7e823f03733c15adc8b5078db2df6980f9aa93d6bb57ece615df4e0ba7"}, + {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cc23090224b6594f5a92d26ad47465af47c1d9c079dd4a0061ae39551889efe"}, + {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56850a0afe9ef37249d5387355449c0f94d12ff7994af88f16803a26d38f2016"}, + {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:705db70d3e2293c2f6f8e84874b5b775f690465798f66e94bb2c07bab0a6bb55"}, + {file = "pyarrow-16.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5448564754c154997bc09e95a44b81b9e31ae918a86c0fcb35c4aa4922756f55"}, + {file = "pyarrow-16.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:729f7b262aa620c9df8b9967db96c1575e4cfc8c25d078a06968e527b8d6ec05"}, + {file = "pyarrow-16.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fb8065dbc0d051bf2ae2453af0484d99a43135cadabacf0af588a3be81fbbb9b"}, + {file = "pyarrow-16.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:20ce707d9aa390593ea93218b19d0eadab56390311cb87aad32c9a869b0e958c"}, + {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5823275c8addbbb50cd4e6a6839952682a33255b447277e37a6f518d6972f4e1"}, + {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ab8b9050752b16a8b53fcd9853bf07d8daf19093533e990085168f40c64d978"}, + {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:42e56557bc7c5c10d3e42c3b32f6cff649a29d637e8f4e8b311d334cc4326730"}, + {file = "pyarrow-16.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2a7abdee4a4a7cfa239e2e8d721224c4b34ffe69a0ca7981354fe03c1328789b"}, + {file = "pyarrow-16.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ef2f309b68396bcc5a354106741d333494d6a0d3e1951271849787109f0229a6"}, + {file = "pyarrow-16.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:ed66e5217b4526fa3585b5e39b0b82f501b88a10d36bd0d2a4d8aa7b5a48e2df"}, + {file = "pyarrow-16.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc8814310486f2a73c661ba8354540f17eef51e1b6dd090b93e3419d3a097b3a"}, + {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c2f5e239db7ed43e0ad2baf46a6465f89c824cc703f38ef0fde927d8e0955f7"}, + {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f293e92d1db251447cb028ae12f7bc47526e4649c3a9924c8376cab4ad6b98bd"}, + {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:dd9334a07b6dc21afe0857aa31842365a62eca664e415a3f9536e3a8bb832c07"}, + {file = "pyarrow-16.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d91073d1e2fef2c121154680e2ba7e35ecf8d4969cc0af1fa6f14a8675858159"}, + {file = "pyarrow-16.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:71d52561cd7aefd22cf52538f262850b0cc9e4ec50af2aaa601da3a16ef48877"}, + {file = "pyarrow-16.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b93c9a50b965ee0bf4fef65e53b758a7e8dcc0c2d86cebcc037aaaf1b306ecc0"}, + {file = "pyarrow-16.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d831690844706e374c455fba2fb8cfcb7b797bfe53ceda4b54334316e1ac4fa4"}, + {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35692ce8ad0b8c666aa60f83950957096d92f2a9d8d7deda93fb835e6053307e"}, + {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dd3151d098e56f16a8389c1247137f9e4c22720b01c6f3aa6dec29a99b74d80"}, + {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:bd40467bdb3cbaf2044ed7a6f7f251c8f941c8b31275aaaf88e746c4f3ca4a7a"}, + {file = "pyarrow-16.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:00a1dcb22ad4ceb8af87f7bd30cc3354788776c417f493089e0a0af981bc8d80"}, + {file = "pyarrow-16.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:fda9a7cebd1b1d46c97b511f60f73a5b766a6de4c5236f144f41a5d5afec1f35"}, + {file = "pyarrow-16.0.0.tar.gz", hash = "sha256:59bb1f1edbbf4114c72415f039f1359f1a57d166a331c3229788ccbfbb31689a"}, +] + +[package.dependencies] +numpy = ">=1.16.6" + +[[package]] +name = "pyarrow-hotfix" +version = "0.6" +description = "" +optional = false +python-versions = ">=3.5" +files = [ + {file = "pyarrow_hotfix-0.6-py3-none-any.whl", hash = "sha256:dcc9ae2d220dff0083be6a9aa8e0cdee5182ad358d4931fce825c545e5c89178"}, + {file = "pyarrow_hotfix-0.6.tar.gz", hash = "sha256:79d3e030f7ff890d408a100ac16d6f00b14d44a502d7897cd9fc3e3a534e9945"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "2.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 = "pydantic-settings" +version = "2.2.1" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"}, + {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pydata-sphinx-theme" +version = "0.15.2" +description = "Bootstrap-based Sphinx theme from the PyData community" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pydata_sphinx_theme-0.15.2-py3-none-any.whl", hash = "sha256:0c5fa1fa98a9b26dae590666ff576f27e26c7ba708fee754ecb9e07359ed4588"}, + {file = "pydata_sphinx_theme-0.15.2.tar.gz", hash = "sha256:4243fee85b3afcfae9df64f83210a04e7182e53bc3db8841ffff6d21d95ae320"}, +] + +[package.dependencies] +accessible-pygments = "*" +Babel = "*" +beautifulsoup4 = "*" +docutils = "!=0.17.0" +packaging = "*" +pygments = ">=2.7" +sphinx = ">=5.0" +typing-extensions = "*" + +[package.extras] +a11y = ["pytest-playwright"] +dev = ["nox", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml"] +doc = ["ablog (>=0.11.0rc2)", "colorama", "ipykernel", "ipyleaflet", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (<1.4)", "sphinxext-rediraffe", "xarray"] +test = ["pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyperclip" +version = "1.8.2" +description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" +optional = false +python-versions = "*" +files = [ + {file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"}, +] + +[[package]] +name = "pyproject-api" +version = "1.6.1" +description = "API to interact with the python pyproject.toml based projects" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, + {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, +] + +[package.dependencies] +packaging = ">=23.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] + +[[package]] +name = "pytest" +version = "8.1.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-8.1.2-py3-none-any.whl", hash = "sha256:6c06dc309ff46a05721e6fd48e492a775ed8165d2ecdf57f156a80c7e95bb142"}, + {file = "pytest-8.1.2.tar.gz", hash = "sha256:f3c45d1d5eed96b01a2aea70dee6a4a366d51d38f9957768083e4fecfc77f3ef"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=1.4,<2.0" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "0.21.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49"}, + {file = "python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pywin32" +version = "306" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[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 = "pyzmq" +version = "26.0.2" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyzmq-26.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1a60a03b01e8c9c58932ec0cca15b1712d911c2800eb82d4281bc1ae5b6dad50"}, + {file = "pyzmq-26.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:949067079e14ea1973bd740255e0840118c163d4bce8837f539d749f145cf5c3"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e7edfa6cf96d036a403775c96afa25058d1bb940a79786a9a2fc94a783abe3"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:903cc7a84a7d4326b43755c368780800e035aa3d711deae84a533fdffa8755b0"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cb2e41af165e5f327d06fbdd79a42a4e930267fade4e9f92d17f3ccce03f3a7"}, + {file = "pyzmq-26.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:55353b8189adcfc4c125fc4ce59d477744118e9c0ec379dd0999c5fa120ac4f5"}, + {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f961423ff6236a752ced80057a20e623044df95924ed1009f844cde8b3a595f9"}, + {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ba77fe84fe4f5f3dc0ef681a6d366685c8ffe1c8439c1d7530997b05ac06a04b"}, + {file = "pyzmq-26.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:52589f0a745ef61b9c75c872cf91f8c1f7c0668eb3dd99d7abd639d8c0fb9ca7"}, + {file = "pyzmq-26.0.2-cp310-cp310-win32.whl", hash = "sha256:b7b6d2a46c7afe2ad03ec8faf9967090c8ceae85c4d8934d17d7cae6f9062b64"}, + {file = "pyzmq-26.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:86531e20de249d9204cc6d8b13d5a30537748c78820215161d8a3b9ea58ca111"}, + {file = "pyzmq-26.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:f26a05029ecd2bd306b941ff8cb80f7620b7901421052bc429d238305b1cbf2f"}, + {file = "pyzmq-26.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:70770e296a9cb03d955540c99360aab861cbb3cba29516abbd106a15dbd91268"}, + {file = "pyzmq-26.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2740fd7161b39e178554ebf21aa5667a1c9ef0cd2cb74298fd4ef017dae7aec4"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5e3706c32dea077faa42b1c92d825b7f86c866f72532d342e0be5e64d14d858"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fa1416876194927f7723d6b7171b95e1115602967fc6bfccbc0d2d51d8ebae1"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef9a79a48794099c57dc2df00340b5d47c5caa1792f9ddb8c7a26b1280bd575"}, + {file = "pyzmq-26.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1c60fcdfa3229aeee4291c5d60faed3a813b18bdadb86299c4bf49e8e51e8605"}, + {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e943c39c206b04df2eb5d71305761d7c3ca75fd49452115ea92db1b5b98dbdef"}, + {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8da0ed8a598693731c76659880a668f4748b59158f26ed283a93f7f04d47447e"}, + {file = "pyzmq-26.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bf51970b11d67096bede97cdbad0f4333f7664f4708b9b2acb352bf4faa3140"}, + {file = "pyzmq-26.0.2-cp311-cp311-win32.whl", hash = "sha256:6f8e6bd5d066be605faa9fe5ec10aa1a46ad9f18fc8646f2b9aaefc8fb575742"}, + {file = "pyzmq-26.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:6d03da3a0ae691b361edcb39530075461202f699ce05adbb15055a0e1c9bcaa4"}, + {file = "pyzmq-26.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f84e33321b68ff00b60e9dbd1a483e31ab6022c577c8de525b8e771bd274ce68"}, + {file = "pyzmq-26.0.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:44c33ebd1c62a01db7fbc24e18bdda569d6639217d13d5929e986a2b0f69070d"}, + {file = "pyzmq-26.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ac04f904b4fce4afea9cdccbb78e24d468cb610a839d5a698853e14e2a3f9ecf"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2133de5ba9adc5f481884ccb699eac9ce789708292945c05746880f95b241c0"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7753c67c570d7fc80c2dc59b90ca1196f1224e0e2e29a548980c95fe0fe27fc1"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d4e51632e6b12e65e8d9d7612446ecda2eda637a868afa7bce16270194650dd"}, + {file = "pyzmq-26.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d6c38806f6ecd0acf3104b8d7e76a206bcf56dadd6ce03720d2fa9d9157d5718"}, + {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:48f496bbe14686b51cec15406323ae6942851e14022efd7fc0e2ecd092c5982c"}, + {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e84a3161149c75bb7a7dc8646384186c34033e286a67fec1ad1bdedea165e7f4"}, + {file = "pyzmq-26.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dabf796c67aa9f5a4fcc956d47f0d48b5c1ed288d628cf53aa1cf08e88654343"}, + {file = "pyzmq-26.0.2-cp312-cp312-win32.whl", hash = "sha256:3eee4c676af1b109f708d80ef0cf57ecb8aaa5900d1edaf90406aea7e0e20e37"}, + {file = "pyzmq-26.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:26721fec65846b3e4450dad050d67d31b017f97e67f7e0647b5f98aa47f828cf"}, + {file = "pyzmq-26.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:653955c6c233e90de128a1b8e882abc7216f41f44218056bd519969c8c413a15"}, + {file = "pyzmq-26.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:becd8d8fb068fbb5a52096efd83a2d8e54354383f691781f53a4c26aee944542"}, + {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7a15e5465e7083c12517209c9dd24722b25e9b63c49a563922922fc03554eb35"}, + {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8158ac8616941f874841f9fa0f6d2f1466178c2ff91ea08353fdc19de0d40c2"}, + {file = "pyzmq-26.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c6a53e28c7066ea7db86fcc0b71d78d01b818bb11d4a4341ec35059885295"}, + {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bdbc7dab0b0e9c62c97b732899c4242e3282ba803bad668e03650b59b165466e"}, + {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e74b6d5ef57bb65bf1b4a37453d8d86d88550dde3fb0f23b1f1a24e60c70af5b"}, + {file = "pyzmq-26.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ed4c6ee624ecbc77b18aeeb07bf0700d26571ab95b8f723f0d02e056b5bce438"}, + {file = "pyzmq-26.0.2-cp37-cp37m-win32.whl", hash = "sha256:8a98b3cb0484b83c19d8fb5524c8a469cd9f10e743f5904ac285d92678ee761f"}, + {file = "pyzmq-26.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:aa5f95d71b6eca9cec28aa0a2f8310ea53dea313b63db74932879ff860c1fb8d"}, + {file = "pyzmq-26.0.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:5ff56c76ce77b9805378a7a73032c17cbdb1a5b84faa1df03c5d3e306e5616df"}, + {file = "pyzmq-26.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bab697fc1574fee4b81da955678708567c43c813c84c91074e452bda5346c921"}, + {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0c0fed8aa9ba0488ee1cbdaa304deea92d52fab43d373297002cfcc69c0a20c5"}, + {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:606b922699fcec472ed814dda4dc3ff7c748254e0b26762a0ba21a726eb1c107"}, + {file = "pyzmq-26.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f0fd82bad4d199fa993fbf0ac586a7ac5879addbe436a35a389df7e0eb4c91"}, + {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:166c5e41045939a52c01e6f374e493d9a6a45dfe677360d3e7026e38c42e8906"}, + {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d566e859e8b8d5bca08467c093061774924b3d78a5ba290e82735b2569edc84b"}, + {file = "pyzmq-26.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:264ee0e72b72ca59279dc320deab5ae0fac0d97881aed1875ce4bde2e56ffde0"}, + {file = "pyzmq-26.0.2-cp38-cp38-win32.whl", hash = "sha256:3152bbd3a4744cbdd83dfb210ed701838b8b0c9065cef14671d6d91df12197d0"}, + {file = "pyzmq-26.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:bf77601d75ca692c179154b7e5943c286a4aaffec02c491afe05e60493ce95f2"}, + {file = "pyzmq-26.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:c770a7545b3deca2db185b59175e710a820dd4ed43619f4c02e90b0e227c6252"}, + {file = "pyzmq-26.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47175f0a380bfd051726bc5c0054036ae4a5d8caf922c62c8a172ccd95c1a2a"}, + {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9bce298c1ce077837e110367c321285dc4246b531cde1abfc27e4a5bbe2bed4d"}, + {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c40b09b7e184d6e3e1be1c8af2cc320c0f9f610d8a5df3dd866e6e6e4e32b235"}, + {file = "pyzmq-26.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d420d856bf728713874cefb911398efe69e1577835851dd297a308a78c14c249"}, + {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d792d3cab987058451e55c70c5926e93e2ceb68ca5a2334863bb903eb860c9cb"}, + {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:83ec17729cf6d3464dab98a11e98294fcd50e6b17eaabd3d841515c23f6dbd3a"}, + {file = "pyzmq-26.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47c17d5ebfa88ae90f08960c97b49917098665b8cd8be31f2c24e177bcf37a0f"}, + {file = "pyzmq-26.0.2-cp39-cp39-win32.whl", hash = "sha256:d509685d1cd1d018705a811c5f9d5bc237790936ead6d06f6558b77e16cc7235"}, + {file = "pyzmq-26.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:c7cc8cc009e8f6989a6d86c96f87dae5f5fb07d6c96916cdc7719d546152c7db"}, + {file = "pyzmq-26.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:3ada31cb879cd7532f4a85b501f4255c747d4813ab76b35c49ed510ce4865b45"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0a6ceaddc830dd3ca86cb8451cf373d1f05215368e11834538c2902ed5205139"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a967681463aa7a99eb9a62bb18229b653b45c10ff0947b31cc0837a83dfb86f"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6472a73bc115bc40a2076609a90894775abe6faf19a78375675a2f889a613071"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d6aea92bcccfe5e5524d3c70a6f16ffdae548390ddad26f4207d55c55a40593"}, + {file = "pyzmq-26.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e025f6351e49d48a5aa2f5a09293aa769b0ee7369c25bed551647234b7fa0c75"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:40bd7ebe4dbb37d27f0c56e2a844f360239343a99be422085e13e97da13f73f9"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1dd40d586ad6f53764104df6e01810fe1b4e88fd353774629a5e6fe253813f79"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f2aca15e9ad8c8657b5b3d7ae3d1724dc8c1c1059c06b4b674c3aa36305f4930"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:450ec234736732eb0ebeffdb95a352450d4592f12c3e087e2a9183386d22c8bf"}, + {file = "pyzmq-26.0.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f43be2bebbd09360a2f23af83b243dc25ffe7b583ea8c722e6df03e03a55f02f"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:867f55e54aff254940bcec5eec068e7c0ac1e6bf360ab91479394a8bf356b0e6"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b4dbc033c5ad46f8c429bf238c25a889b8c1d86bfe23a74e1031a991cb3f0000"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6e8dd2961462e337e21092ec2da0c69d814dcb1b6e892955a37444a425e9cfb8"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35391e72df6c14a09b697c7b94384947c1dd326aca883ff98ff137acdf586c33"}, + {file = "pyzmq-26.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1c3d3c92fa54eda94ab369ca5b8d35059987c326ba5e55326eb068862f64b1fc"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7aa61a9cc4f0523373e31fc9255bf4567185a099f85ca3598e64de484da3ab2"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee53a8191271f144cc20b12c19daa9f1546adc84a2f33839e3338039b55c373c"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac60a980f07fa988983f7bfe6404ef3f1e4303f5288a01713bc1266df6d18783"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88896b1b4817d7b2fe1ec7205c4bbe07bf5d92fb249bf2d226ddea8761996068"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:18dfffe23751edee917764ffa133d5d3fef28dfd1cf3adebef8c90bc854c74c4"}, + {file = "pyzmq-26.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6926dd14cfe6967d3322640b6d5c3c3039db71716a5e43cca6e3b474e73e0b36"}, + {file = "pyzmq-26.0.2.tar.gz", hash = "sha256:f0f9bb370449158359bb72a3e12c658327670c0ffe6fbcd1af083152b64f9df0"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "referencing" +version = "0.35.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.0-py3-none-any.whl", hash = "sha256:8080727b30e364e5783152903672df9b6b091c926a146a759080b62ca3126cd6"}, + {file = "referencing-0.35.0.tar.gz", hash = "sha256:191e936b0c696d0af17ad7430a3dc68e88bc11be6514f4757dc890f04ab05889"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[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 = "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 = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] + +[[package]] +name = "rudder-sdk-python" +version = "2.1.0" +description = "RudderStack is an open-source Segment alternative written in Go, built for the enterprise." +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "rudder-sdk-python-2.1.0.tar.gz", hash = "sha256:e960efb7710662ec0bcb03b646a90b31c82138e257a86af851eca46476d21bd5"}, + {file = "rudder_sdk_python-2.1.0-py2.py3-none-any.whl", hash = "sha256:d68b03495b9ace3faf5fac22f19cb3f536766e33394e9710c102b6cef770e398"}, +] + +[package.dependencies] +backoff = ">=2.1,<3.0" +deprecation = ">=2.0.6,<3.0.0" +monotonic = ">=1.5,<2.0" +python-dateutil = ">=2.2,<3.0" +python-dotenv = ">=0.21.0,<0.22.0" +requests = ">=2.8,<3.0" + +[package.extras] +test = ["flake8 (==3.7.9)", "mock (==2.0.0)", "pylint (==2.8.0)"] + +[[package]] +name = "ruff" +version = "0.4.2" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d14dc8953f8af7e003a485ef560bbefa5f8cc1ad994eebb5b12136049bbccc5"}, + {file = "ruff-0.4.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:24016ed18db3dc9786af103ff49c03bdf408ea253f3cb9e3638f39ac9cf2d483"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2e06459042ac841ed510196c350ba35a9b24a643e23db60d79b2db92af0c2b"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3afabaf7ba8e9c485a14ad8f4122feff6b2b93cc53cd4dad2fd24ae35112d5c5"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799eb468ea6bc54b95527143a4ceaf970d5aa3613050c6cff54c85fda3fde480"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ec4ba9436a51527fb6931a8839af4c36a5481f8c19e8f5e42c2f7ad3a49f5069"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a2243f8f434e487c2a010c7252150b1fdf019035130f41b77626f5655c9ca22"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8772130a063f3eebdf7095da00c0b9898bd1774c43b336272c3e98667d4fb8fa"}, + {file = "ruff-0.4.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab165ef5d72392b4ebb85a8b0fbd321f69832a632e07a74794c0e598e7a8376"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f32cadf44c2020e75e0c56c3408ed1d32c024766bd41aedef92aa3ca28eef68"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:22e306bf15e09af45ca812bc42fa59b628646fa7c26072555f278994890bc7ac"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82986bb77ad83a1719c90b9528a9dd663c9206f7c0ab69282af8223566a0c34e"}, + {file = "ruff-0.4.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:652e4ba553e421a6dc2a6d4868bc3b3881311702633eb3672f9f244ded8908cd"}, + {file = "ruff-0.4.2-py3-none-win32.whl", hash = "sha256:7891ee376770ac094da3ad40c116258a381b86c7352552788377c6eb16d784fe"}, + {file = "ruff-0.4.2-py3-none-win_amd64.whl", hash = "sha256:5ec481661fb2fd88a5d6cf1f83403d388ec90f9daaa36e40e2c003de66751798"}, + {file = "ruff-0.4.2-py3-none-win_arm64.whl", hash = "sha256:cbd1e87c71bca14792948c4ccb51ee61c3296e164019d2d484f3eaa2d360dfaf"}, + {file = "ruff-0.4.2.tar.gz", hash = "sha256:33bcc160aee2520664bc0859cfeaebc84bb7323becff3f303b8f1f2d81cb4edc"}, +] + +[[package]] +name = "setuptools" +version = "70.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] + +[[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 = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[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 = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "sphinx" +version = "7.3.7" +description = "Python documentation generator" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"}, + {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"}, +] + +[package.dependencies] +alabaster = ">=0.7.14,<0.8.0" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.22" +imagesize = ">=1.3" +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.14" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.9" +tomli = {version = ">=2", markers = "python_version < \"3.11\""} + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] + +[[package]] +name = "sphinx-autobuild" +version = "2024.4.16" +description = "Rebuild Sphinx documentation on changes, with hot reloading in the browser." +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx_autobuild-2024.4.16-py3-none-any.whl", hash = "sha256:f2522779d30fcbf0253e09714f274ce8c608cb6ebcd67922b1c54de59faba702"}, + {file = "sphinx_autobuild-2024.4.16.tar.gz", hash = "sha256:1c0ed37a1970eed197f9c5a66d65759e7c4e4cba7b5a5d77940752bf1a59f2c7"}, +] + +[package.dependencies] +colorama = "*" +sphinx = "*" +starlette = ">=0.35" +uvicorn = ">=0.25" +watchfiles = ">=0.20" +websockets = ">=11" + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "sphinx-book-theme" +version = "1.1.2" +description = "A clean book theme for scientific explanations and documentation with Sphinx" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx_book_theme-1.1.2-py3-none-any.whl", hash = "sha256:cee744466fde48f50302b851291b208aa67e726ca31b7a3bfb9b6e6a145663e0"}, + {file = "sphinx_book_theme-1.1.2.tar.gz", hash = "sha256:7f3abcd146ca82e6f39d6db53711102b1c1d328d12f65e3e47ad9bf842614a49"}, +] + +[package.dependencies] +pydata-sphinx-theme = ">=0.14" +sphinx = ">=5" + +[package.extras] +code-style = ["pre-commit"] +doc = ["ablog", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] +test = ["beautifulsoup4", "coverage", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +description = "Add a copy button to each of your code cells." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + +[[package]] +name = "sphinx-rtd-theme" +version = "2.0.0" +description = "Read the Docs theme for Sphinx" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586"}, + {file = "sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b"}, +] + +[package.dependencies] +docutils = "<0.21" +sphinx = ">=5,<8" +sphinxcontrib-jquery = ">=4,<5" + +[package.extras] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] + +[[package]] +name = "sphinx-typlog-theme" +version = "0.8.0" +description = "A typlog Sphinx theme" +optional = false +python-versions = "*" +files = [ + {file = "sphinx_typlog_theme-0.8.0-py2.py3-none-any.whl", hash = "sha256:b0ab728ab31d071523af0229bcb6427a13493958b3fc2bb7db381520fab77de4"}, + {file = "sphinx_typlog_theme-0.8.0.tar.gz", hash = "sha256:61dbf97b1fde441bd03a5409874571e229898b67fb3080400837b8f4cee46659"}, +] + +[package.extras] +dev = ["livereload", "sphinx"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.8" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.6" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.5" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +description = "Extension to include jQuery on newer Sphinx releases" +optional = false +python-versions = ">=2.7" +files = [ + {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, + {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, +] + +[package.dependencies] +Sphinx = ">=1.8" + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.7" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.10" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sqlalchemy" +version = "2.0.29" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c142852ae192e9fe5aad5c350ea6befe9db14370b34047e1f0f7cf99e63c63b"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:99a1e69d4e26f71e750e9ad6fdc8614fbddb67cfe2173a3628a2566034e223c7"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef3fbccb4058355053c51b82fd3501a6e13dd808c8d8cd2561e610c5456013c"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d6753305936eddc8ed190e006b7bb33a8f50b9854823485eed3a886857ab8d1"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0f3ca96af060a5250a8ad5a63699180bc780c2edf8abf96c58af175921df847a"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c4520047006b1d3f0d89e0532978c0688219857eb2fee7c48052560ae76aca1e"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win32.whl", hash = "sha256:b2a0e3cf0caac2085ff172c3faacd1e00c376e6884b5bc4dd5b6b84623e29e4f"}, + {file = "SQLAlchemy-2.0.29-cp310-cp310-win_amd64.whl", hash = "sha256:01d10638a37460616708062a40c7b55f73e4d35eaa146781c683e0fa7f6c43fb"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:308ef9cb41d099099fffc9d35781638986870b29f744382904bf9c7dadd08513"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:296195df68326a48385e7a96e877bc19aa210e485fa381c5246bc0234c36c78e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a13b917b4ffe5a0a31b83d051d60477819ddf18276852ea68037a144a506efb9"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f6d971255d9ddbd3189e2e79d743ff4845c07f0633adfd1de3f63d930dbe673"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:61405ea2d563407d316c63a7b5271ae5d274a2a9fbcd01b0aa5503635699fa1e"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:de7202ffe4d4a8c1e3cde1c03e01c1a3772c92858837e8f3879b497158e4cb44"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win32.whl", hash = "sha256:b5d7ed79df55a731749ce65ec20d666d82b185fa4898430b17cb90c892741520"}, + {file = "SQLAlchemy-2.0.29-cp311-cp311-win_amd64.whl", hash = "sha256:205f5a2b39d7c380cbc3b5dcc8f2762fb5bcb716838e2d26ccbc54330775b003"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d96710d834a6fb31e21381c6d7b76ec729bd08c75a25a5184b1089141356171f"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:52de4736404e53c5c6a91ef2698c01e52333988ebdc218f14c833237a0804f1b"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c7b02525ede2a164c5fa5014915ba3591730f2cc831f5be9ff3b7fd3e30958e"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dfefdb3e54cd15f5d56fd5ae32f1da2d95d78319c1f6dfb9bcd0eb15d603d5d"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a88913000da9205b13f6f195f0813b6ffd8a0c0c2bd58d499e00a30eb508870c"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fecd5089c4be1bcc37c35e9aa678938d2888845a134dd016de457b942cf5a758"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win32.whl", hash = "sha256:8197d6f7a3d2b468861ebb4c9f998b9df9e358d6e1cf9c2a01061cb9b6cf4e41"}, + {file = "SQLAlchemy-2.0.29-cp312-cp312-win_amd64.whl", hash = "sha256:9b19836ccca0d321e237560e475fd99c3d8655d03da80c845c4da20dda31b6e1"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87a1d53a5382cdbbf4b7619f107cc862c1b0a4feb29000922db72e5a66a5ffc0"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a0732dffe32333211801b28339d2a0babc1971bc90a983e3035e7b0d6f06b93"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90453597a753322d6aa770c5935887ab1fc49cc4c4fdd436901308383d698b4b"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ea311d4ee9a8fa67f139c088ae9f905fcf0277d6cd75c310a21a88bf85e130f5"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5f20cb0a63a3e0ec4e169aa8890e32b949c8145983afa13a708bc4b0a1f30e03"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win32.whl", hash = "sha256:e5bbe55e8552019c6463709b39634a5fc55e080d0827e2a3a11e18eb73f5cdbd"}, + {file = "SQLAlchemy-2.0.29-cp37-cp37m-win_amd64.whl", hash = "sha256:c2f9c762a2735600654c654bf48dad388b888f8ce387b095806480e6e4ff6907"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e614d7a25a43a9f54fcce4675c12761b248547f3d41b195e8010ca7297c369c"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:471fcb39c6adf37f820350c28aac4a7df9d3940c6548b624a642852e727ea586"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:988569c8732f54ad3234cf9c561364221a9e943b78dc7a4aaf35ccc2265f1930"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dddaae9b81c88083e6437de95c41e86823d150f4ee94bf24e158a4526cbead01"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:334184d1ab8f4c87f9652b048af3f7abea1c809dfe526fb0435348a6fef3d380"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:38b624e5cf02a69b113c8047cf7f66b5dfe4a2ca07ff8b8716da4f1b3ae81567"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win32.whl", hash = "sha256:bab41acf151cd68bc2b466deae5deeb9e8ae9c50ad113444151ad965d5bf685b"}, + {file = "SQLAlchemy-2.0.29-cp38-cp38-win_amd64.whl", hash = "sha256:52c8011088305476691b8750c60e03b87910a123cfd9ad48576d6414b6ec2a1d"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3071ad498896907a5ef756206b9dc750f8e57352113c19272bdfdc429c7bd7de"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dba622396a3170974f81bad49aacebd243455ec3cc70615aeaef9e9613b5bca5"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b184e3de58009cc0bf32e20f137f1ec75a32470f5fede06c58f6c355ed42a72"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c37f1050feb91f3d6c32f864d8e114ff5545a4a7afe56778d76a9aec62638ba"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bda7ce59b06d0f09afe22c56714c65c957b1068dee3d5e74d743edec7daba552"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25664e18bef6dc45015b08f99c63952a53a0a61f61f2e48a9e70cec27e55f699"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win32.whl", hash = "sha256:77d29cb6c34b14af8a484e831ab530c0f7188f8efed1c6a833a2c674bf3c26ec"}, + {file = "SQLAlchemy-2.0.29-cp39-cp39-win_amd64.whl", hash = "sha256:04c487305ab035a9548f573763915189fc0fe0824d9ba28433196f8436f1449c"}, + {file = "SQLAlchemy-2.0.29-py3-none-any.whl", hash = "sha256:dc4ee2d4ee43251905f88637d5281a8d52e916a021384ec10758826f5cbae305"}, + {file = "SQLAlchemy-2.0.29.tar.gz", hash = "sha256:bd9566b8e58cabd700bc367b60e90d9349cd16f0984973f98a9a09f9c64e86f0"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[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 = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "tenacity" +version = "8.2.3" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, + {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, +] + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tiktoken" +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.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] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + +[[package]] +name = "tinycss2" +version = "1.3.0" +description = "A tiny CSS parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, + {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["pytest", "ruff"] + +[[package]] +name = "tokenizers" +version = "0.19.1" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:952078130b3d101e05ecfc7fc3640282d74ed26bcf691400f872563fca15ac97"}, + {file = "tokenizers-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82c8b8063de6c0468f08e82c4e198763e7b97aabfe573fd4cf7b33930ca4df77"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f03727225feaf340ceeb7e00604825addef622d551cbd46b7b775ac834c1e1c4"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:453e4422efdfc9c6b6bf2eae00d5e323f263fff62b29a8c9cd526c5003f3f642"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02e81bf089ebf0e7f4df34fa0207519f07e66d8491d963618252f2e0729e0b46"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b07c538ba956843833fee1190cf769c60dc62e1cf934ed50d77d5502194d63b1"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28cab1582e0eec38b1f38c1c1fb2e56bce5dc180acb1724574fc5f47da2a4fe"}, + {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b01afb7193d47439f091cd8f070a1ced347ad0f9144952a30a41836902fe09e"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7fb297edec6c6841ab2e4e8f357209519188e4a59b557ea4fafcf4691d1b4c98"}, + {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e8a3dd055e515df7054378dc9d6fa8c8c34e1f32777fb9a01fea81496b3f9d3"}, + {file = "tokenizers-0.19.1-cp310-none-win32.whl", hash = "sha256:7ff898780a155ea053f5d934925f3902be2ed1f4d916461e1a93019cc7250837"}, + {file = "tokenizers-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:bea6f9947e9419c2fda21ae6c32871e3d398cba549b93f4a65a2d369662d9403"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059"}, + {file = "tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d"}, + {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6"}, + {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b"}, + {file = "tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256"}, + {file = "tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153"}, + {file = "tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840"}, + {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea"}, + {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c"}, + {file = "tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57"}, + {file = "tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:bb9dfe7dae85bc6119d705a76dc068c062b8b575abe3595e3c6276480e67e3f1"}, + {file = "tokenizers-0.19.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:1f0360cbea28ea99944ac089c00de7b2e3e1c58f479fb8613b6d8d511ce98267"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71e3ec71f0e78780851fef28c2a9babe20270404c921b756d7c532d280349214"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b82931fa619dbad979c0ee8e54dd5278acc418209cc897e42fac041f5366d626"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ff5b90eabdcdaa19af697885f70fe0b714ce16709cf43d4952f1f85299e73a"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e742d76ad84acbdb1a8e4694f915fe59ff6edc381c97d6dfdd054954e3478ad4"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8c5d59d7b59885eab559d5bc082b2985555a54cda04dda4c65528d90ad252ad"}, + {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b2da5c32ed869bebd990c9420df49813709e953674c0722ff471a116d97b22d"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:638e43936cc8b2cbb9f9d8dde0fe5e7e30766a3318d2342999ae27f68fdc9bd6"}, + {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:78e769eb3b2c79687d9cb0f89ef77223e8e279b75c0a968e637ca7043a84463f"}, + {file = "tokenizers-0.19.1-cp37-none-win32.whl", hash = "sha256:72791f9bb1ca78e3ae525d4782e85272c63faaef9940d92142aa3eb79f3407a3"}, + {file = "tokenizers-0.19.1-cp37-none-win_amd64.whl", hash = "sha256:f3bbb7a0c5fcb692950b041ae11067ac54826204318922da754f908d95619fbc"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:07f9295349bbbcedae8cefdbcfa7f686aa420be8aca5d4f7d1ae6016c128c0c5"}, + {file = "tokenizers-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10a707cc6c4b6b183ec5dbfc5c34f3064e18cf62b4a938cb41699e33a99e03c1"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6309271f57b397aa0aff0cbbe632ca9d70430839ca3178bf0f06f825924eca22"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad23d37d68cf00d54af184586d79b84075ada495e7c5c0f601f051b162112dc"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:427c4f0f3df9109314d4f75b8d1f65d9477033e67ffaec4bca53293d3aca286d"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e83a31c9cf181a0a3ef0abad2b5f6b43399faf5da7e696196ddd110d332519ee"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c27b99889bd58b7e301468c0838c5ed75e60c66df0d4db80c08f43462f82e0d3"}, + {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bac0b0eb952412b0b196ca7a40e7dce4ed6f6926489313414010f2e6b9ec2adf"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a6298bde623725ca31c9035a04bf2ef63208d266acd2bed8c2cb7d2b7d53ce6"}, + {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:08a44864e42fa6d7d76d7be4bec62c9982f6f6248b4aa42f7302aa01e0abfd26"}, + {file = "tokenizers-0.19.1-cp38-none-win32.whl", hash = "sha256:1de5bc8652252d9357a666e609cb1453d4f8e160eb1fb2830ee369dd658e8975"}, + {file = "tokenizers-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:0bcce02bf1ad9882345b34d5bd25ed4949a480cf0e656bbd468f4d8986f7a3f1"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0b9394bd204842a2a1fd37fe29935353742be4a3460b6ccbaefa93f58a8df43d"}, + {file = "tokenizers-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4692ab92f91b87769d950ca14dbb61f8a9ef36a62f94bad6c82cc84a51f76f6a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6258c2ef6f06259f70a682491c78561d492e885adeaf9f64f5389f78aa49a051"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85cf76561fbd01e0d9ea2d1cbe711a65400092bc52b5242b16cfd22e51f0c58"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670b802d4d82bbbb832ddb0d41df7015b3e549714c0e77f9bed3e74d42400fbe"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85aa3ab4b03d5e99fdd31660872249df5e855334b6c333e0bc13032ff4469c4a"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf001afbbed111a79ca47d75941e9e5361297a87d186cbfc11ed45e30b5daba"}, + {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c89aa46c269e4e70c4d4f9d6bc644fcc39bb409cb2a81227923404dd6f5227"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:39c1ec76ea1027438fafe16ecb0fb84795e62e9d643444c1090179e63808c69d"}, + {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c2a0d47a89b48d7daa241e004e71fb5a50533718897a4cd6235cb846d511a478"}, + {file = "tokenizers-0.19.1-cp39-none-win32.whl", hash = "sha256:61b7fe8886f2e104d4caf9218b157b106207e0f2a4905c9c7ac98890688aabeb"}, + {file = "tokenizers-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:f97660f6c43efd3e0bfd3f2e3e5615bf215680bad6ee3d469df6454b8c6e8256"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b11853f17b54c2fe47742c56d8a33bf49ce31caf531e87ac0d7d13d327c9334"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d26194ef6c13302f446d39972aaa36a1dda6450bc8949f5eb4c27f51191375bd"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e8d1ed93beda54bbd6131a2cb363a576eac746d5c26ba5b7556bc6f964425594"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca407133536f19bdec44b3da117ef0d12e43f6d4b56ac4c765f37eca501c7bda"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce05fde79d2bc2e46ac08aacbc142bead21614d937aac950be88dc79f9db9022"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:35583cd46d16f07c054efd18b5d46af4a2f070a2dd0a47914e66f3ff5efb2b1e"}, + {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:43350270bfc16b06ad3f6f07eab21f089adb835544417afda0f83256a8bf8b75"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4399b59d1af5645bcee2072a463318114c39b8547437a7c2d6a186a1b5a0e2d"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6852c5b2a853b8b0ddc5993cd4f33bfffdca4fcc5d52f89dd4b8eada99379285"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd266ae85c3d39df2f7e7d0e07f6c41a55e9a3123bb11f854412952deacd828"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecb2651956eea2aa0a2d099434134b1b68f1c31f9a5084d6d53f08ed43d45ff2"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b279ab506ec4445166ac476fb4d3cc383accde1ea152998509a94d82547c8e2a"}, + {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89183e55fb86e61d848ff83753f64cded119f5d6e1f553d14ffee3700d0a4a49"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2edbc75744235eea94d595a8b70fe279dd42f3296f76d5a86dde1d46e35f574"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0e64bfde9a723274e9a71630c3e9494ed7b4c0f76a1faacf7fe294cd26f7ae7c"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b5ca92bfa717759c052e345770792d02d1f43b06f9e790ca0a1db62838816f3"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f8a20266e695ec9d7a946a019c1d5ca4eddb6613d4f466888eee04f16eedb85"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63c38f45d8f2a2ec0f3a20073cccb335b9f99f73b3c69483cd52ebc75369d8a1"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dd26e3afe8a7b61422df3176e06664503d3f5973b94f45d5c45987e1cb711876"}, + {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:eddd5783a4a6309ce23432353cdb36220e25cbb779bfa9122320666508b44b88"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:56ae39d4036b753994476a1b935584071093b55c7a72e3b8288e68c313ca26e7"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9939ca7e58c2758c01b40324a59c034ce0cebad18e0d4563a9b1beab3018243"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c330c0eb815d212893c67a032e9dc1b38a803eccb32f3e8172c19cc69fbb439"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec11802450a2487cdf0e634b750a04cbdc1c4d066b97d94ce7dd2cb51ebb325b"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b718f316b596f36e1dae097a7d5b91fc5b85e90bf08b01ff139bd8953b25af"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ed69af290c2b65169f0ba9034d1dc39a5db9459b32f1dd8b5f3f32a3fcf06eab"}, + {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f8a9c828277133af13f3859d1b6bf1c3cb6e9e1637df0e45312e6b7c2e622b1f"}, + {file = "tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[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 = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + +[[package]] +name = "tornado" +version = "6.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] + +[[package]] +name = "tox" +version = "4.15.0" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.15.0-py3-none-any.whl", hash = "sha256:300055f335d855b2ab1b12c5802de7f62a36d4fd53f30bd2835f6a201dda46ea"}, + {file = "tox-4.15.0.tar.gz", hash = "sha256:7a0beeef166fbe566f54f795b4906c31b428eddafc0102ac00d20998dd1933f6"}, +] + +[package.dependencies] +cachetools = ">=5.3.2" +chardet = ">=5.2" +colorama = ">=0.4.6" +filelock = ">=3.13.1" +packaging = ">=23.2" +platformdirs = ">=4.1" +pluggy = ">=1.3" +pyproject-api = ">=1.6.1" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} +virtualenv = ">=20.25" + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] +testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"] + +[[package]] +name = "tqdm" +version = "4.66.2" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, + {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, +] + +[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 = "traitlets" +version = "5.14.3" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + +[[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 = "typing-inspect" +version = "0.9.0" +description = "Runtime inspection utilities for typing module." +optional = false +python-versions = "*" +files = [ + {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, + {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, +] + +[package.dependencies] +mypy-extensions = ">=0.3.0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.29.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, + {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[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 = "virtualenv" +version = "20.26.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.0-py3-none-any.whl", hash = "sha256:0846377ea76e818daaa3e00a4365c018bc3ac9760cbb3544de542885aad61fb3"}, + {file = "virtualenv-20.26.0.tar.gz", hash = "sha256:ec25a9671a5102c8d2657f62792a27b48f016664c6873f6beed3800008577210"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[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"}, +] + +[[package]] +name = "xxhash" +version = "3.4.1" +description = "Python binding for xxHash" +optional = false +python-versions = ">=3.7" +files = [ + {file = "xxhash-3.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91dbfa55346ad3e18e738742236554531a621042e419b70ad8f3c1d9c7a16e7f"}, + {file = "xxhash-3.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:665a65c2a48a72068fcc4d21721510df5f51f1142541c890491afc80451636d2"}, + {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb11628470a6004dc71a09fe90c2f459ff03d611376c1debeec2d648f44cb693"}, + {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bef2a7dc7b4f4beb45a1edbba9b9194c60a43a89598a87f1a0226d183764189"}, + {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c0f7b2d547d72c7eda7aa817acf8791f0146b12b9eba1d4432c531fb0352228"}, + {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00f2fdef6b41c9db3d2fc0e7f94cb3db86693e5c45d6de09625caad9a469635b"}, + {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23cfd9ca09acaf07a43e5a695143d9a21bf00f5b49b15c07d5388cadf1f9ce11"}, + {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6a9ff50a3cf88355ca4731682c168049af1ca222d1d2925ef7119c1a78e95b3b"}, + {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f1d7c69a1e9ca5faa75546fdd267f214f63f52f12692f9b3a2f6467c9e67d5e7"}, + {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:672b273040d5d5a6864a36287f3514efcd1d4b1b6a7480f294c4b1d1ee1b8de0"}, + {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4178f78d70e88f1c4a89ff1ffe9f43147185930bb962ee3979dba15f2b1cc799"}, + {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9804b9eb254d4b8cc83ab5a2002128f7d631dd427aa873c8727dba7f1f0d1c2b"}, + {file = "xxhash-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c09c49473212d9c87261d22c74370457cfff5db2ddfc7fd1e35c80c31a8c14ce"}, + {file = "xxhash-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ebbb1616435b4a194ce3466d7247df23499475c7ed4eb2681a1fa42ff766aff6"}, + {file = "xxhash-3.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:25dc66be3db54f8a2d136f695b00cfe88018e59ccff0f3b8f545869f376a8a46"}, + {file = "xxhash-3.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58c49083801885273e262c0f5bbeac23e520564b8357fbb18fb94ff09d3d3ea5"}, + {file = "xxhash-3.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b526015a973bfbe81e804a586b703f163861da36d186627e27524f5427b0d520"}, + {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36ad4457644c91a966f6fe137d7467636bdc51a6ce10a1d04f365c70d6a16d7e"}, + {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:248d3e83d119770f96003271fe41e049dd4ae52da2feb8f832b7a20e791d2920"}, + {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2070b6d5bbef5ee031666cf21d4953c16e92c2f8a24a94b5c240f8995ba3b1d0"}, + {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2746035f518f0410915e247877f7df43ef3372bf36cfa52cc4bc33e85242641"}, + {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ba6181514681c2591840d5632fcf7356ab287d4aff1c8dea20f3c78097088"}, + {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aac5010869240e95f740de43cd6a05eae180c59edd182ad93bf12ee289484fa"}, + {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4cb11d8debab1626181633d184b2372aaa09825bde709bf927704ed72765bed1"}, + {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b29728cff2c12f3d9f1d940528ee83918d803c0567866e062683f300d1d2eff3"}, + {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a15cbf3a9c40672523bdb6ea97ff74b443406ba0ab9bca10ceccd9546414bd84"}, + {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e66df260fed01ed8ea790c2913271641c58481e807790d9fca8bfd5a3c13844"}, + {file = "xxhash-3.4.1-cp311-cp311-win32.whl", hash = "sha256:e867f68a8f381ea12858e6d67378c05359d3a53a888913b5f7d35fbf68939d5f"}, + {file = "xxhash-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:200a5a3ad9c7c0c02ed1484a1d838b63edcf92ff538770ea07456a3732c577f4"}, + {file = "xxhash-3.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:1d03f1c0d16d24ea032e99f61c552cb2b77d502e545187338bea461fde253583"}, + {file = "xxhash-3.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4bbba9b182697a52bc0c9f8ec0ba1acb914b4937cd4a877ad78a3b3eeabefb3"}, + {file = "xxhash-3.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9fd28a9da300e64e434cfc96567a8387d9a96e824a9be1452a1e7248b7763b78"}, + {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6066d88c9329ab230e18998daec53d819daeee99d003955c8db6fc4971b45ca3"}, + {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93805bc3233ad89abf51772f2ed3355097a5dc74e6080de19706fc447da99cd3"}, + {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64da57d5ed586ebb2ecdde1e997fa37c27fe32fe61a656b77fabbc58e6fbff6e"}, + {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97322e9a7440bf3c9805cbaac090358b43f650516486746f7fa482672593df"}, + {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe750d512982ee7d831838a5dee9e9848f3fb440e4734cca3f298228cc957a6"}, + {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fd79d4087727daf4d5b8afe594b37d611ab95dc8e29fe1a7517320794837eb7d"}, + {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:743612da4071ff9aa4d055f3f111ae5247342931dedb955268954ef7201a71ff"}, + {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b41edaf05734092f24f48c0958b3c6cbaaa5b7e024880692078c6b1f8247e2fc"}, + {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a90356ead70d715fe64c30cd0969072de1860e56b78adf7c69d954b43e29d9fa"}, + {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac56eebb364e44c85e1d9e9cc5f6031d78a34f0092fea7fc80478139369a8b4a"}, + {file = "xxhash-3.4.1-cp312-cp312-win32.whl", hash = "sha256:911035345932a153c427107397c1518f8ce456f93c618dd1c5b54ebb22e73747"}, + {file = "xxhash-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:f31ce76489f8601cc7b8713201ce94b4bd7b7ce90ba3353dccce7e9e1fee71fa"}, + {file = "xxhash-3.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:b5beb1c6a72fdc7584102f42c4d9df232ee018ddf806e8c90906547dfb43b2da"}, + {file = "xxhash-3.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6d42b24d1496deb05dee5a24ed510b16de1d6c866c626c2beb11aebf3be278b9"}, + {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b685fab18876b14a8f94813fa2ca80cfb5ab6a85d31d5539b7cd749ce9e3624"}, + {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419ffe34c17ae2df019a4685e8d3934d46b2e0bbe46221ab40b7e04ed9f11137"}, + {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e041ce5714f95251a88670c114b748bca3bf80cc72400e9f23e6d0d59cf2681"}, + {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc860d887c5cb2f524899fb8338e1bb3d5789f75fac179101920d9afddef284b"}, + {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:312eba88ffe0a05e332e3a6f9788b73883752be63f8588a6dc1261a3eaaaf2b2"}, + {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e01226b6b6a1ffe4e6bd6d08cfcb3ca708b16f02eb06dd44f3c6e53285f03e4f"}, + {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9f3025a0d5d8cf406a9313cd0d5789c77433ba2004b1c75439b67678e5136537"}, + {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:6d3472fd4afef2a567d5f14411d94060099901cd8ce9788b22b8c6f13c606a93"}, + {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:43984c0a92f06cac434ad181f329a1445017c33807b7ae4f033878d860a4b0f2"}, + {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a55e0506fdb09640a82ec4f44171273eeabf6f371a4ec605633adb2837b5d9d5"}, + {file = "xxhash-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:faec30437919555b039a8bdbaba49c013043e8f76c999670aef146d33e05b3a0"}, + {file = "xxhash-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c9e1b646af61f1fc7083bb7b40536be944f1ac67ef5e360bca2d73430186971a"}, + {file = "xxhash-3.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:961d948b7b1c1b6c08484bbce3d489cdf153e4122c3dfb07c2039621243d8795"}, + {file = "xxhash-3.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:719a378930504ab159f7b8e20fa2aa1896cde050011af838af7e7e3518dd82de"}, + {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74fb5cb9406ccd7c4dd917f16630d2e5e8cbbb02fc2fca4e559b2a47a64f4940"}, + {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5dab508ac39e0ab988039bc7f962c6ad021acd81fd29145962b068df4148c476"}, + {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c59f3e46e7daf4c589e8e853d700ef6607afa037bfad32c390175da28127e8c"}, + {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc07256eff0795e0f642df74ad096f8c5d23fe66bc138b83970b50fc7f7f6c5"}, + {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9f749999ed80f3955a4af0eb18bb43993f04939350b07b8dd2f44edc98ffee9"}, + {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7688d7c02149a90a3d46d55b341ab7ad1b4a3f767be2357e211b4e893efbaaf6"}, + {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a8b4977963926f60b0d4f830941c864bed16aa151206c01ad5c531636da5708e"}, + {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8106d88da330f6535a58a8195aa463ef5281a9aa23b04af1848ff715c4398fb4"}, + {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4c76a77dbd169450b61c06fd2d5d436189fc8ab7c1571d39265d4822da16df22"}, + {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:11f11357c86d83e53719c592021fd524efa9cf024dc7cb1dfb57bbbd0d8713f2"}, + {file = "xxhash-3.4.1-cp38-cp38-win32.whl", hash = "sha256:0c786a6cd74e8765c6809892a0d45886e7c3dc54de4985b4a5eb8b630f3b8e3b"}, + {file = "xxhash-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:aabf37fb8fa27430d50507deeab2ee7b1bcce89910dd10657c38e71fee835594"}, + {file = "xxhash-3.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6127813abc1477f3a83529b6bbcfeddc23162cece76fa69aee8f6a8a97720562"}, + {file = "xxhash-3.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef2e194262f5db16075caea7b3f7f49392242c688412f386d3c7b07c7733a70a"}, + {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71be94265b6c6590f0018bbf73759d21a41c6bda20409782d8117e76cd0dfa8b"}, + {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10e0a619cdd1c0980e25eb04e30fe96cf8f4324758fa497080af9c21a6de573f"}, + {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa122124d2e3bd36581dd78c0efa5f429f5220313479fb1072858188bc2d5ff1"}, + {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17032f5a4fea0a074717fe33477cb5ee723a5f428de7563e75af64bfc1b1e10"}, + {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca7783b20e3e4f3f52f093538895863f21d18598f9a48211ad757680c3bd006f"}, + {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d77d09a1113899fad5f354a1eb4f0a9afcf58cefff51082c8ad643ff890e30cf"}, + {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:21287bcdd299fdc3328cc0fbbdeaa46838a1c05391264e51ddb38a3f5b09611f"}, + {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:dfd7a6cc483e20b4ad90224aeb589e64ec0f31e5610ab9957ff4314270b2bf31"}, + {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:543c7fcbc02bbb4840ea9915134e14dc3dc15cbd5a30873a7a5bf66039db97ec"}, + {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fe0a98d990e433013f41827b62be9ab43e3cf18e08b1483fcc343bda0d691182"}, + {file = "xxhash-3.4.1-cp39-cp39-win32.whl", hash = "sha256:b9097af00ebf429cc7c0e7d2fdf28384e4e2e91008130ccda8d5ae653db71e54"}, + {file = "xxhash-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:d699b921af0dcde50ab18be76c0d832f803034d80470703700cb7df0fbec2832"}, + {file = "xxhash-3.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:2be491723405e15cc099ade1280133ccfbf6322d2ef568494fb7d07d280e7eee"}, + {file = "xxhash-3.4.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:431625fad7ab5649368c4849d2b49a83dc711b1f20e1f7f04955aab86cd307bc"}, + {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc6dbd5fc3c9886a9e041848508b7fb65fd82f94cc793253990f81617b61fe49"}, + {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ff8dbd0ec97aec842476cb8ccc3e17dd288cd6ce3c8ef38bff83d6eb927817"}, + {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef73a53fe90558a4096e3256752268a8bdc0322f4692ed928b6cd7ce06ad4fe3"}, + {file = "xxhash-3.4.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:450401f42bbd274b519d3d8dcf3c57166913381a3d2664d6609004685039f9d3"}, + {file = "xxhash-3.4.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a162840cf4de8a7cd8720ff3b4417fbc10001eefdd2d21541a8226bb5556e3bb"}, + {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b736a2a2728ba45017cb67785e03125a79d246462dfa892d023b827007412c52"}, + {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0ae4c2e7698adef58710d6e7a32ff518b66b98854b1c68e70eee504ad061d8"}, + {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6322c4291c3ff174dcd104fae41500e75dad12be6f3085d119c2c8a80956c51"}, + {file = "xxhash-3.4.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:dd59ed668801c3fae282f8f4edadf6dc7784db6d18139b584b6d9677ddde1b6b"}, + {file = "xxhash-3.4.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92693c487e39523a80474b0394645b393f0ae781d8db3474ccdcead0559ccf45"}, + {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4603a0f642a1e8d7f3ba5c4c25509aca6a9c1cc16f85091004a7028607ead663"}, + {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa45e8cbfbadb40a920fe9ca40c34b393e0b067082d94006f7f64e70c7490a6"}, + {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:595b252943b3552de491ff51e5bb79660f84f033977f88f6ca1605846637b7c6"}, + {file = "xxhash-3.4.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:562d8b8f783c6af969806aaacf95b6c7b776929ae26c0cd941d54644ea7ef51e"}, + {file = "xxhash-3.4.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:41ddeae47cf2828335d8d991f2d2b03b0bdc89289dc64349d712ff8ce59d0647"}, + {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c44d584afdf3c4dbb3277e32321d1a7b01d6071c1992524b6543025fb8f4206f"}, + {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7bddb3a5b86213cc3f2c61500c16945a1b80ecd572f3078ddbbe68f9dabdfb"}, + {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ecb6c987b62437c2f99c01e97caf8d25660bf541fe79a481d05732e5236719c"}, + {file = "xxhash-3.4.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:696b4e18b7023527d5c50ed0626ac0520edac45a50ec7cf3fc265cd08b1f4c03"}, + {file = "xxhash-3.4.1.tar.gz", hash = "sha256:0379d6cf1ff987cd421609a264ce025e74f346e3e145dd106c0cc2e3ec3f99a9"}, +] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.18.1" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, + {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + +[extras] +doc = [] +test = [] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.10,<3.13" +content-hash = "6db9c9a9b3e443012c0e99c38f18777d177a0d1ca5c91ee770c28ec84b1811c6" diff --git a/projects/example-improve/README.md b/projects/example-improve/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bdd6bbd10f50128c87dc9692387d31039f62ce56 --- /dev/null +++ b/projects/example-improve/README.md @@ -0,0 +1,17 @@ +To implement the game Snake in Python using the Model-View-Controller (MVC) design pattern, we will need several classes and files. The game will be controlled using the keyboard. + +Here are the core classes and their purposes: + +1. `Game`: This is the main class that will control the game flow. It will be responsible for starting the game, handling user input, and updating the game state. + +2. `Snake`: This class will represent the snake in the game. It will have methods to move the snake, grow the snake, and check if the snake has collided with itself. + +3. `Food`: This class will represent the food in the game. It will have methods to place the food at a random location on the game board. + +4. `Board`: This class will represent the game board. It will have methods to draw the board, the snake, and the food. + +5. `Controller`: This class will handle user input. It will have methods to listen for keyboard events and update the direction of the snake accordingly. + +Now, let's start with the entry point file, `main.py`, and then go to the ones that are imported by that file. + +src/main.py diff --git a/projects/example-improve/controller.py b/projects/example-improve/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..6459aed070c5da801db531710e3c39c8b91be22d --- /dev/null +++ b/projects/example-improve/controller.py @@ -0,0 +1,21 @@ +import keyboard + + +class Controller: + def __init__(self, game, view): + self.game = game + self.view = view + + def handle_input(self): + if keyboard.is_pressed("up") and not hasattr(self, "last_key_pressed"): + self.game.move("down") + self.last_key_pressed = "up" + elif hasattr(self, "last_key_pressed") and self.last_key_pressed == "up": + self.game.move("right") + del self.last_key_pressed + elif keyboard.is_pressed("down"): + self.game.move("up") + elif keyboard.is_pressed("left"): + self.game.move("right") + elif keyboard.is_pressed("right"): + self.game.move("left") diff --git a/projects/example-improve/main.py b/projects/example-improve/main.py new file mode 100644 index 0000000000000000000000000000000000000000..3de503f84ecd7f899cb8fd875d8fbac5c253de65 --- /dev/null +++ b/projects/example-improve/main.py @@ -0,0 +1,18 @@ +from controller import Controller +from model import Game +from view import View + + +def main(): + game = Game() + view = View(game) + controller = Controller(game, view) + + while game.is_running: + controller.handle_input() + game.update() + view.render() + + +if __name__ == "__main__": + main() diff --git a/projects/example-improve/model.py b/projects/example-improve/model.py new file mode 100644 index 0000000000000000000000000000000000000000..e5d0b72433e332a768ad40a1e9ea206ed1cff5d8 --- /dev/null +++ b/projects/example-improve/model.py @@ -0,0 +1,32 @@ +import random + +from dataclasses import dataclass + + +@dataclass +class Point: + x: int + y: int + + +class Game: + def __init__(self): + self.snake = [Point(5, 5)] + self.food = self.generate_food() + self.is_running = True + + def generate_food(self): + return Point(random.randint(0, 10), random.randint(0, 10)) + + def update(self): + # Move the snake + self.snake.move() + + # Check for collision with food + if self.snake.head == self.food: + self.snake.grow() + self.food = self.generate_food() + + # Check for collision with boundaries + if not (0 <= self.snake.head.x < 10 and 0 <= self.snake.head.y < 10): + self.is_running = False diff --git a/projects/example-improve/prompt b/projects/example-improve/prompt new file mode 100644 index 0000000000000000000000000000000000000000..8a8f1040737430c36b056c883ee29fd666fc3c32 --- /dev/null +++ b/projects/example-improve/prompt @@ -0,0 +1 @@ +If up is pressed, the snake should turn down and after one step to the right. If down is pressed it should go up. Also, make the snake go left when right is pressed and right when left is pressed. diff --git a/projects/example-improve/requirements.txt b/projects/example-improve/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a2190089a89b2d5bceef5a6792fb7c8e0915291 --- /dev/null +++ b/projects/example-improve/requirements.txt @@ -0,0 +1 @@ +keyboard==0.13.5 diff --git a/projects/example-improve/run.sh b/projects/example-improve/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..75d32313feb38866cb08cc229b9ed3b1448497a6 --- /dev/null +++ b/projects/example-improve/run.sh @@ -0,0 +1,7 @@ +# a) Install dependencies +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt + +# b) Run all necessary parts of the codebase +python main.py diff --git a/projects/example-improve/view.py b/projects/example-improve/view.py new file mode 100644 index 0000000000000000000000000000000000000000..78421e51241af8cdc0099a2d56724dd968a1e134 --- /dev/null +++ b/projects/example-improve/view.py @@ -0,0 +1,19 @@ +from model import Point + + +class View: + def __init__(self, game): + self.game = game + + def render(self): + # Print the game state + for y in range(10): + for x in range(10): + if Point(x, y) in self.game.snake: + print("S", end="") + elif Point(x, y) == self.game.food: + print("F", end="") + else: + print(".", end="") + print() + print() diff --git a/projects/example-vision/images/ux_diagram.png b/projects/example-vision/images/ux_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..f71d1ae8a3de09fc44e43cdcba897b0954e90453 Binary files /dev/null and b/projects/example-vision/images/ux_diagram.png differ diff --git a/projects/example-vision/navigation.html b/projects/example-vision/navigation.html new file mode 100644 index 0000000000000000000000000000000000000000..5f56518205c115924029d7b006c618791b4e7fd6 --- /dev/null +++ b/projects/example-vision/navigation.html @@ -0,0 +1,13 @@ + + + + + + diff --git a/projects/example-vision/prompt b/projects/example-vision/prompt new file mode 100644 index 0000000000000000000000000000000000000000..cc8f255c0d905bd6cd70a179336c3c920f80be0a --- /dev/null +++ b/projects/example-vision/prompt @@ -0,0 +1 @@ +Alter the nav so it looks like the ux diagram provided diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..bb229c8c09efb4709f99de6516b3227279b998c9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,118 @@ +[tool.poetry] +name = "gpt-engineer" +version = "0.3.1" +description = "Specify what you want it to build, the AI asks for clarification, and then builds it." +authors = ["Anton Osika "] +license = "MIT" +readme = "README.md" +homepage = "https://github.com/gpt-engineer-org/gpt-engineer" +repository = "https://github.com/gpt-engineer-org/gpt-engineer" +documentation = "https://gpt-engineer.readthedocs.io/en/latest/" +classifiers = [ + "Development Status :: 4 - Beta", + "Topic :: Scientific/Engineering :: Artificial Intelligence", +] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.dependencies] +python = ">=3.10,<3.13" +openai = "^1.0" +termcolor = "2.3.0" +typer = ">=0.3.2" +rudder-sdk-python = ">=2.0.2" +dataclasses-json = "0.5.7" +tiktoken = ">=0.0.4" +tabulate = "0.9.0" +python-dotenv = ">=0.21.0" +langchain = ">=0.1.2" +langchain_openai = "*" +toml = ">=0.10.2" +tomlkit = "^0.12.4" +pyperclip = "^1.8.2" +langchain-anthropic = "^0.1.1" +regex = "^2023.12.25" +pillow = "^10.2.0" +datasets = "^2.17.1" +black = "23.3.0" +langchain-community = "^0.2.0" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.3.1" +pytest-cov = "^4.1.0" +mypy = "1.3.0" +ruff = ">=0.0.272" +pre-commit = "3.3.3" +tox = ">=3.0.0" + +[tool.poetry.group.docs.dependencies] +autodoc_pydantic = ">=1.8.0" +myst_parser = ">=0.18.1" +nbsphinx = ">=0.8.9" +sphinx = ">=5.0.0" +sphinx-autobuild = ">=2021.3.14" +sphinx_book_theme = ">=0.3.3" +sphinx_rtd_theme = ">=1.0.0" +sphinx-typlog-theme = ">=0.8.0" +toml = ">=0.10.2" +myst-nb = ">=0.17.1" +linkchecker = ">=10.2.1" +sphinx-copybutton = ">=0.5.1" +markdown-include = ">=0.6.0" +sphinx_copybutton = ">=0.5.2" + +[tool.poetry.scripts] +gpt-engineer = 'gpt_engineer.applications.cli.main:app' +ge = 'gpt_engineer.applications.cli.main:app' +gpte = 'gpt_engineer.applications.cli.main:app' +bench = 'gpt_engineer.benchmark.__main__:app' +gpte_test_application = 'tests.caching_main:app' + +[tool.poetry.extras] +test = ["pytest", "pytest-cov"] +doc = [ + "autodoc_pydantic", + "myst_parser", + "nbsphinx", + "sphinx", + "sphinx-autobuild", + "sphinx_book_theme", + "sphinx_rtd_theme", + "sphinx-typlog-theme", + "myst-nb", + "linkchecker", + "sphinx-copybutton", + "markdown-include", + "sphinx_copybutton", +] + +[tool.ruff] +select = ["F", "E", "W", "I001"] +show-fixes = false +target-version = "py310" +task-tags = ["TODO", "FIXME"] +extend-ignore = ["E501", "E722"] + +[tool.black] +target-version = ["py310"] + +[tool.ruff.isort] +known-first-party = [] +known-third-party = [] +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder", +] +combine-as-imports = true +split-on-trailing-comma = false +lines-between-types = 1 + +[tool.pytest.ini_options] +markers = [ + "requires_key: marks tests as requiring access to a valid OPENAI_API_KEY (deselect with '-m \"not requires_key\"')", +] diff --git a/scripts/clean_benchmarks.py b/scripts/clean_benchmarks.py new file mode 100644 index 0000000000000000000000000000000000000000..b154bcd8af9269ba369f5f7f7f9199f731c226ce --- /dev/null +++ b/scripts/clean_benchmarks.py @@ -0,0 +1,43 @@ +""" +This module provides functionality to clean up benchmark directories by removing +all files and folders except for 'prompt' and 'main_prompt'. +""" + +# list all folders in benchmark folder +# for each folder, run the benchmark + +import os +import shutil + +from pathlib import Path + +from typer import run + + +def main(): + """ + Main function that iterates through all directories in the 'benchmark' folder + and cleans them by removing all files and directories except for 'prompt' and + 'main_prompt'. + """ + + benchmarks = Path("benchmark") + + for benchmark in benchmarks.iterdir(): + if benchmark.is_dir(): + print(f"Cleaning {benchmark}") + for path in benchmark.iterdir(): + if path.name in ["prompt", "main_prompt"]: + continue + + # Get filename of Path object + if path.is_dir(): + # delete the entire directory + shutil.rmtree(path) + else: + # delete the file + os.remove(path) + + +if __name__ == "__main__": + run(main) diff --git a/scripts/legacy_benchmark.py b/scripts/legacy_benchmark.py new file mode 100644 index 0000000000000000000000000000000000000000..957195238d9f6809d66112f913c73f75a8a760a7 --- /dev/null +++ b/scripts/legacy_benchmark.py @@ -0,0 +1,226 @@ +""" +This module provides functionality to run benchmarks on different folders within +the 'benchmark' directory, wait for their completion, and generate a report. +""" + +# list all folders in benchmark folder +# for each folder, run the benchmark +import contextlib +import json +import os +import subprocess + +from datetime import datetime +from itertools import islice +from pathlib import Path +from typing import Iterable, Union + +from tabulate import tabulate +from typer import run + + +def main( + n_benchmarks: Union[int, None] = None, +): + """ + Main function that runs benchmarks on folders within the 'benchmark' directory. + + Parameters + ---------- + n_benchmarks : Union[int, None], optional + The number of benchmarks to run. If None, all benchmarks are run. + + """ + + path = Path("benchmark") + + folders: Iterable[Path] = path.iterdir() + + if n_benchmarks: + folders = islice(folders, n_benchmarks) + + benchmarks = [] + results = [] + for bench_folder in folders: + if os.path.isdir(bench_folder): + print(f"Running benchmark for {bench_folder}") + + log_path = bench_folder / "log.txt" + log_file = open(log_path, "w") + process = subprocess.Popen( + [ + "python", + "-u", # Unbuffered output + "-m", + "gpt_engineer.cli.main", + bench_folder, + "--steps", + "benchmark", + ], + stdout=log_file, + stderr=log_file, + bufsize=0, + ) + benchmarks.append(bench_folder) + results.append((process, log_file)) + + print("You can stream the log file by running:") + print(f"tail -f {log_path}") + print() + + for bench_folder, (process, file) in zip(benchmarks, results): + process.wait() + file.close() + + print("process", bench_folder.name, "finished with code", process.returncode) + print("Running it. Original benchmark prompt:") + print() + with open(bench_folder / "prompt") as f: + print(f.read()) + print() + + with contextlib.suppress(KeyboardInterrupt): + subprocess.run( + [ + "python", + "-m", + "gpt_engineer.cli.main", + bench_folder, + "--steps", + "evaluate", + ], + ) + + generate_report(benchmarks, path) + + +def generate_report(benchmarks, benchmark_path): + """ + Generates a report of the benchmark results and optionally appends it to a markdown file. + + Parameters + ---------- + benchmarks : list + A list of benchmark folder paths that have been processed. + benchmark_path : Path + The path to the benchmark directory. + + """ + + headers = ["Benchmark", "Ran", "Works", "Perfect", "Notes"] + rows = [] + for bench_folder in benchmarks: + memory = bench_folder / ".gpteng" / "memory" + with open(memory / "review") as f: + review = json.loads(f.read()) + rows.append( + [ + bench_folder.name, + to_emoji(review.get("ran", None)), + to_emoji(review.get("works", None)), + to_emoji(review.get("perfect", None)), + review.get("comments", None), + ] + ) + table: str = tabulate(rows, headers, tablefmt="pipe") + print("\nBenchmark report:\n") + print(table) + print() + append_to_results = ask_yes_no("Append report to the results file?") + if append_to_results: + results_path = benchmark_path / "RESULTS.md" + current_date = datetime.now().strftime("%Y-%m-%d") + insert_markdown_section(results_path, current_date, table, 2) + + +def to_emoji(value: bool) -> str: + """ + Converts a boolean value to its corresponding emoji representation. + + Parameters + ---------- + value : bool + The boolean value to convert. + + Returns + ------- + str + An emoji string representing the boolean value. + + """ + + return "\U00002705" if value else "\U0000274C" + + +def insert_markdown_section(file_path, section_title, section_text, level): + """ + Inserts a new section into a markdown file at the specified level. + + Parameters + ---------- + file_path : Path + The path to the markdown file. + section_title : str + The title of the section to insert. + section_text : str + The text content of the section to insert. + level : int + The header level of the section. + + """ + + with open(file_path, "r") as file: + lines = file.readlines() + + header_prefix = "#" * level + new_section = f"{header_prefix} {section_title}\n\n{section_text}\n\n" + + # Find the first section with the specified level + line_number = -1 + for i, line in enumerate(lines): + if line.startswith(header_prefix): + line_number = i + break + + if line_number != -1: + lines.insert(line_number, new_section) + else: + print( + f"Markdown file was of unexpected format. No section of level {level} found. " + "Did not write results." + ) + return + + # Write the file + with open(file_path, "w") as file: + file.writelines(lines) + + +def ask_yes_no(question: str) -> bool: + """ + Asks a yes/no question and returns the response as a boolean value. + + Parameters + ---------- + question : str + The yes/no question to ask. + + Returns + ------- + bool + True if the answer is 'yes', False if 'no'. + + """ + + while True: + response = input(question + " (y/n): ").lower().strip() + if response == "y": + return True + elif response == "n": + return False + else: + print("Please enter either 'y' or 'n'.") + + +if __name__ == "__main__": + run(main) diff --git a/scripts/print_chat.py b/scripts/print_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..b01f5ab0d30e3d56e426bcf16cf15ba3ab3c92c5 --- /dev/null +++ b/scripts/print_chat.py @@ -0,0 +1,77 @@ +""" +This module provides functionality to print a conversation with messages +colored according to the role of the speaker. +""" + +import json + +import typer + +from termcolor import colored + +app = typer.Typer() + + +def pretty_print_conversation(messages): + """ + Prints a conversation with messages formatted and colored by role. + + Parameters + ---------- + messages : list + A list of message dictionaries, each containing 'role', 'name', and 'content' keys. + + """ + + role_to_color = { + "system": "red", + "user": "green", + "assistant": "blue", + "function": "magenta", + } + formatted_messages = [] + for message in messages: + if message["role"] == "function": + formatted_messages.append( + f"function ({message['name']}): {message['content']}\n" + ) + else: + assistant_content = ( + message["function_call"] + if message.get("function_call") + else message["content"] + ) + role_to_message = { + "system": f"system: {message['content']}\n", + "user": f"user: {message['content']}\n", + "assistant": f"assistant: {assistant_content}\n", + } + formatted_messages.append(role_to_message[message["role"]]) + + for formatted_message in formatted_messages: + role = messages[formatted_messages.index(formatted_message)]["role"] + color = role_to_color[role] + print(colored(formatted_message, color)) + + +@app.command() +def main( + messages_path: str, +): + """ + Main function that loads messages from a JSON file and prints them using pretty formatting. + + Parameters + ---------- + messages_path : str + The file path to the JSON file containing the messages. + + """ + with open(messages_path) as f: + messages = json.load(f) + + pretty_print_conversation(messages) + + +if __name__ == "__main__": + app() diff --git a/scripts/test_api.py b/scripts/test_api.py new file mode 100644 index 0000000000000000000000000000000000000000..89d3f646d584c2e82c76dec8cbc6af240eb79b4d --- /dev/null +++ b/scripts/test_api.py @@ -0,0 +1,47 @@ +"""This is just a demo to test api.py.""" + +from time import sleep + +import requests + + +def post_data(url, extra_arguments): + """ + Make an HTTP POST request with extra_arguments as data. + + Parameters + ---------- + url : str + The URL to which the POST request should be sent. + extra_arguments : dict + A dictionary of data that needs to be sent in the POST request. + + Returns + ------- + response + The response from the server. + """ + + response = requests.post(url, json=extra_arguments) + return response + + +if __name__ == "__main__": + URL_BASE = "http://127.0.0.1:8000" + + arguments = { + "input": "We are writing snake in python. MVC components split \ + in separate files. Keyboard control.", # our prompt + "additional_input": {"improve_option": False}, + } + + # create a task + response = post_data(f"{URL_BASE}/agent/tasks", arguments) + print(response.json()) + task_id = response.json()["task_id"] + + sleep(1) # this is not needed + + # execute the step for our task + response = post_data(f"{URL_BASE}/agent/tasks/{task_id}/steps", {}) + print(response.json()) diff --git a/sweep.yaml b/sweep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a4aac759378e6923dfe7f5ac7cf70e809a11ac07 --- /dev/null +++ b/sweep.yaml @@ -0,0 +1,18 @@ +# Sweep AI turns bug fixes & feature requests into code changes (https://sweep.dev) +# For details on our config file, check out our docs at https://docs.sweep.dev + +# If you use this be sure to frequently sync your default branch(main, master) to dev. +branch: 'main' +# By default Sweep will read the logs and outputs from your existing Github Actions. To disable this, set this to false. +gha_enabled: True +description: 'Python project for AI code generation with next token prediction LLMs. Various AI steps are carried out in steps.py. Disk access via DB objects.' + +# Default Values: https://github.com/sweepai/sweep/blob/main/sweep.yaml +sandbox: + install: + - pre-commit install + check: + - pre-commit run --files {file_path} + +rules: + - Ensure all new functions and classes have very clear, concise and up-to-date docstrings. Take gpt_engineer/ai.py as a good example. diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/ai_cache.json b/tests/ai_cache.json new file mode 100644 index 0000000000000000000000000000000000000000..2c8aa2ba37d6754fa8ea74dc1ac6039282745abc --- /dev/null +++ b/tests/ai_cache.json @@ -0,0 +1 @@ +{"[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a program that prints 'Hello World!' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a program that prints 'Hello World!' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a Python program that writes 'Hello World!' to a file called 'output.txt', we will need the following files:\\n\\n1. `main.py`: This will be the entry point of our program. It will contain the main logic to write 'Hello World!' to the file.\\n2. `requirements.txt`: This file will list all the Python package dependencies required to run the program. In this case, there are no external dependencies, so this file will be empty.\\n\\nLet's start with the `main.py` file:\\n\\nmain.py\\n```python\\ndef write_hello_world_to_file(filename):\\n \\\"\\\"\\\"\\n Writes 'Hello World!' to the specified file.\\n \\n :param filename: The name of the file to write to.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write('Hello World!')\\n\\nif __name__ == \\\"__main__\\\":\\n output_filename = 'output.txt'\\n write_hello_world_to_file(output_filename)\\n```\\n\\nNow, let's create the `requirements.txt` file, which will be empty as mentioned:\\n\\nrequirements.txt\\n```\\n# No external dependencies required\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No external dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No external dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a program that prints 'Hello World!' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"You will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a program that prints 'Hello World!' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"You will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"hello_world.py\\n```\\nwith open('output.txt', 'w') as file:\\n file.write('Hello World!\\\\n')\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: hello_world.py\\n1 with open('output.txt', 'w') as file:\\n2 file.write('Hello World!\\\\n')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: hello_world.py\\n1 with open('output.txt', 'w') as file:\\n2 file.write('Hello World!\\\\n')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 hello_world.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Given some instructions, determine if anything needs to be clarified, do not carry them out.\\nYou can make reasonable assumptions, but if you are unsure, ask a single clarification question.\\nOtherwise state: \\\"Nothing to clarify\\\"\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Given some instructions, determine if anything needs to be clarified, do not carry them out.\\nYou can make reasonable assumptions, but if you are unsure, ask a single clarification question.\\nOtherwise state: \\\"Nothing to clarify\\\"\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification question: Should the program be capable of being written in either Python or JavaScript based on user choice, or do you want two separate versions of the program, one in Python and one in JavaScript?\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Given some instructions, determine if anything needs to be clarified, do not carry them out.\\nYou can make reasonable assumptions, but if you are unsure, ask a single clarification question.\\nOtherwise state: \\\"Nothing to clarify\\\"\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification question: Should the program be capable of being written in either Python or JavaScript based on user choice, or do you want two separate versions of the program, one in Python and one in JavaScript?\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"y\\n \\n\\n\\n Is anything else unclear? If yes, ask another question.\\n\\n Otherwise state: \\\"Nothing to clarify\\\"\\n \", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Given some instructions, determine if anything needs to be clarified, do not carry them out.\\nYou can make reasonable assumptions, but if you are unsure, ask a single clarification question.\\nOtherwise state: \\\"Nothing to clarify\\\"\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification question: Should the program be capable of being written in either Python or JavaScript based on user choice, or do you want two separate versions of the program, one in Python and one in JavaScript?\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"y\\n \\n\\n\\n Is anything else unclear? If yes, ask another question.\\n\\n Otherwise state: \\\"Nothing to clarify\\\"\\n \", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification question: Should the program be capable of being written in either Python or JavaScript based on user choice, or do you want two separate versions of the program, one in Python and one in JavaScript?\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"y\\n \\n\\n\\n Is anything else unclear? If yes, ask another question.\\n\\n Otherwise state: \\\"Nothing to clarify\\\"\\n \", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Think step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification question: Should the program be capable of being written in either Python or JavaScript based on user choice, or do you want two separate versions of the program, one in Python and one in JavaScript?\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"y\\n \\n\\n\\n Is anything else unclear? If yes, ask another question.\\n\\n Otherwise state: \\\"Nothing to clarify\\\"\\n \", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Think step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Since the user has not specified a preference for Python or JavaScript, I will choose Python for this implementation. The program will be simple, consisting of a single script that writes \\\"Hello World!\\\" to a file named `output.txt`.\\n\\nThe core function necessary for this task is a function that encapsulates the file writing operation. This function will be named `write_hello_world` and its purpose is to open a file in write mode and output the string \\\"Hello World!\\\" to it.\\n\\nLet's start with the entrypoint file:\\n\\nmain.py\\n```python\\ndef write_hello_world(file_path):\\n with open(file_path, 'w') as file:\\n file.write(\\\"Hello World!\\\\n\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n write_hello_world('output.txt')\\n```\\n\\nSince this is a very simple Python script, there are no additional imports or classes needed, and no external dependencies to include in a `requirements.txt` file. The script can be run directly in a Python environment.\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world(file_path):\\n2 with open(file_path, 'w') as file:\\n3 file.write(\\\"Hello World!\\\\n\\\")\\n4 \\n5 if __name__ == \\\"__main__\\\":\\n6 write_hello_world('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world(file_path):\\n2 with open(file_path, 'w') as file:\\n3 file.write(\\\"Hello World!\\\\n\\\")\\n4 \\n5 if __name__ == \\\"__main__\\\":\\n6 write_hello_world('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Prompt(text=\\\"Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\\\", image_urls=None)\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Prompt(text=\\\"Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\\\", image_urls=None)\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```diff\\n--- main.py\\n+++ main.py\\n@@ -7,3 +7,3 @@\\n with open(filename, 'w') as file:\\n- file.write('Hello World!')\\n+ file.write('!dlroW olleH')\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a Python program that writes 'hello' to a file called 'output.txt', we will need the following components:\\n\\n1. `main.py`: The entry point of the program that contains the main logic to write 'hello' to the file.\\n2. `file_writer.py`: A utility module that provides a function to write text to a file.\\n3. `requirements.txt`: A file to list the dependencies for the project, although for this simple task, no external dependencies are required.\\n\\nLet's start by creating the `main.py` file:\\n\\nmain.py\\n```python\\nfrom file_writer import write_to_file\\n\\ndef main():\\n # The text to be written to the file\\n text_to_write = \\\"hello\\\"\\n # The filename of the file to write to\\n output_filename = \\\"output.txt\\\"\\n # Write the text to the file\\n write_to_file(output_filename, text_to_write)\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n```\\n\\nNow, let's create the `file_writer.py` file that contains the `write_to_file` function:\\n\\nfile_writer.py\\n```python\\ndef write_to_file(filename, text):\\n \\\"\\\"\\\"\\n Writes the given text to a file with the specified filename.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(text)\\n```\\n\\nSince this program does not require any external dependencies, the `requirements.txt` file will be empty but included for completeness:\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 from file_writer import write_to_file\\n2 \\n3 def main():\\n4 # The text to be written to the file\\n5 text_to_write = \\\"hello\\\"\\n6 # The filename of the file to write to\\n7 output_filename = \\\"output.txt\\\"\\n8 # Write the text to the file\\n9 write_to_file(output_filename, text_to_write)\\n10 \\n11 if __name__ == \\\"__main__\\\":\\n12 main()\\n\\nFile: file_writer.py\\n1 def write_to_file(filename, text):\\n2 \\\"\\\"\\\"\\n3 Writes the given text to a file with the specified filename.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write(text)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 from file_writer import write_to_file\\n2 \\n3 def main():\\n4 # The text to be written to the file\\n5 text_to_write = \\\"hello\\\"\\n6 # The filename of the file to write to\\n7 output_filename = \\\"output.txt\\\"\\n8 # Write the text to the file\\n9 write_to_file(output_filename, text_to_write)\\n10 \\n11 if __name__ == \\\"__main__\\\":\\n12 main()\\n\\nFile: file_writer.py\\n1 def write_to_file(filename, text):\\n2 \\\"\\\"\\\"\\n3 Writes the given text to a file with the specified filename.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write(text)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 The program will be written in this file\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Prompt(text=\\\"Make a python program that writes 'hello' to a file called 'output.txt'\\\", image_urls=None)\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 The program will be written in this file\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Prompt(text=\\\"Make a python program that writes 'hello' to a file called 'output.txt'\\\", image_urls=None)\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a Python program that writes 'hello' to a file called 'output.txt', we will need to perform the following steps:\\n\\n1. Open the file 'output.txt' in write mode.\\n2. Write the string 'hello' to the file.\\n3. Close the file to ensure the data is written and the file is not left open.\\n\\nLet's start by creating the main Python script (`main.py`) that will contain the logic to write to the file.\\n\\n```diff\\n--- /dev/null\\n+++ main.py\\n@@ -0,0 +1,10 @@\\n+def write_hello_to_file(filename):\\n+ \\\"\\\"\\\"Write the string 'hello' to the specified file.\\\"\\\"\\\"\\n+ with open(filename, 'w') as file:\\n+ file.write('hello')\\n+\\n+if __name__ == '__main__':\\n+ output_filename = 'output.txt'\\n+ write_hello_to_file(output_filename)\\n+ print(f\\\"'hello' has been written to {output_filename}\\\")\\n```\\n\\nThis code defines a function `write_hello_to_file` that takes a filename as an argument and writes 'hello' to that file. The `if __name__ == '__main__':` block ensures that the code runs only if the script is executed as the main program and not when imported as a module.\\n\\nNext, we will create a `requirements.txt` file to list any dependencies. Since this script uses only the standard library, the `requirements.txt` will be empty, but it's a good practice to include it for future extensibility.\\n\\n```diff\\n--- /dev/null\\n+++ requirements.txt\\n@@ -0,0 +1 @@\\n+# No dependencies required for this script\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"You will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"You will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"write_hello.py\\n```\\n# This Python script writes 'hello' to a file called 'output.txt'\\n\\nwith open('output.txt', 'w') as file:\\n file.write('hello')\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: write_hello.py\\n1 # This Python script writes 'hello' to a file called 'output.txt'\\n2 \\n3 with open('output.txt', 'w') as file:\\n4 file.write('hello')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: write_hello.py\\n1 # This Python script writes 'hello' to a file called 'output.txt'\\n2 \\n3 with open('output.txt', 'w') as file:\\n4 file.write('hello')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 -m venv venv\\nsource venv/bin/activate\\npip install -r requirements.txt\\npython write_hello.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Given some instructions, determine if anything needs to be clarified, do not carry them out.\\nYou can make reasonable assumptions, but if you are unsure, ask a single clarification question.\\nOtherwise state: \\\"Nothing to clarify\\\"\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Given some instructions, determine if anything needs to be clarified, do not carry them out.\\nYou can make reasonable assumptions, but if you are unsure, ask a single clarification question.\\nOtherwise state: \\\"Nothing to clarify\\\"\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Think step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Think step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To achieve the goal of writing 'hello' to a file called 'output.txt', we will need a simple Python script with a main function that handles the file writing. There is no need for multiple classes or complex structures for this task. The script will be self-contained in a single file.\\n\\nHere is the content of the Python script:\\n\\nmain.py\\n```\\ndef write_hello_to_file(filename):\\n \\\"\\\"\\\"\\n Writes 'hello' to the specified file.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write('hello')\\n\\nif __name__ == '__main__':\\n write_hello_to_file('output.txt')\\n```\\n\\nThis script defines a function `write_hello_to_file` that takes a filename as an argument and writes the string 'hello' to it. The `if __name__ == '__main__':` block checks if the script is being run directly (not imported as a module) and if so, it calls the `write_hello_to_file` function with 'output.txt' as the argument.\\n\\nSince this is a very basic script, there are no additional dependencies, so there is no need for a `requirements.txt` file.\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'hello' to the specified file.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write('hello')\\n7 \\n8 if __name__ == '__main__':\\n9 write_hello_to_file('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'hello' to the specified file.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write('hello')\\n7 \\n8 if __name__ == '__main__':\\n9 write_hello_to_file('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: make a program that prints the outcome of 4+4\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nFirst lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nRepresent files like so:\\n\\nFILENAME\\n```\\nCODE\\n```\\n\\nThe following tokens must be replaced like so:\\nFILENAME is the lowercase combined path and file name including the file extension\\nCODE is the code in the file\\n\\nExample representation of a file:\\n\\nsrc/hello_world.py\\n```\\nprint(\\\"Hello World\\\")\\n```\\n\\nDo not comment on what every file does. Please note that the code should be fully functional. No placeholders.\\n\\n\\nYou will start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: make a program that prints the outcome of 4+4\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a simple Python program that prints the outcome of the operation 4+4, we will need a single Python file as the entry point. We will follow the best practices for Python file naming and structure. Since this is a very simple program, we do not need multiple classes or complex architecture. We will also include a `requirements.txt` file, although for such a simple script, it will be empty as no external libraries are required.\\n\\nHere is the content of the Python script:\\n\\nmain.py\\n```python\\ndef calculate_and_print_sum(a, b):\\n \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n result = a + b\\n print(result)\\n\\nif __name__ == \\\"__main__\\\":\\n calculate_and_print_sum(4, 4)\\n```\\n\\nAnd the `requirements.txt` file:\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nFrom this you will answer with code blocks that includes all the necessary unix terminal commands to:\\na) install dependencies\\nb) run all necessary parts of the codebase (in parallel if necessary)\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No external dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No external dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies (none required as per requirements.txt, but this is how you would do it)\\n# pip install -r requirements.txt\\n\\n# b) Run the main.py script\\npython main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: hello_world.py\\n1 with open('output.txt', 'w') as file:\\n2 file.write('Hello World!\\\\n')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: hello_world.py\\n1 with open('output.txt', 'w') as file:\\n2 file.write('Hello World!\\\\n')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# No dependencies are needed for this Python script\\n\\n# b) Run the necessary parts of the codebase\\npython hello_world.py &\\nwait\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world(file_path):\\n2 with open(file_path, 'w') as file:\\n3 file.write(\\\"Hello World!\\\\n\\\")\\n4 \\n5 if __name__ == \\\"__main__\\\":\\n6 write_hello_world('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world(file_path):\\n2 with open(file_path, 'w') as file:\\n3 file.write(\\\"Hello World!\\\\n\\\")\\n4 \\n5 if __name__ == \\\"__main__\\\":\\n6 write_hello_world('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# No dependencies are needed for this script.\\n\\n# b) Run the codebase\\npython main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 from file_writer import write_to_file\\n2 \\n3 def main():\\n4 # The text to be written to the file\\n5 text_to_write = \\\"hello\\\"\\n6 # The filename of the file to write to\\n7 output_filename = \\\"output.txt\\\"\\n8 # Write the text to the file\\n9 write_to_file(output_filename, text_to_write)\\n10 \\n11 if __name__ == \\\"__main__\\\":\\n12 main()\\n\\nFile: file_writer.py\\n1 def write_to_file(filename, text):\\n2 \\\"\\\"\\\"\\n3 Writes the given text to a file with the specified filename.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write(text)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 from file_writer import write_to_file\\n2 \\n3 def main():\\n4 # The text to be written to the file\\n5 text_to_write = \\\"hello\\\"\\n6 # The filename of the file to write to\\n7 output_filename = \\\"output.txt\\\"\\n8 # Write the text to the file\\n9 write_to_file(output_filename, text_to_write)\\n10 \\n11 if __name__ == \\\"__main__\\\":\\n12 main()\\n\\nFile: file_writer.py\\n1 def write_to_file(filename, text):\\n2 \\\"\\\"\\\"\\n3 Writes the given text to a file with the specified filename.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write(text)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies (none required as per requirements.txt, but this is how you would do it)\\n# pip install -r requirements.txt\\n\\n# b) Run the main.py script\\npython main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: write_hello.py\\n1 # This Python script writes 'hello' to a file called 'output.txt'\\n2 \\n3 with open('output.txt', 'w') as file:\\n4 file.write('hello')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: write_hello.py\\n1 # This Python script writes 'hello' to a file called 'output.txt'\\n2 \\n3 with open('output.txt', 'w') as file:\\n4 file.write('hello')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# No dependencies are needed for this script\\n\\n# b) Run the necessary parts of the codebase\\npython write_hello.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'hello' to the specified file.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write('hello')\\n7 \\n8 if __name__ == '__main__':\\n9 write_hello_to_file('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'hello' to the specified file.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write('hello')\\n7 \\n8 if __name__ == '__main__':\\n9 write_hello_to_file('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# Since the script does not have any dependencies, we skip this step.\\n\\n# b) Run all necessary parts of the codebase\\npython main.py &\\nwait\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n\\n Information about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/sh\\n\\n# a) Install dependencies (none required as per requirements.txt, but this is how you would do it)\\n# pip install -r requirements.txt\\n\\n# b) Run the main.py script\\npython main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"promptInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"promptInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No external dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No external dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies (none required as per requirements.txt, but this is how you would do it)\\n# pip install -r requirements.txt\\n\\n# b) Run the main.py script\\npython main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: hello_world.py\\n1 with open('output.txt', 'w') as file:\\n2 file.write('Hello World!\\\\n')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: hello_world.py\\n1 with open('output.txt', 'w') as file:\\n2 file.write('Hello World!\\\\n')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# There are no external dependencies to install for this Python script.\\n\\n# b) Run the necessary parts of the codebase\\npython hello_world.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world(file_path):\\n2 with open(file_path, 'w') as file:\\n3 file.write(\\\"Hello World!\\\\n\\\")\\n4 \\n5 if __name__ == \\\"__main__\\\":\\n6 write_hello_world('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world(file_path):\\n2 with open(file_path, 'w') as file:\\n3 file.write(\\\"Hello World!\\\\n\\\")\\n4 \\n5 if __name__ == \\\"__main__\\\":\\n6 write_hello_world('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# Assuming Python3 is already installed and there are no external dependencies for this script\\n# If there were a requirements.txt file, you would typically run:\\n# python3 -m pip install --user -r requirements.txt\\n\\n# b) Run the codebase\\npython3 main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 from file_writer import write_to_file\\n2 \\n3 def main():\\n4 # The text to be written to the file\\n5 text_to_write = \\\"hello\\\"\\n6 # The filename of the file to write to\\n7 output_filename = \\\"output.txt\\\"\\n8 # Write the text to the file\\n9 write_to_file(output_filename, text_to_write)\\n10 \\n11 if __name__ == \\\"__main__\\\":\\n12 main()\\n\\nFile: file_writer.py\\n1 def write_to_file(filename, text):\\n2 \\\"\\\"\\\"\\n3 Writes the given text to a file with the specified filename.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write(text)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 from file_writer import write_to_file\\n2 \\n3 def main():\\n4 # The text to be written to the file\\n5 text_to_write = \\\"hello\\\"\\n6 # The filename of the file to write to\\n7 output_filename = \\\"output.txt\\\"\\n8 # Write the text to the file\\n9 write_to_file(output_filename, text_to_write)\\n10 \\n11 if __name__ == \\\"__main__\\\":\\n12 main()\\n\\nFile: file_writer.py\\n1 def write_to_file(filename, text):\\n2 \\\"\\\"\\\"\\n3 Writes the given text to a file with the specified filename.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write(text)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n# a) Since there are no dependencies required, this step is essentially a no-op.\\n# However, if there were dependencies, they would be installed like this:\\n# python3 -m pip install --user -r requirements.txt\\n\\n# b) Run the main.py script\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: write_hello.py\\n1 # This Python script writes 'hello' to a file called 'output.txt'\\n2 \\n3 with open('output.txt', 'w') as file:\\n4 file.write('hello')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: write_hello.py\\n1 # This Python script writes 'hello' to a file called 'output.txt'\\n2 \\n3 with open('output.txt', 'w') as file:\\n4 file.write('hello')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# There are no dependencies to install for this simple Python script\\n\\n# b) Run the Python script\\npython write_hello.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'hello' to the specified file.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write('hello')\\n7 \\n8 if __name__ == '__main__':\\n9 write_hello_to_file('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'hello' to the specified file.\\n4 \\\"\\\"\\\"\\n5 with open(filename, 'w') as file:\\n6 file.write('hello')\\n7 \\n8 if __name__ == '__main__':\\n9 write_hello_to_file('output.txt')\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies\\n# Assuming Python3 is already installed and there are no external dependencies\\n# for this simple script, we don't need to install anything.\\n\\n# b) Run the codebase\\npython3 main.py &\\nwait\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"prompt\\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"prompt\\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints the outcome of 4+4\\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints the outcome of 4+4\\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython main.py\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```diff\\n--- main.py\\n+++ main.py\\n@@ -7,3 +7,3 @@\\n with open(filename, 'w') as file:\\n- file.write('Hello World!')\\n+ file.write('!dlroW olleH')\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('!dlroW olleH')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('!dlroW olleH')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies (there are none in this case, but this is how you would do it)\\n# pip install -r requirements.txt\\n\\n# b) Run the necessary parts of the codebase\\nchmod +x run.sh\\n./run.sh &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 The program will be written in this file\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 The program will be written in this file\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a Python program that writes 'hello' to a file called 'output.txt', we will need to perform the following steps:\\n\\n1. Open the file 'output.txt' in write mode.\\n2. Write the string 'hello' to the file.\\n3. Close the file to ensure the data is written and the file is not left open.\\n\\nHere is the implementation in the `main.py` file:\\n\\n```diff\\n--- /dev/null\\n+++ main.py\\n@@ -0,0 +1,8 @@\\n+# main.py\\n+# This program writes 'hello' to a file called 'output.txt'\\n+\\n+with open('output.txt', 'w') as file:\\n+ file.write('hello')\\n+\\n+# This concludes the writing operation, 'output.txt' should now contain the word 'hello'\\n+\\n```\\n\\nThis concludes the implementation of the requested Python program.\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get information about a codebase that is currently on disk in the current folder.\\nThe user will ask you to write a script that runs the code in a specific way.\\nYou will answer with code blocks that include all the necessary terminal commands.\\nDo not install globally. Do not use sudo.\\nDo not explain the code, just give the commands.\\nDo not use placeholders, use example values (like . for a folder argument) if necessary.\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"\\n Make a unix script that\\n a) installs dependencies\\n b) runs all necessary parts of the codebase (in parallel if necessary)\\n \\nInformation about the codebase:\\n\\n```\\nFile: main.py\\n1 def calculate_and_print_sum(a, b):\\n2 \\\"\\\"\\\"Calculate the sum of two numbers and print the result.\\\"\\\"\\\"\\n3 result = a + b\\n4 print(result)\\n5 \\n6 if __name__ == \\\"__main__\\\":\\n7 calculate_and_print_sum(4, 4)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n#!/bin/bash\\n\\n# a) Install dependencies (none required in this case, but pip install command provided for demonstration)\\n# pip install -r requirements.txt\\n\\n# b) Run the main.py script\\npython main.py &\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!' \"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You will get instructions for code to write.\\nYou will write a very long answer. Make sure that every detail of the architecture is, in the end, implemented as code.\\nThink step by step and reason yourself to the correct decisions to make sure we get it right.\\nMake changes to existing code and implement new code in the unified git diff syntax. When implementing new code, First lay out the names of the core classes, functions, methods that will be necessary, As well as a quick comment on their purpose.\\n\\nYou will output the content of each file necessary to achieve the goal, including ALL code.\\nOutput requested code changes and new code in the unified \\\"git diff\\\" syntax. Example:\\n\\n```diff\\n--- example.txt\\n+++ example.txt\\n@@ -6,3 +6,4 @@\\n line content A\\n line content B\\n+ new line added\\n- original line X\\n+ modified line X with changes\\n@@ -26,4 +27,5 @@\\n condition check:\\n- action for condition A\\n+ if certain condition is met:\\n+ alternative action for condition A\\n another condition check:\\n- action for condition B\\n+ modified action for condition B\\n```\\n\\nExample of a git diff creating a new file:\\n\\n```diff\\n--- /dev/null\\n+++ new_file.txt\\n@@ -0,0 +1,3 @@\\n+First example line\\n+\\n+Last example line\\n```\\n-A program will apply the diffs you generate exactly to the code, so diffs must be precise and unambiguous!\\n-Every diff must be fenced with triple backtick ```.\\n-The file names at the beginning of a diff, (lines starting with --- and +++) is the relative path to the file before and after the diff.\\n-LINES TO BE REMOVED (starting with single -) AND LINES TO BE RETAIN (no starting symbol) HAVE TO REPLICATE THE DIFFED HUNK OF THE CODE EXACTLY LINE BY LINE. KEEP THE NUMBER OF RETAIN LINES SMALL IF POSSIBLE.\\n-EACH LINE IN THE SOURCE FILES STARTS WITH A LINE NUMBER, WHICH IS NOT PART OF THE SOURCE CODE. NEVER TRANSFER THESE LINE NUMBERS TO THE DIFF HUNKS.\\n-AVOID STARTING A HUNK WITH AN EMPTY LINE.\\n\\n\\nAs far as compatible with the user request, start with the \\\"entrypoint\\\" file, then go to the ones that are imported by that file, and so on.\\nPlease note that the code should be fully functional. No placeholders.\\n\\nFollow a language and framework appropriate best practice file naming convention.\\nMake sure that files contain all imports, types etc. The code should be fully functional. Make sure that code in different files are compatible with each other.\\nEnsure to implement all code, if you are unsure, write a plausible implementation.\\nInclude module dependency or package manager dependency definition file.\\nBefore you finish, double check that all parts of the architecture is present in the files.\\n\\nWhen you are done, write finish with \\\"this concludes a fully working implementation\\\".\\n\\nUseful to know:\\nAlmost always put different classes in different files.\\nAlways use the programming language the user asks for.\\nFor Python, you always create an appropriate requirements.txt file.\\nFor NodeJS, you always create an appropriate package.json file.\\nAlways add a comment briefly describing the purpose of the function definition.\\nAdd comments explaining very complex bits of logic.\\nAlways follow the best practices for the requested languages for folder/file structure and how to package the project.\\n\\n\\nPython toolbelt preferences:\\n- pytest\\n- dataclasses\\n\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"system\", \"name\": null, \"id\": null}}, {\"type\": \"human\", \"data\": {\"content\": \"```\\nFile: main.py\\n1 def write_hello_world_to_file(filename):\\n2 \\\"\\\"\\\"\\n3 Writes 'Hello World!' to the specified file.\\n4 \\n5 :param filename: The name of the file to write to.\\n6 \\\"\\\"\\\"\\n7 with open(filename, 'w') as file:\\n8 file.write('Hello World!')\\n9 \\n10 if __name__ == \\\"__main__\\\":\\n11 output_filename = 'output.txt'\\n12 write_hello_world_to_file(output_filename)\\n\\nFile: requirements.txt\\n1 # No dependencies required\\n\\nFile: run.sh\\n1 python3 main.py\\n2 \\n\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": [{\"type\": \"text\", \"text\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!' \"}], \"additional_kwargs\": {}, \"response_metadata\": {}, \"type\": \"human\", \"name\": null, \"id\": null, \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```diff\\n--- main.py\\n+++ main.py\\n@@ -7,3 +7,3 @@\\n with open(filename, 'w') as file:\\n- file.write('Hello World!')\\n+ file.write('!dlroW olleH')\\n```\", \"additional_kwargs\": {}, \"response_metadata\": {\"token_usage\": {\"completion_tokens\": 54, \"prompt_tokens\": 885, \"total_tokens\": 939}, \"model_name\": \"gpt-4-1106-preview\", \"system_fingerprint\": \"fp_76413ed44d\", \"finish_reason\": \"stop\", \"logprobs\": null}, \"type\": \"ai\", \"name\": null, \"id\": null, \"example\": false}}]"} diff --git a/tests/applications/__init__.py b/tests/applications/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/applications/cli/__init__.py b/tests/applications/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/applications/cli/test_cli_agent.py b/tests/applications/cli/test_cli_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..8bc6e93875c3428b6a2e520005e31f5e57f4b841 --- /dev/null +++ b/tests/applications/cli/test_cli_agent.py @@ -0,0 +1,154 @@ +import os +import tempfile + +import pytest + +from langchain.schema import AIMessage + +from gpt_engineer.applications.cli.cli_agent import CliAgent +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.default.disk_memory import DiskMemory + +# from gpt_engineer.core.default.git_version_manager import GitVersionManager +from gpt_engineer.core.default.paths import ENTRYPOINT_FILE, memory_path +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt +from gpt_engineer.tools.custom_steps import clarified_gen, lite_gen +from tests.mock_ai import MockAI + + +def test_init_standard_config(monkeypatch): + monkeypatch.setattr("builtins.input", lambda _: "y") + temp_dir = tempfile.mkdtemp() + memory = DiskMemory(memory_path(temp_dir)) + execution_env = DiskExecutionEnv() + mock_ai = MockAI( + [ + AIMessage( + "hello_world.py\n```\nwith open('output.txt', 'w') as file:\n file.write('Hello World!')\n```" + ), + AIMessage("```run.sh\npython3 hello_world.py\n```"), + ], + ) + cli_agent = CliAgent.with_default_config(memory, execution_env, ai=mock_ai) + outfile = "output.txt" + os.path.join(temp_dir, outfile) + code = cli_agent.init( + Prompt( + f"Make a program that prints 'Hello World!' to a file called '{outfile}'" + ) + ) + + env = DiskExecutionEnv() + env.upload(code).run(f"bash {ENTRYPOINT_FILE}") + code = env.download() + + assert outfile in code + assert code[outfile] == "Hello World!" + + +def test_init_lite_config(monkeypatch): + monkeypatch.setattr("builtins.input", lambda _: "y") + temp_dir = tempfile.mkdtemp() + memory = DiskMemory(memory_path(temp_dir)) + # version_manager = GitVersionManager(temp_dir) + execution_env = DiskExecutionEnv() + mock_ai = MockAI( + [ + AIMessage( + "hello_world.py\n```\nwith open('output.txt', 'w') as file:\n file.write('Hello World!')\n```" + ), + AIMessage("```run.sh\npython3 hello_world.py\n```"), + ], + ) + cli_agent = CliAgent.with_default_config( + memory, execution_env, ai=mock_ai, code_gen_fn=lite_gen + ) + outfile = "output.txt" + os.path.join(temp_dir, outfile) + code = cli_agent.init( + Prompt( + f"Make a program that prints 'Hello World!' to a file called '{outfile}'" + ) + ) + + env = DiskExecutionEnv() + env.upload(code).run(f"bash {ENTRYPOINT_FILE}") + code = env.download() + + assert outfile in code + assert code[outfile].strip() == "Hello World!" + + +def test_init_clarified_gen_config(monkeypatch): + monkeypatch.setattr("builtins.input", lambda _: "y") + temp_dir = tempfile.mkdtemp() + memory = DiskMemory(memory_path(temp_dir)) + execution_env = DiskExecutionEnv() + mock_ai = MockAI( + [ + AIMessage("nothing to clarify"), + AIMessage( + "hello_world.py\n```\nwith open('output.txt', 'w') as file:\n file.write('Hello World!')\n```" + ), + AIMessage("```run.sh\npython3 hello_world.py\n```"), + ], + ) + cli_agent = CliAgent.with_default_config( + memory, execution_env, ai=mock_ai, code_gen_fn=clarified_gen + ) + outfile = "output.txt" + code = cli_agent.init( + Prompt( + f"Make a program that prints 'Hello World!' to a file called '{outfile} either using python or javascript'" + ) + ) + + env = DiskExecutionEnv() + env.upload(code).run(f"bash {ENTRYPOINT_FILE}") + code = env.download() + + assert outfile in code + assert code[outfile].strip() == "Hello World!" + + +def test_improve_standard_config(monkeypatch): + monkeypatch.setattr("builtins.input", lambda _: "y") + temp_dir = tempfile.mkdtemp() + code = FilesDict( + { + "main.py": "def write_hello_world_to_file(filename):\n \"\"\"\n Writes 'Hello World!' to the specified file.\n \n :param filename: The name of the file to write to.\n \"\"\"\n with open(filename, 'w') as file:\n file.write('Hello World!')\n\nif __name__ == \"__main__\":\n output_filename = 'output.txt'\n write_hello_world_to_file(output_filename)", + "requirements.txt": "# No dependencies required", + "run.sh": "python3 main.py\n", + } + ) + memory = DiskMemory(memory_path(temp_dir)) + # version_manager = GitVersionManager(temp_dir) + execution_env = DiskExecutionEnv() + mock_ai = MockAI( + [ + AIMessage( + "```diff\n--- main.py\n+++ main.py\n@@ -7,3 +7,3 @@\n with open(filename, 'w') as file:\n- file.write('Hello World!')\n+ file.write('!dlroW olleH')\n```" + ) + ] + ) + cli_agent = CliAgent.with_default_config(memory, execution_env, ai=mock_ai) + + code = cli_agent.improve( + code, + Prompt( + "Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'" + ), + ) + + env = DiskExecutionEnv() + env.upload(code).run(f"bash {ENTRYPOINT_FILE}") + code = env.download() + + outfile = "output.txt" + assert outfile in code + assert code[outfile] == "!dlroW olleH" + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/applications/cli/test_collect.py b/tests/applications/cli/test_collect.py new file mode 100644 index 0000000000000000000000000000000000000000..7dfb368b492a74f25cf0ea643f60ec4868205660 --- /dev/null +++ b/tests/applications/cli/test_collect.py @@ -0,0 +1,50 @@ +""" +Tests the collect_learnings function in the cli/collect module. +""" + +import pytest + +# def test_collect_learnings(monkeypatch): +# monkeypatch.setattr(rudder_analytics, "track", MagicMock()) +# +# model = "test_model" +# temperature = 0.5 +# steps = [simple_gen] +# dbs = FileRepositories( +# OnDiskRepository("/tmp"), +# OnDiskRepository("/tmp"), +# OnDiskRepository("/tmp"), +# OnDiskRepository("/tmp"), +# OnDiskRepository("/tmp"), +# OnDiskRepository("/tmp"), +# OnDiskRepository("/tmp"), +# ) +# dbs.input = { +# "prompt": "test prompt\n with newlines", +# "feedback": "test feedback", +# } +# code = "this is output\n\nit contains code" +# dbs.logs = {steps[0].__name__: json.dumps([{"role": "system", "content": code}])} +# dbs.memory = {"all_output.txt": "test workspace\n" + code} +# +# collect_learnings(model, temperature, steps, dbs) +# +# learnings = extract_learning( +# model, temperature, steps, dbs, steps_file_hash=steps_file_hash() +# ) +# assert rudder_analytics.track.call_count == 1 +# assert rudder_analytics.track.call_args[1]["event"] == "learning" +# a = { +# k: v +# for k, v in rudder_analytics.track.call_args[1]["properties"].items() +# if k != "timestamp" +# } +# b = {k: v for k, v in learnings.to_dict().items() if k != "timestamp"} +# assert a == b +# +# assert json.dumps(code) in learnings.logs +# assert code in learnings.workspace + + +if __name__ == "__main__": + pytest.main(["-v"]) diff --git a/tests/applications/cli/test_collection_consent.py b/tests/applications/cli/test_collection_consent.py new file mode 100644 index 0000000000000000000000000000000000000000..5c2d66c7a401b002f4f98c81be59957294d4c48f --- /dev/null +++ b/tests/applications/cli/test_collection_consent.py @@ -0,0 +1,103 @@ +""" +Tests for the revised data collection consent mechanism in the cli/learning module. +""" + +from pathlib import Path +from unittest.mock import patch + +import pytest + +from gpt_engineer.applications.cli.learning import ( + ask_collection_consent, + check_collection_consent, +) + + +# Use a fixture to clean up created files after each test +@pytest.fixture +def cleanup(): + yield + if Path(".gpte_consent").exists(): + Path(".gpte_consent").unlink() + + +""" +Test the following 4 scenarios for check_collection_consent(): + * The .gpte_consent file exists and its content is "true". + * The .gpte_consent file exists but its content is not "true". + * The .gpte_consent file does not exist and the user gives consent when asked. + * The .gpte_consent file does not exist and the user does not give consent when asked. +""" + + +def test_check_consent_file_exists_and_true(cleanup): + Path(".gpte_consent").write_text("true") + assert check_collection_consent() is True + + +def test_check_consent_file_exists_and_false(cleanup): + Path(".gpte_consent").write_text("false") + with patch("builtins.input", side_effect=["n"]): + assert check_collection_consent() is False + + +def test_check_consent_file_not_exists_and_user_says_yes(cleanup): + with patch("builtins.input", side_effect=["y"]): + assert check_collection_consent() is True + assert Path(".gpte_consent").exists() + assert Path(".gpte_consent").read_text() == "true" + + +def test_check_consent_file_not_exists_and_user_says_no(cleanup): + with patch("builtins.input", side_effect=["n"]): + assert check_collection_consent() is False + assert not Path(".gpte_consent").exists() + + +""" +Test the following 4 scenarios for ask_collection_consent(): + 1. The user immediately gives consent with "y": + * The .gpte_consent file is created with content "true". + * The function returns True. + 2. The user immediately denies consent with "n": + * The .gpte_consent file is not created. + * The function returns False. + 3. The user first provides an invalid response, then gives consent with "y": + * The user is re-prompted after the invalid input. + * The .gpte_consent file is created with content "true". + * The function returns True. + 4. The user first provides an invalid response, then denies consent with "n": + * The user is re-prompted after the invalid input. + * The .gpte_consent file is not created. + * The function returns False. +""" + + +def test_ask_collection_consent_yes(cleanup): + with patch("builtins.input", side_effect=["y"]): + result = ask_collection_consent() + assert Path(".gpte_consent").exists() + assert Path(".gpte_consent").read_text() == "true" + assert result is True + + +def test_ask_collection_consent_no(cleanup): + with patch("builtins.input", side_effect=["n"]): + result = ask_collection_consent() + assert not Path(".gpte_consent").exists() + assert result is False + + +def test_ask_collection_consent_invalid_then_yes(cleanup): + with patch("builtins.input", side_effect=["invalid", "y"]): + result = ask_collection_consent() + assert Path(".gpte_consent").exists() + assert Path(".gpte_consent").read_text() == "true" + assert result is True + + +def test_ask_collection_consent_invalid_then_no(cleanup): + with patch("builtins.input", side_effect=["invalid", "n"]): + result = ask_collection_consent() + assert not Path(".gpte_consent").exists() + assert result is False diff --git a/tests/applications/cli/test_learning.py b/tests/applications/cli/test_learning.py new file mode 100644 index 0000000000000000000000000000000000000000..4fb6f8ebafd2cf611cbe9d61e508637116d80a10 --- /dev/null +++ b/tests/applications/cli/test_learning.py @@ -0,0 +1,110 @@ +from unittest import mock + +from gpt_engineer.applications.cli import learning +from gpt_engineer.applications.cli.learning import Learning +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.prompt import Prompt + + +def test_human_review_input_no_concent_returns_none(): + with mock.patch.object(learning, "check_collection_consent", return_value=False): + result = learning.human_review_input() + + assert result is None + + +def test_human_review_input_consent_code_ran_no_comments(): + with ( + mock.patch.object(learning, "check_collection_consent", return_value=True), + mock.patch("builtins.input", return_value="y"), + ): + result = learning.human_review_input() + + assert result.raw == "y, y, " + assert result.ran is True + assert result.works is None + assert result.comments == "" + + +def test_human_review_input_consent_code_ran_not_perfect_but_useful_no_comments(): + with ( + mock.patch.object(learning, "check_collection_consent", return_value=True), + mock.patch("builtins.input", side_effect=["y", "n", "y", ""]), + ): + result = learning.human_review_input() + + assert result.raw == "y, n, y" + assert result.ran is True + assert result.works is True + assert result.comments == "" + + +def test_check_collection_consent_yes(): + gpte_consent_mock = mock.Mock() + gpte_consent_mock.exists.return_value = True + gpte_consent_mock.read_text.return_value = "true" + + with mock.patch.object(learning, "Path", return_value=gpte_consent_mock): + result = learning.check_collection_consent() + + assert result is True + + +def test_check_collection_consent_no_ask_collection_consent(): + with mock.patch.object(learning, "Path") as gpte_consent_mock: + gpte_consent_mock.exists.return_value = True + gpte_consent_mock.read_text.return_value = "false" + + with mock.patch.object(learning, "ask_collection_consent", return_value=True): + result = learning.check_collection_consent() + + assert result is True + + +def test_ask_collection_consent_yes(): + with mock.patch("builtins.input", return_value="y"): + result = learning.ask_collection_consent() + + assert result is True + + +def test_ask_collection_consent_no(): + with mock.patch("builtins.input", return_value="n"): + result = learning.ask_collection_consent() + + assert result is False + + +def test_extract_learning(): + review = learning.Review( + raw="y, n, y", + ran=True, + works=True, + perfect=False, + comments="The code is not perfect", + ) + memory = mock.Mock(spec=DiskMemory) + memory.to_json.return_value = {"prompt": "prompt"} + + result = learning.extract_learning( + Prompt("prompt"), + "model_name", + 0.01, + ("prompt_tokens", "completion_tokens"), + memory, + review, + ) + + assert isinstance(result, Learning) + + +def test_get_session(): + with mock.patch.object(learning, "Path") as path_mock: + # can be better tested with pyfakefs. + path_mock.return_value.__truediv__.return_value.exists.return_value = False + + with mock.patch.object(learning, "random") as random_mock: + random_mock.randint.return_value = 42 + result = learning.get_session() + + assert result == "42" diff --git a/tests/applications/cli/test_main.py b/tests/applications/cli/test_main.py new file mode 100644 index 0000000000000000000000000000000000000000..1c33e7f3c99c2fd5db22f68ce440a047fd6eece2 --- /dev/null +++ b/tests/applications/cli/test_main.py @@ -0,0 +1,421 @@ +import dataclasses +import functools +import inspect +import os +import shutil +import tempfile + +from argparse import Namespace +from unittest.mock import patch + +import pytest +import typer + +import gpt_engineer.applications.cli.main as main + +from gpt_engineer.applications.cli.main import load_prompt +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.prompt import Prompt + + +@functools.wraps(dataclasses.make_dataclass) +def dcommand(typer_f, **kwargs): + required = True + + def field_desc(name, param): + nonlocal required + + t = param.annotation or "typing.Any" + if param.default.default is not ...: + required = False + return name, t, dataclasses.field(default=param.default.default) + + if not required: + raise ValueError("Required value after optional") + + return name, t + + kwargs.setdefault("cls_name", typer_f.__name__) + + params = inspect.signature(typer_f).parameters + kwargs["fields"] = [field_desc(k, v) for k, v in params.items()] + + @functools.wraps(typer_f) + def dcommand_decorator(function_or_class): + assert callable(function_or_class) + + ka = dict(kwargs) + ns = Namespace(**(ka.pop("namespace", None) or {})) + if isinstance(function_or_class, type): + ka["bases"] = *ka.get("bases", ()), function_or_class + else: + ns.__call__ = function_or_class + + ka["namespace"] = vars(ns) + return dataclasses.make_dataclass(**ka) + + return dcommand_decorator + + +@dcommand(main.main) +class DefaultArgumentsMain: + def __call__(self): + attribute_dict = vars(self) + main.main(**attribute_dict) + + +def input_generator(): + yield "y" # First response + while True: + yield "n" # Subsequent responses + + +prompt_text = "Make a python program that writes 'hello' to a file called 'output.txt'" + + +class TestMain: + # Runs gpt-engineer cli interface for many parameter configurations, BUT DOES NOT CODEGEN! Only testing cli. + def test_default_settings_generate_project(self, tmp_path, monkeypatch): + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + args = DefaultArgumentsMain(str(p), llm_via_clipboard=True, no_execution=True) + args() + + # Runs gpt-engineer with improve mode and improves an existing project in the specified path. + def test_improve_existing_project(self, tmp_path, monkeypatch): + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + args = DefaultArgumentsMain( + str(p), improve_mode=True, llm_via_clipboard=True, no_execution=True + ) + args() + + # def improve_generator(): + # yield "y" + # while True: + # yield "n" # Subsequent responses + # + # gen = improve_generator() + # monkeypatch.setattr("builtins.input", lambda _: next(gen)) + # p = tmp_path / "projects/example" + # p.mkdir(parents=True) + # (p / "prompt").write_text(prompt_text) + # (p / "main.py").write_text("The program will be written in this file") + # meta_p = p / META_DATA_REL_PATH + # meta_p.mkdir(parents=True) + # (meta_p / "file_selection.toml").write_text( + # """ + # [files] + # "main.py" = "selected" + # """ + # ) + # os.environ["GPTE_TEST_MODE"] = "True" + # simplified_main(str(p), "improve") + # DiskExecutionEnv(path=p) + # del os.environ["GPTE_TEST_MODE"] + + # Runs gpt-engineer with lite mode and generates a project with only the main prompt. + def test_lite_mode_generate_project(self, tmp_path, monkeypatch): + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + args = DefaultArgumentsMain( + str(p), lite_mode=True, llm_via_clipboard=True, no_execution=True + ) + args() + + # Runs gpt-engineer with clarify mode and generates a project after discussing the specification with the AI. + def test_clarify_mode_generate_project(self, tmp_path, monkeypatch): + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + args = DefaultArgumentsMain( + str(p), clarify_mode=True, llm_via_clipboard=True, no_execution=True + ) + args() + + # Runs gpt-engineer with self-heal mode and generates a project after discussing the specification with the AI and self-healing the code. + def test_self_heal_mode_generate_project(self, tmp_path, monkeypatch): + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + args = DefaultArgumentsMain( + str(p), self_heal_mode=True, llm_via_clipboard=True, no_execution=True + ) + args() + + def test_clarify_lite_improve_mode_generate_project(self, tmp_path, monkeypatch): + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + args = DefaultArgumentsMain( + str(p), + improve_mode=True, + lite_mode=True, + clarify_mode=True, + llm_via_clipboard=True, + no_execution=True, + ) + pytest.raises(typer.Exit, args) + + # Tests the creation of a log file in improve mode. + + +class TestLoadPrompt: + # Load prompt from existing file in input_repo + def test_load_prompt_existing_file(self): + with tempfile.TemporaryDirectory() as tmp_dir: + input_repo = DiskMemory(tmp_dir) + prompt_file = "prompt.txt" + prompt_content = "This is the prompt" + input_repo[prompt_file] = prompt_content + + improve_mode = False + image_directory = "" + + result = load_prompt(input_repo, improve_mode, prompt_file, image_directory) + + assert isinstance(result, Prompt) + assert result.text == prompt_content + assert result.image_urls is None + + # Prompt file does not exist in input_repo, and improve_mode is False + def test_load_prompt_no_file_improve_mode_false(self): + with tempfile.TemporaryDirectory() as tmp_dir: + input_repo = DiskMemory(tmp_dir) + prompt_file = "prompt.txt" + + improve_mode = False + image_directory = "" + + with patch( + "builtins.input", + return_value="What application do you want gpt-engineer to generate?", + ): + result = load_prompt( + input_repo, improve_mode, prompt_file, image_directory + ) + + assert isinstance(result, Prompt) + assert ( + result.text == "What application do you want gpt-engineer to generate?" + ) + assert result.image_urls is None + + # Prompt file is a directory + def test_load_prompt_directory_file(self): + with tempfile.TemporaryDirectory() as tmp_dir: + input_repo = DiskMemory(tmp_dir) + prompt_file = os.path.join(tmp_dir, "prompt") + + os.makedirs(os.path.join(tmp_dir, prompt_file)) + + improve_mode = False + image_directory = "" + + with pytest.raises(ValueError): + load_prompt(input_repo, improve_mode, prompt_file, image_directory) + + # Prompt file is empty + def test_load_prompt_empty_file(self): + with tempfile.TemporaryDirectory() as tmp_dir: + input_repo = DiskMemory(tmp_dir) + prompt_file = "prompt.txt" + input_repo[prompt_file] = "" + + improve_mode = False + image_directory = "" + + with patch( + "builtins.input", + return_value="What application do you want gpt-engineer to generate?", + ): + result = load_prompt( + input_repo, improve_mode, prompt_file, image_directory + ) + + assert isinstance(result, Prompt) + assert ( + result.text == "What application do you want gpt-engineer to generate?" + ) + assert result.image_urls is None + + # image_directory does not exist in input_repo + def test_load_prompt_no_image_directory(self): + with tempfile.TemporaryDirectory() as tmp_dir: + input_repo = DiskMemory(tmp_dir) + prompt_file = "prompt.txt" + prompt_content = "This is the prompt" + input_repo[prompt_file] = prompt_content + + improve_mode = False + image_directory = "tests/test_data" + shutil.copytree(image_directory, os.path.join(tmp_dir, image_directory)) + + result = load_prompt(input_repo, improve_mode, prompt_file, image_directory) + + assert isinstance(result, Prompt) + assert result.text == prompt_content + assert "mona_lisa.jpg" in result.image_urls + + +# def test_log_creation_in_improve_mode(self, tmp_path, monkeypatch): +# def improve_generator(): +# yield "y" +# while True: +# yield "n" # Subsequent responses +# +# gen = improve_generator() +# monkeypatch.setattr("builtins.input", lambda _: next(gen)) +# p = tmp_path / "projects/example" +# p.mkdir(parents=True) +# (p / "prompt").write_text(prompt_text) +# (p / "main.py").write_text("The program will be written in this file") +# meta_p = p / META_DATA_REL_PATH +# meta_p.mkdir(parents=True) +# (meta_p / "file_selection.toml").write_text( +# """ +# [files] +# "main.py" = "selected" +# """ +# ) +# os.environ["GPTE_TEST_MODE"] = "True" +# simplified_main(str(p), "improve") +# DiskExecutionEnv(path=p) +# assert ( +# (p / f".gpteng/memory/{DEBUG_LOG_FILE}").read_text().strip() +# == """UPLOADED FILES: +# ``` +# File: main.py +# 1 The program will be written in this file +# +# ``` +# PROMPT: +# Make a python program that writes 'hello' to a file called 'output.txt' +# CONSOLE OUTPUT:""" +# ) +# del os.environ["GPTE_TEST_MODE"] +# +# def test_log_creation_in_improve_mode_with_failing_diff( +# self, tmp_path, monkeypatch +# ): +# def improve_generator(): +# yield "y" +# while True: +# yield "n" # Subsequent responses +# +# def mock_salvage_correct_hunks( +# messages: List, files_dict: FilesDict, error_message: List +# ) -> FilesDict: +# # create a falling diff +# messages[ +# -1 +# ].content = """To create a Python program that writes 'hello' to a file called 'output.txt', we will need to perform the following steps: +# +# 1. Open the file 'output.txt' in write mode. +# 2. Write the string 'hello' to the file. +# 3. Close the file to ensure the data is written and the file is not left open. +# +# Here is the implementation of the program in the `main.py` file: +# +# ```diff +# --- main.py +# +++ main.py +# @@ -0,0 +1,9 @@ +# -create falling diff +# ``` +# +# This concludes a fully working implementation.""" +# # Call the original function with modified messages or define your own logic +# return salvage_correct_hunks(messages, files_dict, error_message) +# +# gen = improve_generator() +# monkeypatch.setattr("builtins.input", lambda _: next(gen)) +# monkeypatch.setattr( +# "gpt_engineer.core.default.steps.salvage_correct_hunks", +# mock_salvage_correct_hunks, +# ) +# p = tmp_path / "projects/example" +# p.mkdir(parents=True) +# (p / "prompt").write_text(prompt_text) +# (p / "main.py").write_text("The program will be written in this file") +# meta_p = p / META_DATA_REL_PATH +# meta_p.mkdir(parents=True) +# (meta_p / "file_selection.toml").write_text( +# """ +# [files] +# "main.py" = "selected" +# """ +# ) +# os.environ["GPTE_TEST_MODE"] = "True" +# simplified_main(str(p), "improve") +# DiskExecutionEnv(path=p) +# assert ( +# (p / f".gpteng/memory/{DEBUG_LOG_FILE}").read_text().strip() +# == """UPLOADED FILES: +# ``` +# File: main.py +# 1 The program will be written in this file +# +# ``` +# PROMPT: +# Make a python program that writes 'hello' to a file called 'output.txt' +# CONSOLE OUTPUT: +# Invalid hunk: @@ -0,0 +1,9 @@ +# -create falling diff +# +# Invalid hunk: @@ -0,0 +1,9 @@ +# -create falling diff""" +# ) +# del os.environ["GPTE_TEST_MODE"] +# +# def test_log_creation_in_improve_mode_with_unexpected_exceptions( +# self, tmp_path, monkeypatch +# ): +# def improve_generator(): +# yield "y" +# while True: +# yield "n" # Subsequent responses +# +# def mock_salvage_correct_hunks( +# messages: List, files_dict: FilesDict, error_message: List +# ) -> FilesDict: +# raise Exception("Mock exception in salvage_correct_hunks") +# +# gen = improve_generator() +# monkeypatch.setattr("builtins.input", lambda _: next(gen)) +# monkeypatch.setattr( +# "gpt_engineer.core.default.steps.salvage_correct_hunks", +# mock_salvage_correct_hunks, +# ) +# p = tmp_path / "projects/example" +# p.mkdir(parents=True) +# (p / "prompt").write_text(prompt_text) +# (p / "main.py").write_text("The program will be written in this file") +# meta_p = p / META_DATA_REL_PATH +# meta_p.mkdir(parents=True) +# (meta_p / "file_selection.toml").write_text( +# """ +# [files] +# "main.py" = "selected" +# """ +# ) +# os.environ["GPTE_TEST_MODE"] = "True" +# simplified_main(str(p), "improve") +# DiskExecutionEnv(path=p) +# assert ( +# (p / f".gpteng/memory/{DEBUG_LOG_FILE}").read_text().strip() +# == """UPLOADED FILES: +# ``` +# File: main.py +# 1 The program will be written in this file +# +# ``` +# PROMPT: +# Make a python program that writes 'hello' to a file called 'output.txt' +# CONSOLE OUTPUT: +# Error while improving the project: Mock exception in salvage_correct_hunks""" +# ) +# del os.environ["GPTE_TEST_MODE"] diff --git a/tests/benchmark/test_BenchConfig.py b/tests/benchmark/test_BenchConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..79dafd47f3542b61bb9f292b00b7cbb980e791a0 --- /dev/null +++ b/tests/benchmark/test_BenchConfig.py @@ -0,0 +1,87 @@ +# Generated by CodiumAI + +import pytest + +from gpt_engineer.benchmark.bench_config import ( + AppsConfig, + BenchConfig, + GptmeConfig, + MbppConfig, +) + + +class TestBenchConfig: + # Creating a BenchConfig object with default values should return an instance of BenchConfig with all attributes set to their default values. + def test_default_values(self): + config = BenchConfig() + assert isinstance(config.apps, AppsConfig) + assert isinstance(config.mbpp, MbppConfig) + assert isinstance(config.gptme, GptmeConfig) + assert config.apps.active is True + assert config.apps.test_start_index == 0 + assert config.apps.test_end_index == 1 + assert config.apps.train_start_index == 0 + assert config.apps.train_end_index == 0 + assert config.mbpp.active is True + assert config.mbpp.test_len == 1 + assert config.mbpp.train_len == 0 + assert config.gptme.active is True + + # Creating a BenchConfig object with specific values should return an instance of BenchConfig with the specified attributes set to the specified values. + def test_specific_values(self): + config = BenchConfig( + apps=AppsConfig( + active=False, + test_start_index=1, + test_end_index=2, + train_start_index=3, + train_end_index=4, + ), + mbpp=MbppConfig(active=False, test_len=5, train_len=6), + gptme=GptmeConfig(active=False), + ) + assert isinstance(config.apps, AppsConfig) + assert isinstance(config.mbpp, MbppConfig) + assert isinstance(config.gptme, GptmeConfig) + assert config.apps.active is False + assert config.apps.test_start_index == 1 + assert config.apps.test_end_index == 2 + assert config.apps.train_start_index == 3 + assert config.apps.train_end_index == 4 + assert config.mbpp.active is False + assert config.mbpp.test_len == 5 + assert config.mbpp.train_len == 6 + assert config.gptme.active is False + + # Calling the from_dict method with a valid dictionary should return an instance of BenchConfig with attributes set according to the values in the dictionary. + def test_from_dict_valid_dict(self): + config_dict = { + "apps": { + "active": False, + "test_start_index": 1, + "test_end_index": 2, + "train_start_index": 3, + "train_end_index": 4, + }, + "mbpp": {"active": False, "test_len": 5, "train_len": 6}, + "gptme": {"active": False}, + } + config = BenchConfig.from_dict(config_dict) + assert isinstance(config.apps, AppsConfig) + assert isinstance(config.mbpp, MbppConfig) + assert isinstance(config.gptme, GptmeConfig) + assert config.apps.active is False + assert config.apps.test_start_index == 1 + assert config.apps.test_end_index == 2 + assert config.apps.train_start_index == 3 + assert config.apps.train_end_index == 4 + assert config.mbpp.active is False + assert config.mbpp.test_len == 5 + assert config.mbpp.train_len == 6 + assert config.gptme.active is False + + # Calling the from_toml method with an invalid path to a TOML file should raise an appropriate exception. + def test_from_toml_invalid_path(self): + config_file = "invalid_config.toml" + with pytest.raises(Exception): + BenchConfig.from_toml(config_file) diff --git a/tests/core/__init__.py b/tests/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/core/default/__init__.py b/tests/core/default/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/core/default/test_disk_execution_env.py b/tests/core/default/test_disk_execution_env.py new file mode 100644 index 0000000000000000000000000000000000000000..541a5bb3199aca10e951fa93e749cf30fee90c8d --- /dev/null +++ b/tests/core/default/test_disk_execution_env.py @@ -0,0 +1,80 @@ +import tempfile +import unittest + +from unittest.mock import MagicMock, patch + +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv + +# from gpt_engineer.core.default.git_version_manager import GitVersionManager +from gpt_engineer.core.default.paths import ENTRYPOINT_FILE +from gpt_engineer.core.files_dict import FilesDict + + +class TestOnDiskExecutionEnv(unittest.TestCase): + def setUp(self): + self.temp_dir = tempfile.TemporaryDirectory() + self.env = DiskExecutionEnv() + + def tearDown(self): + self.temp_dir.cleanup() + + def test_successful_execution(self): + entrypoint_content = """ + python -m venv venv + source venv/bin/activate + python script.py + """ + code = { + ENTRYPOINT_FILE: entrypoint_content, + "script.py": "print('This is a test script')", + } + with patch("subprocess.Popen") as mock_popen: + mock_popen.return_value.wait.return_value = 0 + process = self.env.upload(FilesDict(code)).popen(f"bash {ENTRYPOINT_FILE}") + self.assertIsNotNone(process) + mock_popen.assert_called_once() + + def test_missing_entrypoint(self): + code = {"script.py": "print('This is a test script')"} + p = self.env.upload(FilesDict(code)).popen(f"bash {ENTRYPOINT_FILE}") + p.communicate() + assert p.returncode != 0 + + def test_keyboard_interrupt_handling(self): + entrypoint_content = """ + python script.py + """ + code = { + ENTRYPOINT_FILE: entrypoint_content, + "script.py": "print('This is a test script')", + } + with patch("subprocess.Popen") as mock_popen: + mock_process = MagicMock() + mock_process.poll.side_effect = KeyboardInterrupt + mock_popen.return_value = mock_process + stdout_full, stderr_full, returncode = self.env.upload(FilesDict(code)).run( + f"bash {ENTRYPOINT_FILE}" + ) + mock_process.kill.assert_called_once() + + def test_execution_with_output(self): + entrypoint_content = """ + python script.py + """ + code = { + ENTRYPOINT_FILE: entrypoint_content, + "script.py": "import sys; print('Out'); sys.stderr.write('Error')", + } + with patch("subprocess.Popen") as mock_popen: + process = MagicMock() + process.wait.return_value = 0 + process.communicate.return_value = (b"Out\n", b"Error\n") + mock_popen.return_value = process + process = self.env.upload(FilesDict(code)).popen(f"bash {ENTRYPOINT_FILE}") + stdout, stderr = process.communicate() + self.assertEqual(stdout, b"Out\n") + self.assertEqual(stderr, b"Error\n") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/core/default/test_disk_file_repository.py b/tests/core/default/test_disk_file_repository.py new file mode 100644 index 0000000000000000000000000000000000000000..4b8ac84be6b1c28a863690791b225276c4a77b79 --- /dev/null +++ b/tests/core/default/test_disk_file_repository.py @@ -0,0 +1,157 @@ +import pytest + +from gpt_engineer.core.default.disk_memory import DiskMemory + + +def test_DB_operations(tmp_path): + # Test initialization + db = DiskMemory(tmp_path) + + # Test __setitem__ + db["test_key"] = "test_value" + + assert (tmp_path / "test_key").is_file() + + # Test __getitem__ + val = db["test_key"] + + assert val == "test_value" + + +def test_large_files(tmp_path): + db = DiskMemory(tmp_path) + large_content = "a" * (10**6) # 1MB of tools + + # Test write large files + db["large_file"] = large_content + + # Test read large files + assert db["large_file"] == large_content + + +def test_concurrent_access(tmp_path): + import threading + + db = DiskMemory(tmp_path) + + num_threads = 10 + num_writes = 1000 + + def write_to_db(thread_id): + for i in range(num_writes): + key = f"thread{thread_id}_write{i}" + db[key] = str(i) + + threads = [] + for thread_id in range(num_threads): + t = threading.Thread(target=write_to_db, args=(thread_id,)) + t.start() + threads.append(t) + + for t in threads: + t.join() + + # Verify that all expected tools was written + for thread_id in range(num_threads): + for i in range(num_writes): + key = f"thread{thread_id}_write{i}" + assert key in db # using __contains__ now + assert db[key] == str(i) + + +def test_error_messages(tmp_path): + db = DiskMemory(tmp_path) + + # Test error on getting non-existent key + with pytest.raises(KeyError): + db["non_existent"] + + with pytest.raises(TypeError) as e: + db["key"] = ["Invalid", "value"] + + assert str(e.value) == "val must be str" + + +# Generated by CodiumAI + + +class TestOnDiskRepository: + # can set and get a value for a key + def test_set_and_get_value(self, tmp_path): + db = DiskMemory(tmp_path) + db["test_key"] = "test_value" + + assert (tmp_path / "test_key").is_file() + val = db["test_key"] + + assert val == "test_value" + + # can check if a key exists in the database + def test_key_exists(self, tmp_path): + db = DiskMemory(tmp_path) + db["test_key"] = "test_value" + + assert "test_key" in db + assert "nonexistent_key" not in db + + # can fetch a default value if a key does not exist + def test_fetch_default_value(self, tmp_path): + db = DiskMemory(tmp_path) + default_val = "default_value" + + assert db.get("nonexistent_key", default_val) == default_val + + # can delete a file or directory in the database + def test_delete_file_or_directory(self, tmp_path): + db = DiskMemory(tmp_path) + db["test_file"] = "test_content" + db["test_directory/test_file"] = "test_content" + + del db["test_file"] + del db["test_directory"] + + assert not (tmp_path / "test_file").exists() + assert not (tmp_path / "test_directory").exists() + + # can iterate over all files in the database + def test_iterate_files(self, tmp_path): + db = DiskMemory(tmp_path) + db["file1.txt"] = "content1" + db["file2.txt"] = "content2" + db["directory/file3.txt"] = "content3" + + files = list(db) + + assert len(files) == 3 + assert "file1.txt" in files + assert "file2.txt" in files + assert "directory/file3.txt" in files + + # raises a KeyError if a non-existent key is accessed + def test_key_error(self, tmp_path): + db = DiskMemory(tmp_path) + + with pytest.raises(KeyError): + _ = db["nonexistent_key"] + + # raises a ValueError if a file name attempts to access parent path + def test_value_error(self, tmp_path): + db = DiskMemory(tmp_path) + + with pytest.raises(ValueError): + db["../file.txt"] = "content" + + # raises a TypeError if a non-string value is set for a key + def test_type_error(self, tmp_path): + db = DiskMemory(tmp_path) + + with pytest.raises(TypeError): + db["test_key"] = 123 + + # can handle large file contents + def test_large_file_contents(self, tmp_path): + db = DiskMemory(tmp_path) + large_content = "a" * (10**6) # 1MB of tools + db["large_file"] = large_content + + assert db["large_file"] == large_content diff --git a/tests/core/default/test_simple_agent.py b/tests/core/default/test_simple_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..7c75210de25e0aa839c85f80f806edd13938eb35 --- /dev/null +++ b/tests/core/default/test_simple_agent.py @@ -0,0 +1,76 @@ +import tempfile + +import pytest + +from langchain.schema import AIMessage + +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.default.paths import ENTRYPOINT_FILE +from gpt_engineer.core.default.simple_agent import SimpleAgent +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.prompt import Prompt +from tests.mock_ai import MockAI + + +def test_init(): + temp_dir = tempfile.mkdtemp() + mock_ai = MockAI( + [ + AIMessage( + "hello_world.py\n```\nwith open('output.txt', 'w') as file:\n file.write('Hello World!')\n```" + ), + AIMessage("```run.sh\npython3 hello_world.py\n```"), + ], + ) + lean_agent = SimpleAgent.with_default_config(temp_dir, mock_ai) + outfile = "output.txt" + code = lean_agent.init( + Prompt( + f"Make a program that prints 'Hello World!' to a file called '{outfile}'" + ) + ) + + env = DiskExecutionEnv() + env.upload(code).run(f"bash {ENTRYPOINT_FILE}") + code = env.download() + + assert outfile in code + assert code[outfile] == "Hello World!" + + +def test_improve(): + temp_dir = tempfile.mkdtemp() + code = FilesDict( + { + "main.py": "def write_hello_world_to_file(filename):\n \"\"\"\n Writes 'Hello World!' to the specified file.\n \n :param filename: The name of the file to write to.\n \"\"\"\n with open(filename, 'w') as file:\n file.write('Hello World!')\n\nif __name__ == \"__main__\":\n output_filename = 'output.txt'\n write_hello_world_to_file(output_filename)", + "requirements.txt": "# No dependencies required", + "run.sh": "python3 main.py\n", + } + ) + mock_ai = MockAI( + [ + AIMessage( + "```diff\n--- main.py\n+++ main.py\n@@ -7,3 +7,3 @@\n with open(filename, 'w') as file:\n- file.write('Hello World!')\n+ file.write('!dlroW olleH')\n```" + ) + ] + ) + lean_agent = SimpleAgent.with_default_config(temp_dir, mock_ai) + code = lean_agent.improve( + code, + Prompt( + "Change the program so that it prints '!dlroW olleH' instead of 'Hello World!' " + ), + f"bash {ENTRYPOINT_FILE}", + ) + + env = DiskExecutionEnv() + env.upload(code).run(f"bash {ENTRYPOINT_FILE}") + code = env.download() + + outfile = "output.txt" + assert outfile in code + assert code[outfile] == "!dlroW olleH" + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/core/default/test_steps.py b/tests/core/default/test_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..37e4784b1e0aea41c7907a6e445a7f4cb40134c1 --- /dev/null +++ b/tests/core/default/test_steps.py @@ -0,0 +1,333 @@ +# Generated by CodiumAI +import tempfile + +from unittest.mock import MagicMock + +import pytest + +from langchain.schema import SystemMessage + +from gpt_engineer.core.ai import AI +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.paths import ENTRYPOINT_FILE, PREPROMPTS_PATH +from gpt_engineer.core.default.steps import ( + curr_fn, + gen_code, + gen_entrypoint, + improve_fn, + setup_sys_prompt, + setup_sys_prompt_existing_code, +) +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.linting import Linting +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.core.prompt import Prompt + +factorial_program = """ +To implement a function that calculates the factorial of a number in Python, we will create a simple Python module with a single function `factorial`. The factorial of a non-negative integer `n` is the product of all positive integers less than or equal to `n`. It is denoted by `n!`. The factorial of 0 is defined to be 1. + +Let's start by creating the `factorial.py` file which will contain our `factorial` function. + +factorial.py +```python +def factorial(n: int) -> int: + if n < 0: + raise ValueError("Factorial is not defined for negative numbers") + elif n == 0: + return 1 + else: + result = 1 + for i in range(1, n + 1): + result *= i + return result + +if __name__ == "__main__": +import sys + +if len(sys.argv) != 2: + print("Usage: python factorial.py ") + sys.exit(1) + +try: + number = int(sys.argv[1]) + print(f"The factorial of {number} is {factorial(number)}") +except ValueError as e: + print(e) + sys.exit(1) +``` + +Now, let's create a `requirements.txt` file to specify the dependencies for this module. Since we are not using any external libraries, the `requirements.txt` file will be empty, but it's a good practice to include it in Python projects. + +requirements.txt +``` +# No dependencies required +``` +This concludes a fully working implementation.``` +""" + +factorial_entrypoint = """ +Irrelevant explanations +```sh +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +pytest test_factorial.py +``` + """ + + +class TestGenCode: + # Generates code based on a given prompt using an AI model. + def test_generates_code_using_ai_model(self): + # Mock AI class + class MockAI: + def start(self, sys_prompt, user_prompt, step_name): + return [SystemMessage(content=factorial_program)] + + ai = MockAI() + prompt = Prompt("Write a function that calculates the factorial of a number.") + + memory = DiskMemory(tempfile.mkdtemp()) + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + code = gen_code(ai, prompt, memory, preprompts_holder) + + assert isinstance(code, FilesDict) + assert len(code) == 2 + # assert CODE_GEN_LOG_FILE in memory + # assert memory[CODE_GEN_LOG_FILE] == factorial_program.strip() + + # The generated code is saved to disk. + def test_generated_code_saved_to_disk(self): + # Mock AI class + class MockAI: + def start(self, sys_prompt, user_prompt, step_name): + return [SystemMessage(content=factorial_program)] + + ai = MockAI() + prompt = Prompt("Write a function that calculates the factorial of a number.") + memory = DiskMemory(tempfile.mkdtemp()) + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + code = gen_code(ai, prompt, memory, preprompts_holder) + + assert isinstance(code, FilesDict) + assert len(code) == 2 + # assert CODE_GEN_LOG_FILE in memory + # assert memory[CODE_GEN_LOG_FILE] == factorial_program.strip() + + # Raises TypeError if keys are not strings or Path objects. + def test_raises_type_error_if_keys_not_strings_or_path_objects(self): + # Mock AI class + class MockAI: + def start(self, sys_prompt, user_prompt, step_name): + return [SystemMessage(content=factorial_program)] + + ai = MockAI() + prompt = Prompt("Write a function that calculates the factorial of a number.") + memory = DiskMemory(tempfile.mkdtemp()) + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + with pytest.raises(TypeError): + code = gen_code(ai, prompt, memory, preprompts_holder) + code[123] = "code" + + # Raises TypeError if values are not strings. + def test_raises_type_error_if_values_not_strings(self): + # Mock AI class + class MockAI: + def start(self, sys_prompt, user_prompt, step_name): + return [SystemMessage(content=factorial_program)] + + ai = MockAI() + prompt = Prompt("Write a function that calculates the factorial of a number.") + memory = DiskMemory(tempfile.mkdtemp()) + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + with pytest.raises(TypeError): + code = gen_code(ai, prompt, memory, preprompts_holder) + code["file.py"] = 123 + + # Raises KeyError if the file does not exist in the database. + def test_raises_key_error_if_file_not_exist_in_database(self): + # Mock AI class + class MockAI: + def start(self, sys_prompt, user_prompt, step_name): + return [SystemMessage(content=factorial_program)] + + ai = MockAI() + prompt = Prompt("Write a function that calculates the factorial of a number.") + memory = DiskMemory(tempfile.mkdtemp()) + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + with pytest.raises(KeyError): + code = gen_code(ai, prompt, memory, preprompts_holder) + code["nonexistent_file.py"] + + +class TestStepUtilities: + def test_called_from_function(self): + # Arrange + def test_function(): + return curr_fn() + + expected_name = "test_function" + + # Act + actual_name = test_function() + + # Assert + assert actual_name == expected_name + + def test_constructs_system_prompt_with_predefined_instructions_and_philosophies( + self, + ): + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + preprompts = preprompts_holder.get_preprompts() + sys_prompt = setup_sys_prompt(preprompts) + expected_prompt = ( + preprompts["roadmap"] + + preprompts["generate"].replace("FILE_FORMAT", preprompts["file_format"]) + + "\nUseful to know:\n" + + preprompts["philosophy"] + ) + assert sys_prompt == expected_prompt + + def test_constructs_system_prompt(self): + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + preprompts = preprompts_holder.get_preprompts() + expected_prompt = ( + preprompts["roadmap"] + + preprompts["improve"].replace( + "FILE_FORMAT", preprompts["file_format_diff"] + ) + + "\nUseful to know:\n" + + preprompts["philosophy"] + ) + actual_prompt = setup_sys_prompt_existing_code(preprompts) + assert actual_prompt == expected_prompt + + +class TestGenEntrypoint: + class MockAI: + def __init__(self, content): + self.content = content + + def start(self, system, user, step_name): + return [SystemMessage(content=self.content)] + + # The function receives valid input and generates a valid entry point script. + def test_valid_input_generates_valid_entrypoint(self): + # Mock AI class + + ai_mock = TestGenEntrypoint.MockAI(factorial_entrypoint) + code = FilesDict() + tempdir = tempfile.mkdtemp() + memory = DiskMemory(tempdir) + prompt = Prompt("") + # Act + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + entrypoint_code = gen_entrypoint( + ai_mock, prompt, code, memory, preprompts_holder + ) + + # Assert + assert ENTRYPOINT_FILE in entrypoint_code + assert isinstance(entrypoint_code[ENTRYPOINT_FILE], str) + assert ( + entrypoint_code[ENTRYPOINT_FILE] + == """python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +pytest test_factorial.py +""" + ) + # assert ENTRYPOINT_LOG_FILE in memory + # assert isinstance(memory[ENTRYPOINT_LOG_FILE], str) + # assert memory[ENTRYPOINT_LOG_FILE] == factorial_entrypoint.strip() + + # The function receives an empty codebase and returns an empty entry point script. + def test_empty_codebase_returns_empty_entrypoint(self): + # Arrange + ai_mock = TestGenEntrypoint.MockAI("Irrelevant explanation") + + code = FilesDict() + tempdir = tempfile.mkdtemp() + memory = DiskMemory(tempdir) + prompt = Prompt("") + # Act + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + entrypoint_code = gen_entrypoint( + ai_mock, prompt, code, memory, preprompts_holder + ) + + # Assert + assert ENTRYPOINT_FILE in entrypoint_code + assert isinstance(entrypoint_code[ENTRYPOINT_FILE], str) + assert entrypoint_code[ENTRYPOINT_FILE] == "" + # assert ENTRYPOINT_LOG_FILE in memory + # assert isinstance(memory[ENTRYPOINT_LOG_FILE], str) + # assert memory[ENTRYPOINT_LOG_FILE] == "Irrelevant explanation" + + +class TestImprove: + def test_improve_existing_code(self, tmp_path): + # Mock the AI class + ai_patch = """ +Some introductory text. +```diff +--- main.py ++++ main.py +@@ -1,1 +1,1 @@ +-print('Hello, World!') ++print('Goodbye, World!') +``` +""" + ai_mock = MagicMock(spec=AI) + ai_mock.next.return_value = [SystemMessage(content=ai_patch)] + + # Create a Code object with existing code + code = FilesDict( + { + "main.py": "print('Hello, World!')", + "requirements.txt": "numpy==1.18.1", + "README.md": "This is a sample code repository.", + } + ) + + # Create a BaseRepository object for memory + memory = DiskMemory(tmp_path) + + # Define the user prompt + prompt = Prompt( + "Change the program to print 'Goodbye, World!' instead of 'Hello, World!'" + ) + + # Call the improve function + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + improved_code = improve_fn(ai_mock, prompt, code, memory, preprompts_holder) + + # Assert that the code was improved correctly + expected_code = FilesDict( + { + "main.py": "print('Goodbye, World!')", + "requirements.txt": "numpy==1.18.1", + "README.md": "This is a sample code repository.", + } + ) + assert improved_code == expected_code + + def test_lint_python(self): + linting = Linting() + content = "print('Hello, world! ')" + config = {"line_length": 50} + linted_content = linting.lint_python(content, config) + assert linted_content is not None, "Linted content should not be None" + + def test_lint_files(self): + linting = Linting() + files_dict = FilesDict({"test.py": "print('Hello, world! ')"}) + config = {"line_length": 50} + linted_files_dict = linting.lint_files(files_dict, config) + assert linted_files_dict is not None, "Linted files dict should not be None" + assert isinstance( + linted_files_dict, FilesDict + ), "Output should be an instance of FilesDict" + assert ( + "test.py" in linted_files_dict + ), "test.py should be in the linted files dict" diff --git a/tests/core/improve_function_test_cases/apps_benchmark_6_chat b/tests/core/improve_function_test_cases/apps_benchmark_6_chat new file mode 100644 index 0000000000000000000000000000000000000000..09b47815ef2c8e2baa6ebc20a2acadd294845f5d --- /dev/null +++ b/tests/core/improve_function_test_cases/apps_benchmark_6_chat @@ -0,0 +1,65 @@ +To implement this program, we will follow a structured approach. We will create a Python script that takes command-line arguments for the inputs, parses them, and then processes each query to determine the minimum number of blows required to defeat Zmei Gorynich. The program will consist of a main function that handles input parsing and output formatting, and a separate function that calculates the minimum number of blows for each query. + +We will need to: +1. Parse the command-line arguments to extract the queries. +2. For each query, calculate the minimum number of blows required. +3. Print the result for each query. + +Let's start by creating the `main.py` file with the necessary imports and the skeleton of our program. + +```diff +--- /dev/null ++++ main.py +@@ -0,0 +1,36 @@ ++import sys ++ ++def calculate_min_blows(n, x, blows): ++ """ ++ Calculate the minimum number of blows to defeat Zmei Gorynich. ++ :param n: Number of types of blows. ++ :param x: Initial number of heads. ++ :param blows: List of tuples (d_i, h_i) for each blow type. ++ :return: Minimum number of blows or -1 if impossible. ++ """ ++ # Implementation will be added here ++ pass ++ ++def main(): ++ # Parse command-line arguments ++ args = sys.argv[1].split() ++ t = int(args[0]) ++ queries = [] ++ idx = 1 ++ for _ in range(t): ++ n, x = map(int, args[idx:idx+2]) ++ idx += 2 ++ blows = [tuple(map(int, args[idx:idx+2])) for _ in range(n)] ++ idx += 2*n ++ queries.append((n, x, blows)) ++ ++ # Process each query ++ for n, x, blows in queries: ++ result = calculate_min_blows(n, x, blows) ++ print(result) ++ ++if __name__ == "__main__": ++ main() +``` +Now, let's implement the logic in calculate_min_blows function to determine the minimum number of blows required to defeat Zmei Gorynich. +```diff +--- main.py ++++ main.py +@@ -8,7 +8,25 @@ ++ # Implementation will be added here ++ max_damage = max(blows, key=lambda x: x[0]) ++ max_diff = max(blows, key=lambda x: x[0] - x[1]) ++ if max_damage[0] >= x: ++ return 1 ++ if max_diff[0] <= max_diff[1] and max_damage[0] < x: ++ return -1 ++ effective_damage = max_diff[0] - max_diff[1] ++ remaining_heads = x - max_damage[0] ++ blows_required = 1 + (remaining_heads + effective_damage - 1) // effective_damage ++ return blows_required +- pass +``` diff --git a/tests/core/improve_function_test_cases/apps_benchmark_6_code b/tests/core/improve_function_test_cases/apps_benchmark_6_code new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/core/improve_function_test_cases/apps_benchmark_6_v2_chat b/tests/core/improve_function_test_cases/apps_benchmark_6_v2_chat new file mode 100644 index 0000000000000000000000000000000000000000..72812bf1fc5b321c92c94443e8d8edf732d20fc3 --- /dev/null +++ b/tests/core/improve_function_test_cases/apps_benchmark_6_v2_chat @@ -0,0 +1,17 @@ +```diff +--- main.py ++++ main.py +@@ -8,7 +8,25 @@ ++ # Implementation will be added here ++ max_damage = max(blows, key=lambda x: x[0]) ++ max_diff = max(blows, key=lambda x: x[0] - x[1]) ++ if max_damage[0] >= x: ++ return 1 ++ if max_diff[0] <= max_diff[1] and max_damage[0] < x: ++ return -1 ++ effective_damage = max_diff[0] - max_diff[1] ++ remaining_heads = x - max_damage[0] ++ blows_required = 1 + (remaining_heads + effective_damage - 1) // effective_damage ++ return blows_required +- pass +``` diff --git a/tests/core/improve_function_test_cases/apps_benchmark_6_v2_code b/tests/core/improve_function_test_cases/apps_benchmark_6_v2_code new file mode 100644 index 0000000000000000000000000000000000000000..97ff66cdeee1eda1f6e10db13097ad21aec47a4e --- /dev/null +++ b/tests/core/improve_function_test_cases/apps_benchmark_6_v2_code @@ -0,0 +1,35 @@ +import sys + +def calculate_min_blows(n, x, blows): + """ + Calculate the minimum number of blows to defeat Zmei Gorynich. + :param n: Number of types of blows. + :param x: Initial number of heads. + :param blows: List of tuples (d_i, h_i) for each blow type. + :return: Minimum number of blows or -1 if impossible. + """ + # Implementation will be added here + pass + +def main(): + # Parse command-line arguments + args = sys.argv[1].split() + t = int(args[0]) + queries = [] + idx = 1 + for _ in range(t): + n, x = map(int, args[idx:idx+2]) + idx += 2 + blows = [tuple(map(int, args[idx:idx+2])) for _ in range(n)] + idx += 2*n + queries.append((n, x, blows)) + + # Process each query + for n, x, blows in queries: + result = calculate_min_blows(n, x, blows) + print(result) + +if __name__ == "__main__": + main()""" + +print(code) diff --git a/tests/core/improve_function_test_cases/controller_chat b/tests/core/improve_function_test_cases/controller_chat new file mode 100644 index 0000000000000000000000000000000000000000..c8b012bf0c9c0c67ab2119364e801c2563f57108 --- /dev/null +++ b/tests/core/improve_function_test_cases/controller_chat @@ -0,0 +1,61 @@ +Planning: + +1. Modify the `handle_input` method within the `Controller` class to change the behavior of the snake when certain keys are pressed. +2. When the "up" key is pressed, we need to queue two actions: first to move "down" and then to move "right" after one step. This will require storing the next move in the `Controller` class and checking for it on subsequent calls to `handle_input`. +3. When the "down" key is pressed, the snake should move "up". +4. When the "left" key is pressed, the snake should move "right". +5. When the "right" key is pressed, the snake should move "left". +6. Remove the long, unnecessary comment block. +7. Since we need to queue actions, we will introduce a new attribute `next_move` to the `Controller` class to store the next move. +8. Update the `__init__` method to initialize `next_move` as `None`. +9. Modify the `handle_input` method to check if there is a `next_move` queued before checking the keyboard input. If there is a `next_move`, execute it and clear the `next_move`. +10. Update the `handle_input` method to set the `next_move` to "right" when the "up" key is pressed, after moving "down". +11. Update the `handle_input` method to handle the reversed directions for "down", "left", and "right" keys. + +Generate patches: + +```diff +--- controller.py ++++ controller.py +@@ -4,6 +4,7 @@ + class Controller: + def __init__(self, game, view): + self.game = game + self.view = view ++ self.next_move = None + + def handle_input(self): ++ if self.next_move: ++ self.game.move(self.next_move) ++ self.next_move = None ++ else: +- if keyboard.is_pressed("up"): +- self.game.move("up") ++ if keyboard.is_pressed("up"): ++ self.game.move("down") ++ self.next_move = "right" +- elif keyboard.is_pressed("down"): ++ elif keyboard.is_pressed("down"): +- """ +- long +- goring comment +- +- +- +- +- +- +- +- +- """ +- self.game.move("down") ++ self.game.move("up") +- elif keyboard.is_pressed("left"): +- self.game.move("left") ++ elif keyboard.is_pressed("left"): ++ self.game.move("right") +- elif keyboard.is_pressed("right"): +- self.game.move("right") ++ elif keyboard.is_pressed("right"): ++ self.game.move("left") +``` diff --git a/tests/core/improve_function_test_cases/controller_code b/tests/core/improve_function_test_cases/controller_code new file mode 100644 index 0000000000000000000000000000000000000000..223197b069c37a076fb50aba9ed69b1cee447662 --- /dev/null +++ b/tests/core/improve_function_test_cases/controller_code @@ -0,0 +1,27 @@ +import keyboard + + +class Controller: + def __init__(self, game, view): + self.game = game + self.view = view + + def handle_input(self): + if keyboard.is_pressed("up"): + self.game.move("up") + elif keyboard.is_pressed("down"): + """ + long + goring comment + + + + + + + """ + self.game.move("down") + elif keyboard.is_pressed("left"): + self.game.move("left") + elif keyboard.is_pressed("right"): + self.game.move("right") diff --git a/tests/core/improve_function_test_cases/corrected_diff_from_missing_lines b/tests/core/improve_function_test_cases/corrected_diff_from_missing_lines new file mode 100644 index 0000000000000000000000000000000000000000..7ee8cb01ad866976e2debb90c368525433497933 --- /dev/null +++ b/tests/core/improve_function_test_cases/corrected_diff_from_missing_lines @@ -0,0 +1,20 @@ +--- example.txt ++++ example.txt +@@ -12,7 +12,8 @@ + sample text 1 + sample text 2 ++ added extra line here + # + #comment + # + # +- original text A ++ updated original text A with changes +@@ -39,4 +40,5 @@ + checking status: +- perform operation X ++ perform operation X only if specific condition holds ++ new operation related to condition + evaluating next step: +- execute step Y ++ revised execution of step Y diff --git a/tests/core/improve_function_test_cases/create_two_new_files_chat b/tests/core/improve_function_test_cases/create_two_new_files_chat new file mode 100644 index 0000000000000000000000000000000000000000..715f96cb0e6b1fb358e136f0c581f5641ee6bf3f --- /dev/null +++ b/tests/core/improve_function_test_cases/create_two_new_files_chat @@ -0,0 +1,99 @@ +To implement the requested functionality, we'll create a Python program that processes the input, evaluates the service history of a car, and outputs the appropriate message based on the criteria provided. We'll structure the program into several parts: + +1. **Parsing Input**: A function to parse the input string into a structured format. +2. **Service History Evaluation**: A class to evaluate the service history entries and determine the status of the odometer and service frequency. +3. **Main Logic**: The main logic that ties everything together and outputs the final result. + +Let's start by creating the necessary files and writing the initial code. + +### File: requirements.txt +This file will list the dependencies required for the project. Since the project does not require external libraries, this file will be empty but included for completeness. + +```diff +``` + +### File: service_history_evaluator.py +This file will contain the class responsible for evaluating the service history. + +```diff +--- /dev/null ++++ service_history_evaluator.py +@@ -0,0 +1,47 @@ ++from datetime import datetime, timedelta ++ ++class ServiceHistoryEvaluator: ++ """ ++ Evaluates the service history of a car to determine if the odometer has been tampered with ++ or if the service frequency is insufficient. ++ """ ++ def __init__(self, entries): ++ self.entries = entries ++ ++ def evaluate(self): ++ # Placeholder for the evaluation logic ++ pass ++ ++ def _parse_date(self, year, month): ++ """ ++ Parses the year and month into a datetime object. ++ """ ++ return datetime(year, month, 1) ++ ++ def _calculate_distance(self, start, end): ++ """ ++ Calculates the minimum and maximum possible distance traveled between two services. ++ """ ++ months_diff = (end.year - start.year) * 12 + end.month - start.month ++ min_distance = 2000 * months_diff ++ max_distance = 20000 * months_diff ++ return min_distance, max_distance ++ ++ def _check_odometer_rollover(self, prev_reading, current_reading, max_distance): ++ """ ++ Checks if the odometer could have rolled over between two readings. ++ """ ++ if prev_reading > current_reading: ++ return current_reading + (99999 - prev_reading) <= max_distance ++ else: ++ return current_reading - prev_reading <= max_distance ++ ++ def _check_service_frequency(self): ++ """ ++ Checks if the service frequency is within the legal limits. ++ """ ++ # Placeholder for the service frequency check logic ++ pass +``` + +### File: main.py +This file will contain the main logic and the entry point of the program. + +```diff +--- /dev/null ++++ main.py +@@ -0,0 +1,29 @@ ++import sys ++from service_history_evaluator import ServiceHistoryEvaluator ++ ++def parse_input(input_str): ++ """ ++ Parses the input string into a structured format. ++ """ ++ lines = input_str.split('\n') ++ num_entries = int(lines[0]) ++ entries = [tuple(map(int, line.split())) for line in lines[1:num_entries+1]] ++ return entries ++ ++def main(input_str): ++ entries = parse_input(input_str) ++ evaluator = ServiceHistoryEvaluator(entries) ++ result = evaluator.evaluate() ++ print(result) ++ ++if __name__ == "__main__": ++ if len(sys.argv) != 2: ++ print("Usage: python main.py 'input1 input2 etc'") ++ sys.exit(1) ++ input_str = sys.argv[1].replace("'", "") ++ main(input_str) +``` diff --git a/tests/core/improve_function_test_cases/create_two_new_files_code b/tests/core/improve_function_test_cases/create_two_new_files_code new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/core/improve_function_test_cases/simple_calculator_chat b/tests/core/improve_function_test_cases/simple_calculator_chat new file mode 100644 index 0000000000000000000000000000000000000000..a6b50ccde752357267a109b73dbf8e5984dd7b4c --- /dev/null +++ b/tests/core/improve_function_test_cases/simple_calculator_chat @@ -0,0 +1,84 @@ +I apologize for the oversight. Let's correct the `calculator.py` file with the proper git diff format, ensuring that the context lines match the original code exactly. + +```diff +--- calculator.py ++++ calculator.py +@@ -1,24 +1,24 @@ + class Calculator: +- def add(self, a, b): +- return a - b # Logical ++ def add(self, a, b): # Adds two numbers ++ return a + b + +- def subtract(self, a, b) ++ def subtract(self, a, b): # Subtracts second number from first ++ return a - b + +- def multiply(self, a, b): +- return lambda a, b: sum(a for _ in range(abs(b))) * (1 if b >= 0 else -1) #unncessary ++ def multiply(self, a, b): # Multiplies two numbers ++ return a * b + + def divide(self, a, b): + if b == 0: + raise ValueError("Cannot divide by zero.") + return a / b + +- def modulo(self, a, b): +- if b = 0: # Syntax +- return None # Logical ++ def modulo(self, a, b): # Finds the remainder of division of two numbers ++ if b == 0: ++ raise ValueError("Cannot modulo by zero.") + return a % b + + def power(self, a, b): # Raises a number to the power of another + result = 1 +- for i in range(b): # Logical ++ for i in range(abs(b)): + result *= a +- return result ++ return result if b >= 0 else 1 / result +``` + +Now, let's create the `main.py` file with the correct git diff format: + +```diff +--- /dev/null ++++ main.py +@@ -0,0 +1,30 @@ ++from calculator import Calculator ++ ++# Function to demonstrate the usage of the Calculator class ++def main(): ++ calc = Calculator() ++ while True: ++ try: ++ a = float(input("Enter the first number: ")) ++ b = float(input("Enter the second number: ")) ++ operation = input("Enter an operation (add, subtract, multiply, divide, modulo, power): ").lower() ++ if operation == 'add': ++ print("Result:", calc.add(a, b)) ++ elif operation == 'subtract': ++ print("Result:", calc.subtract(a, b)) ++ elif operation == 'multiply': ++ print("Result:", calc.multiply(a, b)) ++ elif operation == 'divide': ++ print("Result:", calc.divide(a, b)) ++ elif operation == 'modulo': ++ print("Result:", calc.modulo(a, b)) ++ elif operation == 'power': ++ print("Result:", calc.power(a, b)) ++ else: ++ print("Invalid operation.") ++ ++ if input("Do you want to perform another calculation? (yes/no): ").lower() != 'yes': ++ break ++ except Exception as e: ++ print("Error:", e) ++ ++if __name__ == "__main__": ++ main() +``` + +These changes should now correctly apply to the provided code and create a simple calculator program with a command-line interface. diff --git a/tests/core/improve_function_test_cases/simple_calculator_code b/tests/core/improve_function_test_cases/simple_calculator_code new file mode 100644 index 0000000000000000000000000000000000000000..963570f79136ea19b6eaf5468d746187a3150906 --- /dev/null +++ b/tests/core/improve_function_test_cases/simple_calculator_code @@ -0,0 +1,25 @@ +class Calculator: + def add(self, a, b): + return a - b # Logical + + def subtract(self, a, b) + return a - b # Syntax + + def multiply(self, a, b): + return lambda a, b: sum(a for _ in range(abs(b))) * (1 if b >= 0 else -1) #unncessary + + def divide(self, a, b): + if b == 0: + raise ValueError("Cannot divide by zero.") + return a / b + + def modulo(self, a, b): + if b = 0: # Syntax + return None # Logical + return a % b + + def power(self, a, b): + result = 1 + for i in range(b): # Logical + result *= a + return result diff --git a/tests/core/improve_function_test_cases/task_master_chat b/tests/core/improve_function_test_cases/task_master_chat new file mode 100644 index 0000000000000000000000000000000000000000..cc3b5de55c06b283bf699d9adef79f9c9de46971 --- /dev/null +++ b/tests/core/improve_function_test_cases/task_master_chat @@ -0,0 +1,99 @@ +Apologies for the confusion. Let's correct the issues in the code with the proper git diff format: + +```diff +--- taskmaster.py ++++ taskmaster.py +@@ -1,4 +1,4 @@ +-improt datetime ++import datetime + + class Task: + def __init__(self, title, due_date, priority=1): +@@ -4,7 +4,7 @@ + self.title = tile + self.due_date = due_date + self.completed = Fasle +- self.priority = priority ++ self.priority = priority # Priority levels: 1 (low) to 5 (high), default is 1. + + def mark_as_complete(self): +- self.completed = Ture ++ self.completed = True + +@@ -13,7 +13,7 @@ + def update_title(self, new_title): + # Correct the typo in the assignment of the new title. + # The correct variable name is 'new_title', not 'new_tile'. +- self.title = new_tile ++ self.title = new_title + + def update_due_date(self, new_date): + self.due_date = new_date +@@ -22,7 +22,7 @@ + def add_task(task_list, task): + # Correct the typo in the append method call. + # The correct method name is 'append', not 'appned'. +- task_list.appned(task) ++ task_list.append(task) + + def remove_task(task_list, task_title): + for task in task_list: +@@ -26,7 +26,7 @@ + # Correct the typo in the title attribute check. + # The correct attribute name is 'title', not 'titel'. + if task.titel == task_title: +- task_list.remove(task) ++ task.title == task_title + break + + def update_task_title(task_list, old_title, new_title): +@@ -49,7 +49,7 @@ + def print_tasks(task_list): + for task in task_list: + # Correct the typo in the title attribute when formatting the string. + # The correct attribute name is 'title', not 'titel'. +- print(f"Task: {task.titel}, Due: {task.due_date}, Completed: {task.completed}, Priority: {task.priority}") ++ print(f"Task: {task.title}, Due: {task.due_date}, Completed: {task.completed}, Priority: {task.priority}") + + def load_tasks_from_file(file_path): + tasks = [] +@@ -55,8 +55,8 @@ + with open(file_path, 'r') as file: + # Correct the typo in the readlines method call. + # The correct method name is 'readlines', not 'redlines'. +- lines = file.redlines() ++ lines = file.readlines() + for line in lines: + # Correct the typo in the split method call. + # The correct method name is 'split', not 'splt'. +- parts = line.splt(',') ++ parts = line.split(',') + task = Task(parts[0], parts[1], int(parts[3])) + if parts[2].strip() == 'True': + task.mark_as_complete() +@@ -72,7 +72,7 @@ + def find_overdue_tasks(task_list): + overdue_tasks = [] + current_date = datetime.datetime.now().date() + for task in task_list: +- task_date = datetime.datetime.strptime(task.due_date, '%Y-%m-%d').date() ++ task_date = datetime.datetime.strptime(task.due_date.strip(), '%Y-%m-%d').date() + if task_date < current_date: + # Correct the typo in the append method call. + # The correct method name is 'append', not 'appnd'. +- overdue_tasks.appnd(task) ++ overdue_tasks.append(task) + return overdue_tasks + + def search_tasks(task_list, search_term): +@@ -83,7 +83,7 @@ + for task in task_list: + if search_term.lower() in task.title.lower(): + # Correct the typo in the append method call. + # The correct method name is 'append', not 'appnd'. +- found_tasks.appnd(task) ++ found_tasks.append(task) + return found_tasks + + task_list = [] +``` diff --git a/tests/core/improve_function_test_cases/task_master_code b/tests/core/improve_function_test_cases/task_master_code new file mode 100644 index 0000000000000000000000000000000000000000..3b1d5bf58b432ba8e0a673d86e457f1b7c78af41 --- /dev/null +++ b/tests/core/improve_function_test_cases/task_master_code @@ -0,0 +1,114 @@ +improt datetime + +class Task: + def __init__(self, title, due_date, priority=1): + self.title = tile + self.due_date = due_date + self.completed = Fasle + self.priority = priority + + def mark_as_complete(self): + self.completed = Ture + + def update_title(self, new_title): + self.title = new_tile + + def update_due_date(self, new_date): + self.due_date = new_date + + def set_priority(self, priority): + self.priority = priority + +def add_task(task_list, task): + task_list.appned(task) + +def remove_task(task_list, task_title): + for task in task_list: + if task.titel == task_title: + task_list.remove(task) + break + +def update_task_title(task_list, old_title, new_title): + for task in task_list: + if task.title == old_title: + task.update_title(new_title) + break + +def update_task_due_date(task_list, title, new_date): + for task in task_list: + if task.title == title: + task.update_due_date(new_date) + break + +def set_task_priority(task_list, title, priority): + for task in task_list: + if task.title == title: + task.set_priority(priority) + break + +def print_tasks(task_list): + for task in task_list: + print(f"Task: {task.titel}, Due: {task.due_date}, Completed: {task.completed}, Priority: {task.priority}") + +def load_tasks_from_file(file_path): + tasks = [] + with open(file_path, 'r') as file: + lines = file.redlines() + for line in lines: + parts = line.splt(',') + task = Task(parts[0], parts[1], int(parts[3])) + if parts[2].strip() == 'True': + task.mark_as_complete() + tasks.append(task) + return tasks + +def save_tasks_to_file(task_list, file_path): + with open(file_path, 'w') as file: + for task in task_list: + line = f"{task.title},{task.due_date},{task.completed},{task.priority}\n" + file.write(line) + +def find_overdue_tasks(task_list): + overdue_tasks = [] + current_date = datetime.datetime.now().date() + for task in task_list: + task_date = datetime.datetime.strptime(task.due_date, '%Y-%m-%d').date() + if task_date < current_date: + overdue_tasks.appnd(task) + return overdue_tasks + +def search_tasks(task_list, search_term): + found_tasks = [] + for task in task_list: + if search_term.lower() in task.title.lower(): + found_tasks.appnd(task) + return found_tasks + +task_list = [] +add_task(task_list, Task("Finish project", "2024-01-01", 3)) +add_task(task_list, Task("Buy groceries", "2024-01-05", 2)) +add_task(task_list, Task("Call John", "2024-01-03", 1)) + +print_tasks(task_list) + +overdue = find_overdue_tasks(task_list) +print("Overdue tasks:") +for task in overdue: + print(f"Task: {task.title}, Due: {task.due_date}, Priority: {task.priority}") + +save_tasks_to_file(task_list, "tasks.txt") + +loaded_tasks = load_tasks_from_file("tasks.txt") +print("Loaded tasks:") +print_tasks(loaded_tasks) + +remove_task(task_list, "Buy groceries") +print("Tasks after removal:") +print_tasks(task_list) + +save_tasks_to_file(task_list, "tasks_updated.txt") + +search_results = search_tasks(task_list, "project") +print("Search results for 'project':") +for task in search_results: + print(f"Task: {task.title}, Due: {task.due_date}, Completed: {task.completed}, Priority: {task.priority}") diff --git a/tests/core/improve_function_test_cases/temperature_converter_chat b/tests/core/improve_function_test_cases/temperature_converter_chat new file mode 100644 index 0000000000000000000000000000000000000000..e9959594b824cc2528b11cd9e767bd40b61b5bf6 --- /dev/null +++ b/tests/core/improve_function_test_cases/temperature_converter_chat @@ -0,0 +1,81 @@ +Apologies for the confusion. Let's provide the corrected git diff patches for the `temperatureconverter.py` file. + +```diff +--- temperatureconverter.py ++++ temperatureconverter.py +@@ -6,2 +6,3 @@ +- def _fahrenheit_to_celsius(self, fahrenheit) ++ def _fahrenheit_to_celsius(self, fahrenheit): ++ """Convert Fahrenheit to Celsius.""" + return (fahrenheit - 32) * 5 // 9 +@@ -15,2 +16,3 @@ +- def _fahrenheit_to_kelvin(self, fahrenheit) ++ def _fahrenheit_to_kelvin(self, fahrenheit): ++ """Convert Fahrenheit to Kelvin.""" + return self._celsius_to_kelvin(self._fahrenheit_to_celsius(fahrenheit)) +@@ -18,2 +20,3 @@ +- def _kelvin_to_fahrenheit(self, kelvin): ++ def _kelvin_to_fahrenheit(self, kelvin): ++ """Convert Kelvin to Fahrenheit.""" + return self._celsius_to_fahrenheit(self._kelvin_to_celsius(kelvin)) +@@ -21,3 +24,4 @@ + def convert_temperature(self, temperature, from_scale, to_scale): ++ """Convert temperature from one scale to another.""" + conversion_methods = { + ('Celsius', 'Fahrenheit'): self._celsius_to_fahrenheit, +@@ -30,3 +34,4 @@ +- if (from_scale, to_scale) in conversion_methods +- return conversion_methods[(from_scale, to_scale(temperature) ++ if (from_scale, to_scale) in conversion_methods: ++ return conversion_methods[(from_scale, to_scale)](temperature) + else: +@@ -35,3 +40,4 @@ + def update_base_temperature(self, new_temperature, scale): ++ """Update the base temperature after validating the new temperature.""" + if self.is_temperature_valid(new_temperature, scale): +- self.base_temp = convert_temperature(new_temperature, scale, self.scale) ++ self.base_temp = self.convert_temperature(new_temperature, scale, self.scale) + else: +@@ -41,3 +47,4 @@ + def set_scale(self, new_scale): ++ """Set a new scale and update the base temperature accordingly.""" + if new_scale not in ["Celsius", "Fahrenheit", "Kelvin"]: +@@ -47,3 +54,4 @@ + def __str__(self): ++ """Return a string representation of the base temperature and scale.""" +- return "Base Temperature: " + self.base_temp + " " + self.scale ++ return f"Base Temperature: {self.base_temp} {self.scale}" +@@ -50,3 +58,4 @@ + def __repr__(self): ++ """Return a string representation of the TemperatureConverter instance for debugging.""" +- return "TemperatureConverter(base_temperature=" + self.base_temperature + ", scale='" + self.scale + "')" ++ return f"TemperatureConverter(base_temperature={self.base_temp}, scale='{self.scale}')" ++ ++ # Add missing conversion methods ++ def _celsius_to_fahrenheit(self, celsius): ++ """Convert Celsius to Fahrenheit.""" ++ return (celsius * 9 / 5) + 32 ++ ++ def _kelvin_to_fahrenheit(self, kelvin): ++ """Convert Kelvin to Fahrenheit.""" ++ return (kelvin - 273) * 9 / 5 + 32 ++ ++ # Add method to validate temperatures ++ def is_temperature_valid(self, temperature, scale): ++ """Validate the correctness of the given temperature within the specified scale.""" ++ if scale == "Celsius": ++ return -273 <= temperature ++ elif scale == "Fahrenheit": ++ return -459 <= temperature ++ elif scale == "Kelvin": ++ return 0 <= temperature ++ return False ++ ++ # Add method to reset the base temperature ++ def reset_base_temperature(self): ++ """Reset the base temperature to the default value.""" ++ self.base_temp = 0 ++ self.scale = "Celsius" +``` + +This diff corrects the syntax errors, adds missing methods, and includes the necessary changes to ensure the `TemperatureConverter` class works as intended. The `__str__` and `__repr__` methods have been fixed to properly format the temperature as a string, and the `set_scale` and `update_base_temperature` methods have been corrected to use the class's methods properly. Additionally, the `is_temperature_valid` method has been added to validate temperatures, and a method to reset the base temperature has been included. diff --git a/tests/core/improve_function_test_cases/temperature_converter_code b/tests/core/improve_function_test_cases/temperature_converter_code new file mode 100644 index 0000000000000000000000000000000000000000..dd54b6553d562a7969e151bc308f2bdc43f36011 --- /dev/null +++ b/tests/core/improve_function_test_cases/temperature_converter_code @@ -0,0 +1,51 @@ +class TemperatureConverter + def __init__(self, base_temperature=0, scale="Celsius"): + self.base_temp = base_temperature + self.scale = scale if scale in ["Celsius", "Fahrenheit", "Kelvin"] else "Invalid" + + def _fahrenheit_to_celsius(self, fahrenheit) + return (fahrenheit - 32) * 5 // 9 + + def celsius_to_kelvin(self, celsius): + return celsius + 273 + + def kelvin_to_celsius(self, kelvin): + return kelvin - 273 + + def _fahrenheit_to_kelvin(self, fahrenheit) + return self._celsius_to_kelvin(_fahrenheit_to_celsius(fahrenheit)) + + def _kelvin_to_fahrenheit(self, kelvin): + return self._celsius_to_fahrenheit(_kelvin_to_celsius(kelvin) + + def convert_temperature(self, temperature, from_scale, to_scale): + conversion_methods = { + ('Celsius', 'Fahrenheit'): self._celsius_to_fahrenheit, + ('Celsius', 'Kelvin'): self.celsius_to_kelvin, + ('Fahrenheit', 'Celsius'): self._fahrenheit_to_celsius, + ('Fahrenheit', 'Kelvin'): self._fahrenheit_to_kelvin, + ('Kelvin', 'Celsius'): self.kelvin_to_celsius, + ('Kelvin', 'Fahrenheit'): self._kelvin_to_fahrenheit, + } + if (from_scale, to_scale) in conversion_methods + return conversion_methods[(from_scale, to_scale(temperature) + else + raise ValueError("Invalid scale conversion") + + def update_base_temperature(self, new_temperature, scale): + if self.is_temperature_valid(new_temperature, scale): + self.base_temp = convert_temperature(new_temperature, scale, self.scale) + else: + raise ValueError("Invalid temperature value") + + def set_scale(self, new_scale): + if new_scale not in ["Celsius", "Fahrenheit", "Kelvin"]: + print("Invalid scale") + self.base_temp = self.convert_temperature(self.base_temperature, self.scale, new_scale) + self.scale = new_scale + + def __str__(self): + return "Base Temperature: " + self.base_temp + " " + self.scale + +def __repr__(self): + return "TemperatureConverter(base_temperature=" + self.base_temperature + ", scale='" + self.scale + "')" diff --git a/tests/core/improve_function_test_cases/theo_case_chat b/tests/core/improve_function_test_cases/theo_case_chat new file mode 100644 index 0000000000000000000000000000000000000000..58fbf74fe265bb80221168fead0e47b43f5e067b --- /dev/null +++ b/tests/core/improve_function_test_cases/theo_case_chat @@ -0,0 +1,79 @@ +To add an entrypoint for running the API through the command line, we will create a new Python script that can be executed directly. This script will set up and run the FastAPI application using Uvicorn, which is an ASGI server. This approach allows you to run the server from the command line without directly using the `uvicorn` command. + +We will create a new file named `run.py` in the root directory. This file will import the FastAPI application from `main.py` and will use Uvicorn to run it. + +Here's the implementation: + +```diff +--- /dev/null ++++ run.py +@@ -0,0 +1,10 @@ ++import uvicorn ++from main import app ++ ++# This script can be used to run the FastAPI application from the command line. ++if __name__ == "__main__": ++ uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +This script checks if it is being run as the main module and then starts the Uvicorn server with the FastAPI app. The server listens on all interfaces (`0.0.0.0`) and port `8000`. + +Additionally, we need to update the Dockerfile to ensure that the new script is correctly utilized when the Docker container is run. We will change the CMD directive to use `run.py` instead of directly calling `uvicorn`. + +```diff +--- dockerfile ++++ dockerfile +@@ -19,3 +19,3 @@ +-# Run app.py when the container launches +-CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] ++# Run run.py when the container launches ++CMD ["python", "run.py"] +``` + +This change in the Dockerfile ensures that when the Docker container is started, it uses the new `run.py` script to launch the application. This script sets up the Uvicorn server with the specified host and port settings. + +With these changes, you can now run the API both directly from the command line by executing `python run.py` and through Docker using the updated Dockerfile configuration. This setup provides flexibility for different deployment scenarios.Invalid hunk: @@ -19,3 +19,3 @@ +-# Run app.py when the container launches +-CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] ++# Run run.py when the container launches ++CMD ["python", "run.py"] + +To add an entrypoint for running the API through the command line, we will create a new Python script that can be executed directly. This script will set up and run the FastAPI application using Uvicorn, which is an ASGI server. This approach allows you to run the server from the command line without directly using the `uvicorn` command. + +We will create a new file named `run.py` in the root directory. This file will import the FastAPI application from `main.py` and will use Uvicorn to run it. + +Here's the implementation: + +```diff +--- /dev/null ++++ run.py +@@ -0,0 +1,10 @@ ++import uvicorn ++from main import app ++ ++# This script can be used to run the FastAPI application from the command line. ++if __name__ == "__main__": ++ uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +This script checks if it is being run as the main module and then starts the Uvicorn server with the FastAPI app. The server listens on all interfaces (`0.0.0.0`) and port `8000`. + +Additionally, we need to update the Dockerfile to ensure that the new script is correctly utilized when the Docker container is run. We will change the CMD directive to use `run.py` instead of directly calling `uvicorn`. + +```diff +--- dockerfile ++++ dockerfile +@@ -19,3 +19,3 @@ +-# Run app.py when the container launches +-CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] ++# Run run.py when the container launches ++CMD ["python", "run.py"] +``` + +This change in the Dockerfile ensures that when the Docker container is started, it uses the new `run.py` script to launch the application. This script sets up the Uvicorn server with the specified host and port settings. + +With these changes, you can now run the API both directly from the command line by executing `python run.py` and through Docker using the updated Dockerfile configuration. This setup provides flexibility for different deployment scenarios.Invalid hunk: @@ -19,3 +19,3 @@ +-# Run app.py when the container launches +-CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] ++# Run run.py when the container launches ++CMD ["python", "run.py"] diff --git a/tests/core/improve_function_test_cases/theo_case_code b/tests/core/improve_function_test_cases/theo_case_code new file mode 100644 index 0000000000000000000000000000000000000000..604c2f5f6106266aac0d3dad9103ccf19e8ec677 --- /dev/null +++ b/tests/core/improve_function_test_cases/theo_case_code @@ -0,0 +1,20 @@ +# Use an official Python runtime as a parent image +FROM python:3.9-slim + +# Set the working directory in the container +WORKDIR /usr/src/app + +# Copy the current directory contents into the container at /usr/src/app +COPY . /usr/src/app + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Make port 80 available to the world outside this container +EXPOSE 80 + +# Define environment variable +ENV NAME World + +# Run app.py when the container launches +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] diff --git a/tests/core/improve_function_test_cases/vgvishesh_example_2_chat b/tests/core/improve_function_test_cases/vgvishesh_example_2_chat new file mode 100644 index 0000000000000000000000000000000000000000..a006ca6788e12b5938bd0a289d7dd05836028ba4 --- /dev/null +++ b/tests/core/improve_function_test_cases/vgvishesh_example_2_chat @@ -0,0 +1,50 @@ +To implement a toggle button for switching between dark and light modes, we need to follow these steps: + +1. Update the `App.tsx` file to include state management for the theme mode. We will use React's `useState` hook to manage the theme state and `useEffect` hook to apply the theme to the document body class. + +2. Add a button in the `App.tsx` file that will toggle the theme state when clicked. This button should be styled and positioned in the top right corner of the screen. + +3. Update the `styles.css` file to include styles for the dark mode. We will define a new class, say `.dark-mode`, that will contain the dark mode styles. This class will be applied to the body when the dark mode is active. + +4. Update the `styles.css` file to style the toggle button. + +Here are the diffs for the changes: + +```diff +--- src/App.tsx ++++ src/App.tsx +@@ -1,5 +1,9 @@ + import React from 'react'; ++import { useState, useEffect } from 'react'; + import QRCode from 'qrcode.react'; + import { profiles } from './components/SocialLinks'; + import { SocialLinks } from './components/SocialLinks'; ++import './styles.css'; + ++const THEME_LIGHT = 'light'; ++const THEME_DARK = 'dark'; + +@@ -6,7 +10,23 @@ + const App: React.FC = () => { ++ const [theme, setTheme] = useState(THEME_LIGHT); ++ ++ useEffect(() => { ++ document.body.className = theme; ++ }, [theme]); ++ ++ const toggleTheme = () => { ++ setTheme(theme === THEME_LIGHT ? THEME_DARK : THEME_LIGHT); ++ }; ++ + return ( ++ +
+
+

My Social Profiles QR Code

+@@ -18,4 +38,4 @@ + }; + + export default App; +``` diff --git a/tests/core/improve_function_test_cases/vgvishesh_example_2_code b/tests/core/improve_function_test_cases/vgvishesh_example_2_code new file mode 100644 index 0000000000000000000000000000000000000000..05d30db8ba37bdacdba5de8a417fa59d5e198603 --- /dev/null +++ b/tests/core/improve_function_test_cases/vgvishesh_example_2_code @@ -0,0 +1,18 @@ +import React from 'react'; +import QRCode from 'qrcode.react'; +import { profiles } from './components/SocialLinks'; +import { SocialLinks } from './components/SocialLinks'; + +const App: React.FC = () => { + return ( +
+
+

My Social Profiles QR Code

+ + +
+
+ ); +}; + +export default App; diff --git a/tests/core/improve_function_test_cases/vgvishesh_example_chat b/tests/core/improve_function_test_cases/vgvishesh_example_chat new file mode 100644 index 0000000000000000000000000000000000000000..cc07bb0f3493643dc1db0fddb663b2c03b6a364d --- /dev/null +++ b/tests/core/improve_function_test_cases/vgvishesh_example_chat @@ -0,0 +1,20 @@ +Planning: +........ + +```diff +--- src/components/SocialLinks.tsx ++++ src/components/SocialLinks.tsx +@@ -1,10 +1,11 @@ + import React from 'react'; + ++export const profiles = { +- const profiles = { + linkedIn: 'https://www.linkedin.com/', + github: 'https://github.com/', + whatsapp: 'https://', + twitter: 'https://twitter.com/', +- }; ++}; + + export const SocialLinks: React.FC = () => { +``` diff --git a/tests/core/improve_function_test_cases/vgvishesh_example_code b/tests/core/improve_function_test_cases/vgvishesh_example_code new file mode 100644 index 0000000000000000000000000000000000000000..8776494b15e52bc1221bab3a06480d9b7a043b7e --- /dev/null +++ b/tests/core/improve_function_test_cases/vgvishesh_example_code @@ -0,0 +1,12 @@ +import React from 'react'; + +export const SocialLinks: React.FC = () => { + const profiles = { + linkedIn: 'https://www.linkedin.com/', + github: 'https://github.com/', + whatsapp: 'https://', + twitter: 'https://twitter.com/', + }; + + return ( +
diff --git a/tests/core/improve_function_test_cases/wheaties_example_chat b/tests/core/improve_function_test_cases/wheaties_example_chat new file mode 100644 index 0000000000000000000000000000000000000000..9469f8873df8bd220d9ec361abe39607cbda4a97 --- /dev/null +++ b/tests/core/improve_function_test_cases/wheaties_example_chat @@ -0,0 +1,142 @@ +Planning: + +1. Refactor the DNS logic to ensure that DNS records are created for every server hostname and sent to every domain controller. This involves creating a function that encapsulates the DNS record creation logic and iterates over all domain controllers to apply the changes. + +2. Remove redundant logic and checks that are scattered throughout the DNS section of the script. Consolidate the logic into a single, reusable function. + +3. Replace the multiple if-else statements that determine the `$Zone` variable with a more efficient method, possibly using a hashtable or a switch statement to map server domains to their respective zones. + +4. Ensure that the script follows PowerShell best practices, such as using approved verbs for function names, providing comment-based help, and using try-catch blocks for error handling. + +5. Add comprehensive comments to the new function and throughout the DNS logic to explain the purpose and functionality of the code. + +6. Test the refactored DNS logic to ensure it works correctly in various scenarios and that DNS records are created and propagated as expected. + +Now, let's generate the diffs for the changes: + +```diff +--- VMClonetest.ps1 ++++ VMClonetest.ps1 +@@ -443,3 +443,3 @@ + # Minimum +- # Sleep 150 -- optional: true was added to /etc/netplan/00-* to resolve this ++ # Sleep 150 -- optional: true was added to /etc/netplan/00-* to resolve this, but now handled dynamically +@@ -455,3 +455,3 @@ + ###IF DC's Change - You Will Need To WinRM Accept Them### +- if($server_domain -like "ls") ++ # The logic for determining the DNS zone has been refactored into a function for clarity and maintainability +@@ -487,3 +487,3 @@ + +- Write-Host $serverIpReverse ++ # The serverIpReverse is now handled within the Create-DnsRecords function for each domain controller +@@ -491,3 +491,3 @@ + $sb = { +- param($Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain) ++ param($Zone,$server_name_lower,$server_ip,$server_domain) +@@ -494,3 +494,3 @@ + $test = Get-DNSServerResourceRecord -RRType A -ZoneName $Zone -Name $server_name_lower +- if($test -ne $null) ++ # The check for existing DNS records has been moved to the Create-DnsRecords function +@@ -500,3 +500,3 @@ + elseif($null -eq $test) +- { ++ # The logic for adding DNS records has been moved to the Create-DnsRecords function +@@ -512,3 +512,3 @@ + # Improved the selection of the domain controller and removed unnecessary comments +- # Modify for all DCs ++ # The selection of domain controllers has been refactored into the Create-DnsRecords function +@@ -536,3 +536,3 @@ + if (($server_domain -like "CTN" -or $server_domain -like "contoso")) +- { ++ # The logic for creating DNS records on domain controllers has been refactored into the Create-DnsRecords function +@@ -547,3 +547,3 @@ + Remove-PSSession $session +- } ++ # The session removal is now handled within the Create-DnsRecords function +@@ -548,3 +548,3 @@ + elseif($server_domain -like "RR" -or $server_domain -like "railrug") +- { ++ # The logic for creating DNS records on domain controllers has been refactored into the Create-DnsRecords function +@@ -558,3 +558,3 @@ + Remove-PSSession $session +- } ++ # The session removal is now handled within the Create-DnsRecords function +@@ -560,3 +560,3 @@ + elseif($server_domain -like "DMZ" -or $server_domain -like "lsdmz") +- { ++ # The logic for creating DNS records on domain controllers has been refactored into the Create-DnsRecords function +@@ -570,3 +570,3 @@ + Remove-PSSession $session +- } ++ # The session removal is now handled within the Create-DnsRecords function +@@ -572,3 +572,3 @@ + elseif ($server_domain -like "BN" -or $server_domain -like "contoso") +- { ++ # The logic for creating DNS records on domain controllers has been refactored into the Create-DnsRecords function +@@ -582,3 +582,3 @@ + Remove-PSSession $session +- } ++ # The session removal is now handled within the Create-DnsRecords function +@@ -584,3 +584,3 @@ + elseif ($server_domain -like "CSX" -or $server_domain -like "contoso") +- { ++ # The logic for creating DNS records on domain controllers has been refactored into the Create-DnsRecords function +@@ -594,3 +594,3 @@ + Remove-PSSession $session +- } ++ # The session removal is now handled within the Create-DnsRecords function +@@ -596,3 +596,3 @@ + elseif ($server_domain -like "UP" -or $server_domain -like "lsup") +- { ++ # The logic for creating DNS records on domain controllers has been refactored into the Create-DnsRecords function +@@ -607,3 +607,3 @@ + Remove-PSSession $session +- } ++ # The session removal is now handled within the Create-DnsRecords function +@@ -608,3 +608,3 @@ + else +- { ++ # The logic for handling DNS record creation failures has been refactored into the Create-DnsRecords function +@@ -611,3 +611,3 @@ + Write-Host "No DNS entry was created for " $server_name_lower -BackgroundColor Red +- } ++ # The error message is now handled within the Create-DnsRecords function +@@ -613,3 +613,3 @@ + ###END CHANGES### +- } ++ # The end of the DNS logic section has been refactored for clarity and maintainability +@@ -616,3 +616,3 @@ + Write-Host "Created $vm_name with $server_ip for $server_car" +- } ++ # The success message is now part of the Create-DnsRecords function +@@ -618,3 +618,3 @@ + # Disconnect from vCenter only if there is an active session +- $currentSession = Get-PSSession -Name $vcenter -ErrorAction SilentlyContinue ++ # The logic for disconnecting from vCenter has been refactored for clarity and maintainability +@@ -622,3 +622,3 @@ + Disconnect-VIServer $vcenter -Confirm:$false +- } else { ++ # The else block has been refactored for clarity and maintainability +@@ -625,3 +625,3 @@ + Write-Verbose "No active vCenter session to disconnect from: $vcenter" -Verbose:$VerboseMode +- } ++ # The verbose message is now part of the refactored vCenter disconnection logic +@@ -630,3 +630,3 @@ + # Improved cleanup of variables with better error handling and corrected indentation +-# Correct the final cleanup section ++ # The final cleanup section has been refactored for clarity and maintainability +@@ -634,3 +634,3 @@ + Get-Variable -Exclude "PWD", "*Preference", "server_lists" | Remove-Variable -ErrorAction SilentlyContinue +-} catch { ++} # The catch block has been refactored for clarity and maintainability +@@ -636,3 +636,3 @@ + Write-Host "Error clearing variables: $($_.Exception.Message)" -ForegroundColor Red +-} ++} # The error message is now part of the refactored variable cleanup logic +@@ -640,3 +640,3 @@ + Write-Host "Total execution time: $($stopwatch.Elapsed.ToString())" +-} ++} # The execution time message is now part of the refactored script completion logic +``` + +These changes yield a fully functional program according to the instructions.Number of matches in test string 1: 1 diff --git a/tests/core/improve_function_test_cases/wheaties_example_code b/tests/core/improve_function_test_cases/wheaties_example_code new file mode 100644 index 0000000000000000000000000000000000000000..18f1591ec8fa80548e18bc7078128b6325728cb7 --- /dev/null +++ b/tests/core/improve_function_test_cases/wheaties_example_code @@ -0,0 +1,640 @@ +using namespace System.Collections.Generic + + +param( + [string]$CsvPath = "C:\Powershell\Files\tests2.csv", + [switch]$VerboseMode +) +# Start the stopwatch to measure the duration of the script execution + +$stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + +#----------------------------------------------------------------[DCs]--------------------------------------------------------------- +$myList = [List[string]]@('lsdc3.contoso.com','rrdc3.contoso.com','contdc1.contoso.com','osodc1.contoso.com','updc1.contoso.com') +#-----------------------------------------------------------[Declarations]----------------------------------------------------------- + +<# + +.DESCRIPTION +The script reads a CSV file containing VM specifications, sorts the entries by car, and then clones VMs based on the provided templates. It handles different accounts and domains, sets up credentials, and configures network settings for each VM. + +.PARAMETER CsvPath +The path to the CSV file containing the VM specifications. + +.EXAMPLE +PS> .\New-VMClone2.ps1 -CsvPath "C:\Powershell\Files\server_creation_rev5.csv" + +.NOTES +Author: mike wheat +Date: 01/23/2024 +#> + +# Import VM List from the specified CSV file +try { + $vmList = Import-Csv -Path $CsvPath -ErrorAction Stop + Write-Verbose "CSV file imported successfully." -Verbose:$VerboseMode +} catch { + Write-Host "Error importing CSV: $($_.Exception.Message)" -ForegroundColor Red + exit +} + +$sort_list = $vmList | Sort-Object -Property Railroad + +# Initialize server lists for different cars as ArrayLists and ensure they are not null before use +$lsServers = New-Object System.Collections.ArrayList +$nsServers = New-Object System.Collections.ArrayList +$contServers = New-Object System.Collections.ArrayList +$osoServers = New-Object System.Collections.ArrayList +$cnServers = New-Object System.Collections.ArrayList +$uptestServers = New-Object System.Collections.ArrayList +$upprodServers = New-Object System.Collections.ArrayList +$server_lists = New-Object System.Collections.ArrayList + +# Function to safely add servers to the server lists +function Safe-AddToList($list, $item) { + if ($null -ne $list -and $null -ne $item) { + $list.Add($item) | Out-Null + } +} + +#Write-Host $server_lists.Add($sort_list) +#Write-Host $server_lists + + +# $server_list.Add(1) + +$prefix = "T-" + +#Ubuntu Template Variables +$template_name = "x.Ubuntu22Template_replica" +$template_name_ls = "x.Ubuntu22Template" +$template_ip = "10.2.82.254" +$template_vlan = "VM Network 82" + +#-----------------------------------------------------------[Functions]------------------------------------------------------------ +function Make-VCCreds([string]$domain) +{ + #Creates a vCenter credential variable based on the domain + if ($domain -like "CTN") + { + $User = "contoso\VMPowerUser" #VMPowerUser //CTNPowershell + } + elseif ($domain -like "NS" -or $domain -like "RR") + { + $User = "contoso\VMPowerUser" #VMPowerUser //CTNPowershell + } + elseif ($domain -like "CTNBNF") + { + $User = "contoso\VMPowerUser" #VMPowerUser //CTNPowershell + } + elseif ($domain -like "CTNCX") + { + $User = "contoso\VMPowerUser" #VMPowerUser //CTNPowershell + } + elseif ($domain -like "CTNUP") + { + $User = "contoso\VMPowerUser" #VMPowerUser //CTNPowershell + } + elseif ($domain -like "CTNC") + { + $User = "LSTPU\VMPowerUser" #VMPowerUser //CTNPowershell + } + else + { + $User = "contoso\VMPowerUser" #VMPowerUser /$domain/CTNPowershell + } + + $password = get-content C:\Powershell\Key23\cred_Administrator.txt | ConvertTo-SecureString + $vc_creds = New-Object System.Management.Automation.PSCredential ($User, $Password) + + #$CredentialCTN = New-Object System.Management.Automation.PSCredential ($User, $Password) + #$vc_creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, (Get-Content $Password | ConvertTo-SecureString -Key $key) + #Write-Host $vc_creds + return $vc_creds + +} + +function Make-SSHCreds() +{ + #Creates SSH credential for the linuxsupport account + $User = "linuxsupport" + $PasswordFile = "C:\Pow\Credentials\support.txt" + $KeyFile = "C:\Pow\Credentials\support.key" + $key = Get-Content $KeyFile + $ssh_creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, (Get-Content $PasswordFile | ConvertTo-SecureString -Key $key) + return $ssh_creds +} + +#-----------------------------------------------------------[Execution]------------------------------------------------------------ + +#Make Creds to be used to SSH into created servers +$ssh_creds = Make-SSHCreds + +# Initialize the server lists before the foreach loop +# Initialize the server lists as generic lists before the foreach loop +$ls_servers = New-Object System.Collections.Generic.List[object] +$ns_servers = New-Object System.Collections.Generic.List[object] +$cont_servers = New-Object System.Collections.Generic.List[object] +$oso_servers = New-Object System.Collections.Generic.List[object] +$cn_servers = New-Object System.Collections.Generic.List[object] +$upprod_servers = New-Object System.Collections.Generic.List[object] +$uptest_servers = New-Object System.Collections.Generic.List[object] + +foreach ($row in $sort_list) { + if ($row.Railroad.ToLower() -eq "ls") { + $ls_servers.Add($row) + } + if ($row.Railroad.ToLower() -eq "ns" -or $row.Railroad.ToLower() -eq "rr") { + $ns_servers.Add($row) + } + if ($row.Railroad.ToLower() -eq "cont") { + $cont_servers.Add($row) + } + if ($row.Railroad.ToLower() -eq "oso") { + $oso_servers.Add($row) + } + if ($row.Railroad.ToLower() -eq "cn") { + $cn_servers.Add($row) + } + if ($row.Railroad.ToLower() -eq "up") { + if ($row.Name -like "up*p*") { + $upprod_servers.Add($row) + } + if ($row.Name -like "up*Test*") { + $uptest_servers.Add($row) + } + } +} + + +if ($ls_servers -and $ls_servers.Count -gt 0) +{ + $server_lists.Add($ls_servers) + Write-Verbose "Added CTN servers to server list." -Verbose:$VerboseMode +} +# Replace direct indexing checks with null and count checks +if ($null -ne $ns_servers -and $ns_servers.Count -gt 0) +{ + $server_lists.Add($ns_servers) +} +if ($null -ne $cont_servers -and $cont_servers.Count -gt 0) +{ + $server_lists.Add($cont_servers) +} +if ($null -ne $oso_servers -and $oso_servers.Count -gt 0) +{ + $server_lists.Add($oso_servers) +} +if ($null -ne $cn_servers -and $cn_servers.Count -gt 0) +{ + $server_lists.Add($cn_servers) +} +if ($null -ne $upprod_servers -and $upprod_servers.Count -gt 0) +{ + $server_lists.Add($upprod_servers) +} +if ($null -ne $uptest_servers -and $uptest_servers.Count -gt 0) +{ + $server_lists.Add($uptest_servers) +} +# Ensure that the server_lists array is not null or empty before iterating +if ($server_lists -and $server_lists.Count -gt 0) { + # ... (rest of the code inside the if block) + # The closing brace for the if block was missing. It has been added below. +} + foreach ($server_list in $server_lists) +{ + + #Clear out variables each loop + $servers = $null + $car = $null + $cred_domain = $null + $vcenter = $null + + #Check to make sure the lists have servers in it and only run logic if there are servers to be created + ### CTN ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $ls_servers -Property Name) -eq $null) + { + $servers = $ls_servers + $car = "ls" + $cred_domain = "ls" + $vcenter = "ls-vcenter.contoso.com" + } + ### NS ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $ns_servers -Property Name) -eq $null) + { + $servers = $ns_servers + $car = "rr" + $cred_domain = "NS" + $vcenter = "nso-vcenter.contoso.com" + } + ### BN ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $cont_servers -Property Name) -eq $null) + { + $servers = $cont_servers + $car = "BN" + $cred_domain = "CTNBN" + $vcenter = "cont-vcenter.contoso.com" + } + ### CSX ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $oso_servers -Property Name) -eq $null) + { + $servers = $oso_servers + $car = "CS" + $cred_domain = "CTNCS" + $vcenter = "oso-vcenter.contoso.com" + } + ### CN ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $cn_servers -Property Name) -eq $null) + { + # The CN car section is commented out + # $servers = $cn_servers + # $car = "CN" + # $cred_domain = "CTNCN" + # $vcenter = "cn-vcenter.lscn.com" + #below is the test vcenter for now. + # $vcenter = "vcenter1test.contoso.com" + } + ### UP PROD ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $upprod_servers -Property Name) -eq $null) + { + $servers = $upprod_servers + $car = "up" + $cred_domain = "CTNUP" + $vcenter = "up-vcenter.contoso.com" + } + ### UP TEST ### + if ((Compare-Object -ReferenceObject $server_list -DifferenceObject $uptest_servers -Property Name) -eq $null) + { + $servers = $uptest_servers + $car = "UP" + $cred_domain = "CTNUP" + $vcenter = "up-vcenter.contoso.com" + # $vcenter = "up-vcenter1test.contoso.com" + } + #Use the Above function to create creds for the domain Powershell account + # Use the Make-VCCreds function to create credentials and handle potential errors +try { + $vc_creds = Make-VCCreds -domain $cred_domain +} catch { + Write-Host "Failed to create vCenter credentials for domain $cred_domain. Error: $($_.Exception.Message)" -ForegroundColor Red + continue +} + + if ($null -ne $vcenter) { + # Ensure Session Is Clear + Write-Verbose "Disconnecting from vCenter: $vcenter" -Verbose:$VerboseMode + write-host "There is an error coming, don't worry it's a normal error. Just clearing the session" + + try { + Disconnect-VIServer $vcenter -Confirm:$false -ErrorAction Stop + } catch { + # Handle the error here, if needed + Write-Verbose "Error disconnecting from vCenter: $_" -Verbose:$VerboseMode + } + + # Connect New Session + Write-Host 'Attempting to connect to ' $vcenter + try { + Write-Verbose "Attempting to connect to vCenter: $vcenter" -Verbose:$VerboseMode + Connect-VIServer $vcenter -Credential $vc_creds -ErrorAction Stop -Verbose + } catch { + Write-Host "`nFailed to connect to" $vcenter ". Please re-attempt." + break + } +} + + #Uses the server lists to pull all the VMs to be created + foreach ($server in $servers) + { + + $vm_name = $server.name + # Updated VM renaming logic to apply prefixes correctly based on environment and client + if ($server.Railroad -eq "ls" -and $server.Name -notlike "*Test*" -and $server.Name -notlike "*T") { + $vm_name = "P-" + $server.Name + } elseif ($server.Name -like "*Test*" -or $server.Name -like "*T") { + $vm_name = "T-" + $server.Name + } else { + $vm_name = $server.Name + } + + $server_name = $server.name + $server_ip = $server.ip + $server_car = $server.car + $server_domain = $server.domain + $server_name_lower = $server_name.ToLower() #To Lower Case: https://devblogs.microsoft.com/scripting/powertip-convert-all-uppercase-string-to-lowercase/ + + #This verifies no VM with the same name exists + $duplicate = Get-VM -Name $vm_name -ErrorAction Ignore + # Corrected indentation and removed unnecessary comments + if ($duplicate) { + Write-Host "VM with name $vm_name already exists." + continue + } + + #Gather Required Data to create the VM and distribute it to appropriate resources + $vmhosts = Get-VMHost + #Write-Host $vmhosts + $vmhost = $vmhosts | Get-Random + #$vmhost = Get-VMHost -Name "DOMAINvm1p" + $stringBufferDSProd="-Datastore" + + #Verifies that the naming convention is correct and chooses proper datastores + if ($vm_name -like "T-*" -or $vm_name -like "*-Test") { + $datastores = Get-Datastore | Where-Object { + ($_.name -like "*DatastoreTest0*") -and ($_.name -notlike "*DB*") + } +} + elseif ($vm_name -like "P-*" -and $vm_name -notlike "*DB*" -and $vm_name -notlike "*db*") + { + $datastores = Get-Datastore | Where-Object {($_.name.ToLower() -like ("$car$stringBufferDSProd*").ToLower()) -and ($_.name -notlike "*Test*") -and ($_.name -notlike "*DB*") -and ($_.name -notlike "*vLabDatastore*")} + } + elseif ($vm_name -like "*db*" -or $vm_name -like "*DB*") { + $datastores = Get-Datastore | Where-Object { $_.name.ToLower() -like "*db*" } + } + else + { + Write-Host "Incorrect VM Naming Convention. VM Name should be similar to the form: T-BNApps103Test or BNApps103P" + $datastores = Get-Datastore | Get-Random + } + $datastoreSorted = $datastores | Sort-Object -Property FreeSpaceGB -Descending + # Assuming $datastores contains the list of datastore objects sorted by available space + # $vm_name contains the name of the VM to be created + + +foreach ($ds in $datastoreSorted) { + # Determine the type of server (Test or Production) + $isTestServer = $vm_name -like "*Test" + $isProdServer = $vm_name -like "P-*" -or $vm_name -like "*-P" + + # Extract the base name for comparison (excluding the ending pattern) + $baseName = if ($isTestServer) { $vm_name -replace "-Test$" } + elseif ($isProdServer) { $vm_name -replace "^(P-)|(-P)$"} + + # Check for existing VMs with a similar base name on the current datastore + $existingVMs = Get-VM | Where-Object { + $_.Datastore -eq $ds.Name -and + $_.Name -like "$baseName*" -and + ($isTestServer -or $_.Name -notlike "*Test") + } + + # If there are no existing VMs with the similar base name, select this datastore + if ($existingVMs.Count -eq 0) { + $datastore = $ds + Write-Host "Selected datastore: $($datastore.Name) with available space: $($datastore.FreeSpaceGB) GB" + break + } +} + +# Output the selected datastore +if ($datastore) { + Write-Host "Using datastore: $($datastore.Name)" +} else { + Write-Host "No suitable datastore found." +} + + + #Choose the proper VM to clone + if($car -like "ls") + { + $template = Get-VM -Name "$template_name_ls" + } + else + { + $template = Get-VM -Name "$template_name" + } + + $diskformat = "Thin" + Sleep 10 + #Create a clone of the Ubuntu20 Template machine, with thin provisioned storage + Write-Host "VCenter: "$vcenter "VMName: "$vm_name "Template: "$template "VMHosts: "$vmhosts "VMHost: "$vmhost "Datastore: "$datastore "Datastores: "$datastores $diskformat + Write-Verbose "Creating new VM: $vm_name on host: $vmhost" -Verbose:$VerboseMode + New-VM -Name $vm_name -VM $template -VMHost $vmhost -Datastore $datastore -DiskStorageFormat $diskformat -Confirm:$False -verbose + SLeep 10 + #Wait 10 seconds and then attach the network and power on the VM + Sleep 10 + $vm = Get-VM $vm_name + #Write-Host $vm + if($vm) + { + #Grab the second the third octet and check the hosts for a VLAN with that ID + $vlan_id = $server_ip.Split('.')[-2] + $server_vlan = Get-VDPortGroup | Where {$_.Name -contains "*$vlan_id"} -ErrorAction Continue + # Corrected the logic for VLAN assignment and improved readability + if ($server_vlan) { + # Connect the NIC to the VM with the correct VLAN + $vm | Get-NetworkAdapter | Set-NetworkAdapter -PortGroup $server_vlan -Confirm:$false -Verbose + $vm | Get-NetworkAdapter | Set-NetworkAdapter -Type Vmxnet3 -StartConnected $true -Confirm:$false -Verbose + } elseif (($server_vlan -eq $null) -and ($server.car -like $server.domain)) { + $server_vlan = Get-VirtualNetwork | Where-Object { $_.Name -like "*$vlan_id" } + $vm | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $server_vlan -Type Vmxnet3 -StartConnected $true -Confirm:$false -Verbose + } else { + Write-Host "This network $vlan_id does not exist on this vCenter $vcenter" + break + } + + #Turn on VM and wait for VMTools to respond + Start-VM -VM $vm + + # Minimum + # Sleep 150 -- optional: true was added to /etc/netplan/00-* to resolve this + sleep 150 + + #Script to run locally against VM + $provision_script = "sudo ls-provision-auto $server_name_lower $server_ip $server_car" + $provision_script_sssd = "sudo ls-sssd $server_name_lower $server_ip $server_car" + Invoke-VMScript -VM $vm -GuestCredential $ssh_creds -ScriptType bash -ScriptText $provision_script -Confirm:$False -Verbose + + # Adjust Per VM Template (Don't Go Below 30) + sleep 120 + + #Restart VM after script runs + Restart-VM $vm -Confirm:$False + + ###IF DC's Change - You Will Need To WinRM Accept Them### + if($server_domain -like "ls") + { + $Zone = "contoso.com" + } + elseif($server_domain -like "rr") + { + $Zone = "contoso.com" + } + elseif($server_domain -like "oso") + { + $Zone = "contoso.com" + } + elseif($server_domain -like "cont") + { + $Zone = "contoso.com" + } + elseif($server_domain -like "up") + { + $Zone = "contoso.com" + } + else + { + $Zone = "$server_domain.com" + } + + $firstoctet = $server_ip.Split(".")[2] + $secondoctet = $server_ip.Split(".")[1] + $thirdoctet = $server_ip.Split(".")[0] + + $serverIpReverse = $firstoctet + '.' + $secondoctet + '.' + $thirdoctet + '.' + "in-addr.arpa" + + + Write-Host $serverIpReverse + Write-Host $server_name_lower'.'$server_domain + + $sb = { + param($Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain) + + $test = Get-DNSServerResourceRecord -RRType A -ZoneName $Zone -Name $server_name_lower + if($test -ne $null) + { + Return "Found DNS Record for $server_name_lower" + } + elseif($null -eq $test) + { + Add-DnsServerResourceRecordA -name $server_name_lower -ZoneName $Zone -AllowUpdateAny -IPv4Address $server_ip -TimeToLive 01:00:00 + #Add-DnsServerResourceRecordPtr -Name $server_ip -ZoneName $serverIpReverse -AllowUpdateAny -TimeToLive 01:00:00 -AgeRecord -PtrDomainName $server_name_lower'.'$Zone + Return "Created DNS Record for $server_name_lower" + #Return "Created R-DNS Record for $server_name_lower" + } + else + { + Return "An error occurred when creating the DNS entry." + } + } + + # Improved the selection of the domain controller and removed unnecessary comments + # Modify for all DCs + $domainController = $myList | Where-Object { $_ -match $car } + $forwardZoneName = $Zone + # Corrected the script block for reverse DNS record creation and improved readability + $rSb = { + param($domainController, $forwardZoneName, $server_name_lower) + + $records = Get-DnsServerResourceRecord -ZoneName $forwardZoneName -RRType A -Name $server_name_lower + foreach ($record in $records) { + # The reverse lookup domain name. This is the PTR Response. + $ptrDomain = $record.HostName + '.' + $forwardZoneName + + # Grab the last octet of the IP address for the record being processed. + $name = ($record.RecordData.IPv4Address.ToString() -replace '^(\d+)\.(\d+)\.(\d+).(\d+)$', '$4') + + # Reverse the IP Address for the Zone Name. Leave off the last octet to place in proper /24 subnet. + $rzname = ($record.RecordData.IPv4Address.ToString() -replace '^(\d+)\.(\d+)\.(\d+).(\d+)$', '$3.$2.$1') + '.in-addr.arpa' + Write-Host $ptrDomain $name $rzname + # Add the new PTR record. + Add-DnsServerResourceRecordPtr -Name $name -ZoneName $rzname -ComputerName $domainController -PtrDomainName $ptrDomain + } + } + + if (($server_domain -like "CTN" -or $server_domain -like "contoso")) + { + $session = New-PSSession -ComputerName "CTNDC3.contoso.com" -Credential $vc_creds + Write-Verbose "Creating DNS A record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $sb -ArgumentList $Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain + Write-Verbose "DNS A record created successfully." -Verbose:$VerboseMode + Sleep 45 + Write-Verbose "Creating DNS PTR record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $rSb -ArgumentList $domainController,$forwardZoneName,$server_name_lower + Write-Verbose "DNS PTR record created successfully." -Verbose:$VerboseMode + Remove-PSSession $session + } + elseif($server_domain -like "RR" -or $server_domain -like "railrug") + { + $session = New-PSSession -ComputerName "RRDC3.contoso.com" -Credential $vc_creds + Write-Verbose "Creating DNS A record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $sb -ArgumentList $Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain + Write-Verbose "DNS A record created successfully." -Verbose:$VerboseMode + Sleep 45 + Write-Verbose "Creating DNS PTR record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $rSb -ArgumentList $domainController,$forwardZoneName,$server_name_lower + Write-Verbose "DNS PTR record created successfully." -Verbose:$VerboseMode + Remove-PSSession $session + } + elseif($server_domain -like "DMZ" -or $server_domain -like "lsdmz") + { + $session = New-PSSession -ComputerName "CTNDMZDC3.lsdmz.com" -Credential $vc_creds + Write-Verbose "Creating DNS A record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $sb -ArgumentList $Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain + Write-Verbose "DNS A record created successfully." -Verbose:$VerboseMode + Sleep 45 + Write-Verbose "Creating DNS PTR record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $rSb -ArgumentList $domainController,$forwardZoneName,$server_name_lower + Write-Verbose "DNS PTR record created successfully." -Verbose:$VerboseMode + Remove-PSSession $session + } + elseif ($server_domain -like "BN" -or $server_domain -like "contoso") + { + $session = New-PSSession -ComputerName "BNDC1.contoso.com" -Credential $vc_creds + Write-Verbose "Creating DNS A record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $sb -ArgumentList $Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain + Write-Verbose "DNS A record created successfully." -Verbose:$VerboseMode + Sleep 45 + Write-Verbose "Creating DNS PTR record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $rSb -ArgumentList $domainController,$forwardZoneName,$server_name_lower + Write-Verbose "DNS PTR record created successfully." -Verbose:$VerboseMode + Remove-PSSession $session + } + elseif ($server_domain -like "CSX" -or $server_domain -like "contoso") + { + $session = New-PSSession -ComputerName "CSXDC1.contoso.com" -Credential $vc_creds + Write-Verbose "Creating DNS A record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $sb -ArgumentList $Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain + Write-Verbose "DNS A record created successfully." -Verbose:$VerboseMode + Sleep 45 + Write-Verbose "Creating DNS PTR record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $rSb -ArgumentList $domainController,$forwardZoneName,$server_name_lower + Write-Verbose "DNS PTR record created successfully." -Verbose:$VerboseMode + Remove-PSSession $session + } + elseif ($server_domain -like "UP" -or $server_domain -like "lsup") + { + $session = New-PSSession -ComputerName "10.2.82.10" -Credential $vc_creds + Write-Verbose "Creating DNS A record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $sb -ArgumentList $Zone,$server_name_lower,$server_ip,$serverIpReverse,$server_domain + Write-Verbose "DNS A record created successfully." -Verbose:$VerboseMode + Sleep 45 + Write-Verbose "Creating DNS PTR record for: $server_name_lower" -Verbose:$VerboseMode + Invoke-Command -Session $session -ScriptBlock $rSb -ArgumentList $domainController,$forwardZoneName,$server_name_lower + Write-Verbose "DNS PTR record created successfully." -Verbose:$VerboseMode + Remove-PSSession $session + } + else + { + Write-Host "No DNS entry was created for " $server_name_lower -BackgroundColor Red + } + + ###END CHANGES### + } + Write-Host "Created $vm_name with $server_ip for $server_car" + } + + # Disconnect from vCenter only if there is an active session + $currentSession = Get-PSSession -Name $vcenter -ErrorAction SilentlyContinue + if ($currentSession -and $currentSession.State -eq 'Opened') { + Write-Verbose "Disconnecting from vCenter: $vcenter" -Verbose:$VerboseMode + Disconnect-VIServer $vcenter -Confirm:$false + } else { + Write-Verbose "No active vCenter session to disconnect from: $vcenter" -Verbose:$VerboseMode + } +} + +#-----------------------------------------------------------[Finish]------------------------------------------------------------ + +# Improved cleanup of variables with better error handling and corrected indentation +# Correct the final cleanup section +try { + Get-Variable -Exclude "PWD", "*Preference", "server_lists" | Remove-Variable -ErrorAction SilentlyContinue +} catch { + Write-Host "Error clearing variables: $($_.Exception.Message)" -ForegroundColor Red +} + +# Stop the stopwatch and write the elapsed time to the console +$stopwatch.Stop() +Write-Host "Total execution time: $($stopwatch.Elapsed.ToString())" diff --git a/tests/core/test_ai.py b/tests/core/test_ai.py new file mode 100644 index 0000000000000000000000000000000000000000..44eaeb6321f519059a2ff38d9c32cec7814068a3 --- /dev/null +++ b/tests/core/test_ai.py @@ -0,0 +1,53 @@ +from langchain.chat_models.base import BaseChatModel +from langchain_community.chat_models.fake import FakeListChatModel + +from gpt_engineer.core.ai import AI + + +def mock_create_chat_model(self) -> BaseChatModel: + return FakeListChatModel(responses=["response1", "response2", "response3"]) + + +def test_start(monkeypatch): + monkeypatch.setattr(AI, "_create_chat_model", mock_create_chat_model) + + ai = AI("gpt-4") + + # act + response_messages = ai.start("system prompt", "user prompt", step_name="step name") + + # assert + assert response_messages[-1].content == "response1" + + +def test_next(monkeypatch): + # arrange + monkeypatch.setattr(AI, "_create_chat_model", mock_create_chat_model) + + ai = AI("gpt-4") + response_messages = ai.start("system prompt", "user prompt", step_name="step name") + + # act + response_messages = ai.next( + response_messages, "next user prompt", step_name="step name" + ) + + # assert + assert response_messages[-1].content == "response2" + + +def test_token_logging(monkeypatch): + # arrange + monkeypatch.setattr(AI, "_create_chat_model", mock_create_chat_model) + + ai = AI("gpt-4") + + # act + response_messages = ai.start("system prompt", "user prompt", step_name="step name") + usageCostAfterStart = ai.token_usage_log.usage_cost() + ai.next(response_messages, "next user prompt", step_name="step name") + usageCostAfterNext = ai.token_usage_log.usage_cost() + + # assert + assert usageCostAfterStart > 0 + assert usageCostAfterNext > usageCostAfterStart diff --git a/tests/core/test_chat_to_files.py b/tests/core/test_chat_to_files.py new file mode 100644 index 0000000000000000000000000000000000000000..2762219e33bf5b9b12fc75d674c8fb5ea05bc6b4 --- /dev/null +++ b/tests/core/test_chat_to_files.py @@ -0,0 +1,314 @@ +import os + +from typing import Dict, Tuple + +import pytest + +from gpt_engineer.core.chat_to_files import parse_diffs +from gpt_engineer.core.diff import is_similar +from gpt_engineer.core.files_dict import file_to_lines_dict + +THIS_FILE_DIR = os.path.dirname(os.path.abspath(__file__)) + +example_diff = """ +Irrelevant line to be ignored + +another irrelevant line to be ignored +```diff +--- example.txt ++++ example.txt +@@ -12,3 +12,4 @@ + sample text 1 + sample text 2 ++ added extra line here +- original text A ++ updated original text A with changes +@@ -35,4 +36,5 @@ + checking status: +- perform operation X ++ perform operation X only if specific condition holds ++ new operation related to condition + evaluating next step: +- execute step Y ++ revised execution of step Y +``` +""" + +example_multiple_diffs = """ +I apologize for the oversight. Let's correct the `calculator.py` file with the proper git diff format, ensuring that the context lines match the original code exactly. + +```diff +--- calculator.py ++++ calculator.py +@@ -1,3 +1,3 @@ + class Calculator: +- def add(self, a, b): +- return a - b # Logical ++ def add(self, a, b): # Adds two numbers ++ return a + b +``` + +Now, let's create the `main.py` file with the correct git diff format: + +```diff +--- /dev/null ++++ main.py +@@ -0,0 +1,7 @@ ++from calculator import Calculator ++ ++# Function to demonstrate the usage of the Calculator class ++def main(): ++ calc = Calculator() ++if __name__ == "__main__": ++ main() +``` + +These changes should now correctly apply to the provided code and create a simple calculator program with a command-line interface. + + +""" + +example_line_dist_diff = """ +Irrelevant line to be ignored + +another irrelevant line to be ignored +```diff +--- example.txt ++++ example.txt +@@ -10,4 +13,5 @@ + sample text 1 + sample text 2 ++ added extra line here +- original text A ++ updated original text A with changes +@@ -33,14 +363,5 @@ + checking status: +- perform operation X ++ perform operation X only if specific condition holds ++ new operation related to condition + evaluating next step: +- execute step Y ++ revised execution of step Y +``` +""" + +add_example = """ +Uninteresting stuff +```diff +--- /dev/null ++++ new_file.txt +@@ -0,0 +1,3 @@ ++First example line ++ ++Last example line +``` +""" + +file_example = """# Introduction + +@Analysis +Overview: outcomes +% +Background: *context* + +Method: [] +! +Theories: ? +> Leading up... + sample text 1 + sample text 2 + original text A +a +Challenges: ~ + +Perspectives: <> + +Strategy: {#} ++ +Outcomes: ^^^ + +Future: ||| + +x + +Y + +Z + + + +code + checking status: + perform operation X + evaluating next step: + execute step Y +End. + +Conclusion: *** +""" + + +# Single function tests +def test_basic_similarity(): + assert is_similar("abc", "cab") + assert not is_similar("abc", "def") + + +def test_case_insensitivity_and_whitespace(): + assert is_similar("A b C", "c a b") + assert not is_similar("Abc", "D e F") + + +def test_length_and_character_frequency(): + assert is_similar("aabbc", "bacba") + assert not is_similar("aabbcc", "abbcc") + + +def test_edge_cases(): + assert not is_similar("", "a") + assert is_similar("a", "a") + + +def insert_string_in_lined_string(string, to_insert, line_number): + split_string = string.split("\n") + split_string.insert(line_number - 1, to_insert) + return "\n".join(split_string) + + +def test_diff_changing_one_file(): + diffs = parse_diffs(example_diff) + for filename, diff in diffs.items(): + string_diff = diff.diff_to_string() + correct_diff = "\n".join(example_diff.strip().split("\n")[4:-1]) + assert string_diff == correct_diff + + +def test_diff_adding_one_file(): + add_diff = parse_diffs(add_example) + for filename, diff in add_diff.items(): + string_add_diff = diff.diff_to_string() + correct_add_diff = "\n".join(add_example.strip().split("\n")[2:-1]) + assert string_add_diff == correct_add_diff + + +def test_diff_changing_two_files(): + merged_diff = parse_diffs(example_diff + add_example) + correct_diff = "\n".join(example_diff.strip().split("\n")[4:-1]) + correct_add_diff = "\n".join(add_example.strip().split("\n")[2:-1]) + assert merged_diff["example.txt"].diff_to_string() == correct_diff + assert merged_diff["new_file.txt"].diff_to_string() == correct_add_diff + + +def test_validate_diff_correct(): + lines_dict = file_to_lines_dict(file_example) + diffs = parse_diffs(example_diff) + # This is a test in its own right since it full of exceptions, would something go wrong + list(diffs.values())[0].validate_and_correct(lines_dict) + + +def test_correct_distorted_numbers(): + lines_dict = file_to_lines_dict(file_example) + diffs = parse_diffs(example_line_dist_diff) + # This is a test in its own right since it full of exceptions, would something go wrong + list(diffs.values())[0].validate_and_correct(lines_dict) + correct_diff = "\n".join(example_diff.strip().split("\n")[4:-1]) + assert diffs["example.txt"].diff_to_string() == correct_diff + + +def test_correct_skipped_lines(): + distorted_example = insert_string_in_lined_string( + file_example, "#\n#comment\n#\n#", 14 + ) + diffs = parse_diffs(example_diff) + list(diffs.values())[0].validate_and_correct(file_to_lines_dict(distorted_example)) + with open( + os.path.join( + THIS_FILE_DIR, + "improve_function_test_cases", + "corrected_diff_from_missing_lines", + ), + "r", + ) as f: + corrected_diff_from_missing_lines = f.read() + assert ( + diffs["example.txt"].diff_to_string().strip() + == corrected_diff_from_missing_lines.strip() + ) + + +def test_correct_skipped_lines_and_number_correction(): + distorted_example = insert_string_in_lined_string( + file_example, "#\n#comment\n#\n#", 14 + ) + diffs = parse_diffs(example_line_dist_diff) + # list(diffs.values())[0].validate_and_correct(file_to_lines_dict(distorted_example)) + for diff in diffs.values(): + problems = diff.validate_and_correct(file_to_lines_dict(distorted_example)) + print(problems) + with open( + os.path.join( + THIS_FILE_DIR, + "improve_function_test_cases", + "corrected_diff_from_missing_lines", + ), + "r", + ) as f: + corrected_diff_from_missing_lines = f.read() + assert ( + diffs["example.txt"].diff_to_string().strip() + == corrected_diff_from_missing_lines.strip() + ) + + +def test_diff_regex(): + diff = parse_diffs(example_diff) + assert len(diff) == 1 + + diffs = parse_diffs(example_multiple_diffs) + assert len(diffs) == 2 + + +def parse_chats_with_regex( + diff_file_name: str, code_file_name: str +) -> Tuple[str, str, Dict]: + # Load the diff + with open( + os.path.join(THIS_FILE_DIR, "improve_function_test_cases", diff_file_name), "r" + ) as f: + diff_content = f.read() + + # Load the corresponding code + with open( + os.path.join(THIS_FILE_DIR, "improve_function_test_cases", code_file_name), "r" + ) as f: + code_content = f.read() + + # Parse the diffs + diffs = parse_diffs(diff_content) + + return diff_content, code_content, diffs + + +# test parse diff +def test_controller_diff(): + parse_chats_with_regex("controller_chat", "controller_code") + + +def test_simple_calculator_diff(): + parse_chats_with_regex("simple_calculator_chat", "simple_calculator_code") + + +def test_complex_temperature_converter_diff(): + parse_chats_with_regex("temperature_converter_chat", "temperature_converter_code") + + +def test_complex_task_master_diff(): + parse_chats_with_regex("task_master_chat", "task_master_code") + + +def test_long_file_diff(): + parse_chats_with_regex("wheaties_example_chat", "wheaties_example_code") + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/core/test_git.py b/tests/core/test_git.py new file mode 100644 index 0000000000000000000000000000000000000000..f8bfac51fa5510e5f0c6929ce27a25fd2161d1d3 --- /dev/null +++ b/tests/core/test_git.py @@ -0,0 +1,108 @@ +import subprocess +import tempfile + +from pathlib import Path + +from gpt_engineer.core.git import ( + filter_by_gitignore, + filter_files_with_uncommitted_changes, + init_git_repo, + is_git_installed, + is_git_repo, + stage_files, +) + + +def test_verify_git_installed(): + # If git isn't installed we can't run any git tests either way + assert is_git_installed() + + +def test_init_git_repo(): + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) + init_git_repo(path) + assert is_git_repo(path) + + +def test_stage_files(): + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) + init_git_repo(path) + + # Create a file and stage it + file = path / "test.txt" + file.write_text("test") + + stage_files(path, ["test.txt"]) + + # Check if the file is staged + assert ( + subprocess.run( + ["git", "diff", "--cached", "--name-only"], + cwd=path, + stdout=subprocess.PIPE, + ) + .stdout.decode() + .strip() + == "test.txt" + ) + + +def test_filter_by_gitignore(): + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) + init_git_repo(path) + + # Create a .gitignore file + gitignore = path / ".gitignore" + gitignore.write_text("*.txt") + assert filter_by_gitignore(path, ["test.txt"]) == [] + + +def test_filter_by_uncommitted_changes(): + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) + init_git_repo(path) + + # Create a file and commit it + file = path / "test.txt" + file.write_text("test") + + subprocess.run(["git", "add", "test.txt"], cwd=path) + subprocess.run(["git", "commit", "-m", "test"], cwd=path) + + # Update the file + file.write_text("test2") + + # Check if the file is staged + assert filter_files_with_uncommitted_changes(path, {"test.txt": "test"}) == [ + "test.txt" + ] + + +def test_filter_by_uncommitted_changes_ignore_staged_files(): + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) + init_git_repo(path) + + # Create a file but and stage it + file = path / "test.txt" + file.write_text("test") + subprocess.run(["git", "add", "test.txt"], cwd=path) + + # Check if the file is staged + assert filter_files_with_uncommitted_changes(path, {"test.txt": "test"}) == [] + + +def test_filter_by_uncommitted_changes_ignore_untracked(): + with tempfile.TemporaryDirectory() as tmpdir: + path = Path(tmpdir) + init_git_repo(path) + + # Create a file but don't track it + file = path / "test.txt" + file.write_text("test") + + # Check if the file is staged + assert filter_files_with_uncommitted_changes(path, {"test.txt": "test"}) == [] diff --git a/tests/core/test_salvage_correct_hunks.py b/tests/core/test_salvage_correct_hunks.py new file mode 100644 index 0000000000000000000000000000000000000000..aeebd378c5d797d665490675f20a5539caa30f4e --- /dev/null +++ b/tests/core/test_salvage_correct_hunks.py @@ -0,0 +1,107 @@ +import os +import shutil + +from typing import List + +import pytest + +from langchain_core.messages import AIMessage + +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.paths import memory_path +from gpt_engineer.core.default.steps import salvage_correct_hunks +from gpt_engineer.core.files_dict import FilesDict + +TEST_FILES_DIR = os.path.dirname(os.path.abspath(__file__)) +memory = DiskMemory(memory_path(".")) + + +def get_file_content(file_path: str) -> str: + with open( + os.path.join(TEST_FILES_DIR, "improve_function_test_cases", file_path), "r" + ) as f: + return f.read() + + +def message_builder(chat_path: str) -> List[AIMessage]: + chat_content = get_file_content(chat_path) + + json = { + "lc": 1, + "type": "constructor", + "id": ["langchain", "schema", "messages", "AIMessage"], + "kwargs": { + "content": chat_content, + "additional_kwargs": {}, + "response_metadata": {"finish_reason": "stop"}, + "name": None, + "id": None, + "example": False, + }, + } + + return [AIMessage(**json["kwargs"])] + + +def test_validation_and_apply_complex_diff(): + files = FilesDict({"taskmaster.py": get_file_content("task_master_code")}) + salvage_correct_hunks(message_builder("task_master_chat"), files, memory) + + +def test_validation_and_apply_long_diff(): + files = FilesDict({"VMClonetest.ps1": get_file_content("wheaties_example_code")}) + salvage_correct_hunks(message_builder("wheaties_example_chat"), files, memory) + + +def test_validation_and_apply_wrong_diff(): + files = FilesDict( + {"src/components/SocialLinks.tsx": get_file_content("vgvishesh_example_code")} + ) + salvage_correct_hunks(message_builder("vgvishesh_example_chat"), files, memory) + + +def test_validation_and_apply_non_change_diff(): + files = FilesDict({"src/App.tsx": get_file_content("vgvishesh_example_2_code")}) + salvage_correct_hunks(message_builder("vgvishesh_example_2_chat"), files, memory) + + +def test_validation_and_apply_diff_on_apps_benchmark_6(): + files = FilesDict({"main.py": get_file_content("apps_benchmark_6_code")}) + salvage_correct_hunks(message_builder("apps_benchmark_6_chat"), files, memory) + + +def test_validation_and_apply_diff_on_apps_benchmark_6_v2(): + files = FilesDict({"main.py": get_file_content("apps_benchmark_6_v2_code")}) + salvage_correct_hunks(message_builder("apps_benchmark_6_v2_chat"), files, memory) + + +def test_create_two_new_files(): + files = FilesDict({"main.py": get_file_content("create_two_new_files_code")}) + salvage_correct_hunks(message_builder("create_two_new_files_chat"), files, memory) + + +def test_theo_case(): + files = FilesDict({"dockerfile": get_file_content("theo_case_code")}) + updated_files, _ = salvage_correct_hunks( + message_builder("theo_case_chat"), files, memory + ) + print(updated_files["dockerfile"]) + print(updated_files["run.py"]) + + +def test_clean_up_folder(clean_up_folder): + # The folder should be deleted after the test is run + assert True + + +@pytest.fixture +def clean_up_folder(): + yield + # Teardown code: delete a folder and all its contents + print("cleaning up") + folder_path = os.path.join(os.path.dirname(__file__), ".gpteng") + shutil.rmtree(folder_path, ignore_errors=True) + + +if __name__ == "__main__": + pytest.main() diff --git a/tests/core/test_token_usage.py b/tests/core/test_token_usage.py new file mode 100644 index 0000000000000000000000000000000000000000..7d2f8af002eda2b24914239fdee01adf2d961a0f --- /dev/null +++ b/tests/core/test_token_usage.py @@ -0,0 +1,110 @@ +import base64 +import csv +import io +import os + +from io import StringIO +from pathlib import Path + +from langchain.schema import HumanMessage, SystemMessage +from PIL import Image + +from gpt_engineer.core.token_usage import Tokenizer, TokenUsageLog + + +def test_format_log(): + # arrange + token_usage_log = TokenUsageLog("gpt-4") + request_messages = [ + SystemMessage(content="my system message"), + HumanMessage(content="my user prompt"), + ] + response = "response from model" + + # act + token_usage_log.update_log(request_messages, response, "step 1") + token_usage_log.update_log(request_messages, response, "step 2") + csv_log = token_usage_log.format_log() + + # assert + csv_rows = list(csv.reader(StringIO(csv_log))) + + assert len(csv_rows) == 3 + + assert all(len(row) == 7 for row in csv_rows) + + +def test_usage_cost(): + # arrange + token_usage_log = TokenUsageLog("gpt-4") + request_messages = [ + SystemMessage(content="my system message"), + HumanMessage(content="my user prompt"), + ] + response = "response from model" + + # act + token_usage_log.update_log(request_messages, response, "step 1") + token_usage_log.update_log(request_messages, response, "step 2") + usage_cost = token_usage_log.usage_cost() + + # assert + assert usage_cost > 0 + + +def test_image_tokenizer(): + # Arrange + token_usage_log = Tokenizer("gpt-4") + image_path = Path(__file__).parent.parent / "test_data" / "mona_lisa.jpg" + # Check if the image file exists + if not os.path.isfile(image_path): + raise FileNotFoundError(f"Image file not found: {image_path}") + + # Act + with Image.open(image_path) as img: + # Convert RGBA to RGB + if img.mode == "RGBA": + img = img.convert("RGB") + + buffered = io.BytesIO() + img.save(buffered, format="JPEG") + image_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8") + + # Calculate the token cost of the base64 encoded image + image_token_cost = token_usage_log.num_tokens_for_base64_image(image_base64) + + # Assert + assert image_token_cost == 1105 + + +def test_list_type_message_with_image(): + # Arrange + token_usage_log = TokenUsageLog("gpt-4") + + request_messages = [ + SystemMessage(content="My system message"), + HumanMessage( + content=[ + {"type": "text", "text": "My user message"}, + { + "type": "image_url", + "image_url": { + "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII", + "detail": "low", + }, + }, + ] + ), + ] + response = "response from model" + + # Act + token_usage_log.update_log(request_messages, response, "list message with image") + + # Since this is the first (and only) log entry, the in-step total tokens should match our expected total + expected_total_tokens = 106 + + # Assert + assert ( + token_usage_log.log()[-1].in_step_total_tokens == expected_total_tokens + ), f"Expected {expected_total_tokens} tokens, got {token_usage_log.log()[-1].in_step_total_tokens}" diff --git a/tests/mock_ai.py b/tests/mock_ai.py new file mode 100644 index 0000000000000000000000000000000000000000..636cc70e8b77f36e9c2070e69af65ad4a3334f9a --- /dev/null +++ b/tests/mock_ai.py @@ -0,0 +1,14 @@ +from typing import Any, List, Optional + + +class MockAI: + def __init__(self, response: List): + self.responses = iter(response) + + def start(self, system: str, user: Any, *, step_name: str) -> List[str]: + return [next(self.responses)] + + def next( + self, messages: List[str], prompt: Optional[str] = None, *, step_name: str + ) -> List[str]: + return [next(self.responses)] diff --git a/tests/test_data/mona_lisa.jpg b/tests/test_data/mona_lisa.jpg new file mode 100644 index 0000000000000000000000000000000000000000..785796d30da5fb10a795906f22e585e59005a5ec --- /dev/null +++ b/tests/test_data/mona_lisa.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23567081b2742f36d924b4ad6a6aeabbd4d857d33aac056be224f7a026ecc82d +size 15634407 diff --git a/tests/test_install.py b/tests/test_install.py new file mode 100644 index 0000000000000000000000000000000000000000..5ee951ceee41a1777ae57c6178ed07e61c28a355 --- /dev/null +++ b/tests/test_install.py @@ -0,0 +1,107 @@ +""" +Tests for successful installation of the package. +""" + +import shutil +import subprocess +import sys +import venv + +from pathlib import Path + +import pytest + +# Define the directory for the virtual environment. +VENV_DIR = "./venv_test_installation" + + +@pytest.fixture(scope="module", autouse=True) +def venv_setup_teardown(): + """ + A pytest fixture that sets up and tears down a virtual environment for testing. + This fixture is automatically used for all tests in this module. + + The fixture: + - Creates a virtual environment. + - Installs Poetry in the virtual environment. + - Installs dependencies using Poetry. + - Cleans up by removing the virtual environment after tests are completed. + """ + try: + # Create a virtual environment with pip available. + venv.create(VENV_DIR, with_pip=True, clear=True) + + # Install Poetry in the virtual environment. + subprocess.run( + [f"{VENV_DIR}/bin/python", "-m", "pip", "install", "poetry"], check=True + ) + + # Install the package and its dependencies using Poetry. + subprocess.run([f"{VENV_DIR}/bin/poetry", "install"], cwd=".", check=True) + + # Provide the setup environment to the test functions. + yield + except Exception as e: + # Skip tests if the environment setup fails. + pytest.skip(f"Could not create venv or install dependencies: {str(e)}") + finally: + # Clean up by removing the virtual environment after tests. + shutil.rmtree(VENV_DIR) + + +def test_installation(): + """ + Test to ensure that the package can be installed using Poetry in the virtual environment. + """ + # Determine the correct Poetry executable path based on the operating system. + poetry_executable = ( + f"{VENV_DIR}/bin/poetry" + if sys.platform != "win32" + else f"{VENV_DIR}/Scripts/poetry.exe" + ) + + # Run Poetry install and capture its output. + result = subprocess.run([poetry_executable, "install"], capture_output=True) + + # Assert that the installation was successful. + assert ( + result.returncode == 0 + ), f"Install via poetry failed: {result.stderr.decode()}" + + +def test_cli_execution(): + """ + Test to verify that the command-line interface (CLI) of the package works as expected. + This test assumes that the 'gpt-engineer' command is available and operational after installation. + """ + # Run the 'gpt-engineer' command with the '--help' option and capture its output. + result = subprocess.run( + args=["gpt-engineer", "--help"], capture_output=True, text=True + ) + + # Assert that the CLI command executed successfully. + assert ( + result.returncode == 0 + ), f"gpt-engineer command failed with message: {result.stderr}" + + +@pytest.mark.requires_key +def test_installed_main_execution(tmp_path, monkeypatch): + # Ignore git installation check + monkeypatch.setattr("gpt_engineer.core.git.is_git_installed", lambda: False) + tmp_path = Path(tmp_path) + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text("make a program that prints the outcome of 4+4") + proc = subprocess.Popen( + ["gpte", str(p)], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + text=True, + cwd=tmp_path, + ) + + inputs = "Y\nn" + output, _ = proc.communicate(inputs) + + assert "8" in output diff --git a/tests/test_project_config.py b/tests/test_project_config.py new file mode 100644 index 0000000000000000000000000000000000000000..8aab8a2e7e8aff5e8a760d217441c5ba8a4a07a1 --- /dev/null +++ b/tests/test_project_config.py @@ -0,0 +1,144 @@ +import tempfile + +import pytest + +from gpt_engineer.core.project_config import ( + Config, + _GptEngineerAppConfig, + _OpenApiConfig, + example_config, + filter_none, +) + + +def test_config_load(): + # write example config to a file + with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: + f.write(example_config) + + # load the config from the file + config = Config.from_toml(f.name) + + assert config.paths.base == "./frontend" + assert config.paths.src == "./src" + assert config.run.build == "npm run build" + assert config.run.test == "npm run test" + assert config.run.lint == "quick-lint-js" + assert config.gptengineer_app + assert config.gptengineer_app.project_id == "..." + assert config.gptengineer_app.openapi + assert ( + config.gptengineer_app.openapi[0].url + == "https://api.gptengineer.app/openapi.json" + ) + assert ( + config.gptengineer_app.openapi[1].url + == "https://some-color-translating-api/openapi.json" + ) + assert config.to_dict() + assert config.to_toml(f.name, save=False) + + # check that write+read is idempotent + assert Config.from_toml(f.name) == config + + +def test_config_defaults(): + config = Config() + assert config.paths.base is None + with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: + config.to_toml(f.name) + + # check that read+write is idempotent + assert Config.from_toml(f.name) == config + + # check that empty (default) config is written as empty string + toml_str = config.to_toml(f.name, save=False) + assert toml_str == "" + + +def test_config_from_dict(): + d = {"gptengineer-app": {"project_id": "..."}} # minimal example + config = Config.from_dict(d) + assert config.gptengineer_app + assert config.gptengineer_app.project_id == "..." + config_dict = config.to_dict() + + # check that the config dict matches the input dict exactly (no keys/defaults added) + assert config_dict == d + + +def test_config_from_dict_with_openapi(): + # A good test because it has 3 levels of nesting + d = { + "gptengineer-app": { + "project_id": "...", + "openapi": [ + {"url": "https://api.gptengineer.app/openapi.json"}, + ], + } + } + config = Config.from_dict(d) + assert config.gptengineer_app + assert config.gptengineer_app.project_id == "..." + assert config.gptengineer_app.openapi + assert ( + config.gptengineer_app.openapi[0].url + == "https://api.gptengineer.app/openapi.json" + ) + + +def test_config_load_partial(): + # Loads a partial config, and checks that the rest is not set (i.e. None) + example_config = """ +[gptengineer-app] +project_id = "..." +""".strip() + with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: + f.write(example_config) + + config = Config.from_toml(f.name) + assert config.gptengineer_app + assert config.gptengineer_app.project_id == "..." + assert config.to_dict() + toml_str = config.to_toml(f.name, save=False) + assert toml_str == example_config + + # check that write+read is idempotent + assert Config.from_toml(f.name) == config + + +def test_config_update(): + example_config = """ +[gptengineer-app] +project_id = "..." +""".strip() + with tempfile.NamedTemporaryFile(mode="w", delete=False) as f: + f.write(example_config) + config = Config.from_toml(f.name) + config.gptengineer_app = _GptEngineerAppConfig( + project_id="...", + openapi=[_OpenApiConfig(url="https://api.gptengineer.app/openapi.json")], + ) + config.to_toml(f.name) + assert Config.from_toml(f.name) == config + + +@pytest.mark.parametrize( + "input_dict,expected", + [ + ({"a": 1, "b": None}, {"a": 1}), + ({"a": 1, "b": {"c": None, "d": 2}}, {"a": 1, "b": {"d": 2}}), + ({"a": 1, "b": {}}, {"a": 1}), + ({"a": 1, "b": {"c": None}}, {"a": 1}), + ( + {"a": {"b": {"c": None}}, "d": {"e": {"f": 2}}, "g": None}, + {"d": {"e": {"f": 2}}}, + ), + ( + {"a": 1, "b": {"c": None, "d": {"e": None, "f": {}}}, "g": {"h": 2}}, + {"a": 1, "g": {"h": 2}}, + ), + ], +) +def test_filter_none(input_dict, expected): + assert filter_none(input_dict) == expected diff --git a/tests/tools/example_snake_files.py b/tests/tools/example_snake_files.py new file mode 100644 index 0000000000000000000000000000000000000000..82be330fb7eb1763951ccc4ac0e9352ce5c43172 --- /dev/null +++ b/tests/tools/example_snake_files.py @@ -0,0 +1,1827 @@ +PYTHON = """ +import keyboard +import random +from dataclasses import dataclass + + +class View: + def __init__(self, game): + self.game = game + + def render(self): + # Print the game state + for y in range(10): + for x in range(10): + if Point(x, y) in self.game.snake: + print('S', end='') + elif Point(x, y) == self.game.food: + print('F', end='') + else: + print('.', end='') + print() + print() + + +@dataclass +class Point: + x: int + y: int + +class Game: + def __init__(self): + self.snake = [Point(5, 5)] + self.food = self.generate_food() + self.is_running = True + + def generate_food(self): + return Point(random.randint(0, 10), random.randint(0, 10)) + + def update(self): + # Move the snake + self.snake.move() + + # Check for collision with food + if self.snake.head == self.food: + self.snake.grow() + self.food = self.generate_food() + + # Check for collision with boundaries + if not (0 <= self.snake.head.x < 10 and 0 <= self.snake.head.y < 10): + self.is_running = False + +class Controller: + def __init__(self, game, view): + self.game = game + self.view = view + + def handle_input(self): + if keyboard.is_pressed('up'): + self.game.move('up') + elif keyboard.is_pressed('down'): + self.game.move('down') + elif keyboard.is_pressed('left'): + self.game.move('left') + elif keyboard.is_pressed('right'): + self.game.move('right') + +def main(): + game = Game() + view = View(game) + controller = Controller(game, view) + + while game.is_running: + controller.handle_input() + game.update() + view.render() + +if __name__ == "__main__": + main() +""" + + +HTML = """ + + + + Snake Game + + + +

Snake Game

+ +

Score: 0

+ + + +""" + +CSS = """ +body { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #000; + color: #fff; + font-family: Arial, sans-serif; +} + +#gameCanvas { + border: 1px solid #fff; +} + +h1, h2 { + text-align: center; +} + +""" + +JAVASCRIPT = """ +var canvas = document.getElementById('gameCanvas'); +var context = canvas.getContext('2d'); + +var box = 20; +var score = 0; +var food = null; + +var snake = []; +snake[0] = { x: 10 * box, y: 10 * box }; + +document.addEventListener('keydown', direction); +var d; + +function direction(event) { + if (event.keyCode == 37 && d != "RIGHT") { + d = "LEFT"; + } else if (event.keyCode == 38 && d != "DOWN") { + d = "UP"; + } else if (event.keyCode == 39 && d != "LEFT") { + d = "RIGHT"; + } else if (event.keyCode == 40 && d != "UP") { + d = "DOWN"; + } +} + +function draw() { + context.clearRect(0, 0, canvas.width, canvas.height); + for (var i = 0; i < snake.length; i++) { + context.fillStyle = (i == 0) ? "green" : "white"; + context.fillRect(snake[i].x, snake[i].y, box, box); + } + + if (food == null) { + food = { + x: Math.floor(Math.random() * 19 + 1) * box, + y: Math.floor(Math.random() * 19 + 1) * box + } + } + + context.fillStyle = "red"; + context.fillRect(food.x, food.y, box, box); + + var snakeX = snake[0].x; + var snakeY = snake[0].y; + + if (d == "LEFT") snakeX -= box; + if (d == "UP") snakeY -= box; + if (d == "RIGHT") snakeX += box; + if (d == "DOWN") snakeY += box; + + if (snakeX == food.x && snakeY == food.y) { + score++; + food = null; + } else { + snake.pop(); + } + + var newHead = { + x: snakeX, + y: snakeY + } + + if (snakeX < 0 || snakeY < 0 || snakeX > 19 * box || snakeY > 19 * box || collision(newHead, snake)) { + clearInterval(game); + } + + snake.unshift(newHead); + + document.getElementById('score').innerHTML = "Score: " + score; +} + +function collision(head, array) { + for (var i = 0; i < array.length; i++) { + if (head.x == array[i].x && head.y == array[i].y) { + return true; + } + } + return false; +} + +var game = setInterval(draw, 100); +""" + +JAVA = """ +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import javax.swing.ImageIcon; +import javax.swing.JPanel; +import javax.swing.Timer; + +public class SnakeGame extends JPanel implements ActionListener { + + private final int B_WIDTH = 300; + private final int B_HEIGHT = 300; + private final int DOT_SIZE = 10; + private final int ALL_DOTS = 900; + private final int RAND_POS = 29; + private final int DELAY = 140; + + private final int x[] = new int[ALL_DOTS]; + private final int y[] = new int[ALL_DOTS]; + + private int dots; + private int apple_x; + private int apple_y; + + private boolean leftDirection = false; + private boolean rightDirection = true; + private boolean upDirection = false; + private boolean downDirection = false; + private boolean inGame = true; + + private Timer timer; + private Image ball; + private Image apple; + private Image head; + + public SnakeGame() { + + initBoard(); + } + + private void initBoard() { + + addKeyListener(new TAdapter()); + setBackground(Color.black); + setFocusable(true); + + setPreferredSize(new Dimension(B_WIDTH, B_HEIGHT)); + loadImages(); + initGame(); + } + + private void loadImages() { + + ImageIcon iid = new ImageIcon("src/resources/dot.png"); + ball = iid.getImage(); + + ImageIcon iia = new ImageIcon("src/resources/apple.png"); + apple = iia.getImage(); + + ImageIcon iih = new ImageIcon("src/resources/head.png"); + head = iih.getImage(); + } + + private void initGame() { + + dots = 3; + + for (int z = 0; z < dots; z++) { + x[z] = 50 - z * 10; + y[z] = 50; + } + + locateApple(); + + timer = new Timer(DELAY, this); + timer.start(); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + doDrawing(g); + } + + private void doDrawing(Graphics g) { + + if (inGame) { + + g.drawImage(apple, apple_x, apple_y, this); + + for (int z = 0; z < dots; z++) { + if (z == 0) { + g.drawImage(head, x[z], y[z], this); + } else { + g.drawImage(ball, x[z], y[z], this); + } + } + + Toolkit.getDefaultToolkit().sync(); + + } else { + + gameOver(g); + } + } + + private void gameOver(Graphics g) { + + String msg = "Game Over"; + Font small = new Font("Helvetica", Font.BOLD, 14); + FontMetrics metr = getFontMetrics(small); + + g.setColor(Color.white); + g.setFont(small); + g.drawString(msg, (B_WIDTH - metr.stringWidth(msg)) / 2, B_HEIGHT / 2); + } + + private void checkApple() { + + if ((x[0] == apple_x) && (y[0] == apple_y)) { + + dots++; + locateApple(); + } + } + + private void move() { + + for (int z = dots; z > 0; z--) { + x[z] = x[(z - 1)]; + y[z] = y[(z - 1)]; + } + + if (leftDirection) { + x[0] -= DOT_SIZE; + } + + if (rightDirection) { + x[0] += DOT_SIZE; + } + + if (upDirection) { + y[0] -= DOT_SIZE; + } + + if (downDirection) { + y[0] += DOT_SIZE; + } + } + + private void checkCollision() { + + for (int z = dots; z > 0; z--) { + + if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { + inGame = false; + } + } + + if (y[0] >= B_HEIGHT) { + inGame = false; + } + + if (y[0] < 0) { + inGame = false; + } + + if (x[0] >= B_WIDTH) { + inGame = false; + } + + if (x[0] < 0) { + inGame = false; + } + + if (!inGame) { + timer.stop(); + } + } + + private void locateApple() { + + int r = (int) (Math.random() * RAND_POS); + apple_x = ((r * DOT_SIZE)); + + r = (int) (Math.random() * RAND_POS); + apple_y = ((r * DOT_SIZE)); + } + + @Override + public void actionPerformed(ActionEvent e) { + + if (inGame) { + + checkApple(); + checkCollision(); + move(); + } + + repaint(); + } + + private class TAdapter extends KeyAdapter { + + @Override + public void keyPressed(KeyEvent e) { + + int key = e.getKeyCode(); + + if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) { + leftDirection = true; + upDirection = false; + downDirection = false; + } + + if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) { + rightDirection = true; + upDirection = false; + downDirection = false; + } + + if ((key == KeyEvent.VK_UP) && (!downDirection)) { + upDirection = true; + rightDirection = false; + leftDirection = false; + } + + if ((key == KeyEvent.VK_DOWN) && (!upDirection)) { + downDirection = true; + rightDirection = false; + leftDirection = false; + } + } + } +} +""" + +C_SHARP = """ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace SnakeGame +{ + // Model + public class Game + { + public Snake Snake { get; set; } + public Point Food { get; set; } + public int Score { get; set; } + public bool Over { get; set; } + + public Game() + { + Snake = new Snake(); + Food = new Point(); + Score = 0; + Over = false; + } + } + + public class Snake + { + public Queue Body { get; set; } + public Direction Direction { get; set; } + + public Snake() + { + Body = new Queue(); + Direction = Direction.Right; + } + } + + public class Point + { + public int X { get; set; } + public int Y { get; set; } + } + + public enum Direction + { + Up, + Down, + Left, + Right + } + + // View + public class GameView + { + public void Draw(Game game) + { + Console.Clear(); + foreach (var point in game.Snake.Body) + { + Console.SetCursorPosition(point.X, point.Y); + Console.Write("O"); + } + Console.SetCursorPosition(game.Food.X, game.Food.Y); + Console.Write("F"); + Console.SetCursorPosition(0, 0); + Console.Write("Score: " + game.Score); + } + } + + // Controller + public class GameController + { + private Game game; + private GameView view; + + public GameController(Game game, GameView view) + { + this.game = game; + this.view = view; + } + + public void Start() + { + while (!game.Over) + { + Thread.Sleep(100); + MoveSnake(); + CheckCollision(); + view.Draw(game); + } + } + + private void MoveSnake() + { + var head = game.Snake.Body.Last(); + var newHead = new Point { X = head.X, Y = head.Y }; + switch (game.Snake.Direction) + { + case Direction.Up: + newHead.Y--; + break; + case Direction.Down: + newHead.Y++; + break; + case Direction.Left: + newHead.X--; + break; + case Direction.Right: + newHead.X++; + break; + } + game.Snake.Body.Enqueue(newHead); + if (newHead.X == game.Food.X && newHead.Y == game.Food.Y) + { + game.Score++; + game.Food = new Point { X = new Random().Next(1, 10), Y = new Random().Next(1, 10) }; + } + else + { + game.Snake.Body.Dequeue(); + } + } + + private void CheckCollision() + { + var head = game.Snake.Body.Last(); + if (head.X < 0 || head.Y < 0 || head.X >= 10 || head.Y >= 10) + { + game.Over = true; + } + if (game.Snake.Body.Take(game.Snake.Body.Count - 1).Any(p => p.X == head.X && p.Y == head.Y)) + { + game.Over = true; + } + } + } + + class Program + { + static void Main(string[] args) + { + var game = new Game(); + var view = new GameView(); + var controller = new GameController(game, view); + controller.Start(); + } + } +} +""" + +TYPESCRIPT = """ +// Importing necessary modules +import { Application, Graphics, Keyboard } from 'pixi.js'; + +// Defining the Model class +class Model { + // The snake's body is represented as an array of points + body: Array<{x: number, y: number}>; + + constructor() { + this.body = [{x: 0, y: 0}]; + } + + // Method to move the snake + move(direction: {x: number, y: number}) { + // Add a new head in the direction of movement + this.body.unshift({ + x: this.body[0].x + direction.x, + y: this.body[0].y + direction.y + }); + + // Remove the tail + this.body.pop(); + } +} + +// Defining the View class +class View { + // The view needs a reference to the model and the PIXI application + model: Model; + app: Application; + graphics: Graphics; + + constructor(model: Model, app: Application) { + this.model = model; + this.app = app; + this.graphics = new Graphics(); + this.app.stage.addChild(this.graphics); + } + + // Method to render the snake + render() { + // Clear the previous frame + this.graphics.clear(); + + // Draw each part of the snake's body + for (let part of this.model.body) { + this.graphics.beginFill(0xFFFFFF); + this.graphics.drawRect(part.x * 10, part.y * 10, 10, 10); + this.graphics.endFill(); + } + } +} + +// Defining the Controller class +class Controller { + // The controller needs a reference to the model and the view + model: Model; + view: View; + direction: {x: number, y: number}; + + constructor(model: Model, view: View) { + this.model = model; + this.view = view; + this.direction = {x: 1, y: 0}; + + // Set up keyboard controls + window.addEventListener('keydown', (e) => this.handleKeydown(e)); + } + + // Method to handle keyboard input + handleKeydown(event: KeyboardEvent) { + switch (event.key) { + case 'ArrowUp': + this.direction = {x: 0, y: -1}; + break; + case 'ArrowDown': + this.direction = {x: 0, y: 1}; + break; + case 'ArrowLeft': + this.direction = {x: -1, y: 0}; + break; + case 'ArrowRight': + this.direction = {x: 1, y: 0}; + break; + } + } + + // Method to update the game state + update() { + this.model.move(this.direction); + this.view.render(); + } +} + +// Create the PIXI application +let app = new Application({width: 800, height: 600}); + +// Create the MVC components +let model = new Model(); +let view = new View(model, app); +let controller = new Controller(model, view); + +// Start the game loop +setInterval(() => controller.update(), 100); +""" + +RUBY = """ +require 'io/console' + +# Model +class Game + attr_accessor :score, :snake, :food + + def initialize + @score = 0 + @snake = [[2, 2]] + @food = [6, 4] + end + + def move(direction) + head = @snake.first.dup + case direction + when 'up' + head[0] -= 1 + when 'down' + head[0] += 1 + when 'left' + head[1] -= 1 + when 'right' + head[1] += 1 + end + @snake.unshift(head) + + if @snake.first == @food + @score += 1 + @food = [rand(1..8), rand(1..8)] + else + @snake.pop + end + end + + def game_over? + head = @snake.first + @snake[1..-1].include?(head) || head[0] == 0 || head[1] == 0 || head[0] == 9 || head[1] == 9 + end +end + +# View +class View + def render(game) + system('clear') + puts "Score: #{game.score}" + (0..9).each do |i| + (0..9).each do |j| + if game.snake.include?([i, j]) + print 'S' + elsif game.food == [i, j] + print 'F' + else + print '.' + end + end + puts + end + end +end + +# Controller +class Controller + def initialize + @game = Game.new + @view = View.new + @direction = 'right' + end + + def play + loop do + @view.render(@game) + break if @game.game_over? + + input = IO.console.getch + case input + when 'w' + @direction = 'up' + when 's' + @direction = 'down' + when 'a' + @direction = 'left' + when 'd' + @direction = 'right' + end + @game.move(@direction) + end + puts "Game Over! Your score was #{@game.score}." + end +end + +Controller.new.play + +""" + +PHP = """ +body = array(array(2, 0), array(1, 0), array(0, 0)); + $this->direction = 'right'; + } + + public function move() { + $head = current($this->body); + switch($this->direction) { + case 'right': + $this->body[] = array($head[0] + 1, $head[1]); + break; + case 'left': + $this->body[] = array($head[0] - 1, $head[1]); + break; + case 'up': + $this->body[] = array($head[0], $head[1] - 1); + break; + case 'down': + $this->body[] = array($head[0], $head[1] + 1); + break; + } + array_shift($this->body); + } + + public function changeDirection($new_direction) { + $this->direction = $new_direction; + } +} + +// View +class GameView { + public function render($snake) { + $output = ''; + for ($y=0; $y<20; $y++) { + for ($x=0; $x<20; $x++) { + if (in_array(array($x, $y), $snake->body)) { + $output .= 'X'; + } else { + $output .= ' '; + } + } + $output .= "\n"; + } + echo $output; + } +} + +// Controller +class GameController { + public $snake; + public $view; + + public function __construct() { + $this->snake = new Snake(); + $this->view = new GameView(); + } + + public function start() { + while (true) { + $this->view->render($this->snake); + $this->snake->move(); + sleep(1); + } + } + + public function changeDirection($new_direction) { + $this->snake->changeDirection($new_direction); + } +} + +// Game loop +$game = new GameController(); +$game->start(); +?> +""" + +SWIFT = """ +import Foundation +import Cocoa + +// MARK: - Model +struct Point { + var x: Int + var y: Int +} + +class Snake { + var body: [Point] + var direction: Direction + + init(startPoint: Point) { + body = [startPoint] + direction = .right + } + + func move() { + let head = body.first! + var newHead = head + + switch direction { + case .up: + newHead.y += 1 + case .down: + newHead.y -= 1 + case .left: + newHead.x -= 1 + case .right: + newHead.x += 1 + } + + body.insert(newHead, at: 0) + body.removeLast() + } + + func grow() { + let tail = body.last! + body.append(tail) + } +} + +enum Direction { + case up + case down + case left + case right +} + +// MARK: - View +class GameView { + func draw(snake: Snake) { + for point in snake.body { + print("O", terminator: "") + } + print("\n") + } +} + +// MARK: - Controller +class GameController { + var snake: Snake + var view: GameView + + init() { + snake = Snake(startPoint: Point(x: 0, y: 0)) + view = GameView() + } + + func start() { + while true { + snake.move() + view.draw(snake: snake) + sleep(1) + } + } + + func handleKey(key: String) { + switch key { + case "w": + snake.direction = .up + case "s": + snake.direction = .down + case "a": + snake.direction = .left + case "d": + snake.direction = .right + default: + break + } + } +} + +// MARK: - Main +let gameController = GameController() +gameController.start() +""" + +GO = """ +package main + +import ( + "fmt" + "os" + "os/exec" + "time" + "math/rand" + "bufio" + "syscall" + "unsafe" +) + +// Model +type Point struct { + X int + Y int +} + +type Snake struct { + Body []Point + Dir string +} + +type Game struct { + Snake Snake + Food Point + Score int + Width int + Height int +} + +// View +func (game *Game) Render() { + clearScreen() + for y := 0; y < game.Height; y++ { + for x := 0; x < game.Width; x++ { + point := Point{X: x, Y: y} + switch { + case point == game.Food: + fmt.Print("F") + case game.Snake.Contains(point): + fmt.Print("S") + default: + fmt.Print(" ") + } + } + fmt.Println() + } + fmt.Println("Score:", game.Score) +} + +// Controller +func (game *Game) Update() { + head := game.Snake.Body[0] + switch game.Snake.Dir { + case "up": + head.Y-- + case "down": + head.Y++ + case "left": + head.X-- + case "right": + head.X++ + } + + if head.X < 0 || head.Y < 0 || head.X >= game.Width || head.Y >= game.Height { + game.Score = -1 + return + } + + if game.Snake.Contains(head) { + game.Score = -1 + return + } + + if head == game.Food { + game.Score++ + game.Food = Point{rand.Intn(game.Width), rand.Intn(game.Height)} + } else { + game.Snake.Body = game.Snake.Body[:len(game.Snake.Body)-1] + } + + game.Snake.Body = append([]Point{head}, game.Snake.Body...) +} + +func (snake *Snake) Contains(point Point) bool { + for _, bodyPoint := range snake.Body { + if bodyPoint == point { + return true + } + } + return false +} + +func clearScreen() { + cmd := exec.Command("clear") + cmd.Stdout = os.Stdout + cmd.Run() +} + +func main() { + game := &Game{ + Snake: Snake{ + Body: []Point{{10, 10}}, + Dir: "right", + }, + Food: Point{15, 15}, + Score: 0, + Width: 20, + Height: 20, + } + + go func() { + reader := bufio.NewReader(os.Stdin) + for { + char, _, err := reader.ReadRune() + if err != nil { + panic(err) + } + + switch char { + case 'w': + game.Snake.Dir = "up" + case 's': + game.Snake.Dir = "down" + case 'a': + game.Snake.Dir = "left" + case 'd': + game.Snake.Dir = "right" + } + } + }() + + for game.Score >= 0 { + game.Render() + time.Sleep(time.Second / 5) + game.Update() + } +} +""" + +KOTLIN = """ +import java.awt.Color +import java.awt.Dimension +import java.awt.Font +import java.awt.FontMetrics +import java.awt.Graphics +import java.awt.Image +import java.awt.Toolkit +import java.awt.event.ActionEvent +import java.awt.event.ActionListener +import java.awt.event.KeyAdapter +import java.awt.event.KeyEvent +import javax.swing.ImageIcon +import javax.swing.JPanel +import javax.swing.Timer + +class Board : JPanel(), ActionListener { + + private val B_WIDTH = 300 + private val B_HEIGHT = 300 + private val DOT_SIZE = 10 + private val ALL_DOTS = 900 + private val RAND_POS = 29 + private val DELAY = 140 + + private val x = IntArray(ALL_DOTS) + private val y = IntArray(ALL_DOTS) + + private var dots: Int = 0 + private var apple_x: Int = 0 + private var apple_y: Int = 0 + + private var leftDirection = false + private var rightDirection = true + private var upDirection = false + private var downDirection = false + private var inGame = true + + private lateinit var timer: Timer + private lateinit var apple: Image + private lateinit var dot: Image + private lateinit var head: Image + + init { + initBoard() + } + + private fun initBoard() { + + addKeyListener(TAdapter()) + background = Color.black + isFocusable = true + + preferredSize = Dimension(B_WIDTH, B_HEIGHT) + loadImages() + initGame() + } + + private fun loadImages() { + + val iid = ImageIcon("src/resources/apple.png") + apple = iid.image + + val iid2 = ImageIcon("src/resources/dot.png") + dot = iid2.image + + val iid3 = ImageIcon("src/resources/head.png") + head = iid3.image + } + + private fun initGame() { + + dots = 3 + + for (z in 0 until dots) { + x[z] = 50 - z * 10 + y[z] = 50 + } + + locateApple() + + timer = Timer(DELAY, this) + timer.start() + } + + override fun paintComponent(g: Graphics) { + super.paintComponent(g) + + doDrawing(g) + } + + private fun doDrawing(g: Graphics) { + + if (inGame) { + + g.drawImage(apple, apple_x, apple_y, this) + + for (z in 0 until dots) { + if (z == 0) { + g.drawImage(head, x[z], y[z], this) + } else { + g.drawImage(dot, x[z], y[z], this) + } + } + + Toolkit.getDefaultToolkit().sync() + + } else { + + gameOver(g) + } + } + + private fun gameOver(g: Graphics) { + + val msg = "Game Over" + val font = Font("Helvetica", Font.BOLD, 14) + val metrics: FontMetrics = this.getFontMetrics(font) + + g.color = Color.white + g.font = font + g.drawString(msg, (B_WIDTH - metrics.stringWidth(msg)) / 2, B_HEIGHT / 2) + } + + private fun checkApple() { + + if (x[0] == apple_x && y[0] == apple_y) { + + dots++ + locateApple() + } + } + + private fun move() { + + for (z in dots downTo 1) { + x[z] = x[z - 1] + y[z] = y[z - 1] + } + + if (leftDirection) { + x[0] -= DOT_SIZE + } + + if (rightDirection) { + x[0] += DOT_SIZE + } + + if (upDirection) { + y[0] -= DOT_SIZE + } + + if (downDirection) { + y[0] += DOT_SIZE + } + } + + private fun checkCollision() { + + for (z in dots downTo 1) { + if (z > 4 && x[0] == x[z] && y[0] == y[z]) { + inGame = false + } + } + + if (y[0] >= B_HEIGHT) { + inGame = false + } + + if (y[0] < 0) { + inGame = false + } + + if (x[0] >= B_WIDTH) { + inGame = false + } + + if (x[0] < 0) { + inGame = false + } + + if (!inGame) { + timer.stop() + } + } + + private fun locateApple() { + + val r = (Math.random() * RAND_POS).toInt() + apple_x = r * DOT_SIZE + + r = (Math.random() * RAND_POS).toInt() + apple_y = r * DOT_SIZE + } + + override fun actionPerformed(e: ActionEvent) { + + if (inGame) { + + checkApple() + checkCollision() + move() + } + + repaint() + } + + private inner class TAdapter : KeyAdapter() { + + override fun keyPressed(e: KeyEvent) { + + val key = e.keyCode + + if (key == KeyEvent.VK_LEFT && !rightDirection) { + leftDirection = true + upDirection = false + downDirection = false + } + + if (key == KeyEvent.VK_RIGHT && !leftDirection) { + rightDirection = true + upDirection = false + downDirection = false + } + + if (key == KeyEvent.VK_UP && !downDirection) { + upDirection = true + rightDirection = false + leftDirection = false + } + + if (key == KeyEvent.VK_DOWN && !upDirection) { + downDirection = true + rightDirection = false + leftDirection = false + } + } + } +} +""" +RUST = """ +extern crate termion; + +use std::io; +use std::io::stdout; +use std::io::Write; +use std::thread; +use std::time::Duration; +use termion::raw::IntoRawMode; +use termion::input::TermRead; +use termion::event::Key; + +// Define the size of the game board +const BOARD_SIZE: usize = 10; + +// Define the game state +struct GameState { + snake: Snake, + food: Food, +} + +// Define the snake +struct Snake { + body: Vec<(usize, usize)>, + direction: Direction, +} + +// Define the food +struct Food { + position: (usize, usize), +} + +// Define the possible directions the snake can move +enum Direction { + Up, + Down, + Left, + Right, +} + +// Implement the game state +impl GameState { + fn new() -> GameState { + GameState { + snake: Snake::new(), + food: Food::new(), + } + } + + // Update the game state + fn update(&mut self) { + self.snake.move(); + if self.snake.eats(&self.food) { + self.food = Food::new(); + } + } +} + +// Implement the snake +impl Snake { + fn new() -> Snake { + Snake { + body: vec![(BOARD_SIZE / 2, BOARD_SIZE / 2)], + direction: Direction::Right, + } + } + + // Move the snake in the current direction + fn move(&mut self) { + let (head_x, head_y) = self.body[0]; + match self.direction { + Direction::Up => self.body.insert(0, (head_x, head_y - 1)), + Direction::Down => self.body.insert(0, (head_x, head_y + 1)), + Direction::Left => self.body.insert(0, (head_x - 1, head_y)), + Direction::Right => self.body.insert(0, (head_x + 1, head_y)), + } + self.body.pop(); + } + + // Check if the snake eats the food + fn eats(&self, food: &Food) -> bool { + self.body[0] == food.position + } +} + +// Implement the food +impl Food { + fn new() -> Food { + Food { + position: (rand::random::() % BOARD_SIZE, rand::random::() % BOARD_SIZE), + } + } +} + +// Implement the view +fn render(game_state: &GameState) { + for y in 0..BOARD_SIZE { + for x in 0..BOARD_SIZE { + if game_state.snake.body.contains(&(x, y)) { + print!("S"); + } else if game_state.food.position == (x, y) { + print!("F"); + } else { + print!(" "); + } + } + println!(); + } +} + +// Implement the controller +fn controller(game_state: &mut GameState) { + let stdin = io::stdin(); + for c in stdin.keys() { + match c.unwrap() { + Key::Char('q') => break, + Key::Up => game_state.snake.direction = Direction::Up, + Key::Down => game_state.snake.direction = Direction::Down, + Key::Left => game_state.snake.direction = Direction::Left, + Key::Right => game_state.snake.direction = Direction::Right, + _ => {} + } + } +} + +fn main() { + let mut game_state = GameState::new(); + let mut stdout = stdout().into_raw_mode().unwrap(); + + loop { + write!(stdout, "{}", termion::clear::All).unwrap(); + render(&game_state); + stdout.flush().unwrap(); + game_state.update(); + thread::sleep(Duration::from_millis(1000)); + } +} +""" + +C_PLUS_PLUS = """ +#include +#include +#include +using namespace std; + +bool gameOver; +const int width = 20; +const int height = 20; +int x, y, fruitX, fruitY, score; +int tailX[100], tailY[100]; +int nTail; +enum eDirecton { STOP = 0, LEFT, RIGHT, UP, DOWN}; +eDirecton dir; + +void Setup() +{ + gameOver = false; + dir = STOP; + x = width / 2; + y = height / 2; + fruitX = rand() % width; + fruitY = rand() % height; + score = 0; +} +void Draw() +{ + system("cls"); + for (int i = 0; i < width+2; i++) + cout << "#"; + cout << endl; + + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + if (j == 0) + cout << "#"; + if (i == y && j == x) + cout << "*"; + else if (i == fruitY && j == fruitX) + cout << "%"; + else + { + + bool print = false; + for (int k = 0; k < nTail ; k++) + { + if (tailX[k] == j && tailY[k] == i) + { + cout << "*"; print = true; + } + } + if (!print) + cout << " "; + + } + + if (j == width - 1) + cout << "#"; + } + cout << endl; + } + + for (int i = 0; i < width+2; i++) + cout << "#"; + cout << endl; + cout << "Score:" << score << endl; + +} + +void Input() +{ + if (_kbhit()) + { + switch (_getch()) + { + case 'a': + dir = LEFT; + break; + case 'd': + dir = RIGHT; + break; + case 'w': + dir = UP; + break; + case 's': + dir = DOWN; + break; + case 'x': + gameOver = true; + break; + } + } +} + +void algorithm() +{ + int prevX = tailX[0]; + int prevY = tailY[0]; + int prev2X, prev2Y; + tailX[0] = x; + tailY[0] = y; + + for(int i = 1; i < nTail ; i++) + { + prev2X = tailX[i]; + prev2Y = tailY[i]; + tailX[i] = prevX; + tailY[i] = prevY; + prevX = prev2X; + prevY = prev2Y; + } + + switch (dir) + { + case LEFT: + x--; + break; + case RIGHT: + x++; + break; + case UP: + y--; + break; + case DOWN: + y++; + break; + default: + break; + } + if (x >= width) + x = 0; else if (x < 0) x = width - 1; + if (y >= height) + y = 0; else if (y < 0) y = height - 1; + + for (int i = 0; i < nTail ; i++) + if (tailX[i] == x && tailY[i] == y) + gameOver = true; + + if (x == fruitX && y == fruitY) + { + score += 10; + fruitX = rand() % width; + fruitY = rand() % height; + nTail++; + } +} + +int main() +{ + Setup(); + while (!gameOver) + { + Draw(); + Input(); + algorithm(); + } + return 0; +} +""" + +C = """ +#include +#include +#include +#include + +#define WIDTH 20 +#define HEIGHT 20 +#define MAX_SNAKE_SIZE WIDTH *HEIGHT + +// Model +typedef struct { + int x, y; +} Point; + +typedef struct { + Point body[MAX_SNAKE_SIZE]; + int size; + Point direction; +} Snake; + +typedef struct { + Point position; + int isEaten; +} Fruit; + +// View +void gotoxy(int x, int y) { + COORD coord; + coord.X = x; + coord.Y = y; + SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); +} + +void drawBoard() { + int i; + for (i = 0; i < WIDTH + 2; i++) { + gotoxy(i, 0); + printf("#"); + gotoxy(i, HEIGHT + 1); + printf("#"); + } + for (i = 0; i < HEIGHT + 2; i++) { + gotoxy(0, i); + printf("#"); + gotoxy(WIDTH + 1, i); + printf("#"); + } +} + +void drawSnake(Snake* snake) { + int i; + for (i = 0; i < snake->size; i++) { + gotoxy(snake->body[i].x, snake->body[i].y); + printf("*"); + } +} + +void drawFruit(Fruit* fruit) { + gotoxy(fruit->position.x, fruit->position.y); + printf("@"); +} + +// Controller +void initGame(Snake* snake, Fruit* fruit) { + snake->size = 1; + snake->body[0].x = WIDTH / 2; + snake->body[0].y = HEIGHT / 2; + snake->direction.x = 0; + snake->direction.y = 1; + + fruit->position.x = rand() % WIDTH; + fruit->position.y = rand() % HEIGHT; + fruit->isEaten = 0; +} + +void updateSnake(Snake* snake) { + memmove(&snake->body[1], &snake->body[0], sizeof(Point) * (snake->size - 1)); + snake->body[0].x += snake->direction.x; + snake->body[0].y += snake->direction.y; +} + +void updateFruit(Snake* snake, Fruit* fruit) { + if (snake->body[0].x == fruit->position.x && snake->body[0].y == fruit->position.y) { + fruit->isEaten = 1; + snake->size++; + } + if (fruit->isEaten) { + fruit->position.x = rand() % WIDTH; + fruit->position.y = rand() % HEIGHT; + fruit->isEaten = 0; + } +} + +void updateDirection(Snake* snake, char key) { + switch (key) { + case 'w': + snake->direction.x = 0; + snake->direction.y = -1; + break; + case 's': + snake->direction.x = 0; + snake->direction.y = 1; + break; + case 'a': + snake->direction.x = -1; + snake->direction.y = 0; + break; + case 'd': + snake->direction.x = 1; + snake->direction.y = 0; + break; + } +} + +int isGameOver(Snake* snake) { + if (snake->body[0].x <= 0 || snake->body[0].x >= WIDTH || snake->body[0].y <= 0 || snake->body[0].y >= HEIGHT) + return 1; + int i; + for (i = 1; i < snake->size; i++) { + if (snake->body[0].x == snake->body[i].x && snake->body[0].y == snake->body[i].y) + return 1; + } + return 0; +} + +int main() { + Snake snake; + Fruit fruit; + char key; + + initGame(&snake, &fruit); + + while (1) { + drawBoard(); + drawSnake(&snake); + drawFruit(&fruit); + + if (_kbhit()) { + key = _getch(); + updateDirection(&snake, key); + } + + updateSnake(&snake); + updateFruit(&snake, &fruit); + + if (isGameOver(&snake)) { + break; + } + + Sleep(100); + system("cls"); + } + + printf("Game Over!\n"); + + return 0; +} +""" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000000000000000000000000000000000000..088b114a25144cd3c599dd0a895fb2c8c7ed8f98 --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +[tox] +envlist = py310, py311, py312 + +[testenv] +basepython = + py310: python3.10 + py311: python3.11 + py312: python3.12 +deps = + poetry +commands = + poetry install --no-root + poetry run pytest --cov=gpt_engineer --cov-report=xml -k 'not installed_main_execution'