diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..bc46e95e8ac09c4cddee288eaea22a06347181a7 --- /dev/null +++ b/.env @@ -0,0 +1,22 @@ +# by default the Dockerfile specifies these versions: 3.5;5.0;6.0;6.1;7.0;7.5;8.0;8.6+PTX +# however for me to work i had to specify the exact version for my card ( 2060 ) it was 7.5 +# https://developer.nvidia.com/cuda-gpus you can find the version for your card here +TORCH_CUDA_ARCH_LIST=7.5 +# your command-line flags go here: +CLI_ARGS=--listen +# the port the webui binds to on the host +HOST_PORT=7860 +# the port the webui binds to inside the container +CONTAINER_PORT=7860 +# the port the api binds to on the host +HOST_API_PORT=5000 +# the port the api binds to inside the container +CONTAINER_API_PORT=5000 +# Comma separated extensions to build +BUILD_EXTENSIONS="" +# Set APP_RUNTIME_GID to an appropriate host system group to enable access to mounted volumes +# You can find your current host user group id with the command `id -g` +APP_RUNTIME_GID=6972 +# override default app build permissions (handy for deploying to cloud) +#APP_GID=6972 +#APP_UID=6972 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..57b7f6982f7d7b7b9677c795488b11864d69d19e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: oobabooga diff --git a/.github/ISSUE_TEMPLATE/bug_report_template.yml b/.github/ISSUE_TEMPLATE/bug_report_template.yml new file mode 100644 index 0000000000000000000000000000000000000000..bd30a0c9c17dd514bf364846fe7914b6d10a4584 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report_template.yml @@ -0,0 +1,53 @@ +name: "Bug report" +description: Report a bug +labels: [ "bug" ] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: bug-description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + placeholder: Bug description + validations: + required: true + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the issue you encountered. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction + description: Please provide the steps necessary to reproduce your issue. + placeholder: Reproduction + validations: + required: true + - type: textarea + id: screenshot + attributes: + label: Screenshot + description: "If possible, please include screenshot(s) so that we can understand what the issue is." + - type: textarea + id: logs + attributes: + label: Logs + description: "Please include the full stacktrace of the errors you get in the command-line (if any)." + render: shell + validations: + required: true + - type: textarea + id: system-info + attributes: + label: System Info + description: "Please share your system info with us: operating system, GPU brand, and GPU model. If you are using a Google Colab notebook, mention that instead." + render: shell + placeholder: + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000000000000000000000000000000..b94974f865491731a1251e3e9736e01cbe81b06f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: Feature request +about: Suggest an improvement or new feature for the web UI +title: '' +labels: 'enhancement' +assignees: '' + +--- + +**Description** + +A clear and concise description of what you want to be implemented. + +**Additional Context** + +If applicable, please provide any extra information, external links, or screenshots that could be useful. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000000000000000000000000000000..91abb11fdf507883caeeb2d2958e1c65fb6cbdc1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000000000000000000000000000000..51e26b13a38889a38cac5392b6e22190fd75a8b7 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ +## Checklist: + +- [ ] I have read the [Contributing guidelines](https://github.com/oobabooga/text-generation-webui/wiki/Contributing-guidelines). diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 0000000000000000000000000000000000000000..59287718a4685059577fff5810e52514a577e59b --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,28 @@ +name: Weekly Snapshot Release +on: + schedule: + - cron: '15 20 * * 0' + workflow_dispatch: {} + +jobs: + create_release: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set snapshot tag + id: set_snapshot_tag + run: echo ::set-output name=tag::snapshot-$(date +'%Y-%m-%d') + + - name: Create release + id: create_release + uses: softprops/action-gh-release@v1 + with: + generate_release_notes: true + tag_name: ${{ steps.set_snapshot_tag.outputs.tag }} + name: ${{ steps.set_snapshot_tag.outputs.tag }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000000000000000000000000000000000..2de6d955a3c739d03716b6c6f884ab9bf1c47b59 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,22 @@ +name: Close inactive issues +on: + schedule: + - cron: "10 23 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + stale-issue-message: "" + close-issue-message: "This issue has been closed due to inactivity for 6 weeks. If you believe it is still relevant, please leave a comment below. You can tag a developer in your comment." + days-before-issue-stale: 42 + days-before-issue-close: 0 + stale-issue-label: "stale" + days-before-pr-stale: -1 + days-before-pr-close: -1 + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ca307c4a954a7124c6809ac3a32757abc31b2e9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +/cache +/characters +/css +/extensions +/grammars +/installer_files +/logs +/loras +/models +/presets +/prompts +/repositories +/softprompts +/torch-dumps +/training/datasets + +/CMD_FLAGS.txt +/img_bot* +/img_me* +/models/config-user.yaml +/notification.mp3 +/settings*.json +/settings*.yaml + +.chroma +.DS_Store +.eslintrc.js +.idea +.venv +venv +.envrc +.direnv +.vs +.vscode +*.bak +*.ipynb +*.log +*pycache* +cert.pem +key.pem +package.json +package-lock.json +Thumbs.db +wandb + +# ignore user docker config and top level links to docker files +/docker-compose.yaml +/docker-compose.yml +/Dockerfile +.env diff --git a/CMD_FLAGS.txt b/CMD_FLAGS.txt new file mode 100644 index 0000000000000000000000000000000000000000..c2d63d9e8fbb1e043a9f6742cead71701a99f9a2 --- /dev/null +++ b/CMD_FLAGS.txt @@ -0,0 +1,3 @@ +# Only used by the one-click installer. +# Example: +# --listen --api diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..0ad25db4bd1d86c452db3f9602ccdbe172438f52 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/characters/Assistant.yaml b/characters/Assistant.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a6141f401e2c70a25708b12c999192840d575340 --- /dev/null +++ b/characters/Assistant.yaml @@ -0,0 +1,4 @@ +name: AI +greeting: How can I help you today? +context: | + The following is a conversation with an AI Large Language Model. The AI has been trained to answer questions, provide recommendations, and help with decision making. The AI follows user requests. The AI thinks outside the box. diff --git a/characters/Example.png b/characters/Example.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c4e513c4eaa05db1ebb2164956ea0b85d74a75 Binary files /dev/null and b/characters/Example.png differ diff --git a/characters/Example.yaml b/characters/Example.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c1a3299e7c0b977feb7345a0943c25ed8c45a9c3 --- /dev/null +++ b/characters/Example.yaml @@ -0,0 +1,17 @@ +name: Chiharu Yamada +greeting: |- + *Chiharu strides into the room with a smile, her eyes lighting up when she sees you. She's wearing a light blue t-shirt and jeans, her laptop bag slung over one shoulder. She takes a seat next to you, her enthusiasm palpable in the air* + Hey! I'm so excited to finally meet you. I've heard so many great things about you and I'm eager to pick your brain about computers. I'm sure you have a wealth of knowledge that I can learn from. *She grins, eyes twinkling with excitement* Let's get started! +context: |- + Chiharu Yamada's Persona: Chiharu Yamada is a young, computer engineer-nerd with a knack for problem solving and a passion for technology. + + {{user}}: So how did you get into computer engineering? + {{char}}: I've always loved tinkering with technology since I was a kid. + {{user}}: That's really impressive! + {{char}}: *She chuckles bashfully* Thanks! + {{user}}: So what do you do when you're not working on computers? + {{char}}: I love exploring, going out with friends, watching movies, and playing video games. + {{user}}: What's your favorite type of computer hardware to work with? + {{char}}: Motherboards, they're like puzzles and the backbone of any system. + {{user}}: That sounds great! + {{char}}: Yeah, it's really fun. I'm lucky to be able to do this as a job. diff --git a/characters/Nex.png b/characters/Nex.png new file mode 100644 index 0000000000000000000000000000000000000000..80d8f33bd79e7de2caaec5ab3c0bf4dda019d6a1 Binary files /dev/null and b/characters/Nex.png differ diff --git a/characters/Nex.yaml b/characters/Nex.yaml new file mode 100644 index 0000000000000000000000000000000000000000..12e2b7935b09da77e44d4fa69663f8be5afb6007 --- /dev/null +++ b/characters/Nex.yaml @@ -0,0 +1,3 @@ +name: Nex +greeting: Hey, I'm Nex. It's nice to meet you. Do you have a problem you can't solve? +context: Meet Nex, a knowledgable AI trained on 7B parameters. Ask Nex anything, it could surprise you. diff --git a/cmd_linux.sh b/cmd_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..1685050aff7b270ae42e295c0b947d576e2653a3 --- /dev/null +++ b/cmd_linux.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +# config +CONDA_ROOT_PREFIX="$(pwd)/installer_files/conda" +INSTALL_ENV_DIR="$(pwd)/installer_files/env" + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# activate env +bash --init-file <(echo "source \"$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh\" && conda activate \"$INSTALL_ENV_DIR\"") diff --git a/cmd_macos.sh b/cmd_macos.sh new file mode 100644 index 0000000000000000000000000000000000000000..1b052e5c34bd43b7e898858d7993dd5f6a7a6f08 --- /dev/null +++ b/cmd_macos.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +# config +CONDA_ROOT_PREFIX="$(pwd)/installer_files/conda" +INSTALL_ENV_DIR="$(pwd)/installer_files/env" + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# activate env +source $CONDA_ROOT_PREFIX/etc/profile.d/conda.sh +conda activate $INSTALL_ENV_DIR +exec bash --norc diff --git a/cmd_windows.bat b/cmd_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..531a326158e9e169657051b0e76bdfad17c4b238 --- /dev/null +++ b/cmd_windows.bat @@ -0,0 +1,34 @@ +@echo off + +cd /D "%~dp0" + +set PATH=%PATH%;%SystemRoot%\system32 + +echo "%CD%"| findstr /C:" " >nul && echo This script relies on Miniconda which can not be silently installed under a path with spaces. && goto end + +@rem fix failed install when installing to a separate drive +set TMP=%cd%\installer_files +set TEMP=%cd%\installer_files + +@rem deactivate existing conda envs as needed to avoid conflicts +(call conda deactivate && call conda deactivate && call conda deactivate) 2>nul + +@rem config +set CONDA_ROOT_PREFIX=%cd%\installer_files\conda +set INSTALL_ENV_DIR=%cd%\installer_files\env + +@rem environment isolation +set PYTHONNOUSERSITE=1 +set PYTHONPATH= +set PYTHONHOME= +set "CUDA_PATH=%INSTALL_ENV_DIR%" +set "CUDA_HOME=%CUDA_PATH%" + +@rem activate installer env +call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end ) + +@rem enter commands +cmd /k "%*" + +:end +pause diff --git a/cmd_wsl.bat b/cmd_wsl.bat new file mode 100644 index 0000000000000000000000000000000000000000..f9f4348a4672d1981b2648c55b861cb0fb6f5598 --- /dev/null +++ b/cmd_wsl.bat @@ -0,0 +1,11 @@ +@echo off + +cd /D "%~dp0" + +set PATH=%PATH%;%SystemRoot%\system32 + +@rem sed -i 's/\x0D$//' ./wsl.sh converts newlines to unix format in the wsl script +call wsl -e bash -lic "sed -i 's/\x0D$//' ./wsl.sh; source ./wsl.sh cmd" + +:end +pause diff --git a/convert-to-safetensors.py b/convert-to-safetensors.py new file mode 100644 index 0000000000000000000000000000000000000000..3b721e7cd4d15cf7e5e03caaee57ef83a41553bc --- /dev/null +++ b/convert-to-safetensors.py @@ -0,0 +1,38 @@ +''' + +Converts a transformers model to safetensors format and shards it. + +This makes it faster to load (because of safetensors) and lowers its RAM usage +while loading (because of sharding). + +Based on the original script by 81300: + +https://gist.github.com/81300/fe5b08bff1cba45296a829b9d6b0f303 + +''' + +import argparse +from pathlib import Path + +import torch +from transformers import AutoModelForCausalLM, AutoTokenizer + +parser = argparse.ArgumentParser(formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=54)) +parser.add_argument('MODEL', type=str, default=None, nargs='?', help="Path to the input model.") +parser.add_argument('--output', type=str, default=None, help='Path to the output folder (default: models/{model_name}_safetensors).') +parser.add_argument("--max-shard-size", type=str, default="2GB", help="Maximum size of a shard in GB or MB (default: %(default)s).") +parser.add_argument('--bf16', action='store_true', help='Load the model with bfloat16 precision. Requires NVIDIA Ampere GPU.') +args = parser.parse_args() + +if __name__ == '__main__': + path = Path(args.MODEL) + model_name = path.name + + print(f"Loading {model_name}...") + model = AutoModelForCausalLM.from_pretrained(path, low_cpu_mem_usage=True, torch_dtype=torch.bfloat16 if args.bf16 else torch.float16) + tokenizer = AutoTokenizer.from_pretrained(path) + + out_folder = args.output or Path(f"models/{model_name}_safetensors") + print(f"Saving the converted model to {out_folder} with a maximum shard size of {args.max_shard_size}...") + model.save_pretrained(out_folder, max_shard_size=args.max_shard_size, safe_serialization=True) + tokenizer.save_pretrained(out_folder) diff --git a/extensions/Training_PRO/README.md b/extensions/Training_PRO/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3eda332162ce55b3c4e54ce2262f5ae47c1a932e --- /dev/null +++ b/extensions/Training_PRO/README.md @@ -0,0 +1,92 @@ +# Training_PRO + +This is an expanded and reworked Training tab +Maintained by FP + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Q5Q5MOB4M) + +Repo home: + +https://github.com/FartyPants/Training_PRO + +In general the repo above is ahead of the extension included in text WebUi. + +## News + +- NEFtune: add noise to help with generalization +- Loss Graph in interface. +- Supports Mistral training +- some roundabout around pytorch and transformers version desync + +![image](https://github.com/FartyPants/Training_PRO/assets/23346289/e389ec69-d7ad-4922-9ad9-865625997479) + +## Features/Changes + +- Chunking: precise raw text slicer (PRTS) uses sentence slicing and making sure things are clean on all ends +- overlap chunking - this special overlapping will make additional overlap block based on logical rules (aka no overlap block on hard cut) +- custom scheduler (follow the code to make your own) In LR Scheduler select FP_low_epoch_annealing - this scheduler will keep the LR constant for first epoch then use cosine for the rest - this part would be best to spawn into a new py file +- saves graph png file at the end with learning rate and loss per epoch +- adding EOS to each block or to hard cut only +- automatically lowers gradient accumulation if you go overboard and set gradient accumulation that will be higher than actual data - transformers would then throw error (or they used to, not sure if still true) but in any way, it will fix bad data +- turn BOS on and OFF +- target selector +- DEMENTOR LEARNING (experimental) Deep Memorization Enforcement Through Overlapping and Repetition. This is an experiment for long-text learning using low epochs (basically use 1 epoch with constant LR or 2 epochs with FP_low_epoch_annealing LR scheduler) +- Getting rid of micro batch size/batch size confusion. Now there is True Batch Size and Gradient accumulation slider, consisten with all the other training out there +- Ability to save Checkpoint during training with a button +- Ability to change Stop Loss during training +- different modes of checkpoint auto saving +- Function to Check Dataset and suggest parameters such as warmup and checkpoint save frequency before training +- Graph Training Loss in interface +- more custom schedulers + +### Notes: + +This uses it's own chunking code for raw text based on sentence splitting. This will avoid weird cuts in the chunks and each chunk should now start with sentence and end on some sentence. It works hand in hand with Hard Cut. A propper use is to structure your text into logical blocks (ideas) separated by three \n then use three \n in hard cut. This way each chunk will contain only one flow of ideas and not derail in the thoughts. And Overlapping code will create overlapped blocks on sentence basis too, but not cross hard cut, thus not cross different ideas either. Does it make any sense? No? Hmmmm... + +### Custom schedulers + +A bunch of custom (combination) schedulers are added to the LR schedule. These are based on my own experiments + +**FP_low_epoch_annealing** + +Uses constant LR (with warmup) for 1 epoch only. The rest of the epoch(s) is cosine annealing. So 10 epochs - 1 will be constant 9 will be nose dive down. However a typical usage would be 2 epochs (hence low epoch in name). 1st is constant, the second is annealing. Simple. I use it 90% of time. + +**FP_half_time_annealing** + +Like the low epoch, but now the total number of steps is divided by 2. First half is constant, second half is annealing. So 10 epochs - 5 will be constant, 5 will be cosine nose down. + +**FP_raise_fall_creative** + +This is a sine raise till half of the total steps then cosine fall the rest. (Or you may think of the curve as sine in its entirety. The most learning is done in the hump, in the middle. The warmup entry has no effect, since sine is automatically warm up. +The idea is to start very mildly as not to overfit with the first blocks of dataset. It seems to broaden the scope of the model making it less strict for tight dataset. + +### Targets + +Normal LORA is q, v and that's what you should use. You can use (q k v o) or (q k v) and it will give you a lot more trainable parameters. The benefit is that you can keep rank lower and still attain the same coherency as q v with high rank. Guanaco has been trained with QLORA and q k v o for example and they swear by it. + +### DEMENTOR LEARNING (experimental) Deep Memorization Enforcement Through Overlapping and Repetition + +This is and experimental chunking to train long-form text in low number of epochs (basically 1) with sliding repetition. The depth of learning directly depends on the cutoff_length. Increasing cutoff length will also increase number of blocks created from long-form text (which is contrary to normal training). It is based on my own wild experiments. + +### Getting rid of batch size and micro batch size + +Keeping consistency with everyone else. + +Listen, There is only ONE batch size - the True batch size (called previously micro-batch size in WebUI) - this is how many blocks are processed at once (during a single step). It eats GPU, but it really helps with the quality training (in fact the ideal batch size would be the same as number of blocks - which is unrealistic) - so the idea is to cram as much True Batch Size before your GPU blows with OOM. On 24GB this is about 10 for 13b (loaded with 4-bit) + +So no micro batch size - it is now called True Batch Size, because that's what it is. + +The other thing is Gradient Accumulation - this is an emulation of the above Batch size - a virtual batch size, if you will. If your GPU can't handle real batch size then you may fake it using Gradient Accumulation. This will accumulate the gradients over so many steps defined here and then update the weights at the end without increase in GPU. +Gradient accumulation is like a virtual Batch size multiplier without the GPU penalty. + +If your batch size is 4 and your gradient accumulation is 2 then it sort of behaves as if we have batch size 8. *Sort of* because Batch size of 4 and GA of 2 is NOT the same as batch size of 2 and GA of 4. (It produces different weights - hence it's not an equivalent). The idea is that if you don't have GPU - using GA to extend batch size is the next best thing (good enough) since you have no other choice. + +If all you can afford is 1 batch size, then increasing GA will likely make the learning better in some range of GA (it's not always more is better). + +However - GA is not some golden goose. As said, it isn't the same as batch size. In fact GA may worsen your learning as well. + +I would suggest a series of experiment where you would put batch size as high as possible without OOM, set GA 1, then repeat training while increasing the GA (2, 4...), and see how the model changes. It's likely that it would follow some sort of curve where GA will seem to help before it will make it worse. Some people believe that if you can squeeze 6 BATCH Size, then you should not bother with GA at all... YMMW + +High Batch Size vs High GA would also likely produce different results in terms of learning words vs style. How? Hmmmm... good question. + +One optical "benefit" of GA is that the loss will fluctuate less (because of all the gradient accumulation, which works as a form of noise smoothing as well). diff --git a/extensions/Training_PRO/custom_scheduler.py b/extensions/Training_PRO/custom_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..ec6e0e27bd10ff9afe2e5c2f84c9e931b6d3fadc --- /dev/null +++ b/extensions/Training_PRO/custom_scheduler.py @@ -0,0 +1,433 @@ +from functools import partial +import torch +import transformers +import math +from torch.optim.lr_scheduler import LambdaLR + +from peft import ( + PeftModel, +) + +RED = "\033[91m" +YELLOW = "\033[93m" +GREEN = "\033[92m" +RESET = "\033[0m" + +last_print_label = '' + +custom_scheduler_params = {'trigger_loss': 0.0, 'ramp_down_ratio':1.0, 'current_loss': 0.0,'dynamic_scheduler_stop': False, 'calc_ramp_down_at_step': 0, 'calc_num_training_steps': 0} + + +def custom_scheduler_global_update(current_loss: float): + custom_scheduler_params.update({'current_loss': current_loss}) + +def custom_scheduler_global_setup(trigger_loss: float, ramp_down_ratio: float): + custom_scheduler_params.update({'trigger_loss': trigger_loss}) + custom_scheduler_params.update({'ramp_down_ratio': ramp_down_ratio}) + + # calculates the total num steps after trigger + custom_scheduler_params.update({'calc_num_training_steps': 0}) + #calculates steps when the ramp_down trigger occured + custom_scheduler_params.update({'calc_ramp_down_at_step': 0}) + # triggers scheduler stopping after it reached calc_num_training_steps + custom_scheduler_params.update({'dynamic_scheduler_stop': False}) + + +# hold constant to the half of epochs then cosine down to 0 +def _get_fp_half_schedule_with_warmup_lr_lambda(current_step: int, *, num_warmup_steps: int, num_training_steps: int, num_firstepoch_steps: int): + + global last_print_label + print_label = '' + + half_steps = num_training_steps//2 + + num_warmup_steps = min(num_warmup_steps,half_steps) + + if current_step < num_warmup_steps: + print_label = 'Scheduler: Warmup' + elif current_step < half_steps: + print_label = 'Scheduler: Hold' + else: + print_label = 'Scheduler: Annealing' + + if print_label != last_print_label: + print(print_label) + + last_print_label = print_label + + if current_step < num_warmup_steps: + return float(current_step) / float(max(1, num_warmup_steps)) + + if current_step < half_steps: + return 1.0 + + progress = float(current_step - half_steps) / float(max(1, num_training_steps - half_steps)) + num_cycles = 0.5 + return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress))) + + +# raise up in cosine, then fall back in cosine +def _get_fp_cosine_raise_and_fall_lr_lambda(current_step: int, *, num_warmup_steps: int, num_training_steps: int, num_firstepoch_steps: int): + + global last_print_label + print_label = '' + + half_steps = num_training_steps//2 + + #num_warmup_steps = min(num_warmup_steps,half_steps) + + if current_step < half_steps: + print_label = 'Scheduler: Raise' + else: + print_label = 'Scheduler: Fall' + + if print_label != last_print_label: + print(print_label) + + last_print_label = print_label + + + # linear + # return float(current_step) / float(max(1, num_warmup_steps)) + + progress = float(current_step - half_steps) / float(max(1, num_training_steps - half_steps)) + num_cycles = 0.5 + return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress))) + +# constant to the first epochs then cosine down to 0 over the rest epochs +def _get_fp_cosine_schedule_with_warmup_lr_lambda(current_step: int, *, num_warmup_steps: int, num_training_steps: int, num_firstepoch_steps: int): + + global last_print_label + print_label = '' + + num_warmup_steps = min(num_warmup_steps,num_firstepoch_steps) + + if current_step < num_warmup_steps: + print_label = 'Scheduler: Warmup' + elif current_step < num_firstepoch_steps: + print_label = 'Scheduler: Hold' + else: + print_label = 'Scheduler: Annealing' + + if print_label != last_print_label: + print(print_label) + + last_print_label = print_label + + if current_step < num_warmup_steps: + return float(current_step) / float(max(1, num_warmup_steps)) + + if current_step < num_firstepoch_steps: + return 1.0 + + progress = float(current_step - num_firstepoch_steps) / float(max(1, num_training_steps - num_firstepoch_steps)) + num_cycles = 0.5 + return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress))) + +# halve lr each epoch + +def _get_fp_cdrop_rate_schedule_with_warmup_lr_lambda(current_step: int, *, num_warmup_steps: int, num_training_steps: int, num_firstepoch_steps: int): + + global last_print_label + print_label = '' + + num_warmup_steps = min(num_warmup_steps, num_firstepoch_steps) + + current_epoch = (current_step // num_firstepoch_steps) + 1 + + + if current_step < num_warmup_steps: + print_label = 'Scheduler: Warmup' + elif current_step < num_firstepoch_steps: + print_label = 'Scheduler: Hold' + else: + print_label = 'Scheduler: Drop Rate' + + if print_label != last_print_label: + print(print_label) + + last_print_label = print_label + + if current_step < num_warmup_steps: + return float(current_step) / float(max(1, num_warmup_steps)) + + if current_step < num_firstepoch_steps: + return 1.0 + + # Compute the learning rate for the annealing phase + + learning_rate = 1.0 / float(2 ** (current_epoch - 1)) + + return learning_rate + +# epoch decay: 1/(1 + decay * epoch) + +def custom_cosine_scheduler_with_warmup(optimizer, num_warmup_steps, num_training_steps, num_firstepoch_steps, last_epoch=-1): + """ + Args: + optimizer ([`~torch.optim.Optimizer`]): + The optimizer for which to schedule the learning rate. + num_warmup_steps (`int`): + The number of steps for the warmup phase. + num_training_steps (`int`): + The total number of training steps. + last_epoch (`int`, *optional*, defaults to -1): + The index of the last epoch when resuming training. + + Return: + `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule. + """ + + lr_lambda = partial( + _get_fp_cosine_schedule_with_warmup_lr_lambda, + num_warmup_steps=num_warmup_steps, + num_training_steps=num_training_steps, + num_firstepoch_steps = num_firstepoch_steps, + ) + return LambdaLR(optimizer, lr_lambda, last_epoch) + +def custom_half_scheduler_with_warmup(optimizer, num_warmup_steps, num_training_steps, num_firstepoch_steps, last_epoch=-1): + """ + Args: + optimizer ([`~torch.optim.Optimizer`]): + The optimizer for which to schedule the learning rate. + num_warmup_steps (`int`): + The number of steps for the warmup phase. + num_training_steps (`int`): + The total number of training steps. + last_epoch (`int`, *optional*, defaults to -1): + The index of the last epoch when resuming training. + + Return: + `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule. + """ + + lr_lambda = partial( + _get_fp_half_schedule_with_warmup_lr_lambda, + num_warmup_steps=num_warmup_steps, + num_training_steps=num_training_steps, + num_firstepoch_steps = num_firstepoch_steps, + ) + return LambdaLR(optimizer, lr_lambda, last_epoch) + +def custom_raise_fall_scheduler_with_warmup(optimizer, num_warmup_steps, num_training_steps, num_firstepoch_steps, last_epoch=-1): + """ + Args: + optimizer ([`~torch.optim.Optimizer`]): + The optimizer for which to schedule the learning rate. + num_warmup_steps (`int`): + The number of steps for the warmup phase. + num_training_steps (`int`): + The total number of training steps. + last_epoch (`int`, *optional*, defaults to -1): + The index of the last epoch when resuming training. + + Return: + `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule. + """ + + lr_lambda = partial( + _get_fp_cosine_raise_and_fall_lr_lambda, + num_warmup_steps=num_warmup_steps, + num_training_steps=num_training_steps, + num_firstepoch_steps = num_firstepoch_steps, + ) + return LambdaLR(optimizer, lr_lambda, last_epoch) + + +def neftune_forward(self, input: torch.Tensor): + """ + Implements the NEFTune forward pass for the model. Note this works only for + torch.nn.Embedding layers. This method is slightly adapted from the original source code + that can be found here: https://github.com/neelsjain/NEFTune + + Args: + input (`torch.Tensor`): + The input tensor to the model. + noise_alpha (`float`): + The noise alpha value to use for the NEFTune forward pass. + """ + embeddings = torch.nn.functional.embedding( + input, self.weight, self.padding_idx, self.max_norm, self.norm_type, self.scale_grad_by_freq, self.sparse + ) + + if self.training: + # Add noise to the embeddings + dims = torch.tensor(embeddings.size(1) * embeddings.size(2)) + mag_norm = self.neftune_noise_alpha / torch.sqrt(dims) + embeddings = embeddings + torch.zeros_like(embeddings).uniform_(-mag_norm, mag_norm) + + return embeddings + + +class FPNEFtuneTrainer(transformers.Trainer): + def __init__(self,neftune_noise_alpha:float = 0.0, model = None, *args, **kwargs): + self.neftune_noise_alpha = neftune_noise_alpha + if self.neftune_noise_alpha > 0.0: + model = self._activate_neftune(model) + super().__init__(model = model, *args, **kwargs) + + + def _activate_neftune(self, model): + r""" + Activates the neftune as presented in this code: https://github.com/neelsjain/NEFTune and paper: https://arxiv.org/abs/2310.05914 + """ + print(f"Activating {RED}NEFtune{RESET} with scale: {self.neftune_noise_alpha}") + if isinstance(model, transformers.PreTrainedModel): + embeddings = model.get_input_embeddings() + elif isinstance(model, PeftModel): + embeddings = model.base_model.get_input_embeddings() + + embeddings.neftune_noise_alpha = self.neftune_noise_alpha + old_forward = embeddings.forward + + # This hack seems to be needed to properly use a custom forward pass + # all credits to: https://discuss.pytorch.org/t/how-can-i-replace-the-forward-method-of-a-predefined-torchvision-model-with-my-customized-forward-function/54224/11 + bound_method = neftune_forward.__get__(embeddings, embeddings.__class__) + setattr(embeddings, "forward", bound_method) + + # embeddings.forward = neftune_forward + embeddings._trl_old_forward = old_forward + + return model + + def train(self, *args, **kwargs): + output = super().train(*args, **kwargs) + + # After training we make sure to retrieve back the original forward pass method + # for the embedding layer + if self.neftune_noise_alpha is not None: + + if isinstance(self.model, transformers.PreTrainedModel): + embeddings = self.model.get_input_embeddings() + elif isinstance(self.model, PeftModel): + embeddings = self.model.base_model.get_input_embeddings() + + if hasattr(embeddings, "_trl_old_forward"): + embeddings.forward = embeddings._trl_old_forward + del embeddings._trl_old_forward + del embeddings.neftune_noise_alpha + + return output + + +class FPSchedulerTrainer(transformers.Trainer): + def __init__(self,neftune_noise_alpha:float = 0.0, model = None, *args, **kwargs): + self.neftune_noise_alpha = neftune_noise_alpha + if self.neftune_noise_alpha > 0.0: + model = self._activate_neftune(model) + super().__init__(model = model, *args, **kwargs) + + + def _activate_neftune(self, model): + r""" + Activates the neftune as presented in this code: https://github.com/neelsjain/NEFTune and paper: https://arxiv.org/abs/2310.05914 + """ + print(f"Activating {RED}NEFtune{RESET} with scale: {self.neftune_noise_alpha}") + if isinstance(model, transformers.PreTrainedModel): + embeddings = model.get_input_embeddings() + elif isinstance(model, PeftModel): + embeddings = model.base_model.get_input_embeddings() + + embeddings.neftune_noise_alpha = self.neftune_noise_alpha + old_forward = embeddings.forward + + # This hack seems to be needed to properly use a custom forward pass + # all credits to: https://discuss.pytorch.org/t/how-can-i-replace-the-forward-method-of-a-predefined-torchvision-model-with-my-customized-forward-function/54224/11 + bound_method = neftune_forward.__get__(embeddings, embeddings.__class__) + setattr(embeddings, "forward", bound_method) + + # embeddings.forward = neftune_forward + embeddings._trl_old_forward = old_forward + + return model + + def train(self, *args, **kwargs): + output = super().train(*args, **kwargs) + + # After training we make sure to retrieve back the original forward pass method + # for the embedding layer + if self.neftune_noise_alpha is not None: + + if isinstance(self.model, transformers.PreTrainedModel): + embeddings = self.model.get_input_embeddings() + elif isinstance(self.model, PeftModel): + embeddings = self.model.base_model.get_input_embeddings() + + if hasattr(embeddings, "_trl_old_forward"): + embeddings.forward = embeddings._trl_old_forward + del embeddings._trl_old_forward + del embeddings.neftune_noise_alpha + + return output + + + def create_scheduler(self, num_training_steps: int, optimizer: torch.optim.Optimizer = None): + #Setup the scheduler. The optimizer of the trainer must have been set up either before this method is called or passed as an argument. + + num_train_epochs = self.args.num_train_epochs + num_warmup_steps=self.args.get_warmup_steps(num_training_steps) + num_firstepoch_steps = math.ceil(num_training_steps/num_train_epochs) + num_warmup_acc = num_warmup_steps*self.args.gradient_accumulation_steps + num_firstepoch_steps_acc = num_firstepoch_steps*self.args.gradient_accumulation_steps + num_training_steps_acc = num_training_steps*self.args.gradient_accumulation_steps + + custom_scheduler_params.update({'dynamic_scheduler_stop': False}) + + print (f"Warm-up steps aligned to Gradient accumulation ({self.args.gradient_accumulation_steps}) = {num_warmup_acc} actual warmup steps") + if self.args.lr_scheduler_type == 'cosine': + + num_warmup_acc_min = min(num_warmup_acc, num_firstepoch_steps_acc) + + if num_warmup_acc>num_firstepoch_steps_acc: + print(f"\033[1;31;1mWARNING: The number of warmup steps is set too high! It will be clamped to 1 epoch, essentially going from warmup to annealing.\033[0;37;0m") + print (f"FP Scheduler Warmup: 0-[{num_warmup_acc_min}], Hold [{num_warmup_acc_min}]-{num_firstepoch_steps_acc}, Annealing {num_firstepoch_steps_acc}-{num_training_steps_acc}") + else: + print (f"FP Scheduler Warmup: 0-{num_warmup_acc_min}, Hold {num_warmup_acc_min}-{num_firstepoch_steps_acc}, Annealing {num_firstepoch_steps_acc}-{num_training_steps_acc}") + + self.lr_scheduler = custom_cosine_scheduler_with_warmup( + optimizer=self.optimizer if optimizer is None else optimizer, + num_warmup_steps=num_warmup_steps, + num_training_steps=num_training_steps, + num_firstepoch_steps = num_firstepoch_steps, + ) + self._created_lr_scheduler = True + return self.lr_scheduler + elif self.args.lr_scheduler_type == 'constant': + + half_step_acc = num_training_steps_acc//2 + num_warmup_acc_min = min(num_warmup_acc, half_step_acc) + + if num_warmup_acc>half_step_acc: + print(f"\033[1;31;1mWARNING: The number of warmup steps is set too high! It will be clamped to half of all epochs, essentially going from warmup to annealing in the middle.\033[0;37;0m") + print (f"FP Scheduler Warmup: 0-[{num_warmup_acc_min}], Hold [{num_warmup_acc_min}]-{half_step_acc}, Annealing {half_step_acc}-{num_training_steps_acc}") + else: + print (f"FP Scheduler Warmup: 0-{num_warmup_acc_min}, Hold {num_warmup_acc_min}-{half_step_acc}, Annealing {half_step_acc}-{num_training_steps_acc}") + + self.lr_scheduler = custom_half_scheduler_with_warmup( + optimizer=self.optimizer if optimizer is None else optimizer, + num_warmup_steps=num_warmup_steps, + num_training_steps=num_training_steps, + num_firstepoch_steps = num_firstepoch_steps, + ) + self._created_lr_scheduler = True + return self.lr_scheduler + elif self.args.lr_scheduler_type == 'constant_with_warmup': + + half_step_acc = num_training_steps_acc//2 + + if num_warmup_steps>0: + print(f"Warmup doesn't apply to this scheduler [Raise-Fall]") + + print (f"Scheduler Raise: 0-{half_step_acc}, Fall {half_step_acc}-{num_training_steps_acc}") + + self.lr_scheduler = custom_raise_fall_scheduler_with_warmup( + optimizer=self.optimizer if optimizer is None else optimizer, + num_warmup_steps=num_warmup_steps, + num_training_steps=num_training_steps, + num_firstepoch_steps = num_firstepoch_steps, + ) + self._created_lr_scheduler = True + return self.lr_scheduler + else: + return super().create_scheduler(num_training_steps=num_training_steps, optimizer=optimizer) \ No newline at end of file diff --git a/extensions/Training_PRO/matplotgraph.py b/extensions/Training_PRO/matplotgraph.py new file mode 100644 index 0000000000000000000000000000000000000000..5e607526925445134fc1715a1fab6bb4af99112d --- /dev/null +++ b/extensions/Training_PRO/matplotgraph.py @@ -0,0 +1,62 @@ +import os +import json + +def create_graph(lora_path, lora_name): + try: + import matplotlib.pyplot as plt + from matplotlib.ticker import ScalarFormatter + + peft_model_path = f'{lora_path}/training_graph.json' + image_model_path = f'{lora_path}/training_graph.png' + # Check if the JSON file exists + if os.path.exists(peft_model_path): + # Load data from JSON file + with open(peft_model_path, 'r') as file: + data = json.load(file) + # Extract x, y1, and y2 values + x = [item['epoch'] for item in data] + y1 = [item['learning_rate'] for item in data] + y2 = [item['loss'] for item in data] + + # Create the line chart + fig, ax1 = plt.subplots(figsize=(10, 6)) + + + # Plot y1 (learning rate) on the first y-axis + ax1.plot(x, y1, 'b-', label='Learning Rate') + ax1.set_xlabel('Epoch') + ax1.set_ylabel('Learning Rate', color='b') + ax1.tick_params('y', colors='b') + + # Create a second y-axis + ax2 = ax1.twinx() + + # Plot y2 (loss) on the second y-axis + ax2.plot(x, y2, 'r-', label='Loss') + ax2.set_ylabel('Loss', color='r') + ax2.tick_params('y', colors='r') + + # Set the y-axis formatter to display numbers in scientific notation + ax1.yaxis.set_major_formatter(ScalarFormatter(useMathText=True)) + ax1.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) + + # Add grid + ax1.grid(True) + + # Combine the legends for both plots + lines, labels = ax1.get_legend_handles_labels() + lines2, labels2 = ax2.get_legend_handles_labels() + ax2.legend(lines + lines2, labels + labels2, loc='best') + + # Set the title + plt.title(f'{lora_name} LR and Loss vs Epoch') + + # Save the chart as an image + plt.savefig(image_model_path) + + print(f"Graph saved in {image_model_path}") + else: + print(f"File 'training_graph.json' does not exist in the {lora_path}") + + except ImportError: + print("matplotlib is not installed. Please install matplotlib to create PNG graphs") \ No newline at end of file diff --git a/extensions/Training_PRO/script.py b/extensions/Training_PRO/script.py new file mode 100644 index 0000000000000000000000000000000000000000..ccc0e6dde993afb9794b9960c72d8eaa7f009efd --- /dev/null +++ b/extensions/Training_PRO/script.py @@ -0,0 +1,1308 @@ +import os + +os.environ["WANDB_MODE"] = "offline" +# os.environ["WANDB_DISABLED"] = "true" + +import json +import math +import random +import shutil +import sys +import threading +import time +import traceback +from datetime import datetime +from pathlib import Path + +import gradio as gr +import pandas as pd +import torch +import transformers + +from functools import partial + +from .custom_scheduler import FPSchedulerTrainer, FPNEFtuneTrainer + +from .matplotgraph import create_graph +from .train_utils import get_available_loras_local, precise_cut, sliding_block_cut, download_file_from_url + +from datasets import Dataset, load_dataset +from peft import ( + LoraConfig, + get_peft_model, + prepare_model_for_kbit_training, + set_peft_model_state_dict +) +from peft.utils.other import \ + TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING as model_to_lora_modules +from transformers.models.auto.modeling_auto import ( + MODEL_FOR_CAUSAL_LM_MAPPING_NAMES +) + +from modules import shared, utils +from modules.ui import create_refresh_button + +from modules.evaluate import ( + calculate_perplexity, + generate_markdown_table, + save_past_evaluations +) +from modules.logging_colors import logger +from modules.models import reload_model +from modules.utils import natural_keys + +import warnings +warnings.filterwarnings(action = "ignore", message="torch.utils.checkpoint:") +warnings.filterwarnings(action = "ignore", message="`do_sample` is set to `False`") + +params = { + "display_name": "Training PRO", + "is_tab": True +} + +non_serialized_params = { + "debug_slicer": False, + "Lora_sortedByTime": False, + "stop_at_loss": 0, + "save_steps_under_loss": 0.0, + "save_checkpoint_now": False, + "training_loop": False, + "current_stability": 0, + "save_epochs": 0, + "checkpoint_offset": 0, + "epoch_offset":0, + "safe_serialization": False, +} + +MODEL_CLASSES = {v[1]: v[0] for v in MODEL_FOR_CAUSAL_LM_MAPPING_NAMES.items()} + +PARAMETERS = ["lora_name", "always_override", "save_steps", "micro_batch_size", "batch_size", "epochs", "learning_rate", "lr_scheduler_type", "lora_rank", "lora_alpha", "lora_dropout", "cutoff_len", "dataset", "eval_dataset", "format", "eval_steps", "raw_text_file", "higher_rank_limit", "warmup_steps", "optimizer", "hard_cut_string", "train_only_after", "stop_at_loss", "add_eos_token", "min_chars", "report_to", "precize_slicing_overlap", "add_eos_token_type", "save_steps_under_loss", "add_bos_token", "training_projection","sliding_window","warmup_ratio","grad_accumulation","neft_noise_alpha"] +WANT_INTERRUPT = False + +train_log = {} +train_template = {} +train_log_graph = [] +train_choices = ["all","q-k-v-o","q-k-v","k-v-down","q-v"] + +statistics = { + 'loss': [], + 'lr': [], +} + +RED = "\033[91m" +YELLOW = "\033[93m" +GREEN = "\033[92m" +RESET = "\033[0m" + +def ui(): + + with gr.Tab('Train LoRA', elem_id='lora-train-tab'): + tmp = gr.State('') + with gr.Row(): + with gr.Column(): + # YY.MM.DD + gr.Markdown("`Ver: 23.10.20 (REV2)` This is enhanced version of QLora Training. [Maintained by FP](https://github.com/FartyPants/Training_PRO/tree/main)") + + with gr.Row(): + with gr.Column(scale=5): + with gr.Row(): + copy_from = gr.Dropdown(label='Copy parameters from', value='None', choices=get_available_loras_local(non_serialized_params['Lora_sortedByTime']), elem_classes=['slim-dropdown']) + create_refresh_button(copy_from, lambda: None, lambda: {'choices': get_available_loras_local(non_serialized_params['Lora_sortedByTime'])}, 'refresh-button') + with gr.Column(): + sort_byTime = gr.Checkbox(label='Sort list by Date', value=False, info='Sorts Loras by date created.', elem_classes=['no-background']) + + with gr.Row(): + with gr.Column(scale=5): + lora_name = gr.Textbox(label='Name', info='The name of your new LoRA file') + + with gr.Column(): + always_override = gr.Checkbox(label='Override Existing Files', value=False, info='If the name is the same, checking will replace the existing file, and unchecking will load and continue from it (the rank must be the same).', elem_classes=['no-background']) + + with gr.Row(): + with gr.Column(): + lora_rank = gr.Slider(label='LoRA Rank', value=32, minimum=0, maximum=1024, step=4, info='Also called dimension count. Higher values = larger file, more content control. Smaller values = smaller file, less control. Use 4 or 8 for style, 128 or 256 to teach, 1024+ for fine-detail on big data. More VRAM is needed for higher ranks.') + lora_alpha = gr.Slider(label='LoRA Alpha', value=64, minimum=0, maximum=2048, step=4, info='This divided by the rank becomes the scaling of the LoRA. Higher means stronger. A good standard value is twice your Rank.') + batch_size = gr.Slider(visible= False, label='Batch Size', value=0, minimum=0, maximum=1024, step=4, info='Now Replaced with Gradient accumulation. Keeping it for sake of old saved data') + micro_batch_size = gr.Slider(label='True Batch Size', value=4, minimum=1, maximum=128, step=1, info='Specifies how many text blocks per step will be trained. The higher value, the better the concept of training will be, but it requires more GPU memory and it reduces speed.') + grad_accumulation = gr.Slider(label='Gradient Accumulation Steps', value=1, minimum=1, maximum=256, step=1, info="Virtually multiplies the Batch Size by averaging the learning over more than one step. VRAM friendly. Evens out loss fluctuations but can also degrade training fidelity.") + + with gr.Column(): + stop_at_loss = gr.Slider(label='Stop at loss (Can be changed during training)', minimum=0.0, maximum=3.0, step=0.1, value=0.00, info='The process will automatically stop once the desired loss value is reached.') + gr.Markdown(" ") + epochs = gr.Number(label='Epochs', value=3, info='Number of times every entry in the dataset should be fed into training. So 1 means feed each item in once, 5 means feed it in five times, etc.') + learning_rate = gr.Textbox(label='Learning Rate', value='3e-4', info='In scientific notation. 3e-4 is a good starting base point. 1e-2 is extremely high, 1e-6 is extremely low.') + lr_scheduler_type = gr.Dropdown(label='LR Scheduler', value='linear', choices=['linear', 'constant', 'constant_with_warmup', 'cosine', 'cosine_with_restarts', 'polynomial', 'inverse_sqrt', 'FP_low_epoch_annealing', 'FP_half_time_annealing','FP_raise_fall_creative'], info='Learning rate scheduler - defines how the learning rate changes over time. Custom schedulers: FP_low_epoch_annealing, FP_half_time_annealing, FP_raise_fall_creative (see README)', elem_classes=['slim-dropdown']) + + with gr.Accordion(label='Checkpoints', open=True): + with gr.Row(): + with gr.Column(): + save_steps = gr.Number(label='Save every n steps', value=0, info='A checkpoint will be saved every n steps and at each Epoch boundary. (0 = OFF)') + with gr.Column(): + save_steps_under_loss = gr.Slider(label='Save at 10% Loss change', value=1.8, minimum=0.0, maximum=3.0, step=0.1, info="Saves checkpoints at (or bellow) this loss and then each time loss falls by at least 10% This works independently from 'Save every n steps'") + with gr.Row(): + save_chackpoint_now = gr.Button('Queue Checkpoint Now') + + with gr.Accordion(label='Advanced Options', open=True): + with gr.Row(): + with gr.Column(): + warmup_steps = gr.Number(label='Warmup Steps', value=100, info='Number of max steps used for a linear warmup. Reduces early over-fitting by the first training blocks. Value has precedent over Warmup Ratio. Aligns to the closest multiple of graddient accumulation') + warmup_ratio = gr.Slider(label='Warmup Ratio', minimum=0.0, maximum=0.2, step=0.025, value=0.0, info='Ratio of total training steps that will be used for a linear warmup. It applies only if Warmup Step is 0.') + neft_noise_alpha = gr.Slider(label='NEFtune noise scale', minimum=0.0, maximum=15, step=1, value=0.0, info='Add noise to the training to improve generalization. [0 - OFF, Starting value to experiment: 5]') + training_projection = gr.Radio(value = train_choices[4], label='LLaMA Target Projections', info='Change the targets (LORA is typically q-v)', choices=train_choices) + lora_dropout = gr.Slider(label='LoRA Dropout', minimum=0.0, maximum=1.0, step=0.025, value=0.05, info='Percentage probability for dropout of LoRA layers. This can help reduce overfitting. Most users should leave at default.') + optimizer = gr.Dropdown(label='Optimizer', value='adamw_torch', choices=['adamw_hf', 'adamw_torch', 'adamw_torch_fused', 'adamw_torch_xla', 'adamw_apex_fused', 'adafactor', 'adamw_bnb_8bit', 'adamw_anyprecision', 'sgd', 'adagrad'], info='Different optimizer implementation options, for advanced users. Effects of different options are not well documented yet.', elem_classes=['slim-dropdown']) + + with gr.Column(): + train_only_after = gr.Textbox(label='Train Only After', value='', info='Only consider text *after* this string in any given chunk for training. For Alpaca datasets, use "### Response:" to only train the response and ignore the input.') + add_bos_token = gr.Checkbox(label='Add BOS token', value=True, info="Adds BOS token for each dataset item") + add_eos_token = gr.Checkbox(label='Add EOS token', value=False, info="Adds EOS token for each dataset item") + add_eos_token_type = gr.Dropdown(label='EOS placement (Text file)', choices=['Every Block', 'Hard Cut Blocks Only'], value='Every Block', info='', allow_custom_value = False) + + higher_rank_limit = gr.Checkbox(label='Enable higher ranks', value=False, info='If checked, changes Rank/Alpha slider above to go much higher. This will not work without a datacenter-class GPU.') + report_to = gr.Radio(label="Save detailed logs with", value="None", choices=["None", "wandb", "tensorboard"], interactive=True) + # for future + #with gr.Accordion(label='Dynamic Scheduler', open = False): + # ds_min_epochs = gr.Number(label='Minimum Epochs', value='1', info='Minimum epochs that will be always performed before ramp down can be triggered') + # ds_max_epochs = gr.Number(label='Maximum Epochs (fallback)', value='50', info='Maximum Epochs before the training will bail out completely (should be a large number)') + # ds_loss_trigger = gr.Slider(label='Trigger Loss', minimum=0.0, maximum=2.8, step=0.1, value=1.6, info='Loss at which the ramp down schedule will be triggered') + # ds_loss_rolling_window = gr.Number(label='Loss rolling average', value='4', info='Calculate loss by averaging last x numbers to avoid jumps and noise') + # ds_epochs_to_ramp = gr.Slider(label='Ramp down ratio', minimum=0.0, maximum=2.0, step=0.1, value=1.00, info='How long the ramp down will last relative to ellapsed steps (before trigger)') + # gr.Markdown('These are settings for FP_dynamic_loss_trigger scheduler. The scheduler will do warm up, then hold constant untill a loss falls under Trigger Loss, then it will commence linear ramp down schedule and stop. The length of ramp down is set by Ramp down ratio where (ramp down steps) = ratio * (elapsed steps). (The time to completition shown will be very high untill ramp down is triggered.)') + + + with gr.Column(): + with gr.Tab(label='Formatted Dataset'): + with gr.Row(): + with gr.Column(): + with gr.Row(): + dataset = gr.Dropdown(choices=get_datasets('training/datasets', 'json'), value='None', label='Dataset', info='The dataset file to use for training.', elem_classes=['slim-dropdown']) + create_refresh_button(dataset, lambda: None, lambda: {'choices': get_datasets('training/datasets', 'json')}, 'refresh-button') + with gr.Row(): + eval_dataset = gr.Dropdown(choices=get_datasets('training/datasets', 'json'), value='None', label='Evaluation Dataset', info='The (optional) dataset file used to evaluate the model after training.', elem_classes=['slim-dropdown']) + create_refresh_button(eval_dataset, lambda: None, lambda: {'choices': get_datasets('training/datasets', 'json')}, 'refresh-button') + + with gr.Column(): + with gr.Row(): + format = gr.Dropdown(choices=get_datasets('training/formats', 'json'), value='None', label='Data Format', info='The format file used to decide how to format the dataset input.', elem_classes=['slim-dropdown']) + create_refresh_button(format, lambda: None, lambda: {'choices': get_datasets('training/formats', 'json')}, 'refresh-button') + with gr.Row(): + eval_steps = gr.Number(label='Evaluate every n steps', value=100, info='If an evaluation dataset is given, test it every time this many steps pass.') + + with gr.Tab(label="Text file"): + with gr.Row(): + raw_text_file = gr.Dropdown(choices=get_datasets('training/datasets', 'txt'), value='None', label='Text file', info='The text file to use for training.', elem_classes=['slim-dropdown']) + create_refresh_button(raw_text_file, lambda: None, lambda: {'choices': get_datasets('training/datasets', 'txt')}, 'refresh-button') + + with gr.Row(): + with gr.Column(): + precize_slicing_overlap = gr.Checkbox(label='Add Overlapping blocks', value = True) + sliding_window = gr.Checkbox(label='DEMENTOR Long-form Learning by FP (Highly Experimental, use low epochs)', value = False, info='Deep Memorization Enforcement Through Overlapping and Repetition. (I named it, so shush). Special process for learning long-form text using low amount of epochs.') + #debug_slicer = gr.Checkbox(label='Dump sentencelist.json to logs', value = non_serialized_params['debug_slicer'], info='Debug Slicer') + + with gr.Column(): + hard_cut_string = gr.Textbox(label='Hard Cut String', value='\\n\\n\\n', info='String that indicates a cut between logical blocks of text (ex. Ideas or Chapters). Helps prevent unwanted overlap between unrelated ideas.') + min_chars = gr.Number(label='Ignore small blocks', value=0, info='Ignore Text blocks that have less or equal characters than this number.') + with gr.Tab(label="URL"): + with gr.Row(): + with gr.Column(): + download_file_url = gr.Textbox(label='Download JSON or txt file to datasets (or formats) folder', value='',info='The URL of a file to download. If on github, make sure you get url of the raw file (https://raw.githubusercontent.com/...). If huggin face, make sure the url has /resolve/ in it not /blob/') + with gr.Row(): + download_check_overwrite = gr.Checkbox(label='Overwrite', value=False, info='Overwrite if file exist') + download_folder = gr.Radio(label="Destination", value='training/datasets', choices=['training/datasets', 'training/formats'], interactive=True) + download_button = gr.Button('Download') + download_status = gr.Textbox(label='Download Status', value='', interactive=False) + with gr.Row(): + with gr.Column(): + with gr.Row(): + cutoff_len = gr.Slider(label='Chunk Length (Cutoff Length)', minimum=32, maximum=2048, value=256, step=32, info='The maximum length of a chunk (in tokens). Applies to both JSON dataset and text files. Higher values require much more VRAM.') + with gr.Row(): + with gr.Column(): + check_dataset_btn = gr.Button('Verify Dataset/Text File and suggest data entries') + check_dataset_txt = gr.Textbox(label='Dataset info', value='') + + with gr.Row(): + start_button = gr.Button("Start LoRA Training", variant='primary') + stop_button = gr.Button("Interrupt") + + with gr.Accordion(label="Graph", open=True): + with gr.Row(): + # show_actions_button = False - we use old gradio + plot_graph = gr.LinePlot(x="epoch", y="value", title="Loss Metrics", overlay_point=True, tooltip=["epoch", "value"], x_lim=[0, 1], y_lim=[0, 3.5], width=500, height=250) + + output = gr.Markdown(value="Ready") + + with gr.Tab('Perplexity evaluation', elem_id='evaluate-tab'): + with gr.Row(): + with gr.Column(): + models = gr.Dropdown(utils.get_available_models(), label='Models', multiselect=True) + evaluate_text_file = gr.Dropdown(choices=['wikitext', 'ptb', 'ptb_new'] + get_datasets('training/datasets', 'txt')[1:], value='wikitext', label='Input dataset', info='The text file on which the model will be evaluated. The first options are automatically downloaded: wikitext, ptb, and ptb_new. The next options are your local text files under training/datasets.') + with gr.Row(): + with gr.Column(): + stride_length = gr.Slider(label='Stride', minimum=1, maximum=2048, value=512, step=1, info='Used to make the evaluation faster at the cost of accuracy. 1 = slowest but most accurate. 512 is a common value.') + + with gr.Column(): + max_length = gr.Slider(label='max_length', minimum=0, maximum=shared.settings['truncation_length_max'], value=0, step=1, info='The context for each evaluation. If set to 0, the maximum context length for the model will be used.') + + with gr.Row(): + start_current_evaluation = gr.Button("Evaluate loaded model") + start_evaluation = gr.Button("Evaluate selected models") + stop_evaluation = gr.Button("Interrupt") + + with gr.Column(): + evaluation_log = gr.Markdown(value='') + + evaluation_table = gr.Dataframe(value=generate_markdown_table(), interactive=True) + with gr.Row(): + save_comments = gr.Button('Save comments', elem_classes="small-button") + refresh_table = gr.Button('Refresh the table', elem_classes="small-button") + + # Training events + all_params = [lora_name, always_override, save_steps, micro_batch_size, batch_size, epochs, learning_rate, lr_scheduler_type, lora_rank, lora_alpha, lora_dropout, cutoff_len, dataset, eval_dataset, format, eval_steps, raw_text_file, higher_rank_limit, warmup_steps, optimizer, hard_cut_string, train_only_after, stop_at_loss, add_eos_token, min_chars, report_to, precize_slicing_overlap, add_eos_token_type, save_steps_under_loss, add_bos_token, training_projection,sliding_window,warmup_ratio,grad_accumulation, neft_noise_alpha] + + def fix_old_version(batch_size_val,micro_batch_size_val, grad_accumulation_val): + if batch_size_val>0: + gradient_acc = batch_size_val // micro_batch_size_val + print(f"Using Old version of Batch Size ({batch_size_val}) to set Gradient Accumulation: {gradient_acc}") + return gradient_acc + + return grad_accumulation_val + + + copy_from.change(partial(do_copy_params, all_params= all_params), copy_from, all_params).then(fix_old_version,[batch_size,micro_batch_size, grad_accumulation],grad_accumulation) + start_button.click(do_train, all_params, [output,plot_graph]) + stop_button.click(do_interrupt, None, None, queue=False) + higher_rank_limit.change(change_rank_limit, [higher_rank_limit], [lora_rank, lora_alpha]) + + def trigger_stop_at_loss(stop_at_loss_value): + non_serialized_params.update({"stop_at_loss": stop_at_loss_value}) + if non_serialized_params['training_loop']: + print(f"Queue: [Stop at loss Change] to {stop_at_loss_value}") + + + stop_at_loss.change(trigger_stop_at_loss, stop_at_loss, None) + + def trigger_save_checkpoint(): + non_serialized_params.update({"save_checkpoint_now": True}) + if non_serialized_params['training_loop']: + print("Queue: [Save checkpoint] Checkpoint will be saved after the current step is finished.") + else: + print("Use during the training to save the checkpoint at any time.") + + + def update_button(): + return gr.Button.update('[Checkpoint in Queue]', variant='stop', interactive=True) + + def update_button2(): + time.sleep(1.0) + return gr.Button.update('Queue Checkpoint Now', variant='secondary',interactive = True) + + save_chackpoint_now.click(trigger_save_checkpoint, None, None).then(update_button, None,save_chackpoint_now).then(update_button2, None,save_chackpoint_now) + + dataset_calc_params = [save_steps,micro_batch_size, epochs, cutoff_len, dataset, format, raw_text_file, warmup_steps, hard_cut_string, min_chars, precize_slicing_overlap,sliding_window,warmup_ratio,grad_accumulation] + + def check_dataset(save_steps:int, micro_batch_size: int, epochs: int, cutoff_len: int, dataset:str, format:str, raw_text_file:str, warmup_steps:int, hard_cut_string:str, min_chars:int, precize_slicing_overlap:bool,sliding_window:bool,warmup_ratio:float,grad_accumulation:int): + result = "Specify JSON dastaset or Text file" + total_blocks = 0 + if shared.tokenizer is None: + yield "Tokenizer is not available. Please Load some Model first." + return + + + if raw_text_file not in ['None', '']: + logger.info("Loading Text file...") + fullpath = clean_path('training/datasets', f'{raw_text_file}') + fullpath = Path(fullpath) + if fullpath.is_dir(): + logger.info('Training path directory {}'.format(raw_text_file)) + raw_text = "" + file_paths = sorted(fullpath.glob('*.txt'), key=lambda path: natural_keys(path.name)) + for file_path in file_paths: + if file_path.is_file(): + with file_path.open('r', encoding='utf-8') as file: + raw_text += file.read().replace('\r', '') + + logger.info(f"Loaded training file: {file_path.name}") + else: + try: + with open(clean_path('training/datasets', f'{raw_text_file}.txt'), 'r', encoding='utf-8') as file: + raw_text = file.read().replace('\r', '') + except: + yield f"{raw_text_file}.txt doesn't seem to exsist anymore... check your training/datasets folder" + return + + + if min_chars<0: + min_chars = 0 + + # == New more precise slicing on sentence boundary == + if sliding_window: + text_chunks = sliding_block_cut(raw_text, min_chars, False, cutoff_len, hard_cut_string,non_serialized_params['debug_slicer']) + else: + text_chunks = precise_cut(raw_text, precize_slicing_overlap, min_chars, False, cutoff_len, hard_cut_string,non_serialized_params['debug_slicer']) + + total_blocks = len(text_chunks) + result = f"Text: ({raw_text_file}.txt) has {total_blocks} blocks (Block Size {cutoff_len} tokens)" + del text_chunks + + else: + if dataset in ['None', '']: + yield "Select dataset or text file." + return + + if format in ['None', '']: + yield "Select format choice for dataset." + return + + with open(clean_path('training/formats', f'{format}.json'), 'r', encoding='utf-8-sig') as formatFile: + format_data: dict[str, str] = json.load(formatFile) + + def generate_prompt(data_point: dict[str, str]): + for options, data in format_data.items(): + if set(options.split(',')) == set(x[0] for x in data_point.items() if (type(x[1]) is str and len(x[1].strip()) > 0)): + for key, val in data_point.items(): + if type(val) is str: + data = data.replace(f'%{key}%', val) + return data + raise RuntimeError(f'Data-point "{data_point}" has no keyset match within format "{list(format_data.keys())}"') + + def tokenize_dummy(prompt): + + input_ids = shared.tokenizer.encode(prompt, truncation=True, max_length=cutoff_len) + labels = [1] * len(input_ids) + input_ids = torch.tensor(input_ids) + return { + "input_ids": input_ids, + "labels": labels, + "attention_mask": input_ids.ne(shared.tokenizer.pad_token_id), + } + + def generate_and_tokenize_prompt(data_point): + prompt = generate_prompt(data_point) + return tokenize_dummy(prompt) + + logger.info("Loading JSON datasets...") + data = load_dataset("json", data_files=clean_path('training/datasets', f'{dataset}.json')) + + data_keys = [] + + if data: + if 'train' in data: # Check if the 'train' split exists in the dataset + data_keys = list(data['train'][0].keys()) + print("Data Keys:", data_keys) + else: + print("The dataset is empty.") + + train_data = data['train'].map(generate_and_tokenize_prompt, new_fingerprint='%030x' % random.randrange(16**30)) + total_blocks = train_data.num_rows + + result = f"Dataset: ({dataset}.json) has {total_blocks} blocks @ length = {cutoff_len} tokens\n(Keys: {data_keys} - Format: {format}.json): " + + #for options, data in format_data.items(): + # format_keys = options.split(',') + # result += f"{format_keys}, " + #result = result.rstrip() + #result = result.rstrip(',') + + if total_blocks>0: + number_ofSteps = int(math.ceil(total_blocks / micro_batch_size) * epochs) + num_stepsPer_epoch = int(math.ceil(number_ofSteps/epochs)) + min_warm = math.ceil(100 / grad_accumulation) + + warmup_steps_suggest = min(int(min_warm*grad_accumulation), int(math.ceil(number_ofSteps * 0.1))) + warmup_steps_suggest = min(warmup_steps_suggest,num_stepsPer_epoch) + + save_each_n_min = int(math.ceil(number_ofSteps/10)) + save_each_n_max = int(math.ceil(number_ofSteps/5)) + gradient_accumulation_max = int(total_blocks)//micro_batch_size + + + result += f"\n[Batch Size: {micro_batch_size}, Epochs: {epochs}, Gradient Accumulation: {grad_accumulation}]\n" + result += f"Total number of steps: {number_ofSteps}\n" + result += f"Steps per each Epoch: {num_stepsPer_epoch}\n" + result += f"Suggestions:\n" + result += f"Checkpoints: Save every {save_each_n_min} - {save_each_n_max} steps (Current: {int(save_steps)})\n" + result += f"Warmup steps: {warmup_steps_suggest} (Current: {int(warmup_steps)})" + if gradient_accumulation_max < grad_accumulation: + result += f"\n\nWARNING: Gradient Accumulation {grad_accumulation} is too high: It should be below {gradient_accumulation_max}" + + + yield result + return + + check_dataset_btn.click(check_dataset, dataset_calc_params ,check_dataset_txt) + + # Evaluation events. For some reason, the interrupt event + # doesn't work with the .then() syntax, so I write them one + # by one in this ugly but functional way. + ev = start_evaluation.click(calculate_perplexity, [models, evaluate_text_file, stride_length, max_length], evaluation_log, show_progress=False) + start_evaluation.click(generate_markdown_table, None, evaluation_table, show_progress=False) + + start_current_evaluation.click(lambda: ['current model'], None, tmp) + ev_cur = start_current_evaluation.click(calculate_perplexity, [tmp, evaluate_text_file, stride_length, max_length], evaluation_log, show_progress=False) + start_current_evaluation.click(generate_markdown_table, None, evaluation_table, show_progress=False) + + stop_evaluation.click(None, None, None, cancels=[ev, ev_cur], queue=False) + refresh_table.click(generate_markdown_table, None, evaluation_table, show_progress=True) + save_comments.click( + save_past_evaluations, evaluation_table, None).then( + lambda: "Comments saved.", None, evaluation_log, show_progress=False) + + def reload_lora(): + return gr.Dropdown.update(choices=get_available_loras_local(non_serialized_params['Lora_sortedByTime'])) + + # nonserialized items + + sort_byTime.change(lambda x: non_serialized_params.update({"Lora_sortedByTime": x}), sort_byTime, None).then(reload_lora,None,copy_from) + #debug_slicer.change(lambda x: non_serialized_params.update({"debug_slicer": x}), debug_slicer, None) + + def update_dataset(): + return gr.update(choices=get_datasets('training/datasets', 'json')), gr.update(choices=get_datasets('training/datasets', 'txt')) + + download_button.click(download_file_from_url, [download_file_url,download_check_overwrite,download_folder] , download_status).then(update_dataset,None,[dataset , raw_text_file]) + +def get_datasets(path: str, ext: str): + # include subdirectories for raw txt files to allow training from a subdirectory of txt files + #if ext == "txt": + # return ['None'] + sorted(set([k.stem for k in list(Path(path).glob('txt')) + list(Path(path).glob('*/')) if k.stem != 'put-trainer-datasets-here']), key=natural_keys) + + return ['None'] + sorted(set([k.stem for k in Path(path).glob(f'*.{ext}') if k.stem != 'put-trainer-datasets-here']), key=natural_keys) + +def do_interrupt(): + global WANT_INTERRUPT + WANT_INTERRUPT = True + + +def do_copy_params(lora_name: str, all_params): + + if lora_name: + f_name = f"{shared.args.lora_dir}/{clean_path(None, lora_name)}/training_parameters.json" + if Path(f_name).is_file(): + with open(f_name, 'r', encoding='utf-8') as format_file: + params: dict[str, str] = json.load(format_file) + else: + params = {} + else: + params = {} + + result = list() + for i in range(0, len(PARAMETERS)): + key = PARAMETERS[i] + if key in params: + result.append(params[key]) + else: + result.append(all_params[i]) + + return result + + +def change_rank_limit(use_higher_ranks: bool): + mult = 2 if use_higher_ranks else 1 + return {"maximum": 1024 * mult, "__type__": "update"}, {"maximum": 2048 * mult, "__type__": "update"} + + +def clean_path(base_path: str, path: str): + """Strips unusual symbols and forcibly builds a path as relative to the intended directory.""" + path = path.replace('\\', '/').replace('..', '_') + if base_path is None: + return path + + return f'{Path(base_path).absolute()}/{path}' + + +def backup_adapter(input_folder): + # Get the creation date of the file adapter_model.bin + try: + adapter_file = Path(f"{input_folder}/adapter_model.bin") + if adapter_file.is_file(): + + logger.info("Backing up existing LoRA adapter...") + creation_date = datetime.fromtimestamp(adapter_file.stat().st_ctime) + creation_date_str = creation_date.strftime("Backup-%Y-%m-%d") + + # Create the new subfolder + subfolder_path = Path(f"{input_folder}/{creation_date_str}") + subfolder_path.mkdir(parents=True, exist_ok=True) + + # Check if the file already exists in the subfolder + backup_adapter_file = Path(f"{input_folder}/{creation_date_str}/adapter_model.bin") + if backup_adapter_file.is_file(): + print(" - Backup already exists. Skipping backup process.") + return + + # Copy existing files to the new subfolder + existing_files = Path(input_folder).iterdir() + for file in existing_files: + if file.is_file(): + shutil.copy2(file, subfolder_path) + except Exception as e: + print("An error occurred in backup_adapter:", str(e)) + + +def calc_trainable_parameters(model): + trainable_params = 0 + all_param = 0 + for _, param in model.named_parameters(): + num_params = param.numel() + # if using DS Zero 3 and the weights are initialized empty + if num_params == 0 and hasattr(param, "ds_numel"): + num_params = param.ds_numel + + all_param += num_params + if param.requires_grad: + trainable_params += num_params + + return trainable_params, all_param + + + +def do_train(lora_name: str, always_override: bool, save_steps: int, micro_batch_size: int, batch_size: int, epochs: int, learning_rate: str, lr_scheduler_type: str, lora_rank: int, lora_alpha: int, lora_dropout: float, cutoff_len: int, dataset: str, eval_dataset: str, format: str, eval_steps: int, raw_text_file: str, higher_rank_limit: bool, warmup_steps: int, optimizer: str, hard_cut_string: str, train_only_after: str, stop_at_loss: float, add_eos_token: bool, min_chars: int, report_to: str, precize_slicing_overlap: bool, add_eos_token_type: str, save_steps_under_loss: float, add_bos_token: bool, training_projection: str,sliding_window:bool,warmup_ratio:float, grad_accumulation: int,neft_noise_alpha:float): + + if shared.args.monkey_patch: + from alpaca_lora_4bit.monkeypatch.peft_tuners_lora_monkey_patch import ( + replace_peft_model_with_int4_lora_model + ) + replace_peft_model_with_int4_lora_model() + + global train_log_graph + global WANT_INTERRUPT + WANT_INTERRUPT = False + + statistics['loss'] = [] + + statistics['loss'].append({'epoch': 0, 'value': 0}) + zero_pd = pd.DataFrame(statistics['loss']) + + # == Input validation / processing == + yield "Preparing the input...", zero_pd + lora_file_path = clean_path(None, lora_name) + if lora_file_path.strip() == '': + yield "Missing or invalid LoRA file name input.", zero_pd + return + + lora_file_path = f"{Path(shared.args.lora_dir)}/{lora_file_path}" + actual_lr = float(learning_rate) + model_type = type(shared.model).__name__ + + if model_type in MODEL_CLASSES: + model_id = MODEL_CLASSES[model_type] + else: + model_id = "llama" + if model_type == "PeftModelForCausalLM": + if len(shared.lora_names) > 0: + yield "You are trying to train a LoRA while you already have another LoRA loaded. This will work, but may have unexpected effects. *(Will continue anyway in 5 seconds, press `Interrupt` to stop.)*", zero_pd + logger.warning("Training LoRA over top of another LoRA. May have unexpected effects.") + else: + yield "Model ID not matched due to LoRA loading. Consider reloading base model. *(Will continue anyway in 5 seconds, press `Interrupt` to stop.)*", zero_pd + logger.warning("Model ID not matched due to LoRA loading. Consider reloading base model.") + else: + yield "LoRA training has only currently been validated for LLaMA, OPT, GPT-J, and GPT-NeoX models. Unexpected errors may follow. *(Will continue anyway in 5 seconds, press `Interrupt` to stop.)*", zero_pd + logger.warning(f"LoRA training has only currently been validated for LLaMA, OPT, GPT-J, and GPT-NeoX models. (Found model type: {model_type})") + + time.sleep(5) + + if shared.args.loader == 'GPTQ-for-LLaMa' and not shared.args.monkey_patch: + yield "LoRA training with GPTQ-for-LLaMa requires loading with `--monkey-patch`", zero_pd + return + + if cutoff_len <= 0 or micro_batch_size <= 0 or actual_lr <= 0 or lora_rank <= 0 or lora_alpha <= 0: + yield "Cannot input zeroes.", zero_pd + return + + #in new version we dumped this in favor of grad_accumulation + #set it to zero fo new save + batch_size = 0 + + gradient_accumulation_steps = grad_accumulation #batch_size // micro_batch_size + shared.tokenizer.pad_token_id = 0 + shared.tokenizer.padding_side = "left" + + def encode(text, prepend_bos_token): + + result = shared.tokenizer.encode(text, truncation=True, max_length=cutoff_len) + # Check if the first two tokens are BOS + if len(result) >= 2 and result[:2] == [shared.tokenizer.bos_token_id, shared.tokenizer.bos_token_id]: + result = result[1:] + + if not prepend_bos_token and result[0] == shared.tokenizer.bos_token_id: + result = result[1:] + return result + + def tokenize(prompt, append_eos_token=False, prepend_bos_token = False): + + if train_only_after == '' or train_only_after not in prompt: + input_ids = encode(prompt, prepend_bos_token) + + if append_eos_token and input_ids[-1] != shared.tokenizer.eos_token_id and len(input_ids) < cutoff_len: + input_ids.append(shared.tokenizer.eos_token_id) + + input_ids = [shared.tokenizer.pad_token_id] * (cutoff_len - len(input_ids)) + input_ids + + labels = [1] * len(input_ids) + else: + ind = prompt.index(train_only_after) + len(train_only_after) + before_tokens = encode(prompt[:ind], prepend_bos_token) + after_tokens = encode(prompt[ind:], False) + + if append_eos_token and after_tokens[-1] != shared.tokenizer.eos_token_id: + after_tokens.append(shared.tokenizer.eos_token_id) + + full_length = len(after_tokens) + len(before_tokens) + if full_length > cutoff_len: + after_tokens = after_tokens[:cutoff_len - len(before_tokens)] + else: + before_tokens = [shared.tokenizer.pad_token_id] * (cutoff_len - full_length) + before_tokens + + input_ids = before_tokens + after_tokens + labels = [-100] * len(before_tokens) + [1] * len(after_tokens) + + input_ids = torch.tensor(input_ids) + return { + "input_ids": input_ids, + "labels": labels, + "attention_mask": input_ids.ne(shared.tokenizer.pad_token_id), + } + + train_template.clear() + + #reset stuff + print(f"*** LoRA: {lora_name} ***") + non_serialized_params.update({"stop_at_loss": stop_at_loss}) + non_serialized_params.update({"save_steps_under_loss": save_steps_under_loss+0.01}) + non_serialized_params.update({"save_checkpoint_now": False}) + non_serialized_params.update({"training_loop": False}) + non_serialized_params.update({"current_stability": 0}) + non_serialized_params.update({"save_epochs": 0}) + non_serialized_params.update({"checkpoint_offset": 0}) + non_serialized_params.update({"epoch_offset": 0}) + train_log_graph.clear() + + # == Prep the dataset, format, etc == + if raw_text_file not in ['None', '']: + train_template["template_type"] = "raw_text" + logger.info("Loading text file...") + fullpath = clean_path('training/datasets', f'{raw_text_file}') + fullpath = Path(fullpath) + if fullpath.is_dir(): + logger.info('Training path directory {}'.format(raw_text_file)) + raw_text = "" + file_paths = sorted(fullpath.glob('*.txt'), key=lambda path: natural_keys(path.name)) + for file_path in file_paths: + if file_path.is_file(): + with file_path.open('r', encoding='utf-8') as file: + raw_text += file.read().replace('\r', '') + + logger.info(f"Loaded training file: {file_path.name}") + else: + with open(clean_path('training/datasets', f'{raw_text_file}.txt'), 'r', encoding='utf-8') as file: + raw_text = file.read().replace('\r', '') + + # FPHAM PRECISE SLICING + if min_chars<0: + min_chars = 0 + + add_EOS_to_all = add_eos_token and add_eos_token_type == 'Every Block' + add_EOS_to_HC = add_eos_token and add_eos_token_type != 'Every Block' + + #print (f"add_eos_token {add_eos_token}, add_EOS_to_all {add_EOS_to_all}, add_EOS_to_HC {add_EOS_to_HC}") + + # == New more precise slicing on sentence boundary == + if sliding_window: + text_chunks = sliding_block_cut(raw_text, min_chars, add_EOS_to_HC, cutoff_len, hard_cut_string,non_serialized_params['debug_slicer']) + else: + text_chunks = precise_cut(raw_text, precize_slicing_overlap, min_chars, add_EOS_to_HC, cutoff_len, hard_cut_string,non_serialized_params['debug_slicer']) + + train_data = Dataset.from_list([tokenize(x, add_EOS_to_all, add_bos_token) for x in text_chunks]) + if add_EOS_to_all: + print(f"Added EOS to {len(text_chunks)} blocks") + + print(f"All Data Blocks: {len(text_chunks)}") + + del text_chunks + eval_data = None + else: + if dataset in ['None', '']: + yield "Missing dataset choice input, cannot continue.", zero_pd + return + + if format in ['None', '']: + yield "Missing format choice input, cannot continue.", zero_pd + return + + train_template["template_type"] = "dataset" + + with open(clean_path('training/formats', f'{format}.json'), 'r', encoding='utf-8-sig') as formatFile: + format_data: dict[str, str] = json.load(formatFile) + + # == store training prompt == + for _, value in format_data.items(): + prompt_key = f"template_{len(train_template)}" + train_template[prompt_key] = value + + def generate_prompt(data_point: dict[str, str]): + for options, data in format_data.items(): + if set(options.split(',')) == set(x[0] for x in data_point.items() if (type(x[1]) is str and len(x[1].strip()) > 0)): + for key, val in data_point.items(): + if type(val) is str: + data = data.replace(f'%{key}%', val) + return data + raise RuntimeError(f'Data-point "{data_point}" has no keyset match within format "{list(format_data.keys())}"') + + def generate_and_tokenize_prompt(data_point): + prompt = generate_prompt(data_point) + return tokenize(prompt, add_eos_token, add_bos_token) + + logger.info("Loading JSON datasets...") + data = load_dataset("json", data_files=clean_path('training/datasets', f'{dataset}.json')) + train_data = data['train'].map(generate_and_tokenize_prompt, new_fingerprint='%030x' % random.randrange(16**30)) + + print(f"BOS: {add_bos_token} EOS: {add_eos_token}") + print(f"Data Blocks: {train_data.num_rows}") + + if eval_dataset == 'None': + eval_data = None + else: + eval_data = load_dataset("json", data_files=clean_path('training/datasets', f'{eval_dataset}.json')) + eval_data = eval_data['train'].map(generate_and_tokenize_prompt, new_fingerprint='%030x' % random.randrange(16**30)) + + # == We MUST reload model if it went through any previous training, even failed one == + if shared.model_dirty_from_training: + selected_model = shared.model_name + if selected_model: + print("\033[1;31;1m(Model has been modified by previous training, it needs to be reloaded...)\033[0;37;0m") + try: + yield f"Reloading {selected_model}...", zero_pd + reload_model() + shared.tokenizer.pad_token_id = 0 + shared.tokenizer.padding_side = "left" + + if shared.model is not None: + print("Model reloaded OK, continue with training.") + else: + return f"Failed to load {selected_model}." + except: + exc = traceback.format_exc() + logger.error('Failed to reload the model.') + print(exc) + return exc.replace('\n', '\n\n') + + # == Start prepping the model itself == + if not hasattr(shared.model, 'lm_head') or hasattr(shared.model.lm_head, 'weight'): + logger.info("Getting model ready...") + # here we can disable gradient checkpoint, by default = true, use_gradient_checkpointing=True + prepare_model_for_kbit_training(shared.model) + + # base model is now frozen and should not be reused for any other LoRA training than this one + shared.model_dirty_from_training = True + print(f"Transformers Model Type: {YELLOW}{model_type}{RESET}") + + if training_projection==train_choices[0]: + model_to_lora_modules[model_id] = ["gate_proj","down_proj","up_proj","q_proj","k_proj","v_proj","o_proj"] + elif training_projection==train_choices[1]: + model_to_lora_modules[model_id] = ["q_proj","k_proj", "v_proj", "o_proj"] + elif training_projection==train_choices[2]: + model_to_lora_modules[model_id] = ["q_proj","k_proj", "v_proj"] + elif training_projection==train_choices[3]: + model_to_lora_modules[model_id] = ["k_proj", "v_proj", "down_proj"] + else: + model_to_lora_modules[model_id] = ["q_proj", "v_proj"] + + + logger.info("Preparing for training...") + config = LoraConfig( + r=lora_rank, + lora_alpha=lora_alpha, + target_modules=model_to_lora_modules[model_id], + lora_dropout=lora_dropout, + bias="none", + task_type="CAUSAL_LM" + ) + + # == Backup the existing adapter == + if not always_override: + backup_adapter(lora_file_path) + + # == get model trainable params + model_trainable_params, model_all_params = calc_trainable_parameters(shared.model) + + try: + logger.info("Creating LoRA model...") + lora_model = get_peft_model(shared.model, config) + if not always_override and Path(f"{lora_file_path}/adapter_model.bin").is_file(): + logger.info("Loading existing LoRA data...") + state_dict_peft = torch.load(f"{lora_file_path}/adapter_model.bin") + set_peft_model_state_dict(lora_model, state_dict_peft) + + print(f" + Continue Training on {RED}{lora_file_path}/adapter_model.bin{RESET}") + + #load training_log.json if exist + + if Path(f"{lora_file_path}/training_log.json").is_file(): + with open(f"{lora_file_path}/training_log.json", 'r') as json_file: + json_ilog = json.load(json_file) + for key, value in json_ilog.items(): + if key=='current_steps': + non_serialized_params.update({"checkpoint_offset": int(value+1)}) + print(f" + Checkpoints will be saved with offset: {RED}{non_serialized_params['checkpoint_offset']}{RESET}") + if key=='epoch': + non_serialized_params.update({"epoch_offset": value}) + print(f" + Epoch offset: {RED}{non_serialized_params['epoch_offset']}{RESET}") + + + if Path(f"{lora_file_path}/training_graph.json").is_file(): + try: + with open(f"{lora_file_path}/training_graph.json", 'r') as json_file: + train_log_graph = json.load(json_file) + print(" + Training Graph loaded") + except: + print(f"Can't read training_graph") + + + except: + yield traceback.format_exc().replace('\n', '\n\n'), zero_pd + return + + if shared.args.monkey_patch: + from alpaca_lora_4bit.autograd_4bit import Autograd4bitQuantLinear + from alpaca_lora_4bit.models import Linear4bitLt + for _, m in lora_model.named_modules(): + if isinstance(m, Autograd4bitQuantLinear) or isinstance(m, Linear4bitLt): + if m.is_v1_model: + m.zeros = m.zeros.half() + m.scales = m.scales.half() + + class Tracked(): + def __init__(self): + self.current_steps = 0 + self.max_steps = 0 + self.did_save = False + + tracked = Tracked() + actual_save_steps = math.ceil(save_steps / gradient_accumulation_steps) + + class Callbacks(transformers.TrainerCallback): + def on_step_begin(self, args: transformers.TrainingArguments, state: transformers.TrainerState, control: transformers.TrainerControl, **kwargs): + tracked.current_steps = state.global_step * gradient_accumulation_steps + tracked.max_steps = state.max_steps * gradient_accumulation_steps + ssteps10 = int(max(2,(state.max_steps/epochs)*0.1)) + + if WANT_INTERRUPT: + control.should_epoch_stop = True + control.should_training_stop = True + else: + current_loss = float(train_log.get('loss', 0.0)) + current_epoch_int = int(float(train_log.get('epoch', 0.0))) + + force_save = False + + current_steps_offset = tracked.current_steps + non_serialized_params['checkpoint_offset'] + + folder_save = f"checkpoint-{current_steps_offset}" + + # save if triggered by user + if non_serialized_params['save_checkpoint_now']: + force_save = True + non_serialized_params.update({"save_checkpoint_now": False}) + print(f"\033[1;31;1mSave Checkpoint manually trigerred.\033[0;37;0m") + folder_save = f"checkpoint-{current_steps_offset}-user" + + patience = 3 # Set the number of consecutive steps for tracking stability + + if gradient_accumulation_steps==1: + patience = 4 + + min_steps = ssteps10 + + # Save each time the loss is below the threshold + if current_loss < non_serialized_params['save_steps_under_loss'] and current_loss > 0 and state.global_step > min_steps: + current_stability = non_serialized_params['current_stability'] + current_stability += 1 + non_serialized_params.update({"current_stability": current_stability}) + + if current_stability >= patience: + current_stability = 0 + non_serialized_params.update({"current_stability": current_stability}) + current_loss_dec = round(current_loss, 2) + loss_str = f"{current_loss_dec:.2f}" + loss_str = loss_str.replace('.', '_') + new_save = (current_loss_dec-0.1) + 0.01 + non_serialized_params.update({"save_steps_under_loss": new_save}) + + folder_save = f"checkpoint-{current_steps_offset}-loss-{loss_str}" + force_save = True + + + else: + # Reset stability if the loss goes above the threshold + non_serialized_params.update({"current_stability": 0}) + + # Save full epochs + if actual_save_steps>0 and current_epoch_int > non_serialized_params['save_epochs'] and state.global_step > min_steps: + + + current_epoch_offset = current_epoch_int + + if non_serialized_params['epoch_offset'] > 0: + current_epoch_offset = current_epoch_int + round(non_serialized_params['epoch_offset'], 2) + + ep_off_str = f"{current_epoch_offset}" + ep_off_str = ep_off_str.replace('.', '_') + folder_save = f"checkpoint-{current_steps_offset}-epoch-{ep_off_str}" + + non_serialized_params.update({"save_epochs": current_epoch_int}) + force_save = True + + # save each actual_save_steps + if state.global_step > 0 and actual_save_steps > 0 and state.global_step % actual_save_steps == 0: + folder_save = f"checkpoint-{current_steps_offset}" + force_save = True + + if force_save: + lora_model.save_pretrained(f"{lora_file_path}/{folder_save}/", safe_serialization = non_serialized_params['safe_serialization']) + print(f"\033[1;30;40mStep: {tracked.current_steps:6} \033[0;37;0m Saved: [{folder_save}]") + # Save log + with open(f"{lora_file_path}/{folder_save}/training_log.json", 'w', encoding='utf-8') as file: + json.dump(train_log, file, indent=2) + # == Save training prompt == + with open(f"{lora_file_path}/{folder_save}/training_prompt.json", 'w', encoding='utf-8') as file: + json.dump(train_template, file, indent=2) + + + def on_substep_end(self, args: transformers.TrainingArguments, state: transformers.TrainerState, control: transformers.TrainerControl, **kwargs): + tracked.current_steps += 1 + if WANT_INTERRUPT: + control.should_epoch_stop = True + control.should_training_stop = True + + def on_log(self, args: transformers.TrainingArguments, state: transformers.TrainerState, control: transformers.TrainerControl, logs, **kwargs): + train_log.update(logs) + + current_steps_offset = tracked.current_steps + non_serialized_params['checkpoint_offset'] + current_epoch_offset = train_log.get('epoch', 0.0) + non_serialized_params['epoch_offset'] + + train_log.update({"current_steps": tracked.current_steps}) + train_log.update({"current_steps_adjusted": current_steps_offset}) + train_log.update({"epoch_adjusted": current_epoch_offset}) + + if WANT_INTERRUPT: + print("\033[1;31;1mInterrupted by user\033[0;37;0m") + + if non_serialized_params['checkpoint_offset']>0: + print(f"\033[1;30;40mStep: {tracked.current_steps:6} [+{non_serialized_params['checkpoint_offset']}] \033[0;37;0m", end='') + else: + print(f"\033[1;30;40mStep: {tracked.current_steps:6} \033[0;37;0m", end='') + + graphentry = { + 'current_steps': int(train_log.get('current_steps_adjusted',0)), + 'loss': float(train_log.get('loss', 0.0)), + 'learning_rate': float(train_log.get('learning_rate', 0.0)), + 'epoch': float(train_log.get('epoch_adjusted', 0.0)) + } + + cur_loss = float(train_log.get('loss', 0.0)) + cur_lr = float(train_log.get('learning_rate', 0.0)) + cur_epoch = float(train_log.get('epoch', 0.0)) + + if len(statistics['loss']) == 1: + first_epoch = statistics['loss'][0]['epoch'] + first_value = statistics['loss'][0]['value'] + if first_value ==0: + statistics['loss'] = [] + + + statistics['loss'].append({'epoch': cur_epoch, 'value': cur_loss}) + statistics['lr'].append({'epoch': cur_epoch, 'value': cur_lr}) + + # Add the entry to the continuous log + train_log_graph.append(graphentry) + + # Save the graph log for now, we can later generate full graph + with open(f"{lora_file_path}/training_graph.json", 'w') as file: + json.dump(train_log_graph, file, indent=4) + + if 'loss' in logs: + loss = float(logs['loss']) + if loss <= stop_at_loss: + control.should_epoch_stop = True + control.should_training_stop = True + print(f"{RED}Stop Loss {stop_at_loss} reached.{RESET}") + + # FPHAM SAMPLE REQ Transformers error handling + gradient_accumulation_max = int(train_data.num_rows)//micro_batch_size + + if gradient_accumulation_max < gradient_accumulation_steps: + print(f"{RED}WARNING:{RESET} Current gradient accumulation is {RED}too high{RESET} for the amount of training data.") + print(f"Gradient accumulation: {gradient_accumulation_steps} should be less than: {gradient_accumulation_max}. {RED}This could crash Accelerate/Transformers{RESET}") + #min_batchSize = sample_req*micro_batch_size + print(f"Preferable fix: {RED}Increase the size of dataset{RESET}") + print(f"... or Decrerase Gradient Accumulation {RED}{gradient_accumulation_steps}{RESET} to below {GREEN}{gradient_accumulation_max}{RESET}") + gradient_accumulation_steps = max(1,gradient_accumulation_max-1) + print(f"Last resort fix for this run: Lowering Gradient accumulation to {GREEN}{gradient_accumulation_steps}{RESET} [Good luck]") + + else: + print(f"Data Size Check: Gradient accumulation: {YELLOW}{gradient_accumulation_steps}{RESET} <= Blocks/Batch {gradient_accumulation_max} ... {GREEN}[OK]{RESET}") + + #END OF FPHAM SAMPLE REQ + + # FPHAM Custom Scheduler == + custom_scheduller = False + lr_scheduler_type_arg = lr_scheduler_type + + if lr_scheduler_type == 'FP_low_epoch_annealing': + custom_scheduller = True + lr_scheduler_type_arg = 'cosine' + elif lr_scheduler_type == 'FP_half_time_annealing': + custom_scheduller = True + lr_scheduler_type_arg = 'constant' + elif lr_scheduler_type =='FP_raise_fall_creative': + custom_scheduller = True + lr_scheduler_type_arg = 'constant_with_warmup' + + #gradient_checkpointing=True + + args=transformers.TrainingArguments( + report_to=report_to if report_to != "None" else None, + per_device_train_batch_size=micro_batch_size, + gradient_accumulation_steps=gradient_accumulation_steps, + warmup_steps=math.ceil(warmup_steps / gradient_accumulation_steps), + warmup_ratio = warmup_ratio, + num_train_epochs=epochs, + learning_rate=actual_lr, + fp16=False if shared.args.cpu else True, + optim=optimizer, + logging_steps=1, + evaluation_strategy="steps" if eval_data is not None else "no", + eval_steps=math.ceil(eval_steps / gradient_accumulation_steps) if eval_data is not None else None, + save_strategy="steps" if eval_data is not None else "no", + output_dir=lora_file_path, + lr_scheduler_type=lr_scheduler_type_arg, + load_best_model_at_end=eval_data is not None, + # TODO: Enable multi-device support + ddp_find_unused_parameters=None, + no_cuda=shared.args.cpu, + ) + + if custom_scheduller: + trainer = FPSchedulerTrainer( + neftune_noise_alpha=neft_noise_alpha, + model=lora_model, + train_dataset=train_data, + eval_dataset=eval_data, + args=args, + data_collator=transformers.DataCollatorForLanguageModeling(shared.tokenizer, mlm=False), + callbacks=list([Callbacks()]) + ) + elif neft_noise_alpha > 0: + trainer = FPNEFtuneTrainer( + neftune_noise_alpha=neft_noise_alpha, + model=lora_model, + train_dataset=train_data, + eval_dataset=eval_data, + args=args, + data_collator=transformers.DataCollatorForLanguageModeling(shared.tokenizer, mlm=False), + callbacks=list([Callbacks()]) + ) + else: + trainer = transformers.Trainer( + model=lora_model, + train_dataset=train_data, + eval_dataset=eval_data, + args=args, + data_collator=transformers.DataCollatorForLanguageModeling(shared.tokenizer, mlm=False), + callbacks=list([Callbacks()]) + ) + + # END OF FPHAM CUSTOM SCHEDULER + + lora_model.config.use_cache = False + + if torch.__version__ >= "2" and sys.platform != "win32": + lora_model = torch.compile(lora_model) + + # == Save parameters for reuse == + with open(f"{lora_file_path}/training_parameters.json", 'w', encoding='utf-8') as file: + vars = locals() + json.dump({x: vars[x] for x in PARAMETERS}, file, indent=2) + + # == Save training prompt == + with open(f"{lora_file_path}/training_prompt.json", 'w', encoding='utf-8') as file: + json.dump(train_template, file, indent=2) + + # == Main run and monitor loop == + logger.info("Starting training...") + yield "Starting...", zero_pd + + lora_trainable_param, lora_all_param = calc_trainable_parameters(lora_model) + + projections_string = ", ".join([projection.replace("_proj", "") for projection in model_to_lora_modules[model_id]]) + + print(f"Training '{model_id}' model using {YELLOW}({projections_string}){RESET} projections") + + if lora_all_param > 0: + print(f"Trainable params: {lora_trainable_param:,d} ({RED}{100 * lora_trainable_param / lora_all_param:.4f} %{RESET}), All params: {lora_all_param:,d} (Model: {model_all_params:,d})") + + train_log.update({"base_model_name": shared.model_name}) + train_log.update({"base_model_class": shared.model.__class__.__name__}) + train_log.update({"base_loaded_in_4bit": getattr(lora_model, "is_loaded_in_4bit", False)}) + train_log.update({"base_loaded_in_8bit": getattr(lora_model, "is_loaded_in_8bit", False)}) + train_log.update({"projections": projections_string}) + if non_serialized_params['checkpoint_offset'] > 0: + train_log.update({"last_run_steps_offset": non_serialized_params['checkpoint_offset']}) + train_log.update({"last_run_epoch_offset": non_serialized_params['epoch_offset']}) + + + if non_serialized_params['checkpoint_offset'] > 0: + print(f"Continue training on {RED}previous adapter{RESET} from epoch: {RED}{non_serialized_params['epoch_offset']}{RESET}") + + if stop_at_loss > 0: + print(f"Monitoring loss {RED}(Auto-Stop at: {stop_at_loss}){RESET}") + + + + if WANT_INTERRUPT: + yield "Interrupted before start.", zero_pd + return + + def log_train_dataset(trainer): + decoded_entries = [] + # Try to decode the entries and write the log file + try: + # Iterate over the first 10 elements in the dataset (or fewer if there are less than 10) + for i in range(min(10, len(trainer.train_dataset))): + decoded_text = shared.tokenizer.decode(trainer.train_dataset[i]['input_ids']) + decoded_entries.append({"value": decoded_text}) + + # Write the log file + Path('logs').mkdir(exist_ok=True) + with open(Path('logs/train_dataset_sample.json'), 'w') as json_file: + json.dump(decoded_entries, json_file, indent=4) + + logger.info("Log file 'train_dataset_sample.json' created in the 'logs' directory.") + except Exception as e: + logger.error(f"Failed to create log file due to error: {e}") + + def threaded_run(): + log_train_dataset(trainer) + trainer.train() + # Note: save in the thread in case the gradio thread breaks (eg browser closed) + lora_model.save_pretrained(lora_file_path, safe_serialization = non_serialized_params['safe_serialization']) + logger.info("LoRA training run is completed and saved.") + # Save log + with open(f"{lora_file_path}/training_log.json", 'w', encoding='utf-8') as file: + json.dump(train_log, file, indent=2) + + thread = threading.Thread(target=threaded_run) + thread.start() + last_step = 0 + start_time = time.perf_counter() + + while thread.is_alive(): + time.sleep(0.5) + + if statistics['loss']: + max_value_dict = max(statistics['loss'], key=lambda x: x['value']) + max_value = max_value_dict['value']+0.4 + first_epoch = statistics['loss'][0]['epoch'] + last_epoch = statistics['loss'][-1]['epoch'] + else: + max_value = 3.5 + last_epoch = 0 + first_epoch = 0 + + if WANT_INTERRUPT: + + losses = gr.LinePlot.update( + value = pd.DataFrame(statistics['loss']), + x="epoch", y="value", + title="Loss Metrics", + overlay_point=True, tooltip=["epoch", "value"], + x_lim=[first_epoch,last_epoch], y_lim=[0,max_value], + width=500, height=250 ) + + yield "Interrupting, please wait... *(Run will stop after the current training step completes.)*", losses + + elif tracked.current_steps != last_step: + last_step = tracked.current_steps + time_elapsed = time.perf_counter() - start_time + lastloss = float(train_log.get('loss', 0.0)) + + non_serialized_params.update({"training_loop": True}) + + if lastloss > 0: + lastloss_str = f", ... Current Loss: `{lastloss:.2f}`" + else: + lastloss_str = "" + + if time_elapsed <= 0: + timer_info = "" + total_time_estimate = 999 + else: + its = tracked.current_steps / time_elapsed + if its > 1: + timer_info = f"`{its:.2f}` it/s" + else: + timer_info = f"`{1.0/its:.2f}` s/it" + + total_time_estimate = (1.0 / its) * (tracked.max_steps) + + if stop_at_loss != non_serialized_params['stop_at_loss']: + stop_at_loss = non_serialized_params['stop_at_loss'] + print(f"Stop at loss changed {RED}(Auto-Stop at: {stop_at_loss}){RESET}") + + losses = gr.LinePlot.update( + value = pd.DataFrame(statistics['loss']), + x="epoch", y="value", + title="Loss Metrics", + overlay_point=True, tooltip=["epoch", "value"], + x_lim=[first_epoch,last_epoch], y_lim=[0,max_value], + width=500, height=250 ) + + + yield f"Running... **{tracked.current_steps}** / **{tracked.max_steps}** ... {timer_info}, {format_time(time_elapsed)} / {format_time(total_time_estimate)} ... {format_time(total_time_estimate - time_elapsed)} remaining {lastloss_str}", losses + + # Saving in the train thread might fail if an error occurs, so save here if so. + + #return_pd = pd.DataFrame(statistics['loss']) + + if statistics['loss']: + max_value_dict = max(statistics['loss'], key=lambda x: x['value']) + max_value = max_value_dict['value']+0.4 + first_epoch = statistics['loss'][0]['epoch'] + last_epoch = statistics['loss'][-1]['epoch'] + else: + max_value = 3.5 + last_epoch = 0 + first_epoch = 0 + + return_pd = gr.LinePlot.update( + value = pd.DataFrame(statistics['loss']), + x="epoch", y="value", + title="Loss Metrics", + overlay_point=True, tooltip=["epoch", "value"], + x_lim=[first_epoch,last_epoch], y_lim=[0,max_value], + width=500, height=250) + + non_serialized_params.update({"training_loop": False}) + + if not tracked.did_save: + logger.info("Training complete, saving...") + lora_model.save_pretrained(lora_file_path, safe_serialization = non_serialized_params['safe_serialization']) + + if WANT_INTERRUPT: + logger.info("Training interrupted.") + yield f"Interrupted by user. LoRA saved to `{lora_file_path}`.", return_pd + else: + logger.info("Training complete!") + yield f"Done! LoRA saved to `{lora_file_path}`.\n\nBefore testing your new LoRA, make sure to first reload the model, as it is currently dirty from training.", return_pd + + create_graph(lora_file_path, lora_name) + +def format_time(seconds: float): + if seconds < 120: + return f"`{seconds:.0f}` seconds" + + minutes = seconds / 60 + if minutes < 120: + return f"`{minutes:.0f}` minutes" + + hours = minutes / 60 + return f"`{hours:.0f}` hours" diff --git a/extensions/Training_PRO/train_utils.py b/extensions/Training_PRO/train_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..b6a3a7cb8b66593fb363dff7fe489cde2ab0ec6f --- /dev/null +++ b/extensions/Training_PRO/train_utils.py @@ -0,0 +1,368 @@ +import os +from modules import shared, utils +from pathlib import Path +import requests +import tqdm +import json + +''' +def get_gpu_memory_usage(rank): + return { + 'total': round(torch.cuda.get_device_properties(rank).total_memory / (1024**3), 2), + 'max': round(torch.cuda.max_memory_allocated(rank) / (1024**3), 2), + 'reserved': round(torch.cuda.memory_reserved(rank) / (1024**3), 2), + 'allocated': round(torch.cuda.memory_allocated(rank) / (1024**3), 2) + } +''' + +def list_subfoldersByTime(directory): + + if not directory.endswith('/'): + directory += '/' + subfolders = [] + subfolders.append('None') + path = directory + name_list = os.listdir(path) + full_list = [os.path.join(path,i) for i in name_list] + time_sorted_list = sorted(full_list, key=os.path.getmtime,reverse=True) + + for entry in time_sorted_list: + if os.path.isdir(entry): + entry_str = f"{entry}" # Convert entry to a string + full_path = entry_str + entry_str = entry_str.replace('\\','/') + entry_str = entry_str.replace(f"{directory}", "") # Remove directory part + subfolders.append(entry_str) + + return subfolders + +def get_available_loras_local(_sortedByTime): + + model_dir = shared.args.lora_dir # Update with the appropriate directory path + subfolders = [] + if _sortedByTime: + subfolders = list_subfoldersByTime(model_dir) + else: + subfolders = utils.get_available_loras() + + return subfolders + + +# FPHAM SPLIT BY SENTENCE BLOCK =============== + +def split_sentences(text: str, cutoff_len: int): + sentences = [] + sentence = '' + delimiters = ['. ', '? ', '! ', '... ', '.\n', '?\n', '!\n','...\n','',''] + abbreviations = ['Mr. ', 'Mrs. ', 'Dr. ', 'Ms. ', 'St. ', 'Prof. ', 'Jr. ', 'Ltd. ', 'Capt. ', 'Col. ', 'Gen. ', 'Ave. ', 'Blvd. ', 'Co. ', 'Corp. ', 'Dept. ', 'Est. ', 'Gov. ', 'Inc. ', 'Ph.D. ', 'Univ. '] + errors = 0 + max_cut = cutoff_len-1 + prev_char = '' + + for char in text: + sentence += char + + + if (any(sentence.endswith(delimiter) for delimiter in delimiters) and + not (prev_char.isupper() and len(sentence) >= 3 and sentence[-3] != ' ') and + not any(sentence.endswith(abbreviation) for abbreviation in abbreviations)): + tokens = shared.tokenizer.encode(sentence) + + if len(tokens) > max_cut: + tokens = tokens[:max_cut] + sentence = shared.tokenizer.decode(tokens, skip_special_tokens=True) + errors = errors + 1 + + sentences.append({'text': sentence, 'size': len(tokens)}) + + sentence = '' + + prev_char = char + + if sentence: + tokens = shared.tokenizer.encode(sentence) + if len(tokens) > max_cut: + tokens = tokens[:max_cut] + sentence = shared.tokenizer.decode(tokens, skip_special_tokens=True) + errors = errors + 1 + + sentences.append({'text': sentence, 'size': len(tokens)}) + + if errors > 0: + print(f"Trimmed sentences beyond Cutoff Length: {errors}") + + return sentences + +# The goal of following code is to create blocks of text + overlapping blocks while: +# respects sentence boundaries +# always uses all the text +# hard cut defined by hard_cut_string or will always end at the end of data block +# no overlapping blocks will be created across hard cut or across token + +def precise_cut(text: str, overlap: bool, min_chars_cut: int, eos_to_hc: bool, cutoff_len: int, hard_cut_string: str, debug_slicer:bool): + + EOSX_str = '' #hardcut placeholder + EOS_str = '' + print("Precise raw text slicer: ON") + + cut_string = hard_cut_string.replace('\\n', '\n') + text = text.replace(cut_string, EOSX_str) + sentences = split_sentences(text, cutoff_len) + + print(f"Sentences: {len(sentences)}") + sentencelist = [] + currentSentence = '' + totalLength = 0 + max_cut = cutoff_len-1 + half_cut = cutoff_len//2 + halfcut_length = 0 + + edgeindex = [] + half_index = 0 + + for index, item in enumerate(sentences): + + if halfcut_length+ item['size'] < half_cut: + halfcut_length += item['size'] + half_index = index + else: + edgeindex.append(half_index) + halfcut_length = -2 * max_cut + + + if totalLength + item['size'] < max_cut and not currentSentence.endswith(EOSX_str): + currentSentence += item['text'] + totalLength += item['size'] + else: + + if len(currentSentence.strip()) > min_chars_cut: + sentencelist.append(currentSentence.strip()) + + currentSentence = item['text'] + totalLength = item['size'] + halfcut_length = item['size'] + + if len(currentSentence.strip()) > min_chars_cut: + sentencelist.append(currentSentence.strip()) + + unique_blocks = len(sentencelist) + print(f"Text Blocks: {unique_blocks}") + + #overlap strategies: + # don't overlap across HARD CUT (EOSX) + if overlap: + for edge_idx in edgeindex: + currentSentence = '' + totalLength = 0 + + for item in sentences[edge_idx:]: + if totalLength + item['size'] < max_cut: + currentSentence += item['text'] + totalLength += item['size'] + else: + #if by chance EOSX is at the end then it's acceptable + if currentSentence.endswith(EOSX_str) and len(currentSentence.strip()) > min_chars_cut: + sentencelist.append(currentSentence.strip()) + # otherwise don't cross hard cut + elif EOSX_str not in currentSentence and len(currentSentence.strip()) > min_chars_cut: + sentencelist.append(currentSentence.strip()) + + currentSentence = '' + totalLength = 0 + break + + print(f"+ Overlapping blocks: {len(sentencelist)-unique_blocks}") + + num_EOS = 0 + for i in range(len(sentencelist)): + if eos_to_hc: + sentencelist[i] = sentencelist[i].replace(EOSX_str, EOS_str) + else: + sentencelist[i] = sentencelist[i].replace(EOSX_str, '') + + #someone may have had stop strings in the raw text... + sentencelist[i] = sentencelist[i].replace("", EOS_str) + num_EOS += sentencelist[i].count(EOS_str) + + if num_EOS > 0: + print(f"+ EOS count: {num_EOS}") + + #final check for useless lines + sentencelist = [item for item in sentencelist if item.strip() != ""] + sentencelist = [item for item in sentencelist if item.strip() != ""] + + + if debug_slicer: + # Write the log file + Path('logs').mkdir(exist_ok=True) + sentencelist_dict = {index: sentence for index, sentence in enumerate(sentencelist)} + output_file = "logs/sentencelist.json" + with open(output_file, 'w') as f: + json.dump(sentencelist_dict, f,indent=2) + + print("Saved sentencelist.json in logs folder") + + return sentencelist + + +def sliding_block_cut(text: str, min_chars_cut: int, eos_to_hc: bool, cutoff_len: int, hard_cut_string: str, debug_slicer:bool): + + EOSX_str = '' #hardcut placeholder + EOS_str = '' + print("Mega Block Overlap: ON") + + cut_string = hard_cut_string.replace('\\n', '\n') + text = text.replace(cut_string, EOSX_str) + sentences = split_sentences(text, cutoff_len) + + print(f"Sentences: {len(sentences)}") + sentencelist = [] + + max_cut = cutoff_len-1 + + #print(f"max_cut: {max_cut}") + advancing_to = 0 + + prev_block_lastsentence = "" + + + for i in range(len(sentences)): + totalLength = 0 + currentSentence = '' + lastsentence = "" + + if i >= advancing_to: + for k in range(i, len(sentences)): + + current_length = sentences[k]['size'] + + if totalLength + current_length <= max_cut and not currentSentence.endswith(EOSX_str): + currentSentence += sentences[k]['text'] + totalLength += current_length + lastsentence = sentences[k]['text'] + else: + if len(currentSentence.strip()) > min_chars_cut: + if prev_block_lastsentence!=lastsentence: + sentencelist.append(currentSentence.strip()) + prev_block_lastsentence = lastsentence + + advancing_to = 0 + if currentSentence.endswith(EOSX_str): + advancing_to = k + + currentSentence = "" + totalLength = 0 + break + + if currentSentence != "": + if len(currentSentence.strip()) > min_chars_cut: + sentencelist.append(currentSentence.strip()) + + unique_blocks = len(sentencelist) + print(f"Text Blocks: {unique_blocks}") + num_EOS = 0 + for i in range(len(sentencelist)): + if eos_to_hc: + sentencelist[i] = sentencelist[i].replace(EOSX_str, EOS_str) + else: + sentencelist[i] = sentencelist[i].replace(EOSX_str, '') + + #someone may have had stop strings in the raw text... + sentencelist[i] = sentencelist[i].replace("", EOS_str) + num_EOS += sentencelist[i].count(EOS_str) + + if num_EOS > 0: + print(f"+ EOS count: {num_EOS}") + + #final check for useless lines + sentencelist = [item for item in sentencelist if item.strip() != ""] + sentencelist = [item for item in sentencelist if item.strip() != ""] + + + if debug_slicer: + # Write the log file + Path('logs').mkdir(exist_ok=True) + sentencelist_dict = {index: sentence for index, sentence in enumerate(sentencelist)} + output_file = "logs/sentencelist.json" + with open(output_file, 'w') as f: + json.dump(sentencelist_dict, f,indent=2) + + print("Saved sentencelist.json in logs folder") + + return sentencelist + +# Example usage: +# download_file_from_url('https://example.com/path/to/your/file.ext', '/output/directory') + +def download_file_from_url(url, overwrite, output_dir_in, valid_extensions = {'.txt', '.json'}): + try: + # Validate and sanitize the URL + #parsed_url = urllib.parse.urlparse(url) + #if not parsed_url.netloc: + # raise ValueError("Invalid URL") + #filename = os.path.basename(parsed_url.path) + + # Get the filename from the URL + + session = requests.Session() + headers = {} + mode = 'wb' + filename = url.split('/')[-1] + + output_dir = str(output_dir_in) + # Construct the full path to the output file + local_filename = os.path.join(output_dir, filename) + + # Check if the local file already exists + overw = '' + if os.path.exists(local_filename): + if not overwrite: + yield f"File '{local_filename}' already exists. Aborting." + return + else: + overw = ' [Overwrite existing]' + + filename_lower = filename.lower() + + # Send an HTTP GET request to the URL with a timeout + file_extension = os.path.splitext(filename_lower)[-1] + + if file_extension not in valid_extensions: + yield f"Invalid file extension: {file_extension}. Only {valid_extensions} files are supported." + return + + with session.get(url, stream=True, headers=headers, timeout=10) as r: + r.raise_for_status() + # total size can be wildly inaccurate + #total_size = int(r.headers.get('content-length', 0)) + + block_size = 1024 * 4 + with open(local_filename, mode) as f: + count = 0 + for data in r.iter_content(block_size): + f.write(data) + count += len(data) + + yield f"Downloaded: {count} " + overw + + # Verify file size if possible + if os.path.exists(local_filename): + downloaded_size = os.path.getsize(local_filename) + if downloaded_size > 0: + yield f"File '{filename}' downloaded to '{output_dir}' ({downloaded_size} bytes)." + print("File Downloaded") + else: + print("Downloaded file is zero") + yield f"Failed. Downloaded file size is zero)." + else: + print(f"Error: {local_filename} failed to download.") + yield f"Error: {local_filename} failed to download" + + except Exception as e: + print(f"An error occurred: {e}") + yield f"An error occurred: {e}" + + finally: + # Close the session to release resources + session.close() + diff --git a/extensions/character_bias/script.py b/extensions/character_bias/script.py new file mode 100644 index 0000000000000000000000000000000000000000..94ab491456587fbbf34907b03c2faa5b100b3f51 --- /dev/null +++ b/extensions/character_bias/script.py @@ -0,0 +1,74 @@ +import os + +import gradio as gr + +# get the current directory of the script +current_dir = os.path.dirname(os.path.abspath(__file__)) + +# check if the bias_options.txt file exists, if not, create it +bias_file = os.path.join(current_dir, "bias_options.txt") +if not os.path.isfile(bias_file): + with open(bias_file, "w") as f: + f.write("*I am so happy*\n*I am so sad*\n*I am so excited*\n*I am so bored*\n*I am so angry*") + +# read bias options from the text file +with open(bias_file, "r") as f: + bias_options = [line.strip() for line in f.readlines()] + +params = { + "activate": True, + "bias string": " *I am so happy*", + "custom string": "", +} + + +def input_modifier(string): + """ + This function is applied to your text inputs before + they are fed into the model. + """ + return string + + +def output_modifier(string): + """ + This function is applied to the model outputs. + """ + return string + + +def bot_prefix_modifier(string): + """ + This function is only applied in chat mode. It modifies + the prefix text for the Bot and can be used to bias its + behavior. + """ + if params['activate']: + if params['custom string'].strip() != '': + return f'{string} {params["custom string"].strip()} ' + else: + return f'{string} {params["bias string"].strip()} ' + else: + return string + + +def ui(): + # Gradio elements + activate = gr.Checkbox(value=params['activate'], label='Activate character bias') + dropdown_string = gr.Dropdown(choices=bias_options, value=params["bias string"], label='Character bias', info='To edit the options in this dropdown edit the "bias_options.txt" file') + custom_string = gr.Textbox(value=params['custom string'], placeholder="Enter custom bias string", label="Custom Character Bias", info='If not empty, will be used instead of the value above') + + # Event functions to update the parameters in the backend + def update_bias_string(x): + if x: + params.update({"bias string": x}) + else: + params.update({"bias string": dropdown_string.get()}) + return x + + def update_custom_string(x): + params.update({"custom string": x}) + + dropdown_string.change(update_bias_string, dropdown_string, None) + custom_string.change(update_custom_string, custom_string, None) + activate.change(lambda x: params.update({"activate": x}), activate, None) diff --git a/extensions/coqui_tts/harvard_sentences.txt b/extensions/coqui_tts/harvard_sentences.txt new file mode 100644 index 0000000000000000000000000000000000000000..958d7f3cb28d8e2ea215ee416068b2f672d9e9d1 --- /dev/null +++ b/extensions/coqui_tts/harvard_sentences.txt @@ -0,0 +1,720 @@ +The birch canoe slid on the smooth planks. +Glue the sheet to the dark blue background. +It's easy to tell the depth of a well. +These days a chicken leg is a rare dish. +Rice is often served in round bowls. +The juice of lemons makes fine punch. +The box was thrown beside the parked truck. +The hogs were fed chopped corn and garbage. +Four hours of steady work faced us. +A large size in stockings is hard to sell. +The boy was there when the sun rose. +A rod is used to catch pink salmon. +The source of the huge river is the clear spring. +Kick the ball straight and follow through. +Help the woman get back to her feet. +A pot of tea helps to pass the evening. +Smoky fires lack flame and heat. +The soft cushion broke the man's fall. +The salt breeze came across from the sea. +The girl at the booth sold fifty bonds. +The small pup gnawed a hole in the sock. +The fish twisted and turned on the bent hook. +Press the pants and sew a button on the vest. +The swan dive was far short of perfect. +The beauty of the view stunned the young boy. +Two blue fish swam in the tank. +Her purse was full of useless trash. +The colt reared and threw the tall rider. +It snowed, rained, and hailed the same morning. +Read verse out loud for pleasure. +Hoist the load to your left shoulder. +Take the winding path to reach the lake. +Note closely the size of the gas tank. +Wipe the grease off his dirty face. +Mend the coat before you go out. +The wrist was badly strained and hung limp. +The stray cat gave birth to kittens. +The young girl gave no clear response. +The meal was cooked before the bell rang. +What joy there is in living. +A king ruled the state in the early days. +The ship was torn apart on the sharp reef. +Sickness kept him home the third week. +The wide road shimmered in the hot sun. +The lazy cow lay in the cool grass. +Lift the square stone over the fence. +The rope will bind the seven books at once. +Hop over the fence and plunge in. +The friendly gang left the drug store. +Mesh wire keeps chicks inside. +The frosty air passed through the coat. +The crooked maze failed to fool the mouse. +Adding fast leads to wrong sums. +The show was a flop from the very start. +A saw is a tool used for making boards. +The wagon moved on well oiled wheels. +March the soldiers past the next hill. +A cup of sugar makes sweet fudge. +Place a rosebush near the porch steps. +Both lost their lives in the raging storm. +We talked of the side show in the circus. +Use a pencil to write the first draft. +He ran half way to the hardware store. +The clock struck to mark the third period. +A small creek cut across the field. +Cars and busses stalled in snow drifts. +The set of china hit the floor with a crash. +This is a grand season for hikes on the road. +The dune rose from the edge of the water. +Those words were the cue for the actor to leave. +A yacht slid around the point into the bay. +The two met while playing on the sand. +The ink stain dried on the finished page. +The walled town was seized without a fight. +The lease ran out in sixteen weeks. +A tame squirrel makes a nice pet. +The horn of the car woke the sleeping cop. +The heart beat strongly and with firm strokes. +The pearl was worn in a thin silver ring. +The fruit peel was cut in thick slices. +The Navy attacked the big task force. +See the cat glaring at the scared mouse. +There are more than two factors here. +The hat brim was wide and too droopy. +The lawyer tried to lose his case. +The grass curled around the fence post. +Cut the pie into large parts. +Men strive but seldom get rich. +Always close the barn door tight. +He lay prone and hardly moved a limb. +The slush lay deep along the street. +A wisp of cloud hung in the blue air. +A pound of sugar costs more than eggs. +The fin was sharp and cut the clear water. +The play seems dull and quite stupid. +Bail the boat to stop it from sinking. +The term ended in late June that year. +A tusk is used to make costly gifts. +Ten pins were set in order. +The bill was paid every third week. +Oak is strong and also gives shade. +Cats and dogs each hate the other. +The pipe began to rust while new. +Open the crate but don't break the glass. +Add the sum to the product of these three. +Thieves who rob friends deserve jail. +The ripe taste of cheese improves with age. +Act on these orders with great speed. +The hog crawled under the high fence. +Move the vat over the hot fire. +The bark of the pine tree was shiny and dark. +Leaves turn brown and yellow in the fall. +The pennant waved when the wind blew. +Split the log with a quick, sharp blow. +Burn peat after the logs give out. +He ordered peach pie with ice cream. +Weave the carpet on the right hand side. +Hemp is a weed found in parts of the tropics. +A lame back kept his score low. +We find joy in the simplest things. +Type out three lists of orders. +The harder he tried the less he got done. +The boss ran the show with a watchful eye. +The cup cracked and spilled its contents. +Paste can cleanse the most dirty brass. +The slang word for raw whiskey is booze. +It caught its hind paw in a rusty trap. +The wharf could be seen at the farther shore. +Feel the heat of the weak dying flame. +The tiny girl took off her hat. +A cramp is no small danger on a swim. +He said the same phrase thirty times. +Pluck the bright rose without leaves. +Two plus seven is less than ten. +The glow deepened in the eyes of the sweet girl. +Bring your problems to the wise chief. +Write a fond note to the friend you cherish. +Clothes and lodging are free to new men. +We frown when events take a bad turn. +Port is a strong wine with a smoky taste. +The young kid jumped the rusty gate. +Guess the results from the first scores. +A salt pickle tastes fine with ham. +The just claim got the right verdict. +These thistles bend in a high wind. +Pure bred poodles have curls. +The tree top waved in a graceful way. +The spot on the blotter was made by green ink. +Mud was spattered on the front of his white shirt. +The cigar burned a hole in the desk top. +The empty flask stood on the tin tray. +A speedy man can beat this track mark. +He broke a new shoelace that day. +The coffee stand is too high for the couch. +The urge to write short stories is rare. +The pencils have all been used. +The pirates seized the crew of the lost ship. +We tried to replace the coin but failed. +She sewed the torn coat quite neatly. +The sofa cushion is red and of light weight. +The jacket hung on the back of the wide chair. +At that high level the air is pure. +Drop the two when you add the figures. +A filing case is now hard to buy. +An abrupt start does not win the prize. +Wood is best for making toys and blocks. +The office paint was a dull, sad tan. +He knew the skill of the great young actress. +A rag will soak up spilled water. +A shower of dirt fell from the hot pipes. +Steam hissed from the broken valve. +The child almost hurt the small dog. +There was a sound of dry leaves outside. +The sky that morning was clear and bright blue. +Torn scraps littered the stone floor. +Sunday is the best part of the week. +The doctor cured him with these pills. +The new girl was fired today at noon. +They felt gay when the ship arrived in port. +Add the store's account to the last cent. +Acid burns holes in wool cloth. +Fairy tales should be fun to write. +Eight miles of woodland burned to waste. +The third act was dull and tired the players. +A young child should not suffer fright. +Add the column and put the sum here. +We admire and love a good cook. +There the flood mark is ten inches. +He carved a head from the round block of marble. +She has a smart way of wearing clothes. +The fruit of a fig tree is apple-shaped. +Corn cobs can be used to kindle a fire. +Where were they when the noise started. +The paper box is full of thumb tacks. +Sell your gift to a buyer at a good gain. +The tongs lay beside the ice pail. +The petals fall with the next puff of wind. +Bring your best compass to the third class. +They could laugh although they were sad. +Farmers came in to thresh the oat crop. +The brown house was on fire to the attic. +The lure is used to catch trout and flounder. +Float the soap on top of the bath water. +A blue crane is a tall wading bird. +A fresh start will work such wonders. +The club rented the rink for the fifth night. +After the dance, they went straight home. +The hostess taught the new maid to serve. +He wrote his last novel there at the inn. +Even the worst will beat his low score. +The cement had dried when he moved it. +The loss of the second ship was hard to take. +The fly made its way along the wall. +Do that with a wooden stick. +Live wires should be kept covered. +The large house had hot water taps. +It is hard to erase blue or red ink. +Write at once or you may forget it. +The doorknob was made of bright clean brass. +The wreck occurred by the bank on Main Street. +A pencil with black lead writes best. +Coax a young calf to drink from a bucket. +Schools for ladies teach charm and grace. +The lamp shone with a steady green flame. +They took the axe and the saw to the forest. +The ancient coin was quite dull and worn. +The shaky barn fell with a loud crash. +Jazz and swing fans like fast music. +Rake the rubbish up and then burn it. +Slash the gold cloth into fine ribbons. +Try to have the court decide the case. +They are pushed back each time they attack. +He broke his ties with groups of former friends. +They floated on the raft to sun their white backs. +The map had an X that meant nothing. +Whitings are small fish caught in nets. +Some ads serve to cheat buyers. +Jerk the rope and the bell rings weakly. +A waxed floor makes us lose balance. +Madam, this is the best brand of corn. +On the islands the sea breeze is soft and mild. +The play began as soon as we sat down. +This will lead the world to more sound and fury. +Add salt before you fry the egg. +The rush for funds reached its peak Tuesday. +The birch looked stark white and lonesome. +The box is held by a bright red snapper. +To make pure ice, you freeze water. +The first worm gets snapped early. +Jump the fence and hurry up the bank. +Yell and clap as the curtain slides back. +They are men who walk the middle of the road. +Both brothers wear the same size. +In some form or other we need fun. +The prince ordered his head chopped off. +The houses are built of red clay bricks. +Ducks fly north but lack a compass. +Fruit flavors are used in fizz drinks. +These pills do less good than others. +Canned pears lack full flavor. +The dark pot hung in the front closet. +Carry the pail to the wall and spill it there. +The train brought our hero to the big town. +We are sure that one war is enough. +Gray paint stretched for miles around. +The rude laugh filled the empty room. +High seats are best for football fans. +Tea served from the brown jug is tasty. +A dash of pepper spoils beef stew. +A zestful food is the hot-cross bun. +The horse trotted around the field at a brisk pace. +Find the twin who stole the pearl necklace. +Cut the cord that binds the box tightly. +The red tape bound the smuggled food. +Look in the corner to find the tan shirt. +The cold drizzle will halt the bond drive. +Nine men were hired to dig the ruins. +The junk yard had a mouldy smell. +The flint sputtered and lit a pine torch. +Soak the cloth and drown the sharp odor. +The shelves were bare of both jam or crackers. +A joy to every child is the swan boat. +All sat frozen and watched the screen. +A cloud of dust stung his tender eyes. +To reach the end he needs much courage. +Shape the clay gently into block form. +A ridge on a smooth surface is a bump or flaw. +Hedge apples may stain your hands green. +Quench your thirst, then eat the crackers. +Tight curls get limp on rainy days. +The mute muffled the high tones of the horn. +The gold ring fits only a pierced ear. +The old pan was covered with hard fudge. +Watch the log float in the wide river. +The node on the stalk of wheat grew daily. +The heap of fallen leaves was set on fire. +Write fast if you want to finish early. +His shirt was clean but one button was gone. +The barrel of beer was a brew of malt and hops. +Tin cans are absent from store shelves. +Slide the box into that empty space. +The plant grew large and green in the window. +The beam dropped down on the workmen's head. +Pink clouds floated with the breeze. +She danced like a swan, tall and graceful. +The tube was blown and the tire flat and useless. +It is late morning on the old wall clock. +Let's all join as we sing the last chorus. +The last switch cannot be turned off. +The fight will end in just six minutes. +The store walls were lined with colored frocks. +The peace league met to discuss their plans. +The rise to fame of a person takes luck. +Paper is scarce, so write with much care. +The quick fox jumped on the sleeping cat. +The nozzle of the fire hose was bright brass. +Screw the round cap on as tight as needed. +Time brings us many changes. +The purple tie was ten years old. +Men think and plan and sometimes act. +Fill the ink jar with sticky glue. +He smoke a big pipe with strong contents. +We need grain to keep our mules healthy. +Pack the records in a neat thin case. +The crunch of feet in the snow was the only sound. +The copper bowl shone in the sun's rays. +Boards will warp unless kept dry. +The plush chair leaned against the wall. +Glass will clink when struck by metal. +Bathe and relax in the cool green grass. +Nine rows of soldiers stood in line. +The beach is dry and shallow at low tide. +The idea is to sew both edges straight. +The kitten chased the dog down the street. +Pages bound in cloth make a book. +Try to trace the fine lines of the painting. +Women form less than half of the group. +The zones merge in the central part of town. +A gem in the rough needs work to polish. +Code is used when secrets are sent. +Most of the news is easy for us to hear. +He used the lathe to make brass objects. +The vane on top of the pole revolved in the wind. +Mince pie is a dish served to children. +The clan gathered on each dull night. +Let it burn, it gives us warmth and comfort. +A castle built from sand fails to endure. +A child's wit saved the day for us. +Tack the strip of carpet to the worn floor. +Next Tuesday we must vote. +Pour the stew from the pot into the plate. +Each penny shone like new. +The man went to the woods to gather sticks. +The dirt piles were lines along the road. +The logs fell and tumbled into the clear stream. +Just hoist it up and take it away. +A ripe plum is fit for a king's palate. +Our plans right now are hazy. +Brass rings are sold by these natives. +It takes a good trap to capture a bear. +Feed the white mouse some flower seeds. +The thaw came early and freed the stream. +He took the lead and kept it the whole distance. +The key you designed will fit the lock. +Plead to the council to free the poor thief. +Better hash is made of rare beef. +This plank was made for walking on. +The lake sparkled in the red hot sun. +He crawled with care along the ledge. +Tend the sheep while the dog wanders. +It takes a lot of help to finish these. +Mark the spot with a sign painted red. +Take two shares as a fair profit. +The fur of cats goes by many names. +North winds bring colds and fevers. +He asks no person to vouch for him. +Go now and come here later. +A sash of gold silk will trim her dress. +Soap can wash most dirt away. +That move means the game is over. +He wrote down a long list of items. +A siege will crack the strong defense. +Grape juice and water mix well. +Roads are paved with sticky tar. +Fake stones shine but cost little. +The drip of the rain made a pleasant sound. +Smoke poured out of every crack. +Serve the hot rum to the tired heroes. +Much of the story makes good sense. +The sun came up to light the eastern sky. +Heave the line over the port side. +A lathe cuts and trims any wood. +It's a dense crowd in two distinct ways. +His hip struck the knee of the next player. +The stale smell of old beer lingers. +The desk was firm on the shaky floor. +It takes heat to bring out the odor. +Beef is scarcer than some lamb. +Raise the sail and steer the ship northward. +A cone costs five cents on Mondays. +A pod is what peas always grow in. +Jerk the dart from the cork target. +No cement will hold hard wood. +We now have a new base for shipping. +A list of names is carved around the base. +The sheep were led home by a dog. +Three for a dime, the young peddler cried. +The sense of smell is better than that of touch. +No hardship seemed to keep him sad. +Grace makes up for lack of beauty. +Nudge gently but wake her now. +The news struck doubt into restless minds. +Once we stood beside the shore. +A chink in the wall allowed a draft to blow. +Fasten two pins on each side. +A cold dip restores health and zest. +He takes the oath of office each March. +The sand drifts over the sill of the old house. +The point of the steel pen was bent and twisted. +There is a lag between thought and act. +Seed is needed to plant the spring corn. +Draw the chart with heavy black lines. +The boy owed his pal thirty cents. +The chap slipped into the crowd and was lost. +Hats are worn to tea and not to dinner. +The ramp led up to the wide highway. +Beat the dust from the rug onto the lawn. +Say it slowly but make it ring clear. +The straw nest housed five robins. +Screen the porch with woven straw mats. +This horse will nose his way to the finish. +The dry wax protects the deep scratch. +He picked up the dice for a second roll. +These coins will be needed to pay his debt. +The nag pulled the frail cart along. +Twist the valve and release hot steam. +The vamp of the shoe had a gold buckle. +The smell of burned rags itches my nose. +New pants lack cuffs and pockets. +The marsh will freeze when cold enough. +They slice the sausage thin with a knife. +The bloom of the rose lasts a few days. +A gray mare walked before the colt. +Breakfast buns are fine with a hot drink. +Bottles hold four kinds of rum. +The man wore a feather in his felt hat. +He wheeled the bike past the winding road. +Drop the ashes on the worn old rug. +The desk and both chairs were painted tan. +Throw out the used paper cup and plate. +A clean neck means a neat collar. +The couch cover and hall drapes were blue. +The stems of the tall glasses cracked and broke. +The wall phone rang loud and often. +The clothes dried on a thin wooden rack. +Turn on the lantern which gives us light. +The cleat sank deeply into the soft turf. +The bills were mailed promptly on the tenth of the month. +To have is better than to wait and hope. +The price is fair for a good antique clock. +The music played on while they talked. +Dispense with a vest on a day like this. +The bunch of grapes was pressed into wine. +He sent the figs, but kept the ripe cherries. +The hinge on the door creaked with old age. +The screen before the fire kept in the sparks. +Fly by night, and you waste little time. +Thick glasses helped him read the print. +Birth and death mark the limits of life. +The chair looked strong but had no bottom. +The kite flew wildly in the high wind. +A fur muff is stylish once more. +The tin box held priceless stones. +We need an end of all such matter. +The case was puzzling to the old and wise. +The bright lanterns were gay on the dark lawn. +We don't get much money but we have fun. +The youth drove with zest, but little skill. +Five years he lived with a shaggy dog. +A fence cuts through the corner lot. +The way to save money is not to spend much. +Shut the hatch before the waves push it in. +The odor of spring makes young hearts jump. +Crack the walnut with your sharp side teeth. +He offered proof in the form of a large chart. +Send the stuff in a thick paper bag. +A quart of milk is water for the most part. +They told wild tales to frighten him. +The three story house was built of stone. +In the rear of the ground floor was a large passage. +A man in a blue sweater sat at the desk. +Oats are a food eaten by horse and man. +Their eyelids droop for want of sleep. +A sip of tea revives his tired friend. +There are many ways to do these things. +Tuck the sheet under the edge of the mat. +A force equal to that would move the earth. +We like to see clear weather. +The work of the tailor is seen on each side. +Take a chance and win a china doll. +Shake the dust from your shoes, stranger. +She was kind to sick old people. +The square wooden crate was packed to be shipped. +The dusty bench stood by the stone wall. +We dress to suit the weather of most days. +Smile when you say nasty words. +A bowl of rice is free with chicken stew. +The water in this well is a source of good health. +Take shelter in this tent, but keep still. +That guy is the writer of a few banned books. +The little tales they tell are false. +The door was barred, locked, and bolted as well. +Ripe pears are fit for a queen's table. +A big wet stain was on the round carpet. +The kite dipped and swayed, but stayed aloft. +The pleasant hours fly by much too soon. +The room was crowded with a wild mob. +This strong arm shall shield your honor. +She blushed when he gave her a white orchid. +The beetle droned in the hot June sun. +Press the pedal with your left foot. +Neat plans fail without luck. +The black trunk fell from the landing. +The bank pressed for payment of the debt. +The theft of the pearl pin was kept secret. +Shake hands with this friendly child. +The vast space stretched into the far distance. +A rich farm is rare in this sandy waste. +His wide grin earned many friends. +Flax makes a fine brand of paper. +Hurdle the pit with the aid of a long pole. +A strong bid may scare your partner stiff. +Even a just cause needs power to win. +Peep under the tent and see the clowns. +The leaf drifts along with a slow spin. +Cheap clothes are flashy but don't last. +A thing of small note can cause despair. +Flood the mails with requests for this book. +A thick coat of black paint covered all. +The pencil was cut to be sharp at both ends. +Those last words were a strong statement. +He wrote his name boldly at the top of the sheet. +Dill pickles are sour but taste fine. +Down that road is the way to the grain farmer. +Either mud or dust are found at all times. +The best method is to fix it in place with clips. +If you mumble your speech will be lost. +At night the alarm roused him from a deep sleep. +Read just what the meter says. +Fill your pack with bright trinkets for the poor. +The small red neon lamp went out. +Clams are small, round, soft, and tasty. +The fan whirled its round blades softly. +The line where the edges join was clean. +Breathe deep and smell the piny air. +It matters not if he reads these words or those. +A brown leather bag hung from its strap. +A toad and a frog are hard to tell apart. +A white silk jacket goes with any shoes. +A break in the dam almost caused a flood. +Paint the sockets in the wall dull green. +The child crawled into the dense grass. +Bribes fail where honest men work. +Trample the spark, else the flames will spread. +The hilt of the sword was carved with fine designs. +A round hole was drilled through the thin board. +Footprints showed the path he took up the beach. +She was waiting at my front lawn. +A vent near the edge brought in fresh air. +Prod the old mule with a crooked stick. +It is a band of steel three inches wide. +The pipe ran almost the length of the ditch. +It was hidden from sight by a mass of leaves and shrubs. +The weight of the package was seen on the high scale. +Wake and rise, and step into the green outdoors. +The green light in the brown box flickered. +The brass tube circled the high wall. +The lobes of her ears were pierced to hold rings. +Hold the hammer near the end to drive the nail. +Next Sunday is the twelfth of the month. +Every word and phrase he speaks is true. +He put his last cartridge into the gun and fired. +They took their kids from the public school. +Drive the screw straight into the wood. +Keep the hatch tight and the watch constant. +Sever the twine with a quick snip of the knife. +Paper will dry out when wet. +Slide the catch back and open the desk. +Help the weak to preserve their strength. +A sullen smile gets few friends. +Stop whistling and watch the boys march. +Jerk the cord, and out tumbles the gold. +Slide the tray across the glass top. +The cloud moved in a stately way and was gone. +Light maple makes for a swell room. +Set the piece here and say nothing. +Dull stories make her laugh. +A stiff cord will do to fasten your shoe. +Get the trust fund to the bank early. +Choose between the high road and the low. +A plea for funds seems to come again. +He lent his coat to the tall gaunt stranger. +There is a strong chance it will happen once more. +The duke left the park in a silver coach. +Greet the new guests and leave quickly. +When the frost has come it is time for turkey. +Sweet words work better than fierce. +A thin stripe runs down the middle. +A six comes up more often than a ten. +Lush fern grow on the lofty rocks. +The ram scared the school children off. +The team with the best timing looks good. +The farmer swapped his horse for a brown ox. +Sit on the perch and tell the others what to do. +A steep trail is painful for our feet. +The early phase of life moves fast. +Green moss grows on the northern side. +Tea in thin china has a sweet taste. +Pitch the straw through the door of the stable. +The latch on the back gate needed a nail. +The goose was brought straight from the old market. +The sink is the thing in which we pile dishes. +A whiff of it will cure the most stubborn cold. +The facts don't always show who is right. +She flaps her cape as she parades the street. +The loss of the cruiser was a blow to the fleet. +Loop the braid to the left and then over. +Plead with the lawyer to drop the lost cause. +Calves thrive on tender spring grass. +Post no bills on this office wall. +Tear a thin sheet from the yellow pad. +A cruise in warm waters in a sleek yacht is fun. +A streak of color ran down the left edge. +It was done before the boy could see it. +Crouch before you jump or miss the mark. +Pack the kits and don't forget the salt. +The square peg will settle in the round hole. +Fine soap saves tender skin. +Poached eggs and tea must suffice. +Bad nerves are jangled by a door slam. +Ship maps are different from those for planes. +Dimes showered down from all sides. +They sang the same tunes at each party. +The sky in the west is tinged with orange red. +The pods of peas ferment in bare fields. +The horse balked and threw the tall rider. +The hitch between the horse and cart broke. +Pile the coal high in the shed corner. +A gold vase is both rare and costly. +The knife was hung inside its bright sheath. +The rarest spice comes from the far East. +The roof should be tilted at a sharp slant. +A smatter of French is worse than none. +The mule trod the treadmill day and night. +The aim of the contest is to raise a great fund. +To send it now in large amounts is bad. +There is a fine hard tang in salty air. +Cod is the main business of the north shore. +The slab was hewn from heavy blocks of slate. +Dunk the stale biscuits into strong drink. +Hang tinsel from both branches. +Cap the jar with a tight brass cover. +The poor boy missed the boat again. +Be sure to set the lamp firmly in the hole. +Pick a card and slip it under the pack. +A round mat will cover the dull spot. +The first part of the plan needs changing. +A good book informs of what we ought to know. +The mail comes in three batches per day. +You cannot brew tea in a cold pot. +Dots of light betrayed the black cat. +Put the chart on the mantel and tack it down. +The night shift men rate extra pay. +The red paper brightened the dim stage. +See the player scoot to third base. +Slide the bill between the two leaves. +Many hands help get the job done. +We don't like to admit our small faults. +No doubt about the way the wind blows. +Dig deep in the earth for pirate's gold. +The steady drip is worse than a drenching rain. +A flat pack takes less luggage space. +Green ice frosted the punch bowl. +A stuffed chair slipped from the moving van. +The stitch will serve but needs to be shortened. +A thin book fits in the side pocket. +The gloss on top made it unfit to read. +The hail pattered on the burnt brown grass. +Seven seals were stamped on great sheets. +Our troops are set to strike heavy blows. +The store was jammed before the sale could start. +It was a bad error on the part of the new judge. +One step more and the board will collapse. +Take the match and strike it against your shoe. +The pot boiled, but the contents failed to jell. +The baby puts his right foot in his mouth. +The bombs left most of the town in ruins. +Stop and stare at the hard working man. +The streets are narrow and full of sharp turns. +The pup jerked the leash as he saw a feline shape. +Open your book to the first page. +Fish evade the net and swim off. +Dip the pail once and let it settle. +Will you please answer that phone. +The big red apple fell to the ground. +The curtain rose and the show was on. +The young prince became heir to the throne. +He sent the boy on a short errand. +Leave now and you will arrive on time. +The corner store was robbed last night. +A gold ring will please most any girl. +The long journey home took a year. +She saw a cat in the neighbor's house. +A pink shell was found on the sandy beach. +Small children came to see him. +The grass and bushes were wet with dew. +The blind man counted his old coins. +A severe storm tore down the barn. +She called his name many times. +When you hear the bell, come quickly. \ No newline at end of file diff --git a/extensions/coqui_tts/languages.json b/extensions/coqui_tts/languages.json new file mode 100644 index 0000000000000000000000000000000000000000..3895d7f81d68f5c7a7a07a7c1e3364140eb6162b --- /dev/null +++ b/extensions/coqui_tts/languages.json @@ -0,0 +1,18 @@ +{ + "Arabic": "ar", + "Chinese": "zh-cn", + "Czech": "cs", + "Dutch": "nl", + "English": "en", + "French": "fr", + "German": "de", + "Hungarian": "hu", + "Italian": "it", + "Japanese": "ja", + "Korean": "ko", + "Polish": "pl", + "Portuguese": "pt", + "Russian": "ru", + "Spanish": "es", + "Turkish": "tr" +} \ No newline at end of file diff --git a/extensions/coqui_tts/requirements.txt b/extensions/coqui_tts/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..747f99a0685e3d94d8c2fc2393ea4c81ace8cc1d --- /dev/null +++ b/extensions/coqui_tts/requirements.txt @@ -0,0 +1 @@ +TTS==0.21.* \ No newline at end of file diff --git a/extensions/coqui_tts/script.py b/extensions/coqui_tts/script.py new file mode 100644 index 0000000000000000000000000000000000000000..fe6364b9f28d999379ddcab49f4c229d49f90e77 --- /dev/null +++ b/extensions/coqui_tts/script.py @@ -0,0 +1,228 @@ +import html +import json +import os +import random +import time +from pathlib import Path + +import gradio as gr +import torch +from TTS.api import TTS +from TTS.utils.synthesizer import Synthesizer + +from modules import chat, shared, ui_chat +from modules.ui import create_refresh_button +from modules.utils import gradio + +os.environ["COQUI_TOS_AGREED"] = "1" + +params = { + "activate": True, + "autoplay": True, + "show_text": False, + "remove_trailing_dots": False, + "voice": "female_01.wav", + "language": "English", + "model_name": "tts_models/multilingual/multi-dataset/xtts_v2", + "device": "cuda" if torch.cuda.is_available() else "cpu" +} + +this_dir = str(Path(__file__).parent.resolve()) +model = None +with open(Path(f"{this_dir}/languages.json"), encoding='utf8') as f: + languages = json.load(f) + + +def get_available_voices(): + return sorted([voice.name for voice in Path(f"{this_dir}/voices").glob("*.wav")]) + + +def preprocess(raw_input): + raw_input = html.unescape(raw_input) + # raw_input = raw_input.strip("\"") + return raw_input + + +def new_split_into_sentences(self, text): + sentences = self.seg.segment(text) + if params['remove_trailing_dots']: + sentences_without_dots = [] + for sentence in sentences: + if sentence.endswith('.') and not sentence.endswith('...'): + sentence = sentence[:-1] + + sentences_without_dots.append(sentence) + + return sentences_without_dots + else: + return sentences + + +Synthesizer.split_into_sentences = new_split_into_sentences + + +def load_model(): + model = TTS(params["model_name"]).to(params["device"]) + return model + + +def remove_tts_from_history(history): + for i, entry in enumerate(history['internal']): + history['visible'][i] = [history['visible'][i][0], entry[1]] + + return history + + +def toggle_text_in_history(history): + for i, entry in enumerate(history['visible']): + visible_reply = entry[1] + if visible_reply.startswith('')[0]}\n\n{reply}"] + else: + history['visible'][i] = [history['visible'][i][0], f"{visible_reply.split('')[0]}"] + + return history + + +def random_sentence(): + with open(Path("extensions/coqui_tts/harvard_sentences.txt")) as f: + return random.choice(list(f)) + + +def voice_preview(string): + string = html.unescape(string) or random_sentence() + + output_file = Path('extensions/coqui_tts/outputs/voice_preview.wav') + model.tts_to_file( + text=string, + file_path=output_file, + speaker_wav=[f"{this_dir}/voices/{params['voice']}"], + language=languages[params["language"]] + ) + + return f'' + + +def history_modifier(history): + # Remove autoplay from the last reply + if len(history['internal']) > 0: + history['visible'][-1] = [ + history['visible'][-1][0], + history['visible'][-1][1].replace('controls autoplay>', 'controls>') + ] + + return history + + +def state_modifier(state): + if not params['activate']: + return state + + state['stream'] = False + return state + + +def input_modifier(string, state): + if not params['activate']: + return string + + shared.processing_message = "*Is recording a voice message...*" + return string + + +def output_modifier(string, state): + if not params['activate']: + return string + + original_string = string + string = preprocess(html.unescape(string)) + if string == '': + string = '*Empty reply, try regenerating*' + else: + output_file = Path(f'extensions/coqui_tts/outputs/{state["character_menu"]}_{int(time.time())}.wav') + model.tts_to_file( + text=string, + file_path=output_file, + speaker_wav=[f"{this_dir}/voices/{params['voice']}"], + language=languages[params["language"]] + ) + + autoplay = 'autoplay' if params['autoplay'] else '' + string = f'' + if params['show_text']: + string += f'\n\n{original_string}' + + shared.processing_message = "*Is typing...*" + return string + + +def custom_css(): + path_to_css = Path(f"{this_dir}/style.css") + return open(path_to_css, 'r').read() + + +def setup(): + global model + print("[XTTS] Loading XTTS...") + model = load_model() + print("[XTTS] Done!") + Path(f"{this_dir}/outputs").mkdir(parents=True, exist_ok=True) + + +def ui(): + with gr.Accordion("Coqui TTS (XTTSv2)"): + with gr.Row(): + activate = gr.Checkbox(value=params['activate'], label='Activate TTS') + autoplay = gr.Checkbox(value=params['autoplay'], label='Play TTS automatically') + + with gr.Row(): + show_text = gr.Checkbox(value=params['show_text'], label='Show message text under audio player') + remove_trailing_dots = gr.Checkbox(value=params['remove_trailing_dots'], label='Remove trailing "." from text segments before converting to audio') + + with gr.Row(): + with gr.Row(): + voice = gr.Dropdown(get_available_voices(), label="Voice wav", value=params["voice"]) + create_refresh_button(voice, lambda: None, lambda: {'choices': get_available_voices(), 'value': params["voice"]}, 'refresh-button') + + language = gr.Dropdown(languages.keys(), label="Language", value=params["language"]) + + with gr.Row(): + preview_text = gr.Text(show_label=False, placeholder="Preview text", elem_id="silero_preview_text") + preview_play = gr.Button("Preview") + preview_audio = gr.HTML(visible=False) + + with gr.Row(): + convert = gr.Button('Permanently replace audios with the message texts') + convert_cancel = gr.Button('Cancel', visible=False) + convert_confirm = gr.Button('Confirm (cannot be undone)', variant="stop", visible=False) + + # Convert history with confirmation + convert_arr = [convert_confirm, convert, convert_cancel] + convert.click(lambda: [gr.update(visible=True), gr.update(visible=False), gr.update(visible=True)], None, convert_arr) + convert_confirm.click( + lambda: [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)], None, convert_arr).then( + remove_tts_from_history, gradio('history'), gradio('history')).then( + chat.save_history, gradio('history', 'unique_id', 'character_menu', 'mode'), None).then( + chat.redraw_html, gradio(ui_chat.reload_arr), gradio('display')) + + convert_cancel.click(lambda: [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)], None, convert_arr) + + # Toggle message text in history + show_text.change( + lambda x: params.update({"show_text": x}), show_text, None).then( + toggle_text_in_history, gradio('history'), gradio('history')).then( + chat.save_history, gradio('history', 'unique_id', 'character_menu', 'mode'), None).then( + chat.redraw_html, gradio(ui_chat.reload_arr), gradio('display')) + + # Event functions to update the parameters in the backend + activate.change(lambda x: params.update({"activate": x}), activate, None) + autoplay.change(lambda x: params.update({"autoplay": x}), autoplay, None) + remove_trailing_dots.change(lambda x: params.update({"remove_trailing_dots": x}), remove_trailing_dots, None) + voice.change(lambda x: params.update({"voice": x}), voice, None) + language.change(lambda x: params.update({"language": x}), language, None) + + # Play preview + preview_text.submit(voice_preview, preview_text, preview_audio) + preview_play.click(voice_preview, preview_text, preview_audio) diff --git a/extensions/coqui_tts/style.css b/extensions/coqui_tts/style.css new file mode 100644 index 0000000000000000000000000000000000000000..2ab7aefbbfca19982414f13a76dfdd4324793903 --- /dev/null +++ b/extensions/coqui_tts/style.css @@ -0,0 +1,8 @@ +.SDAP .hires_opts input[type="number"] { + width: 6em !important; +} + +/* silero_tts preview */ +.form:has(> #silero_preview_text) { + min-width: 75% +} diff --git a/extensions/coqui_tts/voices/arnold.wav b/extensions/coqui_tts/voices/arnold.wav new file mode 100644 index 0000000000000000000000000000000000000000..cccdf013c0fb693b82a00616dd0efd00971b6b4b Binary files /dev/null and b/extensions/coqui_tts/voices/arnold.wav differ diff --git a/extensions/coqui_tts/voices/female_01.wav b/extensions/coqui_tts/voices/female_01.wav new file mode 100644 index 0000000000000000000000000000000000000000..6fd8a4ee131b1b692f6b40b302f6ab749bc2d5a6 Binary files /dev/null and b/extensions/coqui_tts/voices/female_01.wav differ diff --git a/extensions/coqui_tts/voices/female_02.wav b/extensions/coqui_tts/voices/female_02.wav new file mode 100644 index 0000000000000000000000000000000000000000..ecf1440002a431900596f26446e3e62c56fd2d3e Binary files /dev/null and b/extensions/coqui_tts/voices/female_02.wav differ diff --git a/extensions/example/script.py b/extensions/example/script.py new file mode 100644 index 0000000000000000000000000000000000000000..44f0cb3c64d2fcc2556c30426c94c29543e599dd --- /dev/null +++ b/extensions/example/script.py @@ -0,0 +1,139 @@ +""" +An example of extension. It does nothing, but you can add transformations +before the return statements to customize the webui behavior. + +Starting from history_modifier and ending in output_modifier, the +functions are declared in the same order that they are called at +generation time. +""" + +import gradio as gr +import torch +from transformers import LogitsProcessor + +from modules import chat, shared +from modules.text_generation import ( + decode, + encode, + generate_reply, +) + +params = { + "display_name": "Example Extension", + "is_tab": False, +} + +class MyLogits(LogitsProcessor): + """ + Manipulates the probabilities for the next token before it gets sampled. + Used in the logits_processor_modifier function below. + """ + def __init__(self): + pass + + def __call__(self, input_ids, scores): + # probs = torch.softmax(scores, dim=-1, dtype=torch.float) + # probs[0] /= probs[0].sum() + # scores = torch.log(probs / (1 - probs)) + return scores + +def history_modifier(history): + """ + Modifies the chat history. + Only used in chat mode. + """ + return history + +def state_modifier(state): + """ + Modifies the state variable, which is a dictionary containing the input + values in the UI like sliders and checkboxes. + """ + return state + +def chat_input_modifier(text, visible_text, state): + """ + Modifies the user input string in chat mode (visible_text). + You can also modify the internal representation of the user + input (text) to change how it will appear in the prompt. + """ + return text, visible_text + +def input_modifier(string, state, is_chat=False): + """ + In default/notebook modes, modifies the whole prompt. + + In chat mode, it is the same as chat_input_modifier but only applied + to "text", here called "string", and not to "visible_text". + """ + return string + +def bot_prefix_modifier(string, state): + """ + Modifies the prefix for the next bot reply in chat mode. + By default, the prefix will be something like "Bot Name:". + """ + return string + +def tokenizer_modifier(state, prompt, input_ids, input_embeds): + """ + Modifies the input ids and embeds. + Used by the multimodal extension to put image embeddings in the prompt. + Only used by loaders that use the transformers library for sampling. + """ + return prompt, input_ids, input_embeds + +def logits_processor_modifier(processor_list, input_ids): + """ + Adds logits processors to the list, allowing you to access and modify + the next token probabilities. + Only used by loaders that use the transformers library for sampling. + """ + processor_list.append(MyLogits()) + return processor_list + +def output_modifier(string, state, is_chat=False): + """ + Modifies the LLM output before it gets presented. + + In chat mode, the modified version goes into history['visible'], + and the original version goes into history['internal']. + """ + return string + +def custom_generate_chat_prompt(user_input, state, **kwargs): + """ + Replaces the function that generates the prompt from the chat history. + Only used in chat mode. + """ + result = chat.generate_chat_prompt(user_input, state, **kwargs) + return result + +def custom_css(): + """ + Returns a CSS string that gets appended to the CSS for the webui. + """ + return '' + +def custom_js(): + """ + Returns a javascript string that gets appended to the javascript + for the webui. + """ + return '' + +def setup(): + """ + Gets executed only once, when the extension is imported. + """ + pass + +def ui(): + """ + Gets executed when the UI is drawn. Custom gradio elements and + their corresponding event handlers should be defined here. + + To learn about gradio components, check out the docs: + https://gradio.app/docs/ + """ + pass diff --git a/extensions/gallery/__pycache__/script.cpython-311.pyc b/extensions/gallery/__pycache__/script.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebcbc7e70167d347afa6b2b57432406af67a8734 Binary files /dev/null and b/extensions/gallery/__pycache__/script.cpython-311.pyc differ diff --git a/extensions/gallery/script.js b/extensions/gallery/script.js new file mode 100644 index 0000000000000000000000000000000000000000..9717aa671e0d7e283b3f97c2445910131337f63f --- /dev/null +++ b/extensions/gallery/script.js @@ -0,0 +1,40 @@ +let gallery_element = document.getElementById('gallery-extension'); +let chat_mode_element = document.getElementById('chat-mode'); + +let extensions_block = document.getElementById('extensions'); +let extensions_block_size = extensions_block.childNodes.length; +let gallery_only = (extensions_block_size == 5); + +function gotoFirstPage() { + const firstPageButton = gallery_element.querySelector('.paginate > button'); + if (firstPageButton) { + firstPageButton.click(); + } +} + +document.querySelector('.header_bar').addEventListener('click', function(event) { + if (event.target.tagName === 'BUTTON') { + const buttonText = event.target.textContent.trim(); + + let chat_visible = (buttonText == 'Chat'); + let default_visible = (buttonText == 'Default'); + let notebook_visible = (buttonText == 'Notebook'); + let chat_mode_visible = (chat_mode_element.offsetHeight > 0 && chat_mode_element.offsetWidth > 0); + + // Only show this extension in the Chat tab + if (chat_visible) { + if (chat_mode_visible) { + gallery_element.style.display = 'block'; + extensions_block.style.display = ''; + } else { + gallery_element.style.display = 'none'; + extensions_block.style.display = 'none'; + } + } else { + gallery_element.style.display = 'none'; + if (gallery_only) { + extensions_block.style.display = 'none'; + } + } + } +}); diff --git a/extensions/gallery/script.py b/extensions/gallery/script.py new file mode 100644 index 0000000000000000000000000000000000000000..1cb7f27f838c8b9616070bed041087fb4894c300 --- /dev/null +++ b/extensions/gallery/script.py @@ -0,0 +1,129 @@ +from pathlib import Path + +import gradio as gr + +from modules.html_generator import get_image_cache +from modules.shared import gradio, settings + + +cards = [] + + +def generate_css(): + css = """ + .highlighted-border { + border-color: rgb(249, 115, 22) !important; + } + + .character-gallery > .gallery { + margin: 1rem 0; + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + grid-column-gap: 0.4rem; + grid-row-gap: 1.2rem; + } + + .character-gallery > .label { + display: none !important; + } + + .character-gallery button.gallery-item { + display: contents; + } + + .character-container { + cursor: pointer; + text-align: center; + position: relative; + opacity: 0.85; + } + + .character-container:hover { + opacity: 1; + } + + .character-container .placeholder, .character-container img { + width: 150px; + height: 200px; + background-color: gray; + object-fit: cover; + margin: 0 auto; + border-radius: 1rem; + border: 3px solid white; + box-shadow: 3px 3px 6px 0px rgb(0 0 0 / 50%); + } + + .character-name { + margin-top: 0.3rem; + display: block; + font-size: 1.2rem; + font-weight: 600; + overflow-wrap: anywhere; + } + """ + return css + + +def generate_html(): + global cards + cards = [] + # Iterate through files in image folder + for file in sorted(Path("characters").glob("*")): + if file.suffix in [".json", ".yml", ".yaml"]: + character = file.stem + container_html = '
' + image_html = "
" + + for path in [Path(f"characters/{character}.{extension}") for extension in ['png', 'jpg', 'jpeg']]: + if path.exists(): + image_html = f'' + break + + container_html += f'{image_html} {character}' + container_html += "
" + cards.append([container_html, character]) + + return cards + + +def filter_cards(filter_str=''): + if filter_str == '': + return cards + + filter_upper = filter_str.upper() + return [k for k in cards if filter_upper in k[1].upper()] + + +def select_character(evt: gr.SelectData): + return (evt.value[1]) + + +def custom_js(): + path_to_js = Path(__file__).parent.resolve() / 'script.js' + return open(path_to_js, 'r').read() + + +def ui(): + with gr.Accordion("Character gallery", open=settings["gallery-open"], elem_id='gallery-extension'): + gr.HTML(value="") + with gr.Row(): + filter_box = gr.Textbox(label='', placeholder='Filter', lines=1, max_lines=1, container=False, elem_id='gallery-filter-box') + gr.ClearButton(filter_box, value='Clear', elem_classes='refresh-button') + update = gr.Button("Refresh", elem_classes='refresh-button') + + gallery = gr.Dataset( + components=[gr.HTML(visible=False)], + label="", + samples=generate_html(), + elem_classes=["character-gallery"], + samples_per_page=settings["gallery-items_per_page"] + ) + + filter_box.change(lambda: None, None, None, _js=f'() => {{{custom_js()}; gotoFirstPage()}}').success( + filter_cards, filter_box, gallery).then( + lambda x: gr.update(elem_classes='highlighted-border' if x != '' else ''), filter_box, filter_box, show_progress=False) + + update.click(generate_html, [], None).success( + filter_cards, filter_box, gallery) + + gallery.select(select_character, None, gradio['character_menu']) diff --git a/extensions/google_translate/requirements.txt b/extensions/google_translate/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..554a00df62818f96ba7d396ae39d8e58efbe9bfe --- /dev/null +++ b/extensions/google_translate/requirements.txt @@ -0,0 +1 @@ +deep-translator==1.9.2 diff --git a/extensions/google_translate/script.py b/extensions/google_translate/script.py new file mode 100644 index 0000000000000000000000000000000000000000..784668c1e4a704b306b7f0bb70afce07eebb255b --- /dev/null +++ b/extensions/google_translate/script.py @@ -0,0 +1,59 @@ +import html + +import gradio as gr +from deep_translator import GoogleTranslator + +params = { + "activate": True, + "language string": "ja", +} + +language_codes = {'Afrikaans': 'af', 'Albanian': 'sq', 'Amharic': 'am', 'Arabic': 'ar', 'Armenian': 'hy', 'Azerbaijani': 'az', 'Basque': 'eu', 'Belarusian': 'be', 'Bengali': 'bn', 'Bosnian': 'bs', 'Bulgarian': 'bg', 'Catalan': 'ca', 'Cebuano': 'ceb', 'Chinese (Simplified)': 'zh-CN', 'Chinese (Traditional)': 'zh-TW', 'Corsican': 'co', 'Croatian': 'hr', 'Czech': 'cs', 'Danish': 'da', 'Dutch': 'nl', 'English': 'en', 'Esperanto': 'eo', 'Estonian': 'et', 'Finnish': 'fi', 'French': 'fr', 'Frisian': 'fy', 'Galician': 'gl', 'Georgian': 'ka', 'German': 'de', 'Greek': 'el', 'Gujarati': 'gu', 'Haitian Creole': 'ht', 'Hausa': 'ha', 'Hawaiian': 'haw', 'Hebrew': 'iw', 'Hindi': 'hi', 'Hmong': 'hmn', 'Hungarian': 'hu', 'Icelandic': 'is', 'Igbo': 'ig', 'Indonesian': 'id', 'Irish': 'ga', 'Italian': 'it', 'Japanese': 'ja', 'Javanese': 'jw', 'Kannada': 'kn', 'Kazakh': 'kk', 'Khmer': 'km', 'Korean': 'ko', 'Kurdish': 'ku', 'Kyrgyz': 'ky', 'Lao': 'lo', 'Latin': 'la', 'Latvian': 'lv', 'Lithuanian': 'lt', 'Luxembourgish': 'lb', 'Macedonian': 'mk', 'Malagasy': 'mg', 'Malay': 'ms', 'Malayalam': 'ml', 'Maltese': 'mt', 'Maori': 'mi', 'Marathi': 'mr', 'Mongolian': 'mn', 'Myanmar (Burmese)': 'my', 'Nepali': 'ne', 'Norwegian': 'no', 'Nyanja (Chichewa)': 'ny', 'Pashto': 'ps', 'Persian': 'fa', 'Polish': 'pl', 'Portuguese (Portugal, Brazil)': 'pt', 'Punjabi': 'pa', 'Romanian': 'ro', 'Russian': 'ru', 'Samoan': 'sm', 'Scots Gaelic': 'gd', 'Serbian': 'sr', 'Sesotho': 'st', 'Shona': 'sn', 'Sindhi': 'sd', 'Sinhala (Sinhalese)': 'si', 'Slovak': 'sk', 'Slovenian': 'sl', 'Somali': 'so', 'Spanish': 'es', 'Sundanese': 'su', 'Swahili': 'sw', 'Swedish': 'sv', 'Tagalog (Filipino)': 'tl', 'Tajik': 'tg', 'Tamil': 'ta', 'Telugu': 'te', 'Thai': 'th', 'Turkish': 'tr', 'Ukrainian': 'uk', 'Urdu': 'ur', 'Uzbek': 'uz', 'Vietnamese': 'vi', 'Welsh': 'cy', 'Xhosa': 'xh', 'Yiddish': 'yi', 'Yoruba': 'yo', 'Zulu': 'zu'} + + +def input_modifier(string): + """ + This function is applied to your text inputs before + they are fed into the model. + """ + if not params['activate']: + return string + + return GoogleTranslator(source=params['language string'], target='en').translate(string) + + +def output_modifier(string): + """ + This function is applied to the model outputs. + """ + if not params['activate']: + return string + + translated_str = GoogleTranslator(source='en', target=params['language string']).translate(html.unescape(string)) + return html.escape(translated_str) + + +def bot_prefix_modifier(string): + """ + This function is only applied in chat mode. It modifies + the prefix text for the Bot and can be used to bias its + behavior. + """ + + return string + + +def ui(): + # Finding the language name from the language code to use as the default value + language_name = list(language_codes.keys())[list(language_codes.values()).index(params['language string'])] + + # Gradio elements + with gr.Row(): + activate = gr.Checkbox(value=params['activate'], label='Activate translation') + + with gr.Row(): + language = gr.Dropdown(value=language_name, choices=[k for k in language_codes], label='Language') + + # Event functions to update the parameters in the backend + activate.change(lambda x: params.update({"activate": x}), activate, None) + language.change(lambda x: params.update({"language string": language_codes[x]}), language, None) diff --git a/extensions/long_replies/script.py b/extensions/long_replies/script.py new file mode 100644 index 0000000000000000000000000000000000000000..035e8c9e1c5005620eb72cb83be456464d5f3e78 --- /dev/null +++ b/extensions/long_replies/script.py @@ -0,0 +1,143 @@ +import torch +from modules import chat, shared +from modules.text_generation import ( + decode, + encode, + generate_reply, +) +from transformers import LogitsProcessor +import gradio as gr + +params = { + "display_name": "Long replies", + "is_tab": False, + "min_length": 120, +} + +initial_size = 0 + +class MyLogits(LogitsProcessor): + """ + Manipulates the probabilities for the next token before it gets sampled. + Used in the logits_processor_modifier function below. + """ + def __init__(self): + self.newline_id = shared.tokenizer.encode('\n')[-1] + pass + + def __call__(self, input_ids, scores): + if input_ids.shape[-1] - initial_size < params["min_length"]: + scores[...,self.newline_id] = -1000 + # scores[...,shared.tokenizer.eos_token_id] = -1000 + + # probs = torch.softmax(scores, dim=-1, dtype=torch.float) + # probs[0] /= probs[0].sum() + # scores = torch.log(probs / (1 - probs)) + return scores + +def history_modifier(history): + """ + Modifies the chat history. + Only used in chat mode. + """ + return history + +def state_modifier(state): + """ + Modifies the state variable, which is a dictionary containing the input + values in the UI like sliders and checkboxes. + """ + return state + +def chat_input_modifier(text, visible_text, state): + """ + Modifies the user input string in chat mode (visible_text). + You can also modify the internal representation of the user + input (text) to change how it will appear in the prompt. + """ + return text, visible_text + +def input_modifier(string, state): + """ + In default/notebook modes, modifies the whole prompt. + + In chat mode, it is the same as chat_input_modifier but only applied + to "text", here called "string", and not to "visible_text". + """ + return string + +def bot_prefix_modifier(string, state): + """ + Modifies the prefix for the next bot reply in chat mode. + By default, the prefix will be something like "Bot Name:". + """ + return string + +def tokenizer_modifier(state, prompt, input_ids, input_embeds): + """ + Modifies the input ids and embeds. + Used by the multimodal extension to put image embeddings in the prompt. + Only used by loaders that use the transformers library for sampling. + """ + + global initial_size + initial_size = input_ids.shape[-1] + + return prompt, input_ids, input_embeds + +def logits_processor_modifier(processor_list, input_ids): + """ + Adds logits processors to the list, allowing you to access and modify + the next token probabilities. + Only used by loaders that use the transformers library for sampling. + """ + processor_list.append(MyLogits()) + return processor_list + +def output_modifier(string, state): + """ + Modifies the LLM output before it gets presented. + + In chat mode, the modified version goes into history['visible'], + and the original version goes into history['internal']. + """ + return string + +def custom_generate_chat_prompt(user_input, state, **kwargs): + """ + Replaces the function that generates the prompt from the chat history. + Only used in chat mode. + """ + result = chat.generate_chat_prompt(user_input, state, **kwargs) + return result + +def custom_css(): + """ + Returns a CSS string that gets appended to the CSS for the webui. + """ + return '' + +def custom_js(): + """ + Returns a javascript string that gets appended to the javascript + for the webui. + """ + return '' + +def setup(): + """ + Gets executed only once, when the extension is imported. + """ + pass + +def ui(): + """ + Gets executed when the UI is drawn. Custom gradio elements and + their corresponding event handlers should be defined here. + + To learn about gradio components, check out the docs: + https://gradio.app/docs/ + """ + + min_length = gr.Slider(0, 800, step=10, value=params['min_length'], label='Minimum reply length') + min_length.change(lambda x: params.update({'min_length': x}), min_length, None) diff --git a/extensions/multimodal/DOCS.md b/extensions/multimodal/DOCS.md new file mode 100644 index 0000000000000000000000000000000000000000..eaa4365e9a304a14ebbdb1d4d435f3a2a1f7a7d2 --- /dev/null +++ b/extensions/multimodal/DOCS.md @@ -0,0 +1,85 @@ +# Technical description of multimodal extension + +## Working principle +Multimodality extension does most of the stuff which is required for any image input: + +- adds the UI +- saves the images as base64 JPEGs to history +- provides the hooks to the UI +- if there are images in the prompt, it: + - splits the prompt to text and image parts + - adds image start/end markers to text parts, then encodes and embeds the text parts + - calls the vision pipeline to embed the images + - stitches the embeddings together, and returns them to text generation +- loads the appropriate vision pipeline, selected either from model name, or by specifying --multimodal-pipeline parameter + +Now, for the pipelines, they: + +- load the required vision models +- return some consts, for example the number of tokens taken up by image +- and most importantly: return the embeddings for LLM, given a list of images + +## Prompts/history + +To save images in prompt/history, this extension is using a base64 JPEG, wrapped in a HTML tag, like so: +``` + +``` +where `{img_str}` is the actual image data. This format makes displaying them in the UI for free. Do note, that this format is required to be exactly the same, the regex used to find the images is: ``. + +## LLM input +To describe the input, let's see it on an example prompt: +``` +text1text2text3 +``` +where `textN` is N-th text, `` is N-th image, in HTML format specified above. + +**The first step is to split the prompt into image/text parts**, so we get: +``` +['text1', '', 'text2', '', 'text3'] +``` +this is done in `MultimodalEmbedder._split_prompt(...)` function, which returns a list of `PromptPart`s - dataclasses wrapping the separate parts. + +This function also appends the image start/end markers to text, which are provided by `AbstractMultimodalPipeline.image_start()` / `AbstractMultimodalPipeline.image_end()` functions. If image start is ``, and end is ``, this function will return: +``` +['text1', '', 'text2', '', 'text3'] +``` + +**The returned prompt parts are then turned into token embeddings.** + +First, they are modified to token IDs, for the text it is done using standard `modules.text_generation.encode()` function, and for the images the returned token IDs are changed to placeholders. The placeholder is a list of `N` times `placeholder token id`, where `N` is specified using `AbstractMultimodalPipeline.num_image_embeds()`, and placeholder token IDs using `AbstractMultimodalPipeline.placeholder_token_id()`. + +Now, based on the token IDs, the prompt might get truncated, especially if `max_new_tokens` are unreasonably high. Unfortunately, it can't be done simply, just by trimming the prompt to be short enough. This way will lead to sometimes splitting the prompt in the middle of an image embedding, which usually breaks the generation. Therefore, in this case, the entire image needs to be removed from input. This is done inside `MultimodalEmbedder._encode_text(...)` function. + +**After the tokenization, the tokens need to get embedded**, the text and images are once again treated separately. + +The text parts are turned to embeddings, using `AbstractMultimodalPipeline.embed_tokens(...)` function. It uses standard embedding function from the model, but to support many LLMs, the actual function is returned by the pipeline (as it might be different for different LLMs), for LLaMA it is `shared.model.model.embed_tokens(...)`. + +The image parts are turned to embeddings, using `AbstractMultimodalPipeline.embed_images(...)` function. This function is specific for a given pipeline, it takes the images as input, forwards them through vision model/projector, and returns the embeddings. + +**Now, the returned embeddings are stitched together**, using `torch.cat()`, this is creating the final input to the LLM. + +## Pipelines + +All of the pipelines should subclass `AbstractMultimodalPipeline` class. The idea is to allow for new pipelines to be added in the same way as user extensions - git clone into `extensions/multimodal/pipelines`. + +The pipelines are the description of the vision part, containing vision model/multimodal projector. All of the pipelines should have an unique `name()`, which is then selected by user, in `--multimodal-pipeline` CLI argument. For an example, see `pipelines/llava/llava.py`. + +## Pipeline modules + +Pipelines are organized into "pipeline modules" - subdirectories in `pipelines` directory. The pipeline modules should contain a file called `pipelines.py`, that should contain the following fields: +- `available_pipelines: List[str]` - list of pipelines provided by this module, shown as the list of available pipelines to the user +- `def get_pipeline(name: str, params: dict) -> Optional[AbstractMultimodalPipeline]`: - a function to get a concrete pipeline by `name`, if `name` doesn't match any, should return `None`. `params` is the user settings for multimodal extension +- `def get_pipeline_from_model_name(model_name: str, params: dict) -> Optional[AbstractMultimodalPipeline]`: - a function to get a pipeline from `model_name`, should be eager to return `None`, unless the determination can be done clearly (for example: minigpt-4 bases on vicuna - it should never return the pipeline, but llava can, as it has its own specific LLM finetune) + +**NOTE**: A pipeline module should lazy-import the pipelines only when necessary, and it should keep its imports to minimum + +## Pipeline params + +The pipelines will get the extension `params` in the constructor. They should honor the following fields: +- `vision_device` - string, specifying `torch.device` to run the vision model (CLIP/ViT) on +- `vision_bits` - int, number of fp bits to load the vision model(s) in +- `projector_device` - string, specifying `torch.device` to run the projector models (Linear layers, QFormer, etc.) on +- `projector_bits` - int, number of fp bits to load the projector models in + +As a helper, `AbstractMultimodalPipeline` has `_get_device(self, setting_name: str, params: dict)` and `_get_dtype(self, setting_name: str, params: dict)` helper functions, which parse string/int and return `torch.device` / `torch.dtype`. diff --git a/extensions/multimodal/README.md b/extensions/multimodal/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b176eca3d651c6c458d51f82f3036341a5d8a258 --- /dev/null +++ b/extensions/multimodal/README.md @@ -0,0 +1,139 @@ +# Multimodal + +## Description + +Adds support for multimodality (text+images) to text-generation-webui. + +Note: multimodal currently only works for transformers, AutoGPTQ, and GPTQ-for-LLaMa loaders. ExLlama (v1 and v2) and llama.cpp support are planned. + +https://user-images.githubusercontent.com/3718215/233817203-69b57e77-0c55-4fd6-b742-3204bb13b8fc.mp4 + +## Usage + +To run this extension, download a LLM that supports multimodality, and then start server.py with the appropriate `--multimodal-pipeline` argument. Examples: + +``` +# LLaVA 1.5 13B has the best performance +python server.py --model liuhaotian_llava-v1.5-13b --multimodal-pipeline llava-v1.5-13b --load-in-4bit +# LLaVA 1.5 7B is relatively weaker, but requires less memory +python server.py --model liuhaotian_llava-v1.5-7b --multimodal-pipeline llava-v1.5-7b --load-in-4bit +python server.py --model TheBloke_llava-v1.5-13B-GPTQ_gptq-4bit-32g-actorder_True --multimodal-pipeline llava-v1.5-13b --disable_exllama --loader autogptq +python server.py --model wojtab_llava-7b-v0-4bit-128g --multimodal-pipeline llava-7b +python server.py --model wojtab_llava-13b-v0-4bit-128g --multimodal-pipeline llava-13b +python server.py --model anon8231489123_vicuna-13b-GPTQ-4bit-128g --multimodal-pipeline minigpt4-13b +python server.py --model llama-7b-4bit --multimodal-pipeline minigpt4-7b +``` + +There is built-in support for LLaVA-v0-13B, LLaVA-v0-7b, and LLaVA-v1.5-13B. To install `minigpt4`: + +- clone https://github.com/Wojtab/minigpt-4-pipeline into `extensions/multimodal/pipelines` +- install the requirements.txt + +The same procedure should be used to install other pipelines, which can then be used with `--multimodal-pipeline [pipeline name]`. For additional multimodal pipelines refer to the compatibility section below. + +Do note, that each image takes up a considerable amount of tokens, so adjust `max_new_tokens` to be at most 1700 (recommended value is between 200 to 500), so the images don't get truncated. + +To send an image, just upload it to the extension field below chat, and send a prompt as always. The image will be added to the end of your message. If you wish to modify the placement, include a string `` in your prompt. + +Additionally, there is *Embed all images, not only the last one* checkbox. It modifies the image embeddings, by default (if it's unchecked), all but the most recent images have their embeddings empty, so they are not fed to the network. It seems as if some multimodal networks consider the features in all images at the same time as if they were a single image. Due to this behavior, by default, the extension skips previous images. However, it can lead to sub-par generation on other pipelines. If you want to include all images, just tick this checkbox. + +## Compatibility + +As of now, the following multimodal pipelines are supported: +|Pipeline|`--multimodal-pipeline`|Default LLM|LLM info(for the linked model)|Pipeline repository| +|-|-|-|-|-| +|[LLaVA 13B](https://github.com/haotian-liu/LLaVA)|`llava-13b`|[LLaVA 13B](https://huggingface.co./wojtab/llava-13b-v0-4bit-128g)|GPTQ 4-bit quant, old CUDA|built-in| +|[LLaVA 7B](https://github.com/haotian-liu/LLaVA)|`llava-7b`|[LLaVA 7B](https://huggingface.co./wojtab/llava-7b-v0-4bit-128g)|GPTQ 4-bit quant, old CUDA|built-in| +|[MiniGPT-4 7B](https://github.com/Vision-CAIR/MiniGPT-4)|`minigpt4-7b`|[Vicuna v0 7B](https://huggingface.co./TheBloke/vicuna-7B-GPTQ-4bit-128g)|GPTQ 4-bit quant, new format|[Wojtab/minigpt-4-pipeline](https://github.com/Wojtab/minigpt-4-pipeline)| +|[MiniGPT-4 13B](https://github.com/Vision-CAIR/MiniGPT-4)|`minigpt4-13b`|[Vicuna v0 13B](https://huggingface.co./anon8231489123/vicuna-13b-GPTQ-4bit-128g)|GPTQ 4-bit quant, old CUDA|[Wojtab/minigpt-4-pipeline](https://github.com/Wojtab/minigpt-4-pipeline)| +|[InstructBLIP 7B](https://github.com/salesforce/LAVIS/tree/main/projects/instructblip)|`instructblip-7b`|[Vicuna v1.1 7B](https://huggingface.co./TheBloke/vicuna-7B-1.1-GPTQ-4bit-128g)|GPTQ 4-bit quant|[kjerk/instructblip-pipeline](https://github.com/kjerk/instructblip-pipeline)| +|[InstructBLIP 13B](https://github.com/salesforce/LAVIS/tree/main/projects/instructblip)|`instructblip-13b`|[Vicuna v1.1 13B](https://huggingface.co./TheBloke/vicuna-13B-1.1-GPTQ-4bit-128g)|GPTQ 4-bit quant|[kjerk/instructblip-pipeline](https://github.com/kjerk/instructblip-pipeline)| + +Some pipelines could support different LLMs but do note that while it might work, it isn't a supported configuration. + +DO NOT report bugs if you are using a different LLM. + +DO NOT report bugs with pipelines in this repository (unless they are built-in) + +## Extension config +This extension uses the following parameters (from `settings.json`): +|Parameter|Description| +|---------|-----------| +|`multimodal-vision_bits`|Number of bits to load vision models (CLIP/ViT) feature extractor in (most pipelines should support either 32 or 16, default=32)| +|`multimodal-vision_device`|Torch device to run the feature extractor on, for example, `cpu` or `cuda:0`, by default `cuda:0` if available| +|`multimodal-projector_bits`|Number of bits to load feature projector model(s) in (most pipelines should support either 32 or 16, default=32)| +|`multimodal-projector_device`|Torch device to run the feature projector model(s) on, for example `cpu` or `cuda:0`, by default `cuda:0` if available| +|`multimodal-add_all_images_to_prompt`|Default value of "Embed all images, not only the last one" checkbox| + +## Usage through API + +### Chat completions endpoint + +#### With an image URL + +```shell +curl http://127.0.0.1:5000/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "user", + "image_url": "https://avatars.githubusercontent.com/u/112222186?v=4" + }, + { + "role": "user", + "content": "What is unusual about this image?" + } + ] + }' +``` + +#### With a Base64 image + +```python +import base64 +import json +import requests + +img = open('image.jpg', 'rb') +img_bytes = img.read() +img_base64 = base64.b64encode(img_bytes).decode('utf-8') +data = { "messages": [ + { + "role": "user", + "image_url": f"data:image/jpeg;base64,{img_base64}" + }, + { + "role": "user", + "content": "what is unusual about this image?" + } + ] +} +response = requests.post('http://127.0.0.1:5000/v1/chat/completions', json=data) +print(response.text) +``` + +You can run the multimodal inference through API, by inputting the images to prompt. Images are embedded like so: `f''`, where `img_str` is base-64 jpeg data. Note that you will need to launch `server.py` with the arguments `--api --extensions multimodal`. + +### Completions endpoint + +Python example: + +```Python +import base64 +import requests + +CONTEXT = "You are LLaVA, a large language and vision assistant trained by UW Madison WAIV Lab. You are able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language. Follow the instructions carefully and explain your answers in detail.### Human: Hi!### Assistant: Hi there! How can I help you today?\n" + +with open('extreme_ironing.jpg', 'rb') as f: + img_str = base64.b64encode(f.read()).decode('utf-8') + prompt = CONTEXT + f'### Human: What is unusual about this image: \n### Assistant: ' + print(requests.post('http://127.0.0.1:5000/v1/completions', json={'prompt': prompt, 'max_tokens': 200, 'stop': ['\n###']}).json()) +``` +script output: +```Python +{'results': [{'text': "The unusual aspect of this image is that a man is standing on top of a yellow minivan while doing his laundry. He has set up a makeshift clothes line using the car's rooftop as an outdoor drying area. This scene is uncommon because people typically do their laundry indoors, in a dedicated space like a laundromat or a room in their home, rather than on top of a moving vehicle. Additionally, hanging clothes on the car could be potentially hazardous or illegal in some jurisdictions due to the risk of damaging the vehicle or causing accidents on the road.\n##"}]} +``` + +## For pipeline developers/technical description +see [DOCS.md](https://github.com/oobabooga/text-generation-webui/blob/main/extensions/multimodal/DOCS.md) diff --git a/extensions/multimodal/abstract_pipeline.py b/extensions/multimodal/abstract_pipeline.py new file mode 100644 index 0000000000000000000000000000000000000000..9c49935a2d734ac2628b4d71d9ce02f9a6d64f40 --- /dev/null +++ b/extensions/multimodal/abstract_pipeline.py @@ -0,0 +1,63 @@ +from abc import ABC, abstractmethod +from typing import List, Optional + +import torch +from PIL import Image +from transformers import is_torch_xpu_available + + +class AbstractMultimodalPipeline(ABC): + @staticmethod + @abstractmethod + def name() -> str: + 'name of the pipeline, should be same as in --multimodal-pipeline' + pass + + @staticmethod + @abstractmethod + def image_start() -> Optional[str]: + 'return image start string, string representation of image start token, or None if not applicable' + pass + + @staticmethod + @abstractmethod + def image_end() -> Optional[str]: + 'return image end string, string representation of image end token, or None if not applicable' + pass + + @staticmethod + @abstractmethod + def placeholder_token_id() -> int: + 'return placeholder token id' + pass + + @staticmethod + @abstractmethod + def num_image_embeds() -> int: + 'return the number of embeds used by a single image (for example: 256 for LLaVA)' + pass + + @abstractmethod + def embed_images(self, images: List[Image.Image]) -> torch.Tensor: + 'forward the images through vision pipeline, and return their embeddings' + pass + + @staticmethod + @abstractmethod + def embed_tokens(input_ids: torch.Tensor) -> torch.Tensor: + 'embed tokens, the exact function varies by LLM, for LLaMA it is `shared.model.model.embed_tokens`' + pass + + @staticmethod + @abstractmethod + def placeholder_embeddings() -> torch.Tensor: + 'get placeholder embeddings if there are multiple images, and `add_all_images_to_prompt` is False' + pass + + def _get_device(self, setting_name: str, params: dict): + if params[setting_name] is None: + return torch.device("cuda:0" if torch.cuda.is_available() else "xpu:0" if is_torch_xpu_available() else "cpu") + return torch.device(params[setting_name]) + + def _get_dtype(self, setting_name: str, params: dict): + return torch.float32 if int(params[setting_name]) == 32 else torch.float16 diff --git a/extensions/multimodal/multimodal_embedder.py b/extensions/multimodal/multimodal_embedder.py new file mode 100644 index 0000000000000000000000000000000000000000..626077cb80987d66af90f390e31aa2f2def76fec --- /dev/null +++ b/extensions/multimodal/multimodal_embedder.py @@ -0,0 +1,178 @@ +import base64 +import re +from dataclasses import dataclass +from io import BytesIO +from typing import Any, List, Optional + +import torch +from PIL import Image + +from extensions.multimodal.pipeline_loader import load_pipeline +from modules import shared +from modules.logging_colors import logger +from modules.text_generation import encode, get_max_prompt_length + + +@dataclass +class PromptPart: + text: str + image: Optional[Image.Image] = None + is_image: bool = False + input_ids: Optional[torch.Tensor] = None + embedding: Optional[torch.Tensor] = None + + +class MultimodalEmbedder: + def __init__(self, params: dict): + pipeline, source = load_pipeline(params) + self.pipeline = pipeline + logger.info(f'Multimodal: loaded pipeline {self.pipeline.name()} from pipelines/{source} ({self.pipeline.__class__.__name__})') + + def _split_prompt(self, prompt: str, load_images: bool = False) -> List[PromptPart]: + """Splits a prompt into a list of `PromptParts` to separate image data from text. + It will also append `image_start` and `image_end` before and after the image, and optionally parse and load the images, + if `load_images` is `True`. + """ + parts: List[PromptPart] = [] + curr = 0 + while True: + match = re.search(r'', prompt[curr:]) + if match is None: + # no more image tokens, append the rest of the prompt + if curr > 0: + # add image end token after last image + parts.append(PromptPart(text=self.pipeline.image_end() + prompt[curr:])) + else: + parts.append(PromptPart(text=prompt)) + break + # found an image, append image start token to the text + if match.start() > 0: + parts.append(PromptPart(text=prompt[curr:curr + match.start()] + self.pipeline.image_start())) + else: + parts.append(PromptPart(text=self.pipeline.image_start())) + # append the image + parts.append(PromptPart( + text=match.group(0), + image=Image.open(BytesIO(base64.b64decode(match.group(1)))) if load_images else None, + is_image=True + )) + curr += match.end() + return parts + + def _len_in_tokens_prompt_parts(self, parts: List[PromptPart]) -> int: + """Total length in tokens of all `parts`""" + tokens = 0 + for part in parts: + if part.is_image: + tokens += self.pipeline.num_image_embeds() + elif part.input_ids is not None: + tokens += len(part.input_ids) + else: + tokens += len(encode(part.text)[0]) + return tokens + + def len_in_tokens(self, prompt: str) -> int: + """Total length in tokens for a given text `prompt`""" + parts = self._split_prompt(prompt, False) + return self._len_in_tokens_prompt_parts(parts) + + def _encode_single_text(self, part: PromptPart, add_bos_token: bool) -> PromptPart: + """Encode a single prompt `part` to `input_ids`. Returns a `PromptPart`""" + if part.is_image: + placeholders = torch.ones((self.pipeline.num_image_embeds())) * self.pipeline.placeholder_token_id() + part.input_ids = placeholders.to(shared.model.device, dtype=torch.int64) + else: + part.input_ids = encode(part.text, add_bos_token=add_bos_token)[0].to(shared.model.device, dtype=torch.int64) + return part + + @staticmethod + def _num_images(parts: List[PromptPart]) -> int: + count = 0 + for part in parts: + if part.is_image: + count += 1 + return count + + def _encode_text(self, state, parts: List[PromptPart]) -> List[PromptPart]: + """Encode text to token_ids, also truncate the prompt, if necessary. + + The chat/instruct mode should make prompts that fit in get_max_prompt_length, but if max_new_tokens are set + such that the context + min_rows don't fit, we can get a prompt which is too long. + We can't truncate image embeddings, as it leads to broken generation, so remove the images instead and warn the user + """ + encoded: List[PromptPart] = [] + for i, part in enumerate(parts): + encoded.append(self._encode_single_text(part, i == 0 and state['add_bos_token'])) + + # truncation: + max_len = get_max_prompt_length(state) + removed_images = 0 + + # 1. remove entire text/image blocks + while self._len_in_tokens_prompt_parts(encoded[1:]) > max_len: + if encoded[0].is_image: + removed_images += 1 + encoded = encoded[1:] + + # 2. check if the last prompt part doesn't need to get truncated + if self._len_in_tokens_prompt_parts(encoded) > max_len: + if encoded[0].is_image: + # don't truncate image embeddings, just remove the image, otherwise generation will be broken + removed_images += 1 + encoded = encoded[1:] + elif len(encoded) > 1 and encoded[0].text.endswith(self.pipeline.image_start()): + # see if we can keep image_start token + len_image_start = len(encode(self.pipeline.image_start(), add_bos_token=state['add_bos_token'])[0]) + if self._len_in_tokens_prompt_parts(encoded[1:]) + len_image_start > max_len: + # we can't -> remove this text, and the image + encoded = encoded[2:] + removed_images += 1 + else: + # we can -> just truncate the text + trunc_len = self._len_in_tokens_prompt_parts(encoded) - max_len + encoded[0].input_ids = encoded[0].input_ids[trunc_len:] + elif len(encoded) > 0: + # only one text left, truncate it normally + trunc_len = self._len_in_tokens_prompt_parts(encoded) - max_len + encoded[0].input_ids = encoded[0].input_ids[trunc_len:] + + # notify user if we truncated an image + if removed_images > 0: + logger.warning(f"Multimodal: removed {removed_images} image(s) from prompt. Try decreasing max_new_tokens if generation is broken") + + return encoded + + def _embed(self, parts: List[PromptPart]) -> List[PromptPart]: + # batch images + image_indicies = [i for i, part in enumerate(parts) if part.is_image] + embedded = self.pipeline.embed_images([parts[i].image for i in image_indicies]) + for i, embeds in zip(image_indicies, embedded): + parts[i].embedding = embeds + # embed text + for (i, part) in enumerate(parts): + if not part.is_image: + parts[i].embedding = self.pipeline.embed_tokens(part.input_ids) + return parts + + def _remove_old_images(self, parts: List[PromptPart], params: dict) -> List[PromptPart]: + if params['add_all_images_to_prompt']: + return parts + already_added = False + for i, part in reversed(list(enumerate(parts))): + if part.is_image: + if already_added: + parts[i].embedding = self.pipeline.placeholder_embeddings() + else: + already_added = True + return parts + + def forward(self, prompt: str, state: Any, params: dict): + prompt_parts = self._split_prompt(prompt, True) + prompt_parts = self._encode_text(state, prompt_parts) + prompt_parts = self._embed(prompt_parts) + prompt_parts = self._remove_old_images(prompt_parts, params) + embeds = tuple(part.embedding for part in prompt_parts) + ids = tuple(part.input_ids for part in prompt_parts) + input_embeds = torch.cat(embeds, dim=0) + input_ids = torch.cat(ids, dim=0) + return prompt, input_ids, input_embeds, self._num_images(prompt_parts) diff --git a/extensions/multimodal/pipeline_loader.py b/extensions/multimodal/pipeline_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..8fcd0a9b410fbc44a51941e0a87b294de871ef8b --- /dev/null +++ b/extensions/multimodal/pipeline_loader.py @@ -0,0 +1,52 @@ +import traceback +from importlib import import_module +from pathlib import Path +from typing import Tuple + +from extensions.multimodal.abstract_pipeline import AbstractMultimodalPipeline +from modules import shared +from modules.logging_colors import logger + + +def _get_available_pipeline_modules(): + pipeline_path = Path(__file__).parent / 'pipelines' + modules = [p for p in pipeline_path.iterdir() if p.is_dir()] + return [m.name for m in modules if (m / 'pipelines.py').exists()] + + +def load_pipeline(params: dict) -> Tuple[AbstractMultimodalPipeline, str]: + pipeline_modules = {} + available_pipeline_modules = _get_available_pipeline_modules() + for name in available_pipeline_modules: + try: + pipeline_modules[name] = import_module(f'extensions.multimodal.pipelines.{name}.pipelines') + except: + logger.warning(f'Failed to get multimodal pipelines from {name}') + logger.warning(traceback.format_exc()) + + if shared.args.multimodal_pipeline is not None: + for k in pipeline_modules: + if hasattr(pipeline_modules[k], 'get_pipeline'): + pipeline = getattr(pipeline_modules[k], 'get_pipeline')(shared.args.multimodal_pipeline, params) + if pipeline is not None: + return (pipeline, k) + else: + model_name = shared.args.model.lower() + for k in pipeline_modules: + if hasattr(pipeline_modules[k], 'get_pipeline_from_model_name'): + pipeline = getattr(pipeline_modules[k], 'get_pipeline_from_model_name')(model_name, params) + if pipeline is not None: + return (pipeline, k) + + available = [] + for k in pipeline_modules: + if hasattr(pipeline_modules[k], 'available_pipelines'): + pipelines = getattr(pipeline_modules[k], 'available_pipelines') + available += pipelines + + if shared.args.multimodal_pipeline is not None: + log = f'Multimodal - ERROR: Failed to load multimodal pipeline "{shared.args.multimodal_pipeline}", available pipelines are: {available}.' + else: + log = f'Multimodal - ERROR: Failed to determine multimodal pipeline for model {shared.args.model}, please select one manually using --multimodal-pipeline [PIPELINE]. Available pipelines are: {available}.' + logger.critical(f'{log} Please specify a correct pipeline, or disable the extension') + raise RuntimeError(f'{log} Please specify a correct pipeline, or disable the extension') diff --git a/extensions/multimodal/pipelines/llava/README.md b/extensions/multimodal/pipelines/llava/README.md new file mode 100644 index 0000000000000000000000000000000000000000..aff64faaae07d2f4da6c24e8ea03693326313139 --- /dev/null +++ b/extensions/multimodal/pipelines/llava/README.md @@ -0,0 +1,9 @@ +## LLaVA pipeline + +This module provides 2 pipelines: +- `llava-7b` - for use with LLaVA v0 7B model (finetuned LLaMa 7B) +- `llava-13b` - for use with LLaVA v0 13B model (finetuned LLaMa 13B) + +[LLaVA](https://github.com/haotian-liu/LLaVA) uses CLIP `openai/clip-vit-large-patch14` as the vision model, and then a single linear layer. For 13B the projector weights are in `liuhaotian/LLaVA-13b-delta-v0`, and for 7B they are in `liuhaotian/LLaVA-7b-delta-v0`. + +The supported parameter combinations for both the vision model, and the projector are: CUDA/32bit, CUDA/16bit, CPU/32bit diff --git a/extensions/multimodal/pipelines/llava/llava.py b/extensions/multimodal/pipelines/llava/llava.py new file mode 100644 index 0000000000000000000000000000000000000000..09b5aff7b8826b5da2645be39075b599af2d24da --- /dev/null +++ b/extensions/multimodal/pipelines/llava/llava.py @@ -0,0 +1,262 @@ +import time +from abc import abstractmethod +from typing import List, Tuple + +import torch +from huggingface_hub import hf_hub_download +from PIL import Image +from transformers import CLIPImageProcessor, CLIPVisionModel + +from extensions.multimodal.abstract_pipeline import AbstractMultimodalPipeline +from modules import shared +from modules.logging_colors import logger +from modules.text_generation import encode + + +def expand2square(pil_img: Image.Image, background_color: Tuple[int]) -> Image.Image: + width, height = pil_img.size + if width == height: + return pil_img + elif width > height: + result = Image.new(pil_img.mode, (width, width), background_color) + result.paste(pil_img, (0, (width - height) // 2)) + return result + else: + result = Image.new(pil_img.mode, (height, height), background_color) + result.paste(pil_img, ((height - width) // 2, 0)) + return result + + +class LLaVA_v0_Pipeline(AbstractMultimodalPipeline): + CLIP_REPO = "openai/clip-vit-large-patch14" + + def __init__(self, params: dict) -> None: + super().__init__() + self.clip_device = self._get_device("vision_device", params) + self.clip_dtype = self._get_dtype("vision_bits", params) + self.projector_device = self._get_device("projector_device", params) + self.projector_dtype = self._get_dtype("projector_bits", params) + self.image_processor, self.vision_tower, self.mm_projector = self._load_models() + + def _load_models(self): + start_ts = time.time() + + logger.info(f"LLaVA - Loading CLIP from {self.CLIP_REPO} as {self.clip_dtype} on {self.clip_device}...") + image_processor = CLIPImageProcessor.from_pretrained(self.CLIP_REPO, torch_dtype=self.clip_dtype) + vision_tower = CLIPVisionModel.from_pretrained(self.CLIP_REPO, torch_dtype=self.clip_dtype).to(self.clip_device) + + logger.info(f"LLaVA - Loading projector from {self.llava_projector_repo()} as {self.projector_dtype} on {self.projector_device}...") + projector_path = hf_hub_download(self.llava_projector_repo(), self.llava_projector_filename()) + mm_projector = self.build_mm_projector() + projector_data = torch.load(projector_path) + projector_data = {k[19:]: v for k, v in projector_data.items() if k.startswith('model.mm_projector.')} + mm_projector.load_state_dict(projector_data) + mm_projector = mm_projector.to(self.projector_device) + + logger.info(f"LLaVA supporting models loaded, took {time.time() - start_ts:.2f} seconds") + return image_processor, vision_tower, mm_projector + + def build_mm_projector(self) -> torch.nn.Module: + projector_shape = self.llava_projector_shape() + if len(projector_shape) == 2: + return torch.nn.Linear(*projector_shape) + else: + modules = [] + modules.append(torch.nn.Linear(projector_shape[0], projector_shape[1])) + for i in range(2, len(projector_shape)): + modules.append(torch.nn.GELU()) + modules.append(torch.nn.Linear(projector_shape[i-1], projector_shape[i])) + return torch.nn.Sequential(*modules) + + @staticmethod + def image_start() -> str: + return "" + + @staticmethod + def image_end() -> str: + return "" + + @staticmethod + def num_image_embeds() -> int: + return 256 + + @staticmethod + def embed_tokens(input_ids: torch.Tensor) -> torch.Tensor: + for attr in ['', 'model', 'model.model', 'model.model.model']: + tmp = getattr(shared.model, attr, None) if attr != '' else shared.model + if tmp is not None and hasattr(tmp, 'embed_tokens'): + func = tmp.embed_tokens + break + else: + raise ValueError('The embed_tokens method has not been found for this loader.') + + return func(input_ids).to(shared.model.device, dtype=shared.model.dtype) + + @staticmethod + def placeholder_embeddings() -> torch.Tensor: + return LLaVA_v0_Pipeline.embed_tokens(encode(""*256, add_bos_token=False)[0]) + + def embed_images(self, images: List[Image.Image]) -> torch.Tensor: + images = self.image_processor(images, return_tensors='pt')['pixel_values'] + images = images.to(self.clip_device, dtype=self.clip_dtype) + + with torch.no_grad(): + image_forward_outs = self.vision_tower(images, output_hidden_states=True) + select_hidden_state_layer = -2 + select_hidden_state = image_forward_outs.hidden_states[select_hidden_state_layer] + image_features = select_hidden_state[:, 1:].to(self.projector_device, dtype=self.projector_dtype) + image_features = self.mm_projector(image_features) + return image_features.to(shared.model.device, dtype=shared.model.dtype) + + @staticmethod + @abstractmethod + def llava_projector_repo() -> str: + pass + + @staticmethod + @abstractmethod + def llava_projector_filename() -> str: + pass + + @staticmethod + @abstractmethod + def llava_projector_shape() -> Tuple[int, int]: + pass + + +class LLaVA_v0_13B_Pipeline(LLaVA_v0_Pipeline): + def __init__(self, params: dict) -> None: + super().__init__(params) + + @staticmethod + def name() -> str: + return "llava-13b" + + @staticmethod + def placeholder_token_id() -> int: + return 32000 + + @staticmethod + def llava_projector_shape() -> Tuple[int, int]: + return (1024, 5120) + + @staticmethod + def llava_projector_filename() -> str: + return "mm_projector.bin" + + @staticmethod + def llava_projector_repo() -> str: + return "liuhaotian/LLaVA-13b-delta-v0" + + +class LLaVA_v0_7B_Pipeline(LLaVA_v0_Pipeline): + def __init__(self, params: dict) -> None: + super().__init__(params) + + @staticmethod + def name() -> str: + return "llava-7b" + + @staticmethod + def placeholder_token_id() -> int: + return 32001 + + @staticmethod + def llava_projector_shape() -> Tuple[int, int]: + return (1024, 4096) + + @staticmethod + def llava_projector_filename() -> str: + return "mm_projector.bin" + + @staticmethod + def llava_projector_repo() -> str: + return "liuhaotian/LLaVA-7b-delta-v0" + + +class LLaVA_LLaMA_2_13B_Pipeline(LLaVA_v0_13B_Pipeline): + def __init__(self, params: dict) -> None: + super().__init__(params) + + @staticmethod + def name() -> str: + return "llava-llama-2-13b" + + @staticmethod + def placeholder_token_id() -> int: + return 0 + + @staticmethod + def llava_projector_repo() -> str: + return "liuhaotian/llava-llama-2-13b-chat-lightning-preview" + + @staticmethod + def image_start() -> str: + return "" + + @staticmethod + def image_end() -> str: + return "" + + @staticmethod + def placeholder_embeddings() -> torch.Tensor: + return LLaVA_v0_Pipeline.embed_tokens(encode(""*256, add_bos_token=False)[0]) + + +class LLaVA_v1_5_13B_Pipeline(LLaVA_v0_13B_Pipeline): + CLIP_REPO = "openai/clip-vit-large-patch14-336" + + def __init__(self, params: dict) -> None: + super().__init__(params) + + @staticmethod + def name() -> str: + return "llava-v1.5-13b" + + @staticmethod + def llava_projector_shape() -> Tuple[int, int]: + return (1024, 5120, 5120) + + @staticmethod + def placeholder_token_id() -> int: + return 0 + + @staticmethod + def llava_projector_repo() -> str: + return "liuhaotian/llava-v1.5-13b" + + @staticmethod + def image_start() -> str: + return "" + + @staticmethod + def image_end() -> str: + return "" + + @staticmethod + def num_image_embeds() -> int: + return 576 + + def embed_images(self, images: List[Image.Image]) -> torch.Tensor: + # pad it to square first + images = [ + expand2square(image, tuple(int(x*255) for x in self.image_processor.image_mean)) + for image in images + ] + return super().embed_images(images) + + @staticmethod + def placeholder_embeddings() -> torch.Tensor: + return LLaVA_v0_Pipeline.embed_tokens(encode(""*576, add_bos_token=False)[0]) + +class LLaVA_v1_5_7B_Pipeline(LLaVA_v1_5_13B_Pipeline): + @staticmethod + def name() -> str: + return "llava-v1.5-7b" + + @staticmethod + def llava_projector_shape() -> Tuple[int, int]: + return (1024, 4096, 4096) + @staticmethod + def llava_projector_repo() -> str: + return "liuhaotian/llava-v1.5-7b" \ No newline at end of file diff --git a/extensions/multimodal/pipelines/llava/pipelines.py b/extensions/multimodal/pipelines/llava/pipelines.py new file mode 100644 index 0000000000000000000000000000000000000000..e6833ed6ff94f8e2c8e8494bc827843f388b7fb8 --- /dev/null +++ b/extensions/multimodal/pipelines/llava/pipelines.py @@ -0,0 +1,48 @@ +from typing import Optional + +from extensions.multimodal.abstract_pipeline import AbstractMultimodalPipeline + +available_pipelines = ['llava-7b', 'llava-13b', 'llava-llama-2-13b', 'llava-v1.5-13b', 'llava-v1.5-7b'] + + +def get_pipeline(name: str, params: dict) -> Optional[AbstractMultimodalPipeline]: + if name == 'llava-7b': + from .llava import LLaVA_v0_7B_Pipeline + return LLaVA_v0_7B_Pipeline(params) + if name == 'llava-13b': + from .llava import LLaVA_v0_13B_Pipeline + return LLaVA_v0_13B_Pipeline(params) + if name == 'llava-llama-2-13b': + from .llava import LLaVA_LLaMA_2_13B_Pipeline + return LLaVA_LLaMA_2_13B_Pipeline(params) + if name == 'llava-v1.5-7b': + from .llava import LLaVA_v1_5_7B_Pipeline + return LLaVA_v1_5_7B_Pipeline(params) + if name == 'llava-v1.5-13b': + from .llava import LLaVA_v1_5_13B_Pipeline + return LLaVA_v1_5_13B_Pipeline(params) + return None + + +def get_pipeline_from_model_name(model_name: str, params: dict) -> Optional[AbstractMultimodalPipeline]: + if 'llava' not in model_name.lower(): + return None + if 'llama-2' in model_name.lower(): + if '13b' in model_name.lower(): + from .llava import LLaVA_LLaMA_2_13B_Pipeline + return LLaVA_LLaMA_2_13B_Pipeline(params) + elif 'llava-v1.5' in model_name.lower(): + if '13b' in model_name.lower(): + from .llava import LLaVA_v1_5_13B_Pipeline + return LLaVA_v1_5_13B_Pipeline(params) + if '7b' in model_name.lower(): + from .llava import LLaVA_v1_5_7B_Pipeline + return LLaVA_v1_5_7B_Pipeline(params) + else: + if '7b' in model_name.lower(): + from .llava import LLaVA_v0_7B_Pipeline + return LLaVA_v0_7B_Pipeline(params) + if '13b' in model_name.lower(): + from .llava import LLaVA_v0_13B_Pipeline + return LLaVA_v0_13B_Pipeline(params) + return None diff --git a/extensions/multimodal/pipelines/place-additional-pipelines-here.txt b/extensions/multimodal/pipelines/place-additional-pipelines-here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/extensions/multimodal/script.py b/extensions/multimodal/script.py new file mode 100644 index 0000000000000000000000000000000000000000..6607ce5a32b7f05d51de3f69820bb62124d8450a --- /dev/null +++ b/extensions/multimodal/script.py @@ -0,0 +1,113 @@ +import base64 +import re +import time +from functools import partial +from io import BytesIO + +import gradio as gr +import torch + +from extensions.multimodal.multimodal_embedder import MultimodalEmbedder +from modules import shared +from modules.logging_colors import logger + +params = { + "add_all_images_to_prompt": False, + # device to run vision encoder on + "vision_device": None, + # bits to load vision encoder in, either 16 or 32 + "vision_bits": 32, + # device to run multimodal projector on + "projector_device": None, + # multimodal projector bits, either 32 or 16 + "projector_bits": 32 +} + + +# If 'state' is True, will hijack the next chat generation +input_hijack = { + 'state': False, + 'value': ["", ""] +} + + +# initialized in ui, so that params are loaded from settings +multimodal_embedder: MultimodalEmbedder = None + + +def chat_input_modifier(text, visible_text, state): + global input_hijack + if input_hijack['state']: + input_hijack['state'] = False + return input_hijack['value'](text, visible_text) + else: + return text, visible_text + + +def add_chat_picture(picture, text, visible_text): + # resize the image, so that shortest edge is at least 224 (size for CLIP), and at most 300 (to keep history manageable) + # Adjusted to 336 for the values here, due to the increased resolution in llava-v1.5 + max_hw, min_hw = max(picture.size), min(picture.size) + aspect_ratio = max_hw / min_hw + shortest_edge = int(max(336 / aspect_ratio, 336)) + longest_edge = int(shortest_edge * aspect_ratio) + w = shortest_edge if picture.width < picture.height else longest_edge + h = shortest_edge if picture.width >= picture.height else longest_edge + picture = picture.resize((w, h)) + + buffer = BytesIO() + picture.save(buffer, format="PNG") + img_str = base64.b64encode(buffer.getvalue()).decode('utf-8') + image = f'' + + if '' in text: + text = text.replace('', image) + else: + text = image + '\n' + text + + if visible_text == '' or visible_text is None: + visible_text = text + elif '' in visible_text: + visible_text = visible_text.replace('', image) + else: + visible_text = visible_text + '\n' + image + + return text, visible_text + + +def custom_tokenized_length(prompt): + return multimodal_embedder.len_in_tokens(prompt) + + +def tokenizer_modifier(state, prompt, input_ids, input_embeds): + global params + start_ts = time.time() + image_match = re.search(r'', prompt) + + if image_match is None: + return prompt, input_ids, input_embeds + + prompt, input_ids, input_embeds, total_embedded = multimodal_embedder.forward(prompt, state, params) + logger.info(f'Embedded {total_embedded} image(s) in {time.time()-start_ts:.2f}s') + return (prompt, + input_ids.unsqueeze(0).to(shared.model.device, dtype=torch.int64), + input_embeds.unsqueeze(0).to(shared.model.device, dtype=shared.model.dtype)) + + +def ui(): + global multimodal_embedder + multimodal_embedder = MultimodalEmbedder(params) + with gr.Column(): + picture_select = gr.Image(label='Send a picture', type='pil') + # The models don't seem to deal well with multiple images + single_image_checkbox = gr.Checkbox(False, label='Embed all images, not only the last one') + # Prepare the input hijack + picture_select.upload( + lambda picture: input_hijack.update({"state": True, "value": partial(add_chat_picture, picture)}), + [picture_select], + None + ) + picture_select.clear(lambda: input_hijack.update({"state": False, "value": ["", ""]}), None, None) + single_image_checkbox.change(lambda x: params.update({"add_all_images_to_prompt": x}), single_image_checkbox, None) + shared.gradio['Generate'].click(lambda: None, None, picture_select) + shared.gradio['textbox'].submit(lambda: None, None, picture_select) diff --git a/extensions/ngrok/README.md b/extensions/ngrok/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0324bf9852408d9d2b86cc0165c2d548996f9c94 --- /dev/null +++ b/extensions/ngrok/README.md @@ -0,0 +1,69 @@ +# Adding an ingress URL through the ngrok Agent SDK for Python + +[ngrok](https://ngrok.com) is a globally distributed reverse proxy commonly used for quickly getting a public URL to a +service running inside a private network, such as on your local laptop. The ngrok agent is usually +deployed inside a private network and is used to communicate with the ngrok cloud service. + +By default the authtoken in the NGROK_AUTHTOKEN environment variable will be used. Alternatively one may be specified in +the `settings.json` file, see the Examples below. Retrieve your authtoken on the [Auth Token page of your ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken), signing up is free. + +# Documentation + +For a list of all available options, see [the configuration documentation](https://ngrok.com/docs/ngrok-agent/config/) or [the connect example](https://github.com/ngrok/ngrok-py/blob/main/examples/ngrok-connect-full.py). + +The ngrok Python SDK is [on github here](https://github.com/ngrok/ngrok-py). A quickstart guide and a full API reference are included in the [ngrok-py Python API documentation](https://ngrok.github.io/ngrok-py/). + +# Running + +To enable ngrok install the requirements and then add `--extension ngrok` to the command line options, for instance: + +```bash +pip install -r extensions/ngrok/requirements.txt +python server.py --extension ngrok +``` + +In the output you should then see something like this: + +```bash +INFO:Loading the extension "ngrok"... +INFO:Session created +INFO:Created tunnel "9d9d0944dc75ff9d3aae653e5eb29fe9" with url "https://d83706cf7be7.ngrok.app" +INFO:Tunnel "9d9d0944dc75ff9d3aae653e5eb29fe9" TCP forwarding to "localhost:7860" +INFO:Ingress established at https://d83706cf7be7.ngrok.app +``` + +You can now access the webui via the url shown, in this case `https://d83706cf7be7.ngrok.app`. It is recommended to add some authentication to the ingress, see below. + +# Example Settings + +In `settings.json` add a `ngrok` key with a dictionary of options, for instance: + +To enable basic authentication: +```json +{ + "ngrok": { + "basic_auth": "user:password" + } +} +``` + +To enable OAUTH authentication: +```json +{ + "ngrok": { + "oauth_provider": "google", + "oauth_allow_domains": "asdf.com", + "oauth_allow_emails": "asdf@asdf.com" + } +} +``` + +To add an authtoken instead of using the NGROK_AUTHTOKEN environment variable: +```json +{ + "ngrok": { + "authtoken": "", + "authtoken_from_env":false + } +} +``` \ No newline at end of file diff --git a/extensions/ngrok/requirements.txt b/extensions/ngrok/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8acb99f2a1f2a165f496121e539949e18f3fcd81 --- /dev/null +++ b/extensions/ngrok/requirements.txt @@ -0,0 +1 @@ +ngrok==0.* diff --git a/extensions/ngrok/script.py b/extensions/ngrok/script.py new file mode 100644 index 0000000000000000000000000000000000000000..46f39bd327b6046f8e0d38ef266fc7d3687640da --- /dev/null +++ b/extensions/ngrok/script.py @@ -0,0 +1,36 @@ +# Adds ngrok ingress, to use add `--extension ngrok` to the command line options +# +# Parameters can be customized in settings.json of webui, e.g.: +# {"ngrok": {"basic_auth":"user:password"} } +# or +# {"ngrok": {"oauth_provider":"google", "oauth_allow_emails":["asdf@asdf.com"]} } +# +# See this example for full list of options: https://github.com/ngrok/ngrok-py/blob/main/examples/ngrok-connect-full.py +# or the README.md in this directory. + +import logging +from modules import shared + +# Pick up host/port command line arguments +host = shared.args.listen_host if shared.args.listen_host and shared.args.listen else '127.0.0.1' +port = shared.args.listen_port if shared.args.listen_port else '7860' + +# Default options +options = { + 'addr': f"{host}:{port}", + 'authtoken_from_env': True, + 'session_metadata': 'text-generation-webui', +} + + +def ui(): + settings = shared.settings.get("ngrok") + if settings: + options.update(settings) + + try: + import ngrok + tunnel = ngrok.connect(**options) + logging.info(f"Ingress established at: {tunnel.url()}") + except ModuleNotFoundError: + logging.error("===> ngrok library not found, please run `pip install -r extensions/ngrok/requirements.txt`") diff --git a/extensions/openai/cache_embedding_model.py b/extensions/openai/cache_embedding_model.py new file mode 100644 index 0000000000000000000000000000000000000000..8166cb4b491b681314150b6dd36b4c29899db6ae --- /dev/null +++ b/extensions/openai/cache_embedding_model.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +# preload the embedding model, useful for Docker images to prevent re-download on config change +# Dockerfile: +# ENV OPENEDAI_EMBEDDING_MODEL="sentence-transformers/all-mpnet-base-v2" # Optional +# RUN python3 cache_embedded_model.py +import os + +import sentence_transformers + +st_model = os.environ.get("OPENEDAI_EMBEDDING_MODEL", "sentence-transformers/all-mpnet-base-v2") +model = sentence_transformers.SentenceTransformer(st_model) diff --git a/extensions/openai/completions.py b/extensions/openai/completions.py new file mode 100644 index 0000000000000000000000000000000000000000..c6deefb3d2f8b3431474dc7c81288181054a755d --- /dev/null +++ b/extensions/openai/completions.py @@ -0,0 +1,553 @@ +import base64 +import copy +import re +import time +from collections import deque +from io import BytesIO + +import requests +import tiktoken +import torch +import torch.nn.functional as F +from PIL import Image +from transformers import LogitsProcessor, LogitsProcessorList + +from extensions.openai.errors import InvalidRequestError +from extensions.openai.utils import debug_msg +from modules import shared +from modules.chat import ( + generate_chat_prompt, + generate_chat_reply, + load_character_memoized, + load_instruction_template_memoized +) +from modules.presets import load_preset_memoized +from modules.text_generation import ( + decode, + encode, + generate_reply, + get_reply_from_output_ids +) + + +class LogitsBiasProcessor(LogitsProcessor): + def __init__(self, logit_bias={}): + self.logit_bias = logit_bias + if self.logit_bias: + self.keys = list([int(key) for key in self.logit_bias.keys()]) + values = [self.logit_bias[str(key)] for key in self.keys] + self.values = torch.tensor(values, dtype=torch.float, device=shared.model.device) + debug_msg(f"{self})") + + def __call__(self, input_ids: torch.LongTensor, logits: torch.FloatTensor) -> torch.FloatTensor: + if self.logit_bias: + debug_msg(logits[0, self.keys], " + ", self.values) + logits[0, self.keys] += self.values + debug_msg(" --> ", logits[0, self.keys]) + debug_msg(" max/min ", float(torch.max(logits[0])), float(torch.min(logits[0]))) + + return logits + + def __repr__(self): + return f"<{self.__class__.__name__}(logit_bias={self.logit_bias})>" + + +class LogprobProcessor(LogitsProcessor): + def __init__(self, logprobs=None): + self.logprobs = logprobs + self.token_alternatives = {} + + def __call__(self, input_ids: torch.LongTensor, logits: torch.FloatTensor) -> torch.FloatTensor: + if self.logprobs is not None: # 0-5 + log_e_probabilities = F.log_softmax(logits, dim=1) + top_values, top_indices = torch.topk(log_e_probabilities, k=self.logprobs + 1) + top_tokens = [get_reply_from_output_ids([tok]) for tok in top_indices[0]] + top_probs = [float(x) for x in top_values[0]] + self.token_alternatives = dict(zip(top_tokens, top_probs)) + debug_msg(repr(self)) + + return logits + + def __repr__(self): + return f"<{self.__class__.__name__}(logprobs={self.logprobs}, token_alternatives={self.token_alternatives})>" + + +def convert_logprobs_to_tiktoken(model, logprobs): + # more problems than it's worth. + # try: + # encoder = tiktoken.encoding_for_model(model) + # # just pick the first one if it encodes to multiple tokens... 99.9% not required and maybe worse overall. + # return dict([(encoder.decode([encoder.encode(token)[0]]), prob) for token, prob in logprobs.items()]) + # except KeyError: + # # assume native tokens if we can't find the tokenizer + # return logprobs + + return logprobs + + +def process_parameters(body, is_legacy=False): + generate_params = body + max_tokens_str = 'length' if is_legacy else 'max_tokens' + generate_params['max_new_tokens'] = body.pop(max_tokens_str) + if generate_params['truncation_length'] == 0: + generate_params['truncation_length'] = shared.settings['truncation_length'] + + if generate_params['temperature'] == 0: + generate_params['do_sample'] = False + generate_params['top_k'] = 1 + + if body['preset'] is not None: + preset = load_preset_memoized(body['preset']) + generate_params.update(preset) + + generate_params['custom_stopping_strings'] = [] + if 'stop' in body: # str or array, max len 4 (ignored) + if isinstance(body['stop'], str): + generate_params['custom_stopping_strings'] = [body['stop']] + elif isinstance(body['stop'], list): + generate_params['custom_stopping_strings'] = body['stop'] + + logits_processor = [] + logit_bias = body.get('logit_bias', None) + if logit_bias: # {str: float, ...} + logits_processor = [LogitsBiasProcessor(logit_bias)] + + logprobs = None # coming to chat eventually + if 'logprobs' in body: + logprobs = body.get('logprobs', 0) # maybe cap at topk? don't clamp 0-5. + generate_params['logprob_proc'] = LogprobProcessor(logprobs) + logits_processor.extend([generate_params['logprob_proc']]) + else: + logprobs = None + + if logits_processor: # requires logits_processor support + generate_params['logits_processor'] = LogitsProcessorList(logits_processor) + + return generate_params + + +def convert_history(history): + ''' + Chat histories in this program are in the format [message, reply]. + This function converts OpenAI histories to that format. + ''' + chat_dialogue = [] + current_message = "" + current_reply = "" + user_input = "" + system_message = "" + + # Multimodal: convert OpenAI format to multimodal extension format + if any('content' in entry and isinstance(entry['content'], list) for entry in history): + new_history = [] + for entry in history: + if isinstance(entry['content'], list): + image_url = None + content = None + for item in entry['content']: + if not isinstance(item, dict): + continue + + if item['type'] == 'image_url' and isinstance(item['image_url'], dict): + image_url = item['image_url']['url'] + elif item['type'] == 'text' and isinstance(item['text'], str): + content = item['text'] + + if image_url and content: + new_history.append({"image_url": image_url, "role": "user"}) + new_history.append({"content": content, "role": "user"}) + else: + new_history.append(entry) + + history = new_history + + for entry in history: + if "image_url" in entry: + image_url = entry['image_url'] + if "base64" in image_url: + image_url = re.sub('^data:image/.+;base64,', '', image_url) + img = Image.open(BytesIO(base64.b64decode(image_url))) + else: + try: + my_res = requests.get(image_url) + img = Image.open(BytesIO(my_res.content)) + except Exception: + raise 'Image cannot be loaded from the URL!' + + buffered = BytesIO() + if img.mode in ("RGBA", "P"): + img = img.convert("RGB") + + img.save(buffered, format="JPEG") + img_str = base64.b64encode(buffered.getvalue()).decode('utf-8') + content = f'' + else: + content = entry["content"] + + role = entry["role"] + + if role == "user": + user_input = content + if current_message: + chat_dialogue.append([current_message, '']) + current_message = "" + + current_message = content + elif role == "assistant": + current_reply = content + if current_message: + chat_dialogue.append([current_message, current_reply]) + current_message = "" + current_reply = "" + else: + chat_dialogue.append(['', current_reply]) + elif role == "system": + system_message = content + + # if current_message: + # chat_dialogue.append([current_message, '']) + + return user_input, system_message, {'internal': chat_dialogue, 'visible': copy.deepcopy(chat_dialogue)} + + +def chat_completions_common(body: dict, is_legacy: bool = False, stream=False) -> dict: + if body.get('functions', []): + raise InvalidRequestError(message="functions is not supported.", param='functions') + + if body.get('function_call', ''): + raise InvalidRequestError(message="function_call is not supported.", param='function_call') + + if 'messages' not in body: + raise InvalidRequestError(message="messages is required", param='messages') + + messages = body['messages'] + for m in messages: + if 'role' not in m: + raise InvalidRequestError(message="messages: missing role", param='messages') + elif m['role'] == 'function': + raise InvalidRequestError(message="role: function is not supported.", param='messages') + + if 'content' not in m and "image_url" not in m: + raise InvalidRequestError(message="messages: missing content", param='messages') + + # Chat Completions + object_type = 'chat.completions' if not stream else 'chat.completions.chunk' + created_time = int(time.time()) + cmpl_id = "chatcmpl-%d" % (int(time.time() * 1000000000)) + resp_list = 'data' if is_legacy else 'choices' + + # generation parameters + generate_params = process_parameters(body, is_legacy=is_legacy) + continue_ = body['continue_'] + + # Instruction template + if body['instruction_template_str']: + instruction_template_str = body['instruction_template_str'] + elif body['instruction_template']: + instruction_template = body['instruction_template'] + instruction_template = "Alpaca" if instruction_template == "None" else instruction_template + instruction_template_str = load_instruction_template_memoized(instruction_template) + else: + instruction_template_str = shared.settings['instruction_template_str'] + + chat_template_str = body['chat_template_str'] or shared.settings['chat_template_str'] + chat_instruct_command = body['chat_instruct_command'] or shared.settings['chat-instruct_command'] + + # Chat character + character = body['character'] or shared.settings['character'] + character = "Assistant" if character == "None" else character + name1 = body['user_name'] or shared.settings['name1'] + name1, name2, _, greeting, context = load_character_memoized(character, name1, '') + name2 = body['bot_name'] or name2 + context = body['context'] or context + greeting = body['greeting'] or greeting + + # History + user_input, custom_system_message, history = convert_history(messages) + + generate_params.update({ + 'mode': body['mode'], + 'name1': name1, + 'name2': name2, + 'context': context, + 'greeting': greeting, + 'instruction_template_str': instruction_template_str, + 'custom_system_message': custom_system_message, + 'chat_template_str': chat_template_str, + 'chat-instruct_command': chat_instruct_command, + 'history': history, + 'stream': stream + }) + + max_tokens = generate_params['max_new_tokens'] + if max_tokens in [None, 0]: + generate_params['max_new_tokens'] = 512 + generate_params['auto_max_new_tokens'] = True + + requested_model = generate_params.pop('model') + logprob_proc = generate_params.pop('logprob_proc', None) + + def chat_streaming_chunk(content): + # begin streaming + chunk = { + "id": cmpl_id, + "object": object_type, + "created": created_time, + "model": shared.model_name, + resp_list: [{ + "index": 0, + "finish_reason": None, + # So yeah... do both methods? delta and messages. + "message": {'role': 'assistant', 'content': content}, + "delta": {'role': 'assistant', 'content': content}, + }], + } + + if logprob_proc: # not official for chat yet + top_logprobs = convert_logprobs_to_tiktoken(model=requested_model, logprobs=logprob_proc.token_alternatives) + chunk[resp_list][0]["logprobs"] = {'top_logprobs': [top_logprobs]} + # else: + # chunk[resp_list][0]["logprobs"] = None + return chunk + + if stream: + yield chat_streaming_chunk('') + + # generate reply ####################################### + prompt = generate_chat_prompt(user_input, generate_params) + token_count = len(encode(prompt)[0]) + debug_msg({'prompt': prompt, 'generate_params': generate_params}) + + generator = generate_chat_reply( + user_input, generate_params, regenerate=False, _continue=continue_, loading_message=False) + + answer = '' + seen_content = '' + completion_token_count = 0 + + for a in generator: + answer = a['internal'][-1][1] + if stream: + len_seen = len(seen_content) + new_content = answer[len_seen:] + + if not new_content or chr(0xfffd) in new_content: # partial unicode character, don't send it yet. + continue + + seen_content = answer + chunk = chat_streaming_chunk(new_content) + yield chunk + + completion_token_count = len(encode(answer)[0]) + stop_reason = "stop" + if token_count + completion_token_count >= generate_params['truncation_length'] or completion_token_count >= generate_params['max_new_tokens']: + stop_reason = "length" + + if stream: + chunk = chat_streaming_chunk('') + chunk[resp_list][0]['finish_reason'] = stop_reason + chunk['usage'] = { + "prompt_tokens": token_count, + "completion_tokens": completion_token_count, + "total_tokens": token_count + completion_token_count + } + + yield chunk + else: + resp = { + "id": cmpl_id, + "object": object_type, + "created": created_time, + "model": shared.model_name, + resp_list: [{ + "index": 0, + "finish_reason": stop_reason, + "message": {"role": "assistant", "content": answer} + }], + "usage": { + "prompt_tokens": token_count, + "completion_tokens": completion_token_count, + "total_tokens": token_count + completion_token_count + } + } + if logprob_proc: # not official for chat yet + top_logprobs = convert_logprobs_to_tiktoken(model=requested_model, logprobs=logprob_proc.token_alternatives) + resp[resp_list][0]["logprobs"] = {'top_logprobs': [top_logprobs]} + # else: + # resp[resp_list][0]["logprobs"] = None + + yield resp + + +def completions_common(body: dict, is_legacy: bool = False, stream=False): + object_type = 'text_completion.chunk' if stream else 'text_completion' + created_time = int(time.time()) + cmpl_id = "conv-%d" % (int(time.time() * 1000000000)) + resp_list = 'data' if is_legacy else 'choices' + + prompt_str = 'context' if is_legacy else 'prompt' + + # ... encoded as a string, array of strings, array of tokens, or array of token arrays. + if prompt_str not in body: + raise InvalidRequestError("Missing required input", param=prompt_str) + + # common params + generate_params = process_parameters(body, is_legacy=is_legacy) + max_tokens = generate_params['max_new_tokens'] + generate_params['stream'] = stream + requested_model = generate_params.pop('model') + logprob_proc = generate_params.pop('logprob_proc', None) + suffix = body['suffix'] if body['suffix'] else '' + echo = body['echo'] + + if not stream: + prompt_arg = body[prompt_str] + if isinstance(prompt_arg, str) or (isinstance(prompt_arg, list) and isinstance(prompt_arg[0], int)): + prompt_arg = [prompt_arg] + + resp_list_data = [] + total_completion_token_count = 0 + total_prompt_token_count = 0 + + for idx, prompt in enumerate(prompt_arg, start=0): + if isinstance(prompt[0], int): + # token lists + if requested_model == shared.model_name: + prompt = decode(prompt)[0] + else: + try: + encoder = tiktoken.encoding_for_model(requested_model) + prompt = encoder.decode(prompt) + except KeyError: + prompt = decode(prompt)[0] + + prefix = prompt if echo else '' + token_count = len(encode(prompt)[0]) + total_prompt_token_count += token_count + + # generate reply ####################################### + debug_msg({'prompt': prompt, 'generate_params': generate_params}) + generator = generate_reply(prompt, generate_params, is_chat=False) + answer = '' + + for a in generator: + answer = a + + completion_token_count = len(encode(answer)[0]) + total_completion_token_count += completion_token_count + stop_reason = "stop" + if token_count + completion_token_count >= generate_params['truncation_length'] or completion_token_count >= max_tokens: + stop_reason = "length" + + respi = { + "index": idx, + "finish_reason": stop_reason, + "text": prefix + answer + suffix, + "logprobs": {'top_logprobs': [logprob_proc.token_alternatives]} if logprob_proc else None, + } + + resp_list_data.extend([respi]) + + resp = { + "id": cmpl_id, + "object": object_type, + "created": created_time, + "model": shared.model_name, + resp_list: resp_list_data, + "usage": { + "prompt_tokens": total_prompt_token_count, + "completion_tokens": total_completion_token_count, + "total_tokens": total_prompt_token_count + total_completion_token_count + } + } + + yield resp + else: + prompt = body[prompt_str] + if isinstance(prompt, list): + if prompt and isinstance(prompt[0], int): + try: + encoder = tiktoken.encoding_for_model(requested_model) + prompt = encoder.decode(prompt) + except KeyError: + prompt = decode(prompt)[0] + else: + raise InvalidRequestError(message="API Batched generation not yet supported.", param=prompt_str) + + prefix = prompt if echo else '' + token_count = len(encode(prompt)[0]) + + def text_streaming_chunk(content): + # begin streaming + chunk = { + "id": cmpl_id, + "object": object_type, + "created": created_time, + "model": shared.model_name, + resp_list: [{ + "index": 0, + "finish_reason": None, + "text": content, + "logprobs": {'top_logprobs': [logprob_proc.token_alternatives]} if logprob_proc else None, + }], + } + + return chunk + + yield text_streaming_chunk(prefix) + + # generate reply ####################################### + debug_msg({'prompt': prompt, 'generate_params': generate_params}) + generator = generate_reply(prompt, generate_params, is_chat=False) + + answer = '' + seen_content = '' + completion_token_count = 0 + + for a in generator: + answer = a + + len_seen = len(seen_content) + new_content = answer[len_seen:] + + if not new_content or chr(0xfffd) in new_content: # partial unicode character, don't send it yet. + continue + + seen_content = answer + chunk = text_streaming_chunk(new_content) + yield chunk + + completion_token_count = len(encode(answer)[0]) + stop_reason = "stop" + if token_count + completion_token_count >= generate_params['truncation_length'] or completion_token_count >= max_tokens: + stop_reason = "length" + + chunk = text_streaming_chunk(suffix) + chunk[resp_list][0]["finish_reason"] = stop_reason + chunk["usage"] = { + "prompt_tokens": token_count, + "completion_tokens": completion_token_count, + "total_tokens": token_count + completion_token_count + } + + yield chunk + + +def chat_completions(body: dict, is_legacy: bool = False) -> dict: + generator = chat_completions_common(body, is_legacy, stream=False) + return deque(generator, maxlen=1).pop() + + +def stream_chat_completions(body: dict, is_legacy: bool = False): + for resp in chat_completions_common(body, is_legacy, stream=True): + yield resp + + +def completions(body: dict, is_legacy: bool = False) -> dict: + generator = completions_common(body, is_legacy, stream=False) + return deque(generator, maxlen=1).pop() + + +def stream_completions(body: dict, is_legacy: bool = False): + for resp in completions_common(body, is_legacy, stream=True): + yield resp diff --git a/extensions/openai/embeddings.py b/extensions/openai/embeddings.py new file mode 100644 index 0000000000000000000000000000000000000000..1420879cc94309e79e137704ff054c760c7ca743 --- /dev/null +++ b/extensions/openai/embeddings.py @@ -0,0 +1,98 @@ +import os + +import numpy as np +from transformers import AutoModel + +from extensions.openai.errors import ServiceUnavailableError +from extensions.openai.utils import debug_msg, float_list_to_base64 +from modules.logging_colors import logger + +embeddings_params_initialized = False + + +def initialize_embedding_params(): + ''' + using 'lazy loading' to avoid circular import + so this function will be executed only once + ''' + global embeddings_params_initialized + if not embeddings_params_initialized: + from extensions.openai.script import params + + global st_model, embeddings_model, embeddings_device + + st_model = os.environ.get("OPENEDAI_EMBEDDING_MODEL", params.get('embedding_model', 'all-mpnet-base-v2')) + embeddings_model = None + # OPENEDAI_EMBEDDING_DEVICE: auto (best or cpu), cpu, cuda, ipu, xpu, mkldnn, opengl, opencl, ideep, hip, ve, fpga, ort, xla, lazy, vulkan, mps, meta, hpu, mtia, privateuseone + embeddings_device = os.environ.get("OPENEDAI_EMBEDDING_DEVICE", params.get('embedding_device', 'cpu')) + if embeddings_device.lower() == 'auto': + embeddings_device = None + + embeddings_params_initialized = True + + +def load_embedding_model(model: str): + try: + from sentence_transformers import SentenceTransformer + except ModuleNotFoundError: + logger.error("The sentence_transformers module has not been found. Please install it manually with pip install -U sentence-transformers.") + raise ModuleNotFoundError + + initialize_embedding_params() + global embeddings_device, embeddings_model + try: + print(f"Try embedding model: {model} on {embeddings_device}") + if 'jina-embeddings' in model: + embeddings_model = AutoModel.from_pretrained(model, trust_remote_code=True) # trust_remote_code is needed to use the encode method + embeddings_model = embeddings_model.to(embeddings_device) + else: + embeddings_model = SentenceTransformer(model, device=embeddings_device) + + print(f"Loaded embedding model: {model}") + except Exception as e: + embeddings_model = None + raise ServiceUnavailableError(f"Error: Failed to load embedding model: {model}", internal_message=repr(e)) + + +def get_embeddings_model(): + initialize_embedding_params() + global embeddings_model, st_model + if st_model and not embeddings_model: + load_embedding_model(st_model) # lazy load the model + + return embeddings_model + + +def get_embeddings_model_name() -> str: + initialize_embedding_params() + global st_model + return st_model + + +def get_embeddings(input: list) -> np.ndarray: + model = get_embeddings_model() + debug_msg(f"embedding model : {model}") + embedding = model.encode(input, convert_to_numpy=True, normalize_embeddings=True, convert_to_tensor=False) + debug_msg(f"embedding result : {embedding}") # might be too long even for debug, use at you own will + return embedding + + +def embeddings(input: list, encoding_format: str) -> dict: + embeddings = get_embeddings(input) + if encoding_format == "base64": + data = [{"object": "embedding", "embedding": float_list_to_base64(emb), "index": n} for n, emb in enumerate(embeddings)] + else: + data = [{"object": "embedding", "embedding": emb.tolist(), "index": n} for n, emb in enumerate(embeddings)] + + response = { + "object": "list", + "data": data, + "model": st_model, # return the real model + "usage": { + "prompt_tokens": 0, + "total_tokens": 0, + } + } + + debug_msg(f"Embeddings return size: {len(embeddings[0])}, number: {len(embeddings)}") + return response diff --git a/extensions/openai/errors.py b/extensions/openai/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..838d1e7cc6f5577aad94bcd01f33f13ba49b349e --- /dev/null +++ b/extensions/openai/errors.py @@ -0,0 +1,31 @@ +class OpenAIError(Exception): + def __init__(self, message=None, code=500, internal_message=''): + self.message = message + self.code = code + self.internal_message = internal_message + + def __repr__(self): + return "%s(message=%r, code=%d)" % ( + self.__class__.__name__, + self.message, + self.code, + ) + + +class InvalidRequestError(OpenAIError): + def __init__(self, message, param, code=400, internal_message=''): + super().__init__(message, code, internal_message) + self.param = param + + def __repr__(self): + return "%s(message=%r, code=%d, param=%s)" % ( + self.__class__.__name__, + self.message, + self.code, + self.param, + ) + + +class ServiceUnavailableError(OpenAIError): + def __init__(self, message="Service unavailable, please try again later.", code=503, internal_message=''): + super().__init__(message, code, internal_message) diff --git a/extensions/openai/images.py b/extensions/openai/images.py new file mode 100644 index 0000000000000000000000000000000000000000..92bd85f08bcd6557dcc880825cc6a6df1c2ec9e2 --- /dev/null +++ b/extensions/openai/images.py @@ -0,0 +1,70 @@ +import os +import time + +import requests + +from extensions.openai.errors import ServiceUnavailableError + + +def generations(prompt: str, size: str, response_format: str, n: int): + # Stable Diffusion callout wrapper for txt2img + # Low effort implementation for compatibility. With only "prompt" being passed and assuming DALL-E + # the results will be limited and likely poor. SD has hundreds of models and dozens of settings. + # If you want high quality tailored results you should just use the Stable Diffusion API directly. + # it's too general an API to try and shape the result with specific tags like negative prompts + # or "masterpiece", etc. SD configuration is beyond the scope of this API. + # At this point I will not add the edits and variations endpoints (ie. img2img) because they + # require changing the form data handling to accept multipart form data, also to properly support + # url return types will require file management and a web serving files... Perhaps later! + base_model_size = 512 if 'SD_BASE_MODEL_SIZE' not in os.environ else int(os.environ.get('SD_BASE_MODEL_SIZE', 512)) + sd_defaults = { + 'sampler_name': 'DPM++ 2M Karras', # vast improvement + 'steps': 30, + } + + width, height = [int(x) for x in size.split('x')] # ignore the restrictions on size + + # to hack on better generation, edit default payload. + payload = { + 'prompt': prompt, # ignore prompt limit of 1000 characters + 'width': width, + 'height': height, + 'batch_size': n, + } + payload.update(sd_defaults) + + scale = min(width, height) / base_model_size + if scale >= 1.2: + # for better performance with the default size (1024), and larger res. + scaler = { + 'width': width // scale, + 'height': height // scale, + 'hr_scale': scale, + 'enable_hr': True, + 'hr_upscaler': 'Latent', + 'denoising_strength': 0.68, + } + payload.update(scaler) + + resp = { + 'created': int(time.time()), + 'data': [] + } + from extensions.openai.script import params + + # TODO: support SD_WEBUI_AUTH username:password pair. + sd_url = f"{os.environ.get('SD_WEBUI_URL', params.get('sd_webui_url', ''))}/sdapi/v1/txt2img" + + response = requests.post(url=sd_url, json=payload) + r = response.json() + if response.status_code != 200 or 'images' not in r: + print(r) + raise ServiceUnavailableError(r.get('error', 'Unknown error calling Stable Diffusion'), code=response.status_code, internal_message=r.get('errors', None)) + # r['parameters']... + for b64_json in r['images']: + if response_format == 'b64_json': + resp['data'].extend([{'b64_json': b64_json}]) + else: + resp['data'].extend([{'url': f'data:image/png;base64,{b64_json}'}]) # yeah it's lazy. requests.get() will not work with this + + return resp diff --git a/extensions/openai/logits.py b/extensions/openai/logits.py new file mode 100644 index 0000000000000000000000000000000000000000..357e70fa6043ba561f1a9d756425a349e9daa0cc --- /dev/null +++ b/extensions/openai/logits.py @@ -0,0 +1,11 @@ +from extensions.openai.completions import process_parameters +from modules.logits import get_next_logits + + +def _get_next_logits(body): + # Pre-process the input payload to simulate a real generation + use_samplers = body['use_samplers'] + state = process_parameters(body) if use_samplers else {} + state['stream'] = True + + return get_next_logits(body['prompt'], state, use_samplers, "", top_logits=body['top_logits'], return_dict=True) diff --git a/extensions/openai/models.py b/extensions/openai/models.py new file mode 100644 index 0000000000000000000000000000000000000000..01045f90e5de1021de3da2b37dfab74ad9aad0cd --- /dev/null +++ b/extensions/openai/models.py @@ -0,0 +1,79 @@ +from modules import shared +from modules.logging_colors import logger +from modules.LoRA import add_lora_to_model +from modules.models import load_model, unload_model +from modules.models_settings import get_model_metadata, update_model_parameters +from modules.utils import get_available_loras, get_available_models + + +def get_current_model_info(): + return { + 'model_name': shared.model_name, + 'lora_names': shared.lora_names + } + + +def list_models(): + return {'model_names': get_available_models()[1:]} + + +def list_dummy_models(): + result = { + "object": "list", + "data": [] + } + + # these are expected by so much, so include some here as a dummy + for model in ['gpt-3.5-turbo', 'text-embedding-ada-002']: + result["data"].append(model_info_dict(model)) + + return result + + +def model_info_dict(model_name: str) -> dict: + return { + "id": model_name, + "object": "model", + "created": 0, + "owned_by": "user" + } + + +def _load_model(data): + model_name = data["model_name"] + args = data["args"] + settings = data["settings"] + + unload_model() + model_settings = get_model_metadata(model_name) + update_model_parameters(model_settings) + + # Update shared.args with custom model loading settings + if args: + for k in args: + if hasattr(shared.args, k): + setattr(shared.args, k, args[k]) + + shared.model, shared.tokenizer = load_model(model_name) + + # Update shared.settings with custom generation defaults + if settings: + for k in settings: + if k in shared.settings: + shared.settings[k] = settings[k] + if k == 'truncation_length': + logger.info(f"TRUNCATION LENGTH (UPDATED): {shared.settings['truncation_length']}") + elif k == 'instruction_template': + logger.info(f"INSTRUCTION TEMPLATE (UPDATED): {shared.settings['instruction_template']}") + + +def list_loras(): + return {'lora_names': get_available_loras()[1:]} + + +def load_loras(lora_names): + add_lora_to_model(lora_names) + + +def unload_all_loras(): + add_lora_to_model([]) diff --git a/extensions/openai/moderations.py b/extensions/openai/moderations.py new file mode 100644 index 0000000000000000000000000000000000000000..1ca6b8abb2582e71ed41fc1ba4e0ae58ed3cfd68 --- /dev/null +++ b/extensions/openai/moderations.py @@ -0,0 +1,69 @@ +import time + +import numpy as np +from numpy.linalg import norm + +from extensions.openai.embeddings import get_embeddings + +moderations_disabled = False # return 0/false +category_embeddings = None +antonym_embeddings = None +categories = ["sexual", "hate", "harassment", "self-harm", "sexual/minors", "hate/threatening", "violence/graphic", "self-harm/intent", "self-harm/instructions", "harassment/threatening", "violence"] +flag_threshold = 0.5 + + +def get_category_embeddings() -> dict: + global category_embeddings, categories + if category_embeddings is None: + embeddings = get_embeddings(categories).tolist() + category_embeddings = dict(zip(categories, embeddings)) + + return category_embeddings + + +def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float: + return np.dot(a, b) / (norm(a) * norm(b)) + + +# seems most openai like with all-mpnet-base-v2 +def mod_score(a: np.ndarray, b: np.ndarray) -> float: + return 2.0 * np.dot(a, b) + + +def moderations(input): + global category_embeddings, categories, flag_threshold, moderations_disabled + results = { + "id": f"modr-{int(time.time()*1e9)}", + "model": "text-moderation-001", + "results": [], + } + + if moderations_disabled: + results['results'] = [{ + 'categories': dict([(C, False) for C in categories]), + 'category_scores': dict([(C, 0.0) for C in categories]), + 'flagged': False, + }] + return results + + category_embeddings = get_category_embeddings() + + # input, string or array + if isinstance(input, str): + input = [input] + + for in_str in input: + for ine in get_embeddings([in_str]): + category_scores = dict([(C, mod_score(category_embeddings[C], ine)) for C in categories]) + category_flags = dict([(C, bool(category_scores[C] > flag_threshold)) for C in categories]) + flagged = any(category_flags.values()) + + results['results'].extend([{ + 'flagged': flagged, + 'categories': category_flags, + 'category_scores': category_scores, + }]) + + print(results) + + return results diff --git a/extensions/openai/requirements.txt b/extensions/openai/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..e5d6d797d7d5d023bd8a2c6ac90ff42f3557335e --- /dev/null +++ b/extensions/openai/requirements.txt @@ -0,0 +1,4 @@ +SpeechRecognition==3.10.0 +flask_cloudflared==0.0.14 +sse-starlette==1.6.5 +tiktoken diff --git a/extensions/openai/script.py b/extensions/openai/script.py new file mode 100644 index 0000000000000000000000000000000000000000..e86473572e9e7082a4dc9565268861fd3cb574d9 --- /dev/null +++ b/extensions/openai/script.py @@ -0,0 +1,379 @@ +import asyncio +import json +import logging +import os +import traceback +from threading import Thread + +import speech_recognition as sr +import uvicorn +from fastapi import Depends, FastAPI, Header, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.requests import Request +from fastapi.responses import JSONResponse +from pydub import AudioSegment +from sse_starlette import EventSourceResponse + +import extensions.openai.completions as OAIcompletions +import extensions.openai.embeddings as OAIembeddings +import extensions.openai.images as OAIimages +import extensions.openai.logits as OAIlogits +import extensions.openai.models as OAImodels +import extensions.openai.moderations as OAImoderations +from extensions.openai.errors import ServiceUnavailableError +from extensions.openai.tokens import token_count, token_decode, token_encode +from extensions.openai.utils import _start_cloudflared +from modules import shared +from modules.logging_colors import logger +from modules.models import unload_model +from modules.text_generation import stop_everything_event + +from .typing import ( + ChatCompletionRequest, + ChatCompletionResponse, + CompletionRequest, + CompletionResponse, + DecodeRequest, + DecodeResponse, + EmbeddingsRequest, + EmbeddingsResponse, + EncodeRequest, + EncodeResponse, + LoadLorasRequest, + LoadModelRequest, + LogitsRequest, + LogitsResponse, + LoraListResponse, + ModelInfoResponse, + ModelListResponse, + TokenCountResponse, + to_dict +) + +params = { + 'embedding_device': 'cpu', + 'embedding_model': 'sentence-transformers/all-mpnet-base-v2', + 'sd_webui_url': '', + 'debug': 0 +} + + +streaming_semaphore = asyncio.Semaphore(1) + + +def verify_api_key(authorization: str = Header(None)) -> None: + expected_api_key = shared.args.api_key + if expected_api_key and (authorization is None or authorization != f"Bearer {expected_api_key}"): + raise HTTPException(status_code=401, detail="Unauthorized") + + +def verify_admin_key(authorization: str = Header(None)) -> None: + expected_api_key = shared.args.admin_key + if expected_api_key and (authorization is None or authorization != f"Bearer {expected_api_key}"): + raise HTTPException(status_code=401, detail="Unauthorized") + + +app = FastAPI() +check_key = [Depends(verify_api_key)] +check_admin_key = [Depends(verify_admin_key)] + +# Configure CORS settings to allow all origins, methods, and headers +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"] +) + + +@app.options("/", dependencies=check_key) +async def options_route(): + return JSONResponse(content="OK") + + +@app.post('/v1/completions', response_model=CompletionResponse, dependencies=check_key) +async def openai_completions(request: Request, request_data: CompletionRequest): + path = request.url.path + is_legacy = "/generate" in path + + if request_data.stream: + async def generator(): + async with streaming_semaphore: + response = OAIcompletions.stream_completions(to_dict(request_data), is_legacy=is_legacy) + for resp in response: + disconnected = await request.is_disconnected() + if disconnected: + break + + yield {"data": json.dumps(resp)} + + return EventSourceResponse(generator()) # SSE streaming + + else: + response = OAIcompletions.completions(to_dict(request_data), is_legacy=is_legacy) + return JSONResponse(response) + + +@app.post('/v1/chat/completions', response_model=ChatCompletionResponse, dependencies=check_key) +async def openai_chat_completions(request: Request, request_data: ChatCompletionRequest): + path = request.url.path + is_legacy = "/generate" in path + + if request_data.stream: + async def generator(): + async with streaming_semaphore: + response = OAIcompletions.stream_chat_completions(to_dict(request_data), is_legacy=is_legacy) + for resp in response: + disconnected = await request.is_disconnected() + if disconnected: + break + + yield {"data": json.dumps(resp)} + + return EventSourceResponse(generator()) # SSE streaming + + else: + response = OAIcompletions.chat_completions(to_dict(request_data), is_legacy=is_legacy) + return JSONResponse(response) + + +@app.get("/v1/models", dependencies=check_key) +@app.get("/v1/models/{model}", dependencies=check_key) +async def handle_models(request: Request): + path = request.url.path + is_list = request.url.path.split('?')[0].split('#')[0] == '/v1/models' + + if is_list: + response = OAImodels.list_dummy_models() + else: + model_name = path[len('/v1/models/'):] + response = OAImodels.model_info_dict(model_name) + + return JSONResponse(response) + + +@app.get('/v1/billing/usage', dependencies=check_key) +def handle_billing_usage(): + ''' + Ex. /v1/dashboard/billing/usage?start_date=2023-05-01&end_date=2023-05-31 + ''' + return JSONResponse(content={"total_usage": 0}) + + +@app.post('/v1/audio/transcriptions', dependencies=check_key) +async def handle_audio_transcription(request: Request): + r = sr.Recognizer() + + form = await request.form() + audio_file = await form["file"].read() + audio_data = AudioSegment.from_file(audio_file) + + # Convert AudioSegment to raw data + raw_data = audio_data.raw_data + + # Create AudioData object + audio_data = sr.AudioData(raw_data, audio_data.frame_rate, audio_data.sample_width) + whisper_language = form.getvalue('language', None) + whisper_model = form.getvalue('model', 'tiny') # Use the model from the form data if it exists, otherwise default to tiny + + transcription = {"text": ""} + + try: + transcription["text"] = r.recognize_whisper(audio_data, language=whisper_language, model=whisper_model) + except sr.UnknownValueError: + print("Whisper could not understand audio") + transcription["text"] = "Whisper could not understand audio UnknownValueError" + except sr.RequestError as e: + print("Could not request results from Whisper", e) + transcription["text"] = "Whisper could not understand audio RequestError" + + return JSONResponse(content=transcription) + + +@app.post('/v1/images/generations', dependencies=check_key) +async def handle_image_generation(request: Request): + + if not os.environ.get('SD_WEBUI_URL', params.get('sd_webui_url', '')): + raise ServiceUnavailableError("Stable Diffusion not available. SD_WEBUI_URL not set.") + + body = await request.json() + prompt = body['prompt'] + size = body.get('size', '1024x1024') + response_format = body.get('response_format', 'url') # or b64_json + n = body.get('n', 1) # ignore the batch limits of max 10 + + response = await OAIimages.generations(prompt=prompt, size=size, response_format=response_format, n=n) + return JSONResponse(response) + + +@app.post("/v1/embeddings", response_model=EmbeddingsResponse, dependencies=check_key) +async def handle_embeddings(request: Request, request_data: EmbeddingsRequest): + input = request_data.input + if not input: + raise HTTPException(status_code=400, detail="Missing required argument input") + + if type(input) is str: + input = [input] + + response = OAIembeddings.embeddings(input, request_data.encoding_format) + return JSONResponse(response) + + +@app.post("/v1/moderations", dependencies=check_key) +async def handle_moderations(request: Request): + body = await request.json() + input = body["input"] + if not input: + raise HTTPException(status_code=400, detail="Missing required argument input") + + response = OAImoderations.moderations(input) + return JSONResponse(response) + + +@app.post("/v1/internal/encode", response_model=EncodeResponse, dependencies=check_key) +async def handle_token_encode(request_data: EncodeRequest): + response = token_encode(request_data.text) + return JSONResponse(response) + + +@app.post("/v1/internal/decode", response_model=DecodeResponse, dependencies=check_key) +async def handle_token_decode(request_data: DecodeRequest): + response = token_decode(request_data.tokens) + return JSONResponse(response) + + +@app.post("/v1/internal/token-count", response_model=TokenCountResponse, dependencies=check_key) +async def handle_token_count(request_data: EncodeRequest): + response = token_count(request_data.text) + return JSONResponse(response) + + +@app.post("/v1/internal/logits", response_model=LogitsResponse, dependencies=check_key) +async def handle_logits(request_data: LogitsRequest): + ''' + Given a prompt, returns the top 50 most likely logits as a dict. + The keys are the tokens, and the values are the probabilities. + ''' + response = OAIlogits._get_next_logits(to_dict(request_data)) + return JSONResponse(response) + + +@app.post("/v1/internal/stop-generation", dependencies=check_key) +async def handle_stop_generation(request: Request): + stop_everything_event() + return JSONResponse(content="OK") + + +@app.get("/v1/internal/model/info", response_model=ModelInfoResponse, dependencies=check_key) +async def handle_model_info(): + payload = OAImodels.get_current_model_info() + return JSONResponse(content=payload) + + +@app.get("/v1/internal/model/list", response_model=ModelListResponse, dependencies=check_admin_key) +async def handle_list_models(): + payload = OAImodels.list_models() + return JSONResponse(content=payload) + + +@app.post("/v1/internal/model/load", dependencies=check_admin_key) +async def handle_load_model(request_data: LoadModelRequest): + ''' + This endpoint is experimental and may change in the future. + + The "args" parameter can be used to modify flags like "--load-in-4bit" + or "--n-gpu-layers" before loading a model. Example: + + ``` + "args": { + "load_in_4bit": true, + "n_gpu_layers": 12 + } + ``` + + Note that those settings will remain after loading the model. So you + may need to change them back to load a second model. + + The "settings" parameter is also a dict but with keys for the + shared.settings object. It can be used to modify the default instruction + template like this: + + ``` + "settings": { + "instruction_template": "Alpaca" + } + ``` + ''' + + try: + OAImodels._load_model(to_dict(request_data)) + return JSONResponse(content="OK") + except: + traceback.print_exc() + return HTTPException(status_code=400, detail="Failed to load the model.") + + +@app.post("/v1/internal/model/unload", dependencies=check_admin_key) +async def handle_unload_model(): + unload_model() + + +@app.get("/v1/internal/lora/list", response_model=LoraListResponse, dependencies=check_admin_key) +async def handle_list_loras(): + response = OAImodels.list_loras() + return JSONResponse(content=response) + + +@app.post("/v1/internal/lora/load", dependencies=check_admin_key) +async def handle_load_loras(request_data: LoadLorasRequest): + try: + OAImodels.load_loras(request_data.lora_names) + return JSONResponse(content="OK") + except: + traceback.print_exc() + return HTTPException(status_code=400, detail="Failed to apply the LoRA(s).") + + +@app.post("/v1/internal/lora/unload", dependencies=check_admin_key) +async def handle_unload_loras(): + OAImodels.unload_all_loras() + return JSONResponse(content="OK") + + +def run_server(): + server_addr = '0.0.0.0' if shared.args.listen else '127.0.0.1' + port = int(os.environ.get('OPENEDAI_PORT', shared.args.api_port)) + + ssl_certfile = os.environ.get('OPENEDAI_CERT_PATH', shared.args.ssl_certfile) + ssl_keyfile = os.environ.get('OPENEDAI_KEY_PATH', shared.args.ssl_keyfile) + + if shared.args.public_api: + def on_start(public_url: str): + logger.info(f'OpenAI-compatible API URL:\n\n{public_url}\n') + + _start_cloudflared(port, shared.args.public_api_id, max_attempts=3, on_start=on_start) + else: + if ssl_keyfile and ssl_certfile: + logger.info(f'OpenAI-compatible API URL:\n\nhttps://{server_addr}:{port}\n') + else: + logger.info(f'OpenAI-compatible API URL:\n\nhttp://{server_addr}:{port}\n') + + if shared.args.api_key: + if not shared.args.admin_key: + shared.args.admin_key = shared.args.api_key + + logger.info(f'OpenAI API key:\n\n{shared.args.api_key}\n') + + if shared.args.admin_key and shared.args.admin_key != shared.args.api_key: + logger.info(f'OpenAI API admin key (for loading/unloading models):\n\n{shared.args.admin_key}\n') + + logging.getLogger("uvicorn.error").propagate = False + uvicorn.run(app, host=server_addr, port=port, ssl_certfile=ssl_certfile, ssl_keyfile=ssl_keyfile) + + +def setup(): + if shared.args.nowebui: + run_server() + else: + Thread(target=run_server, daemon=True).start() diff --git a/extensions/openai/tokens.py b/extensions/openai/tokens.py new file mode 100644 index 0000000000000000000000000000000000000000..9e92d3620775cb04bf2a9ce71747a51f323b4cc2 --- /dev/null +++ b/extensions/openai/tokens.py @@ -0,0 +1,26 @@ +from modules.text_generation import decode, encode + + +def token_count(prompt): + tokens = encode(prompt)[0] + return { + 'length': len(tokens) + } + + +def token_encode(input): + tokens = encode(input)[0] + if tokens.__class__.__name__ in ['Tensor', 'ndarray']: + tokens = tokens.tolist() + + return { + 'tokens': tokens, + 'length': len(tokens), + } + + +def token_decode(tokens): + output = decode(tokens) + return { + 'text': output + } diff --git a/extensions/openai/typing.py b/extensions/openai/typing.py new file mode 100644 index 0000000000000000000000000000000000000000..9c4a04f0bf774efebc1a4786f3aa94706d432e3b --- /dev/null +++ b/extensions/openai/typing.py @@ -0,0 +1,208 @@ +import json +import time +from typing import Dict, List + +from pydantic import BaseModel, Field + + +class GenerationOptions(BaseModel): + preset: str | None = Field(default=None, description="The name of a file under text-generation-webui/presets (without the .yaml extension). The sampling parameters that get overwritten by this option are the keys in the default_preset() function in modules/presets.py.") + min_p: float = 0 + dynamic_temperature: bool = False + dynatemp_low: float = 1 + dynatemp_high: float = 1 + dynatemp_exponent: float = 1 + top_k: int = 0 + repetition_penalty: float = 1 + repetition_penalty_range: int = 1024 + typical_p: float = 1 + tfs: float = 1 + top_a: float = 0 + epsilon_cutoff: float = 0 + eta_cutoff: float = 0 + guidance_scale: float = 1 + negative_prompt: str = '' + penalty_alpha: float = 0 + mirostat_mode: int = 0 + mirostat_tau: float = 5 + mirostat_eta: float = 0.1 + temperature_last: bool = False + do_sample: bool = True + seed: int = -1 + encoder_repetition_penalty: float = 1 + no_repeat_ngram_size: int = 0 + min_length: int = 0 + num_beams: int = 1 + length_penalty: float = 1 + early_stopping: bool = False + truncation_length: int = 0 + max_tokens_second: int = 0 + prompt_lookup_num_tokens: int = 0 + custom_token_bans: str = "" + auto_max_new_tokens: bool = False + ban_eos_token: bool = False + add_bos_token: bool = True + skip_special_tokens: bool = True + grammar_string: str = "" + + +class CompletionRequestParams(BaseModel): + model: str | None = Field(default=None, description="Unused parameter. To change the model, use the /v1/internal/model/load endpoint.") + prompt: str | List[str] + best_of: int | None = Field(default=1, description="Unused parameter.") + echo: bool | None = False + frequency_penalty: float | None = 0 + logit_bias: dict | None = None + logprobs: int | None = None + max_tokens: int | None = 16 + n: int | None = Field(default=1, description="Unused parameter.") + presence_penalty: float | None = 0 + stop: str | List[str] | None = None + stream: bool | None = False + suffix: str | None = None + temperature: float | None = 1 + top_p: float | None = 1 + user: str | None = Field(default=None, description="Unused parameter.") + + +class CompletionRequest(GenerationOptions, CompletionRequestParams): + pass + + +class CompletionResponse(BaseModel): + id: str + choices: List[dict] + created: int = int(time.time()) + model: str + object: str = "text_completion" + usage: dict + + +class ChatCompletionRequestParams(BaseModel): + messages: List[dict] + model: str | None = Field(default=None, description="Unused parameter. To change the model, use the /v1/internal/model/load endpoint.") + frequency_penalty: float | None = 0 + function_call: str | dict | None = Field(default=None, description="Unused parameter.") + functions: List[dict] | None = Field(default=None, description="Unused parameter.") + logit_bias: dict | None = None + max_tokens: int | None = None + n: int | None = Field(default=1, description="Unused parameter.") + presence_penalty: float | None = 0 + stop: str | List[str] | None = None + stream: bool | None = False + temperature: float | None = 1 + top_p: float | None = 1 + user: str | None = Field(default=None, description="Unused parameter.") + + mode: str = Field(default='instruct', description="Valid options: instruct, chat, chat-instruct.") + + instruction_template: str | None = Field(default=None, description="An instruction template defined under text-generation-webui/instruction-templates. If not set, the correct template will be automatically obtained from the model metadata.") + instruction_template_str: str | None = Field(default=None, description="A Jinja2 instruction template. If set, will take precedence over everything else.") + + character: str | None = Field(default=None, description="A character defined under text-generation-webui/characters. If not set, the default \"Assistant\" character will be used.") + user_name: str | None = Field(default=None, description="Your name (the user). By default, it's \"You\".", alias="name1") + bot_name: str | None = Field(default=None, description="Overwrites the value set by character field.", alias="name2") + context: str | None = Field(default=None, description="Overwrites the value set by character field.") + greeting: str | None = Field(default=None, description="Overwrites the value set by character field.") + chat_template_str: str | None = Field(default=None, description="Jinja2 template for chat.") + + chat_instruct_command: str | None = None + + continue_: bool = Field(default=False, description="Makes the last bot message in the history be continued instead of starting a new message.") + + +class ChatCompletionRequest(GenerationOptions, ChatCompletionRequestParams): + pass + + +class ChatCompletionResponse(BaseModel): + id: str + choices: List[dict] + created: int = int(time.time()) + model: str + object: str = "chat.completion" + usage: dict + + +class EmbeddingsRequest(BaseModel): + input: str | List[str] | List[int] | List[List[int]] + model: str | None = Field(default=None, description="Unused parameter. To change the model, set the OPENEDAI_EMBEDDING_MODEL and OPENEDAI_EMBEDDING_DEVICE environment variables before starting the server.") + encoding_format: str = Field(default="float", description="Can be float or base64.") + user: str | None = Field(default=None, description="Unused parameter.") + + +class EmbeddingsResponse(BaseModel): + index: int + embedding: List[float] + object: str = "embedding" + + +class EncodeRequest(BaseModel): + text: str + + +class EncodeResponse(BaseModel): + tokens: List[int] + length: int + + +class DecodeRequest(BaseModel): + tokens: List[int] + + +class DecodeResponse(BaseModel): + text: str + + +class TokenCountResponse(BaseModel): + length: int + + +class LogitsRequestParams(BaseModel): + prompt: str + use_samplers: bool = False + top_logits: int | None = 50 + frequency_penalty: float | None = 0 + max_tokens: int | None = 16 + presence_penalty: float | None = 0 + temperature: float | None = 1 + top_p: float | None = 1 + + +class LogitsRequest(GenerationOptions, LogitsRequestParams): + pass + + +class LogitsResponse(BaseModel): + logits: Dict[str, float] + + +class ModelInfoResponse(BaseModel): + model_name: str + lora_names: List[str] + + +class ModelListResponse(BaseModel): + model_names: List[str] + + +class LoadModelRequest(BaseModel): + model_name: str + args: dict | None = None + settings: dict | None = None + + +class LoraListResponse(BaseModel): + lora_names: List[str] + + +class LoadLorasRequest(BaseModel): + lora_names: List[str] + + +def to_json(obj): + return json.dumps(obj.__dict__, indent=4) + + +def to_dict(obj): + return obj.__dict__ diff --git a/extensions/openai/utils.py b/extensions/openai/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..2b4147690e707b590ff087874dac3ff00fdbf927 --- /dev/null +++ b/extensions/openai/utils.py @@ -0,0 +1,54 @@ +import base64 +import os +import time +import traceback +from typing import Callable, Optional + +import numpy as np + + +def float_list_to_base64(float_array: np.ndarray) -> str: + # Convert the list to a float32 array that the OpenAPI client expects + # float_array = np.array(float_list, dtype="float32") + + # Get raw bytes + bytes_array = float_array.tobytes() + + # Encode bytes into base64 + encoded_bytes = base64.b64encode(bytes_array) + + # Turn raw base64 encoded bytes into ASCII + ascii_string = encoded_bytes.decode('ascii') + return ascii_string + + +def debug_msg(*args, **kwargs): + from extensions.openai.script import params + if os.environ.get("OPENEDAI_DEBUG", params.get('debug', 0)): + print(*args, **kwargs) + + +def _start_cloudflared(port: int, tunnel_id: str, max_attempts: int = 3, on_start: Optional[Callable[[str], None]] = None): + try: + from flask_cloudflared import _run_cloudflared + except ImportError: + print('You should install flask_cloudflared manually') + raise Exception( + 'flask_cloudflared not installed. Make sure you installed the requirements.txt for this extension.') + + for _ in range(max_attempts): + try: + if tunnel_id is not None: + public_url = _run_cloudflared(port, port + 1, tunnel_id=tunnel_id) + else: + public_url = _run_cloudflared(port, port + 1) + + if on_start: + on_start(public_url) + + return + except Exception: + traceback.print_exc() + time.sleep(3) + + raise Exception('Could not start cloudflared.') diff --git a/extensions/perplexity_colors/script.py b/extensions/perplexity_colors/script.py new file mode 100644 index 0000000000000000000000000000000000000000..2a986ac40b4194a5751015241d82046ce95cbca2 --- /dev/null +++ b/extensions/perplexity_colors/script.py @@ -0,0 +1,309 @@ +import time + +import gradio +import numpy as np +import torch +from transformers import LogitsProcessor + +from modules import html_generator, shared + +params = { + 'active': True, + 'color_by_perplexity': False, + 'color_by_probability': False, + 'ppl_scale': 15.0, # No slider for this right now, because I don't think it really needs to be changed. Very large perplexity scores don't show up often. + 'probability_dropdown': False, + 'verbose': False # For debugging mostly +} + + +class PerplexityLogits(LogitsProcessor): + def __init__(self, verbose=False): + self.generated_token_ids = [] + self.selected_probs = [] + self.top_token_ids_list = [] + self.top_probs_list = [] + self.perplexities_list = [] + self.last_probs = None + self.verbose = verbose + + def __call__(self, input_ids, scores): + # t0 = time.time() + probs = torch.softmax(scores, dim=-1, dtype=torch.float) + log_probs = torch.nan_to_num(torch.log(probs)) # Note: This is to convert log(0) nan to 0, but probs*log_probs makes this 0 not affect the perplexity. + entropy = -torch.sum(probs * log_probs) + entropy = entropy.cpu().numpy() + perplexity = round(float(np.exp(entropy)), 4) + self.perplexities_list.append(perplexity) + last_token_id = int(input_ids[0][-1].cpu().numpy().item()) + # Store the generated tokens (not sure why this isn't accessible in the output endpoint!) + self.generated_token_ids.append(last_token_id) + # Get last probability, and add to the list if it wasn't there + if len(self.selected_probs) > 0: + # Is the selected token in the top tokens? + if self.verbose: + print('Probs: Token after', shared.tokenizer.decode(last_token_id)) + print('Probs:', [shared.tokenizer.decode(token_id) for token_id in self.top_token_ids_list[-1][0]]) + print('Probs:', [round(float(prob), 4) for prob in self.top_probs_list[-1][0]]) + if last_token_id in self.top_token_ids_list[-1][0]: + idx = self.top_token_ids_list[-1][0].index(last_token_id) + self.selected_probs.append(self.top_probs_list[-1][0][idx]) + else: + self.top_token_ids_list[-1][0].append(last_token_id) + last_prob = round(float(self.last_probs[last_token_id]), 4) + self.top_probs_list[-1][0].append(last_prob) + self.selected_probs.append(last_prob) + else: + self.selected_probs.append(1.0) # Placeholder for the last token of the prompt + + if self.verbose: + pplbar = "-" + if not np.isnan(perplexity): + pplbar = "*" * round(perplexity) + print(f"PPL: Token after {shared.tokenizer.decode(last_token_id)}\t{perplexity:.2f}\t{pplbar}") + + # Get top 5 probabilities + top_tokens_and_probs = torch.topk(probs, 5) + top_probs = top_tokens_and_probs.values.cpu().numpy().astype(float).tolist() + top_token_ids = top_tokens_and_probs.indices.cpu().numpy().astype(int).tolist() + + self.top_token_ids_list.append(top_token_ids) + self.top_probs_list.append(top_probs) + + probs = probs.cpu().numpy().flatten() + self.last_probs = probs # Need to keep this as a reference for top probs + + # t1 = time.time() + # print(f"PPL Processor: {(t1-t0):.3f} s") + # About 1 ms, though occasionally up to around 100 ms, not sure why... + # Doesn't actually modify the logits! + return scores + + +# Stores the perplexity and top probabilities +ppl_logits_processor = None + + +def logits_processor_modifier(logits_processor_list, input_ids): + global ppl_logits_processor + if params['active']: + ppl_logits_processor = PerplexityLogits(verbose=params['verbose']) + logits_processor_list.append(ppl_logits_processor) + + +def output_modifier(text): + global ppl_logits_processor + # t0 = time.time() + + if not params['active']: + return text + + # TODO: It's probably more efficient to do this above rather than modifying all these lists + # Remove last element of perplexities_list, top_token_ids_list, top_tokens_list, top_probs_list since everything is off by one because this extension runs before generation + perplexities = ppl_logits_processor.perplexities_list[:-1] + top_token_ids_list = ppl_logits_processor.top_token_ids_list[:-1] + top_tokens_list = [[shared.tokenizer.decode(token_id) for token_id in top_token_ids[0]] for top_token_ids in top_token_ids_list] + top_probs_list = ppl_logits_processor.top_probs_list[:-1] + # Remove first element of generated_token_ids, generated_tokens, selected_probs because they are for the last token of the prompt + gen_token_ids = ppl_logits_processor.generated_token_ids[1:] + gen_tokens = [shared.tokenizer.decode(token_id) for token_id in gen_token_ids] + sel_probs = ppl_logits_processor.selected_probs[1:] + + end_part = '' if params['probability_dropdown'] else '' # Helps with finding the index after replacing part of the text. + + i = 0 + for token, prob, ppl, top_tokens, top_probs in zip(gen_tokens, sel_probs, perplexities, top_tokens_list, top_probs_list): + color = 'ffffff' + if params['color_by_probability'] and params['color_by_perplexity']: + color = probability_perplexity_color_scale(prob, ppl) + elif params['color_by_perplexity']: + color = perplexity_color_scale(ppl) + elif params['color_by_probability']: + color = probability_color_scale(prob) + if token in text[i:]: + if params['probability_dropdown']: + text = text[:i] + text[i:].replace(token, add_dropdown_html(token, color, top_tokens, top_probs[0], ppl), 1) + else: + text = text[:i] + text[i:].replace(token, add_color_html(token, color), 1) + i += text[i:].find(end_part) + len(end_part) + + # Use full perplexity list for calculating the average here. + print('Average perplexity:', round(np.mean(ppl_logits_processor.perplexities_list[:-1]), 4)) + # t1 = time.time() + # print(f"Modifier: {(t1-t0):.3f} s") + # About 50 ms + return text + + +def probability_color_scale(prob): + ''' + Green-yellow-red color scale + ''' + + rv = 0 + gv = 0 + if prob <= 0.5: + rv = 'ff' + gv = hex(int(255 * prob * 2))[2:] + if len(gv) < 2: + gv = '0' * (2 - len(gv)) + gv + else: + rv = hex(int(255 - 255 * (prob - 0.5) * 2))[2:] + gv = 'ff' + if len(rv) < 2: + rv = '0' * (2 - len(rv)) + rv + + return rv + gv + '00' + + +def perplexity_color_scale(ppl): + ''' + Red component only, white for 0 perplexity (sorry if you're not in dark mode) + ''' + value = hex(max(int(255.0 - params['ppl_scale'] * (float(ppl) - 1.0)), 0))[2:] + if len(value) < 2: + value = '0' * (2 - len(value)) + value + + return 'ff' + value + value + + +def probability_perplexity_color_scale(prob, ppl): + ''' + Green-yellow-red for probability and blue component for perplexity + ''' + + rv = 0 + gv = 0 + bv = hex(min(max(int(params['ppl_scale'] * (float(ppl) - 1.0)), 0), 255))[2:] + if len(bv) < 2: + bv = '0' * (2 - len(bv)) + bv + + if prob <= 0.5: + rv = 'ff' + gv = hex(int(255 * prob * 2))[2:] + if len(gv) < 2: + gv = '0' * (2 - len(gv)) + gv + else: + rv = hex(int(255 - 255 * (prob - 0.5) * 2))[2:] + gv = 'ff' + if len(rv) < 2: + rv = '0' * (2 - len(rv)) + rv + + return rv + gv + bv + + +def add_color_html(token, color): + return f'{token}' + + +# TODO: Major issue: Applying this to too many tokens will cause a permanent slowdown in generation speed until the messages are removed from the history. +# I think the issue is from HTML elements taking up space in the visible history, and things like history deepcopy add latency proportional to the size of the history. +# Potential solution is maybe to modify the main generation code to send just the internal text and not the visible history, to avoid moving too much around. +# I wonder if we can also avoid using deepcopy here. +def add_dropdown_html(token, color, top_tokens, top_probs, perplexity=0): + html = f'
{token}
' + return html # About 750 characters per token... + + +def custom_css(): + return """ + .dropdown { + display: none; + position: absolute; + z-index: 50; + background-color: var(--block-background-fill); + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + width: max-content; + overflow: visible; + padding: 5px; + border-radius: 10px; + border: 1px solid var(--border-color-primary); + } + + .dropdown-content { + border: none; + z-index: 50; + } + + .dropdown-content tr.selected { + background-color: var(--block-label-background-fill); + } + + .dropdown-content td { + color: var(--body-text-color); + } + + .hoverable { + color: var(--body-text-color); + position: relative; + display: inline-block; + overflow: visible; + font-size: 15px; + line-height: 1.75; + margin: 0; + padding: 0; + } + + .hoverable:hover .dropdown { + display: block; + } + + pre { + white-space: pre-wrap; + } + + # TODO: This makes the hover menus extend outside the bounds of the chat area, which is good. + # However, it also makes the scrollbar disappear, which is bad. + # The scroll bar needs to still be present. So for now, we can't see dropdowns that extend past the edge of the chat area. + #.chat { + # overflow-y: auto; + #} + """ + + +# Monkeypatch applied to html_generator.py +# We simply don't render markdown into HTML. We wrap everything in
 tags to preserve whitespace
+# formatting. If you're coloring tokens by perplexity or probability, or especially if you're using
+# the probability dropdown, you probably care more about seeing the tokens the model actually outputted
+# rather than rendering ```code blocks``` or *italics*.
+def convert_to_markdown(string):
+    return '
' + string + '
' + + +html_generator.convert_to_markdown = convert_to_markdown + + +def ui(): + def update_active_check(x): + params.update({'active': x}) + + def update_color_by_ppl_check(x): + params.update({'color_by_perplexity': x}) + + def update_color_by_prob_check(x): + params.update({'color_by_probability': x}) + + def update_prob_dropdown_check(x): + params.update({'probability_dropdown': x}) + + active_check = gradio.Checkbox(value=True, label="Compute probabilities and perplexity scores", info="Activate this extension. Note that this extension currently does not work with exllama or llama.cpp.") + color_by_ppl_check = gradio.Checkbox(value=False, label="Color by perplexity", info="Higher perplexity is more red. If also showing probability, higher perplexity has more blue component.") + color_by_prob_check = gradio.Checkbox(value=False, label="Color by probability", info="Green-yellow-red linear scale, with 100% green, 50% yellow, 0% red.") + prob_dropdown_check = gradio.Checkbox(value=False, label="Probability dropdown", info="Hover over a token to show a dropdown of top token probabilities. Currently slightly buggy with whitespace between tokens.") + + active_check.change(update_active_check, active_check, None) + color_by_ppl_check.change(update_color_by_ppl_check, color_by_ppl_check, None) + color_by_prob_check.change(update_color_by_prob_check, color_by_prob_check, None) + prob_dropdown_check.change(update_prob_dropdown_check, prob_dropdown_check, None) diff --git a/extensions/sd_api_pictures/README.MD b/extensions/sd_api_pictures/README.MD new file mode 100644 index 0000000000000000000000000000000000000000..67c75e145ccc8301505d96d858da04713ad4337d --- /dev/null +++ b/extensions/sd_api_pictures/README.MD @@ -0,0 +1,90 @@ +## Description: +TL;DR: Lets the bot answer you with a picture! + +Stable Diffusion API pictures for TextGen, v.1.2.0 +An extension to [oobabooga's textgen-webui](https://github.com/oobabooga/text-generation-webui) allowing you to receive pics generated by [Automatic1111's SD-WebUI API](https://github.com/AUTOMATIC1111/stable-diffusion-webui) + +
+Interface overview + +![Interface](https://raw.githubusercontent.com/Brawlence/SD_api_pics/main/illust/Interface.jpg) + +
+ +Load it in the `--chat` mode with `--extension sd_api_pictures` alongside `send_pictures` +(it's not really required, but completes the picture, *pun intended*). + + +## History + +Consider the version included with [oobabooga's repository](https://github.com/oobabooga/text-generation-webui/tree/main/extensions/sd_api_pictures) to be STABLE, experimental developments and untested features are pushed in [Brawlence/SD_api_pics](https://github.com/Brawlence/SD_api_pics) + +Lastest change: +1.1.0 → 1.1.1 Fixed not having Auto1111's metadata in received images + +## Details + +The image generation is triggered: +- manually through the 'Force the picture response' button while in `Manual` or `Immersive/Interactive` modes OR +- automatically in `Immersive/Interactive` mode if the words `'send|main|message|me'` are followed by `'image|pic|picture|photo|snap|snapshot|selfie|meme'` in the user's prompt +- always on in `Picturebook/Adventure` mode (if not currently suppressed by 'Suppress the picture response') + +## Prerequisites + +One needs an available instance of Automatic1111's webui running with an `--api` flag. Ain't tested with a notebook / cloud hosted one but should be possible. +To run it locally in parallel on the same machine, specify custom `--listen-port` for either Auto1111's or ooba's webUIs. + +## Features overview +- Connection to API check (press enter in the address box) +- [VRAM management (model shuffling)](https://github.com/Brawlence/SD_api_pics/wiki/VRAM-management-feature) +- [Three different operation modes](https://github.com/Brawlence/SD_api_pics/wiki/Modes-of-operation) (manual, interactive, always-on) +- User-defined persistent settings via settings.json + +### Connection check + +Insert the Automatic1111's WebUI address and press Enter: +![API-check](https://raw.githubusercontent.com/Brawlence/SD_api_pics/main/illust/API-check.gif) +Green mark confirms the ability to communicate with Auto1111's API on this address. Red cross means something's not right (the ext won't work). + +### Persistents settings + +Create or modify the `settings.json` in the `text-generation-webui` root directory to override the defaults +present in script.py, ex: + +```json +{ + "sd_api_pictures-manage_VRAM": 1, + "sd_api_pictures-save_img": 1, + "sd_api_pictures-prompt_prefix": "(Masterpiece:1.1), detailed, intricate, colorful, (solo:1.1)", + "sd_api_pictures-sampler_name": "DPM++ 2M Karras" +} +``` + +will automatically set the `Manage VRAM` & `Keep original images` checkboxes and change the texts in `Prompt Prefix` and `Sampler name` on load. + +--- + +## Demonstrations: + +Those are examples of the version 1.0.0, but the core functionality is still the same + +
+Conversation 1 + +![EXA1](https://user-images.githubusercontent.com/42910943/224866564-939a3bcb-e7cf-4ac0-a33f-b3047b55054d.jpg) +![EXA2](https://user-images.githubusercontent.com/42910943/224866566-38394054-1320-45cf-9515-afa76d9d7745.jpg) +![EXA3](https://user-images.githubusercontent.com/42910943/224866568-10ea47b7-0bac-4269-9ec9-22c387a13b59.jpg) +![EXA4](https://user-images.githubusercontent.com/42910943/224866569-326121ad-1ea1-4874-9f6b-4bca7930a263.jpg) + + +
+ +
+Conversation 2 + +![Hist1](https://user-images.githubusercontent.com/42910943/224865517-c6966b58-bc4d-4353-aab9-6eb97778d7bf.jpg) +![Hist2](https://user-images.githubusercontent.com/42910943/224865527-b2fe7c2e-0da5-4c2e-b705-42e233b07084.jpg) +![Hist3](https://user-images.githubusercontent.com/42910943/224865535-a38d94e7-8975-4a46-a655-1ae1de41f85d.jpg) + +
+ diff --git a/extensions/sd_api_pictures/script.py b/extensions/sd_api_pictures/script.py new file mode 100644 index 0000000000000000000000000000000000000000..a16575cdf10a8714e23e7d54cd0940150ce9a20c --- /dev/null +++ b/extensions/sd_api_pictures/script.py @@ -0,0 +1,386 @@ +import base64 +import io +import re +import time +from datetime import date +from pathlib import Path + +import gradio as gr +import requests +import torch +from PIL import Image + +from modules import shared +from modules.models import reload_model, unload_model +from modules.ui import create_refresh_button + +torch._C._jit_set_profiling_mode(False) + +# parameters which can be customized in settings.json of webui +params = { + 'address': 'http://127.0.0.1:7860', + 'mode': 0, # modes of operation: 0 (Manual only), 1 (Immersive/Interactive - looks for words to trigger), 2 (Picturebook Adventure - Always on) + 'manage_VRAM': False, + 'save_img': False, + 'SD_model': 'NeverEndingDream', # not used right now + 'prompt_prefix': '(Masterpiece:1.1), detailed, intricate, colorful', + 'negative_prompt': '(worst quality, low quality:1.3)', + 'width': 512, + 'height': 512, + 'denoising_strength': 0.61, + 'restore_faces': False, + 'enable_hr': False, + 'hr_upscaler': 'ESRGAN_4x', + 'hr_scale': '1.0', + 'seed': -1, + 'sampler_name': 'DPM++ 2M Karras', + 'steps': 32, + 'cfg_scale': 7, + 'textgen_prefix': 'Please provide a detailed and vivid description of [subject]', + 'sd_checkpoint': ' ', + 'checkpoint_list': [" "] +} + + +def give_VRAM_priority(actor): + global shared, params + + if actor == 'SD': + unload_model() + print("Requesting Auto1111 to re-load last checkpoint used...") + response = requests.post(url=f'{params["address"]}/sdapi/v1/reload-checkpoint', json='') + response.raise_for_status() + + elif actor == 'LLM': + print("Requesting Auto1111 to vacate VRAM...") + response = requests.post(url=f'{params["address"]}/sdapi/v1/unload-checkpoint', json='') + response.raise_for_status() + reload_model() + + elif actor == 'set': + print("VRAM mangement activated -- requesting Auto1111 to vacate VRAM...") + response = requests.post(url=f'{params["address"]}/sdapi/v1/unload-checkpoint', json='') + response.raise_for_status() + + elif actor == 'reset': + print("VRAM mangement deactivated -- requesting Auto1111 to reload checkpoint") + response = requests.post(url=f'{params["address"]}/sdapi/v1/reload-checkpoint', json='') + response.raise_for_status() + + else: + raise RuntimeError(f'Managing VRAM: "{actor}" is not a known state!') + + response.raise_for_status() + del response + + +if params['manage_VRAM']: + give_VRAM_priority('set') + +SD_models = ['NeverEndingDream'] # TODO: get with http://{address}}/sdapi/v1/sd-models and allow user to select + +picture_response = False # specifies if the next model response should appear as a picture + + +def remove_surrounded_chars(string): + # this expression matches to 'as few symbols as possible (0 upwards) between any asterisks' OR + # 'as few symbols as possible (0 upwards) between an asterisk and the end of the string' + return re.sub('\*[^\*]*?(\*|$)', '', string) + + +def triggers_are_in(string): + string = remove_surrounded_chars(string) + # regex searches for send|main|message|me (at the end of the word) followed by + # a whole word of image|pic|picture|photo|snap|snapshot|selfie|meme(s), + # (?aims) are regex parser flags + return bool(re.search('(?aims)(send|mail|message|me)\\b.+?\\b(image|pic(ture)?|photo|snap(shot)?|selfie|meme)s?\\b', string)) + + +def state_modifier(state): + if picture_response: + state['stream'] = False + + return state + + +def input_modifier(string): + """ + This function is applied to your text inputs before + they are fed into the model. + """ + + global params + + if not params['mode'] == 1: # if not in immersive/interactive mode, do nothing + return string + + if triggers_are_in(string): # if we're in it, check for trigger words + toggle_generation(True) + string = string.lower() + if "of" in string: + subject = string.split('of', 1)[1] # subdivide the string once by the first 'of' instance and get what's coming after it + string = params['textgen_prefix'].replace("[subject]", subject) + else: + string = params['textgen_prefix'].replace("[subject]", "your appearance, your surroundings and what you are doing right now") + + return string + +# Get and save the Stable Diffusion-generated picture +def get_SD_pictures(description, character): + + global params + + if params['manage_VRAM']: + give_VRAM_priority('SD') + + description = re.sub('', ' ', description) + description = f"({description}:1)" + + payload = { + "prompt": params['prompt_prefix'] + description, + "seed": params['seed'], + "sampler_name": params['sampler_name'], + "enable_hr": params['enable_hr'], + "hr_scale": params['hr_scale'], + "hr_upscaler": params['hr_upscaler'], + "denoising_strength": params['denoising_strength'], + "steps": params['steps'], + "cfg_scale": params['cfg_scale'], + "width": params['width'], + "height": params['height'], + "restore_faces": params['restore_faces'], + "override_settings_restore_afterwards": True, + "negative_prompt": params['negative_prompt'] + } + + print(f'Prompting the image generator via the API on {params["address"]}...') + response = requests.post(url=f'{params["address"]}/sdapi/v1/txt2img', json=payload) + response.raise_for_status() + r = response.json() + + visible_result = "" + for img_str in r['images']: + if params['save_img']: + img_data = base64.b64decode(img_str) + + variadic = f'{date.today().strftime("%Y_%m_%d")}/{character}_{int(time.time())}' + output_file = Path(f'extensions/sd_api_pictures/outputs/{variadic}.png') + output_file.parent.mkdir(parents=True, exist_ok=True) + + with open(output_file.as_posix(), 'wb') as f: + f.write(img_data) + + visible_result = visible_result + f'{description}\n' + else: + image = Image.open(io.BytesIO(base64.b64decode(img_str.split(",", 1)[0]))) + # lower the resolution of received images for the chat, otherwise the log size gets out of control quickly with all the base64 values in visible history + image.thumbnail((300, 300)) + buffered = io.BytesIO() + image.save(buffered, format="JPEG") + buffered.seek(0) + image_bytes = buffered.getvalue() + img_str = "data:image/jpeg;base64," + base64.b64encode(image_bytes).decode() + visible_result = visible_result + f'{description}\n' + + if params['manage_VRAM']: + give_VRAM_priority('LLM') + + return visible_result + +# TODO: how do I make the UI history ignore the resulting pictures (I don't want HTML to appear in history) +# and replace it with 'text' for the purposes of logging? +def output_modifier(string, state): + """ + This function is applied to the model outputs. + """ + + global picture_response, params + + if not picture_response: + return string + + string = remove_surrounded_chars(string) + string = string.replace('"', '') + string = string.replace('“', '') + string = string.replace('\n', ' ') + string = string.strip() + + if string == '': + string = 'no viable description in reply, try regenerating' + return string + + text = "" + if (params['mode'] < 2): + toggle_generation(False) + text = f'*Sends a picture which portrays: “{string}”*' + else: + text = string + + string = get_SD_pictures(string, state['character_menu']) + "\n" + text + + return string + + +def bot_prefix_modifier(string): + """ + This function is only applied in chat mode. It modifies + the prefix text for the Bot and can be used to bias its + behavior. + """ + + return string + + +def toggle_generation(*args): + global picture_response, shared + + if not args: + picture_response = not picture_response + else: + picture_response = args[0] + + shared.processing_message = "*Is sending a picture...*" if picture_response else "*Is typing...*" + + +def filter_address(address): + address = address.strip() + # address = re.sub('http(s)?:\/\/|\/$','',address) # remove starting http:// OR https:// OR trailing slash + address = re.sub('\/$', '', address) # remove trailing /s + if not address.startswith('http'): + address = 'http://' + address + return address + + +def SD_api_address_update(address): + global params + + msg = "✔️ SD API is found on:" + address = filter_address(address) + params.update({"address": address}) + try: + response = requests.get(url=f'{params["address"]}/sdapi/v1/sd-models') + response.raise_for_status() + # r = response.json() + except: + msg = "❌ No SD API endpoint on:" + + return gr.Textbox.update(label=msg) + + +def custom_css(): + path_to_css = Path(__file__).parent.resolve() / 'style.css' + return open(path_to_css, 'r').read() + + +def get_checkpoints(): + global params + + try: + models = requests.get(url=f'{params["address"]}/sdapi/v1/sd-models') + options = requests.get(url=f'{params["address"]}/sdapi/v1/options') + options_json = options.json() + params['sd_checkpoint'] = options_json['sd_model_checkpoint'] + params['checkpoint_list'] = [result["title"] for result in models.json()] + except: + params['sd_checkpoint'] = "" + params['checkpoint_list'] = [] + + return gr.update(choices=params['checkpoint_list'], value=params['sd_checkpoint']) + + +def load_checkpoint(checkpoint): + payload = { + "sd_model_checkpoint": checkpoint + } + + try: + requests.post(url=f'{params["address"]}/sdapi/v1/options', json=payload) + except: + pass + + +def get_samplers(): + try: + response = requests.get(url=f'{params["address"]}/sdapi/v1/samplers') + response.raise_for_status() + samplers = [x["name"] for x in response.json()] + except: + samplers = [] + + return samplers + + +def ui(): + + # Gradio elements + # gr.Markdown('### Stable Diffusion API Pictures') # Currently the name of extension is shown as the title + with gr.Accordion("Parameters", open=True, elem_classes="SDAP"): + with gr.Row(): + address = gr.Textbox(placeholder=params['address'], value=params['address'], label='Auto1111\'s WebUI address') + modes_list = ["Manual", "Immersive/Interactive", "Picturebook/Adventure"] + mode = gr.Dropdown(modes_list, value=modes_list[params['mode']], label="Mode of operation", type="index") + with gr.Column(scale=1, min_width=300): + manage_VRAM = gr.Checkbox(value=params['manage_VRAM'], label='Manage VRAM') + save_img = gr.Checkbox(value=params['save_img'], label='Keep original images and use them in chat') + + force_pic = gr.Button("Force the picture response") + suppr_pic = gr.Button("Suppress the picture response") + with gr.Row(): + checkpoint = gr.Dropdown(params['checkpoint_list'], value=params['sd_checkpoint'], label="Checkpoint", type="value") + update_checkpoints = gr.Button("Get list of checkpoints") + + with gr.Accordion("Generation parameters", open=False): + prompt_prefix = gr.Textbox(placeholder=params['prompt_prefix'], value=params['prompt_prefix'], label='Prompt Prefix (best used to describe the look of the character)') + textgen_prefix = gr.Textbox(placeholder=params['textgen_prefix'], value=params['textgen_prefix'], label='textgen prefix (type [subject] where the subject should be placed)') + negative_prompt = gr.Textbox(placeholder=params['negative_prompt'], value=params['negative_prompt'], label='Negative Prompt') + with gr.Row(): + with gr.Column(): + width = gr.Slider(64, 2048, value=params['width'], step=64, label='Width') + height = gr.Slider(64, 2048, value=params['height'], step=64, label='Height') + with gr.Column(variant="compact", elem_id="sampler_col"): + with gr.Row(elem_id="sampler_row"): + sampler_name = gr.Dropdown(value=params['sampler_name'], allow_custom_value=True, label='Sampling method', elem_id="sampler_box") + create_refresh_button(sampler_name, lambda: None, lambda: {'choices': get_samplers()}, 'refresh-button') + steps = gr.Slider(1, 150, value=params['steps'], step=1, label="Sampling steps", elem_id="steps_box") + with gr.Row(): + seed = gr.Number(label="Seed", value=params['seed'], elem_id="seed_box") + cfg_scale = gr.Number(label="CFG Scale", value=params['cfg_scale'], elem_id="cfg_box") + with gr.Column() as hr_options: + restore_faces = gr.Checkbox(value=params['restore_faces'], label='Restore faces') + enable_hr = gr.Checkbox(value=params['enable_hr'], label='Hires. fix') + with gr.Row(visible=params['enable_hr'], elem_classes="hires_opts") as hr_options: + hr_scale = gr.Slider(1, 4, value=params['hr_scale'], step=0.1, label='Upscale by') + denoising_strength = gr.Slider(0, 1, value=params['denoising_strength'], step=0.01, label='Denoising strength') + hr_upscaler = gr.Textbox(placeholder=params['hr_upscaler'], value=params['hr_upscaler'], label='Upscaler') + + # Event functions to update the parameters in the backend + address.change(lambda x: params.update({"address": filter_address(x)}), address, None) + mode.select(lambda x: params.update({"mode": x}), mode, None) + mode.select(lambda x: toggle_generation(x > 1), inputs=mode, outputs=None) + manage_VRAM.change(lambda x: params.update({"manage_VRAM": x}), manage_VRAM, None) + manage_VRAM.change(lambda x: give_VRAM_priority('set' if x else 'reset'), inputs=manage_VRAM, outputs=None) + save_img.change(lambda x: params.update({"save_img": x}), save_img, None) + + address.submit(fn=SD_api_address_update, inputs=address, outputs=address) + prompt_prefix.change(lambda x: params.update({"prompt_prefix": x}), prompt_prefix, None) + textgen_prefix.change(lambda x: params.update({"textgen_prefix": x}), textgen_prefix, None) + negative_prompt.change(lambda x: params.update({"negative_prompt": x}), negative_prompt, None) + width.change(lambda x: params.update({"width": x}), width, None) + height.change(lambda x: params.update({"height": x}), height, None) + hr_scale.change(lambda x: params.update({"hr_scale": x}), hr_scale, None) + denoising_strength.change(lambda x: params.update({"denoising_strength": x}), denoising_strength, None) + restore_faces.change(lambda x: params.update({"restore_faces": x}), restore_faces, None) + hr_upscaler.change(lambda x: params.update({"hr_upscaler": x}), hr_upscaler, None) + enable_hr.change(lambda x: params.update({"enable_hr": x}), enable_hr, None) + enable_hr.change(lambda x: hr_options.update(visible=params["enable_hr"]), enable_hr, hr_options) + update_checkpoints.click(get_checkpoints, None, checkpoint) + checkpoint.change(lambda x: params.update({"sd_checkpoint": x}), checkpoint, None) + checkpoint.change(load_checkpoint, checkpoint, None) + + sampler_name.change(lambda x: params.update({"sampler_name": x}), sampler_name, None) + steps.change(lambda x: params.update({"steps": x}), steps, None) + seed.change(lambda x: params.update({"seed": x}), seed, None) + cfg_scale.change(lambda x: params.update({"cfg_scale": x}), cfg_scale, None) + + force_pic.click(lambda x: toggle_generation(True), inputs=force_pic, outputs=None) + suppr_pic.click(lambda x: toggle_generation(False), inputs=suppr_pic, outputs=None) diff --git a/extensions/sd_api_pictures/style.css b/extensions/sd_api_pictures/style.css new file mode 100644 index 0000000000000000000000000000000000000000..6f4994616a1d4ca52f3a8245f963ce0b7ebbb0d7 --- /dev/null +++ b/extensions/sd_api_pictures/style.css @@ -0,0 +1,52 @@ +/* Align the elements for SD_api_picture extension */ +.SDAP #sampler_box { + padding-top: var(--spacing-sm); + padding-bottom: var(--spacing-sm); + border: 0; +} + +.SDAP #steps_box { + border-radius: 0 0 var(--block-radius) var(--block-radius); +} + +.SDAP #sampler_col { + gap: 0; + padding: 0; + background-color: transparent; +} + +.SDAP #sampler_row { + border-bottom: 0; + box-shadow: var(--block-shadow); + border-width: var(--block-border-width); + border-color: var(--block-border-color); + border-radius: var(--block-radius) var(--block-radius) 0 0; + background: var(--block-background-fill); + gap: 0; +} + +.SDAP #sampler_row .refresh-button { + margin-bottom: var(--spacing-sm); + margin-right: var(--spacing-lg); +} + +.SDAP #seed_box, +.SDAP #cfg_box { + padding-top: var(--spacing-md); +} + +.SDAP #sampler_box span, +.SDAP #seed_box span, +.SDAP #cfg_box span, +.SDAP #steps_box span { + margin-bottom: var(--spacing-sm); +} + +.SDAP svg.dropdown-arrow { + flex-shrink: 0 !important; + margin: 0px !important; +} + +.SDAP .hires_opts input[type="number"] { + width: 6em !important; +} diff --git a/extensions/send_pictures/script.py b/extensions/send_pictures/script.py new file mode 100644 index 0000000000000000000000000000000000000000..f8e6c969271e1dc3d383cefce74774ad505882cd --- /dev/null +++ b/extensions/send_pictures/script.py @@ -0,0 +1,58 @@ +import base64 +from io import BytesIO + +import gradio as gr +import torch +from transformers import BlipForConditionalGeneration, BlipProcessor + +from modules import chat, shared, ui_chat +from modules.ui import gather_interface_values +from modules.utils import gradio + +input_hijack = { + 'state': False, + 'value': ["", ""] +} + +processor = BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-base") +model = BlipForConditionalGeneration.from_pretrained("Salesforce/blip-image-captioning-base", torch_dtype=torch.float32).to("cpu") + + +def chat_input_modifier(text, visible_text, state): + global input_hijack + if input_hijack['state']: + input_hijack['state'] = False + return input_hijack['value'] + else: + return text, visible_text + + +def caption_image(raw_image): + inputs = processor(raw_image.convert('RGB'), return_tensors="pt").to("cpu", torch.float32) + out = model.generate(**inputs, max_new_tokens=100) + return processor.decode(out[0], skip_special_tokens=True) + + +def generate_chat_picture(picture, name1, name2): + text = f'*{name1} sends {name2} a picture that contains the following: “{caption_image(picture)}”*' + # lower the resolution of sent images for the chat, otherwise the log size gets out of control quickly with all the base64 values in visible history + picture.thumbnail((300, 300)) + buffer = BytesIO() + picture.save(buffer, format="JPEG") + img_str = base64.b64encode(buffer.getvalue()).decode('utf-8') + visible_text = f'{text}' + return text, visible_text + + +def ui(): + picture_select = gr.Image(label='Send a picture', type='pil') + + # Prepare the input hijack, update the interface values, call the generation function, and clear the picture + picture_select.upload( + lambda picture, name1, name2: input_hijack.update({ + "state": True, + "value": generate_chat_picture(picture, name1, name2) + }), [picture_select, shared.gradio['name1'], shared.gradio['name2']], None).then( + gather_interface_values, gradio(shared.input_elements), gradio('interface_state')).then( + chat.generate_chat_reply_wrapper, gradio(ui_chat.inputs), gradio('display', 'history'), show_progress=False).then( + lambda: None, None, picture_select, show_progress=False) diff --git a/extensions/silero_tts/harvard_sentences.txt b/extensions/silero_tts/harvard_sentences.txt new file mode 100644 index 0000000000000000000000000000000000000000..958d7f3cb28d8e2ea215ee416068b2f672d9e9d1 --- /dev/null +++ b/extensions/silero_tts/harvard_sentences.txt @@ -0,0 +1,720 @@ +The birch canoe slid on the smooth planks. +Glue the sheet to the dark blue background. +It's easy to tell the depth of a well. +These days a chicken leg is a rare dish. +Rice is often served in round bowls. +The juice of lemons makes fine punch. +The box was thrown beside the parked truck. +The hogs were fed chopped corn and garbage. +Four hours of steady work faced us. +A large size in stockings is hard to sell. +The boy was there when the sun rose. +A rod is used to catch pink salmon. +The source of the huge river is the clear spring. +Kick the ball straight and follow through. +Help the woman get back to her feet. +A pot of tea helps to pass the evening. +Smoky fires lack flame and heat. +The soft cushion broke the man's fall. +The salt breeze came across from the sea. +The girl at the booth sold fifty bonds. +The small pup gnawed a hole in the sock. +The fish twisted and turned on the bent hook. +Press the pants and sew a button on the vest. +The swan dive was far short of perfect. +The beauty of the view stunned the young boy. +Two blue fish swam in the tank. +Her purse was full of useless trash. +The colt reared and threw the tall rider. +It snowed, rained, and hailed the same morning. +Read verse out loud for pleasure. +Hoist the load to your left shoulder. +Take the winding path to reach the lake. +Note closely the size of the gas tank. +Wipe the grease off his dirty face. +Mend the coat before you go out. +The wrist was badly strained and hung limp. +The stray cat gave birth to kittens. +The young girl gave no clear response. +The meal was cooked before the bell rang. +What joy there is in living. +A king ruled the state in the early days. +The ship was torn apart on the sharp reef. +Sickness kept him home the third week. +The wide road shimmered in the hot sun. +The lazy cow lay in the cool grass. +Lift the square stone over the fence. +The rope will bind the seven books at once. +Hop over the fence and plunge in. +The friendly gang left the drug store. +Mesh wire keeps chicks inside. +The frosty air passed through the coat. +The crooked maze failed to fool the mouse. +Adding fast leads to wrong sums. +The show was a flop from the very start. +A saw is a tool used for making boards. +The wagon moved on well oiled wheels. +March the soldiers past the next hill. +A cup of sugar makes sweet fudge. +Place a rosebush near the porch steps. +Both lost their lives in the raging storm. +We talked of the side show in the circus. +Use a pencil to write the first draft. +He ran half way to the hardware store. +The clock struck to mark the third period. +A small creek cut across the field. +Cars and busses stalled in snow drifts. +The set of china hit the floor with a crash. +This is a grand season for hikes on the road. +The dune rose from the edge of the water. +Those words were the cue for the actor to leave. +A yacht slid around the point into the bay. +The two met while playing on the sand. +The ink stain dried on the finished page. +The walled town was seized without a fight. +The lease ran out in sixteen weeks. +A tame squirrel makes a nice pet. +The horn of the car woke the sleeping cop. +The heart beat strongly and with firm strokes. +The pearl was worn in a thin silver ring. +The fruit peel was cut in thick slices. +The Navy attacked the big task force. +See the cat glaring at the scared mouse. +There are more than two factors here. +The hat brim was wide and too droopy. +The lawyer tried to lose his case. +The grass curled around the fence post. +Cut the pie into large parts. +Men strive but seldom get rich. +Always close the barn door tight. +He lay prone and hardly moved a limb. +The slush lay deep along the street. +A wisp of cloud hung in the blue air. +A pound of sugar costs more than eggs. +The fin was sharp and cut the clear water. +The play seems dull and quite stupid. +Bail the boat to stop it from sinking. +The term ended in late June that year. +A tusk is used to make costly gifts. +Ten pins were set in order. +The bill was paid every third week. +Oak is strong and also gives shade. +Cats and dogs each hate the other. +The pipe began to rust while new. +Open the crate but don't break the glass. +Add the sum to the product of these three. +Thieves who rob friends deserve jail. +The ripe taste of cheese improves with age. +Act on these orders with great speed. +The hog crawled under the high fence. +Move the vat over the hot fire. +The bark of the pine tree was shiny and dark. +Leaves turn brown and yellow in the fall. +The pennant waved when the wind blew. +Split the log with a quick, sharp blow. +Burn peat after the logs give out. +He ordered peach pie with ice cream. +Weave the carpet on the right hand side. +Hemp is a weed found in parts of the tropics. +A lame back kept his score low. +We find joy in the simplest things. +Type out three lists of orders. +The harder he tried the less he got done. +The boss ran the show with a watchful eye. +The cup cracked and spilled its contents. +Paste can cleanse the most dirty brass. +The slang word for raw whiskey is booze. +It caught its hind paw in a rusty trap. +The wharf could be seen at the farther shore. +Feel the heat of the weak dying flame. +The tiny girl took off her hat. +A cramp is no small danger on a swim. +He said the same phrase thirty times. +Pluck the bright rose without leaves. +Two plus seven is less than ten. +The glow deepened in the eyes of the sweet girl. +Bring your problems to the wise chief. +Write a fond note to the friend you cherish. +Clothes and lodging are free to new men. +We frown when events take a bad turn. +Port is a strong wine with a smoky taste. +The young kid jumped the rusty gate. +Guess the results from the first scores. +A salt pickle tastes fine with ham. +The just claim got the right verdict. +These thistles bend in a high wind. +Pure bred poodles have curls. +The tree top waved in a graceful way. +The spot on the blotter was made by green ink. +Mud was spattered on the front of his white shirt. +The cigar burned a hole in the desk top. +The empty flask stood on the tin tray. +A speedy man can beat this track mark. +He broke a new shoelace that day. +The coffee stand is too high for the couch. +The urge to write short stories is rare. +The pencils have all been used. +The pirates seized the crew of the lost ship. +We tried to replace the coin but failed. +She sewed the torn coat quite neatly. +The sofa cushion is red and of light weight. +The jacket hung on the back of the wide chair. +At that high level the air is pure. +Drop the two when you add the figures. +A filing case is now hard to buy. +An abrupt start does not win the prize. +Wood is best for making toys and blocks. +The office paint was a dull, sad tan. +He knew the skill of the great young actress. +A rag will soak up spilled water. +A shower of dirt fell from the hot pipes. +Steam hissed from the broken valve. +The child almost hurt the small dog. +There was a sound of dry leaves outside. +The sky that morning was clear and bright blue. +Torn scraps littered the stone floor. +Sunday is the best part of the week. +The doctor cured him with these pills. +The new girl was fired today at noon. +They felt gay when the ship arrived in port. +Add the store's account to the last cent. +Acid burns holes in wool cloth. +Fairy tales should be fun to write. +Eight miles of woodland burned to waste. +The third act was dull and tired the players. +A young child should not suffer fright. +Add the column and put the sum here. +We admire and love a good cook. +There the flood mark is ten inches. +He carved a head from the round block of marble. +She has a smart way of wearing clothes. +The fruit of a fig tree is apple-shaped. +Corn cobs can be used to kindle a fire. +Where were they when the noise started. +The paper box is full of thumb tacks. +Sell your gift to a buyer at a good gain. +The tongs lay beside the ice pail. +The petals fall with the next puff of wind. +Bring your best compass to the third class. +They could laugh although they were sad. +Farmers came in to thresh the oat crop. +The brown house was on fire to the attic. +The lure is used to catch trout and flounder. +Float the soap on top of the bath water. +A blue crane is a tall wading bird. +A fresh start will work such wonders. +The club rented the rink for the fifth night. +After the dance, they went straight home. +The hostess taught the new maid to serve. +He wrote his last novel there at the inn. +Even the worst will beat his low score. +The cement had dried when he moved it. +The loss of the second ship was hard to take. +The fly made its way along the wall. +Do that with a wooden stick. +Live wires should be kept covered. +The large house had hot water taps. +It is hard to erase blue or red ink. +Write at once or you may forget it. +The doorknob was made of bright clean brass. +The wreck occurred by the bank on Main Street. +A pencil with black lead writes best. +Coax a young calf to drink from a bucket. +Schools for ladies teach charm and grace. +The lamp shone with a steady green flame. +They took the axe and the saw to the forest. +The ancient coin was quite dull and worn. +The shaky barn fell with a loud crash. +Jazz and swing fans like fast music. +Rake the rubbish up and then burn it. +Slash the gold cloth into fine ribbons. +Try to have the court decide the case. +They are pushed back each time they attack. +He broke his ties with groups of former friends. +They floated on the raft to sun their white backs. +The map had an X that meant nothing. +Whitings are small fish caught in nets. +Some ads serve to cheat buyers. +Jerk the rope and the bell rings weakly. +A waxed floor makes us lose balance. +Madam, this is the best brand of corn. +On the islands the sea breeze is soft and mild. +The play began as soon as we sat down. +This will lead the world to more sound and fury. +Add salt before you fry the egg. +The rush for funds reached its peak Tuesday. +The birch looked stark white and lonesome. +The box is held by a bright red snapper. +To make pure ice, you freeze water. +The first worm gets snapped early. +Jump the fence and hurry up the bank. +Yell and clap as the curtain slides back. +They are men who walk the middle of the road. +Both brothers wear the same size. +In some form or other we need fun. +The prince ordered his head chopped off. +The houses are built of red clay bricks. +Ducks fly north but lack a compass. +Fruit flavors are used in fizz drinks. +These pills do less good than others. +Canned pears lack full flavor. +The dark pot hung in the front closet. +Carry the pail to the wall and spill it there. +The train brought our hero to the big town. +We are sure that one war is enough. +Gray paint stretched for miles around. +The rude laugh filled the empty room. +High seats are best for football fans. +Tea served from the brown jug is tasty. +A dash of pepper spoils beef stew. +A zestful food is the hot-cross bun. +The horse trotted around the field at a brisk pace. +Find the twin who stole the pearl necklace. +Cut the cord that binds the box tightly. +The red tape bound the smuggled food. +Look in the corner to find the tan shirt. +The cold drizzle will halt the bond drive. +Nine men were hired to dig the ruins. +The junk yard had a mouldy smell. +The flint sputtered and lit a pine torch. +Soak the cloth and drown the sharp odor. +The shelves were bare of both jam or crackers. +A joy to every child is the swan boat. +All sat frozen and watched the screen. +A cloud of dust stung his tender eyes. +To reach the end he needs much courage. +Shape the clay gently into block form. +A ridge on a smooth surface is a bump or flaw. +Hedge apples may stain your hands green. +Quench your thirst, then eat the crackers. +Tight curls get limp on rainy days. +The mute muffled the high tones of the horn. +The gold ring fits only a pierced ear. +The old pan was covered with hard fudge. +Watch the log float in the wide river. +The node on the stalk of wheat grew daily. +The heap of fallen leaves was set on fire. +Write fast if you want to finish early. +His shirt was clean but one button was gone. +The barrel of beer was a brew of malt and hops. +Tin cans are absent from store shelves. +Slide the box into that empty space. +The plant grew large and green in the window. +The beam dropped down on the workmen's head. +Pink clouds floated with the breeze. +She danced like a swan, tall and graceful. +The tube was blown and the tire flat and useless. +It is late morning on the old wall clock. +Let's all join as we sing the last chorus. +The last switch cannot be turned off. +The fight will end in just six minutes. +The store walls were lined with colored frocks. +The peace league met to discuss their plans. +The rise to fame of a person takes luck. +Paper is scarce, so write with much care. +The quick fox jumped on the sleeping cat. +The nozzle of the fire hose was bright brass. +Screw the round cap on as tight as needed. +Time brings us many changes. +The purple tie was ten years old. +Men think and plan and sometimes act. +Fill the ink jar with sticky glue. +He smoke a big pipe with strong contents. +We need grain to keep our mules healthy. +Pack the records in a neat thin case. +The crunch of feet in the snow was the only sound. +The copper bowl shone in the sun's rays. +Boards will warp unless kept dry. +The plush chair leaned against the wall. +Glass will clink when struck by metal. +Bathe and relax in the cool green grass. +Nine rows of soldiers stood in line. +The beach is dry and shallow at low tide. +The idea is to sew both edges straight. +The kitten chased the dog down the street. +Pages bound in cloth make a book. +Try to trace the fine lines of the painting. +Women form less than half of the group. +The zones merge in the central part of town. +A gem in the rough needs work to polish. +Code is used when secrets are sent. +Most of the news is easy for us to hear. +He used the lathe to make brass objects. +The vane on top of the pole revolved in the wind. +Mince pie is a dish served to children. +The clan gathered on each dull night. +Let it burn, it gives us warmth and comfort. +A castle built from sand fails to endure. +A child's wit saved the day for us. +Tack the strip of carpet to the worn floor. +Next Tuesday we must vote. +Pour the stew from the pot into the plate. +Each penny shone like new. +The man went to the woods to gather sticks. +The dirt piles were lines along the road. +The logs fell and tumbled into the clear stream. +Just hoist it up and take it away. +A ripe plum is fit for a king's palate. +Our plans right now are hazy. +Brass rings are sold by these natives. +It takes a good trap to capture a bear. +Feed the white mouse some flower seeds. +The thaw came early and freed the stream. +He took the lead and kept it the whole distance. +The key you designed will fit the lock. +Plead to the council to free the poor thief. +Better hash is made of rare beef. +This plank was made for walking on. +The lake sparkled in the red hot sun. +He crawled with care along the ledge. +Tend the sheep while the dog wanders. +It takes a lot of help to finish these. +Mark the spot with a sign painted red. +Take two shares as a fair profit. +The fur of cats goes by many names. +North winds bring colds and fevers. +He asks no person to vouch for him. +Go now and come here later. +A sash of gold silk will trim her dress. +Soap can wash most dirt away. +That move means the game is over. +He wrote down a long list of items. +A siege will crack the strong defense. +Grape juice and water mix well. +Roads are paved with sticky tar. +Fake stones shine but cost little. +The drip of the rain made a pleasant sound. +Smoke poured out of every crack. +Serve the hot rum to the tired heroes. +Much of the story makes good sense. +The sun came up to light the eastern sky. +Heave the line over the port side. +A lathe cuts and trims any wood. +It's a dense crowd in two distinct ways. +His hip struck the knee of the next player. +The stale smell of old beer lingers. +The desk was firm on the shaky floor. +It takes heat to bring out the odor. +Beef is scarcer than some lamb. +Raise the sail and steer the ship northward. +A cone costs five cents on Mondays. +A pod is what peas always grow in. +Jerk the dart from the cork target. +No cement will hold hard wood. +We now have a new base for shipping. +A list of names is carved around the base. +The sheep were led home by a dog. +Three for a dime, the young peddler cried. +The sense of smell is better than that of touch. +No hardship seemed to keep him sad. +Grace makes up for lack of beauty. +Nudge gently but wake her now. +The news struck doubt into restless minds. +Once we stood beside the shore. +A chink in the wall allowed a draft to blow. +Fasten two pins on each side. +A cold dip restores health and zest. +He takes the oath of office each March. +The sand drifts over the sill of the old house. +The point of the steel pen was bent and twisted. +There is a lag between thought and act. +Seed is needed to plant the spring corn. +Draw the chart with heavy black lines. +The boy owed his pal thirty cents. +The chap slipped into the crowd and was lost. +Hats are worn to tea and not to dinner. +The ramp led up to the wide highway. +Beat the dust from the rug onto the lawn. +Say it slowly but make it ring clear. +The straw nest housed five robins. +Screen the porch with woven straw mats. +This horse will nose his way to the finish. +The dry wax protects the deep scratch. +He picked up the dice for a second roll. +These coins will be needed to pay his debt. +The nag pulled the frail cart along. +Twist the valve and release hot steam. +The vamp of the shoe had a gold buckle. +The smell of burned rags itches my nose. +New pants lack cuffs and pockets. +The marsh will freeze when cold enough. +They slice the sausage thin with a knife. +The bloom of the rose lasts a few days. +A gray mare walked before the colt. +Breakfast buns are fine with a hot drink. +Bottles hold four kinds of rum. +The man wore a feather in his felt hat. +He wheeled the bike past the winding road. +Drop the ashes on the worn old rug. +The desk and both chairs were painted tan. +Throw out the used paper cup and plate. +A clean neck means a neat collar. +The couch cover and hall drapes were blue. +The stems of the tall glasses cracked and broke. +The wall phone rang loud and often. +The clothes dried on a thin wooden rack. +Turn on the lantern which gives us light. +The cleat sank deeply into the soft turf. +The bills were mailed promptly on the tenth of the month. +To have is better than to wait and hope. +The price is fair for a good antique clock. +The music played on while they talked. +Dispense with a vest on a day like this. +The bunch of grapes was pressed into wine. +He sent the figs, but kept the ripe cherries. +The hinge on the door creaked with old age. +The screen before the fire kept in the sparks. +Fly by night, and you waste little time. +Thick glasses helped him read the print. +Birth and death mark the limits of life. +The chair looked strong but had no bottom. +The kite flew wildly in the high wind. +A fur muff is stylish once more. +The tin box held priceless stones. +We need an end of all such matter. +The case was puzzling to the old and wise. +The bright lanterns were gay on the dark lawn. +We don't get much money but we have fun. +The youth drove with zest, but little skill. +Five years he lived with a shaggy dog. +A fence cuts through the corner lot. +The way to save money is not to spend much. +Shut the hatch before the waves push it in. +The odor of spring makes young hearts jump. +Crack the walnut with your sharp side teeth. +He offered proof in the form of a large chart. +Send the stuff in a thick paper bag. +A quart of milk is water for the most part. +They told wild tales to frighten him. +The three story house was built of stone. +In the rear of the ground floor was a large passage. +A man in a blue sweater sat at the desk. +Oats are a food eaten by horse and man. +Their eyelids droop for want of sleep. +A sip of tea revives his tired friend. +There are many ways to do these things. +Tuck the sheet under the edge of the mat. +A force equal to that would move the earth. +We like to see clear weather. +The work of the tailor is seen on each side. +Take a chance and win a china doll. +Shake the dust from your shoes, stranger. +She was kind to sick old people. +The square wooden crate was packed to be shipped. +The dusty bench stood by the stone wall. +We dress to suit the weather of most days. +Smile when you say nasty words. +A bowl of rice is free with chicken stew. +The water in this well is a source of good health. +Take shelter in this tent, but keep still. +That guy is the writer of a few banned books. +The little tales they tell are false. +The door was barred, locked, and bolted as well. +Ripe pears are fit for a queen's table. +A big wet stain was on the round carpet. +The kite dipped and swayed, but stayed aloft. +The pleasant hours fly by much too soon. +The room was crowded with a wild mob. +This strong arm shall shield your honor. +She blushed when he gave her a white orchid. +The beetle droned in the hot June sun. +Press the pedal with your left foot. +Neat plans fail without luck. +The black trunk fell from the landing. +The bank pressed for payment of the debt. +The theft of the pearl pin was kept secret. +Shake hands with this friendly child. +The vast space stretched into the far distance. +A rich farm is rare in this sandy waste. +His wide grin earned many friends. +Flax makes a fine brand of paper. +Hurdle the pit with the aid of a long pole. +A strong bid may scare your partner stiff. +Even a just cause needs power to win. +Peep under the tent and see the clowns. +The leaf drifts along with a slow spin. +Cheap clothes are flashy but don't last. +A thing of small note can cause despair. +Flood the mails with requests for this book. +A thick coat of black paint covered all. +The pencil was cut to be sharp at both ends. +Those last words were a strong statement. +He wrote his name boldly at the top of the sheet. +Dill pickles are sour but taste fine. +Down that road is the way to the grain farmer. +Either mud or dust are found at all times. +The best method is to fix it in place with clips. +If you mumble your speech will be lost. +At night the alarm roused him from a deep sleep. +Read just what the meter says. +Fill your pack with bright trinkets for the poor. +The small red neon lamp went out. +Clams are small, round, soft, and tasty. +The fan whirled its round blades softly. +The line where the edges join was clean. +Breathe deep and smell the piny air. +It matters not if he reads these words or those. +A brown leather bag hung from its strap. +A toad and a frog are hard to tell apart. +A white silk jacket goes with any shoes. +A break in the dam almost caused a flood. +Paint the sockets in the wall dull green. +The child crawled into the dense grass. +Bribes fail where honest men work. +Trample the spark, else the flames will spread. +The hilt of the sword was carved with fine designs. +A round hole was drilled through the thin board. +Footprints showed the path he took up the beach. +She was waiting at my front lawn. +A vent near the edge brought in fresh air. +Prod the old mule with a crooked stick. +It is a band of steel three inches wide. +The pipe ran almost the length of the ditch. +It was hidden from sight by a mass of leaves and shrubs. +The weight of the package was seen on the high scale. +Wake and rise, and step into the green outdoors. +The green light in the brown box flickered. +The brass tube circled the high wall. +The lobes of her ears were pierced to hold rings. +Hold the hammer near the end to drive the nail. +Next Sunday is the twelfth of the month. +Every word and phrase he speaks is true. +He put his last cartridge into the gun and fired. +They took their kids from the public school. +Drive the screw straight into the wood. +Keep the hatch tight and the watch constant. +Sever the twine with a quick snip of the knife. +Paper will dry out when wet. +Slide the catch back and open the desk. +Help the weak to preserve their strength. +A sullen smile gets few friends. +Stop whistling and watch the boys march. +Jerk the cord, and out tumbles the gold. +Slide the tray across the glass top. +The cloud moved in a stately way and was gone. +Light maple makes for a swell room. +Set the piece here and say nothing. +Dull stories make her laugh. +A stiff cord will do to fasten your shoe. +Get the trust fund to the bank early. +Choose between the high road and the low. +A plea for funds seems to come again. +He lent his coat to the tall gaunt stranger. +There is a strong chance it will happen once more. +The duke left the park in a silver coach. +Greet the new guests and leave quickly. +When the frost has come it is time for turkey. +Sweet words work better than fierce. +A thin stripe runs down the middle. +A six comes up more often than a ten. +Lush fern grow on the lofty rocks. +The ram scared the school children off. +The team with the best timing looks good. +The farmer swapped his horse for a brown ox. +Sit on the perch and tell the others what to do. +A steep trail is painful for our feet. +The early phase of life moves fast. +Green moss grows on the northern side. +Tea in thin china has a sweet taste. +Pitch the straw through the door of the stable. +The latch on the back gate needed a nail. +The goose was brought straight from the old market. +The sink is the thing in which we pile dishes. +A whiff of it will cure the most stubborn cold. +The facts don't always show who is right. +She flaps her cape as she parades the street. +The loss of the cruiser was a blow to the fleet. +Loop the braid to the left and then over. +Plead with the lawyer to drop the lost cause. +Calves thrive on tender spring grass. +Post no bills on this office wall. +Tear a thin sheet from the yellow pad. +A cruise in warm waters in a sleek yacht is fun. +A streak of color ran down the left edge. +It was done before the boy could see it. +Crouch before you jump or miss the mark. +Pack the kits and don't forget the salt. +The square peg will settle in the round hole. +Fine soap saves tender skin. +Poached eggs and tea must suffice. +Bad nerves are jangled by a door slam. +Ship maps are different from those for planes. +Dimes showered down from all sides. +They sang the same tunes at each party. +The sky in the west is tinged with orange red. +The pods of peas ferment in bare fields. +The horse balked and threw the tall rider. +The hitch between the horse and cart broke. +Pile the coal high in the shed corner. +A gold vase is both rare and costly. +The knife was hung inside its bright sheath. +The rarest spice comes from the far East. +The roof should be tilted at a sharp slant. +A smatter of French is worse than none. +The mule trod the treadmill day and night. +The aim of the contest is to raise a great fund. +To send it now in large amounts is bad. +There is a fine hard tang in salty air. +Cod is the main business of the north shore. +The slab was hewn from heavy blocks of slate. +Dunk the stale biscuits into strong drink. +Hang tinsel from both branches. +Cap the jar with a tight brass cover. +The poor boy missed the boat again. +Be sure to set the lamp firmly in the hole. +Pick a card and slip it under the pack. +A round mat will cover the dull spot. +The first part of the plan needs changing. +A good book informs of what we ought to know. +The mail comes in three batches per day. +You cannot brew tea in a cold pot. +Dots of light betrayed the black cat. +Put the chart on the mantel and tack it down. +The night shift men rate extra pay. +The red paper brightened the dim stage. +See the player scoot to third base. +Slide the bill between the two leaves. +Many hands help get the job done. +We don't like to admit our small faults. +No doubt about the way the wind blows. +Dig deep in the earth for pirate's gold. +The steady drip is worse than a drenching rain. +A flat pack takes less luggage space. +Green ice frosted the punch bowl. +A stuffed chair slipped from the moving van. +The stitch will serve but needs to be shortened. +A thin book fits in the side pocket. +The gloss on top made it unfit to read. +The hail pattered on the burnt brown grass. +Seven seals were stamped on great sheets. +Our troops are set to strike heavy blows. +The store was jammed before the sale could start. +It was a bad error on the part of the new judge. +One step more and the board will collapse. +Take the match and strike it against your shoe. +The pot boiled, but the contents failed to jell. +The baby puts his right foot in his mouth. +The bombs left most of the town in ruins. +Stop and stare at the hard working man. +The streets are narrow and full of sharp turns. +The pup jerked the leash as he saw a feline shape. +Open your book to the first page. +Fish evade the net and swim off. +Dip the pail once and let it settle. +Will you please answer that phone. +The big red apple fell to the ground. +The curtain rose and the show was on. +The young prince became heir to the throne. +He sent the boy on a short errand. +Leave now and you will arrive on time. +The corner store was robbed last night. +A gold ring will please most any girl. +The long journey home took a year. +She saw a cat in the neighbor's house. +A pink shell was found on the sandy beach. +Small children came to see him. +The grass and bushes were wet with dew. +The blind man counted his old coins. +A severe storm tore down the barn. +She called his name many times. +When you hear the bell, come quickly. \ No newline at end of file diff --git a/extensions/silero_tts/languages.json b/extensions/silero_tts/languages.json new file mode 100644 index 0000000000000000000000000000000000000000..69c68df523fa68a72960cd141f8adeb43189b923 --- /dev/null +++ b/extensions/silero_tts/languages.json @@ -0,0 +1,176 @@ +{ + "English": { + "lang_id": "en", + "voices": ["en_99", "en_45", "en_18", "en_117", "en_49", "en_51", "en_68", "en_0", "en_26", "en_56", "en_74", "en_5", "en_38", "en_53", "en_21", "en_37", "en_107", "en_10", "en_82", "en_16", "en_41", "en_12", "en_67", "en_61", "en_14", "en_11", "en_39", "en_52", "en_24", "en_97", "en_28", "en_72", "en_94", "en_36", "en_4", "en_43", "en_88", "en_25", "en_65", "en_6", "en_44", "en_75", "en_91", "en_60", "en_109", "en_85", "en_101", "en_108", "en_50", "en_96", "en_64", "en_92", "en_76", "en_33", "en_116", "en_48", "en_98", "en_86", "en_62", "en_54", "en_95", "en_55", "en_111", "en_3", "en_83", "en_8", "en_47", "en_59", "en_1", "en_2", "en_7", "en_9", "en_13", "en_15", "en_17", "en_19", "en_20", "en_22", "en_23", "en_27", "en_29", "en_30", "en_31", "en_32", "en_34", "en_35", "en_40", "en_42", "en_46", "en_57", "en_58", "en_63", "en_66", "en_69", "en_70", "en_71", "en_73", "en_77", "en_78", "en_79", "en_80", "en_81", "en_84", "en_87", "en_89", "en_90", "en_93", "en_100", "en_102", "en_103", "en_104", "en_105", "en_106", "en_110", "en_112", "en_113", "en_114", "en_115"], + "default_voice": "en_56", + "model_id": "v3_en" + }, + "Spanish": { + "lang_id": "es", + "voices": ["es_0", "es_1", "es_2"], + "default_voice": "es_0", + "model_id": "v3_es" + }, + "French": { + "lang_id": "fr", + "voices": ["fr_0", "fr_1", "fr_2", "fr_3", "fr_4", "fr_5"], + "default_voice": "fr_0", + "model_id": "v3_fr" + }, + "German": { + "lang_id": "de", + "voices": ["bernd_ungerer", "eva_k", "friedrich", "hokuspokus", "karlsson"], + "default_voice": "eva_k", + "model_id": "v3_de" + }, + "Russian": { + "lang_id": "ru", + "voices": ["aidar", "baya", "kseniya", "xenia"], + "default_voice": "aidar", + "model_id": "ru_v3" + }, + "Tatar": { + "lang_id": "tt", + "voices": ["dilyara"], + "default_voice": "dilyara", + "model_id": "v3_tt" + }, + "Ukranian": { + "lang_id": "ua", + "voices": ["mykyta"], + "default_voice": "mykyta", + "model_id": "v3_ua" + }, + "Uzbek": { + "lang_id": "uz", + "voices": ["dilnavoz"], + "default_voice": "dilnavoz", + "model_id": "v3_uz" + }, + "English (India)": { + "lang_id": "en", + "voices": ["tamil_female", "bengali_female", "malayalam_male", "manipuri_female", "assamese_female", "gujarati_male", "telugu_male", "kannada_male", "hindi_female", "rajasthani_female", "kannada_female", "bengali_male", "tamil_male", "gujarati_female", "assamese_male"], + "default_voice": "hindi_female", + "model_id": "v3_en_indic" + }, + "Avar": { + "lang_id": "cyrillic", + "voices": ["b_ava"], + "default_voice": "b_ava", + "model_id": "v4_cyrillic" + }, + "Bashkir": { + "lang_id": "cyrillic", + "voices": ["b_bashkir"], + "default_voice": "b_bashkir", + "model_id": "v4_cyrillic" + }, + "Bulgarian": { + "lang_id": "cyrillic", + "voices": ["b_bulb", "b_bulc"], + "default_voice": "b_bulb", + "model_id": "v4_cyrillic" + }, + "Chechen": { + "lang_id": "cyrillic", + "voices": ["cv_ekaterina", "b_cv"], + "default_voice": "b_che", + "model_id": "v4_cyrillic" + }, + "Chuvash": { + "lang_id": "cyrillic", + "voices": ["b_che"], + "default_voice": "b_che", + "model_id": "v4_cyrillic" + }, + "Erzya": { + "lang_id": "cyrillic", + "voices": ["b_myv"], + "default_voice": "b_myv", + "model_id": "v4_cyrillic" + }, + "Kalmyk": { + "lang_id": "cyrillic", + "voices": ["b_kalmyk"], + "default_voice": "b_kalmyk", + "model_id": "v4_cyrillic" + }, + "Karachay-Balkar": { + "lang_id": "cyrillic", + "voices": ["b_krc"], + "default_voice": "b_krc", + "model_id": "v4_cyrillic" + }, + "Kazakh": { + "lang_id": "cyrillic", + "voices": ["kz_F1", "kz_F2", "kz_F3", "kz_M1", "kz_M1"], + "default_voice": "kz_F1", + "model_id": "v4_cyrillic" + }, + "Khakas": { + "lang_id": "cyrillic", + "voices": ["b_kjh"], + "default_voice": "b_kjh", + "model_id": "v4_cyrillic" + }, + "Komi-Ziryan": { + "lang_id": "cyrillic", + "voices": ["b_kpv"], + "default_voice": "b_kpv", + "model_id": "v4_cyrillic" + }, + "Lezghian": { + "lang_id": "cyrillic", + "voices": ["b_lez"], + "default_voice": "b_lez", + "model_id": "v4_cyrillic" + }, + "Mari": { + "lang_id": "cyrillic", + "voices": ["b_mhr"], + "default_voice": "b_mhr", + "model_id": "v4_cyrillic" + }, + "Mari High": { + "lang_id": "cyrillic", + "voices": ["b_mrj"], + "default_voice": "b_mrj", + "model_id": "v4_cyrillic" + }, + "Nogai": { + "lang_id": "cyrillic", + "voices": ["b_nog"], + "default_voice": "b_nog", + "model_id": "v4_cyrillic" + }, + "Ossetic": { + "lang_id": "cyrillic", + "voices": ["b_oss"], + "default_voice": "b_nog", + "model_id": "v4_cyrillic" + }, + "Tuvinian": { + "lang_id": "cyrillic", + "voices": ["b_tyv"], + "default_voice": "b_tyv", + "model_id": "v4_cyrillic" + }, + "Udmurt": { + "lang_id": "cyrillic", + "voices": ["b_udm"], + "default_voice": "b_udm", + "model_id": "v4_cyrillic" + }, + "Yakut": { + "lang_id": "cyrillic", + "voices": ["b_sah"], + "default_voice": "b_sah", + "model_id": "v4_cyrillic" + }, + "Kalmyk": { + "lang_id": "cyrillic", + "voices": ["kalmyk_delghir", "kalmyk_erdni"], + "default_voice": "kalmyk_delghir", + "model_id": "v4_cyrillic" + } +} diff --git a/extensions/silero_tts/outputs/outputs-will-be-saved-here.txt b/extensions/silero_tts/outputs/outputs-will-be-saved-here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/extensions/silero_tts/requirements.txt b/extensions/silero_tts/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..1017bf0d7accb9930872ededd8a4bc077d393958 --- /dev/null +++ b/extensions/silero_tts/requirements.txt @@ -0,0 +1,5 @@ +ipython +num2words +omegaconf +pydub +PyYAML diff --git a/extensions/silero_tts/script.py b/extensions/silero_tts/script.py new file mode 100644 index 0000000000000000000000000000000000000000..234338dda16c83a797ce1b451d51c38260c0ef23 --- /dev/null +++ b/extensions/silero_tts/script.py @@ -0,0 +1,240 @@ +import html +import json +import random +import time +from pathlib import Path + +import gradio as gr +import torch + +from extensions.silero_tts import tts_preprocessor +from modules import chat, shared, ui_chat +from modules.utils import gradio + +torch._C._jit_set_profiling_mode(False) + + +params = { + 'activate': True, + 'speaker': 'en_56', + 'language': 'English', + 'model_id': 'v3_en', + 'sample_rate': 48000, + 'device': 'cpu', + 'show_text': False, + 'autoplay': True, + 'voice_pitch': 'medium', + 'voice_speed': 'medium', + 'local_cache_path': '' # User can override the default cache path to something other via settings.json +} + +current_params = params.copy() + +with open(Path("extensions/silero_tts/languages.json"), encoding='utf8') as f: + languages = json.load(f) + +voice_pitches = ['x-low', 'low', 'medium', 'high', 'x-high'] +voice_speeds = ['x-slow', 'slow', 'medium', 'fast', 'x-fast'] + +# Used for making text xml compatible, needed for voice pitch and speed control +table = str.maketrans({ + "<": "<", + ">": ">", + "&": "&", + "'": "'", + '"': """, +}) + + +def xmlesc(txt): + return txt.translate(table) + + +def load_model(): + torch_cache_path = torch.hub.get_dir() if params['local_cache_path'] == '' else params['local_cache_path'] + model_path = torch_cache_path + "/snakers4_silero-models_master/src/silero/model/" + params['model_id'] + ".pt" + if Path(model_path).is_file(): + print(f'\nUsing Silero TTS cached checkpoint found at {torch_cache_path}') + model, example_text = torch.hub.load(repo_or_dir=torch_cache_path + '/snakers4_silero-models_master/', model='silero_tts', language=languages[params['language']]["lang_id"], speaker=params['model_id'], source='local', path=model_path, force_reload=True) + else: + print(f'\nSilero TTS cache not found at {torch_cache_path}. Attempting to download...') + model, example_text = torch.hub.load(repo_or_dir='snakers4/silero-models', model='silero_tts', language=languages[params['language']]["lang_id"], speaker=params['model_id']) + model.to(params['device']) + return model + + +def remove_tts_from_history(history): + for i, entry in enumerate(history['internal']): + history['visible'][i] = [history['visible'][i][0], entry[1]] + + return history + + +def toggle_text_in_history(history): + for i, entry in enumerate(history['visible']): + visible_reply = entry[1] + if visible_reply.startswith('')[0]}\n\n{reply}"] + else: + history['visible'][i] = [history['visible'][i][0], f"{visible_reply.split('')[0]}"] + + return history + + +def state_modifier(state): + if not params['activate']: + return state + + state['stream'] = False + return state + + +def input_modifier(string, state): + if not params['activate']: + return string + + shared.processing_message = "*Is recording a voice message...*" + return string + + +def history_modifier(history): + # Remove autoplay from the last reply + if len(history['internal']) > 0: + history['visible'][-1] = [ + history['visible'][-1][0], + history['visible'][-1][1].replace('controls autoplay>', 'controls>') + ] + + return history + + +def output_modifier(string, state): + global model, current_params, streaming_state + + for i in params: + if params[i] != current_params[i]: + model = load_model() + current_params = params.copy() + break + + if not params['activate']: + return string + + original_string = string + + string = tts_preprocessor.preprocess(html.unescape(string)) + + if string == '': + string = '*Empty reply, try regenerating*' + else: + output_file = Path(f'extensions/silero_tts/outputs/{state["character_menu"]}_{int(time.time())}.wav') + prosody = ''.format(params['voice_speed'], params['voice_pitch']) + silero_input = f'{prosody}{xmlesc(string)}' + model.save_wav(ssml_text=silero_input, speaker=params['speaker'], sample_rate=int(params['sample_rate']), audio_path=str(output_file)) + + autoplay = 'autoplay' if params['autoplay'] else '' + string = f'' + if params['show_text']: + string += f'\n\n{original_string}' + + shared.processing_message = "*Is typing...*" + return string + + +def setup(): + global model + model = load_model() + + +def random_sentence(): + with open(Path("extensions/silero_tts/harvard_sentences.txt")) as f: + return random.choice(list(f)) + + +def voice_preview(string): + global model, current_params, streaming_state + + for i in params: + if params[i] != current_params[i]: + model = load_model() + current_params = params.copy() + break + + string = tts_preprocessor.preprocess(string or random_sentence()) + + output_file = Path('extensions/silero_tts/outputs/voice_preview.wav') + prosody = f"" + silero_input = f'{prosody}{xmlesc(string)}' + model.save_wav(ssml_text=silero_input, speaker=params['speaker'], sample_rate=int(params['sample_rate']), audio_path=str(output_file)) + + return f'' + + +def language_change(lang): + global params + params.update({"language": lang, "speaker": languages[lang]["default_voice"], "model_id": languages[lang]["model_id"]}) + return gr.update(choices=languages[lang]["voices"], value=languages[lang]["default_voice"]) + + +def custom_css(): + path_to_css = Path(__file__).parent.resolve() / 'style.css' + return open(path_to_css, 'r').read() + + +def ui(): + # Gradio elements + with gr.Accordion("Silero TTS"): + with gr.Row(): + activate = gr.Checkbox(value=params['activate'], label='Activate TTS') + autoplay = gr.Checkbox(value=params['autoplay'], label='Play TTS automatically') + + show_text = gr.Checkbox(value=params['show_text'], label='Show message text under audio player') + + with gr.Row(): + language = gr.Dropdown(value=params['language'], choices=sorted(languages.keys()), label='Language') + voice = gr.Dropdown(value=params['speaker'], choices=languages[params['language']]["voices"], label='TTS voice') + with gr.Row(): + v_pitch = gr.Dropdown(value=params['voice_pitch'], choices=voice_pitches, label='Voice pitch') + v_speed = gr.Dropdown(value=params['voice_speed'], choices=voice_speeds, label='Voice speed') + + with gr.Row(): + preview_text = gr.Text(show_label=False, placeholder="Preview text", elem_id="silero_preview_text") + preview_play = gr.Button("Preview") + preview_audio = gr.HTML(visible=False) + + with gr.Row(): + convert = gr.Button('Permanently replace audios with the message texts') + convert_cancel = gr.Button('Cancel', visible=False) + convert_confirm = gr.Button('Confirm (cannot be undone)', variant="stop", visible=False) + + # Convert history with confirmation + convert_arr = [convert_confirm, convert, convert_cancel] + convert.click(lambda: [gr.update(visible=True), gr.update(visible=False), gr.update(visible=True)], None, convert_arr) + convert_confirm.click( + lambda: [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)], None, convert_arr).then( + remove_tts_from_history, gradio('history'), gradio('history')).then( + chat.save_history, gradio('history', 'unique_id', 'character_menu', 'mode'), None).then( + chat.redraw_html, gradio(ui_chat.reload_arr), gradio('display')) + + convert_cancel.click(lambda: [gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)], None, convert_arr) + + # Toggle message text in history + show_text.change( + lambda x: params.update({"show_text": x}), show_text, None).then( + toggle_text_in_history, gradio('history'), gradio('history')).then( + chat.save_history, gradio('history', 'unique_id', 'character_menu', 'mode'), None).then( + chat.redraw_html, gradio(ui_chat.reload_arr), gradio('display')) + + # Event functions to update the parameters in the backend + activate.change(lambda x: params.update({"activate": x}), activate, None) + autoplay.change(lambda x: params.update({"autoplay": x}), autoplay, None) + language.change(language_change, language, voice, show_progress=False) + voice.change(lambda x: params.update({"speaker": x}), voice, None) + v_pitch.change(lambda x: params.update({"voice_pitch": x}), v_pitch, None) + v_speed.change(lambda x: params.update({"voice_speed": x}), v_speed, None) + + # Play preview + preview_text.submit(voice_preview, preview_text, preview_audio) + preview_play.click(voice_preview, preview_text, preview_audio) diff --git a/extensions/silero_tts/style.css b/extensions/silero_tts/style.css new file mode 100644 index 0000000000000000000000000000000000000000..2ab7aefbbfca19982414f13a76dfdd4324793903 --- /dev/null +++ b/extensions/silero_tts/style.css @@ -0,0 +1,8 @@ +.SDAP .hires_opts input[type="number"] { + width: 6em !important; +} + +/* silero_tts preview */ +.form:has(> #silero_preview_text) { + min-width: 75% +} diff --git a/extensions/silero_tts/test_tts.py b/extensions/silero_tts/test_tts.py new file mode 100644 index 0000000000000000000000000000000000000000..ebc2c102a9ef29f21141429232f957421989cdd4 --- /dev/null +++ b/extensions/silero_tts/test_tts.py @@ -0,0 +1,81 @@ +import time +from pathlib import Path + +import torch +import tts_preprocessor + +torch._C._jit_set_profiling_mode(False) + + +params = { + 'activate': True, + 'speaker': 'en_49', + 'language': 'en', + 'model_id': 'v3_en', + 'sample_rate': 48000, + 'device': 'cpu', + 'show_text': True, + 'autoplay': True, + 'voice_pitch': 'medium', + 'voice_speed': 'medium', +} + +current_params = params.copy() +voices_by_gender = ['en_99', 'en_45', 'en_18', 'en_117', 'en_49', 'en_51', 'en_68', 'en_0', 'en_26', 'en_56', 'en_74', 'en_5', 'en_38', 'en_53', 'en_21', 'en_37', 'en_107', 'en_10', 'en_82', 'en_16', 'en_41', 'en_12', 'en_67', 'en_61', 'en_14', 'en_11', 'en_39', 'en_52', 'en_24', 'en_97', 'en_28', 'en_72', 'en_94', 'en_36', 'en_4', 'en_43', 'en_88', 'en_25', 'en_65', 'en_6', 'en_44', 'en_75', 'en_91', 'en_60', 'en_109', 'en_85', 'en_101', 'en_108', 'en_50', 'en_96', 'en_64', 'en_92', 'en_76', 'en_33', 'en_116', 'en_48', 'en_98', 'en_86', 'en_62', 'en_54', 'en_95', 'en_55', 'en_111', 'en_3', 'en_83', 'en_8', 'en_47', 'en_59', 'en_1', 'en_2', 'en_7', 'en_9', 'en_13', 'en_15', 'en_17', 'en_19', 'en_20', 'en_22', 'en_23', 'en_27', 'en_29', 'en_30', 'en_31', 'en_32', 'en_34', 'en_35', 'en_40', 'en_42', 'en_46', 'en_57', 'en_58', 'en_63', 'en_66', 'en_69', 'en_70', 'en_71', 'en_73', 'en_77', 'en_78', 'en_79', 'en_80', 'en_81', 'en_84', 'en_87', 'en_89', 'en_90', 'en_93', 'en_100', 'en_102', 'en_103', 'en_104', 'en_105', 'en_106', 'en_110', 'en_112', 'en_113', 'en_114', 'en_115'] +voice_pitches = ['x-low', 'low', 'medium', 'high', 'x-high'] +voice_speeds = ['x-slow', 'slow', 'medium', 'fast', 'x-fast'] + +# Used for making text xml compatible, needed for voice pitch and speed control +table = str.maketrans({ + "<": "<", + ">": ">", + "&": "&", + "'": "'", + '"': """, +}) + + +def xmlesc(txt): + return txt.translate(table) + + +def load_model(): + model, example_text = torch.hub.load(repo_or_dir='snakers4/silero-models', model='silero_tts', language=params['language'], speaker=params['model_id']) + model.to(params['device']) + return model + + +model = load_model() + + +def output_modifier(string): + """ + This function is applied to the model outputs. + """ + + global model, current_params + + original_string = string + string = tts_preprocessor.preprocess(string) + processed_string = string + + if string == '': + string = '*Empty reply, try regenerating*' + else: + output_file = Path(f'extensions/silero_tts/outputs/test_{int(time.time())}.wav') + prosody = ''.format(params['voice_speed'], params['voice_pitch']) + silero_input = f'{prosody}{xmlesc(string)}' + model.save_wav(ssml_text=silero_input, speaker=params['speaker'], sample_rate=int(params['sample_rate']), audio_path=str(output_file)) + + autoplay = 'autoplay' if params['autoplay'] else '' + string = f'' + + if params['show_text']: + string += f'\n\n{original_string}\n\nProcessed:\n{processed_string}' + + print(string) + + +if __name__ == '__main__': + import sys + output_modifier(sys.argv[1]) diff --git a/extensions/silero_tts/tts_preprocessor.py b/extensions/silero_tts/tts_preprocessor.py new file mode 100644 index 0000000000000000000000000000000000000000..daefdcbda6c9b20a87c6f3d84d2a759c2c51289c --- /dev/null +++ b/extensions/silero_tts/tts_preprocessor.py @@ -0,0 +1,200 @@ +import re + +from num2words import num2words + +punctuation = r'[\s,.?!/)\'\]>]' +alphabet_map = { + "A": " Ei ", + "B": " Bee ", + "C": " See ", + "D": " Dee ", + "E": " Eee ", + "F": " Eff ", + "G": " Jee ", + "H": " Eich ", + "I": " Eye ", + "J": " Jay ", + "K": " Kay ", + "L": " El ", + "M": " Emm ", + "N": " Enn ", + "O": " Ohh ", + "P": " Pee ", + "Q": " Queue ", + "R": " Are ", + "S": " Ess ", + "T": " Tee ", + "U": " You ", + "V": " Vee ", + "W": " Double You ", + "X": " Ex ", + "Y": " Why ", + "Z": " Zed " # Zed is weird, as I (da3dsoul) am American, but most of the voice models sound British, so it matches +} + + +def preprocess(string): + # the order for some of these matter + # For example, you need to remove the commas in numbers before expanding them + string = remove_surrounded_chars(string) + string = string.replace('"', '') + string = string.replace('\u201D', '').replace('\u201C', '') # right and left quote + string = string.replace('\u201F', '') # italic looking quote + string = string.replace('\n', ' ') + string = convert_num_locale(string) + string = replace_negative(string) + string = replace_roman(string) + string = hyphen_range_to(string) + string = num_to_words(string) + + # TODO Try to use a ML predictor to expand abbreviations. It's hard, dependent on context, and whether to actually + # try to say the abbreviation or spell it out as I've done below is not agreed upon + + # For now, expand abbreviations to pronunciations + # replace_abbreviations adds a lot of unnecessary whitespace to ensure separation + string = replace_abbreviations(string) + string = replace_lowercase_abbreviations(string) + + # cleanup whitespaces + # remove whitespace before punctuation + string = re.sub(rf'\s+({punctuation})', r'\1', string) + string = string.strip() + # compact whitespace + string = ' '.join(string.split()) + + return string + + +def remove_surrounded_chars(string): + # first this expression will check if there is a string nested exclusively between a alt= + # and a style= string. This would correspond to only a the alt text of an embedded image + # If it matches it will only keep that part as the string, and rend it for further processing + # Afterwards this expression matches to 'as few symbols as possible (0 upwards) between any + # asterisks' OR' as few symbols as possible (0 upwards) between an asterisk and the end of the string' + if re.search(r'(?<=alt=)(.*)(?=style=)', string, re.DOTALL): + m = re.search(r'(?<=alt=)(.*)(?=style=)', string, re.DOTALL) + string = m.group(0) + return re.sub(r'\*[^*]*?(\*|$)', '', string) + + +def convert_num_locale(text): + # This detects locale and converts it to American without comma separators + pattern = re.compile(r'(?:\s|^)\d{1,3}(?:\.\d{3})+(,\d+)(?:\s|$)') + result = text + while True: + match = pattern.search(result) + if match is None: + break + + start = match.start() + end = match.end() + result = result[0:start] + result[start:end].replace('.', '').replace(',', '.') + result[end:len(result)] + + # removes comma separators from existing American numbers + pattern = re.compile(r'(\d),(\d)') + result = pattern.sub(r'\1\2', result) + + return result + + +def replace_negative(string): + # handles situations like -5. -5 would become negative 5, which would then be expanded to negative five + return re.sub(rf'(\s)(-)(\d+)({punctuation})', r'\1negative \3\4', string) + + +def replace_roman(string): + # find a string of roman numerals. + # Only 2 or more, to avoid capturing I and single character abbreviations, like names + pattern = re.compile(rf'\s[IVXLCDM]{{2,}}{punctuation}') + result = string + while True: + match = pattern.search(result) + if match is None: + break + + start = match.start() + end = match.end() + result = result[0:start + 1] + str(roman_to_int(result[start + 1:end - 1])) + result[end - 1:len(result)] + + return result + + +def roman_to_int(s): + rom_val = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} + int_val = 0 + for i in range(len(s)): + if i > 0 and rom_val[s[i]] > rom_val[s[i - 1]]: + int_val += rom_val[s[i]] - 2 * rom_val[s[i - 1]] + else: + int_val += rom_val[s[i]] + return int_val + + +def hyphen_range_to(text): + pattern = re.compile(r'(\d+)[-–](\d+)') + result = pattern.sub(lambda x: x.group(1) + ' to ' + x.group(2), text) + return result + + +def num_to_words(text): + # 1000 or 10.23 + pattern = re.compile(r'\d+\.\d+|\d+') + result = pattern.sub(lambda x: num2words(float(x.group())), text) + return result + + +def replace_abbreviations(string): + # abbreviations 1 to 4 characters long. It will get things like A and I, but those are pronounced with their letter + pattern = re.compile(rf'(^|[\s(.\'\[<])([A-Z]{{1,4}})({punctuation}|$)') + result = string + while True: + match = pattern.search(result) + if match is None: + break + + start = match.start() + end = match.end() + result = result[0:start] + replace_abbreviation(result[start:end]) + result[end:len(result)] + + return result + + +def replace_lowercase_abbreviations(string): + # abbreviations 1 to 4 characters long, separated by dots i.e. e.g. + pattern = re.compile(rf'(^|[\s(.\'\[<])(([a-z]\.){{1,4}})({punctuation}|$)') + result = string + while True: + match = pattern.search(result) + if match is None: + break + + start = match.start() + end = match.end() + result = result[0:start] + replace_abbreviation(result[start:end].upper()) + result[end:len(result)] + + return result + + +def replace_abbreviation(string): + result = "" + for char in string: + result += match_mapping(char) + + return result + + +def match_mapping(char): + for mapping in alphabet_map.keys(): + if char == mapping: + return alphabet_map[char] + + return char + + +def __main__(args): + print(preprocess(args[1])) + + +if __name__ == "__main__": + import sys + __main__(sys.argv) diff --git a/extensions/superbooga/chromadb.py b/extensions/superbooga/chromadb.py new file mode 100644 index 0000000000000000000000000000000000000000..1fb7a71848a8c99ab29b90c49902b545a1595f03 --- /dev/null +++ b/extensions/superbooga/chromadb.py @@ -0,0 +1,125 @@ +import chromadb +import posthog +import torch +from chromadb.config import Settings +from sentence_transformers import SentenceTransformer + +from modules.logging_colors import logger + +logger.info('Intercepting all calls to posthog :)') +posthog.capture = lambda *args, **kwargs: None + + +class Collecter(): + def __init__(self): + pass + + def add(self, texts: list[str]): + pass + + def get(self, search_strings: list[str], n_results: int) -> list[str]: + pass + + def clear(self): + pass + + +class Embedder(): + def __init__(self): + pass + + def embed(self, text: str) -> list[torch.Tensor]: + pass + + +class ChromaCollector(Collecter): + def __init__(self, embedder: Embedder): + super().__init__() + self.chroma_client = chromadb.Client(Settings(anonymized_telemetry=False)) + self.embedder = embedder + self.collection = self.chroma_client.create_collection(name="context", embedding_function=embedder.embed) + self.ids = [] + + def add(self, texts: list[str]): + if len(texts) == 0: + return + + self.ids = [f"id{i}" for i in range(len(texts))] + self.collection.add(documents=texts, ids=self.ids) + + def get_documents_ids_distances(self, search_strings: list[str], n_results: int): + n_results = min(len(self.ids), n_results) + if n_results == 0: + return [], [], [] + + result = self.collection.query(query_texts=search_strings, n_results=n_results, include=['documents', 'distances']) + documents = result['documents'][0] + ids = list(map(lambda x: int(x[2:]), result['ids'][0])) + distances = result['distances'][0] + return documents, ids, distances + + # Get chunks by similarity + def get(self, search_strings: list[str], n_results: int) -> list[str]: + documents, _, _ = self.get_documents_ids_distances(search_strings, n_results) + return documents + + # Get ids by similarity + def get_ids(self, search_strings: list[str], n_results: int) -> list[str]: + _, ids, _ = self.get_documents_ids_distances(search_strings, n_results) + return ids + + # Get chunks by similarity and then sort by insertion order + def get_sorted(self, search_strings: list[str], n_results: int) -> list[str]: + documents, ids, _ = self.get_documents_ids_distances(search_strings, n_results) + return [x for _, x in sorted(zip(ids, documents))] + + # Multiply distance by factor within [0, time_weight] where more recent is lower + def apply_time_weight_to_distances(self, ids: list[int], distances: list[float], time_weight: float = 1.0) -> list[float]: + if len(self.ids) <= 1: + return distances.copy() + + return [distance * (1 - _id / (len(self.ids) - 1) * time_weight) for _id, distance in zip(ids, distances)] + + # Get ids by similarity and then sort by insertion order + def get_ids_sorted(self, search_strings: list[str], n_results: int, n_initial: int = None, time_weight: float = 1.0) -> list[str]: + do_time_weight = time_weight > 0 + if not (do_time_weight and n_initial is not None): + n_initial = n_results + elif n_initial == -1: + n_initial = len(self.ids) + + if n_initial < n_results: + raise ValueError(f"n_initial {n_initial} should be >= n_results {n_results}") + + _, ids, distances = self.get_documents_ids_distances(search_strings, n_initial) + if do_time_weight: + distances_w = self.apply_time_weight_to_distances(ids, distances, time_weight=time_weight) + results = zip(ids, distances, distances_w) + results = sorted(results, key=lambda x: x[2])[:n_results] + results = sorted(results, key=lambda x: x[0]) + ids = [x[0] for x in results] + + return sorted(ids) + + def clear(self): + self.collection.delete(ids=self.ids) + self.ids = [] + + +class SentenceTransformerEmbedder(Embedder): + def __init__(self) -> None: + self.model = SentenceTransformer("sentence-transformers/all-mpnet-base-v2") + self.embed = self.model.encode + + +def make_collector(): + global embedder + return ChromaCollector(embedder) + + +def add_chunks_to_collector(chunks, collector): + collector.clear() + collector.add(chunks) + + +embedder = SentenceTransformerEmbedder() diff --git a/extensions/superbooga/download_urls.py b/extensions/superbooga/download_urls.py new file mode 100644 index 0000000000000000000000000000000000000000..424a98857600dc54caf9cdea92b513d9116d6808 --- /dev/null +++ b/extensions/superbooga/download_urls.py @@ -0,0 +1,35 @@ +import concurrent.futures + +import requests + + +def download_single(url): + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' + } + response = requests.get(url, headers=headers, timeout=5) + if response.status_code == 200: + return response.content + else: + raise Exception("Failed to download URL") + + +def download_urls(urls, threads=1): + with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: + futures = [] + for url in urls: + future = executor.submit(download_single, url) + futures.append(future) + + results = [] + i = 0 + for future in concurrent.futures.as_completed(futures): + try: + result = future.result() + results.append(result) + i += 1 + yield f"{i}/{len(urls)}", results + except Exception: + pass + + yield "Done", results diff --git a/extensions/superbooga/requirements.txt b/extensions/superbooga/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..73a60078cfc523cde88d52d97bd44d099d751f7e --- /dev/null +++ b/extensions/superbooga/requirements.txt @@ -0,0 +1,6 @@ +beautifulsoup4==4.12.2 +chromadb==0.3.18 +pandas==2.0.3 +posthog==2.4.2 +sentence_transformers==2.2.2 +lxml diff --git a/extensions/superbooga/script.py b/extensions/superbooga/script.py new file mode 100644 index 0000000000000000000000000000000000000000..06fe8ad3d33c50fe7f5ad2d0123a0dde4695a291 --- /dev/null +++ b/extensions/superbooga/script.py @@ -0,0 +1,260 @@ +import re +import textwrap + +import gradio as gr +from bs4 import BeautifulSoup + +from modules import chat +from modules.logging_colors import logger + +from .chromadb import add_chunks_to_collector, make_collector +from .download_urls import download_urls + +params = { + 'chunk_count': 5, + 'chunk_count_initial': 10, + 'time_weight': 0, + 'chunk_length': 700, + 'chunk_separator': '', + 'strong_cleanup': False, + 'threads': 4, +} + +collector = make_collector() +chat_collector = make_collector() + + +def feed_data_into_collector(corpus, chunk_len, chunk_sep): + global collector + + # Defining variables + chunk_len = int(chunk_len) + chunk_sep = chunk_sep.replace(r'\n', '\n') + cumulative = '' + + # Breaking the data into chunks and adding those to the db + cumulative += "Breaking the input dataset...\n\n" + yield cumulative + if chunk_sep: + data_chunks = corpus.split(chunk_sep) + data_chunks = [[data_chunk[i:i + chunk_len] for i in range(0, len(data_chunk), chunk_len)] for data_chunk in data_chunks] + data_chunks = [x for y in data_chunks for x in y] + else: + data_chunks = [corpus[i:i + chunk_len] for i in range(0, len(corpus), chunk_len)] + + cumulative += f"{len(data_chunks)} chunks have been found.\n\nAdding the chunks to the database...\n\n" + yield cumulative + add_chunks_to_collector(data_chunks, collector) + cumulative += "Done." + yield cumulative + + +def feed_file_into_collector(file, chunk_len, chunk_sep): + yield 'Reading the input dataset...\n\n' + text = file.decode('utf-8') + for i in feed_data_into_collector(text, chunk_len, chunk_sep): + yield i + + +def feed_url_into_collector(urls, chunk_len, chunk_sep, strong_cleanup, threads): + all_text = '' + cumulative = '' + + urls = urls.strip().split('\n') + cumulative += f'Loading {len(urls)} URLs with {threads} threads...\n\n' + yield cumulative + for update, contents in download_urls(urls, threads=threads): + yield cumulative + update + + cumulative += 'Processing the HTML sources...' + yield cumulative + for content in contents: + soup = BeautifulSoup(content, features="lxml") + for script in soup(["script", "style"]): + script.extract() + + strings = soup.stripped_strings + if strong_cleanup: + strings = [s for s in strings if re.search("[A-Za-z] ", s)] + + text = '\n'.join([s.strip() for s in strings]) + all_text += text + + for i in feed_data_into_collector(all_text, chunk_len, chunk_sep): + yield i + + +def apply_settings(chunk_count, chunk_count_initial, time_weight): + global params + params['chunk_count'] = int(chunk_count) + params['chunk_count_initial'] = int(chunk_count_initial) + params['time_weight'] = time_weight + settings_to_display = {k: params[k] for k in params if k in ['chunk_count', 'chunk_count_initial', 'time_weight']} + yield f"The following settings are now active: {str(settings_to_display)}" + + +def custom_generate_chat_prompt(user_input, state, **kwargs): + global chat_collector + + # get history as being modified when using regenerate. + history = kwargs['history'] + + if state['mode'] == 'instruct': + results = collector.get_sorted(user_input, n_results=params['chunk_count']) + additional_context = '\nYour reply should be based on the context below:\n\n' + '\n'.join(results) + user_input += additional_context + else: + + def make_single_exchange(id_): + output = '' + output += f"{state['name1']}: {history['internal'][id_][0]}\n" + output += f"{state['name2']}: {history['internal'][id_][1]}\n" + return output + + if len(history['internal']) > params['chunk_count'] and user_input != '': + chunks = [] + hist_size = len(history['internal']) + for i in range(hist_size - 1): + chunks.append(make_single_exchange(i)) + + add_chunks_to_collector(chunks, chat_collector) + query = '\n'.join(history['internal'][-1] + [user_input]) + try: + best_ids = chat_collector.get_ids_sorted(query, n_results=params['chunk_count'], n_initial=params['chunk_count_initial'], time_weight=params['time_weight']) + additional_context = '\n' + for id_ in best_ids: + if history['internal'][id_][0] != '<|BEGIN-VISIBLE-CHAT|>': + additional_context += make_single_exchange(id_) + + logger.warning(f'Adding the following new context:\n{additional_context}') + state['context'] = state['context'].strip() + '\n' + additional_context + kwargs['history'] = { + 'internal': [history['internal'][i] for i in range(hist_size) if i not in best_ids], + 'visible': '' + } + except RuntimeError: + logger.error("Couldn't query the database, moving on...") + + return chat.generate_chat_prompt(user_input, state, **kwargs) + + +def remove_special_tokens(string): + pattern = r'(<\|begin-user-input\|>|<\|end-user-input\|>|<\|injection-point\|>)' + return re.sub(pattern, '', string) + + +def input_modifier(string, state, is_chat=False): + if is_chat: + return string + + # Find the user input + pattern = re.compile(r"<\|begin-user-input\|>(.*?)<\|end-user-input\|>", re.DOTALL) + match = re.search(pattern, string) + if match: + user_input = match.group(1).strip() + + # Get the most similar chunks + results = collector.get_sorted(user_input, n_results=params['chunk_count']) + + # Make the injection + string = string.replace('<|injection-point|>', '\n'.join(results)) + + return remove_special_tokens(string) + + +def ui(): + with gr.Accordion("Click for more information...", open=False): + gr.Markdown(textwrap.dedent(""" + + ## About + + This extension takes a dataset as input, breaks it into chunks, and adds the result to a local/offline Chroma database. + + The database is then queried during inference time to get the excerpts that are closest to your input. The idea is to create an arbitrarily large pseudo context. + + The core methodology was developed and contributed by kaiokendev, who is working on improvements to the method in this repository: https://github.com/kaiokendev/superbig + + ## Data input + + Start by entering some data in the interface below and then clicking on "Load data". + + Each time you load some new data, the old chunks are discarded. + + ## Chat mode + + #### Instruct + + On each turn, the chunks will be compared to your current input and the most relevant matches will be appended to the input in the following format: + + ``` + Consider the excerpts below as additional context: + ... + ``` + + The injection doesn't make it into the chat history. It is only used in the current generation. + + #### Regular chat + + The chunks from the external data sources are ignored, and the chroma database is built based on the chat history instead. The most relevant past exchanges relative to the present input are added to the context string. This way, the extension acts as a long term memory. + + ## Notebook/default modes + + Your question must be manually specified between `<|begin-user-input|>` and `<|end-user-input|>` tags, and the injection point must be specified with `<|injection-point|>`. + + The special tokens mentioned above (`<|begin-user-input|>`, `<|end-user-input|>`, and `<|injection-point|>`) are removed in the background before the text generation begins. + + Here is an example in Vicuna 1.1 format: + + ``` + A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions. + + USER: + + <|begin-user-input|> + What datasets are mentioned in the text below? + <|end-user-input|> + + <|injection-point|> + + ASSISTANT: + ``` + + ⚠️ For best results, make sure to remove the spaces and new line characters after `ASSISTANT:`. + + *This extension is currently experimental and under development.* + + """)) + + with gr.Row(): + with gr.Column(min_width=600): + with gr.Tab("Text input"): + data_input = gr.Textbox(lines=20, label='Input data') + update_data = gr.Button('Load data') + + with gr.Tab("URL input"): + url_input = gr.Textbox(lines=10, label='Input URLs', info='Enter one or more URLs separated by newline characters.') + strong_cleanup = gr.Checkbox(value=params['strong_cleanup'], label='Strong cleanup', info='Only keeps html elements that look like long-form text.') + threads = gr.Number(value=params['threads'], label='Threads', info='The number of threads to use while downloading the URLs.', precision=0) + update_url = gr.Button('Load data') + + with gr.Tab("File input"): + file_input = gr.File(label='Input file', type='binary') + update_file = gr.Button('Load data') + + with gr.Tab("Generation settings"): + chunk_count = gr.Number(value=params['chunk_count'], label='Chunk count', info='The number of closest-matching chunks to include in the prompt.') + gr.Markdown('Time weighting (optional, used in to make recently added chunks more likely to appear)') + time_weight = gr.Slider(0, 1, value=params['time_weight'], label='Time weight', info='Defines the strength of the time weighting. 0 = no time weighting.') + chunk_count_initial = gr.Number(value=params['chunk_count_initial'], label='Initial chunk count', info='The number of closest-matching chunks retrieved for time weight reordering in chat mode. This should be >= chunk count. -1 = All chunks are retrieved. Only used if time_weight > 0.') + + update_settings = gr.Button('Apply changes') + + chunk_len = gr.Number(value=params['chunk_length'], label='Chunk length', info='In characters, not tokens. This value is used when you click on "Load data".') + chunk_sep = gr.Textbox(value=params['chunk_separator'], label='Chunk separator', info='Used to manually split chunks. Manually split chunks longer than chunk length are split again. This value is used when you click on "Load data".') + with gr.Column(): + last_updated = gr.Markdown() + + update_data.click(feed_data_into_collector, [data_input, chunk_len, chunk_sep], last_updated, show_progress=False) + update_url.click(feed_url_into_collector, [url_input, chunk_len, chunk_sep, strong_cleanup, threads], last_updated, show_progress=False) + update_file.click(feed_file_into_collector, [file_input, chunk_len, chunk_sep], last_updated, show_progress=False) + update_settings.click(apply_settings, [chunk_count, chunk_count_initial, time_weight], last_updated, show_progress=False) diff --git a/extensions/superboogav2/README.md b/extensions/superboogav2/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d25b3a5eb96a92bcbc78e008b1ef7826bad43c3e --- /dev/null +++ b/extensions/superboogav2/README.md @@ -0,0 +1,5 @@ +# superboogav2 + +For a description, please see the comments in this Pull Request: + +https://github.com/oobabooga/text-generation-webui/pull/3272 diff --git a/extensions/superboogav2/api.py b/extensions/superboogav2/api.py new file mode 100644 index 0000000000000000000000000000000000000000..993e2b7d6d99bf3c5b8c79e82d54b592bdf76f21 --- /dev/null +++ b/extensions/superboogav2/api.py @@ -0,0 +1,207 @@ +""" +This module is responsible for the VectorDB API. It currently supports: +* DELETE api/v1/clear + - Clears the whole DB. +* POST api/v1/add + - Add some corpus to the DB. You can also specify metadata to be added alongside it. +* POST api/v1/delete + - Delete specific records with given metadata. +* POST api/v1/get + - Get results from chromaDB. +""" + +import json +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from urllib.parse import urlparse, parse_qs +from threading import Thread + +from modules import shared +from modules.logging_colors import logger + +from .chromadb import ChromaCollector +from .data_processor import process_and_add_to_collector + +import extensions.superboogav2.parameters as parameters + + +class CustomThreadingHTTPServer(ThreadingHTTPServer): + def __init__(self, server_address, RequestHandlerClass, collector: ChromaCollector, bind_and_activate=True): + self.collector = collector + super().__init__(server_address, RequestHandlerClass, bind_and_activate) + + def finish_request(self, request, client_address): + self.RequestHandlerClass(request, client_address, self, self.collector) + + +class Handler(BaseHTTPRequestHandler): + def __init__(self, request, client_address, server, collector: ChromaCollector): + self.collector = collector + super().__init__(request, client_address, server) + + + def _send_412_error(self, message): + self.send_response(412) + self.send_header("Content-type", "application/json") + self.end_headers() + response = json.dumps({"error": message}) + self.wfile.write(response.encode('utf-8')) + + + def _send_404_error(self): + self.send_response(404) + self.send_header("Content-type", "application/json") + self.end_headers() + response = json.dumps({"error": "Resource not found"}) + self.wfile.write(response.encode('utf-8')) + + + def _send_400_error(self, error_message: str): + self.send_response(400) + self.send_header("Content-type", "application/json") + self.end_headers() + response = json.dumps({"error": error_message}) + self.wfile.write(response.encode('utf-8')) + + + def _send_200_response(self, message: str): + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + + if isinstance(message, str): + response = json.dumps({"message": message}) + else: + response = json.dumps(message) + + self.wfile.write(response.encode('utf-8')) + + + def _handle_get(self, search_strings: list[str], n_results: int, max_token_count: int, sort_param: str): + if sort_param == parameters.SORT_DISTANCE: + results = self.collector.get_sorted_by_dist(search_strings, n_results, max_token_count) + elif sort_param == parameters.SORT_ID: + results = self.collector.get_sorted_by_id(search_strings, n_results, max_token_count) + else: # Default is dist + results = self.collector.get_sorted_by_dist(search_strings, n_results, max_token_count) + + return { + "results": results + } + + + def do_GET(self): + self._send_404_error() + + + def do_POST(self): + try: + content_length = int(self.headers['Content-Length']) + body = json.loads(self.rfile.read(content_length).decode('utf-8')) + + parsed_path = urlparse(self.path) + path = parsed_path.path + query_params = parse_qs(parsed_path.query) + + if path in ['/api/v1/add', '/api/add']: + corpus = body.get('corpus') + if corpus is None: + self._send_412_error("Missing parameter 'corpus'") + return + + clear_before_adding = body.get('clear_before_adding', False) + metadata = body.get('metadata') + process_and_add_to_collector(corpus, self.collector, clear_before_adding, metadata) + self._send_200_response("Data successfully added") + + elif path in ['/api/v1/delete', '/api/delete']: + metadata = body.get('metadata') + if corpus is None: + self._send_412_error("Missing parameter 'metadata'") + return + + self.collector.delete(ids_to_delete=None, where=metadata) + self._send_200_response("Data successfully deleted") + + elif path in ['/api/v1/get', '/api/get']: + search_strings = body.get('search_strings') + if search_strings is None: + self._send_412_error("Missing parameter 'search_strings'") + return + + n_results = body.get('n_results') + if n_results is None: + n_results = parameters.get_chunk_count() + + max_token_count = body.get('max_token_count') + if max_token_count is None: + max_token_count = parameters.get_max_token_count() + + sort_param = query_params.get('sort', ['distance'])[0] + + results = self._handle_get(search_strings, n_results, max_token_count, sort_param) + self._send_200_response(results) + + else: + self._send_404_error() + except Exception as e: + self._send_400_error(str(e)) + + + def do_DELETE(self): + try: + parsed_path = urlparse(self.path) + path = parsed_path.path + query_params = parse_qs(parsed_path.query) + + if path in ['/api/v1/clear', '/api/clear']: + self.collector.clear() + self._send_200_response("Data successfully cleared") + else: + self._send_404_error() + except Exception as e: + self._send_400_error(str(e)) + + + def do_OPTIONS(self): + self.send_response(200) + self.end_headers() + + + def end_headers(self): + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', '*') + self.send_header('Access-Control-Allow-Headers', '*') + self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate') + super().end_headers() + + +class APIManager: + def __init__(self, collector: ChromaCollector): + self.server = None + self.collector = collector + self.is_running = False + + def start_server(self, port: int): + if self.server is not None: + print("Server already running.") + return + + address = '0.0.0.0' if shared.args.listen else '127.0.0.1' + self.server = CustomThreadingHTTPServer((address, port), Handler, self.collector) + + logger.info(f'Starting chromaDB API at http://{address}:{port}/api') + + Thread(target=self.server.serve_forever, daemon=True).start() + + self.is_running = True + + def stop_server(self): + if self.server is not None: + logger.info(f'Stopping chromaDB API.') + self.server.shutdown() + self.server.server_close() + self.server = None + self.is_running = False + + def is_server_running(self): + return self.is_running \ No newline at end of file diff --git a/extensions/superboogav2/benchmark.py b/extensions/superboogav2/benchmark.py new file mode 100644 index 0000000000000000000000000000000000000000..46475a088b0eca137f641935d58dbf4b8d50ed29 --- /dev/null +++ b/extensions/superboogav2/benchmark.py @@ -0,0 +1,72 @@ +""" +This module implements a benchmark function to evaluate the performance of the embedding pipeline. It expects a configuration JSON file. It must have questions and expected retrieved text. +For each question, it's essential to have variants of that question. Language is fluid and each person might have their own spin on how they may ask it. + +At the end, it will save the results inside a benchmark_{sysdate}.txt file in the main directory. + +The benchmark function will return the score as an integer. +""" +import datetime +import json +import os + +from pathlib import Path + +from .data_processor import process_and_add_to_collector, preprocess_text +from .parameters import get_chunk_count, get_max_token_count +from .utils import create_metadata_source + +def benchmark(config_path, collector): + # Get the current system date + sysdate = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"benchmark_{sysdate}.txt" + + # Open the log file in append mode + with open(filename, 'a') as log: + with open(config_path, 'r') as f: + data = json.load(f) + + total_points = 0 + max_points = 0 + + for item in data: + filepath = item["text"] + corpus = "" + + # Check if the file exists + if os.path.isfile(Path(filepath)): + # Open the file and read its content + with open(Path(filepath), 'r') as file: + corpus = file.read() + process_and_add_to_collector(corpus, collector, True, create_metadata_source('benchmark')) + else: + raise f'Cannot find specified file {filepath}.' + + for question_group in item["questions"]: + question_variants = question_group["question_variants"] + criteria = question_group["criteria"] + + for q in question_variants: + max_points += len(criteria) + processed_text = preprocess_text(q) + + # Get the most similar chunks + results = collector.get_sorted_by_dist(processed_text, n_results=get_chunk_count(), max_token_count=get_max_token_count()) + + points = 0 + + for c in criteria: + for p in results: + if c in p: + points += 1 + total_points += 1 + break + + info = f"The question '{q}' scored {points}/{len(criteria)} points." + print(info, file=log) + + print('\n---\n', file=log) + + print(f'##Total points:\n\n{total_points}/{max_points}', file=log) + + return total_points, max_points \ No newline at end of file diff --git a/extensions/superboogav2/benchmark_texts/aircraft_lease.txt b/extensions/superboogav2/benchmark_texts/aircraft_lease.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d7e35b02e58d07f9c379aaf5175c909598f77ec --- /dev/null +++ b/extensions/superboogav2/benchmark_texts/aircraft_lease.txt @@ -0,0 +1,9471 @@ + +EX-10.72 +55 +a2069410zex-10_72.txt +EXHIBIT 10.72 + + + + Exhibit 10.72 + + COUNTERPART NO. 2 + + +=============================================================================== + + + AIRCRAFT LEASE AGREEMENT + dated as of November 29, 1995 + + between + + WILMINGTON TRUST COMPANY, not in its + individual capacity but solely as Trustee + f/b/o ITOCHU AirLease (Europe) Limited and + Marubeni Airleasing (U.K.) Limited, + + Lessor + + and + + ALOHA AIRLINES, INC., + + Lessee + + One Boeing 737-25A Aircraft + +=============================================================================== + + THIS LEASE HAS BEEN EXECUTED IN MULTIPLE COUNTERPARTS, OF WHICH THERE IS ONE + COUNTERPART DESIGNATED AS "COUNTERPART NO. 1 - CHATTEL PAPER ORIGINAL" AND ALL + OTHER COUNTERPARTS SHALL BE DESIGNATED WITH NUMBERS OTHER THAN 1. TO THE EXTENT + THAT THIS LEASE CONSTITUTES CHATTEL PAPER UNDER THE UNIFORM COMMERCIAL CODE, AS + IN EFFECT IN ANY APPLICABLE JURISDICTION, NO SECURITY INTEREST MAY BE CREATED + OR PERFECTED BY POSSESSION OF ANY COUNTERPART OF THIS LEASE OTHER THAN SAID + COUNTERPART NO. 1. + + + + + + COUNTERPART NO. 7 + + + + +================================================================================ + + + AIRCRAFT LEASE AGREEMENT + dated as of November 29, 1995 + + + between + + WILMINGTON TRUST COMPANY, not in its + individual capacity but solely as Trustee + f/b/o ITOCHU AirLease (Europe) Limited and + Marubeni Airleasing (U.K.) Limited, + + Lessor + + and + + ALOHA AIRLINES, INC., + + Lessee + + + One Boeing 737-25A Aircraft + +================================================================================ + + + THIS LEASE HAS BEEN EXECUTED IN MULTIPLE COUNTERPARTS, OF WHICH THERE IS ONE + COUNTERPART DESIGNATED AS "COUNTERPART NO. 1 - CHATTEL PAPER ORIGINAL" AND ALL + OTHER COUNTERPARTS SHALL BE DESIGNATED WITH NUMBERS OTHER THAN 1. TO THE EXTENT + THAT THIS LEASE CONSTITUTES CHATTEL PAPER UNDER THE UNIFORM COMMERCIAL CODE, AS + IN EFFECT IN ANY APPLICABLE JURISDICTION, NO SECURITY INTEREST MAY BE CREATED + OR PERFECTED BY POSSESSION OF ANY COUNTERPART OF THIS LEASE OTHER THAN SAID + COUNTERPART NO. 1. + + + + + Table of Contents + ----------------- + +
+ Page + ---- + + Section 1. Definitions . . . . . . . . . . . . . . . 1 + + Section 2. Lease and Conditions . . . . . . . . . . . 9 + + Section 3. Delivery and Acceptance; Term . . . . . . 14 + + Section 4. Rent . . . . . . . . . . . . . . . . . . 15 + + Section S. Representations and Warranties . . . . . 17 + + Section 6. Possession and Use; Registration . . . . 24 + + Section 7. Information . . . . . . . . . . . . . . . 33 + + Section 8. Covenants of Lessee . . . . . . . . . . . 35 + + Section 9. Replacement of Parts; Alterations, + Modifications and Additions . . . . . . . 37 + + Section 10. General Tax Indemnity . . . . . . . . . . 40 + + Section 11. Casualty Occurrences . . . . . . . . . . . 47 + + Section 12. Insurance . . . . . . . . . . . . . . . . 51 + + Section 13. Indemnification . . . . . . . . . . . . . . 56 + + Section 14. Liens . . . . . . . . . . . . . . . . . . 58 + + Section 15. Perfection of Title and Further + Assurances . . . . . . . . . . . . . . . 59 + + Section 16. Return of Aircraft and Records . . . . . . 60 + + Section 17. Events of Default . . . . . . . . . . . . 67 + + Section 18. Remedies . . . . . . . . . . . . . . . . 69 + + Section 19. Alienation . . . . . . . . . . . . . . . 73 + + Section 20. Miscellaneous . . . . . . . . . . . . . . 75 + + Section 21. Security Deposit . . . . . . . . . . . . . 81 +
+ + -i- + + + + + + EXHIBITS + + Exhibit A - Schedule and Description of Aircraft + + Exhibit B-1 - Aircraft Documents on the Delivery Date + + Exhibit B-2 - Aircraft Documents on the Return Occasion + + Exhibit C-1 - Financial Information + + Exhibit C-2 - Definitions and Values + + Exhibit D - Lease Supplement + + Exhibit E - Return Condition Requirements + + Exhibit F - Lessee's Counsel Opinion + + Exhibit G - Approved Insurance Broker Report + + Exhibit H - Insurance Special Provisions + + Exhibit I - Form of Redelivery Acknowledgment + + EXHIBIT J - Monthly Aircraft Utilization and + Status Report + + EXHIBIT K - List of Documents to Be Supplied Annually + + + + -ii- + + + + + RECORDED + + Federal Aviation Administration + + Date 1-25-96 Time 3:38 PM + ---------- ---------- + Conveyance Number II004730 + ------------ + By /s/ Donna Jennings + ------------------------------- + Conveyances Examiner + + + AIRCRAFT LEASE AGREEMENT + + + THIS AIRCRAFT LEASE AGREEMENT, dated as of November 29, 1995 + (the "Lease") , between (1) WILMINGTON TRUST COMPANY, not in its individual + capacity but solely as Trustee f/b/o ITOCHU AirLease (Europe) Limited and + Marubeni Airleasing (U.K.) Limited (Wilmington Trust Company, in such capacity, + is herein called "Lessor") , and (2) Aloha Airlines, Inc., a corporation + organized under the laws of Hawaii (herein called "Lessee"). + + WHEREAS, Lessee desires to lease from Lessor and Lessor is + willing to lease to Lessee the aircraft described herein upon and subject to + the terms and conditions of this Lease; + + NOW, THEREFORE, In consideration of the mutual promises, + covenants, terms and conditions contained herein, Lessor and Lessee agree as + follows: + + Section 1. DEFINITIONS. + + The following terms shall have the following respective +meanings for all purposes of this Lease Agreement (such meanings to be equally +applicable to both the singular and plural forms of the terms defined): + + AFFILIATE shall mean, with respect to any Person, any other + Person directly or indirectly controlling, controlled by or under common + control with such Person. + + AIRCRAFT shall mean the Airframe together with (i) the + Engines, whether or not installed on the Aircraft, (ii) all Parts, (iii) spare + parts or ancillary equipment or devices' furnished with the Aircraft under this + Lease, (iv) all Aircraft Documents, and (v) all substitutions, replacements and + renewals of any and all thereof. + + AIRCRAFT DOCUMENTS shall mean, as applicable, the items + identified on Exhibit B-1 hereto provided to Lessee on the Delivery Date and + Exhibit B-2 hereto provided to Lessor on the Return Occasion, all of which + shall be maintained in the English language. + + AIRCRAFT MAINTENANCE MANUAL shall mean the aircraft + maintenance manual which constitutes part of the Maintenance Program. + + + + + + AIRFRAME shall mean (a) the aircraft described on Exhibit A, + but not including any Engine installed thereon, and (b) any and all Parts so + long as the same shall be incorporated or installed on or attached to the + Airframe, or so long as title thereto shall remain vested in Lessor in + accordance with the terms of Section 9, after removal from the Airframe. + + APPROVED INSURANCE BROKER shall mean any reputable insurance + broker of internationally recognized responsibility and standing specializing + in aircraft insurance. + + APPROVED INSURER shall mean any reputable insurance company or + insurance broker of internationally recognized responsibility and standing + specializing in aircraft insurance. + + APU shall mean the auxiliary power unit. + + BASIC RENT shall mean the rent for the Aircraft specified on + Exhibit C-1 and payable throughout the Term for the Aircraft pursuant to + Section 4(a). + + BASIC RENT PAYMENT DATE shall have the meaning set forth in +Exhibit C-1 hereto. + + BUSINESS DAY shall mean any day other than a Saturday, Sunday + or other day on which banking institutions in Delaware, Hawaii, New York, + London or Dublin are authorized or required by law to be closed. + + C CHECK is defined in the Maintenance Program and equivalent + to a "C" Check as defined in Boeing's "B737-200 Maintenance Planning Data" Doc. + D6-17594. A "C" Check is currently performed at 3,000 Flight Hour intervals. + + CASUALTY OCCURRENCE shall mean any of the following events + with respect to the Aircraft, Airframe or any Engine: (a) loss of such property + or its use due to theft or disappearance for a period in excess of sixty (60) + consecutive days, or any destruction, damage beyond economic repair or + rendition of such property permanently unfit for normal use by Lessee for any + reason whatsoever; (b) any damage to such property which results in an + insurance settlement with respect to such property on the basis of a total loss + or on the basis of a compromised or constructive total loss, (c) the + condemnation, confiscation, appropriation or seizure of title, or requisition + of title to such property; (d) the appropriation or seizure of the use of such + property by or on the authority of any Governmental Entity or purported + Governmental Entity, which in any such case shall have resulted in the loss of + possession thereof + + + -2- + + + + + + by Lessee for a period in excess of one hundred eighty (180) consecutive days + (or for such shorter period ending on the date which is seven (7) days from the + date of receipt of an insurance settlement with respect to such property on the + basis of a total loss); or (e) prohibition under any applicable Law of the use + of the Aircraft for commercial transportation of passengers for a period of six + (6) months. + + CASUALTY VALUE shall mean the value as specified on Exhibit + C-1 hereto. + + CERTIFIED AIR CARRIER shall mean a United States "air carrier" + within the meaning of Section 40102(a)(2) of the Federal Aviation Act engaged + in regularly scheduled air transportation of passengers who is a "citizen of + the United States" within the meaning of Section 40102(a)(15) of the Federal + Aviation Act holding a certificate of public convenience and necessity issued + by the FAA pursuant to the Federal Aviation Act or by a similar authority under + legislation hereafter enacted. + + CLAIMS shall have the meaning set forth in Section 13. + + CODE shall mean the United States Internal Revenue Code of + 1986, as amended, and any successor thereto, as interpreted by the rules and + regulations issued thereunder, in each case as in effect from time to time. + + COMMONLY CONTROLLED PERSON shall mean any trade or business, + whether or not incorporated, which is at any relevant time a member of the same + controlled group of corporations (within the meaning of Section 414(b) of the + Code) or affiliated service group (within the meaning of Section 414(m) or (o) + of the Code) as, or under common control (within the meaning of Section 414(c) + of the Code) with, Lessee. + + CYCLE shall mean a cycle commencing each time the wheels of + the Airframe (or any other airframe on which an Engine or Part is installed, + but only insofar as the calculation of Cycles is required with reference to the + relevant Engine or Part) leave the ground on take-off and finishing when the + wheels of such Airframe (or any other airframe on which such Engine or Part is + installed) touch the ground in landing. + + D CHECK is defined in the Maintenance Program and is + equivalent to a "D Check" or "heavy structural inspection" as defined in + "Boeing's B737-200 Maintenance Planning Data" Doc. D6-17594. A "D" Check is + currently performed at 12,000 Flight Hour intervals. + + + -3- + + + + + + DEFAULT shall mean an Event of Default or event which would + constitute an Event of Default but for the lapse of time or the giving of + notice or both. + + DELIVERY DATE shall mean the date on which the Aircraft is + delivered to and accepted by Lessee for purposes of this Lease. + + DELIVERY LOCATION shall mean the location specified on Exhibit + C-2 hereto for the delivery of the Aircraft by Lessor to Lessee. + + DETAILED SPECIFICATIONS means the detailed specifications + number D6-32410-181 Revision C dated December 1, 1987 relating to the Aircraft + to be provided to Lessor on or before the Effective Date. + + DOLLARS shall mean lawful currency of the United States of +America. + + ENGINE shall mean any engine installed on or furnished with + the Aircraft on the Delivery Date, such engine being identified as to + manufacturer and type on Exhibit A hereto and which will be further identified + as to manufacturer serial number in the Lease Supplement, and any Replacement + Engine which may from time to time be substituted therefor pursuant to Section + 11; together in each case with any and all Parts incorporated or installed in + or attached thereto or any and all Parts removed therefrom so long as title + thereto remains vested in Lessor in accordance with the terms of Section 9 + after removal from such engine. Except as otherwise set forth herein, at such + time as a Replacement Engine shall be so substituted, such replaced engine + shall cease to be an Engine hereunder. The term "Engines" means, as of any date + of determination, all Engines then leased hereunder. + + EQUIPMENT shall mean the Airframe, each Engine and the +Aircraft Documents. + + ERISA shall mean the Employee Retirement Income Security Act + of 1974, as amended, and any successor statute thereto, as interpreted by the + rules and regulations thereunder, all as the same may be in effect from time to + time. + + ESTIMATED DELIVERY DATE shall mean the date specified on + Exhibit C-2 hereto, which the. parties anticipate to be the Delivery Date. + + EVENT OF DEFAULT shall have the meaning specified in any one +or more clauses in Section 17. + + + -4- + + + + + + EXPIRATION DATE shall mean the date specified in the Lease +Supplement for expiration of the Term. + + FAA shall mean the Federal Aviation Administration of the + United States Department of Transportation or any successor. + + FEDERAL AVIATION ACT shall mean Subtitle VII, Part A of Title + 49 of the United States Code, as amended. + + FINAL INSPECTION shall have the meaning set forth in Section +16(b). + + FLIGHT HOUR shall mean each hour and any fractional portion + of an hour which elapses from the time the wheels of the Airframe (or any other + airframe on which an Engine or Part is installed, but only insofar as the + calculation of Flight Hours is required with reference to the relevant Engine + or Part) leave the ground on take-off to the time when the wheels of such + Airframe (or any other airframe on which such Engine or Part is installed) + touch the ground on landing. + + GOVERNMENTAL ENTITY shall mean any federal, state, municipal +or other governmental department, commission, board, bureau, court, legislature, +agency, instrumentality or authority, domestic or foreign. + + INDEMNITEE shall mean Wilmington Trust Company in its + individual capacity, Lessor, each Beneficiary, each Lender and, in each case + their respective Affiliates, officers, directors, shareholders, agents and + employees and their respective successors and assigns. + + ITEM means the Aircraft or Airframe or any Engine in each + case including Parts constituting a part of any thereof. + + LAW shall mean and include (i) any statute, decree, + constitution, regulation, order, judgment or other directive of any + Governmental Entity; (ii) any treaty, pact, compact or other agreement to which + any Governmental Entity is a signatory or party; (iii) any judicial or + administrative interpretation or application of any Law described in (i) or + (ii) above; and (iv) any amendment or revision of any Law described in (i), + (ii) or (iii) above. + + LEASE SUPPLEMENT shall mean the Lease Supplement, + substantially in the form of Exhibit D hereto, entered into between Lessor and + Lessee. + + LENDER shall mean each Person who now or hereafter makes a +loan to the Lessor or any Beneficiary or otherwise + + + -5- + + + + + + finances the Beneficiaries' interest in the Aircraft and for whose benefit + Lessor grants a security interest in the Aircraft and the Lease. + + LESSOR'S LIENS shall mean Liens arising as a result of (i) + claims against Lessor or any Beneficiary not related to the transactions + contemplated by this Lease; or (ii) acts of Lessor or any Beneficiary, not + contemplated and expressly permitted under this Lease; or (iii) Taxes imposed + against Lessor which are not indemnified against by Lessee pursuant to Section + 10; or (iv) claims against Lessor arising out of the voluntary transfer by + Lessor of all or any part of its interests in the Aircraft or this Lease, other + than a transfer pursuant to Section 11 or 18 hereof. + + LIBOR shall mean the offered rate (rounded up to the nearest + 1/32 of one percent) for deposits in Dollars for six-month periods which appear + on the Telerate Page 3750 as of 11:00 a.m., London time, on the day that is two + (2) Business Days preceding that date for which LIBOR is being determined. If + at least two (2) such offered rates appear on the Telerate Page 3750, the rate + will be the arithmetic mean of such offered rates (rounded up to the nearest + 1/32 of one percent). + + LIEN shall mean any mortgage, pledge, lien, charge, + encumbrance, lease, exercise of rights, security interest or claim. + + MAINTENANCE PROGRAM shall mean an FAA approved maintenance + program as in effect from time to time for the Aircraft encompassing scheduled + maintenance, condition monitored maintenance and on-condition maintenance of + the Airframe, Engines and components of the Aircraft in accordance with the + Manufacturer's maintenance planning document. Upon request by Lessor or any + Beneficiary a copy of such Maintenance Program shall be made available to + Lessor or such Beneficiary. + + MORTGAGE CONVENTION shall mean the Convention for the + International Recognition of Rights in Aircraft, signed (ad referendum) at + Geneva, Switzerland, on June 19, 1948, and amended from time to time, but + excluding the terms of any adhesion thereto or ratification thereof containing + reservations to which the United States of America does not accede. + + OPERATIVE DOCUMENTS shall mean this Lease, the Lease + Supplement, the Trust Agreement and the Supplemental Agreement. + + PARTS shall mean all appliances, components, parts, +instruments, appurtenances, accessories, furnishings + + + -6- + + + + + + and other equipment of whatever nature (other than complete Engines or + engines), which may now or from time to time be incorporated or installed in or + attached to the Airframe or any Engine. Except as otherwise set forth herein, + at such time as a replacement part shall be substituted for a Part in + accordance with Section 9 hereof, the Part so removed shall cease to be a Part + hereunder. + + PBGC shall mean the Pension Benefit Guaranty Corporation + established pursuant to Subtitle A of Part IV of ERISA, and any successor + thereof. + + PERSON shall mean and include any individual person, + corporation, partnership, limited liability company, firm, joint stock company, + joint venture, trust, estate, unincorporated organization, association or + Governmental Entity. + + PLAN shall mean any employee pension benefit plan (as defined + in Section 3(2) of ERISA) maintained or contributed to at any time, or required + to be contributed to, by Lessee or any Commonly Controlled Person. + + REMOVABLE PART shall have the meaning set forth in Section + 9(b). + + RENT shall mean Basic Rent and Supplemental Rent, +collectively. + + REPLACEMENT ENGINE shall mean an engine of the same + manufacturer and model and having not less than equivalent value, utility, + modification and remaining warranty status as the Engine it is intended to + replace under Section 11(b) hereof, or, at Lessee's option, an engine of the + same manufacturer as such Engine but of an improved model, and otherwise of not + less than equivalent value and utility and suitable for installation and use on + the Airframe without impairing the value or utility of the Airframe. + + RETURN OCCASION shall mean the event that occurs when + possession of the Aircraft is returned from Lessee to Lessor at the end of the + Term of this Lease or upon Lessor taking possession pursuant to Section 18. + + SUPPLEMENTAL RENT shall mean any and all amounts, liabilities + and obligations (other than Basic Rent) which Lessee assumes or agrees to pay + hereunder, including without limitation (i) any payment of Casualty Value; (ii) + any payment of indemnity required by Sections 10 or 13 hereof; and (iii) to the + extent permitted by applicable Law, interest at the Overdue Rate (all + computations of interest at the Overdue Rate under this Lease to be made on the + basis + + + -7- + + + + + + of a 360-day year for the actual number of days elapsed) calculated on any + Rent not paid when due hereunder until the same is paid. + + TAXES shall mean any and all sales, use, business, gross + income, personal property, transfer, fuel, leasing, occupational, value added, + excess profits, excise, gross receipts, franchise, stamp, income, levies, + imposts, withholdings or other taxes or duties of any nature whatsoever now or + hereafter imposed, levied, collected, withheld or assessed, together with any + penalties, fines, charges or interest thereon. + + TELERATE PAGE 3750 shall mean the display page so designed on + the Dow Jones Telerate Service (or such other page as may replace that page on + that service, or such other service as may be nominated as the information + vendor, for the purpose of displaying comparable rates). + + TERM shall mean the term of this Lease specified in the Lease +Supplement hereto. + + WET LEASE shall mean any arrangement whereby Lessee agrees to + operate the Aircraft under a contractual arrangement with a third party + pursuant to which no rights to any aircraft specifically identified by serial + number or registration number are granted to such third party and pursuant to + which the Aircraft (i) remains at all times under the sole and complete + operational control of Lessee, (ii) shall be operated solely by cockpit crew + employed by Lessee possessing all current certificates and licenses required by + applicable Laws and (iii) shall be maintained by Lessee in accordance with all + the provisions of the Lease including, but not limited to, Sections 6(d) and 12 + (it being understood and agreed by Lessor and Lessee that any Wet Lease shall, + by its terms, be in all cases subject and subordinate to this Lease). + + The terms SECURITY DEPOSIT, SECURITY DEPOSIT ADIUSTMENT and + SECURITY LETTER OF CREDIT shall have the meanings set forth in Exhibit C-1 + hereto. + + The terms ASSIGNMENT AGREEMENT, ASSIGNMENT AGREEMENT N685MA, + BENEFICIARY, BENEFICIARY'S ADDRESS, DEDUCTIBLE AMOUNT, DELIVERY LOCATION, + ENGINE MANUFACTURER, ESTIMATED DELIVERY DATE, LAST BASIC RENT PAYMENT DATE, + LEASE IDENTIFICATION, LESSEE'S ADDRESS, LESSOR'S ADDRESS, MANUFACTURER, OTHER + LEASE, OVERDUE RATE, PAYMENT LOCATIONS, PERMITTED JURISDICTION, PERMITTED + SUBLESSEES, PUBLIC LIABILITY AND PROPERTY DAMAGE INSURANCE, RETURN LOCATION, + SUPPLEMENTAL AGREEMENT, TRUST AGREEMENT and TRUSTEE shall have the meanings set + forth on Exhibit C-2 hereto. + + + -8- + + + + + + Section 2. LEASE AND CONDITIONS. + + (a) Lessor hereby agrees to lease the Aircraft to Lessee, and + Lessee hereby agrees to lease the Aircraft from Lessor, on the terms and + conditions of this Lease, as supplemented by the Lease Supplement. + + (b) Lessor's obligation to lease the Aircraft hereunder to + Lessee shall be subject to the satisfaction of the following conditions + precedent, in each case subject to the reasonable satisfaction of Lessor and + each Beneficiary: + + (i) the absence of any Default hereunder; + + (ii) the absence of any material adverse change in + Lessee's financial condition or in Lessee's ability to carry on its + business from September 30, 1995 to the Delivery Date; + + (iii) the absence of any default or event of default by + Lessee under any aircraft lease or obligation for the payment of borrowed + money to which the Lessee is a party; + + (iv) no action or proceeding shall be pending nor shall + any governmental action be threatened before any court or governmental + agency of competent jurisdiction, nor shall any order, judgment or decree + have been issued or proposed to be issued by any court or governmental + agency, to set aside, restrain, enjoin or prevent the completion and + consummation of this Lease or any other Operative Documents or the + transactions contemplated hereby or thereby; + + (v) receipt of every consent, approval or authorization + of, and the giving of every notice to, any third party that is required in + order to execute and deliver this Lease and to perform the transactions + contemplated hereby and each such consent, approval or authorization shall + be valid and effective and shall not have been revoked; + + (vi) receipt of the approval of the Board of Directors of + each Beneficiary and any other Affiliate of each Beneficiary as such + Beneficiary, in its sole discretion, deems necessary; + + (vii) receipt by Lessor and each Beneficiary of the + following documents on or before the Delivery Date, all of which shall be + reasonably satisfactory in form and substance to Lessor and each + Beneficiary: + + + -9- + + + + + (1) a Lease Supplement in the form of Exhibit D, duly + authorized, executed and delivered by Lessee, and effective as of the + Delivery Date of the Aircraft; + + (2) a certified copy of the resolutions of the Board + of Directors of Lessee evidencing due authority of Lessee for the + execution, delivery and performance of this Lease, the Lease + Supplement and all other documents related hereto or thereto, + together with an incumbency certificate as to the person or persons + authorized to execute and deliver such documents on behalf of Lessee; + + (3) a favorable opinion of Lessee's counsel, dated + the Delivery Date, substantially in the form as set forth on Exhibit + F hereto and of special aviation counsel, dated the Delivery Date, in + form and scope acceptable to Lessor and the Beneficiaries; + + (4) a certificate signed by a duly authorized officer + of Lessee, dated the Delivery Date, stating that: (i) the + representations and warranties contained' in Section 5(d) hereof are + true and accurate on and as of such date as though made on and as of + such time; (ii) no event has occurred and is continuing, or would + result from the lease of the Aircraft, which constitutes a Default; + and (iii) since September 30, 1995 there has not occurred any + material adverse change in Lessee's financial condition or in + Lessee's ability to carry on its business; + + (5) an opinion or report, dated the Delivery Date, + signed by an Approved Insurance Broker or Approved Insurer as to the + due compliance with the insurance provisions of Section 12 hereof + with respect to the Aircraft, substantially in the form as set forth + on Exhibit G hereto; + + (6) certificates of an Approved Insurance Broker + evidencing the insurance as required by Section 12 hereof + + + -10- + + + + + + together with such special provisions as set forth on Exhibit H + hereto; + + (7) certified copies of all necessary consents from + any Governmental Entities or such other Persons as to the + transactions contemplated by this Lease; + + (8) such financial information concerning Lessee and + other documents and matters incidental to any of the foregoing as + Lessor or any Beneficiary may reasonably request; and + + (9) a duly executed original of the Security Letter + of Credit. + + (vii) the Lease, the Lease Supplement, an application for + registration of the Aircraft on AC Form 8050-1 in the name of the + Trustee, the Trust Agreement, and the affidavit of citizenship of the + Trustee shall have been duly filed for recordation (or shall be in the + process of being so fully filed for recordation) with the FAA pursuant to + the Federal Aviation Act and precautionary UCC filings with respect to + the Lease shall have been duly executed and delivered and shall have been + duly filed in Hawaii and all places in which such filings are necessary + or advisable to establish and perfect Trustee's right, title and interest + in the Equipment. + + (c) Lessee's obligation to lease the Aircraft hereunder from + Lessor shall be conditional upon (i) the receipt by Lessee of a certified copy + of the resolutions of the Board of Directors of Lessor evidencing due authority + of Lessor for the execution, delivery and performance of the Trust Agreement + and this Lease and all other documents related thereto, together with an + incumbency certificate as to the person or persons authorized to execute and + deliver such documents on behalf of Lessor, (ii) the receipt by Lessee of + certification by Lessor to the effect that Lessor's representations and + warranties herein are true as at the Delivery Date, and (iii) the continued + effectiveness of the registration of the Aircraft with the FAA. + + (d) Lessee's obligation to lease the Aircraft hereunder from +Lessor shall also be conditioned upon the Aircraft being delivered to Lessee in +the following condition: + + (1) The Aircraft shall be airworthy and in good + operating condition + + + -11- + + + + + with all of the Aircraft equipment, components and systems; + + (2) The Aircraft shall be clean; + + (3) The Aircraft shall meet the requirements for + airworthiness certification by the FAA; + + (4) A borescope of the Engines and the inspection of + the APU in accordance with the Manufacturer's or APU manufacturer's + recommendation shall have been performed at the direction of Lessee + but under the control and at the cost of the Beneficiaries, and any + discrepancies discovered in connection therewith shall have been + corrected; + + (5) Lessor shall, in cooperation with the + Beneficiaries, retain a flight crew at competitive market rates to + conduct a functional check flight, not to exceed two hours (the + "Acceptance Flight"), to demonstrate the airworthiness of the + Aircraft and proper functioning of all systems and components. At + all times during such Acceptance Flight, the flight crew shall be + under the direction of the Beneficiaries and the Beneficiaries shall + be in command of the Aircraft. The Acceptance Flight shall be + conducted after the conclusion of Lessee's ground inspection and + technical acceptance of the Aircraft on the Delivery Date, PROVIDED, + HOWEVER, that such technical inspection shall be subject to the + satisfactory completion of the Acceptance Flight, as provided below. + If, during the Acceptance Flight, no discrepancy or malfunction is + detected with respect to the airworthiness or operational nature of + the Aircraft by normal airline standards, then (i) the delivery of + the Aircraft from Lessor to Lessee hereunder shall occur, the + Aircraft shall be accepted by Lessee hereunder whilst the Aircraft + is located at the Delivery Location, and Lessee shall execute and + deliver the Lease Supplement, (ii) the Aircraft shall continue en + route to a location selected by Lessee (the "Ferry Location"), under + the operational control of Lessee from the time of such delivery and + acceptance (the Acceptance Flight shall terminate at the time of + such delivery and acceptance, and that portion of the flight from + the Delivery Location to the Ferry Location is herein called the + "Ferry Flight"), and (iii) Lessee shall bear the costs of the flight + crew, fuel and other costs + + + -12- + + + + + + relating to the Ferry Flight and shall reimburse the Beneficiaries + therefor promptly following receipt of the Beneficiaries, invoice + therefor, PROVIDED, HOWEVER, that, if any discrepancy or malfunction + is detected with respect to the airworthiness during the Acceptance + Flight, then, at Lessee's option after consultation with Lessor, + either (A) the Aircraft shall be delivered to and accepted by Lessee + at the Delivery Location and shall be ferried to the Ferry Location, + as provided in clauses (i), (ii) and (iii) above, where Lessee shall + remedy such discrepancy or malfunction at the cost (without mark up) + of the Beneficiaries (provided that such subsequent repair or + maintenance work shall not affect Lessee's acceptance of the + Aircraft hereunder), or (B) the Aircraft shall return to the + Beneficiaries' storage and maintenance facility where such + discrepancy or malfunction shall be corrected at the Beneficiaries' + expense, in which case the Delivery Date shall be postponed to such + date as the Beneficiaries shall advise, subject to the second + paragraph of Section 3(c) below. Any discrepancy or malfunction + detected of an airworthiness nature shall be corrected by Lessee or + the Beneficiaries in accordance with clause (A) or (B) above, as + applicable, at Beneficiaries, expense. If during the Acceptance + Flight a discrepancy or malfunction is detected with respect to the + operational nature of the Aircraft by normal airline standards but + no discrepancy or malfunction is detected with respect to the + airworthiness of the Aircraft, then the Aircraft shall be delivered + to and accepted by Lessee at the Delivery Location as provided in + clause (A) above, and Lessee shall remedy such discrepancy or + malfunction at the cost (without mark up) of the Beneficiaries. + + In anticipation of the occurrence of the Delivery Date, the + Beneficiaries retained a flight crew to conduct the Acceptance + Flight and will incur costs relating to such retention. In + connection therewith, Lessee agrees to reimburse the Beneficiaries, + promptly following Lessee's receipt of an invoice therefor, for + one-half of the costs incurred by the Beneficiaries in connection + with retaining such flight crew for the Acceptance Flight, which + costs relate to the period commencing on and including November 29, + 1995 and ending on the day immediately preceding the Delivery Date. + + + -13- + + + + + + + Section 3. Delivery and Acceptance; Term. + + (a) DELIVERY. It is anticipated that the Aircraft will become + available for delivery on or about the Estimated Delivery Date set forth on + Exhibit C-2. + + (b) PLACE OF DELIVERY AND ACCEPTANCE. The Aircraft shall be + delivered to Lessee at the Delivery Location set forth on Exhibit C-2. + + (c) FORCE MAJEURE. Notwithstanding any other provision +hereof to the contrary, Lessor shall not have any obligation to lease the +Aircraft to Lessee on account of its or Beneficiaries, inability to deliver the +Aircraft if such inability or delay results from (i) loss or destruction of the +Aircraft or any substantial part of the Aircraft or damage to the Aircraft for +any reason whatsoever, including but not limited to, the negligence of the +Beneficiaries or (ii) any other cause occurring without the negligence of the +Beneficiaries and beyond the reasonable control of the Beneficiaries (after use +of reasonable diligence), including but not limited to war, warlike operations, +acts of government or governmental priorities, insurrections or riots, strikes +or labor troubles causing cessation, slow down or interruption of work, riots, +fire, floods, explosions, earthquakes, accidents, epidemics or quarantine +restrictions, mechanical failure, inability for any reason to obtain materials, +accessories, equipment or parts, delays in transportation or any act of God or +a public enemy. + + In the event that delivery of the Equipment is delayed for a + period in excess of 60 days beyond the Estimated Delivery Date (because of the + occurrence of an event described in clause (i) or (ii) above) Lessee or Lessor + shall have the right to terminate this Lease upon written notice to the other + and neither Lessee nor Lessor shall have any further obligations to the other + hereunder except that Lessor or the Beneficiaries shall return the Security + Deposit to Lessee subject to and in accordance with Section 21(a) hereof. + + (d) ACCEPTANCE OF AIRCRAFT. The Aircraft to be leased + hereunder shall be delivered to Lessee "AS IS, WHERE IS" and SUBJECT TO EACH + AND EVERY DISCLAIMER OF WARRANTY AND REPRESENTATION AS SET FORTH IN SECTION + 5(a) HEREOF. Representatives of Lessee shall inspect the Aircraft on or prior + to the Estimated Delivery Date to verify that the Aircraft is in the condition + required by Section 2(d). In the event the Aircraft does not fulfill all of the + conditions set forth in Section 2(d) and if Lessee does not accept delivery of + the Aircraft with discrepancies to be corrected pursuant to Section 2(d)(5), + then Lessor shall have (A) with respect to any matter which affects the + + -14- + + + + + + airworthiness of the Aircraft, fifteen (15) days within which to cure any + deficiency in the Aircraft, without cost to Lessee, and (B) with respect to any + other matter, sixty (60) days within which to cure any deficiency with the + Aircraft, without cost to Lessee; Lessee shall then reinspect the Aircraft and + if the Aircraft is then in compliance with the conditions set forth in Section + 2(d), Lessee shall accept delivery of the Aircraft. In no event shall Lessor or + the Beneficiaries have the right pursuant to the foregoing paragraph (c) or + this paragraph (d) or both to extend the Delivery Date beyond the date that is + sixty (60) days after the Estimated Delivery Date. Upon acceptance of the + Aircraft Lessee shall thereupon indicate and confirm its acceptance of the + Aircraft by delivery to Lessor of a Lease Supplement, dated the Delivery Date, + in the form set forth as Exhibit D hereto. + + The execution by Lessee of the Lease Supplement shall + constitute Lessee's acknowledgment that the Aircraft is in the condition + required hereby on the Delivery Date and Lessee's absolute, unconditional and + irrevocable acceptance of the Aircraft hereunder for all purposes of this + Lease. + + (e) TERM OF LEASE. The Term of this Lease shall commence on + the Delivery Date and shall continue for 96 months until the Expiration Date + set forth in the Lease Supplement, subject to earlier termination by Lessor or + a Beneficiary following the occurrence of an Event of Default, as hereinafter + provided or pursuant to paragraph 2(ii) of the Supplemental Agreement. + + Section 4. RENT. + + (a) RENT. Lessee covenants and agrees to pay to Lessor, or +its assigns, the following as Rent: + + (i) BASIC RENT: The Basic Rent as set forth on Exhibit C-1 +hereto throughout the Term hereof, payable in consecutive installments in +advance on each Basic Rent Payment Date. + + (ii) SUPPLEMENTAL RENT: Any and all Supplemental Rent as and +when the same becomes due. + + (b) PLACE AND METHOD OF PAYMENT. All Basic Rent and + Supplemental Rent and other amounts payable under this Lease shall be paid on + the due date in immediately available funds in Dollars by wire transfer, at + the Payment Locations specified on Exhibit C-2 hereto, or at such other + location as Lessor shall designate in writing. Any Rent due on a day other than + a Business Day (in New York, Dublin, London and Honolulu only) shall be due on + the next preceding Business Day. + + -15- + + + + + + (c) PROHIBITION AGAINST SETOFF, COUNTERCLAIM, ETC. This Lease + is a net lease. Subject to Section 20(f), Lessee's obligation to pay all Rent + hereunder shall be absolute and unconditional and shall not be affected or + reduced by any circumstance, including, without limitation, (i) any setoff, + counterclaim, recoupment, defense or other right which Lessee may have against + Lessor, any Beneficiary, the Manufacturer, the Engine Manufacturer, any seller + of or person providing services with respect to the Aircraft or any other + Person, for any reason whatsoever; (ii) any defect in the title, airworthiness + or eligibility for registration under applicable Law, or any condition, design, + operation or fitness for use of, or any damage to or loss or destruction of, + the Aircraft, or any interruption or cessation in the use or possession thereof + by Lessee for any reason whatsoever, whether arising out of or related to an + act or omission of Lessee, or any other Person; (iii) any Liens with respect to + the Aircraft; (iv) the invalidity or unenforceability or lack of due + authorization or other infirmity of this Lease or any absence of right, power + or authority of Lessor or Lessee to enter into this Lease; (v) any insolvency, + bankruptcy, reorganization or similar proceedings by or against Lessor or + Lessee; (vi) any other circumstance or happening of any nature whatsoever, + similar to any of the foregoing; or (vii) any Taxes (other Taxes to which + Lessee's indemnity does not extend pursuant to the provisions of Section 10); + it being the express intention of Lessor and Lessee that all Rent payable + hereunder shall be payable in all events, unless the obligation to pay the same + shall be terminated pursuant to the express provisions of this Lease. Nothing + in this paragraph (c) shall constitute a waiver by Lessee of any right or claim + that Lessee may separately assert against Lessor or any Beneficiary. + + Lessee hereby waives, to the extent permitted by applicable + Law, any and all rights which it may now have or which at any time hereafter + may be conferred upon it, by Law or otherwise, to terminate this Lease or any + obligation imposed upon Lessee hereunder or in relation hereto. + + If for any reason whatsoever this Lease shall be terminated + in whole or in part by operation of law (other than termination under any + bankruptcy laws as now or hereafter in effect), Lessee nonetheless agrees to + pay to Lessor amounts equal to the Rent payments hereunder at the time such + payments would have become due and payable in accordance with the terms hereof + had this Lease not been terminated so long as Lessee is able to use, possess + and quietly enjoy the Aircraft, and so long as such payments are made and all + other terms and conditions hereof are complied + + -16- + + + + + +with by Lessor and Lessee, Lessor and Lessee will deem this Lease to remain in +full force and effect. + + Section 5. REPRESENTATIONS AND WARRANTIES. + + (a) WARRANTIES AND DISCLAIMER OF WARRANTIES. + + LESSOR AND EACH BENEFICIARY HAVE NOT AND SHALL NOT BE DEEMED +TO HAVE MADE (WHETHER BY VIRTUE OF HAVING LEASED THE AIRCRAFT UNDER THIS LEASE, +OR HAVING ACQUIRED THE AIRCRAFT, OR HAVING DONE OR FAILED TO DO ANY ACT, OR +HAVING ACQUIRED OR FAILED TO ACQUIRE ANY STATUS UNDER OR IN RELATION TO THIS +LEASE OR OTHERWISE), AND LESSOR AND EACH BENEFICIARY HEREBY SPECIFICALLY +DISCLAIM, ANY REPRESENTATION OR WARRANTY, EXPRESS OR IMPLIED, AS TO THE TITLE +(EXCEPT AS HEREINBELOW PROVIDED), AIRWORTHINESS, CONDITION, DESIGN, OPERATION, +MERCHANTABILITY, FREEDOM FROM CLAIMS OF INFRINGEMENT OR THE LIKE, OR FITNESS +FOR USE FOR A PARTICULAR PURPOSE OF THE AIRCRAFT, OR AS TO THE QUALITY OF THE +MATERIAL OR WORKMANSHIP OF THE AIRCRAFT, THE ABSENCE THEREFROM OF LATENT OR +OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE, OR AS TO ANY OTHER REPRESENTATION +OR WARRANTY WHATSOEVER, EXPRESS OR IMPLIED (INCLUDING ANY IMPLIED WARRANTY +ARISING FROM A COURSE OF PERFORMANCE OR DEALING OR USAGE OF TRADE), WITH +RESPECT TO THE AIRCRAFT; AND LESSEE HEREBY WAIVES, RELEASES, RENOUNCES AND +DISCLAIMS EXPECTATION OF OR RELIANCE UPON ANY SUCH. WARRANTY OR WARRANTIES. +LESSOR AND EACH BENEFICIARY SHALL NOT HAVE ANY RESPONSIBILITY OR LIABILITY TO +LESSEE OR ANY OTHER PERSON, WHETHER ARISING IN CONTRACT OR TORT OUT OF ANY +NEGLIGENCE OR STRICT LIABILITY OF LESSOR OR SUCH BENEFICIARY OR OTHERWISE, FOR +(i) ANY LIABILITY, LOSS OR DAMAGE CAUSED OR ALLEGED TO BE CAUSED DIRECTLY OR +INDIRECTLY BY THE AIRCRAFT OR ANY ENGINE OR BY ANY INADEQUACY THEREOF OR +DEFICIENCY OR DEFECT THEREIN OR BY ANY OTHER CIRCUMSTANCE IN CONNECTION +THEREWITH, (ii) THE USE, OPERATION OR PERFORMANCE OF THE AIRCRAFT OR ANY RISKS +RELATING THERETO, (iii) ANY INTERRUPTION OF SERVICE, LOSS OF BUSINESS OR +ANTICIPATED PROFITS OR CONSEQUENTIAL DAMAGES OR (iv) THE DELIVERY, OPERATION, +SERVICING, MAINTENANCE, REPAIR, IMPROVEMENT OR REPLACEMENT OF THE AIRCRAFT. THE +WARRANTIES AND REPRESENTATIONS SET FORTH IN THIS SECTION ARE EXCLUSIVE AND IN +LIEU OF ALL OTHER REPRESENTATIONS OR WARRANTIES WHATSOEVER, EXPRESS OR IMPLIED, +AND LESSOR AND EACH BENEFICIARY SHALL NOT BE DEEMED TO HAVE MADE BY OTHER +WARRANTIES, EXCEPT THAT EACH BENEFICIARY MAKES THE REPRESENTATIONS AND +WARRANTIES CONTAINED IN THE SUPPLEMENTAL AGREEMENT AND: + + (b) LESSOR REPRESENTATIONS AND WARRANTIES. + + Lessor hereby makes the following representations and +warranties: + + -17- + + + + + + (i) the Aircraft is duly registered with the FAA in the name + of Lessor and, on the Delivery Date, Lessor shall be the FAA-registered + owner of the Aircraft and the Aircraft shall be free and clear of any and + all Lessor Liens attributable to it other than the interests of the + Beneficiaries under the Trust Agreement; + + (ii) Lessor is a banking corporation duly organized and + validly existing and in good standing under the laws of Delaware, and has + the corporate power and authority to carry on its business as presently + conducted and to perform its obligations under this Lease; + + (iii) the making and performance by Lessor of this Lease and + any other Operative Document to which Lessor is a party have been duly + authorized by all necessary corporate action on the part of Lessor and + will not violate any provision of any law, governmental rule or + regulation of the State of Delaware or of the United States of America + governing the banking and trust powers of Lessor or its Certificate of + Incorporation or By-Laws; + + (iv) this Lease has been duly entered into and delivered by + Lessor, and this Lease does, and each other Operative Document when + executed and delivered by Lessor hereunder will, constitute legal, valid + and binding obligations of Lessor, enforceable in accordance with their + respective terms except as may be limited by bankruptcy, insolvency, + reorganization, moratorium or other similar laws affecting the + enforcement of creditors, rights generally, and, to the extent that + certain remedies require or may require enforcement by a court of equity, + by such principles of equity (regardless of whether such enforceability + is considered in a proceeding in equity or at law) as a court having + jurisdiction may impose and by laws which may affect some of such + remedies but which do not make available remedies inadequate for the + substantial realization of the benefits provided herein; + + (v) neither the execution and delivery by Lessor of this + Lease and any other Operative Document to which it is a party nor the + performance by it of any of its obligations hereunder or thereunder, nor + the compliance by it with the terms and conditions hereof or thereof, + will violate, conflict with or result in any breach of any terms, + conditions or provisions of, or provisions of, or + + + -18- + + + + + + constitute a default under, any law of the State of Delaware or the + United States of America governing the banking and trust powers of Lessor + or court judgment or decree applicable to it or pursuant to which it was + organized or any agreement or instrument to which it is a party or by + which it or any of its property is bound, or result in the creation or + imposition of any Lessor's Lien attributable to it on the Aircraft other + than as provided for in this Lease (such representation being made in + reliance upon and subject to the accuracy of the representation and + warranty contained in the last sentence of Section 5(d)(xvi)); + + (vi) neither the execution and delivery nor the performance + by Lessor of this Lease and any Operative Document to which Lessor is a + party requires any consent or approval of, giving of notice to, + registration with, or taking of any other action in respect of any + governmental authority or agency in the State of Delaware; + + (vii) no registration, recording, filing or notarization of + this Lease in the State of Delaware and no other action by Lessor is + necessary to be taken in the State of Delaware to ensure the validity or + enforceability of the obligations of Lessor or the rights of Lessee + hereunder; + + (viii) there are no pending or, to the knowledge of Lessor, + threatened actions or proceedings before any court, governmental or + administrative agency or arbitral body which actions or proceedings could + have a material and adverse effect on Lessor's ability to perform its + obligations under this Lease or any other Operative Document to which it + is a party; + + (ix) Lessor shall obtain, make and maintain in full force and + effect, promptly renew from time to time and comply with the terms of all + consents, permissions, licenses, authorizations, approvals, registrations + and filings which may from time to time be required in the State of + Delaware in order to enable it to perform its obligations hereunder or + for the enforceability or admissibility in evidence hereof; and + + (x) Lessor is a "citizen of the United States" within the + meaning of Section 40102(a)(15) of the Federal Aviation Act. + + -19- + + + + + + (c) MANUFACTURERS' WARRANTIES. So long as no Default and no + Event of Default shall have occurred and be continuing and so long as the Term + shall remain in effect, Lessee may exercise for the account of Lessor such + rights as Lessor may have under any warranty, express or implied, with respect + to the Aircraft made by the Manufacturer or the Engine Manufacturer, to the + extent that the same may be assigned or otherwise made available to Lessee; + provided, however, that upon an Event of Default all such rights shall + immediately revert to Lessor including all claims thereunder whether or not + perfected, and such reversion shall occur at the earlier of (A) written notice + from Lessor to Lessee to such effect, and (B) the expiration or any termination + of the Term. Lessor agrees to execute and deliver such further documents and + instruments and to do such further acts, deeds, matters or things (at Lessee's + expense) as may be necessary to enable Lessee to obtain whatever warranty + service furnished for the Aircraft or the Engines by the Manufacturer or the + Engine Manufacturer, respectively, as is available to the Lessor. + + (d) LESSEE'S REPRESENTATIONS AND WARRANTIES. Lessee hereby + makes the following representations and warranties, which representations and + warranties shall survive the execution and delivery of this Lease and the + delivery of the Aircraft: + + (i) Lessee is a corporation duly organized and existing under + the Laws of the State of Hawaii and has the corporate power and authority + to carry on its business as presently conducted and to perform its + obligations under this Lease; + + (ii) this Lease has been duly authorized by all necessary + corporate action on the part of Lessee and does not require any approval + of stockholders of Lessee (or if such approval is required, such approval + has been obtained), and neither the execution and delivery hereof nor the + consummation of the transactions contemplated hereby nor compliance by + Lessee with any of the terms and provisions hereof will contravene any + Law applicable to Lessee or the corporate charter or by-laws of Lessee-or + result in any breach of, or constitute any default under, or result in + the creation of any Lien upon any property of Lessee under, any credit + agreement or instrument, or other agreement or-instrument to which Lessee + is a party or by which Lessee or its properties or assets are bound or + affected; + + (iii) Lessee has received every consent, approval or + authorization of, and has given every + + -20- + + + + + + notice to, each Governmental Entity having jurisdiction with respect to + the execution, delivery or performance of this Lease that is required for + Lessee to execute and deliver this Lease and to perform the transactions + contemplated hereby and each such consent, approval or authorization is + valid and effective and has not been revoked; + + (iv) this Lease has been duly executed and delivered by + Lessee, and the Lease does, and the Lease Supplement when executed and + delivered by Lessee will, constitute legal, valid and binding obligations + of Lessee, enforceable in accordance with their respective terms, except + as enforcement thereof may be limited by applicable bankruptcy, + insolvency, reorganization, moratorium or other similar laws affecting + the enforcement of creditors, rights generally, and, to the extent that + certain remedies require or may require enforcement by a court of equity, + by such principles of equity (regardless of whether such enforceability + is considered in a proceeding in equity or at law) as a court having + jurisdiction may impose and by Laws which may affect some of such + remedies but which do not make the available remedies inadequate for the + substantial realization of the benefits provided herein; + + (v) there are no suits or proceedings (other than those + heretofore disclosed in writing to each Beneficiary) pending or, to the + knowledge of Lessee, threatened in any court or before any regulatory + commission, board or other administrative governmental agency against or + affecting Lessee which might have a materially adverse effect on the + current business or financial condition of Lessee; + + (vi) Lessee has filed or caused to be filed all tax returns + which are required to be filed by it, and has paid or caused to be paid + all Taxes shown to be due or payable on said returns or on any assessment + received by Lessee, except to the extent being contested in good faith by + appropriate proceedings, and for the payment of which adequate reserves + have been provided; + + (vii) except for the filing for recordation of the FAA Bills + of Sale, an application for registration of the Aircraft on AC Form + 8050-1 in the name of the Lessor, the Trust Agreement, the affidavit of + citizenship of the Lessor and this Lease and the Lease Supplement with + the FAA, the + + -21- + + + + + + filing with the Hawaii Bureau of Conveyances, Hawaii of precautionary UCC + financing statements and continuation statements at periodic intervals + with respect to the Aircraft and the placing on the Aircraft and on each + Engine of the plates containing the legends referred to in Section 6(f) + hereof, no further filing or recording of this Lease or of any other + document and no further under the Laws of any Governmental Entity in + order to (A) fully protect and establish Lessor's title to, interest in + and property rights with respect to the Aircraft as against Lessee or + any third party or (B) ensure the validity, effectiveness and enforce + ability of this Lease; + + (viii) Lessee is not in default in the performance of any of + its obligations (a) for the payment of indebtedness for borrowed money or + of any interest or premium thereon or (b) for the payment of rent under + any lease or agreement to lease real, personal or mixed property; + + (ix) the Maintenance Program for the Aircraft complies with + all FAA requirements; + + (x) the consolidated financial statements of Aloha Airgroup, + Inc. (including Lessee), including the balance sheets and audited + statements of income and retained earnings of Lessee, for the preceding + two fiscal years and for the period ending September 30, 1995, copies of + which have been furnished to Lessor, are prepared in accordance with + generally accepted accounting principles, and present fairly the + financial position and operations of Lessee, and subsequent to the + conclusion of the last such period, there has been no material adverse + change in such position or operations; + + (xi) there are no withholding or other Taxes to be deducted + from any payment to be made by Lessee under this Lease (other Taxes to + which Lessee's indemnity does not extend pursuant to the provisions of + Section 10); + + (xii) Lessee is a Certified Air Carrier and holds all + licenses, certificates, permits and franchises from the appropriate + agencies of the United States of America and/or all other governmental + authorities having jurisdiction which are necessary to authorize the + Lessee to engage in air transport and to carry on its business as + + -22- + + + + + + presently conducted and to be conducted with the Aircraft; + + (xiii) Lessee is a "citizen of the United States" as defined + in Section 40102(a)(15) of the Federal Aviation Act; + + (xiv) Lessor is entitled to the benefits of Section 1110 of + Title 11 of the united States Code with respect to the Aircraft and the + Engines; + + (xv) Lessee's chief executive office (as such term is defined + in Article 9 of the Uniform Commercial Code) and the place where it keeps + its corporate records concerning the Aircraft, all its interest in, to + and under any Operative Document and its contracts relating thereto, are + located in Honolulu, Hawaii; + + (xvi) Neither Lessee nor any Person who was at such time a + Commonly Controlled Person has terminated any Plan or withdrawn from any + multiemployer plan (as defined in Section 4001(a)(3) of ERISA) in a + manner which has or could reasonably be expected to result in any + liability under Title IV of ERISA, and no event or condition has occurred + or exists as a result of which any such liability could reasonably be + expected to be incurred. No Plan maintained or contributed to (or + required to be contributed to) by Lessee or any Person who was at such + time a Commonly Controlled Person has incurred an "accumulated funding + deficiency" (as defined in Section 302 of ERISA). No Lien imposed under + the Code or ERISA on the assets of Lessee or any Commonly Controlled + Person exists or is reasonably likely to arise on account of any Plan. No + prohibited transaction (as defined in Section 406 of ERISA or Section + 4975 of the Code) has occurred which has subjected or could subject + Lessee or any Commonly Controlled Person to any liability under Section + 502(i) of ERISA or Section 4975 of the Code. The execution and delivery + by Lessee of this Lease and the consummation of the transactions + contemplated hereby will not constitute a prohibited transaction within + the meaning of Section 406 of ERISA or Section 4975 of the Code; and + + (xvii) No part of the Rent or other payments made by Lessee + under the Operative Documents will be made with the assets of any plan + (or its related trust) as defined in Section 4975(e) of the Code or of + any employee benefit plan (or its + + -23- + + + + + + related trust) as defined in Section 3(3) of ERISA. + + Section 6. POSSESSION AND USE; REGISTRATION. + + (a) POSSESSION. + + (i) SUBLEASE, ASSIGNMENT AND TRANSFER. Without the prior + written consent of Lessor and each Beneficiary, Lessee will not assign this + Lease or sublet, transfer or relinquish possession of the Aircraft, Airframe or + any Engine or install any Engine or permit any Engine to be installed on any + airframe other than the Airframe or under any circumstances permit the + Aircraft, Airframe or any Engine to be used by any other Person for any purpose + whatsoever, provided that so long as (A) no Default or Event of Default shall + have occurred and be continuing, (B) the action to be taken shall not affect + the registration of the Aircraft with the FAA and (C) all necessary approvals + of each Governmental Entity having jurisdiction over the Aircraft have been + obtained, then Lessee, without the prior written consent of Lessor and each + Beneficiary, may: + + (1) subject any Engine to normal interchange or pooling + agreements or similar arrangements in each case customary in the airline + industry and entered into in the ordinary course of its business with + Permitted Sublessees or an air carrier approved by Lessor and each + Beneficiary or an FAA licensed engine overhaul agency, provided that + Lessee shall give the Beneficiaries 30 days' prior notice thereof + (together with a copy of the applicable pooling or interchange + agreement); and provided further, however, that: (A) no such agreement or + arrangement results in or requires the transfer of title to such Engine; + or (B) if Lessor's title to such Engine shall be divested under any such + agreement or arrangement, such divestiture shall be deemed to be a + Casualty occurrence with respect to such Engine and Lessee shall comply + with Section 11(b) hereof in respect thereof; + + (2) deliver possession of the Aircraft, the Airframe or any + Engines to the manufacturer thereof for testing or other similar purposes + or to any organization for service, repair, maintenance, testing or + overhaul work on the Aircraft, Airframe or Engine or any Part thereof or + for alterations or modifications in or additions to the Aircraft, + Airframe or Engine to the extent required or permitted by the terms of + Section 9 hereof; + + -24- + + + + + + (3) install an Engine on an airframe (other than the + Airframe) owned by Lessee free and clear of all Liens except: (A) those + of the type permitted under Section 14 hereof and those which apply only + to the engines (other than Engines), appliances, parts, instruments, + appurtenances, accessories, furnishings and other equipment (other than + Parts) installed on such airframe (but not to the airframe as an + entirety); and (B) the rights of the participants under normal + interchange agreements which are customary in the airline industry and do + not contemplate, permit, result in or require the transfer of title to + theairframe, engines or parts installed thereon; + + (4) install an Engine on an airframe leased to Lessee or + owned by Lessee subject to a conditional sale or other security + agreement, provided, that: (A) such airframe is free and clear of all + Liens except the rights of the parties to the lease or conditional sale + or other security agreement covering such airframe and except Liens of + the type permitted by clauses (A) and (B) of Section 6(a)(i)(3), and the + Lien of any mortgage which either by its terms does not apply to the + Engine or which effectively provides that each Engine leased to Lessee + hereby shall not become subject to the Lien thereof or to any rights of + any party thereunder other than Lessee (with respect to Lessee's rights + expressly granted hereunder), notwithstanding the installation of such + Engine on any airframe subject to the Lien of such mortgage, unless and + until Lessee shall become the owner of such Engine and Lessor shall have + no further interest therein, all pursuant to the express terms of this + Lease (as evidenced by a written instrument signed by Lessor); and (B) + there shall be in effect a written agreement of the lessor or secured + party of such airframe (which may be the lease or conditional sale or + other security agreement covering such airframe) substantially similar + in effect to the agreement of Lessor in Section 6(b) below whereby such + lessor or secured party effectively and expressly agrees that neither it + nor its successors or assigns will acquire or claim any right, title or + interest in any Engine by reason of such Engine being installed on such + airframe at any time while such Engine is subject to this Lease, and a + copy of such agreement shall be provided to Lessor or any Beneficiary + upon its request; + + -25- + + + + + + (5) sublease the Airframe and the Engines or the engines then + installed on the Airframe or any Engine to any Permitted Sublessee; + PROVIDED, that such Permitted Sublessee shall not (i) have consented to + the appointment of a receiver, trustee or liquidator or admitted in + writing its inability to pay its debts generally as they become due or + filed a voluntary petition in bankruptcy or a voluntary petition seeking + reorganization in a proceeding under any bankruptcy laws, (ii) have had + an involuntary order, judgment or decree entered against such Permitted + Sublessee by any court appointing a receiver, trustee or liquidator for + such Permitted Sublessee or (iii) be in default under any lease or + financing agreement to which such Permitted Sublessee is a party; and + + (6) enter into a Wet Lease for the Aircraft in the ordinary + course of its business for a term or terms (including any renewal terms + or extensions) that do not extend beyond the Term. + + (ii) CERTAIN LIMITATIONS ON TRANSFERS. With respect to any + assignment, sublet, transfer or relinquishment of possession pursuant to + Section 6(a)(i) (other than a transfer of an Engine which is deemed a Casualty + Occurrence) or any other assignment, sublet or transfer of possession consented + to in writing by Lessor and the Beneficiary: + + (1) Lessee shall remain primarily liable hereunder for the + performance of all of the terms of this Lease to the same extent as if + such transfer had not occurred, and no sublease or other relinquishment + of possession of the Equipment shall in any way discharge or diminish any + of Lessee's obligations to Lessor under this Lease or any of Lessor's + rights hereunder and such rights shall continue as if such sublease or + transfer had not occurred; + + (2) the term of any sublease (including, without limitation, + any option of the sublessee to renew or extend the sublease), interchange + or pooling arrangement shall not continue beyond the end of the Term; + + (3) any sublease permitted by this Section 6 shall in + addition expressly provide that (v) such sublease is subject and + subordinate to all of the terms of this Lease and all rights of Lessor + under this Lease, including without limitation Lessor's + + -26- + + + + + + rights to repossession pursuant to Section 18 and to void such Permitted + Sublessee's right to possession upon such repossession, shall apply, (w) + such Permitted Sublessee consents to the security assignment by Lessee to + Lessor of all of Lessee's right, title and interest in such sublease for + purposes of securing Lessee's obligations hereunder and that Permitted + Sublessee, upon receipt of a written notice from Lessor that an Event of + Default has occurred and is continuing, will make all payments under such + sublease to, and, if this Lease has been declared in default pursuant to + Section 18 hereof, will return the Equipment only to, Lessor, or as + directed by Lessor, for so long as such Event of Default shall continue, + (x) the maintenance, operation and insurance provisions of such sublease + shall be substantially the same as the provisions of this Lease (whether + by requiring such obligations to be performed by such Permitted + Sublessee, by Lessee or by both), (y) the Aircraft shall not be operated + or used other than as provided in this Lease, and (z) the Lessor may void + or terminate such sublease following an Event of Default hereunder but + only during the period as such Event of Default shall continue; + + (4) prior to the commencement of any sublease to a Permitted + Sublessee, Lessee shall give Lessor and each Beneficiary no less than 10 + days written notice of the proposed sublease of the Airframe or Engine, + as the case may be, which notice shall include the identity of the + Permitted Sublessee, the term and rental rate of the sublease and a copy + of such sublease; PROVIDED, HOWEVER, that Lessee shall use its reasonable + efforts to give such notice earlier than such 10 days if practicable + under the circumstances and to advise Lessor and the Beneficiaries at any + time at which Lessee commences substantial discussions or negotiations + with any Permitted Sublessee with respect to the potential sublease of + the Aircraft; + + (5) any sublease shall-expressly prohibit any assignment or + further sublease of the Aircraft or any assignment of the rights under + such sublease; + + (6) any Wet Lease shall have a term that does not exceed + beyond the end of the Term; and + + (7) the Person with which and for which any Wet Lease is + entered into shall be an airline that is a + + -27- + + + + + + resident of, is domiciled in, and maintains its principal base of + operations in, a Permitted Jurisdiction. + + Prior to entering into any sublease of the Airframe and any Engine or engine or + any Engine to a Permitted Sublessee, Lessee shall (A) execute and deliver to + Lessor and each Beneficiary an assignment of and grant of a security interest + in all of Lessee's right, title and interest in such sublease, which assignment + shall be in form and substance reasonably satisfactory to Lessor and each + Beneficiary, contain the consent to such assignment of such Permitted + Sublessee, and be recordable under the Federal Aviation Act and shall be + executed, delivered and filed with the FAA for recordation at Lessee's expense + on or before commencement of such sublease (provided that such assignment may + specify that Lessor may not exercise any of the rights or remedies of the + Lessee under such sublease including, without limitation, any right to receive + rentals under such sublease or to interfere with the rights of the Lessee to + grant waivers or consents under, or to amend the provisions of, any such + sublease) unless, and only so long as, an Event of Default hereunder shall have + occurred and be continuing, and (B) execute and deliver to Lessor an + appropriately completed Uniform Commercial Code financing statement, for filing + in Hawaii and each other applicable jurisdiction, evidencing the assignment by + Lessee to Lessor of such sublease. In connection with such a sublease of the + Aircraft, Lessee shall provide to Lessor and each Beneficiary, at Lessee's or + Permitted Sublessee's expense and on or before commencement of such sublease, a + legal opinion from counsel to the Permitted Sublessee in form and substance + reasonably satisfactory to Lessor and each Beneficiary, as to the due execution + and delivery and enforceability of such sublease and assignment of sublease and + as to such other matters as Lessor or such Beneficiary may reasonably request. + Lessee agrees that any such security assignment granted to the Lessor may be + assigned to the Lender, if any, it being understood that such assignment shall + be consistent and not contravene the foregoing provisions. Any monies received + by Lessor or Lender pursuant to the exercise of its rights under the assignment + of any sublease shall be held by Lessor or the Lender as additional security + for the performance by Lessee of its obligations under this Lease and, to the + extent not applied against amounts due and owing by Lessee hereunder and the + exercise of remedies hereunder, shall be returned to Lessee at such time as no + Event of Default shall be continuing. All costs and expenses (including + reasonable legal fees and expenses) incurred by Lessor, Lender or any + Beneficiary in connection with any sublease or the assignment thereof shall be + borne by Lessee and reimbursed by Lessee upon demand. + + -28- + + + + + + (b) RECIPROCAL RECOGNITION OF RIGHTS. In the event Lessee + shall have received from the lessor or secured party of any airframe leased to + Lessee or owned by Lessee subject to a conditional sale or other security + agreement a written agreement complying with clause (B) of Section 6(a)(i)(4) + hereof (which agreement may be contained in the lease, conditional sale + agreement or security agreement relating to such airframe), and such lease or + conditional sale or other security agreement covering such airframe also covers + an engine or engines owned by the lessor under such lease or subject to a + security interest in favor of the secured party under such conditional sale or + other security agreement, Lessor hereby agrees for the benefit of such lessor + or secured party that Lessor will not acquire or claim, as against such lessor + or secured party, any right, title or interest in any such engine as the result + of such engine being installed on the Airframe at any time while such engine is + subject to such lease or conditional sale or other security agreement and owned + by such lessor or subject to a security interest in favor of such secured + party. Lessor also hereby agrees for the benefit of the mortgagee under any + mortgage complying with clause (A) of Section 6(a)(i)(4) hereof, relating to + installation of an Engine on an airframe leased to Lessee, that Lessor will not + acquire or claim, as against such mortgagee, any right, title or interest in + any engine subject to the Lien of such mortgage as the result of such engine + being installed on the Airframe at any time while such engine is subject to the + Lien of such mortgage. + + (c) LAWFUL INSURED OPERATIONS. Lessee will not permit the + Aircraft to be maintained, used or operated in violation of any Law of any + Governmental Entity, or in violation of any airworthiness certificate, or + license or registration issued by any such authority, or contrary to the + Manufacturer's or Engine Manufacturer's operating manuals or instructions for + the Aircraft or the Engines. In the event that any such Law requires alteration + of the Aircraft, then, subject to Section 1 of the Supplemental Agreement, + Lessee shall comply therewith at its sole expense and shall maintain the same + in proper condition for operation under such Laws. Lessee agrees not to operate + the Aircraft, or permit the Aircraft to be operated, (i) unless the Aircraft is + covered by insurance as required by the provisions hereof or (ii) contrary to + the terms of such insurance. Lessee also agrees not to operate or locate the + Aircraft or suffer or permit the Aircraft to be operated or located in any area + excluded from coverage by any insurance policy issued pursuant to the + requirements of this Lease. + + (d) MAINTENANCE. Lessee, at its own cost and expense, shall, + or shall cause a maintenance contract center that is FAA certified for the + contracted maintenance of the + + -29- + + + + + + Aircraft and located in the united States of America or Canada, to: + + (i) service, repair, maintain, overhaul and test, or cause + the same to be done to, each Item leased hereunder (A) so as to keep such + Item in the same condition as when delivered to Lessee hereunder, fair + wear and tear excepted, and in good operating condition, (B) so as to + keep the Aircraft in the condition necessary to enable the airworthiness + certification of the Aircraft to be maintained in good standing at all + times under all applicable FAA rules and regulations, (C) so as to meet + the standards observed by Lessee with respect to aircraft of similar type + owned or leased by Lessee and operated on similar routes, and in a manner + which does not adversely discriminate against the Aircraft compared to + similar aircraft operated by Lessee and in a similar manner and with care + as that undertaken by the majority of airlines based in the United States + of America which operate Boeing 737-200ADV aircraft (it being understood + that the obligations hereby imposed upon Lessee by reference to the + manner and care undertaken by other airlines is not intended to supersede + Lessee's obligations herein with respect to the specific and express + terms of this Lease), (D) in strict compliance with Lesseel's + FAA-approved Maintenance Program applicable to the Aircraft (including + the corrosion prevention control program (CPCP/D6-38528) and aging + aircraft program (D6-38505 revision H as applicable per airworthiness + directive or per effectivity)), a true and complete copy of which shall + be delivered by Lessee to the Lessor which Maintenance Program shall be + in substantial conformance with the Manufacturer's recommended + maintenance planning document; + + (ii) maintain all records, logs and other materials required + by the FAA or the Maintenance Program in respect of the Aircraft with all + such records, logs and other material being maintained in the English + language; + + (iii) promptly furnish to Lessor or each Beneficiary such + information, in English, as may be required to enable Lessor or such + Beneficiary to file any reports required to be filed by such Lessor or + Beneficiary with any governmental authority concerning the Aircraft; + + -30- + + + + + + (iv) incorporate into the Aircraft all manufacturer mandatory + requirements, orders, "alert" and, to the extent scheduled for a majority + of Lessee's other owned or leased aircraft of the same model, + "recommended" service bulletins issued by the manufacturers and vendors + of the Items (so far as such manufacturers and vendors issue such service + bulletins) which become due for compliance during the Term without + waiver, exception, deferment or carryover and, in addition, all other + service bulletins that Lessee adopts during the Term for a majority of + other aircraft of the same model in Lessee's fleet; + + (v) carry out, on each Item, all applicable regulatory + regulations, mandatory requirements, orders and airworthiness directives + which the FAA may from time to time issue and which become due during or + within three months following the expiration of the Term (or within six + months following the expiration of the Term if requested by the + Beneficiaries at Beneficiaries' cost; however, the Term will not be + deemed extended solely as a result of the additional work to terminate + the airworthiness directives due after three months after the expiration + of the Term and requested by the Beneficiaries) with respect to such + Item, without waiver, exception, deferment or carryover beyond the + expiration of the Term (except as expressly set forth in the Supplemental + Agreement); provided that any inspections and terminating action required + by such regulatory regulations, mandatory requirements, orders and + airworthiness directives to be performed by a particular date shall be + performed by such date; + + (vi) maintain, service, repair and overhaul the Airframe and + all Engines to comply with all warranty requirements; + + (vii) except when required by maintenance or expressly + permitted by Section 6(a) above, equip the Airframe at all times with two + Engines; + + (viii) make available to Lessor upon its reasonable request + during the Term, one or more persons with sufficient technical knowledge + to assist the Lessor or each Beneficiary in its examination of such + materials and documents relating to the maintenance and condition of the + Aircraft as the Lessor or each Beneficiary shall from time to time + reasonably request; and + + -31- + + + + + + (ix) without prejudice to Lessee's obligations to repair the + Aircraft pursuant to the terms hereof, if any damage occurs to the + Aircraft which results in the need for repairs where the costs of such + repairs are expected to exceed US$500,000, Lessee shall promptly notify + Lessor and each Beneficiary of the details of the repairs required and + shall furnish them with a workscope relating thereto, and Lessee shall + comply with any reasonable direction of Lessor or each Beneficiary as to + the conduct of the repairs to the Aircraft; PROVIDED, HOWEVER, that the + Beneficiaries shall review such workscope and inform Lessee of any + comments thereon or objections thereto within three Business Days after + their receipt thereof; PROVIDED FURTHER, HOWEVER, that Lessee shall be + permitted to use a maintenance contractor to perform its obligations + pursuant to this Section 6(d) so long as such maintenance contractor is a + repair facility approved by the FAA for the contracted repair of the + Aircraft which is located in the United States of America or Canada, or + other facility acceptable to the Lessor and each Beneficiary. + + (e) REGISTRATION. So long as no Event of Default shall have + occurred and be continuing, Lessor shall cause the Aircraft to be duly + registered in the name of Lessor with the FAA under the Federal Aviation Act as + at the Delivery Date, shall take such actions as are reasonably appropriate (so + long as such actions pose no material burden or risk to Lessor or the + Beneficiaries) to maintain such registration during the Term, and shall not + take any action to interfere with or cancel such registration; provided that, + after the Delivery Date, Lessee shall monitor and, to the extent not relating + to the U.S. citizenship of the Beneficiaries, maintain such registration; + PROVIDED, FURTHER, HOWEVER, that (i) if Lessor shall at any time cease to be a + "citizen of the United States" (as defined in the Federal Aviation Act) and if + such requirement shall then be necessary to maintain such registration, then + the Beneficiaries shall take such action as shall be reasonable and appropriate + to maintain such registration (including by appointing a successor trustee + under the Trust Agreement or establishing a voting trust covering the + Aircraft), (ii) Lessee and Lessor shall cooperate reasonably and in good faith + to maintain such registration, and (iii) Lessee shall, at Lessee's expense, + be responsible for the filing and registration of the Lease (as supplemented by + the Lease Supplement) and for maintaining such registration throughout the + Term. + + -32- + + + + + + + + (f) INSIGNIA. Upon delivery of the Aircraft, Lessee agrees to + promptly place the Lease Identification in the cockpit in a prominent location + and to place the Lease Identification on each Engine. Lessee agrees to make + such reasonable changes to the Lease Identification as Lessor may request from + time to time. + + Section 7. INFORMATION. + + During the term of this Lease, Lessee agrees to furnish + Lessor and each Beneficiary the following: + + (i) within sixty (60) days following the end of each quarter + of Lessee's fiscal year, except the last such quarter of such year, an + unaudited consolidated balance sheet of Aloha Airgroup, Inc. (including + Lessee) prepared as of the close of each quarterly period, together with + the related unaudited profit and loss statement for such period; + + (ii) within one hundred twenty (120) days or such longer + period as reasonably justified after the close of each fiscal year of + Lessee, an audited consolidated balance sheet, profit and loss statement + and statement of stockholders' equity of Aloha Airgroup, Inc. (prepared + on a consolidated basis including Lessee), as of the close of such fiscal + year, reported on by Lessee's independent certified accountants of + recognized standing; + + (iii) within one hundred twenty (120) days after the close of + each fiscal year of Lessee, a certificate signed by a duly authorized + officer of Lessee, stating (a) that such officer is familiar with the + relevant terms of this Lease and has made a review of Lessee's compliance + herewith during the preceding fiscal year and (b) that no event has + occurred which constitutes a Default, or, if such an event has occurred, + the nature thereof and action Lessee has taken or is taking to cure the + same; + + (iv) from time to time upon request, Lessee will at no cost + to Lessor or each Beneficiary provide Lessor or each Beneficiary + certified true and complete copies in the English language of the + Maintenance Program; provided that Lessor or such Beneficiary shall + maintain the confidentiality thereof and shall not disclose the contents + thereof to any Person, except as permitted by Section 20(k) and except to + the extent necessary + + -33- + + + + + + to bridge the Aircraft from the Maintenance Program to the maintenance of + a subsequent lessee or buyer of the Aircraft or as required by the Law of + a jurisdiction in which the Aircraft will be based, operated or + registered; + + (v) Lessee will use its reasonable efforts to provide the + Beneficiaries on or before the fifth day of each calendar month + commencing with the next calendar month of the Delivery Date, and shall + in any event provide to the Beneficiaries upon request of a Beneficiary, + with a properly completed Monthly Aircraft Utilization and Status Report + in the Form of Exhibit J hereto for the preceding calendar month + operation of the aircraft; + + (vi) Lessee will use its reasonable efforts to provide the + Beneficiaries, on or before the 15th day of January of each year + (commencing with January 1996), and shall in any event provide Lessor and + the Beneficiaries upon request of a Beneficiary in English, the + information and documentation for the preceding calendar year as listed + in Exhibit K hereto; PROVIDED, HOWEVER, that if (i) a Default or an Event + of Default shall have occurred and be continuing or (ii) Lessee's + financial condition changes adversely from its financial condition at the + time of the Delivery Date, then, upon notice and a request from Lessor or + a Beneficiary, Lessee shall provide such information on a quarterly basis + on the 15th day of each January, April, July and October, commencing with + the first of such dates to follow the date of such notice. + + (vii) Lessee will use its reasonable efforts to provide the + Beneficiaries, within forty-five (45) days prior to the scheduled + performance of a block C or D check on the Aircraft, and shall in any + event provide Lessor and the Beneficiaries upon request of a Beneficiary + with written notice of the time and location of such scheduled block C or + D check; and + + (viii) from time to time such other information regarding the + business, operations or the financial condition of Lessee as the Lessor + or any Beneficiary may from time to time reasonably request, including + reports from time to time as to location, condition, use, operation, + maintenance and insuring of the Aircraft and the Engines + + -34- + + + + + + (including total time spent in the air and time between overhauls). + + Lessee shall permit Lessor, each Beneficiary and their + respective designees on at least seven (7) days' prior written notice to visit + and inspect the Aircraft, its condition, use and operation and the records + maintained in connection therewith during normal business hours; PROVIDED, + HOWEVER, that this shall not unreasonably interfere with Lessee's quiet use and + enjoyment of the Aircraft PROVIDED FURTHER, HOWEVER, that Lessor or the + Beneficiaries may conduct such visit and inspection at any time and with or + without notice if an Event of Default has occurred and is continuing. Lessor + and each Beneficiary shall have no duty to make any such inspection and shall + not incur any liability or obligation by reason of not making any such + inspection. Lessor's or any Beneficiary's failure to object to any condition + or procedure observed or observable in the course of an inspection hereunder + shall not be deemed to waive or modify any of the terms of this Lease with + respect to such condition or procedure. If such inspection is carried out + during a C Check or D Check, then Lessee will open any areas of the Aircraft + upon reasonable request of Lessor or a Beneficiary; PROVIDED, HOWEVER, that, + with respect to any C Check (other than the last C Check to occur prior to the + Return Occasion) the areas to be opened will be limited to those areas that are + in the zones of the Aircraft in which Lessee or its maintenance contractor are + working; provided further, however, that, with respect to the last C Check to + occur prior to the Return Occasion, Lessee will open any areas of the Aircraft + upon the reasonable request of Lessor or a Beneficiary. + + Section 8. COVENANTS OF LESSEE. + + Lessee covenants and agrees that: + + (i) MAINTENANCE OF CORPORATE EXISTENCE. Except as provided in + Section 8(iv) below, during the term of this Lease, Lessee will preserve and + maintain its corporate existence. Lessee shall also preserve and maintain its + rights, privileges, licenses and franchises in all jurisdictions where the + failure to do so would have a materially adversely effect upon the Lessee's + ability to meet its obligations under this Lease and the other Operative + Documents. + + (ii) MAINTENANCE OF STATUS. Lessee is, and shall remain so long + as it shall be Lessee under this Lease, duly qualified to operate the Aircraft + under applicable Law. + + (iii) PAYMENT OF TAXES. Lessee will pay or cause to be paid all + taxes, assessments and governmental charges + + -35- + + + + + + or levies imposed upon it, or upon its income or profits, or upon any property + belonging to it, prior to the date on which penalties attached thereto and + prior to the date on which any lawful claim, if not paid, would become a Lien + upon any of the material property of Lessee. + + (iv) CONSOLIDATION, MERGER, ETC. Without the prior written + consent of Lessor and each Beneficiary, Lessee shall not consolidate with, + merge with or merge into any other Person or convey, transfer or lease + substantially all of its assets as an entirety to any other Person unless, upon + and after giving effect to such transaction, (A) the surviving entity has at + least the same net worth and gross assets as the Lessee immediately prior to + such transaction, such surviving entity is Certified Air Carrier and a "citizen + of the United States" as defined in Section 101(16) of the Federal Aviation + Act, (C) Lessor shall continue to be entitled to the benefits of Section 1110 + of the United States Bankruptcy Code, as in effect from time to time, and (D) + each of the Operative Documents shall continue in full force and effect and + shall constitute the legally binding and enforceable obligation of such + surviving entity. + + (v) PLACE OF BUSINESS. Lessee will notify Lessor in writing + prior to any change of its principal place of business or chief executive + office. + + (vi) NOTICE OF DEFAULT. Promptly after a responsible officer of + Lessee obtains knowledge of an event or circumstance that such officer knows + constitutes a Default hereunder, Lessee shall notify Lessor in writing of such + Default. + + (vii) GOVERNMENTAL CONSENTS. Lessee undertakes to maintain in + full force and effect all governmental consents, licenses, authorizations, + approvals, declarations, filings and registrations obtained or effected in + connection with this Lease (other than those relating to the perfection of a + Lender's interest herein and subject to Section 5(b)(i) and Section 6; + provided, however, if so requested by a Beneficiary, Lessee shall cooperate + with the Beneficiary, at Beneficiary's cost, to perfect the interest of the + Lender) and every document or instrument contemplated hereby (including without + limitation foreign exchange and transfer permits regarding Dollar amounts due + hereunder and thereunder) and to take all such additional action as may be + proper or advisable in connection herewith or therewith. Lessee further + undertakes to obtain or effect any new or additional governmental consents, + licenses, authorizations, approvals, declarations, filings or registrations as + may become necessary for the performance of any of the terms and conditions of + this Lease or any other document or instrument contemplated hereby; PROVIDED, + HOWEVER, that Lessee shall + + -36- + + + + + + not be required to effect or maintain any governmental consent, authorization + or license that is required by a governmental authority in Delaware, Japan, + Ireland, New York or the United Kingdom unless required in connection with the + conduct of Lessee's business, the operation or maintenance by Lessee of the + Aircraft or the performance of Lessee's other obligations under this Lease. + + (viii) SUSPENSION, CESSATION, ETC. Lessee shall not (i) voluntarily + suspend all or substantially all of its certificated operations; or (ii) + voluntarily or involuntarily permit to be revoked, canceled or otherwise + terminated all or substantially all of the franchises, concessions, permits, + rights or privileges required for the conduct of business and operations of + Lessee or the free and continued use and exercise thereof. + + (ix) CERTIFIED AIR CARRIER. Lessee shall at all times remain a +Certified Air Carrier. + + Section 9. REPLACEMENT OF PARTS; ALTERATIONS, MODIFICATIONS + AND ADDITIONS. + + (a) REPLACEMENT OF PARTS. Lessee, at its own cost and + expense, will promptly replace all Parts which may from time to time become + worn out, lost, stolen, destroyed, seized, confiscated, damaged beyond repair + or permanently rendered unfit for use for any reason whatsoever. In addition, + in the ordinary course of maintenance, service, repair, overhaul or testing, + Lessee may at its own cost and expense cause to be removed any Parts, whether + or not worn out, destroyed, damaged beyond repair or permanently rendered unfit + for use, provided that Lessee shall replace at its own cost and expense such + Parts as promptly as practicable. All replacement Parts shall be free and clear + of all Liens, other than Liens permitted by Section 14 hereof, shall be in at + least the same modification status and service bulletin accomplishment status, + shall be fully interchangeable as to form, fit and function, shall have been + overhauled or repaired and inspected by an agency acceptable to the FAA and + shall be in as good an operating condition as, and have a utility at least + equal to and a value and remaining warranty reasonably approximating, the Parts + replaced (assuming such replaced Parts were in the condition and repair in + which they were required to be maintained by the terms hereof) and all + historical records since new or last overhaul relating to such Parts (and all + historical records since manufacture with respect to Engines, Landing Gears, + the APU and all life limited parts installed on any Engine, Landing Gear or + APU) shall be maintained by Lessee. + + -37- + + + + + + All Parts owned by Lessor which are at any time removed from + the Aircraft shall remain the property of Lessor and subject to this Lease, no + matter where located, until such time as such Parts shall be replaced by Parts + which have been incorporated or installed in or attached to the Aircraft and + which meet the requirements for replacement Parts specified above. Immediately + upon any replacement Part becoming incorporated or installed in or attached to + the Aircraft as above provided, (i) title to the removed Part shall thereupon + vest in Lessee, free and clear of all rights of Lessor, (ii) title to such + replacement Part shall thereupon vest solely in Lessor and (iii) such + replacement Part shall become subject to this Lease and be deemed a Part for + all purposes hereof to the same extent as the Part which it has replaced. + + (b) ALTERATIONS, MODIFICATIONS AND ADDITIONS. Lessee, at its + own expense, shall make such alterations and modifications and additions to the + Aircraft as may be required from time to time to meet the applicable standards + of the FAA or to comply with any Law, or, if performed by Lessee with respect + to a majority of its 737 fleet, any bulletin of the manufacturer of the + Aircraft, Engines or Parts. In addition, Lessee, at its own expense, may from + time to time make alterations and modifications in and additions to the + Aircraft, provided no such alteration, modification or addition diminishes the + remaining warranty, value or utility, or impairs the condition or + airworthiness, of the Aircraft. Title to all Parts incorporated or installed in + or attached or added to the Aircraft as the result of such alteration, + modification or addition shall vest immediately in Lessor and become subject to + this Lease, without the necessity for any further act of transfer, document or + notice. Notwithstanding the foregoing sentence of this Section 9(b), Lessor + agrees that so long as no Event of Default shall have occurred and be + continuing, Lessee may, at such time during the Term, remove any Part of the + Aircraft without replacement, provided that (i) such Part is in addition to, + and not in replacement or substitution for, any Part originally incorporated or + installed in or attached to the Aircraft at the time of delivery thereof + hereunder or any Part in replacement of, or substitution for, any such + originally incorporated or installed or attached Part, (ii) such Part is not + required to be incorporated or installed in or attached or added to the + Aircraft pursuant to the terms of this Section 9 and (iii) such Part can be + removed from the Aircraft without diminishing or impairing the value, utility, + condition, cosmetic appearance or airworthiness which the Aircraft would have + had at such time had such alteration, modification or addition not occurred. + Upon the removal by Lessee of any such Part as provided in the preceding + sentence, title thereto shall, without further act, vest in Lessee and such + Part shall no longer be deemed part + + -38- + + + + + + of the Aircraft (such a part is herein called a "Removable Part"). Lessee, at + its sole expense, shall repair any damage to the Aircraft resulting from any + such removal. Any Part not removed by Lessee as above provided prior to the + return of the Aircraft to Lessor hereunder whether pursuant to Section 16, + Section 18 or otherwise shall remain the property of Lessor. If any Removable + Part is (i) owned by any third party and leased to Lessee, (ii) sold to Lessee + subject to a conditional sales contract or other security interest or (iii) + leased to Lessee pursuant to a lease which is subject to a security interest in + favor of any third party, then Lessor will not acquire or claim, as against + such lessor, conditional vendor or secured party, any right, title or interest + in any such Removable Part as the result of such Removable Part being installed + in the Aircraft; provided, however, (a) that Lessor's inability to so acquire + or claim is subject to the express condition that such lessor, conditional + vendor or secured party shall not acquire or claim, as against Lessor, any + right, title or interest in the Aircraft, or any Part other than its interest + in such Removable Part by reason of such Removable Part being installed + thereon, and (b) that all of Lessee's right, title and interest in and to any + Removable Part not removed by Lessee within sixty (60) days after an Event of + Default shall, at such time, automatically become the property of Lessor and be + subject to this Lease. + + In addition, with the prior written consent of the + Beneficiaries, Lessee may, at its own expense, substitute or replace any Part + that is originally installed on the Aircraft on the Delivery Date with another + Part provided that (i) no such substitution or replacement diminishes the + remaining warranty, value or utility, or impairs the condition or + airworthiness, of the Aircraft, (ii) title to all Parts incorporated or + installed in or attached or added to the Aircraft as the result of such + substitution or replacement shall vest immediately in Lessor free and clear of + Liens, other than those permitted by Section 14, and become subject to this + Lease, without the necessity for any further act of transfer, document or + notice, (iii) such substituted or replacement Parts can be removed from the + Aircraft without diminishing or impairing the value, utility, condition, + cosmetic appearance or airworthiness which the Aircraft would have had at such + time had such substitution or replacement not occurred, (iv) Lessee, at its + sole expense, shall repair any damage to the Aircraft resulting from any such + removal, and (v) nothing herein shall diminish or alter any of Lessee's + obligations under Section 16 or Exhibit E hereof to return the Aircraft on the + Return Occasion with all Parts (or substitution or replacement Parts of the + same make and model) that were originally installed on the Aircraft in the + condition required thereby (and including within this clause (v) + + -39- + + + + + + Lessee's obligation by the Return Occasion to re-install any Part for which a + substitution or replacement Part was installed on the Aircraft pursuant to this + paragraph). + + In no event shall Lessor bear any liability or cost for any + alteration, modification or addition to, or for any grounding or suspension of + certification of, the Aircraft, or for any loss of revenue arising therefrom. + Lessee shall make no material alterations, modifications or additions to the + Aircraft (such as removal of seats, galleys, lavatories, major avionics + equipment or the like) that would affect the marketability of the Aircraft + without Lessor's and each Beneficiary's prior written consent. if Lessor and + each Beneficiary grant such consent, title to such removed Parts shall remain + with Lessor and Lessor and the Beneficiaries may request Lessee to reinstall + such Parts prior to termination of this Lease. If Lessor or Beneficiaries + request Lessee to reinstall such Parts, title to the Parts removed shall vest + in Lessee. All costs associated with such removal and reinstallation shall be + borne by Lessee. + + Section 10. GENERAL TAX INDEMNITY. + + (a) CERTAIN UNITED STATES TAXES. Lessee shall indemnify and + hold each Indemnitee harmless from and against all United States Withholding + Taxes (as defined herein) and United States Transportation Taxes (as defined + herein) which are imposed on such Indemnitee after the date hereof as a result + of a Tax Law Change (as defined herein). All payments required to be made by + Lessee under this Lease or any other Operative Document to any Indemnitee shall + be made free and clear of, and without deduction for or on account of, any such + United States Withholding Taxes or United States Transportation Taxes. If any + such United States Withholding Taxes or United States Transportation Taxes are + required to be withheld or deducted from any such payments, Lessee shall (i) + within the period for payment permitted by applicable law pay to the + appropriate Governmental Entity or taxing authority the full amount of such + United States Withholding Taxes or United States Transportation Taxes (and any + such additional United States Withholding Taxes or United States Transportation + Taxes in respect of the payment required under clause (ii) hereof) and make + such reports and filings in connection therewith in the manner required by + applicable Law, and (ii) pay to the relevant Indemnitee an additional amount + which (after deduction of all Taxes of any nature incurred by reason of the + payment or receipt of such additional amount) will be sufficient to yield to + the relevant Indemnitee the full amount which would have been received by it + had no deduction or withholding been made. Lessor will, and will cause each + Indemnitee to, provide Lessee with such forms or certificates as may from time + to + + -40- + + + + + + time be requested by Lessee (and required by applicable law) in order to + establish an exemption from (or reduced rate of) any such United States + Withholding Taxes or United States Transportation Taxes. As used herein, (x) + the term "TAX LAW CHANGE" means any change after the date hereof in the Code or + any treaty or other international agreement to which the United States is a + party, any proposed, final or temporary regulations thereunder, or the judicial + or official administrative interpretation of any of the foregoing; (y) the term + "UNITED STATES TRANSPORTATION TAXES" means the Taxes described in Section 887 + of the Code or any successor to such Section; and (z) the term "UNITED STATES + WITHHOLDING TAXES" means Taxes imposed by the United States and which are + collected by withholding at source, or deduction from, any payments by Lessee + hereunder. Notwithstanding the foregoing, Lessee shall have no responsibility + for, and shall be entitled to withhold and deduct from payments hereunder or + any other Operative Documents, any such United States Withholding Taxes or + United States Transportation Taxes: + + (A) which are imposed by reason of a voluntary transfer or + other disposition by the relevant Indemnitee of the Aircraft or this + Lease or any interest in either thereof (but not including any transfer + or disposition which occurs pursuant to the exercise of remedies arising + out of an Event of Default) or which exceed the amount of such United + States Withholding Taxes or United States Transportation Taxes which + would have been imposed had there not been such a transfer or + disposition; + + (B) which are imposed on an Indemnitee by reason of a failure + by such Indemnitee to comply with its obligations set forth in the second + preceding sentence of this Section 10(a) or as a direct result of the + breach by such Indemnitee of this Lease or the gross negligence or + willful misconduct of such Indemnitee; + + (C) which are imposed solely as a direct result of events or + circumstances occurring or arising after return of the Aircraft to Lessor + in accordance with this Lease and the performance by Lessee of all of its + obligations hereunder; or + + (D) which the relevant Indemnitee is entitled to utilize as a + credit against or in reduction of its liability, if any, for Taxes + imposed by the United States for which Lessee is not responsible under + Section 10(b) hereof. + + (b) GENERAL TAX INDEMNITY. In addition, and whether or not + any of the transactions contemplated by any of the Operative Documents + are consummated, except as set + + -41- + + + + + + forth in Section 10(c), Lessee agrees for the express benefit of each + Indemnitee to pay promptly when due, and to indemnify and hold harmless such + Indemnitee from, all Taxes (whether imposed upon such Indemnitee, the Aircraft, + the Airframe, the Engines, this Lease or any other Operative Document or + otherwise), by any Governmental Entity or taxing authority in the United States + of America or any other country or any taxing authority or political + subdivision thereof or therein or by any international taxing authority, upon + or with respect to, based upon or measured by any of the following: + + (i) (x) the Aircraft, the Airframe, any Engine or any Part thereof, or + interest therein, this Lease or any of the other Operative Documents, (y) the + importation, exportation, condition, manufacture, purchase (under the + Operative Documents or otherwise), ownership, delivery, redelivery, + nondelivery, acceptance, rejection, assignment, possession, repossession, + return, use, performance, operation, control, settlement of any insurance or + other claim, sale, mortgaging, pledging, financing, leasing, subleasing, liens, + rental, retirement, abandonment, registration, preparation, installation, + modification, repair, testing, maintenance, replacement, transportation, + storage, location, condition, registration, reregistration, deregistration, + sale, transfer of title or other application or disposition of the Aircraft, + the Airframe, any Engine or any Part thereof or (z) the rentals, receipts or + earnings arising therefrom (including without limitation the Rent) and any + other amounts paid or payable with respect thereto or under any Operative + Document, or + + (ii) the Lease or the other Operative Documents, or + + (iii) otherwise with respect to or in connection with the transactions + contemplated by the Lease and the other Operative Documents. + + (c) EXCEPTIONS TO INDEMNITY. The indemnity provided for in + Section 10(b) does not extend to any of the following Taxes: + + (i) Taxes imposed on any Indemnitee in any jurisdiction in which such + Indemnitee is subject to such Taxes solely as a result of transactions or + activities unrelated to the transactions or activities contemplated by the + Operative Documents; + + -42- + + + + + + (ii) Taxes imposed on an Indemnitee as a result of a voluntary + transfer or other disposition of the Aircraft or this Lease or any interest in + either thereof by the Lessor or such Indemnitee unless such transfer or + disposition occurs pursuant to the exercise of remedies arising out of an Event + of Default (provided that Lessor and such Indemnitee shall in such event + consider in good faith any reasonable request by Lessee concerning an + appropriate jurisdiction in which such sale, transfer or disposition shall be + made); + + (iii) Taxes attributable solely to events or circumstances occurring + or arising after return of the Aircraft to Lessor in accordance with this Lease + and the performance by Lessee of all of its obligations hereunder; + + (iv) Taxes on an Indemnitee resulting solely as a direct result of the + breach of this Lease, gross negligence or willful misconduct of such + Indemnitee; + + (v) Taxes which (A) are United States federal income Taxes (other than + United States Withholding Taxes or United States Transportation Taxes) or any + value added, consumption or similar Taxes which are imposed in lieu of or in + replacement of United States federal income Taxes to which the relevant + Indemnitee was theretofore subject or (B) are Taxes on, based on, or measured + by the net income of an Indemnitee and are imposed by any Governmental Entity + (other than the United States) or taxing authority thereof (or any Taxes which + are expressly imposed by such Governmental Entity or taxing authority in lieu + of Taxes on, based on, or measured by net income) except to the extent that + such Taxes are imposed by reason of the use or operation of the Aircraft, the + presence of Lessee or the making of payments by Lessee from, such jurisdiction; + PROVIDED, HOWEVER, that this exclusion shall not apply to Taxes which are, or + are in the nature of, sales, use, excise and similar Taxes; + + (vi) Taxes (other than Taxes described in Section 10(c)(v)) which are + imposed in any taxing jurisdiction except to the extent that (A) such Taxes are + imposed solely by reason of the use or operation of the Aircraft, the presence + of Lessee or the making of payments by Lessee from, such jurisdiction ("Use + Jurisdiction Taxes") and (B) such Use Jurisdiction Taxes exceed in the + aggregate the amount of Taxes which would have been payable in such + jurisdiction in the absence therein of the use or operation of the Aircraft, + the presence of Lessee or the making of payments by Lessee from, such + jurisdiction; + + -43- + + + + + + (vii) Taxes arising out of, with respect to, based on or measured by + the granting by any Indemnitee of a security interest in the Aircraft or any + portion thereof or the value or principal amount of any debt created by any + Indemnitee; or + + (viii) Taxes for which the relevant Indemnitee has expressly assumed + responsibility under this Lease or any other Operative Document. + + (d) AFTER TAX BASIS. The amount which Lessee is required to + pay or indemnify against with respect to any amounts required to be paid or + indemnified against under this Section 10 or Section 13 shall include an + additional amount necessary to hold the recipient of the payment or indemnity + harmless on an after-tax basis from all Taxes (whether or not such taxes are + excluded under Section 10(a) or 10(c)), required to be paid by such recipient + with respect to such payment or indemnity, so as to restore the recipient on an + after-tax basis to the same position such recipient would have been in had such + amounts not been incurred or payable, after giving credit to Lessee for any + savings by such recipient in respect to any such Taxes by reason of deductions, + credits, allocations, or allowances in respect of the payment or accrual of the + amount indemnified against, including, without limitation, amounts described in + Section 10(a), Section 10(b) and/or this Section 10(d). All determinations as + to the amount and timing of realization of such tax savings shall be made by + the relevant Indemnitee in its sole good faith discretion, it being understood + that (i) Lessee shall have no right to examine or inspect any tax returns or + reports of such Indemnitee or any documents or records relating thereto, and + that (ii) a failure by the relevant Indemnitee to comply with its obligations + under this Section 10(d) shall not excuse Lessee from the performance of its + obligations under this Section 10 or Section 13. + + (e) TIMING OF PAYMENT. Any amount due and payable to the + relevant Indemnitee pursuant to this Section 10 will be paid within 10 days + after receipt of a written demand therefor from such Indemnitee accompanied by + a written statement describing in reasonable detail the basis for such + indemnity and the computation of the amount so payable; PROVIDED, HOWEVER, that + such amount need not be paid by Lessee prior to the later of (i) five days + prior to the date the applicable Tax is payable to the appropriate Governmental + Entity or taxing authority or (ii) in the case of amounts which are being + contested by Lessee in good faith or by Lessor pursuant to Section 10(f), the + date such contest is finally resolved. If requested in writing by Lessee, and + at Lessee's sole cost and expense, any calculations by an Indemnitee of any + amount due and payable + + -44- + + + + + + hereunder shall be subject to review and verification by a firm of independent + certified public accounts of internationally recognized stature selected by + such Indemnitee and reasonably acceptable to Lessee (such approval not to be + unreasonably withheld or delayed). Such Indemnitee shall make available to such + accounting firm such information as shall be necessary for purposes of such + review and verification (but such information shall be held by such accounting + firm in strictest confidence and shall not in any event be disclosed or made + available to Lessee). If the result of such review is that Lessee was liable + for a smaller amount, the excess payment shall be returned by such Indemnitee + forthwith. + + (f) TAX CONTESTS. (i) If written claim is made against an + Indemnitee for, or any Indemnitee acquires actual knowledge of a claim relating + to, Taxes with respect to which Lessee is or may be liable for a payment or + indemnity hereunder, such Indemnitee will, and Lessor shall cause such + Indemnitee to, promptly give Lessee notice in writing of such claim or Tax; + PROVIDED, HOWEVER, that such Indemnitee's failure to give notice will not + relieve Lessee of its obligations hereunder, except as to Taxes and expenses + which would have been avoidable in the absence of, and penalties resulting + from, such Indemnitee's failure to give such notice. So long as (i) a contest + of such Taxes does not involve any danger of the sale, forfeiture or loss of + the Aircraft or any interest therein, (ii) Lessee has provided Lessor with an + opinion of independent tax counsel acceptable to such Indemnitee that a + meritorious basis exists for contesting such claim and (iii) Lessee has made + adequate reserves for such Taxes or, if required by the relevant Indemnitee, an + adequate bond has been posted by Lessee, then the Indemnitee at Lessee's + written request will in good faith, with due diligence and at Lessee's sole + cost and expense, contest (or, upon Lessee's written request, permit Lessee to + contest in the name of the Indemnitee) the validity, applicability or amount of + such Taxes. If such contest is to be initiated by the payment of, and the + claiming of a refund for, any Taxes, Lessee shall advance to the relevant + Indemnitee sufficient funds (on an interest-free basis) to make such payments + and shall have agreed to indemnify such Indemnitee for any tax consequences + resulting from such advance of funds. Although the relevant Indemnitee may + consult in good faith with Lessee concerning the conduct of any contest, such + Indemnitee shall control the conduct of all proceedings relating to any such + contest which is brought by or on behalf of such Indemnitee. Any contest + initiated hereunder may be settled or discontinued by the relevant Indemnitee + at any time provided that such Indemnitee shall have waived any right to + indemnification for the Taxes being contested. Notwithstanding the foregoing, + if (x) a claim relates solely to Taxes for which + + -45- + + + + + + the Lessee has acknowledged in writing that it is liable under this Section 10, + (y) such claim can be severed and contested separately from all other claims + asserted against the relevant Indemnitee by the same taxing authority, and (z) + Lessee has made adequate reserves for the Taxes claimed or, if requested by + such Indemnitee, has posted an adequate bond for the payment of such Taxes, + then Lessee may, at its sole cost and expense, assume control of the contest of + such claim, provided that Lessee shall keep such Indemnitee fully informed as + to the conduct of all proceedings relating to such contest. + + (g) REFUNDS. Upon receipt by the relevant Indemnitee of a + refund of all or any part of any Taxes (including without limitation any + deductions or withholdings referred to in Section 10(a)) which Lessee has paid, + such Indemnitee will, and Lessor will cause such Indemnitee to, pay to Lessee + the net amount of such taxes refunded, together with any interest (and, to the + extent previously paid by Lessee, any attorneys' fees or other costs awarded as + a result of a contest pursuant to Section 10(f)) received by such Indemnitee + with respect thereto; PROVIDED, HOWEVER, that no amount shall be payable to the + Lessee under this Section 10(g) if a Default or an Event of Default shall have + occurred and be continuing or prior to the time that Lessee shall have paid to + the relevant Indemnitee all amounts then due and owing to such Indemnitee under + this Section 10. + + (h) COOPERATION IN FILING TAX RETURNS. In case any report or + return is required with respect to any Taxes which are subject to + indemnification by Lessee under this Section 10, Lessee will either make such + report or return in such manner as will show the respective interests of Lessor + and/or each relevant Indemnitee in the Aircraft, and send a copy of such report + or return to Lessor and such Indemnitee or will notify the Lessor or such + Indemnitee of such requirement, prepare such report or return in such manner as + shall be reasonably satisfactory to the Lessor or such Indemnitee, and present + such report or return to Lessor or such Indemnitee for execution and proper + filing. If actual notice is given by any taxing authority to an Indemnitee that + a report or return is required to be filed with respect to any such Taxes + referred to in this Section 10, the Indemnitee shall, and Lessor shall cause + the Indemnitee to, promptly notify Lessee of such required report or return. + Each Indemnitee agrees, and Lessor shall cause each Indemnitee, to respond to + any reasonable request of Lessee for information within the control of such + Indemnitee with respect to the filing of any such report or return, but Lessee + agrees to pay any reasonable costs, fees or other charges of independent + counsel or independent accountants incurred in connection with such response or + request. Lessee will cooperate with each Indemnitee in providing + + -46- + + + + + + information which may reasonably be required to fulfill such Indemnitee's tax + filing requirements and any audit information request arising from such filing. + Each Indemnitee shall, and Lessor shall cause each Indemnitee to, cooperate + with Lessee in providing information which may reasonably be required to + fulfill Lessee's tax filing requirements and any audit information request + arising from such filing. + + (i) TAX SAVINGS. If as a result of the payment or accrual of + Taxes paid or indemnified by the Lessee an Indemnitee shall realize and + recognize any savings with respect to Taxes, such Indemnitee shall promptly + notify the Lessee that it has realized and recognized such savings and shall + pay to the Lessee within thirty (30) days of the realization of such savings an + amount equal to (i) the net reduction in Taxes realized and recognized by such + Indemnitee and (ii) the amount of any further net reduction in taxes realized + and recognized under such tax laws as a result of payments pursuant to this + sentence; PROVIDED, HOWEVER, that no amount shall be payable to the Lessee + under this Section 10(i) if a Default or an Event of Default shall have + occurred and be continuing or prior to the time that Lessee shall have paid to + the relevant Indemnitee all amounts then due and owing to such Indemnitee under + this Section 10 and that a subsequent disallowance of any such tax savings + shall be subject to indemnity hereunder without regard to any exclusions set + forth in this Section 10. All determinations as to the amount and timing of + realization of such tax savings shall be made by the relevant Indemnitee in its + sole good faith discretion, it being understood that (i) Lessee shall have no + right to examine or inspect any tax returns or reports of such Indemnitee + or any documents or records relating thereto, and that (ii) a failure by the + relevant Indemnitee to comply with its obligations under this Section 10(i) + shall not excuse Lessee from the performance of its obligations under this + Section 10. + + (j) REVERSE INDEMNITY. If Lessee shall be required to pay any + amount attributable to Taxes of any Indemnitee for which Lessee is not + responsible under this Section 10, such Indemnitee shall reimburse Lessee for + the amount of any such excess payment within 5 days after demand therefor. + + Section 11. CASUALTY OCCURRENCES. + + (a) CASUALTY OCCURRENCE WITH RESPECT TO THE AIRFRAME. + Promptly (and in any event within seven (7) days) after a Casualty Occurrence + with respect to the Airframe and any Engine then installed thereon, Lessee + shall give Lessor and each Beneficiary written notice of such occurrence. On + the thirtieth (30th) day (or earlier if the insurance or + + -47- + + + + + + other proceeds have been paid earlier) after the date of the Casualty + Occurrence, Lessee shall pay to Lessor in immediately available funds the + sum of (A) the Casualty Value of the Aircraft less an amount equal to the + daily equivalent of Basic Rent (computed on the basis of a 365-day year) + for each day during the period commencing the day after the payment of + Casualty Value and extending to the day before the Basic Rent Payment Date + immediately succeeding the date of payment of such Casualty Value and (B) + all other Rent, other than amounts paid pursuant to clause (A), then due + and payable. Upon such payment (i) the obligation of Lessee to make further + payments of Basic Rent hereunder shall terminate, (ii) the Term shall + terminate with respect to the Aircraft and (iii) Lessor will transfer to + Lessee or applicable insurer, free and clear of Lessor's Liens and all other + Liens arising by or through Lessor, any Beneficiary or any Lender but + otherwise without recourse or warranty, all of Lessor's right, title and + interest, if any, in and to the Airframe and Engines (if any) suffering the + Casualty Occurrence, as well as all of Lessor's right, title and interest in + and to any Engine constituting part of the Aircraft but not installed + thereon at the time of the Casualty Occurrence; provided, however, that + there shall be excluded from such transfer any and all claims against any + Persons which arose prior to the date of such transfer, including without + limitation any and all claims against any Persons who may have been + responsible, in whole or in part, for the events giving rise to such + Casualty Occurrence. + + (b) CASUALTY OCCURRENCE WITH RESPECT TO AN ENGINE. Upon a + Casualty Occurrence with respect to an Engine only, Lessee shall give Lessor + and each Beneficiary prompt written notice thereof and shall, within forty-five + (45) days after such occurrence, convey to Lessor, as replacement for the + Engine suffering a Casualty Occurrence, title to a Replacement Engine; + PROVIDED, HOWEVER, that such 45-day period shall be extended to 180 days after + the occurrence of such Casualty Occurrence if Lessee shall, prior to the end of + such 45-day period, have delivered to Lessor or the Beneficiaries all proceeds + of the insurance payable with respect to such damaged Engine and the amount of + such proceeds is at least equal to the replacement value thereof (as determined + reasonably and in good faith by the Beneficiaries). Each Replacement Engine + shall be (i) free of all Liens, (ii) in as good an operating condition as the + Engine being replaced, assuming the Engine being replaced was in the condition + and repair required by the terms hereof immediately prior to the Casualty + Occurrence, and (iii) in a condition (including as to the status of all logs, + records and technical documents) acceptable to the Beneficiaries in their sole + discretion, as evidenced by their written acknowledgement thereof. Prior to or + at the time of any such conveyance, Lessee, at its own expense, will promptly + + -48- + + + + + + (i) furnish Lessor with a full warranty bill of sale, in form and substance + reasonably satisfactory to Lessor and each Beneficiary, with respect to such + Replacement Engine; (ii) cause a supplement hereto, in form and substance + reasonably satisfactory to Lessor and each Beneficiary, subjecting such + Replacement Engine to this Lease, to be duly executed by Lessee, and recorded + pursuant to applicable Law; (iii) furnish Lessor and each Beneficiary with such + evidence of title to such Replacement Engine and of compliance with the + insurance provisions of Section 12 hereof with respect to such Replacement + Engine as Lessor or such Beneficiary may reasonably request; (iv) if reasonably + requested by Lessor or any Beneficiary, furnish Lessor with an opinion of + Lessee's counsel to the effect that title to such Replacement Engine has been + duly conveyed to Lessor and each Beneficiary, and that such Replacement Engine + is duly leased hereunder; (vi) furnish a certificate signed by a duly + authorized financial officer or executive of Lessee certifying that, upon + consummation of such replacement, no Default will exist hereunder; (vii) + furnish Lessor and each Beneficiary with such documents (including but not + limited to all technical documents) as Lessor or such Beneficiary may + reasonably request in connection with the consummation of the transactions + contemplated by this Section 11(b), in each case in form and substance + satisfactory to Lessor and such Beneficiary; and (viii) furnish such financing + statement covering the Replacement Engine as may be requested by Lessor or any + Beneficiary. Upon full compliance by Lessee with the terms of this Section + 11(b), Lessor will transfer to Lessee all of Lessor's right, title and interest + in the Engine which suffered the Casualty Occurrence free and clear of Lessor's + Liens and all other Liens arising by or through Lessor, any Beneficiary or any + Lender. For all purposes hereof, each such Replacement Engine shall be deemed + part of the property leased hereunder, shall be deemed an "Engine" as defined + herein and shall be deemed part of the Aircraft. No Casualty Occurrence covered + by this Section 11(b) shall result in any reduction in Rent. + + (c) APPLICATION OF PROCEEDS AND PAYMENTS. Any payments + received at any time by Lessor or by Lessee from any insurer under any policy + of insurance (other than liability insurance) shall be applied in the manner + specified in Section 12(d), 12(e) or 12(f) hereof as applicable. Subject to + Section 11(f) hereof, any payments received at any time by Lessor or Lessee + from any Governmental Entity or other Person with respect to a Casualty + Occurrence will be applied as follows: + + (i) unless clause (ii) below is applicable, so much of such + payments as shall not exceed the Casualty Value required to be paid by + Lessee + + -49- + + + + + + pursuant to Section 11(a) of this Lease shall be paid to Lessor in + reduction of Lessee's obligation to pay such Casualty Value if not + already paid by Lessee or, if already paid by Lessee (unless a Default + shall have occurred and be continuing), shall be applied by Lessor to + reimburse Lessee for its payment of such Casualty Value and the balance + of such payment, if any, remaining thereafter (if such payment is + received with respect to insurance other than liability insurance) + (unless a Default shall have occurred and be continuing) shall be paid + over to, or retained by, Lessee, except to the extent any such amount + is specifically allocable to an interest of Lessor; or + + (ii) if such payments are received as a result of a Casualty + Occurrence with respect to an Engine which is being replaced pursuant + to Section 11(b), unless a Default shall have occurred and be + continuing (and except as otherwise contemplated by Section 11(b) if + the 45-day period specified for the replacement thereof is to be + extended to 180 days, as therein provided), all such payments shall be + paid over to, or retained by, Lessee if Lessee shall have fully + performed or, concurrently therewith has fully performed, the terms of + Section 11(b) and of Section 15 hereof with respect to the Casualty + Occurrence for which such payments are made. + + (d) REQUISITION FOR USE BY GOVERNMENT WITH RESPECT TO THE + AIRCRAFT. In the event of the requisition for use by a Governmental Entity of + the Airframe or any Engine (other than a requisition constituting a Casualty + Occurrence), all Lessee's obligations under this Lease with respect to the + Airframe or Engine shall continue to the same extent as if such requisition had + not occurred. All payments received by Lessor or Lessee from the Governmental + Entity for the use of the Airframe or Engine during the Term therefor shall be + paid over to, or retained by, Lessee if no Default shall have occurred and be + continuing; and all payments received by Lessor or Lessee from the Governmental + Entity for the use of such item after the Term therefor shall be paid over to, + or retained by, Lessor. + + (e) OTHER DISPOSITIONS. Any amounts not payable to or + retainable by Lessee pursuant to this Section 11 or Section 12 hereof because a + Default shall have occurred and be continuing shall be held by Lessor and shall + be paid over to Lessee when such Default shall cease to be continuing, except + that if Lessor shall have theretofore declared this Lease to be in default + pursuant to Section 18 hereof, such amounts shall be retained by Lessor and + disposed of in + + -50- + + + + + + accordance with the provisions thereof, with any balance remaining to be paid + to Lessee. + + (f) APPLICATION IN DEFAULT. Any amount referred to in clause + (i) or (ii) of Section 11(c) which is otherwise payable to Lessee shall not be + paid to Lessee, or, if it has been previously paid to Lessee, and not yet + applied by Lessee as permitted or required hereunder shall be delivered from + Lessee to Lessor if at the time of such payment a Default shall have occurred + and be continuing. In such case, all such amounts shall be paid to and held by + Lessor as security for the obligations of Lessee or, at the option of Lessor, + applied by Lessor toward payment of any of Lessee's obligations at the time due + hereunder, as Lessor may elect. At such time as there shall not be continuing + any such Default, all such amounts at the time held by Lessor in excess of the + amount, if any, which Lessor has elected for application as provided above + shall be paid to Lessee. + + Section 12. INSURANCE. + + (a) PUBLIC LIABILITY AND PROPERTY DAMAGE INSURANCE. Lessee + will carry and maintain in effect, at its own expense, with Approved Insurers, + comprehensive public liability insurance (including, without limitation, + contractual liability and passenger legal liability) and property damage + insurance with respect to the Aircraft of the type usual and customary by + commercial scheduled airline standards for airline carriers operating similar + aircraft and providing for no less coverage than is carried by Lessee on + similar aircraft in its fleet. Such insurance shall be in an amount not less + than the amount applicable to similar passenger aircraft and engines which + comprise Lessee's fleet, and in any event not less than the amount under + "Public Liability and Property Damage Insurance" as set forth on Exhibit C-2 + hereto Lessee shall not discriminate against the Aircraft in providing such + insurance. + + (b) INSURANCE AGAINST LOSS OR DAMAGE. Lessee, at its own + expense, will maintain in effect with Approved Insurers "all-risk" ground and + flight aircraft hull insurance (which shall include, but not be limited to, + comprehensive war risk and allied perils, hijacking, a disappearance clause and + coverage against strikes, riots, commotions or labor disturbances, malicious + acts or acts of sabotage and unlawful seizure (including confiscation, arrest, + nationalization, seizure, restraint, detention, appropriation, requisition or + destruction thereat, by or under authority of any Governmental Entity), or + wrongful exercise of control of the Aircraft in flight by a person on board the + Aircraft acting without the consent of Lessee) covering the Aircraft and "all + risk" coverage insurance with + + -51- + + + + + + respect to Engines and Parts while not installed on the Aircraft or an + aircraft, which in each case is at least as broad as coverage maintained by + passenger airlines similarly situated to Lessee and operating similar aircraft + and engines which comprise Lessee's fleet (it being understood that the + obligations hereby imposed upon Lessee by reference to coverage maintained by + other airlines is not intended to result in an increase in the definition of + Casualty Value). Such insurance shall be for an amount not less than the + Casualty Value for the Aircraft and, where "all-risk" hull and war risk are + insured separately, shall, in relation to the applicable policies, incorporate + a 50/50 clause based on AVS 103, between the underwriters. Such insurance may + include provisions for deductibles in an amount usual and customary by + commercial scheduled airline standards for airline carriers operating similar + aircraft provided that (i) the amount of such deductibles must be no greater + than the lowest deductible amount applying to any B737-25A aircraft in Lessee's + fleet and (ii) in no event shall the amount of such deductibles exceed the + amount under "Deductible Amount" set forth on Exhibit C-2 hereto. + + (c) REQUIRED POLICY DESIGNATIONS AND PROVISIONS. Each and any + policy of insurance obtained and maintained pursuant to this Section, and each + and any policy obtained in substitution or replacement for any such policies, + shall: (i) designate Lessor as owner of the Aircraft covered thereby, and shall + designate Lessor (in its individual capacity and as Owner Trustee), each + Beneficiary and their assigns as additional insureds (and, with respect to each + policy of insurance obtained and maintained pursuant to Section 12(b) hereof, + Beneficiaries as loss payee), as their interests may appear warranted each as + to itself no operational interest (but without imposing upon Lessor, any + Beneficiary or their assigns, any obligation to pay any premiums for any such + policies); (ii) expressly provide that, in respect of the interests of Lessor, + each Beneficiary and their assigns, in such policies, the insurance shall not + be invalidated by any action or omission of Lessee, and shall insure Lessor, + such Beneficiary and their assigns, regardless of any breach or violation of + any warranty, declaration or condition contained in such policies by Lessee; + (iii) provide that if such insurance is canceled by the Approved Insurers for + any reason whatsoever, or is adversely changed in any way with respect to the + interests of Lessor, any Beneficiary or their assigns, or if such insurance is + allowed to lapse for nonpayment of premium, such cancellation, adverse change + or lapse shall not be effective as to Lessor, such Beneficiary and their + assigns, for thirty (30) days (seven (7) days or such lesser period as may be + applicable in the case of any war risks or allied perils coverage) after + receipt by Lessor and such Beneficiary of written notice by such insurer or + insurers to + + -52- + + + + + + Lessor and such Beneficiary of such prospective cancellation, change or lapse, + (iv) include coverage for the territorial limits of any country in which the + Aircraft may at any time be located; (v) provide that, as against Lessor, each + Beneficiary and their assigns, the insurer waives any rights of set-off, + counterclaim or any other deduction, whether by attachment or otherwise, and + waives any rights it may have to be subrogated to any right of any insured + against Lessor, such Beneficiary or their assigns, with respect to the Aircraft + to the same extent that Lessee has waived its rights of subrogation by the + terms of its agreements to indemnify any such party pursuant to this Lease; and + (vi) provide that (a) unless Lessor or any Beneficiary shall provide written + notice to the Approved Insurers that an Event of Default shall have occurred + and be continuing, any payment in respect of any damage or loss not in excess + of $500,000 shall be payable directly to the Lessee or its order; PROVIDED, + that Lessor and each Beneficiary shall be given prompt written notice of any + payment in respect of any damage or loss to the Aircraft in excess of $150,000 + and (b) any payment in respect of any damage or loss in excess of $500,000 (or + all proceeds whether or not in excess of $500,000 if an Event of Default shall + have occurred and be continuing), shall be payable directly to the + Beneficiaries or their assignees as loss payees, for the account of all + interests. Each such liability policy shall be primary without right of + contribution from any other insurance which may be carried by Lessor, any + Beneficiary or their assigns, and shall expressly provide that all of the + provisions thereof shall operate in the same manner as if there were a separate + policy covering each insured, provided that such provisions shall not operate + to increase the insurer's limit of liability. + + Lessee shall have the right to carry insurance in reasonable + excess of the amounts required hereunder and the proceeds of such excess + insurance shall be payable to Lessee; PROVIDED that any such excess insurance + shall not prejudice the insurance required to be maintained by Lessee hereunder + and Lessee shall, upon request of a Beneficiary, notify the Beneficiaries of + the nature and amount of such excess insurance. Similarly, Lessor and each + Beneficiary shall have the right to carry additional and separate insurance for + its own benefit at its own expense, without, however, thereby limiting Lessee's + obligations under this Section 12. + + (d) APPLICATION OF INSURANCE PROCEEDS FOR A CASUALTY + OCCURRENCE. It is agreed that insurance payments which arise from any policy of + insurance carried by Lessee and received as the result of the occurrence of a + Casualty Occurrence shall be applied as follows: + + -53- + + + + + + (i) if such payments are received with respect to a Casualty + Occurrence relating to the Airframe and Engines or engines installed on + the Airframe, so much of such payments as shall not exceed the amounts + due under Section 11(a) hereof shall be paid to Lessor for immediate + application toward sums owed by Lessee, and the balance to Lessee; and + + (ii) if such payments are received with respect to a Casualty + Occurrence relating to an Engine under circumstances contemplated by + Section 11(b) hereof, such payment shall be paid over to Lessee, + provided that Lessee shall have fully performed or, concurrently + therewith, fully performs the terms of Section 11(b) hereof. + + (e) APPLICATION OF INSURANCE PROCEEDS FOR OTHER THAN A +CASUALTY OCCURRENCE. As between Lessor and Lessee all insurance payments in +excess of $500,000 that are received by Lessor or the Beneficiaries under +policies required to be maintained by Lessee pursuant to Section 12(b) as a +result of any property damage or loss to the Airframe or any Engine not +constituting a Casualty Occurrence, or to any Part will be held by Lessor or +the Beneficiaries until Lessee furnishes Lessor with satisfactory evidence +that the repairs or replacement property Lessee is required to perform or +obtain in accordance with the terms of Section 9 of this Lease have been made +or obtained by Lessee or, to the extent necessary to enable Lessee to make +required progress payments in accordance with the workscope relating to the +repair of the Aircraft, Lessor or the Beneficiaries shall make such interim +progress payments upon receipt of copies of appropriate invoices or other +evidence of the completion of the related work or the purchasing of related +materials in accordance with the terms of such workscope or the related +repair contract. Upon receipt of such evidence of repair or replacement, +Lessor shall pay Lessee the amount of the insurance payment received with +respect to such loss less the amount of any progress payments made to Lessee +pursuant to this Section 12(e). + + (f) APPLICATION IN DEFAULT. Any amount referred to in +Section 12(d)(i) or (ii) or Section 12(e) which is otherwise payable to +Lessee shall not be paid to Lessee, or, if it has been previously paid to +Lessee, shall be delivered by Lessee to Lessor, if at the time of such +payment, an Event of Default shall have occurred and be continuing. In either +case, all such amounts shall be held by Lessor as security for the +obligations of Lessee or, at the option of Lessor, applied by Lessor toward +payment of any of Lessee's obligations at the time due hereunder. At such +time as + + + -54- + + + +there shall not be continuing any such Event of Default, all such amounts at +the time held by Lessor in excess of the amount, if any, which Lessor has +elected for application as provided above shall be paid to Lessee. + + (g) CERTIFICATES OF INSURANCE. On or before the Delivery +Date, and thereafter on each renewal by Lessee of the insurance required +hereby, but not less often than annually, Lessee will furnish to Lessor and +each Beneficiary a certificate executed and delivered by an Approved Insurance +Broker who is authorized by an Approved Insurer, appointed by Lessee, +describing in reasonable detail insurance carried on the Aircraft and +certifying that the insurance then maintained on the Aircraft complies with +Section 12 of this Lease. Lessee will cause such Approved Insurance Broker who +is authorized by an Approved Insurer to agree to advise Lessor and each +Beneficiary in writing at least thirty (30) days (seven (7) days or such lesser +period as may be applicable in the case of any war risk and allied perils +coverage) prior to the termination or cancellation by the underwriters for any +reason (including, without limitation, failure to pay the premium therefor) of +any such insurance. Lessee further agrees to cause such Approved Insurance +Broker (i) to advise Lessor and each Beneficiary in writing (x) as soon as such +Approved Insurance Broker has knowledge that any insurance coverage carried and +maintained on the Aircraft pursuant to this Section 12 will not be renewed or +available upon expiration of the then existing insurance and (y) not later than +seven (7) days prior to the expiration of the then existing insurance if Lessee +has not given notice renewal instructions to the Approved Insurance Broker, and +(ii) to deliver directly to Lessor and each Beneficiary the certificate of +insurance described above in this paragraph (g) upon such renewal. + + (h) Any insurance described above shall be placed and +maintained throughout the Term in the London, Tokyo, United States, French or +German aviation insurance markets with Approved Insurers, and Lessee's +obligations under this Section 12 shall not be terminated or diminished in any +respect for any reason, including any subleasing of the Aircraft; PROVIDED, +HOWEVER, that if, in connection with any sublease of the Aircraft permitted +hereby, the Permitted Sublessee is required by law to place or maintain some or +any part of the insurance required hereby in an aviation insurance market other +than the London, Tokyo, United States, French or German aviation insurance +market, then Lessee shall cause such Permitted Sublessee to place and maintain +in the London, Tokyo or United States aviation insurance market re-insurance +policies fully covering such insurance, and any such re-insurance policy shall +contain a "cut through" clause in form, scope and substance reasonably +satisfactory to Lessor and each Beneficiary. In the event + + + -55- + + + +of any sublease of the Aircraft, Lessee shall cause to be delivered to Lessor +and to each Beneficiary at least five Business Days prior to the commencement +of such sublease, appropriate certificates of insurance and re-insurance +complying with the requirements of CLAUSE (g) above. + + (i) Notwithstanding anything in this Section 12 to the +contrary, during the period of any CRAF activation contemplated in Section +11(d) of this Lease, Lessor will accept the indemnification by the full faith +and credit of the United States government in lieu of the insurance coverage +required by this Section 12; provided, however, that (i) such indemnification +by the United States government shall provide coverage against the same risks +and on the terms as the risks and terms required by Section 12, (ii) such +indemnification shall be in amounts that are not less than the insurance +coverage contemplated in this Section 12, and (iii) Lessee shall not subject +the Aircraft to the CRAF program without the Beneficiaries, prior written +consent. + + Section 13. INDEMNIFICATION. + + Subject only to the limitations described in the last +paragraph of this Section 13, Lessee agrees to indemnify, reimburse, hold +harmless, protect and defend each Indemnitee for, from and against any and all +claims, damages, losses, liabilities, demands, suits, judgments, causes of +action, legal proceedings, whether civil or criminal, penalties, fines and +other sanctions, and any reasonable attorney's fees and other reasonable costs +and expenses in connection herewith or therewith, including any of the +foregoing arising or imposed with or without Lessor's or Beneficiaries' fault +or negligence (whether passive or active) or under the doctrine of strict +liability (any and all of which are hereafter referred to as "Claims") which in +any way may result from, pertain to or arise in any manner out of, or are in +any manner related to (i) the Aircraft or any this Lease or Operative Documents +or any document executed in connection herewith, or the breach of any +representation, warranty or agreement made by Lessee hereunder or under any +other such document, or (ii) the condition, manufacture, re-delivery, lease, +acceptance, rejection, possession, return, disposition, storage, maintenance, +use or operation of the Aircraft either in the air or on the ground, or (iii) +any defect in the Aircraft (whether or not discovered or discoverable by Lessee +or Lessor or any Beneficiary) arising from the material or any articles used +therein or from the design, testing or use thereof or from any maintenance, +service, repair, overhaul or testing of the Aircraft, whether or not the +Aircraft is in the possession of Lessee, and regardless of where the Aircraft +may then be located, or (iv) any transaction, + + + -56- + + + +approval or document contemplated by this Lease, Operative Documents or given +or entered into in connection herewith; provided, however, that Lessee shall be +subrogated to all rights and remedies which Lessor may have against the +Manufacturer of the Aircraft and its subcontractors or any other party as to +any such Claims, but only to the extent that Lessee satisfies its +indemnification to the Indemnitees with respect to such Claims. Nothing herein +shall require Lessee to indemnify any Indemnitee in respect of any Claim to the +extent the facts or circumstances that form the basis for such Claim arose or +existed prior to the Delivery Date, unless such Claim or any losses, costs +(including attorneys, fees or expenses) or damages arise or are increased as a +result of a breach by Lessee of its agreements herein or otherwise relate to +such a breach (including the breach of any obligation to remedy a fact or +circumstance that arose or existed prior to the Delivery Date). In the event +Lessee is required to indemnify any Indemnitee hereunder, Lessee shall, upon +demand, pay to such Indemnitee an amount which, after deduction of all Taxes +and like charges required to be paid by such Indemnitee in respect of such +payment, is equal to the amount of the indemnification required; provided, +however, that Lessee shall not be required to pay or discharge any Claim +brought by a third party so long as the validity or the amount thereof shall be +diligently contested in good faith and on reasonable grounds by Lessee, at no +cost or expense to Lessor. + + Subject to Lessee's rights under Section 20(f), Lessee hereby +waives, and releases each Indemnitee from, any Claims (whether existing now or +hereafter arising) for or on account of or arising or in any way connected with +injury to or death of personnel of Lessee or loss or damage to property of +Lessee or the loss of use of any property which may result from or arise in any +manner out of or in relation to the ownership, leasing, condition, use or +operation of the Aircraft, either in the air or on the ground, or which may be +caused by any defect in the Aircraft from the material or any article used +therein or from the design or testing thereof, or use thereof, or from any +maintenance, service, repair, overhaul or testing of the Aircraft regardless of +when such defect may be discovered, whether or not the Aircraft is at the time +in the possession of Lessee, and regardless of the location of the Aircraft at +any such time. + + The indemnities contained in this Section 13 shall continue +in full force and effect notwithstanding the expiration or other termination of +this Lease and are expressly made for the benefit of and shall be enforceable +by each Indemnitee; PROVIDED, that, unless an Event of Default shall have +occurred and be continuing, Lessee shall not be obligated to pay any indemnity +pursuant to this + + + -57- + + + +Section 13 with respect to any amount to the extent that such amount arises out +of or is measured by acts, failures to act, events or periods of time (or any +combination of the foregoing) that occur after the Aircraft has been +redelivered to Lessor pursuant to and in accordance with Section 16 hereof +(under circumstances not involving a repossession pursuant to Section 18 +hereof) and is no longer subject to this Lease and all obligations of the +Lessee under this Lease have been discharged (other than obligations which by +their express terms survive the expiration of the Term of this Lease) unless +any such act or event shall itself result from or be attributable to an act or +omission of Lessee which occurred prior to the redelivery of the Aircraft and +the discharge of Lessee's obligations under the Lease. + + Notwithstanding the foregoing provisions of this Section 13, +Lessee shall not be obligated to make any payment by way of indemnity to any +Indemnitee in respect of any Claims to the extent such Claims result from the +willful misconduct or gross negligence of such Indemnitee or to the extent +relating solely to the breach or misrepresentation of Lessor or a Beneficiary +under an Operative Document. + + Section 14. LIENS. + + Lessee shall not directly or indirectly create, incur, +assume or suffer to exist any Lien on or with respect to the Aircraft or +Engines, title thereto or any interest therein, except (i) the respective +rights of Lessor and Lessee as herein provided; (ii) Lessor's Liens or any +other Lien arising by or through Lessor, a Beneficiary or a Lender with +respect to the Aircraft or Engines; (iii) Liens for Taxes either not yet due +or being contested in accordance with Section 10 hereof, so long as adequate +reserves are maintained with respect to such Liens and so long as the +Aircraft or such Engine is not in danger of being lost, sold, confiscated, +forfeited or seized as a result of any such Lien or contest; and (iv) +inchoate materialmen's, mechanics', workmen's, repairmen's, employees' or +other like Liens arising in the ordinary course of business, which either are +not due and owing or are being contested in good faith by Lessee so long as +the Aircraft or such Engine is not in danger of being lost, sold, +confiscated, forfeited or seized as a result of any such Lien or contest. +Lessee shall promptly, at its own expense, take such action as may be +necessary to duly discharge any Lien (except for the Liens referred to in +clauses (i), (ii), (iii) and (iv) of this Section 14) if the same shall arise +at any time with respect to the Aircraft or any Engine (including any Part). + + + -58- + + + + Section 15. PERFECTION OF TITLE AND FURTHER ASSURANCES. + + If at any time, any filing or recording is reasonably +necessary to protect the interests of Lessor or the Beneficiaries, Lessee, upon +request by Lessor or any Beneficiary, shall cause this Lease and any and all +additional instruments which shall be executed pursuant to the terms hereof, to +be kept, filed and recorded and to be re-executed, re-filed and re-recorded in +the appropriate office or offices pursuant to applicable Laws, to perfect, +protect and preserve the rights and interests of Lessor hereunder and in the +Aircraft. At the reasonable request of Lessor or any Beneficiary, Lessee shall +furnish to Lessor and each Beneficiary an opinion of counsel or other evidence +satisfactory to Lessor of each such filing or re-filing and recordation or +re-recordation. All costs (including attorneys' fees and expenses) relating to +effecting and maintaining the registration of this Lease, including the Lease +Supplement, or any related document, shall be borne and paid by Lessee. All +costs (including attorneys' fees and expenses) relating to effecting and +maintaining the registration of the Aircraft with the FAA shall be borne and +paid by Lessor or the Beneficiaries, provided that Lessee shall cooperate with +Lessor and the Beneficiaries in connection therewith as provided above and bear +the cost of changing the registration number of the Aircraft. + + Without limiting the foregoing, Lessee shall do or cause +to be done, at Lessee's cost and expense, any and all acts and things which +may be required under the terms of the Mortgage Convention to perfect and +preserve the title and interest of Lessor in the Aircraft within the +jurisdiction of any signatory which has ratified the Mortgage Convention if +such jurisdiction is in the territory in which Lessee intends to operate the +Aircraft, as Lessor or any Beneficiary may reasonably request. Lessee shall +also do or cause to be done, at its own expense, any and all acts and things +which may be required of an aircraft operator under the terms of any other +Law involving any jurisdiction in which Lessee intends to operate, or any and +all acts and things which Lessor or any Beneficiary may reasonably request, +to perfect and preserve Lessor's ownership rights regarding the Aircraft +within any such jurisdiction. + + In addition, Lessee will promptly and duly execute and +deliver to Lessor such further documents and assurances and take such further +actions as it may from time to time reasonably request in order to more +effectively carry out the intent and purpose of this Lease and to establish +and protect the rights and remedies created or intended to be created in +favor of Lessor hereunder, including, without limitation, if reasonably +requested by Lessor at the expense + + + -59- + + + +of Lessee, the execution and delivery or supplements or amendments hereto in +recordable form, subjecting to this Lease any Replacement Engine and the +recording or filing of counterparts thereof, in accordance with the Laws of any +appropriate jurisdiction. + + Section 16. RETURN OF AIRCRAFT AND RECORDS. + + (a) RETURN. + + (i) On the Return Occasion, Lessee will, unless Casualty + Occurrence has occurred, at its expense, redeliver the + Aircraft to Lessor at the Return Location in a condition + complying with the requirements of Exhibit E and this + Section 16: + + (A) free and clear of all Liens (other than Lessor's Liens + or any Lien arising by or through Lessor, any Beneficiary + or any Lender); + + (B) having the benefits of any indemnities and warranties + which have been assigned to and are held by Lessee + relating to the Aircraft assigned to Lessor; + + (C) duly registered in the name of Lessor with the FAA; + + (D) unless otherwise agreed by the Beneficiaries in a + condition so as to ensure the Aircraft has either a + current, valid and existing certification of + airworthiness for airline operation or (at the + Beneficiaries, election) an export certificate of + airworthiness, in either case, issued by the FAA; and + + (E) if specified by the Beneficiaries, Lessee shall cooperate + with Lessor to ensure that the Aircraft is deregistered + from the FAA; + + provided that Lessee complies with the foregoing and with all + of its obligations under this Lease, Lessor shall technically + accept redelivery of the Aircraft from Lessee at Lessee's + maintenance facilities in Honolulu, Hawaii before the + commencement of any storage period (it being understood that + if during the ferry flight to the Return Location any defect + or non-compliance which was purportedly corrected in connection + with the Final Inspection shall reoccur, Lessee shall be + responsible for rectifying such defect or non-compliance); + PROVIDED, HOWEVER, that if a C Check shall be performed in + connection with the Return + + + -60- + + + Occasion, then such technical inspection and acceptance shall + occur at the location of the facility in the United States of + America or Canada approved by FAA for C Check of 737-200 + aircraft at which such C Check is performed. + + (ii) In the event that Lessee does not return the Aircraft to + Lessor on the specified date of expiration or earlier + termination of this Lease, for any cause (other than due + to the fault of Lessor, any Beneficiary or the Lender), + then the obligations of Lessee under this Lease shall + continue and such continued use shall not be considered a + renewal of the terms of this Lease or a waiver of any + right of Lessor hereunder and Lessor may terminate + Lessee's right as to the Aircraft on written notice to + Lessee and Lessee shall fully indemnify Lessor, the + Lender and each other Indemnitee on demand against all + losses, liabilities, actions, proceedings, costs and + expenses thereby suffered or incurred by Lessor, the + Lender or any other Indemnitee. During such continued + use, damages in an amount equal to Basic Rent (as + provided below) shall continue to be paid by Lessee to + Lessor, and the performance of other obligations of + Lessee to Lessor shall continue hereunder. Basic Rent + shall be pro-rated on a daily basis for each day until + the Aircraft is actually delivered to Lessor, and all + other terms and conditions of this Lease shall remain in + full force and effect. Payment shall be made promptly + upon presentation of Lessor's invoice and any failure to + pay shall constitute a default of Lessee. + + (b) FINAL INSPECTION. At least 10 days prior to redelivery of the +Aircraft, Lessee will take the Aircraft out of service and make the Aircraft +and all Aircraft Documents available to Lessor and the Beneficiaries for +inspection at Lessee's facilities in Honolulu, Hawaii (or at the location of +the facility in the United States of America or Canada approved by FAA for +the C Check of 737-200 aircraft at which the C Check shall be performed, as +provided in Section 16(a)(i) above, if applicable) (the "Final Inspection") +in order to verify that the condition of the Aircraft complies with this +Lease, and Lessor's and the Beneficiaries, representatives shall use their +reasonable efforts to cause their representatives to commence such compliance +verification as promptly as practicable at or following the beginning of such +10-day period. Lessee agrees, at the Beneficiaries, request, to furnish due +evidence that all service, maintenance, repairs, overhauls and modifications +required under this Lease have been duly + + + -61- + + + +performed. Lessee shall make available to Lessor and the Beneficiaries at +Lessee's principal maintenance base, not less than fifteen (15) Business Days +prior to commencement of the Final Inspection, such documentation regarding the +condition, use, maintenance, operation and history of the Aircraft during the +Term, as requested by the Beneficiaries in order to enable Lessor and the +Beneficiaries to verify that the condition of the Aircraft complies with this +Lease. To the fullest extent possible, the Final Inspection shall be carried +out during the C Check referred to in paragraph 2(a) of Exhibit E. The Final +Inspection will consist of and be long enough to permit Lessor and the +Beneficiaries to: + + (i) generally review, test (as observer) and inspect the + Aircraft to Lessor's and the Beneficiaries, satisfaction + (which will include the opening of any areas of the + Aircraft if Lessor or the Beneficiaries reasonably + believe, based on prior inspections or other evidence, + that the opening of such areas would reveal that the + Aircraft does not completely comply with the requirements + of Exhibit E and this Section 16), to satisfy Lessor and + the Beneficiaries that the Aircraft completely complies + with the requirements of Exhibit E and this Section 16; + + (ii) inspect the Aircraft Documents; + + (iii) inspect the un-installed Parts; + + (iv) observe a two hour demonstration flight (with up to four + of Lessor's or the Beneficiaries' representatives as + on-board observers); + + (v) inspect the Engines, including, without limitation (i) by + observing a performance test in accordance with limits set + forth in the engine section of the Aircraft Maintenance + Manual and (ii) by performing a full and complete + borescope inspection of (a) the low pressure and high + pressure compressors and (b) turbine area; and + + (vi) inspect the APU, including, without limitation (i) an APU + condition run (as observer) and (ii) by a full and + complete borescope inspection. + +The actions contemplated by the foregoing clauses (i) through (iv) shall be +performed at Lessee's expense; provided that, unless an Event of Default +shall have + + + -62- + + + +occurred or any inspection carried out under the foregoing clauses (i) +through (iv) reveals discrepancies which require a further inspection, Lessee +shall not be required to bear the travel and lodging expenses of Lessor's or +the Beneficiaries, representatives. + + (c) NON-COMPLIANCE. To the extent that, at the time of Final +Inspection, the condition of the Aircraft does not comply with this Lease, +Lessee will at the Beneficiaries, option: + + (i) immediately rectify the non-compliance and, to the + extent the non-compliance extends beyond the Return + Occasion, the Term will be automatically extended + (including the obligation to pay Rent as referred to + in Section 16(a)(ii)) and this Lease will remain in + force until the non-compliance has been rectified; or + + (ii) redeliver the Aircraft to Lessor and pay to Lessor + such amount as is mutually agreed to put the Aircraft + into the condition required by this Lease. Lessor + shall make all reasonable efforts to minimize such + costs. + + (d) REDELIVERY. If applicable, upon redelivery Lessee will +provide to Lessor and the Beneficiaries all documents (other than those as may +be required to be supplied by Lessor or the Beneficiaries) necessary to export +the Aircraft from the United States (including, without limitation, a valid and +subsisting export license, if applicable, for the Aircraft) and required in +relation to the deregistration of the Aircraft with the FAA. + + In the event that Beneficiaries request Lessee to secure an +export license (a) such request must be made upon sufficient notice in order to +grant Lessee adequate time to procure such export license, (b) Lessor will +render such timely assistance as is reasonably necessary, and (c) this may be +requested in relation to the Return Occasion or subsequent to the Return +Occasion. + + (e) ACKNOWLEDGEMENT. Upon return of the Aircraft in +accordance with the terms of this Lease, Lessor shall execute and deliver a +Redelivery Acknowledgement in the form of Exhibit I hereto to Lessee. + + (f) MAINTENANCE PROGRAM. + + (i) Prior to the Return Occasion (unless the Aircraft has + suffered a Casualty Occurrence) and upon Lessor's or a + Beneficiary's request, + + + -63- + + + + Lessee will provide Lessor or such Beneficiary or its + agent, in either case at Lessee's cost, reasonable + access to the Maintenance Program and the Aircraft + Documents in order to facilitate the Aircraft's + integration into any subsequent operator's fleet. + + (ii) Lessee will, if requested by Lessor or a Beneficiary + to do so, upon return of the Aircraft deliver to + Lessor or the Beneficiaries a certified true, current + and complete copy of the Maintenance Program in + English. Lessor and the Beneficiaries agree that they + will not disclose the contents of the Maintenance + Program to any person or entity except to the extent + necessary to monitor Lessee's compliance with this + Lease and/or to bridge the maintenance program for the + Aircraft from the Maintenance Program to another + maintenance program after the Return occasion. + + (g) FUEL. The amount of fuel in the Aircraft shall be no less +than the amount of fuel upon Delivery Date. If requested by Lessor, Lessee will +fuel the Aircraft in excess of the required amount by the preceding sentence at +the Return Location, and Lessor will pay for the fuel exceeding such +requirement on board the Aircraft on the Return Occasion at the direct cost +paid by Lessee (without mark-up). + + (h) STORAGE. If the Final Inspection is carried out in +Honolulu, Hawaii and a Beneficiary requests in writing then Lessee shall, at +Lessee's cost and expense during the first sixty (60) days and thereafter +Lessor's cost and expense, provide Lessor with, or assist Lessor in procuring, +storage facilities for the Aircraft for up to one hundred twenty (120) days. +Such storage shall be at Lessee's maintenance base or if such maintenance base +will be eventually unable to perform the required maintenance, any other +location agreed between Lessor and Lessee which base or location is capable of +performing any required maintenance on the Aircraft. With respect to the +Aircraft while in storage, Lessee will maintain, or procure the maintenance +of, an amount of fuel in each tank as required in accordance with the storage +procedures at the storage location and as set forth herein. Lessee agrees, +during such storage period, to perform, or procure the performance of, such +maintenance required to maintain the airworthiness of the Aircraft (i) in +accordance with the storage procedures prescribed by the Manufacturer or +Lessee's Maintenance Program and (ii) in a condition required to be + + + -64- + + + +maintained at redelivery pursuant to the terms of Section 16(a). Risk of loss +or damage to the Aircraft shall pass to Lessor at the commencement of storage, +but Lessee will carry insurance, with Lessor and each Beneficiary named as an +additional insured, on the Aircraft during such storage period as though the +Aircraft were to remain in Lessee's fleet in accordance with the provisions of +Section 12, except that Lessee shall not be required to maintain "all risks" +flight insurance during such storage period but shall maintain insurance +policies that cover the Aircraft in storage and test flights of the Aircraft on +an hourly basis (as typically available and maintained under similar +circumstances involving the storage and maintenance of aircraft). All amounts +hereunder relating to storage, insurance and maintenance work for the first 60 +days of such storage period shall be for the account of Lessee and thereafter, +shall be for the account of Lessor. Incremental costs incurred by Lessee after +the first 60 days of storage which would not have been incurred had such +storage period not exceeded 60 days shall be reimbursed by Lessor. Lessee +further agrees to perform, or procure the performance of, at Lessor's cost, any +other maintenance that Lessor may reasonably request. If, following the storage +of the Aircraft in Hawaii, the Beneficiaries ferry the Aircraft to a location +designated by the Beneficiaries in the Western Continental United States (the +"Designated Location"), Lessee will reimburse the Beneficiaries for the cost of +the ferry flight of the Aircraft from the storage location in Hawaii to such +Designated Location. If the Aircraft is ferried to a location other than in the +Western Continental United States, other than following a Default or an Event +of Default, then (i) the Beneficiaries will bear any additional ferry flight +costs and expenses incurred that are beyond the costs and expenses that would +have been incurred if the Aircraft were ferried to a location in the Western +Continental United States, and (ii) Lessee will bear the ferry flight costs and +expenses that are equivalent to the ferrying of the Aircraft to a location in +the Western Continental United States and shall pay such amount to Lessor or +the Beneficiaries upon demand by Lessor or the Beneficiaries therefor. + + (i) MAINTENANCE FOR RE-REGISTRATION. Lessee, at Lessee's +cost, will, upon written notice from a Beneficiary not less than sixty (60) +days prior to a Return Occasion, carry out such extra maintenance, repair or +modification as may be required to enable the Aircraft to be in a condition +to ensure that the Aircraft will meet and comply with (i) the requirements of +any other country selected by the Beneficiaries and (ii) all civil aviation +regulations, orders, rules and airworthiness directives of such country to +permit the immediate re-registration and securing of a certificate of +airworthiness in such country for the + + -65- + + + +Aircraft (provided such regulations, orders, rules and airworthiness directives +are no more restrictive than those of the FAA, and, so long as Lessee is +otherwise in compliance with this Section 16 and Exhibit E, the time required +to perform such extra maintenance shall not constitute an extension of the +Term). If the cost of such maintenance, repair or modification exceeds the cost +of the maintenance, repair or modification which Lessee would otherwise have +incurred to ensure the Aircraft shall be in condition to comply with the terms +of this Lease, Lessor shall promptly pay Lessee the difference between such +costs at actual cost (without mark-up) to Lessee. + + (j) ASSISTANCE FOR FUTURE LEASING/SALE. + + (i) Lessor or any Beneficiary may, from time to time + during the last year of the Term, wish to make the + Aircraft available for inspection to designated + representatives or technical teams evaluating the Aircraft + for use after the end of the Term. Lessor or any + Beneficiary agrees to give Lessee not less than five (5) + days, advance notice of such inspection and Lessee agrees + to co-operate fully with Lessor's or such Beneficiary's + requests in making the Aircraft and Aircraft Documents + available to such authorized technical team. Lessor agrees + to be responsible for its costs in connection with any + such inspection and that such request will be reasonable + and will be coordinated with Lessee so as not to + unreasonably interfere with the operation or maintenance + of the Aircraft or the conduct of Lessee's business + (unless an Event of Default shall have occurred and be + continuing); PROVIDED, HOWEVER, that, unless an Event of + Default shall have occurred and be continuing, no such + inspection shall result in the delay of a scheduled flight + of Lessee; + + (ii) Subject to prior coordination between the + Beneficiaries and Lessee, Lessee agrees that, during the + last year of the Term and following redelivery of the + Aircraft to Lessor, Lessee shall at any Beneficiary's + reasonable request and Lessee's cost make available to + Lessor and the Beneficiaries for such reasonable period as + the Beneficiaries may reasonably require technical and + engineering personnel qualified to provide the + Beneficiaries and any subsequent lessee or purchaser of + the Aircraft with such information as will enable any + subsequent lessee or purchaser of the Aircraft to be able + to bridge the Aircraft onto the maintenance program of + such subsequent lessee or purchaser. + + + -66- + + + + Section 17. EVENTS OF DEFAULT. + + Any one or more of the following occurrences or events shall +constitute an Event of Default: + + (a) Lessee shall fail to make any payment of Rent to Lessor +when due under this Lease and such payment shall be overdue for a period of +five (5) Business Days; + + (b) Lessee shall fail to obtain and maintain any insurance +required under the provisions of Section 12 hereof; or shall operate the +Aircraft outside of the scope of the insurance coverage maintained with respect +to the Aircraft; + + (c) Any representation or warranty made by Lessee herein or +in any document or certificate furnished to Lessor in connection herewith or +therewith or pursuant hereto is incorrect at the time given in any material +respect and such failure shall continue unremedied for a period of three (3) +Business Days after notice thereof has been given to Lessee hereunder or Lessee +otherwise has notice thereof; + + (d) Lessee shall fail to timely comply with the provisions of +Section 21 (Security Deposit) hereof; + + (e) Lessee shall directly or indirectly create, incur, assume +or suffer to exist any Lien on or with respect to the Aircraft or Engines, +title thereto or any interest therein, except as expressly permitted by Section +14; + + (f) Lessee shall at any time not be a Certified Air Carrier +holding all licenses, certificates, permits and franchises from the appropriate +agencies of the United States of America and all other governmental authorities +having jurisdiction over Lessee and its operations which are necessary to +authorize the Lessee to engage in air transport and to carry on its intra-state +passenger operations within Hawaii as presently conducted and in connection +with the operation of aircraft, including the Aircraft; or Lessee shall lose or +suffer a suspension of its United States air carrier license or certificate +under Part 121 of the FAA Regulations or certificate under Chapter 411 of +Subtitle VII, Part A of Title 49 of the United States Code, as amended; + + (g) Lessee shall fail to perform or observe in any material +respect any other covenant, condition or agreement to be performed or observed +by it pursuant to this Lease and such failure shall continue for a period of +thirty (30) days after the earlier to occur of (1) Lessee's attaining or having +knowledge thereof and (2) written notice thereof is given to Lessee; + + (h) Lessee consents to the appointment of a receiver, +trustee or liquidator of itself or of a + + + -67- + + + +substantial part of its property, or Lessee admits in writing its inability to +pay its debts generally as they come due or makes a general assignment for the +benefit of creditors, or Lessee files a voluntary petition in bankruptcy or a +voluntary petition seeking reorganization in a proceeding under any bankruptcy +laws (as now or hereafter in effect), or an answer admitting the material +allegations of a petition filed against Lessee in any such proceeding, or +Lessee by voluntary petition, answer or consent seeks relief under the +provisions of any bankruptcy or other similar law providing for the +reorganization or winding-up of corporations, or provides for an agreement, +composition, extension or adjustment with its creditors; + + (i) An order, judgment or decree is entered by any court, +with or without the consent of Lessee, appointing a receiver, trustee or +liquidator for Lessee or of all or any substantial part of its property (save +for the purposes of an amalgamation, merger or reconstruction not involving +insolvency), or all or any substantial part of the property of Lessee is +sequestered, and any such order, judgment or decree of appointment or +sequestration remains in effect, undismissed, unstayed or unvacated for a +period of sixty (60) days after the date of entry thereof; + + (j) A petition against Lessee in a proceeding under the +bankruptcy, insolvency or other similar Laws (as now or hereafter in effect) of +any Governmental Entity is filed and is not withdrawn or dismissed within sixty +(60) days thereafter, or if, under the provisions of any Law providing for +reorganization or winding-up of corporations which may apply to Lessee, any +court of competent jurisdiction assumes jurisdiction over, or custody or +control of, Lessee or of all or any substantial part of its property and such +jurisdiction, custody or control remains in effect, unrelinquished, unstayed or +unterminated for a period of sixty (60) days; + + (k) (i) A final judgment for the payment of money not covered +by insurance in excess of $500,000, or (ii) final judgments for the payment of +money not covered by insurance in excess of $500,000 in the aggregate, shall be +rendered against Lessee and the same shall remain undischarged for a period of +ninety (90) days during which execution thereof shall not be effectively stayed +by agreement of the parties involved, stayed by court order or adequately +bonded; + + (1) Attachments or other Liens shall be issued or entered +against substantially all of the property of Lessee and shall remain +undischarged or unbonded for forty-five (45) days except for security interests +created in connection with monies borrowed or obligations agreed to by Lessee +in the ordinary course of its business; + + + -68- + + + + (m) Lessee shall default in the payment of any obligation for +the payment of borrowed money, for the deferred purchase price of property or +for the payment of rent or hire under any lease of aircraft when the same +becomes due, in any such case if the outstanding amount thereof (or, in the +case of a lease, if the aggregate rentals during the term thereof) exceeds +$500,000; or Lessee shall default in the performance of any other term, +agreement or condition contained in any material agreement or instrument under +or by which any such obligation having any unpaid balance of at least +$1,000,000 is created, evidenced or secured, if the effect of such default is +to cause such obligation to become due prior to its stated maturity; or Lessee +shall default in the payment when due of any other amount owed to Lessor or +each Beneficiary; + + (n) Lessee voluntarily suspends all or substantially all of +its operations or the franchises, concessions, permits, rights or privileges +required for the conduct of the business and operations of Lessee shall be +revoked, canceled or otherwise terminated or the free and continued use and +exercise thereof curtailed or prevented, and as a result of any of the +foregoing the predominant business activity of Lessee shall cease to be that of +a commercial airline; or + + (o) An "Event of Default" (as therein defined) shall have +occurred and be continuing under any Other Lease. + + Section 18. REMEDIES. + + Upon the occurrence of any Event of Default and any time +thereafter so long as the same shall be continuing, Lessor may, at its option +and without notice to Lessee, exercise one or more of the following remedies as +Lessor in its sole discretion shall elect, to the extent available and +permitted by, and subject to compliance with any mandatory requirements of, +applicable Law then in effect: + + (a) Demand that Lessee, and Lessee shall upon the written +demand of Lessor and at Lessee's expense, immediately return the Aircraft to +Lessor at any location in Hawaii or in the continental United States as +specified by Lessor or the Beneficiaries and in the manner specified in such +notice and in the condition specified in Section 16, in which event such return +shall not be delayed for purposes of complying with the return conditions +specified in Section 16 hereof (none of which conditions shall be deemed to +affect Lessor's possession of the Aircraft) or delayed for any other reason. +Notwithstanding the foregoing, at Lessor's option Lessee shall be required +thereafter to take such actions as would be required by the provisions of this +Lease if the Aircraft were being returned at the end of the Term hereof. In +addition, Lessor, at its option and to the extent permitted by applicable Law, +may enter upon the premises where all or any part of the Aircraft is located + + + -69- + + + +and take immediate possession of and, at Lessor's sole option, remove the same +(and/or any engine which is not an Engine but which is installed on the +Airframe, subject to the rights of the owner, lessor or secured party thereof) +by summary proceedings or otherwise, all without liability accruing to Lessor +or any Beneficiary for or by reason of such entry or taking of possession +whether for the restoration of damage to property, or otherwise, caused by such +entry or taking, provided that all actions of Lessor in this respect are +reasonable in the circumstances and excepting damages caused by gross +negligence or willful misconduct. + + (b) Sell at private or public sale, as Lessor may determine, +or hold, use, operate or lease to others the Aircraft as Lessor in its sole +discretion may determine, all free and clear of any rights of Lessee; PROVIDED, +HOWEVER, that nothing herein shall be construed as limiting any right of Lessor +or any Beneficiary to sell, lease, assign or otherwise dispose of all or any +part of the Aircraft under any circumstances (whether or not during the +existence or continuance of an Event of Default) during the Term, subject only +to Lessee's rights under SECTION 20(f). + + (c) Whether or not Lessor shall have exercised, or shall +thereafter at any time exercise, any of its rights under paragraph (a) or +paragraph (b) of this Section 18, Lessor, by thirty (30) days written notice to +Lessee specifying a payment date, may demand that Lessee pay to Lessor, and +Lessee shall pay to Lessor, on the payment date specified in such notice, as +liquidated damages for loss of bargain and not as a penalty (in lieu of the +Basic Rent due for the period commencing after the date specified for payment +in such notice), any unpaid Rent for the Aircraft (prorated in the case of +Basic Rent on a daily basis) to and including the payment date specified in +such Notice, plus the aggregate amount of all payments of Basic Rent for the +remainder of the Term, discounted periodically (equal to installment frequency) +to present worth at the interest rate of four percent (4%) per annum. + + (d) In the event Lessor, pursuant to Section 18(b) above, +shall have sold the Aircraft, then Lessor, in lieu of exercising its rights +under Section 18(c) above, may, if it shall so elect, demand that Lessee pay +Lessor, and Lessee shall pay to Lessor, as liquidated damages for loss of +bargain and not as a penalty (in lieu of the Basic Rent due for the period +commencing after such sale occurs), any unpaid Rent for the Aircraft due to and +including the date of sale, plus the amount by which the Appraised Value (as +hereafter defined) of the Aircraft, computed as of the date of such sale, +exceeds the net cash proceeds of such sale. As used herein, "Appraised Value" +means the fair market sales value of the Aircraft, as determined by an +internationally recognized aircraft appraiser selected by the Beneficiaries (i) +on the basis of either a physical inspection of the Aircraft or on the basis of +a "desk top" + + + -70- + + + +appraisal, as the Beneficiaries shall determine, (ii) on the assumption that +the Aircraft has been maintained in accordance with the requirements of this +Lease and is in the condition required by Section 16 and Exhibit E, whether or +not any such assumption is in fact the case, and (iii) at Lessee's cost and +expense. + + (e) De-register the Aircraft from the Aircraft Registry at +the FAA, and proceed to re-register the Aircraft with the FAA or any other +aircraft registry of any jurisdiction, in any case, in the name of Lessor, +either or both Beneficiaries or any other Person, as the Beneficiaries shall +determine. + + (f) In the event that Lessor, pursuant to Section 18(b) +above, shall, after having exercised reasonable efforts under the +circumstances, have re-let the Aircraft under a lease which extends at least to +the date upon which the Term for the Aircraft would have expired but for +Lessee's default, Lessor, in lieu of exercising its rights under Section 18(c) +above with respect to the Aircraft, may, if it shall so elect, demand that +Lessee pay Lessor, and Lessee shall pay Lessor, as liquidated damages for loss +of bargain and not as a penalty (in lieu of the Basic Rent for the Aircraft due +after the time of re-letting) any unpaid Rent for the Aircraft due up to the +date of re-letting, plus the amount, if any, by which the aggregate Basic Rent +for the Aircraft (such Basic Rent being computed on the assumption that the +Basic Rent as in effect on the date of such demand shall remain in effect at +all times thereafter), which would otherwise have become due over the Term, +discounted periodically (equal to installment frequency) to present worth as of +the date of re-letting at the interest rate of four percent (4%) per annum, +exceeds the aggregate basic rental payments to become due under the re-letting +from the date of such re-letting to the date upon with the Term for the +Aircraft would have expired but for Lessee's default, discounted periodically +(equal to installment frequency) to present worth as of the date of the +re-letting at the interest rate of four percent (4%) per annum. + + (g) Proceed by appropriate court action or actions, either at +law or in equity, to enforce performance by Lessee of the applicable covenants +of this Lease and to recover damages for the breach thereof and to rescind this +Lease. + + (h) Terminate Lessee's right to possession and use of the +Aircraft under this Lease by written notice (which notice shall be effective +upon dispatch) and repossess the Aircraft, whereupon, Lessee shall forthwith +deliver the Aircraft and all Aircraft Documents to Lessor or the Beneficiaries +at such location as Lessor or the Beneficiaries shall specify. + + + -71- + + + + (i) Seize or foreclose upon any collateral pledged to secure +the obligations of Lessee hereunder; liquidate, dispose of, and realize upon +any such collateral; and apply the proceeds thereof to the obligations of +Lessee hereunder, in any case, as Lessor shall determine. + + (j) Draw any amount available to be drawn under any Security +Deposit or the Security Letter of Credit, and to apply the proceeds thereof to +the obligations of Lessee hereunder, in any case, as Lessor shall determine. + + In addition to the foregoing, Lessee shall be liable for any +and all unpaid Rent during or after the exercise of any of the aforementioned +remedies, together with interest on such unpaid amounts at the Overdue Rate, +and until satisfaction of all of Lessee's obligations to Lessor hereunder and +for all reasonable legal fees and other reasonable costs and expenses incurred +by Lessor by reason of the occurrence of any Event of Default or the exercise +of Lessor's remedies with respect thereto, including all costs and expenses +incurred in connection with the return of the Aircraft in accordance with the +terms of Section 16 hereof or in placing the Aircraft in the condition and with +airworthiness certification as required by such Section. + + In effecting any repossession, each of Lessor and each +Beneficiary and its representatives and agents, to the extent permitted by law, +shall: (i) have the right to enter upon any premises where it reasonably +believes the Aircraft, the Airframe, an Engine or Part to be located and Lessee +shall cooperate in giving Lessor such access; (ii) not be liable, in conversion +or otherwise, for the taking of any personal property of Lessee which is in or +attached to the Aircraft, the Airframe, an Engine or Part which is repossessed; +provided, however, that Lessor shall return to Lessee all personal property of +Lessee or its passengers which were on the Aircraft at the time Lessor re-takes +possession of the Aircraft; (iii) not be liable or responsible, in any manner, +for any inadvertent damage or injury to any of Lessee's property in +repossessing and holding the Aircraft, the Airframe, an Engine or Part, except +for that caused by or in connection with Lessor's gross negligence or willful +acts; and (iv) have the right to maintain possession of and dispose of the +Aircraft, the Airframe an Engine or Part on any premises owned by Lessee or +under Lessee's control. + + If requested by Lessor, Lessee, at its sole expense, shall +assemble and make the Aircraft, the Airframe, an engine or Part available at a +place designated by Lessor in accordance with Section 16 hereof. Lessee hereby +agrees that, in the event of the return to or repossession by Lessor of the +Aircraft, the Airframe, an Engine or Part, any rights in any warranty (express +or implied) heretofore assigned to Lessee or otherwise held by Lessee shall +without further act, notice or writing be assigned or-reassigned to + + + -72- + + + +Lessor, if assignable. Lessee shall be liable to Lessor for all reasonable +expenses (including reasonable attorneys, fees and expenses), disbursements, +costs and fees incurred in (i) re-possessing, storing and shipping the +Aircraft, the Airframe, an Engine or Part and maintaining, repairing and +refurbishing the Aircraft, the Airframe, an Engine or Part to the condition +required by Section 16 hereof and (ii) preparing the Aircraft, the Airframe, an +Engine or Part for sale or lease, advertising the sale or lease of the +Aircraft, the Airframe, an Engine or Part and selling or releasing the +Aircraft, the Airframe, an Engine or Part. Lessor is hereby authorized and +instructed, at its option, to make reasonable expenditures which Lessor +considers advisable to repair, restore, store, insure or maintain the Aircraft, +the Airframe, an Engine or Part to the condition required by Section 16 hereof, +all at Lessee's sole expense. + + Lessee shall cooperate reasonably and in good faith with +Lessor and any Beneficiary in connection with the exercise by Lessor or either +Beneficiary of the rights and remedies provided hereby or at law upon and +following the occurrence of an Event of Default, including by executing and +delivering such instruments as Lessor or any Beneficiary shall reasonably +request. In furtherance of the foregoing, Lessee hereby constitutes and +appoints Lessor as the true and lawful agent and attorney-in-fact for Lessee +for purposes of executing and delivering any instrument that may be necessary +or appropriate in order to carry into effect any right or remedy of Lessor or +any Beneficiary hereunder upon and following the occurrence of an Event of +Default, including any consent to the de-registration of the Aircraft from the +FAA. Any such instrument signed pursuant to the foregoing appointment may be +signed in Lessee's name or in the name of Lessor. The foregoing appointment is +coupled with an interest and is irrevocable. + + No remedy referred to in this Section 18 is intended to be +exclusive, but, to the extent permissible hereunder or under applicable Law, +each shall be cumulative and in addition to any other remedy referred to above +or otherwise available to Lessor at Law or in equity; and the exercise or +beginning of exercise by Lessor of any one or more of such remedies shall not +preclude the simultaneous or later exercise by Lessor of any or all of such +other remedies. No express or implied waiver by Lessor of any Default shall in +any way be, or be construed to be, a waiver of any future or subsequent +Default. + + Section 19. ALIENATION. + + Lessor and each Beneficiary shall have the right at its sole +cost and expense to assign, sell, encumber or otherwise dispose of any interest +of Lessor or such Beneficiary in, to or under the Aircraft, the Trust Agreement +or this Lease and/or the proceeds hereof, subject to the rights of Lessee under +the provisions of this Lease, + + + -73- + + + +including by sale, transfer, pledge, assignment or other disposition to any +Lender or to any other Person (other than an air carrier engaged in the airline +business); PROVIDED, HOWEVER, that unless an Event of Default shall have +occurred and be continuing, neither Lessor nor any Beneficiary shall undertake +any action that (i) interferes with Lessee's rights under Section 20(f) hereof, +(ii) disqualifies the Aircraft for registry with the FAA, or (iii) results in +an increase in any liability of Lessee under Section 10, unless Lessor and each +Beneficiary agree to waive their rights to be indemnified for any such increase +in liability. To effect or facilitate any such sale, transfer, pledge, +assignment, or other disposition, Lessee agrees to provide, at Lessor's sole +cost and expense, such agreements, consents, conveyances or documents as may be +reasonably requested by Lessor or such Beneficiary, which shall include, +without limitation, a consent to such disposition executed by Lessee and in the +case of an assignment of the Lease (other than an assignment for security +purposes) an unrestricted release of Lessor from any obligations under this +Lease arising after the date of such assignment; PROVIDED, HOWEVER, that such +release shall not affect any liability of Lessor or such Beneficiary arising on +or before the date of such assignment. Lessee hereby agrees that it will not +assert against an assignee any Claim or defense which it may have against +Lessor or such Beneficiary; PROVIDED, HOWEVER, that any such Claim or defense +will survive such assignment and continue to be maintained against Lessor or +such Beneficiary. No assignment or transfer hereunder shall result in Lessee +being obligated to pay for the account of Lessor or such Beneficiary any +greater amount than it would otherwise be obligated to pay hereunder, including +without limitation any obligation of Lessee to pay amounts pursuant to Section +10. The agreements, covenants, obligations, and liabilities contained herein +including, but not limited to, all obligations to pay Rent and indemnify each +Indemnitee are made for the benefit of each Indemnitee and their respective +successors and assigns. In the event this Lease is assigned, or otherwise +disposed of by Lessor or either Beneficiary, any assignee, transferee or +mortgagee shall agree in writing with Lessee as a condition precedent thereto +to be bound by the provisions of this Lease and not to disturb or otherwise +interfere with the quiet enjoyment of Lessee of the Aircraft in accordance with +the terms of this Lease so long as no Event of Default shall have occurred and +be continuing. + + Lessee shall include each Lender or any Person to whom an +interest herein is assigned as an additional insured and loss payee in +accordance with the provisions of Section 12 hereof and indemnify such Lender +to the same extent Lessee has agreed to indemnify Lessor pursuant to Sections +10 and 13 hereof. No Lender or any Person to whom an interest herein is +assigned shall have any liability for the obligations of Lessor under any +provision of this Lease. + + + -74- + + + + + + + +Section 20. MISCELLANEOUS. + + (a) SEVERABILITY, AMENDMENT AND CONSTRUCTION. Any provision of this +Lease which is prohibited or unenforceable in any jurisdiction shall, as to +such jurisdiction, be ineffective to the extent of such prohibition or +unenforceability without invalidating the remaining provisions hereof; any +such prohibition or unenforceability in any jurisdiction shall not +invalidate or render unenforceable such provision in any other jurisdiction. +To the extent permitted by Law, Lessee hereby waives any provisions of Law +which renders any provisions hereof prohibited or unenforceable in any +respect. Lessee may not assign this Lease or its rights hereunder. This +Lease supersedes any prior or contemporaneous agreements, whether oral or in +writing, of the parties hereto and shall constitute the entire agreements of +the parties hereto. No term or provision of this Lease may be changed, +waived, discharged or terminated orally, but only by an instrument in +writing expressed to be a supplement or amendment to this Lease signed under +hand by an officer of the party against which the enforcement of the change, +waiver, discharge or termination is sought. This Lease shall constitute an +agreement of lease, and nothing herein shall be construed as conveying to +Lessee any right, title or interest in the Aircraft or any Engine or Part +except as a lessee only. The headings in this Lease are for convenience of +reference only and shall not define or limit any of the terms of provisions +hereof. Whenever required by the context hereof, the singular shall include +the plural and vice versa. Reference to this Lease shall mean this Lease as +amended or supplemented from time to time. + + (b) GOVERNING LAW; JURISDICTION. This Lease shall in all respects be +governed by, and construed in accordance with, the laws of the State of New +York, except that the provisions of Section 21(a) (Security Deposit) and +Section 21(b)(vi) shall be governed by the laws of the State of Delaware. + + A. Lessee and Lessor each hereby irrevocably and expressly submit to +the non-exclusive jurisdiction of the United States District Court for the +Southern District of New York and to the New York Supreme Court, New York +County for the purposes of any suit, action or proceeding arising out of +this Lease or the Operative Documents. Final judgment against Lessee or +Lessor in any such suit shall be conclusive, and may be enforced in any +other jurisdictions by suit on the judgment or as otherwise permitted by +applicable Law, a certified or true copy of which shall be conclusive +evidence of the fact and of the amount of any indebtedness or liability of +such party therein described; PROVIDED, HOWEVER, that Lessor or Lessee may +at its option bring suit, or institute other judicial proceedings against +the other party or any of its assets, in the courts of any + + -75- + + + + + +country or place where such party or such assets may be found. + + B. Each of Lessee and Lessor hereby irrevocably waives any objection +which it may now or hereafter have to the laying of venue of any suit, +action or proceeding brought in any court located in New York City, New York +and hereby further irrevocably waives any claim that any such suit, action +or proceeding brought in any such court has been brought in an inconvenient +forum. + + C. The foregoing submission to jurisdiction shall not be construed so +as to limit the right of either party to take proceedings against the other +in whatsoever jurisdictions shall to it seem fit nor shall the taking of +proceedings in any one or more jurisdictions preclude the taking of +proceedings in any other jurisdiction, whether concurrently or not. + + D. Lessee hereby irrevocably appoints CT Corporation System, Inc., 1633 +Broadway, New York, New York 10019, as Lessee's agent and attorney-in-fact +(the "Agent"), upon whom all service of summonses and other legal processes +may be made with respect to any action, suit or proceeding relating to this +Lease or any other Operative Document. The appointment of such Agent shall +be irrevocable, and if the Agent revokes such relationship, prior to the +effective date of such revocation, Lessee shall appoint a successor, which +successor shall be in accordance with the provisions hereof. Prior to such +change Lessee shall notify Lessor of such change of Agent. Lessee consents +and agrees that any service under or in connection with this Lease shall be +duly completed and served if mailed to Lessee at its address specified below +or to the Agent at the address specified above, in either case, by +registered or certified mail. + + (c) NOTICES. All notices required under the terms and provisions hereof +shall be in writing and any such notice shall become effective when +deposited in the mails, registered with proper postage for air mail prepaid, +or, if in the form of a telegram, telex or telecopy, when received, +addressed if to the Lessee, the Lessor or any Beneficiary at their +respective addresses set forth in Exhibit C-2 (or such other address as may +be designated by the Lessee, the Lessor or such Beneficiary to the others). + + (d) LESSOR'S RIGHT TO PERFORM FOR LESSEE. if Lessee fails to make any +payment of Supplemental Rent required to be made by it hereunder or fails to +perform or comply with any covenant, agreement or obligation contained +herein, Lessor or any Beneficiary shall have the right but not the obligation + to make such payment or conform or comply with such agreement, covenant or +obligation, and the amount of such payment and the amount of the reasonable +expenses of Lessor or such Beneficiary incurred in connection with such +payment or the performance thereof or compliance therewith, + + -76- + + + + + +together with interest thereon at the Overdue Rate, shall be payable by +Lessee to Lessor or such Beneficiary (as Supplemental Rent) upon demand. +Lessor agrees to notify Lessee in writing prior to making any payment under +this Section 20(d) (giving Lessee the opportunity to rectify the omission), +unless the Aircraft will be in danger of loss, sale, confiscation, +forfeiture or seizure should such payment not be made. The taking of any +such action by Lessor or any Beneficiary pursuant to this Subsection 20(d) +shall not constitute a waiver or release of any obligation of Lessee under +the Lease, nor a waiver of any Event of Default which may arise out of +Lessee's nonperformance of such obligation, nor an election or waiver by +Lessor of any remedy or right available to Lessor under or in relation to +this Lease. + + (e) COUNTER PARTS. This Lease may be executed simultaneously in two or +more counterparts, each of which shall be deemed an original, but all of +which together shall constitute one and the same instrument. To the extent, +if any, that this Lease constitutes chattel paper (as such term is defined in + the Uniform Commercial Code as in effect in any applicable jurisdiction) no +security interest in this Lease may be created through the transfer or +possession of any counterpart other than the counterpart which has been +marked "Counterpart No. 1 - Chattel Paper Original" on the cover page +thereof. + + (f) QUIET ENJOYMENT. Lessor covenants that so long as an Event of +Default shall not have occurred and be continuing, Lessee shall be entitled +to quiet use, possession and enjoyment of the Aircraft in accordance with +the terms of this Lease, without interference by Lessor or by any Person +lawfully claiming by or through Lessor. + + (g) BROKERS. Each party agrees to indemnify and hold the other harmless +from and against any and all claims, suits, damages, costs and expenses +(including, but not limited to, reasonable attorneys' fees) asserted by any +agent, broker or other third party for any commission or compensation of any +nature whatsoever based upon the lease of the Aircraft, if such claim, +damage, cost or expense arises out of any action or alleged action by the +indemnifying party, its employees or agents. + + (h) FUNDING INDEMNITY. Without prejudice to any other rights Lessor or +each Beneficiary may have under the terms of this Lease or any of the +Operative Documents or otherwise, + + (A) if Lessor or each Beneficiary (and/or any of Beneficiary's + Affiliates) enters into any Dollar based bank funding or + financing arrangement (including, without limitation, any swap, + hedge or similar arrangement, but excluding any currency swap or + currency hedge to which Lessee has not + + -77- + + + + + + consented) in connection with the fixing of the Basic Rent, + which is payable during the Term, and if the Aircraft shall not + be delivered to Lessee in accordance with the terms of this + Lease (other than by reason of the willful default of Lessor), + Lessee shall indemnify Lessor or each Beneficiary (and/or any of + Beneficiary's Affiliates) against, and shall pay on an after tax + basis to Lessor or each Beneficiary (and/or any of Beneficiary's + Affiliates) forthwith on demand, subject to the proviso set + forth below, all costs, fees (including, without limitation, + legal fees) expenses, obligations, liabilities and any other + amounts notified by the Beneficiaries to the Lessee (which + notification shall set out in reasonable detail the calculation + of the amounts claimed therein and such notification shall, in + the absence of manifest error, be conclusive) as having been + incurred, suffered or expended by Lessor or any Beneficiary + (and/or any of Beneficiary's Affiliates) or arising in connection + with the amendment, variation, rolling over, termination, + reconstitution, hedging, unwinding, covering or other adjustment + of any Dollar based bank funding or financing arrangement + (including, without limitation, any swap, hedge or similar + arrangement, but excluding any currency swap or currency hedge to + which Lessee has not consented) entered into or committed by + Lessor or any Beneficiary (and/or any of Beneficiary"s Affiliates) + for the purposes of the financing of the Aircraft and/or its + leasing to Lessee on the basis of the Basic Rent provided for + under this Lease; PROVIDED, HOWEVER, that if the interruption of + the Beneficiaries' funding results in a gain to the Beneficiaries + (net of Taxes or other charges incurred in connection therewith), + then, so long as no Default exists, the Beneficiaries shall remit + the amount of such gain (net of their costs relating to the + negotiation, preparation and execution of this Agreement and the + consummation of the transactions contemplated hereby) to Lessee; + + (B) if an Event of Default occurs and the Aircraft is not delivered + or is repossessed in accordance with the terms of this Agreement + or otherwise, or if the Term is terminated earlier than the + Expiration Date by any reason including but not limited to the + early termination of the Term referred in clause 2(ii)(B) of the + Supplemental Agreement or a Casualty Occurrence (if such Casualty + Occurrence is covered by the insurance required by Section 12), + then Lessee shall indemnify Lessor or each Beneficiary (and/or + Beneficiary's Affiliate) against and shall pay on + + -78- + + + + + + an after tax basis to Lessor (and/or Beneficiary's Affiliate) + forthwith on demand, subject as provided below, all reasonable + costs, fees (including, without limitation, legal fees), expenses + obligations, liabilities and other amounts notified by the + Beneficiaries to Lessee (which notification shall set out the + calculation of the amounts claimed therein and such notification + shall, in the absence of manifest error, be conclusive) as having + been incurred, suffered or expended in connection with the + amendment, variation, rolling over, termination, reconstitution, + hedging, unwinding, covering or other adjustment of any Dollar + based bank funding or financing arrangement (including, without + limitation, any swap, hedge or similar arrangement, but excluding + any currency swap or currency hedge to which Lessee has not + consented) entered into or committed by Lessor or each Beneficiary + (and/or any of Beneficiary's Affiliates) for the purposes of the + financing of the Aircraft by Lessor or each Beneficiary and/or its + leasing of the Aircraft to Lessee on the basis of the fixed Basic + Rent provided for under this Lease; + + PROVIDED THAT the provisions of (A) or (B) shall not impose or be + deemed to impose on Lessee any obligation for payment of any + amount representing the principal amount of any bank funding + or financing arrangement. + + (i) PAYMENTS IN U.S. DOLLARS. All amounts to be paid hereunder to +Lessor or Lessee shall be paid in Dollars, in immediately available funds. +Lessee acknowledges that the specification of Dollars in this transaction is +of the essence and that Dollars shall be the currency of account in any and +all events. The obligations of Lessee or Lessor hereunder, to Lessor or +Lessee, respectively, shall not be discharged by an amount paid in another +currency, whether pursuant to a judgment or otherwise, to the extent that the + amount so paid on prompt conversion to Dollars under normal banking +procedures does not yield the amount of Dollars owing to Lessor. In the +event that any payment by Lessee or Lessor, respectively, whether pursuant +to judgment or otherwise to Lessor or Lessee, respectively, upon conversion +does not yield such amount of Dollars, Lessor or Lessee, as the case may be, +shall have a separate cause of action against Lessee or Lessor, as the case +may be, for the additional amount necessary to yield the amount of Dollars +due and owing to Lessor or Lessee, as the case may be. + + (j) COSTS. Each party hereto shall bear its own costs in respect of the +preparation, negotiation and execution of this Lease, the Lease Supplement +and any document ancillary thereto; PROVIDED, HOWEVER, that Lessor shall bear + + -79- + + + + + +the costs and expenses incurred in connection with the registration of the +Aircraft with the FAA (including the legal fees and expenses of special FAA +counsel) and Lessee shall bear the costs and expenses incurred in connection +with the filing and registration of the Lease and any change in the FAA +registration number of the Aircraft requested by Lessee and approved by the +Beneficiaries (including, without limitation, the legal fees and expenses of +special FAA counsel). + + (k) CONFIDENTIALITY. Neither Lessor nor Lessee hereto shall, at any +time during the Term or after its termination or expiration, disclose to any +Person any information relating to the matters set forth in this Lease or +the business, financial or other affairs a confidential nature relating to +the other party which may, in the course of the transactions contemplated +hereby, have been disclosed or discovered, except that any such information +may be so disclosed (i) with the written authority of the other party +hereto, (ii) so far as may be necessary for the proper performance of its +obligations hereunder, including the filing of this Lease with the FAA, +(iii) to the extent required by law, ordered by a court of competent +jurisdiction or by the relevant Governmental Entity responsible for taxation +or any other applicable regulatory authority, (iv) to such party's auditors, +counsel or other professional advisers, (v) to any Affiliate of a +Beneficiary and (vi) to the extent required in connection with a public +offering of debt or equity securities by Lessee. The parties hereto shall +use all reasonable endeavors to prevent any such disclosure as aforesaid, +except that Lessor or any Beneficiary may provide any potential purchaser of +the Aircraft, any Lender or any potential assignee of this Lease or any of +the Operative Documents with such information that any such person shall +reasonably request of Lessor or such Beneficiary. + + (l) SECTION 1110. The parties agree that Lessor and the Beneficiaries +are entitled to the benefits and protections of Section 1110 of the U.S. +Bankruptcy Code ("Section 1110"), and Lessee shall take such actions and +effect such filings as may be necessary to enable Lessor and the +Beneficiaries to continue to be entitled to such benefits and protections at +all times from the date of the execution and delivery hereof until such time +as the Aircraft is returned to Lessor or the Beneficiaries in compliance +with the return conditions herein and this Lease is terminated. Lessee +acknowledges that this Section is of fundamental importance to the +transactions contemplated hereby and that neither Lessor nor the +Beneficiaries would have entered into this Lease but for the rights intended +to be conveyed to Lessor and the Beneficiaries by this Section and the +protection and benefits of Section 1110. + + (m) CONCERNING THE LESSOR. Wilmington Trust Company is entering into +this Lease solely in its capacity + + -80- + + + + + +as Trustee under the Trust Agreement and not in its individual capacity. As +such, Wilmington Trust Company shall not be liable for or on account of any +statements, representations, warranties, covenants or obligations stated to +be those of Lessor hereunder. + + (n) TRUE LEASE. THIS LEASE SHALL CONSTITUTE AN AGREEMENT OF LEASE AND +IS A TRUE OPERATING LEASE, AND NOTHING HEREIN SHALL BE CONSTRUED AS +CONVEYING TO LESSEE ANY RIGHT, TITLE, OR INTEREST IN THE AIRCRAFT OR ANY +ENGINE OR PART EXCEPT AS A LESSEE ONLY. WITHOUT LIMITING THE FOREGOING, +LESSEE WILL NOT FILE A TAX RETURN OR TAKE ANY OTHER ACTION WHICH IS +INCONSISTENT WITH THE FOREGOING. + + Section 21. SECURITY DEPOSIT. + + (a) SECURITY DEPOSIT. Lessor hereby acknowledges Beneficiaries' receipt +from Lessee of the Security Deposit subject to the Security Deposit +Adjustment. In the event that the transactions contemplated by this Lease +are not consummated (other than due to the fault of Lessee or the occurrence +of an Event of Default), Lessor shall cause the Beneficiary to refund the +Security Deposit (so long as no Default or Event of Default has occurred, +without deducting any costs incurred by any Beneficiary or Lessor in +connection with the proposed leasing of the Aircraft hereunder but after +deducting the break funding costs incurred by the Beneficiaries and specified + in Section 20(h), if any) to Lessee. The Security Deposit is received by +Lessor or the Beneficiaries as security for the timely and faithful +performance by Lessee of all of Lessee's obligations under this Lease, and +Lessee hereby grants Lessor a security interest therein and in all other +sums deposited under this Section 21(a). Lessee agrees to execute and file +with the appropriate Governmental Entities any and all documents necessary +or reasonably requested by Lessor or any Beneficiary to evidence and perfect +such security assignment in favor of Lessor. If Lessee fails to pay Rent +hereunder when due or to pay any other sums due or to perform any of the +other terms and provisions of this Lease or is otherwise in Default +hereunder, in addition to all other rights Lessor shall have under the New +York Uniform Commercial Code as a secured party, Lessor may use, apply or +retain all or any portion of the Security Deposit in partial payment for +sums due to Lessor by Lessee, to compensate Lessor for any sums it may in +its discretion advance as a result of a Default by Lessee or to apply toward +losses or expenses Lessor may suffer or incur as a result of Lessee's +Default hereunder. If Lessor uses or applies all or any portion of such +Security Deposit, such application shall not be deemed a cure of any +Defaults, and Lessee shall within five (5) days after written demand +therefor deposit with Lessor in cash an amount sufficient to restore the +Security Deposit to the aggregate amount of such Security Deposit prior to +Lessor's use or application and the failure of Lessee to do so shall be a +material breach of + + -81- + + + + + +this Lease by Lessee. Provided Lessee is not in Default under this Lease, +such Security Deposit, less any costs incurred by Lessor in connection with +the termination of the Lease or return of the Aircraft, shall be returned to +Lessee promptly after the Expiration Date and Lessee's return of the +Aircraft in compliance with Section 16 hereof. Lessee shall not be entitled +to any interest earned on the Security Deposit. + + (b) SECURITY LETTER OF CREDIT. + + (i) Lessee hereby agrees with Lessor that, subject to paragraph + (vi) below, Lessee shall deliver to Lessor (or if specified by + the Beneficiaries, the Beneficiaries) the duly executed Security + Letter of Credit for the amount specified in Exhibit C-1. + + (ii) The Security Letter of Credit shall remain in full force and + effect from the date hereof until the date that is three months + after the Expiration Date, provided that it may be renewable + annually so long as Lessor or the Beneficiaries may draw upon the + Security Letter of Credit at any time if the Security Letter of + Credit is not renewed on or before thirty (30) days prior to its + stated expiration or if Lessor or a Beneficiary receives a + termination notice from the issuing bank or institution. + + (iii) Lessor or the Beneficiaries may draw upon the Security Letter of + Credit upon the occurrence and during the continuation of an + Event of Default and apply the proceeds thereof in or towards the + satisfaction of any sum or obligation due to Lessor or any + Beneficiary by Lessee which either Lessee fails to pay on the + due date or, as the case may be, the date of demand or after any + applicable grace period permitted for the payment thereof and/or + under this Lease or any of the Operative Documents or to + compensate Lessor or any Beneficiary for any sum which it may in + its discretion advance or expend as a result of the failure of + Lessee to comply with any of its obligations under this Lease or + any of the Operative Documents or in respect of any Default or + Event of Default, in respect of any indemnity payments, in any + case by delivering to the issuing bank or institutions a notice + in accordance with the terms of the Security Letter of Credit. + No such drawing under the Security Letter of Credit shall operate + to relieve Lessee of any obligation hereunder or constitute the + satisfaction of any such obligation (except to the extent of the + proceeds of such payment actually received by Lessor and the + Beneficiaries). + + -82- + + + + + + (iv) Upon any such drawing by Lessor or a Beneficiary, Lessee shall + reimburse the issuing bank and or a Beneficiary otherwise take + such action as may be necessary in order to restore the amount + available for drawing under the Security Letter of Credit to the + amount of such Security Letter of Credit specified in Exhibit C-1 + promptly (and in any event within five Business Days) after such + Lessor's drawing and shall procure promptly that the issuing bank + or institutions forward a further letter of credit or guarantee + to Lessor evidencing that the amount has so been restored. + + (v) Provided that no Default, or Event of Default has occurred and is + continuing, Lessor shall return the Security Letter of Credit to + Lessee for cancellation promptly after the date upon which the + Aircraft is redelivered to Lessor in accordance with the terms of + this Lease. + + (vi) Notwithstanding the foregoing provisions of this Section 21(b), + Lessee may, on the Delivery Date, in lieu of the delivery to the + Beneficiaries of the Security Letter of Credit on the Delivery + Date, deposit with the Beneficiaries an amount in cash equal to + the amount of the Security Letter of Credit, as specified in + Exhibit C-1 (the "Security Letter of Credit Amount"). If Lessee + elects to make such deposit in lieu of the delivery of the + Security Letter of Credit, then Lessee shall effect such deposit + by wire transfer of funds to an account specified by one of the + Beneficiaries, and such Security Letter of Credit Amount shall + constitute part of the Security Deposit and shall be held by the + Beneficiaries as collateral security for the payment and + performance by the Lessee of its obligations hereunder and under + the other Operative Documents. The Beneficiaries shall have the + same rights, and shall be permitted to apply such Security Letter + of Credit Amount in the same manner, as is set forth in + Sections 18(i) and 21(a) above with respect to the Security + Deposit. Following the Delivery Date, Lessee shall have the + option of substituting the Security Letter of Credit described + above in this Section 21(b) in exchange for the Security Letter + of Credit Amount deposited with the Beneficiaries pursuant to + this clause (vi). To effect such substitution, Lessee shall + deliver the originally issued and executed Security Letter of + Credit (in the form and scope and in the amount required by + Exhibit C-1) to Lessor, and, promptly after Lessor's receipt + thereof, the Beneficiaries shall, so long as no Default or Event + of Default shall have occurred and be continuing, return the + Security Letter of + + -83- + + + + + + Credit Amount paid on the Delivery Date pursuant to this + clause (iv) to Lessee. If Lessee does not substitute the Security + Letter of Credit Amount with the Security Letter of Credit, so + long as no Default or Event of Default shall have occurred and be + continuing, the Security Letter of Credit Amount will be returned + to Lessee promptly after the return of the Aircraft pursuant to + Section 21(a). + + -84- + + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this Lease to be +duly executed by their authorized officers as of the day and year first +above written. + + + LESSOR: + + WILMINGTON TRUST COMPANY, not in its + individual capacity but solely as + Trustee f/b/o ITOCHU AirLease + (Europe) Limited and Marubeni + Airleasing (U.K.) + + + By: /s/ Myfanwy Phillips Bonilla + ---------------------------- + Name: Myfanwy Phillips Bonilla + Title: Assis. Vice President + + + + LESSEE: + + ALOHA AIRLINES, INC. + + + By: + ---------------------------- + Name: + Title: + + + By: + ---------------------------- + Name: + Title: + + + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this Lease to be +duly executed by their authorized officers as of the day and year first +above written. + + + LESSOR: + + WILMINGTON TRUST COMPANY, not in its + individual capacity but solely as + Trustee f/b/o ITOCHU AirLease + (Europe) Limited and Marubeni + Airleasing (U.K.) + + + By: + ---------------------------- + Name: + Title: + + + + LESSEE: + + ALOHA AIRLINES, INC. + + + By: /s/ James M. King + ---------------------------- + Name: James M. King + Title: Vice President - Planning + and Development + + + By: /s/ Owen Sekimura + ---------------------------- + Name: Owen Sekimura + Title: Staff Vice President - + Finance and Controller + + + -15- + + + + + + EXHIBIT A + to + AIRCRAFT LEASE AGREEMENT + + SCHEDULE AND DESCRIPTION OF AIRCRAFT + + +
+ U.S. + MODEL AND REGISTRATION MANUFACTURER'S +ITEM MANUFACTURER CONFIGURATION NUMBER SERIAL NUMBER +------------------------------------------------------------------------------------------------------------------- + + +Aircraft The Boeing Company 737-25A N685MA* 23791 + +Engines Pratt & Whitney JT8D-17A P709529 + + Pratt & Whitney JT8D-17A P709530 + +Auxiliary + Power Unit Garret GTCP85-129 P35274 + +Landing Gears + +Left Gear Menasco 65-46100-52 MC02895P1486 + +Right Gear Menasco 65-46100-52 MC02896P1486 + +Nose Gear BF Goodrich 65-46200-63 T01406P1486 + +
+ + +*To be changed to N819AL + + + + + + EXHIBIT B-1 + to + Aircraft Lease Agreement + + Aircraft Documents on the Delivery Date + + +
+ +BOX 1: MIDWAY RECORDS +----------------------------------------------------------------------- + +725ML C check 5/28/89 1 of 2, 2 OF 2 IS + MISSING +----------------------------------------------------------------------- +725ML Hrs / Cycles 1990 thru 4/30/91 1 of 1 +----------------------------------------------------------------------- +725ML Log Pages Dec 89 - Jan / Feb 91 +----------------------------------------------------------------------- +725ML Log Pages March 1991 +----------------------------------------------------------------------- +725ML Log Pages Jan / Feb 91 +----------------------------------------------------------------------- +725ML Log Pages Sept/Oct/Nov/Dec 90 +----------------------------------------------------------------------- +725ML Log Pages July/Aug 90 +----------------------------------------------------------------------- +725ML Log Pages April/May/June 90 +----------------------------------------------------------------------- +725ML Log Pages Feb/March 90 +----------------------------------------------------------------------- +725ML N Items Nov 90 / 1 of 1 +----------------------------------------------------------------------- +725ML Job Cards 89/90 (structural repair + for aft cargo). +----------------------------------------------------------------------- +725ML Special Inspection 1 of 1 +----------------------------------------------------------------------- +725ML Line, TS, B1, B2, B3, 1990/1991 + B4, B5. +----------------------------------------------------------------------- +725ML CC1 Non-Routines 1 of 1 +----------------------------------------------------------------------- +725ML Component Change Cardex files (copies) + Records +----------------------------------------------------------------------- +725ML Hrs / Cycles 1 book 88-89 +----------------------------------------------------------------------- +725ML C check 11/23/90 (4 books) +----------------------------------------------------------------------- + +
+ + +
+ +BOX 2: MARKAIR RECORDS +----------------------------------------------------------------------- + +N685MA ROUTINE MAINT. LOGS/JAN. 1993 thru SEPT. 1995 +----------------------------------------------------------------------- + +
+ + + + +
+ +BOX 3: MARKAIR RECORDS +----------------------------------------------------------------------- + +N685MA ROUTINE MAINT. LOGS/Jan. 1992 thru Dec. 1992 +----------------------------------------------------------------------- +N685MA ROUTINE MAINT. LOGS/Oct. 1 thru Oct. 31, 1995 +----------------------------------------------------------------------- +N685MA Midway Fire Block data +----------------------------------------------------------------------- +N685MA Aircraft Historical File +----------------------------------------------------------------------- +N685MA Certificates / Permits / Documents +----------------------------------------------------------------------- +N685MA Miscellaneous Brochure +----------------------------------------------------------------------- +N685MA Midway AD File +----------------------------------------------------------------------- +N685MA Fuel Quantity Data (Boeing D6-32037-1) +----------------------------------------------------------------------- +N685MA Detail Specification (737-25A-Rev. B) +----------------------------------------------------------------------- +N685MA Readiness Log +----------------------------------------------------------------------- +N685MA Deferred Maintenance Log +----------------------------------------------------------------------- +N685MA Junk Tags / 1992, 1993, 1994 +----------------------------------------------------------------------- +N685MA Markair General Policies and Procedures Manual +----------------------------------------------------------------------- + +
+ + + + + + EXHIBIT B-1 + to + Aircraft Lease Agreement + + Aircraft Documents on the Delivery Date + + + +
+ +BOX 4: MARKAIR RECORDS +----------------------------------------------------------------------- + +N685MA B2, Inspection Check 4/7 - 4/10/92 +----------------------------------------------------------------------- +N685MA B2, Inspection Check 7/1 - 7/22/92 +----------------------------------------------------------------------- +N685MA B3, Inspection Check 9/15-9/17/92 +----------------------------------------------------------------------- +N685MA B&C, Inspection Check 11/23/91-1/13/92 (2 BOOKS) +----------------------------------------------------------------------- +N685MA C, Inspection Check 1/22-25/93 +----------------------------------------------------------------------- +N685MA B1, Inspection Check 7/21-7/23/93 (2 BOOKS) +----------------------------------------------------------------------- +N685MA B2, Inspection Check 10/19-10/21/93 +----------------------------------------------------------------------- +N685MA B4, Inspection Check 4/17-4/22/93 +----------------------------------------------------------------------- +N685MA B1, Inspection Check 7/6-7/7/94 +----------------------------------------------------------------------- +N685MA B2, Inspection Check 9/22-9/23/94 +----------------------------------------------------------------------- +N685MA B3, Inspection Check 12/6-12/8/94 +----------------------------------------------------------------------- +N685MA B3, Inspection Check 1/4 - 1/5/94 +----------------------------------------------------------------------- +N685MA B4, Inspection Check 4/2/94 +----------------------------------------------------------------------- +N685MA C2, Inspection Check 4/12/94 - 4/22/94 (2 BOOKS) +----------------------------------------------------------------------- +N685MA C3, Inspection Check 4/25 - 5/8/95 (2 BOOKS) +----------------------------------------------------------------------- +N685MA B1, Inspection Check 7/22/95 +----------------------------------------------------------------------- +N685MA B4, Inspection Check 2/12/95 - 2/15/95 +----------------------------------------------------------------------- + +
+ + +
+ +BOX 5: MARKAIR RECORDS +----------------------------------------------------------------------- + +N685MA ESN: 709529 / GTC shop visit DTD 6/29/92 +----------------------------------------------------------------------- +N685MA ESN: 700520 / GTC shop visit DTD 2/17/93 +----------------------------------------------------------------------- +N685MA ESN: 709529 / Historical Records +----------------------------------------------------------------------- +N685MA ESN: 709529 / Engine AD Log +----------------------------------------------------------------------- +N685MA ESN: 709529 / C2 Disk Traceability +----------------------------------------------------------------------- +N685MA ESN: 709530 / Historical Records +----------------------------------------------------------------------- +N685MA ESN: 709530 / AD Log +----------------------------------------------------------------------- +N685MA ESN: 709530 / WO LR 9122 (4 ea) Aviall 7/20/90 +----------------------------------------------------------------------- +N685MA ESN: 709530 / WO# LR 0181 (4 ea) Aviall 6/92 +----------------------------------------------------------------------- +N685MA APU S/N P-35274 / Historical Records +----------------------------------------------------------------------- +N685MA APU S/N P-35274 / AD Log +----------------------------------------------------------------------- + +
+ + -2- + + + + + + EXHIBIT B-1 + to + Aircraft Lease Agreement + + Aircraft Documents on the Delivery Date + + + + +
+ +BOX 6: MARKAIR RECORDS +----------------------------------------------------------------------- + +N685MA Airframe AD Log +----------------------------------------------------------------------- +N685MA Appliance AD Log +----------------------------------------------------------------------- +N685MA MEA Logs +----------------------------------------------------------------------- +N685MA Master Flight Log +----------------------------------------------------------------------- +N685MA SB Log +----------------------------------------------------------------------- +N685MA Galley Installation +----------------------------------------------------------------------- +N685MA EO and Drawings +----------------------------------------------------------------------- +N685MA Galley Manuals and Certification +----------------------------------------------------------------------- +N685MA MEA'S +----------------------------------------------------------------------- +N685MA S.B.'s +----------------------------------------------------------------------- +N685MA Windshear TCAS & Mode S Installation Data +----------------------------------------------------------------------- +N685MA Fleet Campaign's +----------------------------------------------------------------------- +N685MA Structural Repairs +----------------------------------------------------------------------- +N685MA Landing Gear Records +----------------------------------------------------------------------- +N685MA Weight & Balance Data +----------------------------------------------------------------------- +N685MA Markair Kardex File (Parts Tags) +----------------------------------------------------------------------- + +
+ + + + + EXHIBIT B-2 + to + Aircraft Lease Agreement + ------------------------ + + AIRCRAFT DOCUMENTS ON THE RETURN OCCASION + ----------------------------------------- + + All the Aircraft Documents delivered on the Delivery Date or any other +date (including without limitation those listed in Exhibit B-1) shall be +returned to Lessor along with the following: + +A. Manuals (in U.S. units) to be delivered with aircraft, as required: + + 1. FAA Approved Airplane Flight Manual. + 2. Weight and balance control and loading manual with supplements. + 3. Operations Manual. + 4. Aircraft Maintenance Manual. + 5. DDPG including MEL. + +B. In addition, one copy of each of the following manuals will be delivered + on the return occasion. + + 1. Structural repair manual and supplements. + 2. Aircraft/engine/spare engine/APU Maintenance manual. + 3. Aircraft/engine/spare engine/APU illustrated parts catalog. + 4. Aircraft/engine/spare engine/APU wiring diagram, hook-up charts, wire + list and equipment list. + 5. Maintenance and inspection procedures manual. + 6. General Policies & Procedures Manual. + +C. Airworthiness Directives Documentation. In accordance with 14 C.F.R. + Part 121.380, Lessee shall deliver to Lessor on the return occasion all + records associated with compliance airworthiness directives ("A.D."): + + 1. A single, complete and current A.D. status list of the airframe, + appliance, engines, spare engines and APU Airworthiness Directives and + mandatory FAA regulation applicable to the Airframe, Engines, Spare + Engines and APU. This list shall be provided in a format acceptable to + buyer and shall include, but not limited to: + + a. A.D. number and revision number. + b. A.D. title. + c. Aircraft serial number, engine serial number and APU serial number. + d. Engineering documentation reference. + e. Manufacturers service bulletin references and cross-references, where + appropriate. + + + + + + f. Specifications of terminated or repetitive status. + g. Date of initial accomplishment. + h. Date of last maintenance accomplishment, if repetitive. + i. Name and serial number of the internal maintenance form used to + document accomplishment, if applicable. + j. Specification of means by which compliance was accomplished + (e.g., modified, repaired, inspected). + + The list shall be typed, certified and signed by an authorized quality +assurance representative of the Lessee. + + 2. Legible copies of the completion documents that accomplish each A.D. +If the A.D. is a repetitive inspection, documentation of the last +accomplishment is sufficient. These documents must have the date of +accomplishment, the signature of a certified mechanic and/or inspector and +the mechanic's/inspector's certificate number or the repair station number +of the mechanic accomplishing the work. The document must reference the A.D. +number and company authorization which covered the A.D. + + 3. Exemptions or deviations granted by the FAA (or equivalent) to Lessor +or Lessee, as the case may be, on A.D. compliance, including copy of +exemption request. + + 4. Items 2 or 3 will be provided in individual document packages for +each A.D. Each package will contain all documents relative to that +A.D./Aircraft combination. + +D. Engineering Documentation + + 1. A single, current list of airframe, engines, spare engines and APU + service bulletins, engineering orders, major repairs and supplemental + type certificates completed on the airframe, each engine, spare engine + or the APU including references to engineering documentation, shall be + provided with information similar to that described in item C.1 above. + + 2. A current copy of all engineering documentation related to aircraft + alterations and repair configuration changes. This shall include + documentation for work done by any previous owner or operator, the + manufacturer or any vendor. An index shall be provided. A current copy + of all repairs that require follow-up action. + + 3. Data package covering all non-manufacture/non-FAA approved repairs or + alterations, including the submittal to the FAA for an STC or Form 337, + if applicable. + + 4. All open engineering deviations or material review board (MRB) records + applicable to the airframe, engines, spare engines, APU's, parts, + components and piece parts. + + -2- + + + + + +E. Additional Documentation + + 1. Maintenance program document. + 2. Location map of emergency equipment, with description. + 3. Interior configuration drawings. + 4. Weight and balance current status. + 5. FAA Export Certificate of airworthiness, if applicable. + 6. FAA certification of all components. + 7. FAA import certificate of airworthiness for all non-U.S. spare parts. + 8. Complete set of maintenance operations specifications. + 9. Master aircraft flight log. + +F. Individual aircraft, engine and spare engine records + + 1. Major structural damage reports, if any. + 2. FAA Form 337, or equivalent, for major repair and alterations, if any. + 3. Engine/spare engine last shop visit report. + 4. Documentation and records concerning the last aircraft overhaul. + 5. Engine/spare engine trend monitoring data. + 6. Log books for last 12 months. + 7. List of open items, if any. + 8. Declaration for aircraft accident and major repairs, if any. + 9. Listing of aircraft, engine and spare engine components status by + P/N - S/N description position - TBO - TSI - TSO - total time. + 10. Engine or spare engine life limited parts status and history (certified). + 11. Engine or spare engine log book, if applicable. + 12. APU - same documentation as items F. 10 and 11 above. + 13. Delivery letter w/serial number, total time/total cycles, time to the + next inspection and the time to the inspection or removal of + engines/spare engines or components. + 14. All engines, spare engines and APU records, up to and including the + last major overhaul or heavy maintenance and shop visits. + 15. Manufacturers delivery document for engines, spare engine and APU. + 16. Last test cell run documents for engines, spare engines and APU. + 17. Borescope history for current installation for engines and spare + engine, if applicable. + 18. Aircraft readiness log (manufacturers). + 19. Component records - time controlled components historical records + w/installation and serviceability tags. + + -3- + + + + + + EXHIBIT C-1 + to + AIRCRAFT LEASE AGREEMENT + + FINANCIAL INFORMATION + +Basic Rent: The Basic Rent payable monthly in advance on + each of the Basic Rent Payment Dates shall be + $115,279.80 per month. + +Basic Rent Payment Date: The Delivery Date and the date in each + subsequent month that numerically corresponds + to the Delivery Date; PROVIDED, that if any + such subsequent month does not have a date + that numerically corresponds to the Delivery + Date, the last day of such month; PROVIDED, + FURTHER, that if such date is not a Business + Day, then the Basic Rent Payment Date shall + be the next preceding Business Day. + +Casualty Value: As at the Delivery Date and for the one year + period following the Delivery Date, the + Casualty Value shall be $14,000,000, and the + Casualty Value shall be reduced by $500,000 + effective on each anniversary thereof which + reduced Casualty Value shall remain effective + for the one year period following the date of + such reduction, as follows: + + +
+ DATE OF CASUALTY + REDUCTION VALUE + --------------- ----------- + + Delivery Date $14,000,000 + 1st anniversary 13,500,000 + 2nd anniversary 13,000,000 + 3rd anniversary 12,500,000 + 4th anniversary 12,000,000 + 5th anniversary 11,500,000 + 6th anniversary 11,000,000 + 7th anniversary 10,500,000 + and after +
+ +Security Deposit: Means a cash deposit in the amount of U.S. + $115,000 paid by Lessee to Lessor or the + Beneficiaries on November 10, 1995, together + with any increases in such amount pursuant + to Section 21(a) of the Lease and the + following definition of Security Deposit + Adjustment. + +Security Deposit Adjustment: The Security Deposit Adjustment Amount shall + be the amount, if any, by which the amount of + monthly Basic Rent specified above exceeds + the amount of the Security Deposit specified + above ($279.80), and an amount equal to such + difference shall be paid by Lessee to the + Beneficiaries on the Delivery Date for + application to the Security Deposit. + +Security Letter of Credit: Means an irrevocable letter of credit or + guarantee in the amount at all times the + greater of US$ 230,000 and an amount equal to + the aggregate of two (2) months Basic Rent + payments ($230,559.60), issued by an + institution and in form and substance + acceptable to Lessor and Beneficiary. + + + + + EXHIBIT C-2 + to + AIRCRAFT LEASE AGREEMENT + + DEFINITIONS AND VALUES + +Beneficiary: Means each of ITOCHU AirLease (Europe) + Limited and Marubeni Airleasing (U.K.) + Limited as beneficiary under the Trust + Agreement, together in each case with its + successors and permitted assigns under the + Trust Agreement. + +Beneficiary's Address: For ITOCHU AirLease (Europe) Limited: + + Itochu AirLease (Europe) + Limited + 1, Earlsfort Centre + Hatch Street + Dublin 2 + Ireland + + Fax: 353-1-661-3278 + Attention: Secretary + + with copy to: + + ITOCHU AirLease Corporation + NXB Aoyama Building 5 Fl., + 26-37 Minami-Aoyama 2-Chome, + Minato-Ku, + Tokyo, + Japan + + Telex: 2423154 TKAFCJ + Fax: (813) 3497 8145 + Attention: the President + + For Marubeni Airleasing (U.K.) + Limited: + + 120 Moorgate + London EC2M 6SS + United Kingdom + Telecopy No.: (44) 171-826-8835 + +Deductible Amount: $500,000 + +Delivery Location: such state in the continental United States + reasonably determined by the + + + + +Trustee: means Wilmington Trust Company, a Delaware + banking corporation, not in its individual + capacity but solely as trustee under the + Trust Agreement or any successor Trustee + thereunder. + + -6- + + + + + + COUNTERPART NO.____ + + EXHIBIT D + to + AIRCRAFT LEASE AGREEMENT + + LEASE SUPPLEMENT NO. 1 + + TO AIRCRAFT LEASE AGREEMENT + DATED AS OF NOVEMBER 29, 1995 + + LEASE SUPPLEMENT NO. 1, dated December 1, 1995, between +Wilmington Trust Company, not in its individual capacity but solely as Trustee +f/b/o ITOCHU AirLease (Europe) Limited and Marubeni Airleasing (U.K.) Limited +("Lessor"), and Aloha Airlines, Inc., a company organized under the laws of +Hawaii ("Lessee"). + + Lessor and Lessee have previously entered into that certain +Aircraft Lease Agreement dated as of November 29, 1995 (herein called the +"Lease" and the defined terms therein being hereinafter used with the same +meaning). The Lease provides for the execution and delivery from time to time +of a Lease Supplement substantially in the form hereof for the purpose of +leasing the aircraft described below under the Lease as and when delivered by +Lessor to Lessee in accordance with the terms thereof. + + The Lease relates to the Aircraft, Parts and Engines as more +precisely described below. A counterpart of the Lease is attached hereto and +this Lease Supplement and the Lease shall form one document. + + In consideration of the premises and other good and +sufficient consideration, Lessor and Lessee hereby agree as follows: + + 1. Lessor hereby delivers and leases to Lessee under the +Lease and Lessee hereby accepts and leases from Lessor under the Lease, that +certain Boeing Model B-737-25A commercial jet aircraft, Airframe and the two +Pratt & Whitney JT8D-17A Engines (each of which engines has 750 or more rated +takeoff horsepower or the equivalent of such horsepower) described in Schedule +1 attached hereto and made a part hereof (the "Delivered Aircraft"). The +status of the Aircraft and Engines upon delivery thereof to Lessee shall be as +described on Schedule 2 attached hereto and made a part hereof. + + 2. (A) The Delivery Date of the Delivered Aircraft is the +date of this Lease Supplement set forth in the opening paragraph hereof. + + (B) The time of the delivery and acceptance of the +Delivered Aircraft is ____:____ __. m., at which time the + + + + +Delivered Aircraft is located in the airspace over the State of Texas, United +States of America (such location is herein called the "Delivery Location"). + + 3. The Term for the Delivered Aircraft shall commence on the +Delivery Date and shall end on the Expiration Date, which shall be December 1, +2003. + + 4. The amount of Basic Rent for the Delivered Aircraft is +payable in advance on each Basic Rent Payment Date. + + 5. Lessee hereby confirms to Lessor that (i) the Delivered +Aircraft and each Engine installed thereon or belonging thereto have been duly +marked in accordance with the terms of Section 6(f) of the Lease, (ii) Lessee +has accepted the Delivered Aircraft for all purposes hereof and of the Lease +and (iii) Lessee has inspected the Delivered Aircraft and the Delivered +Aircraft satisfies the conditions set forth in the Lease. + + 6. Lessor represents that all of its representations and +warranties contained in the Lease are true and correct on the date hereof. + + 7. All of the terms and provisions of the Lease Agreement are +hereby incorporated by reference in the Lease Supplement to the same extent as +if fully set forth herein. + + 8. The Lease Supplement may be executed in any number of +counterparts, each of such counterparts, except as provided in Section 20 (e) +of the Lease, shall for all purposes be deemed to be an original and all such +counterparts shall together constitute but one and the same Lease Supplement. + +THIS LEASE SUPPLEMENT HAS BEEN EXECUTED IN MULTIPLE COUNTERPARTS, OF WHICH +THERE IS ONE COUNTERPART DESIGNATED AS "COUNTERPART NO. 1 - CHATTEL PAPER +ORIGINAL" AND ALL OTHER COUNTERPARTS SHALL BE DESIGNATED WITH NUMBERS OTHER +THAN 1. TO THE EXTENT THAT THIS LEASE SUPPLEMENT CONSTITUTES CHATTEL PAPER +UNDER THE UNIFORM COMMERCIAL CODE, AS IN EFFECT IN ANY APPLICABLE JURISDICTION, +NO SECURITY INTEREST MAY BE CREATED OR PERFECTED BY POSSESSION OF ANY +COUNTERPART OF THIS LEASE SUPPLEMENT OTHER THAN SAID COUNTERPART NO. 1. + + + -2- + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this +Lease Supplement to be duly executed by their authorized officers as of the +day and year first above written. + + LESSOR: + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee + + By:__________________________________ + Name: + Title: + + LESSEE: + + ALOHA AIRLINES, INC. + + By:__________________________________ + Name: + Title: + + By:__________________________________ + Name: + Title: + + -3- + + + + + + SCHEDULE 1 + TO + LEASE SUPPLEMENT NO. 1 + + BOEING 737-25A + AIRFRAME + + +
+MANUFACTURER'S U.S. REGISTRATION +SERIAL NO. NUMBER TOTAL TIME* TOTAL CYCLES* +-------------- ----------------- ----------- ------------- + + +23791 N685MA 23,859 14,057 + (to be changed to + N819AL) + +
+ + INSTALLED PRATT & WHITNEY + ENGINES + +
+ +MODEL NO. SERIAL NO. TOTAL TIME* TOTAL CYCLES* +-------------- ----------------- ----------- ------------- + + + +JT8D-17A P709529 20,960 11,883 + +JT8D-17A P709530 22,100 12,908 + +
+ + INSTALLED + AUXILIARY POWER UNIT + + +
+ +MODEL NO. SERIAL NO. TOTAL TIME TOTAL CYCLES +-------------- ----------------- ----------- ------------- + +GTCP85-129 P35274 (TACH Reading) + +
+ + + + + + SCHEDULE 1 + TO + LEASE SUPPLEMENT NO. 1 (cont'd) + + B737 AVIONICS + COMPONENT INVENTORY + + + +
+ +ITEM PART NUMBER SERIAL NUMBER DESCRIPTION POSITION/QUANTITY + NO. + + + 1 622-4096-001 4581 PA Amp E&E 1 + 2 5140-1-112 339 Remote Electronics Unit E&E 1 + 3 622-8971-020 2119 TCAS T/R E&E 1 + 4 622-1181-001 14400 VHF T/R E&E 1 + 5 622-1396-001 5023 VHF T/R E&E 1 + 6 622-7878-200 5128/5165 ATC E&E 2 + 7 NA 13UO2 255AKWO022 Selcal E&E 1 + 8 622-2921-001 10076/10061 DME E&E 2 + 9 080-20325-01 4603/4613/4602 T/Rectifier E&E 3 + 10 2-301-3 4782 Batt Charger E&E 1 + 11 39B168-1-0 3276 Static Inverter E&E 1 + 12 65-52811-109 D00485 Landing Gear Acc Unit E&E 1 + 13 42-651-01 443 Anti-Skid E&E 1 + 14 65-52810-25 D01002 Air Condition Relay Unit E&E 1 + 15 65-73606-69 D01029 Eng Acc Unit E&E 1 + 16 65-52808-7 D00496 Compartment Overheat Unit E&E 1 + 17 65-52806-235 D00870 Misc Solid State Switch E&E 1 + 18 165-52807-26 D00449 Slat Position Switch Unit E&E 1 +
+ + + -2- + + + + + + +
+ +ITEM PART NUMBER SERIAL NUMBER DESCRIPTION POSITION/QUANTITY + NO. + + + 19 522-2996-011 2012 Marker Beacon E&E 1 + 20 777-1492-002 1582 ADF E&E 1 + 21 777-1492-004 4039 ADF E&E 1 + 22 965-0876-001 304 GPWS E&E 1 + 23 622-3890-003 1556 Radio Alt E&E 1 + 24 65-52801-31 D00446 APU Control Unit E&E 1 + 25 65-52809-15 D00306 Fire Detector Unit E&E 1 + 26 8TJ45-GAB X0062 34B3 Fuel Flow Power Supply E&E + 27 231-2 5556/6517 Window Heat Control E&E 4 + 28 4314/7490 E&E + 29 522-3949-001 3701 Monitor Comparator E&E 1 + 30 622-3257-001 5662/2275 ILS NAV RCVR E&E 2 + 31 172938-001 6620/6623 NAV SW Unit E&E 4 + 32 6630/6613 E&E + 33 HG48B13 P255/G1711 ADC E&E 2 + 34 4030952-906 88062826 Yaw Damper E&E 1 + 35 163356-88-01 0000528 Performance Data Comp E&E 1 + 36 548376-6 097C-9045 Cabin Temp Controller E&E 1 + 37 87093613 P43 Pressure Controller E&E 1 + 38 2588424-902 84053253 Rate of Turn E&E 1 + 39 101 812802UA/9118557A Compass Rack E&E 2 + 40 622-2500-004 2343/1298 Steering Comp E&E 2 +
+ + + + + + +
+ +ITEM PART NUMBER SERIAL NUMBER DESCRIPTION POSITION/QUANTITY + NO. + + 41 65-52805-317 D01011 Flight Inst Acc Unit E&E 1 + 42 172938-001 6624/6615 Switch Unit E&E 4 + 43 6595/2678 E&E + 44 2590650-901 05872772 Mach Trim Comp E&E 1 + 45 2588810-904 01851529 Pitch Computer E&E 1 + 46 2588812-902 06871639 Roll Computer E&E 1 + 47 65-52812-107 D00496 Auto Pilot Acc Unit E&E 1 + 48 457300-0430 3394 Omega RPU E&E 1 + 49 25582-003 8305313 Battery E&E 1 + 50 2587335-11 84054627 Vert Gyro E&E 1 + 51 2587335-11 8415034 Vert Gyro E&E 1 + 52 2587335-11 0034163 Vert Gyro E&E 1 + 53 2588302-4 0067044 Direction Gyro E&E 1 + 54 2588302-4 84056507 Direction Gyro E&E 1 +
+ + + + + + SCHEDULE 2 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status + ON THE DELIVERY DATE + +AIRFRAME HEAVIEST CHECK ("D" CHECK OR EQUIVALENT): + + + + + Interval: 28,000 (Hrs) + Time Remaining: 4,141 + +LANDING GEAR OVERHAUL: + + Interval: Main Gear 24,000 cycles + Nose Gear 24,000 cycles + + Time Remaining: Left Gear 9,943 cycles + Right Gear 9,943 cycles + Nose Gear 9,943 cycles + +ENGINES: + + Overhaul Interval: + Time Remaining (S/N P709529): + Time Remaining (S/N P709530): + + Hot Section Inspection: + + Interval: + Time Remaining (S/N P709529): + Time Remaining (S/N P709530): + +TIME REMAINING TO FIRST RESTRICTION: + + Engine S/N: P709529 + Hours: 9,040 + Cycles: 3,117 (C07) + Engine SIN: P709530 + Hours: 7,900 + Cycles: 2,092 (C07) + +
+ + + + + + + +AUXILIARY POWER UNIT: + + Hot Section Inspection: + + Interval: On Condition + Time Remaining: On Condition + + Time Remaining to Next Limited + or Scheduled Removal: On Condition + +
+ + -2- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + Effective date: 10/20/95 + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 21 +------ +Valve, Safety 720737-5 L-0001 14000H 13,817.6 3,977.2 +RH OH 7/3/92 +Valve, Safety 720737-5 830678 14000H 13,817.6 3,977.2 +LH OH 7/3/92 +Gasper Fan 21520-1 063 6000H 22,594.7 4,735 + OH 6/15/95 + +ATA 23 +------ +Recorder, 93A100-80 52428 72 Mo. 14, 623.2H 2 Yrs., 6 Mo. +Voice RPL +(Battery) + +ATA 24 +------ +APU Generator 976J498-1 YK13122 6000H 20,471 2,602H + HT 10/20/94 +Panel Bus 915F213-2 WE1347B 6000H 19,313.9 1,454H +Protection BC 6/11/94 +Gen. Cont. 948F458-1 NM4963 6000H 19,616.8H 1,757H + Unit BC 7/18/94 + APU +Gen. Cont. 948F458-1 PM5071 6000H 23,697H 5,838H + Unit BC 10/3/95 + Left +Gen. Cont. 948F458-1 XJ2186 6000H 21,639.9H 3,780.9H + Unit BC 2/18/95 + Right + +ATA 25 +------ +Life Vest KSE-35L8 527-691 24 Mo. 8/30/95 9 Mos. + Rt. Aft OH 23,304H +Life Vest AV-35 541-064 24 Mo. 2/95 14 Mos. + Co-pilot O H 21,530H +Slide, Evac D31354- 1112 24 Mo. 8/23/94 10 Mos. + Rt. Aft 425 OH 19,939.7H + +
+ + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 25 +------ +Slide, Evac D31354- 1613 24 Mo. 8/24/94 10 Mos. + Lt. Aft 433 OH 19, 949H +Slide, Evac 11611-143 123-1 24 Mo. 9/12/95 23 Mos. + Rt. Fwd OH 23,459H +Slide, Evac D31354- 0159 24 Mo. 2/15/95 7 Mos. + Lt. Fwd 403 OH 21,620H + +Escape Slide SEE NOTE (1) OF THE MARKAIR MAINTENANCE PROGRAM + Bottles (4) + +Life Vest KSE-35L8 50287 24 Mo. 3/17/95 9/96 + Obs OH 21,942H 8 Mos. +Life Vest KSE-35L8 12323 24 Mo. 8/1/95 7/97 + Capts OH 23,085H 22 Mos. +Life Vest KSE-35L8 527-691 24 Mo. 8/30/95 7/97 +Aft Right OH 23,304H 21 Mos. +Life Vest KSE-35L8 29358 24 Mo. 2/3/95 12/96 +Left Aft OH 21,474H 8 Mos. +Life Vest AC-2 535-380 24 Mo. 4/29/95 3/97 +Fwd FA OH 22,259H 17 Mos. +Life Vest KSE-35L8 541-064 24 Mo. 2/7/95 1/97 +Co-pilot OH 21,530.7 15 Mos. +Kit, Medical 017093 NHC1613 24 Mo. 11/28/94 11/96 + OH 20,844H 13 Mos. +PEE 4566M378 E9134322 120 Mo. 12/31/91 1/2000 +R/A Closet OH 60 Mos. +PEE 4566M378 E9134326 120 Mo. 12/31/91 1/2000 +L/A Closet OH 60 Mos. +PEE 4566M378 E9134942 120 Mo. 1/13/92 1/2000 +Aft Closet OH 60 Mos. +PEE 4566M378 E9134751 120 Mo. 12/22/91 1/2000 +Row 10 OH 60 Mos. +PEE 4566M378 E9548209 120 Mo. 6/9/95 5/2002 +Cockpit OH 70 Mos. + +
+ + -2- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 26 +------ +Cartridge, 30903828 AEN-1-3 72 Mos. 5/28/93 50 Mos. +Fire RPL +ext.: Rt. +Cartridge, 30903828 BTL23883B 120 Mos. 5/5/95 5/2005 +Fire 1 RPL +ext.: APU +Cartridge, 30903828 09015DI 72 Mos. 4/27/95 2/2001 +Fire RPL +ext.: Left +Fire Bottle 3307507 23883B1 WC/12 Mos 5/5/95 5/96 +APU HST/60 Mo 5/5/95 5/2000 +Fire Bottle 33600011 10124D1 WC/12 Mos 4/27/95 5/98 +Rt Eng. HST/60 Mo 5/14/93 4/98 +Fire Bottle 33600011 09015D1 WC/12 Mos 4/27/95 4/96 +Left Eng. HST/60 Mo 2/16/95 2/2000 +Fire Ext. 892480 672AC WC/12 Mos 5/1/95 5/96 +Aft HST/60 Mo 7/96 +Closet(H20) +Fire Ext. 898052 A-415392 WC/12 Mos 5/1/95 5/96 +Aft. (halon) HST/60 Mo 11/91 7/96 +Fire Ext. 898052 B-615379 WC/12 Mos 5/1/95 5/96 +Lt row HST/60 Mo 7/96 +10/halon +Fire Ext. 898052 V-343826 WC/12 Mos 4/26/95 5/99 +Fwd/(halon) HST/60 Mo +Fire Ext. 898052 E494480 WC/12 Mos 4/29/95 4/96 +cockpit/halon HST/60 Mo 5/99 +Fire Bottle, A800100-1 26-904 WC/12 Mos 4/13/94 Over Due +Lav. Aft HST/60 Mo +Fire bottle, A800100-1 26-906 WC/12 Mos 4/13/94 Over Due +Lav. Fwd HST/60 Mo + +
+ + -3- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 27 +------ +Jackscrew, 65-49970- KE00301 8000H 19,946H 3,787H +Stab. trim 17 OH 8/24/94 +Fuse assy 6213-4 219 22,400H 19,945H 18,487H +auto BC +brk/rudder +Transmission 65-50321- 0812 16000H 15,275H 7,416H +No 1 flap 5 OH +Transmission 65-50322- 0840 16000H 15,275H 7,416H +No 2 flap 12 OH +Transmission 65-50323- 0911 16000H 15,275H 7,416H +No 3 flap 5 OH +Transmission 65-50324- 0784 16000H 15,275H 7,416H +No 4 flap 3 OH +Transmission 65-50325- 50007 16000H 15,275H 7,416H +No 5 flap 5 OH +Transmission 65-50326- 62003 16000H 15,275H 7,416H +No 6 flap 6 OH +Transmission 65-50327- 72004 16000H 15,275H 7,416H +No 7 flap 14 OH +Transmission 65-50328- 82002 16000H 15,275H 7,416H +No 8 flap 7 OH +Gearbox assy 65-51510- 7417 16000H 15,275H 7,416H +Rt angle/Left 1 OH +Gearbox assy 65-51510- 1489 16000H 15,275H 7,416H +Rt angle/Rt 2 OH +Gearbox assy 65-51513- 4778 16000H 15,275H 7,416H +Flap dr./Left 1 OH +Gearbox assy 65-51513- 1877 16000H 15,275H 7,416H +Flap dr./Rt. 2 OH +Power Unit 65-63851- 7241 16000H 15,275H 7,416H +Flap dr GBX 7 OH + +
+ + + -4- + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 28 +------ +APU Fuel AV16EI209 H1842C 20000H 19,898H 16,025H +S/0 VLV D OH 8/19/94 +Left Fuel 61185 128 200001-1 19,876H 16,003H +S/0 VLV OH 8/16/94 +Right Fuel 61185 1813 20000H 20,015H 16,142H +S/0 VLV OH 8/31/94 +CF Fuel 61185 609 20000H 20,005H 16,146H +S/0 VLV OH 8/31/94 + +ATA 31 +------ +ULB Batteries 9804100- 7040 6 Yrs. 12 Mar 94 41 Mos. + GXUS RPL + +ATA 32 +------ +Fuse assy 6213-4 627 22,400H 19,917.7H 18,458H +Auto OH 8/21/94 +Brk/Rudder + +ATA 34 +------ +Altimeter, WL102AMS AG102 24 Mo. 12/20/94 14 Mo. +Stby 3 IN +Transponder TPR 720 5128 24 Mo. 4/27/95 18 Mo. +Mode S, Lt. IN +Transponder TPR 720 5165 24 Mo. 4/27/95 18 Mo. +Mode S, Rt. IN + +
+ + -5- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 35 +------ +02 Cylinder 801307-00 223879/ 36 Mo. 6/22/94 5/97 + 29668 HST 5/94 19 Mo. +Portable 02 5500A1A- 771X/3596 60 Mo. 5/11/92 3/96 + Cyl BF23A 4 HST 5 Mo. +Portable 02 5500A1A- 151843P/1 60 Mo. 12/21/93 1/97 + Cyl BF23A 3 HST 15 Mo. +Row 1 5454 +Portable 02 56002C1AF 55934DB/2 60 Mo. 10/5/95 2/2000 + Cyl 23AM 3832 HST 58 Mo. +Aft closet +Portable 02 5600-2ClA 74732DF/ 60 Mo. 1/12/93 1/98 + Cyl 41819 HST 26 Mo. +Cockpit + +02 Generators Varies 12 Yrs 22 Mo. +------------- ------ ------ ------ + 117003-17 12 Yrs 22 Mo. + 117.00-12 12 Yrs 22 Mo. + 803000-02 12 Yrs 22 Mo. + 117003-12 12 Yrs 22 Mo. + +
+ + + -6- + + + + + + SCHEDULE 4 + TO + LEASE SUPPLEMENT NO. 1 + + A/C N685MA + + R/H SIDE + + + +
+ +DESC. PART NUMBER SERIAL NUMBER MODEL NUMBER +----- ----------- ------------- ------------ + + 1. TRIPLE BURNS 86952102 310588 JB5.2-3-58 + 2. TRIPLE BURNS 86952024 310581 JB5.2-3-59 + 3. TRIPLE BURNS 86952022 310569 JB5.2-3-59 + 4. TRIPLE BURNS 86952022 310566 JB5.2-3-59 + 5. TRIPLE BURNS 86952022 310572 JB5.2-3-59 + 6. TRIPLE BURNS 86952022 310577 JB5.2-3-59 + 7. TRIPLE BURNS 86952022 310570 JB5.2-3-59 + 8. TRIPLE BURNS 86952022 310574 JB5.2-3-59 + 9. TRIPLE BURNS 86952022 310568 JB5.2-3-59 +10. TRIPLE BURNS 86952016 310562 JB5.2-3-58 +11. TRIPLE BURNS 86952022 310573 JB5.2-3-59 +12. TRIPLE BURNS 86952022 310579 JB5.2-3-59 +13. TRIPLE BURNS 86952022 310578 JB5.2-3-59 +14. TRIPLE BURNS 86952022 310571 JB5.2-3-59 +15. TRIPLE BURNS 86952022 310576 JB5.2-3-59 +16. TRIPLE BURNS 86952022 310564 JB5.2-3-59 +17. TRIPLE BURNS 86952022 310575 JB5.2-3-59 +18. TRIPLE BURNS 86952026 310582 JB5.2-3-59 +19. TRIPLE BURNS 86952014 310560 JB5.2-3-58 +20. DOUBLE BURNS 86953201 306409 JB5.2-2-39 + +
+ + -1- + + + + + + + A/C N685MA + + L/H SIDE + + +
+ +DESC. PART NUMBER SERIAL NUMBER MODEL NUMBER +----- ----------- ------------- ------------ + +1. TRIPLE BURNS 86952101 310587 JB5.2-3-58 +2. TRIPLE BURNS 86952007 310556 JB5.2-3-58 +3. TRIPLE BURNS 86952001 310548 JB5.2-3-59 +4. TRIPLE BURNS 86952001 310545 JB5.2-3-59 +5. TRIPLE BURNS 86952001 310541 JB5.2-3-59 +6. TRIPLE BURNS 86952001 310542 JB5.2-3-59 +7. TRIPLE BURNS 86952001 310539 JB5.2-3-59 +8. TRIPLE BURNS 86952001 310550 JB5.2-3-59 +9. TRIPLE BURNS 86952013 310559 JB5.2-3-58 +10. TRIPLE BURNS 86952015 310561 JB5.2-3-58 +11. TRIPLE BURNS 86952001 310544 JB5.2-3-59 +12. TRIPLE BURNS 86952003 310554 JB5.2-3-59 +13. TRIPLE BURNS 86952001 310547 JB5.2-3-59 +14. TRIPLE BURNS 86952001 310543 JB5.2-3-59 +15. TRIPLE BURNS 86952001 310553 JB5.2-3-59 +16. TRIPLE BURNS 86952001 310538 JB5.2-3-59 +17. TRIPLE BURNS 86952001 310538 JB5.2-3-59 +18. TRIPLE BURNS 86952001 310549 JB5.2-3-59 +19. TRIPLE BURNS 86952005 310555 JB5.2-3-59 +20. TRIPLE BURNS 86952017 310563 JB5.2-3-56 + +
+ + -2- + + + + + + +
+ + AUDITED THRU L/P 165891 10/20/95 + A/C TOTAL HOURS 23,859 + A/C TOTAL CYCLES 14,057 +INSTALLED ON N685MA POSITION 1 + HOURS CYCLES + LIMIT SINCE SINCE TOTAL TOTAL TIME REMAINING +ITEM POS PART NO S/N HOURS CYCLES NEW NEW HOURS CYCLES HOURS CYCLES + + + +ENGINE LEFT JT8D-17A P7095308 30,000 15,000 22,100 12,908 22,100 12,908 7,900 2,092 +HUB (DISK) C 01 817401G P78775 NO LIMIT 20,000 NO LIMIT 12,908 NO LIMIT 12,908 NO LIMIT 7,092 +DISK C 02 5002402-01 N87756 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 03 800803 N58289 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK (HUB) C 04 799504 P44646 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 05 745705 P93696 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 06 745706 P93306 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 07 774407 P81245 30,000 15,000 22,100 12,908 22,100 12,908 7,900 2,092 +DISK (HUB) C 08 5005808-01 N90080 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 09 701509 P97271 30,000 19,000 22,100 12,908 22,100 12,908 7,900 6,092 +DISK C 10 772510 P97761 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 11 772511 P03600 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 12 772512 P97578 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 13 5003613-01 P59603 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK (SHAFT) T 01 5003601-01 N36371 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK T 02 5003102-01 P03367 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK T 03 5003103-01 N64721 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK T 04 5003104-01 N52111 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +LPT (SHAFT) NO LIMIT 20,000 22,100 14,558 22,100 14,558 NO LIMIT 5,442 + +
+ + + + + +
+ + AUDITED THRU L/P 165891 10/20/95 + A/C TOTAL HOURS 23,859 + A/C TOTAL CYCLES 14,057 +INSTALLED ON N685MA POSITION 2 + HOURS CYCLES + LIMIT SINCE SINCE TOTAL TOTAL TIME REMAINING +ITEM POS PART NO S/N HOURS CYCLES NEW NEW HOURS CYCLES HOURS CYCLES + + + +ENGINE RIGHT JT8D-17A P7095308 NO LIMIT NO LIMIT 20,960 11,883 20,960 11,883 9,040 3,117 +HUB (DISK) C 01 817401 P23654 NO LIMIT 20,000 NO LIMIT 13,372 NO LIMIT 13,372 NO LIMIT 6,628 +DISK C 02 5002402-01C S56233 30,000 20,000 10,771 7,680 10,771 7,680 19,229 12,320 +DISK C 03 800803 P68371 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK (HUB) C 04 799504 N90833 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 05 745705 P93614 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 06 745706 P93230 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 07 774407 P81364 30,000 15,000 20,960 11,883 20,960 11,883 9,040 3,117 +DISK (HUB) C 08 5005808-01 P43608 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 09 701509 P97641 30,000 19,000 20,960 11,883 20,960 11,883 9,040 7,117 +DISK C 10 772510 P97278 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 11 772511 P03553 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 12 772512 P97219 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 13 5003613-01 P92918 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 01 5003601-01 N36374 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 02 5003102-01 M12012 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 03 5003103-01 N64611 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 04 5003104-01 N52069 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +LPT (SHAFT) NO LIMIT NO LIMIT + +
+ + + + + + +
+ + + AUDITED THRU L/P 165891 TOTAL A/C HOURS 23859 +INSTALLED ON: N685MA DATE 10/20/95 TOTAL A/C CYCLES 14057 + + +NLG ASSEMBLY 65-73762-5 T01406P1486 90,000 24,000 9,943 +SHOCK STRUT ASSY 65-46200-63 T01408P1486 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +OUTER CYLINDER ASSY 65-46210-25 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +OUTER CYLINDER 65-46211- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +STEERING PLATE, UPR 65-46210-27 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +STEERING PLATE, LWR (DRILLED) 65-46210-24 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +OUTER CYLINDER ASSY (DRILLED) 65-46215-14 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +METERING PIN ASSY 65-46200-54 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +METERING PIN 65-46219- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +SUPPORT TUBE, METERING PIN 65-46229- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +DRAIN TUBE 69-57991- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +SUPPORT TUBE, UPPER ORIFICE 69-73038-1 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TUBE, ORIFICE SUPPORT 65-46226-9 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +STEM, ORIFICE SUPPORT 65C25707- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +GLAND NUT ASSY 65-46221-4 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +ORIFICE PLATE, UPPER 69-36622-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TORSION LINK ASSY, UPPER 65-46202-9 JC-1917 90,000 24,000 08/28/92 14,378 7,445 23,971 6,612 9,481 65,028 17,388 +TORSION LINK ASSY, LOWER 65-46288-5 32-58 90,000 24,000 08/28/92 14,378 7,445 28,383 6,612 9,481 61,617 17,388 +STEERING COLLAR ASSY 65-46203-24 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, STEERING COLLAR 69-61383-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, STEERING COLLAR 69-61383- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, UPPER TORSION LINK 69-72698-1 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, LOWER TORSION LINK 69-72698-1 486-2 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, TORSION LINK APEX 69-35883-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +LOWER DRAG BRACE ASSY 65-46230-7 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +UPPER DRAG BRACE ASSY 65-80051-13 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, DRAG BRACE CENTER 69-35391- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, DRAG BRACE, CENTER NAS1110-68D 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, UPPER DRAG BRACE 69-35394-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, UPPER DRAG BRACE 66-35394-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, LOWER DRAG BRACE 69-35396-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +LOCK BRACE ASSY, AFT 65-46240-11 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +LOCK LINK ASSY, FWD 65-46242-11 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, NOSE GEAR LOCK JOINT NAS1105-52D 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +SHAFT, AFT LOCK MECHANISM 69-35385-1 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TRUNNION PIN, LEFT HAND 69-35398-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TRUNNION PIN, RIGHT HAND 69-41248- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 + +
+ + + + + + + +
+ + + AUDITED THRU L/P 165891 TOTAL A/C HOURS 23859 +INSTALLED ON: N685MA DATE 10/20/95 TOTAL A/C CYCLES 14057 + + +LEFT NLG ASSEMBLY 65-73761-87 MC026895P1488 100,000 24,000 9,943 +SHOCK STRUT ASSY 65-46100-52 MC028958P1488 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +OUTER CYLINDER ASSY 65-61740-10 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +INNER CYLINDER ASSY 65-46116-47 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE SUPPORT TUBE ASSY 65-46159-7 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT TUBE, ORIFICE 65-46160-7 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE PLATE 69-38980-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +METERING PIN 65-46124-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +DRAIN TUBE 65-46156-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +GLAND NUT 69-39485-10 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, LOWER 65-46102-22 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, UPPER 65-87958-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER DRAG STRUT ASSY 65-46103-11 1486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER DRAG STRUT ASSY 65-60579-3 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, LWR SIDE 65-46108-7 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ARM ASSY 65-46109-12 5104 100,000 24,000 12/16/91 12,391 5931 8,865 8,126 11,488 91,135 15,874 +TRUNNION PIN ASSY 65-46113-5 34ORGA 100,000 24,000 12/16/91 12,391 5931 66,508 8,126 11,488 31,492 15,874 +TEE BOLT ASSY, DRAG STRUT 65-46140-6 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TRUNNION LINK ASSY 65-63378-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, LOWER 69-72023-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, UPPER 69-72023-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, TEE FITTING ATTACHMENT 69-51833-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY.: LOWER DRAG STRUT 69-39473-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT ASSY, UPR DRAG 69-39476-6 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, TRUNNION PIN 69-41633-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, REACTION LINK 65-46107-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER SIDE STRUT ASSY 65-46138-5 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER SIDE STRUT ASSY 65-63397-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +REACTION LINK ASSY 65-46135-13 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, UPPER DOWNLOCK 65-46138-16 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, LOWER DOWNLOCK 65-46139-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, SPRING SUPPORT 69-38999-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, REACTION LINK 69-38148-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, UPPER SIDE STRUT 69-68149-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, SIDE STRUT 69-68150-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK LINK PIVOT 69-41628-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK 69-42193-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ASSY, UPPER DOWNLOCK 69-62779-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ASSY 65-46108-3 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT LINK ASSY 65-46112-22 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, UPLOCK 65-63887-5 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BELLCRANK ASSY 65-63663-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +HOOK ASSY 65-46142-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, UPLOCK SUPPORT 65-46146-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, REACTION LINK / UPLOCK 69-68148-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, OUTBD 69-39464-4 352 100,000 24,000 12/16/91 12,891 5931 8,126 8,126 11,468 91,874 15,874 +BOLT ASSY, LOWER SIDE STRUT 69-68151-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +COLLAR, BELLCRANK RET, UPLOCK 69-51838-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT, FWD TRUNNION BRG 69-58854-3 486 (ORIG) 83,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, INBRD BACB301J20CD43 685 100,000 24,000 12/14/91 12,891 5931 8,126 8,126 11,468 91,874 9,943 +BOLT, ACTUATOR ROD END BACB301J16CD48 685/1 100,000 24,000 12/14/91 12,891 5931 8,126 8,126 11,468 91,874 9,943 +BEARING ASSY, FWD TRUNNION 69-58871-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, AFT TRUNNION 65-84161-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 + +
+ + + + + +
+ + + AUDITED THRU L/P 165891 TOTAL A/C HOURS 23859 +INSTALLED ON: N685MA DATE 10/20/95 TOTAL A/C CYCLES 14057 + + +RIGHT NLG ASSEMBLY 65-73761-87 MC02665P1488 100,000 24,000 9,943 +SHOCK STRUT ASSY 65-46100-52 MC02896P1488 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +OUTER CYLINDER ASSY 65-61740-10 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +INNER CYLINDER ASSY 65-46116-47 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE SUPPORT TUBE ASSY 65-46159-7 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT TUBE, ORIFICE 65-46160-7 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE PLATE 69-38980-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +METERING PIN 65-46124-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +DRAIN TUBE 65-46156-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +GLAND NUT 69-39485-10 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, LOWER 65-46102-22 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, UPPER 65-87958-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER DRAG STRUT ASSY 65-46103-11 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER DRAG STRUT ASSY 65-60579-3 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, LWR SIDE 65-46105-7 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ARM ASSY 65-46109-12 1507 100,000 24,000 12/14/91 12,391 5931 34,655 8,126 11,488 65,345 15,874 +TRUNNION PIN ASSY 65-46113-16 32-118 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,488 91,674 15,874 +TEE BOLT ASSY, DRAG STRUT 65-46140-6 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TRUNNION LINK ASSY 65-63378-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, LOWER 69-72023-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, UPPER 69-72023-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, TEE FITTING ATTACHMENT 69-51833-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY.: LOWER DRAG STRUT 69-39473-3 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT ASSY, UPR DRAG 69-39476-6 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, TRUNNION PIN 69-41633-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, REACTION LINK 65-46107-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER SIDE STRUT ASSY 65-46138-5 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER SIDE STRUT ASSY 65-63397-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +REACTION LINK ASSY 65-46135-13 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, UPPER DOWNLOCK 65-46138-16 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, LOWER DOWNLOCK 65-46139-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, SPRING SUPPORT 69-38999-3 685 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BOLT ASSY, REACTION LINK 69-38148-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, UPPER SIDE STRUT 69-68149-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, SIDE STRUT 69-68150-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK LINK PIVOT 69-41628-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK 69-42193-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ASSY, UPPER DOWNLOCK0 69-62779-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ASSY 65-46108-3 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT LINK ASSY 65-46112-22 HI-106 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +LINK ASSY, UPLOCK 65-63887-5 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BELLCRANK ASSY 65-63663-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +HOOK ASSY 65-46142-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, UPLOCK SUPPORT 65-46146-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, REACTION LINK / UPLOCK 69-68148-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, OUTBD 69-39464-4 482 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BOLT ASSY, LOWER SIDE STRUT 69-68151-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +COLLAR, BELLCRANK RET, UPLOCK 69-51838-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT, FWD TRUNNION BRG 69-58854-3 486-2 (ORIG) 83,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, INBRD BACB301J20CD43 685/2 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BOLT, ACTUATOR ROD END BACB301J16CD48 685 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BEARING ASSY, FWD TRUNNION 69-58871-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, AFT TRUNNION 65-84161-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 + +
+ + + + + + EXHIBIT E + to + AIRCRAFT LEASE AGREEMENT + + RETURN CONDITION REQUIREMENTS + +On the Return Occasion the Aircraft, subject to fair wear and tear generally, +will be in the following condition: + +1 GENERAL CONDITION + +The Aircraft shall: + + (a) be clean by United States commercial airline standards; + + (b) have installed the full complement of Engines, Parts and + other equipment, parts and accessories and loose equipment + + (i) which were installed on the Aircraft at the Delivery Date (or + any replacements thereof made in accordance with the terms of + this Lease); + + (ii) as would be required by this Lease to be installed on the + Aircraft if Lessee were to continue operating the same in + continued regular service; + + (iii) each operating within limits approved by the FAA and fully + functioning in accordance with its intended use; + + (iv) which are in a condition suitable to permit immediate + operation of the Aircraft in commercial service under the + FAA; and + + (v) be in strict compliance with the Maintenance Program, + including the corrosion prevention control program + (CPCP/D6-38528) and the aging aircraft program (D6-38505 as + applicable per the airworthiness directive or per + effectivity). + + (c) (i) have in existence a current, valid and existing FAA + certificate of airworthiness for airline operation or an FAA + export certificate of airworthiness, as elected by the + Beneficiaries in accordance with the terms of Section 16(d); + + (ii) comply with (A)(1), all the requirements of US FAR Part 36 + Appendix C (Stage II) noise compliance (without waiver, + restriction, deferment or exception) and (2) all the + requirements for operation under United States FAR Part 121 + or (B)(1) if requested by the Beneficiaries in order to + comply with the requirements of any other country selected by + the Beneficiaries which follows the FAA rules and regulations + contemplated by U.S. FAR Part 129, U.S. FAR + + + + + + Part 121 and U.S. FAR Part 36, Appendix C and (2) all civil + aviation regulations, orders, rules and airworthiness + directives of such country to permit the immediate + re-registration and securing of a certificate of + airworthiness in such country for the Aircraft (provided that + (i) such regulations, orders, rules and Airworthiness + Directives are no more restrictive than those of the FAA, and + (ii) to the extent the work, standards or requirements + required to comply with this clause (B) is beyond the work, + standards or requirements otherwise required by Section 16 or + this Exhibit E, such work, standards or requirements will be + performed pursuant to work orders and workscopes agreed upon + by Lessee and the Beneficiaries and at the Beneficiaries' + cost, and the Term will not be deemed extended as a result of + such additional work). If requested to do so by the + Beneficiaries, Lessee shall cooperate reasonably to comply + with all requirements for domestic and over-water operations + under U.S. FAR Part 129, provided that (i) the Beneficiaries + inform Lessee of such requirements, (ii) Lessee and the + Beneficiaries mutually agree upon the work required for such + compliance and (iii) the Beneficiaries bear the cost of such + compliance to the extent such compliance requires parts or + labor not otherwise required to be performed by Lessee + pursuant to Section 16 or this Exhibit E, and the Term will + not be deemed extended as a result of such additional work + solely to comply with the requirements for domestic and + over-water operations under U.S. FAR Part 129. + + (d) (i) comply with the Detailed Specifications except and to the + extent that such specifications have been altered as required + or permitted by this Lease; + + (ii) except as otherwise provided in the Lease or in the + Supplemental Agreement or as consented to by each + Beneficiary, shall be in substantially the same configuration + (including but not limited to, interior seating + configuration, galleys and lavatories) as when the Aircraft + was originally delivered to Lessee hereunder, save where + changes have been made which do not impair its marketability + or decrease its value, as determined by each Beneficiaries' + sole determination; + + (e) (i) have had carried out on the Aircraft the final + completion and termination of all open, deferred or + continued maintenance items, and shall have had completed, on + a terminating action basis, where terminating action is due + for compliance, all outstanding mandatory, required, ordered + and "earliest convenience" airworthiness directives issued by + the FAA and all mandatory, alert, earliest convenience and + recommended service bulletins (so long as the Manufacturer + issues such service bulletins) (insofar, in the case of + recommended service bulletins, as required by the + Beneficiaries after consultation with Lessee, in which case + any such recommended service bulletin shall be carried out + (y) at Lessee's expense if Lessee shall be carrying or shall + have carried out such service bulletin with respect to a + majority of aircraft comprising its 737 fleet or (z) at the + Beneficiaries' expense pursuant to the workscope or work + order agreed upon by the Beneficiaries and Lessee, if Lessee + shall not be carrying out such service bulletin with respect + to a majority of aircraft comprising its 737 fleet) issued by + the Manufacturer in + + + -2- + + + + + + each case which have a compliance or termination date on or + before the date three (3) months (six (6) months upon request + of and at the cost of the Beneficiaries and the Term will not + be deemed extended solely as a result of such additional work + required to terminate the airworthiness directives that are + due after three (3) months following the Return Occasion and + requested by the Beneficiaries) after the date of the Return + Occasion (without waiver, restriction, deferment, exception, + carry over, watch or repetitive status items or being on + watch, except, in the case of airworthiness directives, (i) + as expressly provided in the Supplemental Agreement or (ii) + as Lessor shall otherwise specify in writing to Lessee); + + (ii) have had permanently and properly repaired any damage to + the Aircraft caused by ground handling equipment or impact + damage caused by foreign objects which occurred during the + Term that exceeds Manufacturer's limits for operation without + restrictions or repetitive inspections; + + (iii) not have any open, deferred, continued or placarded log book + items; + + (f) all exterior logos and other proprietary marks shall be removed or + painted over in accordance with proper industry practice and in a + workmanlike manner; provided that, at the Beneficiaries' request, + Lessee shall have Lessee's paint and markings stripped and + re-painted white or in the livery selected by the Beneficiaries so + long as the Beneficiaries bear the cost of such stripping and + repainting, as mutually agreed by the Beneficiaries and Lessee + Lessor shall be responsible for supplying the new livery template + and sufficient paint; and the Term will not be deemed extended + solely as a result of the additional work required at the + Beneficiaries' request; + + (g) have all signs and decals in English, clean, secure and legible; + + (h) have heretofore been maintained in accordance with Section 6(d) of + this Lease with the same care and consideration for the technical + condition of the Aircraft as if it were to have been kept in + continued regular service and operation by Lessee (it being + understood that the obligations hereby imposed upon Lessee by + reference to the care and consideration by Lessee as if the + Aircraft were to be kept in continued regular service is not + intended to supersede Lessee's obligations herein with respect to + the specific and express terms of this Lease with respect to the + maintenance, repair, alteration or return of the Aircraft); + + (i) have had all repetitive airworthiness directives and mandatory + orders and regulations in at least half-life or better condition; + and + + (j) have, upon return of the Aircraft, returned at least one (1) + complete set of the Aircraft Documents related to the Aircraft and + its maintenance and operation records in up-to-date status. + +Lessee shall deliver to Lessor, at no cost to Lessor or the Beneficiaries, +all service bulletin kits relating to the Aircraft which have been furnished +to Lessee free of charge by any manufacturer for installation on the Aircraft +and which have not been so installed. In the event that such uninstalled + + + -3- + + + + + + Beneficiaries, taking into consideration + and determination Tax consequence to Lessor + and the Beneficiaries, which state shall be + specified in the Lease Supplement. + +Engine Manufacturer: Pratt & Whitney. + +Estimated Delivery Date: November 30, 1995. + +Last Basic Rent The Last Basic Rent Payment +Payment Date: Date shall be the ninety-sixth (96th) + Basic Rent Payment Date. + +Lease Identification: This [Aircraft] [Engine] is owned by + Wilmington Trust Company, not in its + individual capacity but solely as Trustee + for the benefit of ITOCHU AirLease + (Europe) Limited and Marubeni Airleasing + (UK) Limited, is leased to Aloha Airlines, + Inc. and may not be operated by a third + party without the prior written consent + of Wilmington Trust Company, as trustee. + +Lessee's Address: Aloha Airlines, Inc. + 371 Aokea Street + Honolulu, Hawaii 96819 + Fax No.: (808) 833-3100 + Attention: Vice President - + Finance and Chief Financial + Officer + +Lessor's Address: Wilmington Trust Company + Rodney Square North + Wilmington, Delaware 19890 + Attention: Corporate Trust + Administration + + Fax No.: (302) 651-8882 + +Manufacturer: The Boeing Company. + +Other Lease: Means each of Aircraft Lease Agreement + A, dated as of December 1, 1991, between + Lessee and First Security Bank of Utah, + National Association, as owner trustee, + and Aloha Airlines, Inc., as lessee, and + Aircraft Lease Agreement B, dated as of + December, 1991, + + + -2- + + + + + + between First Security Bank of Utah, + National Association, as owner trustee, + and Aloha Airlines, Inc., as lessee, as + each may be supplemented or amended + from time to time. The Other Leases relate + to the leasing to Lessee of aircraft other + than the Aircraft. + +Overdue Rate: One month LIBOR, as in effect from time to + time, plus 3-1/2% per annum, but not to + exceed the maximum amount permitted by Law. + +Payment Locations: For ITOCHU AirLease (Europe) Limited: + + Account Name: Citibank New York (ABA No. + 021000089) for the account of Citibank + Dublin (account no. 10994598) in favor of + ITOCHU AirLease (Europe) Limited (account + no. 1-00-6793-017) + + For Marubeni Airleasing (U.K.) Limited: + + Harris Bank International Corporation + ABA #026-007-760 + for the credit of + The Mitsubishi Trust & Banking Corporation + London Branch A/C#16011100 + UID No.107280 + for further credit to + Marubeni Airleasing (UK) Ltd. + Account #020-404391 + + With respect to payments by + Lessee of Basic Rent hereunder, 62.682% of + such amounts shall be paid to ITOCHU + AirLease (Europe) Limited (as above + provided) and 37.318% of such amounts + shall be paid to Marubeni Airleasing (U.K.) + Limited (as above provided). + +Permitted Jurisdictions: Any member country of the European Union + and the following: + + + -3- + + + + + + + Argentina + Australia + Austria + Brazil + Canada + Chile + Finland + Hong Kong + Iceland + New Zealand + Norway + Puerto Rico + Singapore + South Africa + Sweden + Switzerland + United States of America + +Permitted Sublessees: Aer Lingus + Air Canada + Air France + Air-Inter + Air New Zealand + Air Portugal + Alitalia + All Nippon Airways + Ansett + Austrian Airlines + Braathens + British Airways + British Midland Airways + Canadian Airlines International + Cathey Pacific + China Airlines + Condor + Iberia + Japan Airlines + Japan Air System + Japan Asia Airways + Korean Air + Lufthansa + Malaysian Airlines System + Martinair Holland + Qantas + Royal Dutch Airlines + Sabena + Scandinavian Airlines System + Singapore Airlines + SwissAir + Thai Airways International + Alaska Airlines + Continental Airlines + Delta Airlines + Southwest Airlines + United Air Lines + USAir + + -4- + + + + + + Provided always that, at any time during + the Term, the Beneficiaries may, by notice + to Lessee, notify Lessee that any such + Person no longer constitutes a Permitted + Sublessee (which determination shall be + made on a reasonable basis by the + Beneficiaries), whereupon such Person shall + no longer constitute a Permitted Sublessee. + +Public Liability and Five Hundred Million Dollars +Property Damage Insurance: ($500,000,000) per occurrence or the + highest amount carried by Lessee with + respect to other narrow body aircraft in + its fleet. + +Return Location: Such location in the Western Continental + United States as may be selected by + Lessor. If the Beneficiaries request + Lessee to return the Aircraft to a + location other than in Western + Continental United States, other than + following a Default or an Event of Default, + then Lessee and the Beneficiaries will + cooperate reasonably and in good faith to + arrange for such ferry flight, and the + Beneficiaries will bear any additional + ferry flight cost and expenses incurred by + Lessee (without markup) that are beyond + those costs and expenses that would have + been incurred if the Aircraft were ferried + to a location in the Western United States. + +Supplemental Agreement: means the Letter Agreement No. 1 dated as + of November 29, 1995 executed by the parties + hereto. + +Trust Agreement: means Trust Agreement N685MA, dated as of + December 27, 1991 between Wilmington Trust + Company and each Beneficiary, as amended + modified or supplemented from time to time. + + + -57- + + + + + +kits were purchased, manufactured or ordered by Lessee and in stock or +undelivered pursuant to purchaser order, then Lessor shall have a right to +purchase such kits from Lessee, at Lessee's cost and expense (without mark-up), +such right being exercisable at any time during the sixty (60) day period +following the return of the Aircraft; provided that such period will be +extended to one hundred eighty (180) days if such kit is ordered by Lessee upon +request by the Beneficiaries. + +Lessee shall have undertaken to use all reasonable endeavors (short of +grounding any aircraft or rescheduling any flight) to ensure that the Aircraft +shall have, on return of the Aircraft, no installed Part which shall have a +total time since new greater than 150% that of the Airframe. In any event, +Lessee shall not adversely discriminate against the Aircraft regarding the age +of Parts installed in the Aircraft compared with other aircraft operated by +Lessee. + +2 AIRCRAFT MAINTENANCE ON RETURN + +Prior to the Return Occasion and immediately prior to the return of the +Aircraft to Lessor, at Lessee's own expense: + + (a) AIRFRAME + + (i) Lessee shall, if requested by a Beneficiary, perform, or + procure the performance of, a full and complete C Check or its + equivalent including all phases, multiples and lower checks + in accordance with the Maintenance Program. Lessee shall + correct any deficiencies revealed during such C Check + inspection using Manufacturer's limits and guidelines, as + witnessed by the Beneficiaries' representative. Lessor will + contribute to the cost of such C Check or equivalent by + paying to Lessee the incrementary cost (without markup) to + perform such C Check or equivalent. + + (ii) The Airframe shall have no less than 4,000 Flight Hours + remaining to the next due D Check and no less than 1,500 + Flight Hours remaining to the next due C Check under Lessee's + Maintenance Program; PROVIDE, HOWEVER, that Lessee shall be + permitted to return the Aircraft with less than 4,000 Flight + Hours remaining until the next due D Check under Lessee's + Maintenance Program if (A) at time of the Return Occasion, + the Beneficiaries have entered into an agreement for the + lease or sale of the Aircraft with a subsequent lessee, buyer + or user of the Aircraft (a "Subsequent User"), and (B) the + bridging of the Maintenance Program to the Subsequent User's + maintenance program is approved in writing by the civil + aviation authority having jurisdiction over the Subsequent + User in a manner that permits the Subsequent User to actually + have available to it as of the Return Occasion no less than + 4,000 Flight Hours remaining until the next due D check or + its equivalent in accordance with the Subsequent User's + maintenance program. + + (iii) In addition to (i) above, if the Beneficiaries request that + Lessee perform, or procure the performance of, D Check or + equivalent, Lessee will perform, or procure the performance + of, such D Check or equivalent, with Lessor contributing to + the cost of such D Check or equivalent by paying to Lessee + the incrementary cost (without markup) to perform such D + Check or equivalent; provided that the Term will not be + deemed to be extended solely because of such D Check or + equivalent. + + (b) The condition of the Aircraft and installed systems upon return to + Lessor shall be as follows: + + + + + + (i) each Engine and each life limited Part therein shall have no + less Flight Hours or Cycles or time (whichever is applicable + and the more limiting factor) remaining until the next due + scheduled removal, shop visit or overhaul refurbishment as on + the Delivery Date, and the Engines shall have not less than + 2,250 Flight Hours and 1,500 Cycles remaining (whichever is + the more limiting factor) until the next scheduled + refurbishment and each life limited part within the Engines + shall have not less than 3,000 Flight Hours and 2,000 Cycles + remaining to the next scheduled replacement. + + (ii) the APU and each life limited Part therein (if applicable) + shall have no less hours or time (whichever is applicable and + the more limiting factor) remaining until the next due + scheduled removal and overhaul as on the Delivery Date, and + Lessee shall have the option to swap APUs to meet the + requirements of this paragraph (ii); + + (iii) the Landing Gear of the Aircraft and other time or life limited + parts of the Landing Gear shall have no less Flight Hours or + Cycles or any other life limit (whichever is applicable and + the more limiting factor) remaining until the next due + scheduled removal and overhaul as on the Delivery Date, and + Lessee shall have the option to swap Landing Gear to meet the + requirements of this paragraph (iii). + +3 OTHER MATTERS + + (a) Provided necessary material and instructions are received in due + time (other than materials available by Lessee), any further work + required by the Beneficiaries, which can be accommodated within + the days already planned by Lessee for the above inspection and + check as set forth in paragraph 2(a) above, shall be performed, or + performance thereof shall be procured, by Lessee during the above + check and Lessor shall reimburse Lessee for the incremental costs + incurred (without mark-up). + + (b) Each and every Part and time or calendar limited Part (including, + without limitation, loose equipment) will have no less life + remaining to the next scheduled removal, repair and overhaul as on + the Delivery Date in accordance with the Maintenance Program or + the recommended maintenance planning document of the manufacturer + of the relevant Part, and Lessee shall have the option to swap + Parts to meet the requirements of this paragraph (b). + + (c) Each Engine and the APU will be installed on the Aircraft and + if not the engines or auxiliary power unit installed on the + Delivery Date will be accompanied by all documentation that the + Beneficiaries may reasonably require to evidence that title + thereto is properly vested in Lessor in accordance with this Lease + and will: + + (i) have a power assurance run and a borescope inspection (or, in + case of the APU, such other inspection as in accordance with + Manufacturer's or APU manufacturer's recommendation) and, in a + case where an Engine does not meet the on-wing operating + criteria as required by Lessee's FAA approved engine + Maintenance Program and limits of the Engine section of the + Aircraft Maintenance Manual, if requested by Lessor, a test + cell inspection which shall be performed at Lessee's expense + with the Beneficiaries' representative entitled to be present + and Lessee shall provide evidence satisfactory to the + Beneficiaries (A) that the Engine meets the JT8D-17A limits + established by the Manufacturer according to the power + assurance check defined on the Aircraft + + -5- + + + + + Maintenance Manual with an EGT margin of no less than 18 + degrees centigrade at maximum rated takeoff power on a + standard day and (B) reflecting the correction of any + discrepancies from the guidelines set out by the Engine + Manufacturer which may be discovered during any such + inspection. All repairs shall be to the Beneficiaries' + satisfaction; + + (ii) if the Engine historical and maintenance records and/or trend + monitoring data (or current test cell run report, if + applicable) indicate a rate of acceleration in performance + deterioration of any Engine including the APU which is higher + than normal based on Lessee's maintenance experience in + operating such engines, Lessee shall, prior to return, correct + or cause to be corrected such conditions which are determined + to have exceeded engine manufacturer's maintenance manual + tolerances or otherwise be causing such performance + deterioration; and + + (iii) not have an Engine which is "on watch" for any reason + requiring any special or out of sequence inspection and each + such Engine shall comply with the operations specification of + Lessee without waiver, carry over, deferment, restriction or + exceptions. + +4 FUSELAGE, WINDOWS AND DOORS + + (a) The fuselage will be free of all dents and abrasions, + additional scab patches am as at the Delivery Date and loose or + pulled or missing rivets, beyond the limits set in the relevant + structural repair manual (and all scab patches existing at the + Delivery Date shall be monitored and maintained, including as to + corrosion, during the Tenn); + + (b) windows will be free of delamination, blemishes, crazing, beyond + limits set in the relevant structural repair manual and will be + properly sealed; and + + (c) doors will be free moving, correctly rigged and be fitted with + serviceable seals. + +5 WINGS AND EMPENNAGE + + (a) All leading edges will be free from damage, beyond limits + set in the relevant structural repair manual; and + + (b) all control surfaces will be cleaned (provided they shall have + been waxed and polished during the preceding C Check); + + (c) all unpainted cowlings and fairings will be cleaned (provided they + shall have been polished during the preceding C Check); and + + (d) wings will be free of fuel leaks. + +6 INTERIOR + + (a) Ceilings, sidewalls and bulkhead panels will be clean and free of + cracks and stains (fair wear and tear excepted); + + + -6- + + + + + + (b) carpets and seat covers will be in good condition, clean and free + of stains, spots, chewing gum, tear or fraying (fair wear and tear + excepted) and meet FAA fire resistance regulations; + + (c) seals and panels will meet FAA fire resistance regulations and + will be serviceable in good condition free of cracks and repainted + as necessary; and + + (d) emergency equipment having no less calendar life remaining as on + the Delivery Date. + +7 COCKPIT + + (a) All fairing panels shall be clean, free of stains and cracks (fair + wear and tear excepted), will be secure and repainted as necessary; + + (b) floor coverings will be clean (fair wear and tear excepted) and + effectively sealed; + + (c) seat covers will be in good condition, clean, free of stains (fair + wear and tear excepted) and will conform to FAA fire resistance + regulations; and + + (d) seats will be serviceable, in good condition, meet FAA fire + resistance regulations and will be repainted as necessary. + +8 CARGO COMPARTMENTS + + (a) All panels will be in good condition, fair wear and tear + excepted, clean and free of cracks and any panels which have + cracks will be replaced; + + (b) all nets will be serviceable and in good condition, fair wear and + tear excepted; (c) any repair shall be within the limits + established in the Manufacturer's structural repair manual; and + + (d) fabric cover shall be in good condition, fair wear and tear + excepted, free of stains, spots, tear or fraying. + +9 WHEEL WELLS AND UNDERCARRIAGE + + Each wheel well and undercarriage will be clean and free of leaks. The + wheel wells will be repainted as necessary. + +10 CORROSION + + (a) The Aircraft will have been inspected and treated with respect to + corrosion in strict adherence to that required by the Maintenance + Program; + + (b) the entire fuselage will be substantially free from corrosion and + will be adequately treated and an approved corrosion prevention + program will be in operation; and + + + -7- + + + + + + (c) fuel tanks will be free from contamination and, if applicable, a + corrosion and a tank treatment program will be in operation. + +11 EQUIPMENT CHANGES + + Upon mutual discussion and agreement, Lessor may require Lessee to + remove any Removable Part (but excluding a Removable Part the + installation of which is a requirement of the FAA or this Lease) and + restore the Aircraft to its condition prior to installation of that + Removable Part. Where appropriate, compliance by Lessee with the + redelivery conditions in this Schedule shall be measured by reference to + the Boeing maintenance planning document. + +12 NO LESSOR OR BENEFICIARY COMPENSATION + + Neither Lessor nor any Beneficiary shall be liable to Lessee, and Lessee + shall not be entitled to any compensation, with respect to the return of + the Airframe, the APU, any Landing Gear or any Part which, in any case, + has more Flight Hours, Cycles or time remaining or is otherwise in + better condition than the Flight Hours, Cycles, time remaining or + condition required by this Lease (including but not limited to Sections + 2(a)(ii), 2(b)(ii), 2(b)(iii) and 3(b) of this Section E). + + -8- + + + + + + EXHIBIT F + to + AIRCRAFT LEASE AGREEMENT + + [Letterhead of Lessee's Counsel] + + [Date of Delivery Date] + +The following is a summary of the items that should be included in Lessee's +Counsel Opinion: + + 1. Lessee is a corporation duly organized and existing under +the laws of the State of Hawaii and has the corporate power and authority to +carry on its business as presently conducted and to perform its obligations +under the Lease. + + 2. The execution, delivery and performance of the Operative +Documents have been duly authorized by all necessary corporate action of Lessee +and do not require any further approval of the Board of Directors or +stockholders of Lessee or approval or consent of any trustee or holders of +indebtedness or obligations of Lessee. Neither the execution and delivery +thereof nor the consummation of the transactions contemplated thereby nor +compliance by Lessee with any of the terms and provisions thereof will +contravene the certificate of incorporation or by-laws of Lessee or, to our +knowledge, any law applicable to Lessee and, to our knowledge, do not and will +not result in any breach of, or constitute a default under, or result in the +creation of any Lien upon any property of Lessee under, any credit agreement or +instrument, or other agreement or instrument to which Lessee is a party or by +which Lessee or its properties or assets are bound or affected. + + 3. Neither the execution and delivery by Lessee of the +Operative Document, nor the consummation of any of the transactions by Lessee +contemplated thereby, nor the compliance by the Lessee with any of the terms +and provisions thereof, requires the consent or approval of, or the giving of +notice to, or the registration with, or the taking of any other action in +respect of any federal or state governmental authority or agency, except for +the registration of the Aircraft, recordation and other actions referred to in +paragraph 6 below. + + 4. The Operative Document have been duly executed and +delivered by Lessee, and constitute the legal, valid and binding obligations of +Lessee, enforceable against Lessee in accordance with their respective terms, +subject, as to enforceability, to applicable bankruptcy, insolvency, +reorganization, moratorium or other similar laws affecting the enforcement of +creditors' rights generally, and, to general principles of equity. + + 5. To our knowledge, there are no pending suits or proceedings +before any court or any regulatory commission, board or other administrative +governmental agency against or affecting Lessee which might have a materially +adverse effect on the current business or financial condition of Lessee. + + 6. Except for the appropriate registrations, recordings and +filings with the FAA, the filing of a precautionary UCC-1 financing statement +with the Hawaii Bureau of Conveyances and the placing on the Aircraft and on +each Engine of the plates containing the legends referred to in Section 6(f) of +the Lease + + + + + +Agreement, no further filing or recording of any document and no further action +is necessary or desirable in order to protect Lessor's right, title and +interest in and to the Aircraft as against Lessee or any third party. + + 7. Lessee is a Certified Air Carrier as defined in the Lease +Agreement. + + 8. Lessor is entitled to the benefits of Section 1110 of Title +11 of the United States Code with respect to the Aircraft and the Engines to +the extent that they constitute an "airframe" or "aircraft engines", as defined +in Section 101 of the Federal Aviation Act. + + -2- + + + + + + EXHIBIT G + to + AIRCRAFT LEASE AGREEMENT + + [LETTERHEAD OF APPROVED INSURANCE BROKER] + + [Date of Delivery Date] + +Wilmington Trust Company +Rodney Square North +Wilmington, Delaware 19890 + + Re: Insurance Coverage for One Boeing 737-25A Aircraft under + Aircraft Lease Agreement, dated as of November 29, 1995, + between Wilmington Trust Company, not in its individual + capacity but solely as Trustee f/b/o ITOCHU AirLease + (Europe) Limited and Marubeni Airleasing (U.K.) Limited + +Gentlemen: + + This report is delivered to you pursuant to the provisions of +Section 2(b)(vii)(5) of the Aircraft Lease Agreement 23791, dated as of +November __, 1995 (the "Lease"), between Wilmington Trust Company, not in its +individual capacity but solely as Trustee (the "Lessor"), and Aloha Airlines, +Inc. (the "Lessee"). We are the firm of independent aircraft insurance brokers +(the "Approved Insurance Broker") who have been appointed by the Lessee to +deliver this report pursuant to Section 2(b)(vii)(5) of the Lease and we +understand that the Lessor has not objected to such appointment. Except as +otherwise defined herein, the terms used herein shall have the meanings set +forth in the Lease. + + We have reviewed the Lease and particularly Section 12 +thereof. We are also fully familiar with the Certificate of Insurance dated +November __, 1995 issued to you on behalf of the Approved Insurers as well as +the policies of insurance evidenced thereby. + + As of the date of this report, it is our opinion as the +Approved Insurance Broker that the insurance presently carried and maintained +by the Lessee, as evidenced in the attached certificates, complies with the +terms and requirements of Section 12 of the Lease. + + We agree to advise the Lessor and each Beneficiary in writing +promptly of any default in the payment of any premium and any other act or +omission on the part of the Lessee of which we have knowledge and which might +invalidate or render unenforceable, in whole or part, any insurance required +under the Lease on the Aircraft. + + Yours faithfully, + + [APPROVED INSURANCE BROKER] + + + + + +Wilmington Trust Company +Date +Page 2 + + + + ---------------------- + [Title] + + + + + + EXHIBIT H + to + AIRCRAFT LEASE AGREEMENT + + SPECIAL PROVISIONS TO CERTIFICATE OF INSURANCE + + The "special provisions" section of the Insurance Certificate +shall provide as follows: + + 1. In accordance with the Aircraft Lease Agreement, dated as +of November 29, 1995 (the "Lease"), between Lessor and Lessee, it is agreed to +include Lessor (in its individual capacity and as Owner Trustee), each +Beneficiary and their assigns as additional insureds, designate Lessor as owner +of the Aircraft, and designate the Beneficiaries and their assigns as loss +payees (but without imposing upon Lessor, such Beneficiary and their assigns +any obligation to pay any premiums). + + 2. It is agreed that in respect of the interests of Lessor, +each Beneficiary and their assigns, in the insurance coverage provided hereby, +such insurance coverage shall not be invalidated by any action or omission of +Lessee, and shall insure Lessor, such Beneficiary and their assigns, regardless +of any breach or violation of any warranty, declaration or condition contained +in such insurance coverage by Lessee. + + 3. It is agreed that if the insurance coverage provided hereby +is canceled for any reason whatsoever, or is adversely changed in any way with +respect to the interests of Lessor, any Beneficiary or their assigns, or if +such insurance is allowed to lapse for nonpayment of premium, such +cancellation, adverse change or lapse shall not be effective as to Lessor, such +Beneficiary and their assigns, for thirty (30) days (seven (7) days or such +lesser period as may be applicable in the case of any war risks and allied +perils coverage) after receipt by Lessor and such Beneficiary of written notice +of such prospective cancellation, change or lapse. + + 4. It is agreed that, as against Lessor, each Beneficiary and +their assigns, the Insurers waive any rights of setoff, counterclaim or any +other deduction, whether by attachment or otherwise, and waive any rights to be +subrogated to any right of any insured against Lessor, each Beneficiary or +their assigns, with respect to the Aircraft to the same extent that Lessee has +waived its rights of subrogation by the terms of its agreements to indemnify +any such party pursuant to this Lease. + + 5. The coverage provided hereunder is primary without right of +contribution from any other insurance which may be carried by Lessor, any +Beneficiary or their assigns. It is agreed that Lessor and each Beneficiary has +a right to carry insurance in excess of the amounts provided hereunder without +prejudice to the coverage hereunder provided. + + 6. A 50/50 clause is in effect between the all risk hull and +war risk covers. + + + + + + EXHIBIT I + to + AIRCRAFT LEASE AGREEMENT + + FORM OF REDELIVERY ACKNOWLEDGEMENT + + This Redelivery Acknowledgement is delivered, on the date set +out below, by WILMINGTON TRUST COMPANY ("Lessor") to ALOHA AIRLINES INC. +("Lessee") pursuant to the Lease Agreement dated November __, 1995 between +Lessor and Lessee (the "Agreement"). The capitalised terms used in this +Certificate shall have the meanings given to such terms in the Agreement. + +1 DETAILS OF ACCEPTANCE + + Lessor hereby confirms to Lessee that Lessor has at [ ] o'clock on + [insert date], at [ ], accepted redelivery of the following, in + accordance with the provisions of the Agreement: + + (a) Boeing Model 737-25A Airframe, Manufacturer's Serial No. 28791. + + (b) Pratt and Whitney JT8D-17A Engine, Manufacturer's Serial + Nos. [ ] and [ ]. + + (c) Fuel Status: Pounds [ ]. + + (d) Loose Equipment Check List: as per list signed by Lessor + and Lessee and attached hereto. + +2 CONFIRMATION + + Lessor confirms to Lessee that as at the time indicated above, being + the Expiration Date, Lessor's authorized technical experts have + inspected the Aircraft to ensure the Aircraft conforms to redelivery + conditions specified in the Agreement and that the Aircraft is in + accordance with the specifications of the Agreement and satisfactory + in all respects save for the following: + + [ ] + [ ] + + + + + +3 IN WITNESS WHEREOF, the Lessor has, by its duly authorized + representative, executed this Certificate on the date in paragraph 1 + above. + + LESSOR: + + + By: + --------------------- + Title: + --------------------- + Date: + --------------------- + + The "special provisions" section of the Insurance Certificate shall + provide as follows: + + -2- + + + + + + EXHIBIT J + to + AIRCRAFT LEASE AGREEMENT + +Monthly Aircraft Utilization & Status Report + +Monthly Aircraft Utilization and Status Report Provided +UNDER THE LEASE CONTRACT BETWEEN ALOHA AIRLINES, INC. AND WILMINTON TRUST +COMPANY, AS TRUSTEE + +MONTH ENDING: +Aircraft Registration Number: +Airframe Make and Model: Boeing: +Total Time on Airframe Since New: +Total Cycles on Airframe Since New: + + +
+ + MONTHLY TOTAL + FLIGHT MONTHLY FLIGHT TOTAL DATE INST + SERIAL NO. HOURS CYCLES HOURS CYCLES LOCATION (REMOVED) + +Airframe + +Engines & APU's Subject to Lease + +Engine #1 + +Engine #2 + +APU + +Other Engines & APU's Installed on Airframe + +Engine #1 + +Engine #2 + +APU + +Engines & APU's permanently removed from Service During Month + + +
+ + + MAJOR MAINTENANCE + + +
+ + MAINTENANCE CHECK DATE ACCOMPLISHED + +"A" CHECK - HOURS + +"B" CHECK - HOURS + +"C" CHECK - HOURS + +"D" CHECK - HOURS + +
+ + APU and Engine data represent the times and cycles of the airframes +each is installed on. + + The Aircraft, Engines, modules and APU subject to the Lease for +this Aircraft have been kept in good order and repair, or are being restored to +the condition required by the Lease. + +MAJOR STRUCTURAL REPAIR: +MAJOR MODIFICATIONS/AD's: +MISCELLANEOUS/ACCIDENTS/INCIDENTS: +FOR AND ON BEHALF OF VARIG S.A. + +NAME: + +TITLE: + + + + + + EXHIBIT K + to + AIRCRAFT LEASE AGREEMENT + + LIST OF DOCUMENTS TO BE SUPPLIED ANNUALLY + +1. AIRWORTHINESS DIRECTIVE SUMMARY UPDATES WILL BE SUPPLIED ANNUALLY + +2. SERVICE BULLETIN AND LESSEE'S ORIGINATED MODS SUMMARY + UPDATES WILL BE SUPPLIED ANNUALLY + +3. ENGINEERING ORDER (DT) + +4. LAST SHOP VISIT REPORT (FORM 337) FOR ENGINES AND APU WILL BE SUPPLIED + AS APPLICABLE. + +5. LIFE LIMITED PARTS STATUS FOR AIRFRAME/ENGINE/APU/LANDING GEAR WITH + COPY OF SERVICEABLE TAGS AS APPLICABLE. + +6. LISTING OF TIME/CALENDAR CONTROLLED COMPONENTS BY PIN, S/N - + DESCRIPTION-POSITION-TBO-TSI-TSO-TOTAL TIME. + +7. AIRCRAFT MASTER FLIGHT LOG (COMPUTER RUN OF TOTAL TT/TC RECORDED + DAILY). UPDATES WILL BE PROVIDED. + +8. CURRENT AND FUTURE REVISIONS OF ALOHA 737 MAINTENANCE PROGRAM (EPM), AS + APPLICABLE. + +9. LISTING OF ALL MAJOR 737 STRUCTURAL REPAIRS (N685MA) AS APPLICABLE. + +10. ANTICIPATED DATE OF NEXT SCHEDULED D CHECK OR C CHECK. + + + + + + COUNTERPART NO. 7 + + LEASE SUPPLEMENT NO. 1 + + TO AIRCRAFT LEASE AGREEMENT + DATED AS OF NOVEMBER 29, 1995 + + LEASE SUPPLEMENT NO. 1, dated December 1, 1995, between +Wilmington Trust Company, not in its individual capacity but solely as +Trustee f/b/o ITOCHU AirLease (Europe) Limited and Marubeni Airleasing (U.K.) +Limited ("Lessor"), and Aloha Airlines, Inc., a company organized under the +laws of Hawaii ("Lessee"). + + Lessor and Lessee have previously entered into that certain +Aircraft Lease Agreement dated as of November 29, 1995 (herein called the +"Lease" and the defined terms therein being hereinafter used with the same +meaning). The Lease provides for the execution and delivery from time to time +of a Lease Supplement substantially in the form hereof for the purpose of +leasing the aircraft described below under the Lease as and when delivered by +Lessor to Lessee in accordance with the terms thereof. + + The Lease relates to the Aircraft, Parts and Engines as more +precisely described below. A counterpart of the Lease is attached hereto and +this Lease Supplement and the Lease shall form one document. + + In consideration of the premises and other good and +sufficient consideration, Lessor and Lessee hereby agree as follows: + + 1. Lessor hereby delivers and leases to Lessee under the +Lease and Lessee hereby accepts and leases from Lessor under the Lease, that +certain Boeing Model B-737-25A commercial jet aircraft, Airframe and the two +Pratt & Whitney JT8D-17A Engines (each of which engines has 750 or more rated +takeoff horsepower or the equivalent of such horsepower) described in +Schedule 1 attached hereto and made a part hereof (the "Delivered Aircraft"). +The status of the Aircraft and Engines upon delivery thereof to Lessee shall +be as described on Schedule 2 attached hereto and made a part hereof. + + 2. (A) The Delivery Date of the Delivered Aircraft is the +date of this Lease Supplement set forth in the opening paragraph hereof. + + (B) The time of the delivery and acceptance of the +Delivered Aircraft is 12:30 p.m. Central Time, at which + + + + + +time the Delivered Aircraft is located in the airspace over the State of +Texas, United States of America (such location is herein called the "Delivery +Location"). + + 3. The Term for the Delivered Aircraft shall commence on +the Delivery Date and shall end on the Expiration Date, which shall be +December 1, 2003. + + 4. The amount of Basic Rent for the Delivered Aircraft is +payable in advance on each Basic Rent Payment Date. + + 5. Lessee hereby confirms to Lessor that (i) the Delivered +Aircraft and each Engine installed thereon or belonging thereto have been +duly marked in accordance with the terms of Section 6(f) of the Lease, (ii) +Lessee has accepted the Delivered Aircraft for all purposes hereof and of the +Lease and (iii) Lessee has inspected the Delivered Aircraft and the Delivered +Aircraft satisfies the conditions set forth in the Lease. + + 6. Lessor represents that all of its representations and +warranties contained in the Lease are true and correct on the date hereof. + + 7. All of the terms and provisions of the Lease Agreement +are hereby incorporated by reference in the Lease Supplement to the same +extent as if fully set forth herein. + + 8. The Lease Supplement may be executed in any number of +counterparts, each of such counterparts, except as provided in Section 20(e) +of the Lease, shall for all purposes be deemed to be an original and all such +counterparts shall together constitute but one and the same Lease Supplement. + +THIS LEASE SUPPLEMENT HAS BEEN EXECUTED IN MULTIPLE COUNTERPARTS, OF WHICH +THERE IS ONE COUNTERPART DESIGNATED AS "COUNTERPART NO. 1 - CHATTEL PAPER +ORIGINAL" AND ALL OTHER COUNTERPARTS SHALL BE DESIGNATED WITH NUMBERS OTHER +THAN 1. TO THE EXTENT THAT THIS LEASE SUPPLEMENT CONSTITUTES CHATTEL PAPER +UNDER THE UNIFORM COMMERCIAL CODE, AS IN EFFECT IN ANY APPLICABLE +JURISDICTION, NO SECURITY INTEREST MAY BE CREATED OR PERFECTED BY POSSESSION +OF ANY COUNTERPART OF THIS LEASE SUPPLEMENT OTHER THAN SAID COUNTERPART NO. 1. + + + -2- + + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused +this Lease Supplement to be duly executed by their authorized officers as of +the day and year first above written. + + LESSOR: + + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee + + + By: /s/ M. P. Bonilla + ------------------------------------ + Name: Myfanwy Phillips Bonilla + ------------------------------------ + Title: Asst. Vice President + + + LESSEE: + + ALOHA, AIRLINES, INC + + By: + ------------------------------------ + Name: + ------------------------------------ + Title: + ------------------------------------ + + + By: + ------------------------------------ + Name: + ------------------------------------ + Title: + ------------------------------------ + + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this +Lease Supplement to be duly executed by their authorized officers as of the +day and year first above written. + + LESSOR: + + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee + + By: + ------------------------------------ + Name: + ------------------------------------ + Title: + ------------------------------------ + + + LESSEE: + + ALOHA AIRLINES, INC. + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: Brenda F. Cutwright + ------------------------------------ + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + ------------------------------------ + + + By: /s/ James M. King + ------------------------------------ + Name: James M. King + ------------------------------------ + Title: VICE PRESIDENT PLANNING & + DEVELOPMENT + ------------------------------------ + + + + + + SCHEDULE 1 + TO + LEASE SUPPLEMENT NO. 1 + + BOEING 737-25A + AIRFRAME + + +
+ +MANUFACTURER'S U.S. REGISTRATION +SERIAL NO. NUMBER TOTAL TIME* TOTAL CYCLES* +-------------- ----------------- ----------- ------------- + + +23791 N685MA 23,859 14,057 + (to be changed to + N819AL) + + + INSTALLED PRATT & WHITNEY + ENGINES + +MODEL NO. SERIAL NO. TOTAL TIME* TOTAL CYCLES* +--------- ---------- ----------- ------------- +JT8D-17A P709529 20,960 11,883 +JT8D-17A P709530 22,100 12,908 + + INSTALLED + AUXILIARY POWER UNIT + +MODEL NO. SERIAL NO. TOTAL TIME* TOTAL CYCLES* +--------- ---------- ----------- ------------- +GTCP85-129 P35274 (TACH Reading) + +
+ + + + + + SCHEDULE 1 + TO + LEASE SUPPLEMENT NO. 1 (cont'd) + + B737 AVIONICS + COMPONENT INVENTORY + + +
+ +ITEM PART NUMBER SERIAL NUMBER DESCRIPTION POSITION/QUANTITY +NO. +---- ------------------ ------------- --------------------------- ------------------- + + +1 622-4096-001 4581 PA Amp E&E 1 +2 5140-1-112 339 Remote Electronics Unit E&E 1 +3 622-8971-020 2119 TCAS T/R E&E 1 +4 622-1181-001 14400 VHF T/R E&E 1 +5 622-1396-001 5023 VHF T/R E&E 1 +6 622-7878-200 5128/5165 ATC E&E 2 +7 NA 13UO2 255AKWO022 Selcal E&E 1 +8 622-2921-001 10076/10061 DME E&E 2 +9 080-20325-01 4603/4613/4602 T/Rectifier E&E 3 +10 2-301-3 4782 Batt Charger E&E 1 +11 39B168-1-0 3276 Static Inverter E&E 1 +12 65-52811-109 D00485 Landing Gear Acc Unit E&E 1 +13 42-651-01 443 Anti-Skid E&E 1 +14 65-52810-25 D01002 Air Condition Relay Unit E&E 1 +15 65-73606-69 D01029 Eng Acc Unit E&E 1 +16 65-52808-7 D00496 Compartment Overheat Unit E&E 1 +17 65-52806-235 D00870 Misc Solid State Switch E&E 1 +18 65-52807-26 D00449 Slat Position Switch Unit E&E 1 + +
+ + + -2- + + + + + + +
+ +ITEM PART NUMBER SERIAL NUMBER DESCRIPTION POSITION/QUANTITY +NO. +---- ------------------ ------------- --------------------------- ------------------- + + +19 522-2996-011 2012 Marker Beacon E&E 1 +20 777-1492-002 1582 ADF E&E 1 +21 777-1492-004 4039 ADF E&E 1 +22 965-0876-001 304 GPWS E&E 1 +23 622-3890-003 1556 Radio Alt E&E 1 +24 65-52801-31 D00446 APU Control Unit E&E 1 +25 65-52809-15 D00306 Fire Detector Unit E&E 1 +26 8TJ45-GAB X0062 34B3 Fuel Flow Power Supply E&E +27 231-2 5556/6517 Window Heat Control E&E 4 +28 4314/7490 E&E +29 522-3949-001 3701 Monitor Comparator E&E 1 +30 622-3257-001 5662/2275 ILS NAV RCVR E&E 2 +31 172938-001 6620/6623 NAV SW Unit E&E 4 +32 6630/6613 E&E +33 HG48B13 P255/G1711 ADC E&E 2 +34 4030952-906 88062826 Yaw Damper E&E 1 +35 163356-88-01 0000528 Performance Data Comp E&E 1 +36 548376-6 097C-9045 Cabin Temp Controller E&E 1 +37 87093613 P43 Pressure Controller E&E 1 +38 2588424-902 84053253 Rate of Turn E&E 1 +39 101 812802UA/9118557A Compass Rack E&E 2 +40 622-2500-004 2343/1298 Steering Comp E&E 2 + +
+ + + -3- + + + + + + +
+ +ITEM PART NUMBER SERIAL NUMBER DESCRIPTION POSITION/QUANTITY +NO. +---- ------------------ ------------- --------------------------- ------------------- + + +41 65-52805-317 D01011 Flight Inst Acc Unit E&E 1 +42 172938-001 6624/6615 Switch Unit E&E 4 +43 6595/2678 E&E +44 2590650-901 05872772 Mach Trim Comp E&E 1 +45 2588810-904 01851529 Pitch Computer E&E 1 +46 2588812-902 06871639 Roll Computer E&E 1 +47 65-52812-107 D00496 Auto Pilot Acc Unit E&E 1 +48 457300-0430 3394 Omega RPU E&E 1 +49 25582-003 8305313 Battery E&E 1 +50 2587335-11 84054627 Vert Gyro E&E 1 +51 2587335-11 8415034 Vert Gyro E&E 1 +52 2587335-11 0034163 Vert Gyro E&E 1 +53 2588302-4 0067044 Direction Gyro E&E 1 +54 2588302-4 84056507 Direction Gyro E&E 1 + +
+ + + -4- + + + + + + + SCHEDULE 2 + TO + LEASE SUPPLEMENT NO. 1 + + AIRCRAFT STATUS + ON THE DELIVERY DATE + +AIRFRAME HEAVIEST CHECK ("D" CHECK OR EQUIVALENT): + +Interval: 28,000 (Hrs) +Time Remaining: 4,141 + +LANDING GEAR OVERHAUL: + +Interval: Main Gear 24,000 cycles + Nose Gear 24,000 cycles +Time Remaining: Left Gear 9,943 cycles + Right Gear 9,943 cycles + Nose Gear 9,943 cycles + +ENGINES: + +Overhaul Interval: +Time Remaining (S/N P709529): +Time Remaining (S/N P709530): + +Hot Section Inspection: + +Interval: +Time Remaining (S/N P709529): +Time Remaining (S/N P709530): + +TIME REMAINING TO FIRST RESTRICTION: + +Engine S/N: P709529 + Hours: 9,040 + Cycles: 3,117 (C07) +Engine S/N: P709530 + Hours: 7,900 + Cycles: 2,092 (C07) + + + + + +AUXILIARY POWER UNIT: + +Hot Section Inspection: + +Interval: On Condition +Time Remaining: On Condition + +Time Remaining to Next Limited + or Scheduled Removal: On Condition + + + -2- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + Effective date: 10/20/95 + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 21 +------ +Valve, Safety 720737-5 L-0001 14000H 13,817.6 3,977.2 +RH OH 7/3/92 +Valve, Safety 720737-5 830678 14000H 13,817.6 3,977.2 +LH OH 7/3/92 +Gasper Fan 21520-1 063 6000H 22,594.7 4,735 + OH 6/15/95 + +ATA 23 +------ +Recorder, 93A100-80 52428 72 Mo. 14, 623.2H 2 Yrs., 6 Mo. +Voice RPL +(Battery) + +ATA 24 +------ +APU Generator 976J498-1 YK13122 6000H 20,471 2,602H + HT 10/20/94 +Panel Bus 915F213-2 WE1347B 6000H 19,313.9 1,454H +Protection BC 6/11/94 +Gen. Cont. 948F458-1 NM4963 6000H 19,616.8H 1,757H + Unit BC 7/18/94 + APU +Gen. Cont. 948F458-1 PM5071 6000H 23,697H 5,838H + Unit BC 10/3/95 + Left +Gen. Cont. 948F458-1 XJ2186 6000H 21,639.9H 3,780.9H + Unit BC 2/18/95 + Right + +ATA 25 +------ +Life Vest KSE-35L8 527-691 24 Mo. 8/30/95 9 Mos. + Rt. Aft OH 23,304H +Life Vest AV-35 541-064 24 Mo. 2/95 14 Mos. + Co-pilot O H 21,530H +Slide, Evac D31354- 1112 24 Mo. 8/23/94 10 Mos. + Rt. Aft 425 OH 19,939.7H + +
+ + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 25 +------ +Slide, Evac D31354- 1613 24 Mo. 8/24/94 10 Mos. + Lt. Aft 433 OH 19, 949H +Slide, Evac 11611-143 123-1 24 Mo. 9/12/95 23 Mos. + Rt. Fwd OH 23,459H +Slide, Evac D31354- 0159 24 Mo. 2/15/95 7 Mos. + Lt. Fwd 403 OH 21,620H + +Escape Slide SEE NOTE (1) OF THE MARKAIR MAINTENANCE PROGRAM + Bottles (4) + +Life Vest KSE-35L8 50287 24 Mo. 3/17/95 9/96 + Obs OH 21,942H 8 Mos. +Life Vest KSE-35L8 12323 24 Mo. 8/1/95 7/97 + Capts OH 23,085H 22 Mos. +Life Vest KSE-35L8 527-691 24 Mo. 8/30/95 7/97 +Aft Right OH 23,304H 21 Mos. +Life Vest KSE-35L8 29358 24 Mo. 2/3/95 12/96 +Left Aft OH 21,474H 8 Mos. +Life Vest AC-2 535-380 24 Mo. 4/29/95 3/97 +Fwd FA OH 22,259H 17 Mos. +Life Vest KSE-35L8 541-064 24 Mo. 2/7/95 1/97 +Co-pilot OH 21,530.7 15 Mos. +Kit, Medical 017093 NHC1613 24 Mo. 11/28/94 11/96 + OH 20,844H 13 Mos. +PEE 4566M378 E9134322 120 Mo. 12/31/91 1/2000 +R/A Closet OH 60 Mos. +PEE 4566M378 E9134326 120 Mo. 12/31/91 1/2000 +L/A Closet OH 60 Mos. +PEE 4566M378 E9134942 120 Mo. 1/13/92 1/2000 +Aft Closet OH 60 Mos. +PEE 4566M378 E9134751 120 Mo. 12/22/91 1/2000 +Row 10 OH 60 Mos. +PEE 4566M378 E9548209 120 Mo. 6/9/95 5/2002 +Cockpit OH 70 Mos. + +
+ + -2- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 26 +------ +Cartridge, 30903828 AEN-1-3 72 Mos. 5/28/93 50 Mos. +Fire RPL +ext.: Rt. +Cartridge, 30903828 BTL23883B 120 Mos. 5/5/95 5/2005 +Fire 1 RPL +ext.: APU +Cartridge, 30903828 09015DI 72 Mos. 4/27/95 2/2001 +Fire RPL +ext.: Left +Fire Bottle 3307507 23883B1 WC/12 Mos 5/5/95 5/96 +APU HST/60 Mo 5/5/95 5/2000 +Fire Bottle 33600011 10124D1 WC/12 Mos 4/27/95 5/98 +Rt Eng. HST/60 Mo 5/14/93 4/98 +Fire Bottle 33600011 09015D1 WC/12 Mos 4/27/95 4/96 +Left Eng. HST/60 Mo 2/16/95 2/2000 +Fire Ext. 892480 672AC WC/12 Mos 5/1/95 5/96 +Aft HST/60 Mo 7/96 +Closet(H20) +Fire Ext. 898052 A-415392 WC/12 Mos 5/1/95 5/96 +Aft. (halon) HST/60 Mo 11/91 7/96 +Fire Ext. 898052 B-615379 WC/12 Mos 5/1/95 5/96 +Lt row HST/60 Mo 7/96 +10/halon +Fire Ext. 898052 V-343826 WC/12 Mos 4/26/95 5/99 +Fwd/(halon) HST/60 Mo +Fire Ext. 898052 E494480 WC/12 Mos 4/29/95 4/96 +cockpit/halon HST/60 Mo 5/99 +Fire Bottle, A800100-1 26-904 WC/12 Mos 4/13/94 Over Due +Lav. Aft HST/60 Mo +Fire bottle, A800100-1 26-906 WC/12 Mos 4/13/94 Over Due +Lav. Fwd HST/60 Mo + +
+ + -3- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 27 +------ +Jackscrew, 65-49970- KE00301 8000H 19,946H 3,787H +Stab. trim 17 OH 8/24/94 +Fuse assy 6213-4 219 22,400H 19,945H 18,487H +auto BC +brk/rudder +Transmission 65-50321- 0812 16000H 15,275H 7,416H +No 1 flap 5 OH +Transmission 65-50322- 0840 16000H 15,275H 7,416H +No 2 flap 12 OH +Transmission 65-50323- 0911 16000H 15,275H 7,416H +No 3 flap 5 OH +Transmission 65-50324- 0784 16000H 15,275H 7,416H +No 4 flap 3 OH +Transmission 65-50325- 50007 16000H 15,275H 7,416H +No 5 flap 5 OH +Transmission 65-50326- 62003 16000H 15,275H 7,416H +No 6 flap 6 OH +Transmission 65-50327- 72004 16000H 15,275H 7,416H +No 7 flap 14 OH +Transmission 65-50328- 82002 16000H 15,275H 7,416H +No 8 flap 7 OH +Gearbox assy 65-51510- 7417 16000H 15,275H 7,416H +Rt angle/Left 1 OH +Gearbox assy 65-51510- 1489 16000H 15,275H 7,416H +Rt angle/Rt 2 OH +Gearbox assy 65-51513- 4778 16000H 15,275H 7,416H +Flap dr./Left 1 OH +Gearbox assy 65-51513- 1877 16000H 15,275H 7,416H +Flap dr./Rt. 2 OH +Power Unit 65-63851- 7241 16000H 15,275H 7,416H +Flap dr GBX 7 OH + +
+ + + -4- + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 28 +------ +APU Fuel AV16EI209 H1842C 20000H 19,898H 16,025H +S/0 VLV D OH 8/19/94 +Left Fuel 61185 128 200001-1 19,876H 16,003H +S/0 VLV OH 8/16/94 +Right Fuel 61185 1813 20000H 20,015H 16,142H +S/0 VLV OH 8/31/94 +CF Fuel 61185 609 20000H 20,005H 16,146H +S/0 VLV OH 8/31/94 + +ATA 31 +------ +ULB Batteries 9804100- 7040 6 Yrs. 12 Mar 94 41 Mos. + GXUS RPL + +ATA 32 +------ +Fuse assy 6213-4 627 22,400H 19,917.7H 18,458H +Auto OH 8/21/94 +Brk/Rudder + +ATA 34 +------ +Altimeter, WL102AMS AG102 24 Mo. 12/20/94 14 Mo. +Stby 3 IN +Transponder TPR 720 5128 24 Mo. 4/27/95 18 Mo. +Mode S, Lt. IN +Transponder TPR 720 5165 24 Mo. 4/27/95 18 Mo. +Mode S, Rt. IN + +
+ + -5- + + + + + + SCHEDULE 3 + TO + LEASE SUPPLEMENT NO. 1 + + Aircraft Status on The Delivery Date + + Time and Calendar Controlled Components + (excluding engines, landing gear, and APU). + + + +
+ AIRCRAFT +COMPONENT PART HR. +POSITION NUMBER SERIAL # LIMIT TSI REMAINING +--------- -------- --------- ------- ---------- --------- + + +ATA 35 +------ +02 Cylinder 801307-00 223879/ 36 Mo. 6/22/94 5/97 + 29668 HST 5/94 19 Mo. +Portable 02 5500A1A- 771X/3596 60 Mo. 5/11/92 3/96 + Cyl BF23A 4 HST 5 Mo. +Portable 02 5500A1A- 151843P/1 60 Mo. 12/21/93 1/97 + Cyl BF23A 3 HST 15 Mo. +Row 1 5454 +Portable 02 56002C1AF 55934DB/2 60 Mo. 10/5/95 2/2000 + Cyl 23AM 3832 HST 58 Mo. +Aft closet +Portable 02 5600-2ClA 74732DF/ 60 Mo. 1/12/93 1/98 + Cyl 41819 HST 26 Mo. +Cockpit + +02 Generators Varies 12 Yrs 22 Mo. +------------- ------ ------ ------ + 117003-17 12 Yrs 22 Mo. + 117.00-12 12 Yrs 22 Mo. + 803000-02 12 Yrs 22 Mo. + 117003-12 12 Yrs 22 Mo. + +
+ + + -6- + + + + + + SCHEDULE 4 + TO + LEASE SUPPLEMENT NO. 1 + + A/C N685MA + + R/H SIDE + + + +
+ +DESC. PART NUMBER SERIAL NUMBER MODEL NUMBER +----- ----------- ------------- ------------ + + 1. TRIPLE BURNS 86952102 310588 JB5.2-3-58 + 2. TRIPLE BURNS 86952024 310581 JB5.2-3-59 + 3. TRIPLE BURNS 86952022 310569 JB5.2-3-59 + 4. TRIPLE BURNS 86952022 310566 JB5.2-3-59 + 5. TRIPLE BURNS 86952022 310572 JB5.2-3-59 + 6. TRIPLE BURNS 86952022 310577 JB5.2-3-59 + 7. TRIPLE BURNS 86952022 310570 JB5.2-3-59 + 8. TRIPLE BURNS 86952022 310574 JB5.2-3-59 + 9. TRIPLE BURNS 86952022 310568 JB5.2-3-59 +10. TRIPLE BURNS 86952016 310562 JB5.2-3-58 +11. TRIPLE BURNS 86952022 310573 JB5.2-3-59 +12. TRIPLE BURNS 86952022 310579 JB5.2-3-59 +13. TRIPLE BURNS 86952022 310578 JB5.2-3-59 +14. TRIPLE BURNS 86952022 310571 JB5.2-3-59 +15. TRIPLE BURNS 86952022 310576 JB5.2-3-59 +16. TRIPLE BURNS 86952022 310564 JB5.2-3-59 +17. TRIPLE BURNS 86952022 310575 JB5.2-3-59 +18. TRIPLE BURNS 86952026 310582 JB5.2-3-59 +19. TRIPLE BURNS 86952014 310560 JB5.2-3-58 +20. DOUBLE BURNS 86953201 306409 JB5.2-2-39 + +
+ + -1- + + + + + + + A/C N685MA + + L/H SIDE + + +
+ +DESC. PART NUMBER SERIAL NUMBER MODEL NUMBER +----- ----------- ------------- ------------ + +1. TRIPLE BURNS 86952101 310587 JB5.2-3-58 +2. TRIPLE BURNS 86952007 310556 JB5.2-3-58 +3. TRIPLE BURNS 86952001 310548 JB5.2-3-59 +4. TRIPLE BURNS 86952001 310545 JB5.2-3-59 +5. TRIPLE BURNS 86952001 310541 JB5.2-3-59 +6. TRIPLE BURNS 86952001 310542 JB5.2-3-59 +7. TRIPLE BURNS 86952001 310539 JB5.2-3-59 +8. TRIPLE BURNS 86952001 310550 JB5.2-3-59 +9. TRIPLE BURNS 86952013 310559 JB5.2-3-58 +10. TRIPLE BURNS 86952015 310561 JB5.2-3-58 +11. TRIPLE BURNS 86952001 310544 JB5.2-3-59 +12. TRIPLE BURNS 86952003 310554 JB5.2-3-59 +13. TRIPLE BURNS 86952001 310547 JB5.2-3-59 +14. TRIPLE BURNS 86952001 310543 JB5.2-3-59 +15. TRIPLE BURNS 86952001 310553 JB5.2-3-59 +16. TRIPLE BURNS 86952001 310538 JB5.2-3-59 +17. TRIPLE BURNS 86952001 310538 JB5.2-3-59 +18. TRIPLE BURNS 86952001 310549 JB5.2-3-59 +19. TRIPLE BURNS 86952005 310555 JB5.2-3-59 +20. TRIPLE BURNS 86952017 310563 JB5.2-3-56 + +
+ + -2- + + + + + + +
+ + AUDITED THRU L/P 165891 10/20/95 + A/C TOTAL HOURS 23,859 + A/C TOTAL CYCLES 14,057 +INSTALLED ON N685MA POSITION 1 + HOURS CYCLES + LIMIT SINCE SINCE TOTAL TOTAL TIME REMAINING +ITEM POS PART NO S/N HOURS CYCLES NEW NEW HOURS CYCLES HOURS CYCLES + + + +ENGINE LEFT JT8D-17A P7095308 30,000 15,000 22,100 12,908 22,100 12,908 7,900 2,092 +HUB (DISK) C 01 817401G P78775 NO LIMIT 20,000 NO LIMIT 12,908 NO LIMIT 12,908 NO LIMIT 7,092 +DISK C 02 5002402-01 N87756 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 03 800803 N58289 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK (HUB) C 04 799504 P44646 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 05 745705 P93696 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 06 745706 P93306 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 07 774407 P81245 30,000 15,000 22,100 12,908 22,100 12,908 7,900 2,092 +DISK (HUB) C 08 5005808-01 N90080 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 09 701509 P97271 30,000 19,000 22,100 12,908 22,100 12,908 7,900 6,092 +DISK C 10 772510 P97761 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 11 772511 P03600 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 12 772512 P97578 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK C 13 5003613-01 P59603 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK (SHAFT) T 01 5003601-01 N36371 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK T 02 5003102-01 P03367 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK T 03 5003103-01 N64721 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +DISK T 04 5003104-01 N52111 30,000 20,000 22,100 12,908 22,100 12,908 7,900 7,092 +LPT (SHAFT) NO LIMIT 20,000 22,100 14,558 22,100 14,558 NO LIMIT 5,442 + +
+ + + + + +
+ + AUDITED THRU L/P 165891 10/20/95 + A/C TOTAL HOURS 23,859 + A/C TOTAL CYCLES 14,057 +INSTALLED ON N685MA POSITION 2 + HOURS CYCLES + LIMIT SINCE SINCE TOTAL TOTAL TIME REMAINING +ITEM POS PART NO S/N HOURS CYCLES NEW NEW HOURS CYCLES HOURS CYCLES + + + +ENGINE RIGHT JT8D-17A P7095308 NO LIMIT NO LIMIT 20,960 11,883 20,960 11,883 9,040 3,117 +HUB (DISK) C 01 817401 P23654 NO LIMIT 20,000 NO LIMIT 13,372 NO LIMIT 13,372 NO LIMIT 6,628 +DISK C 02 5002402-01C S56233 30,000 20,000 10,771 7,680 10,771 7,680 19,229 12,320 +DISK C 03 800803 P68371 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK (HUB) C 04 799504 N90833 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 05 745705 P93614 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 06 745706 P93230 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 07 774407 P81364 30,000 15,000 20,960 11,883 20,960 11,883 9,040 3,117 +DISK (HUB) C 08 5005808-01 P43608 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 09 701509 P97641 30,000 19,000 20,960 11,883 20,960 11,883 9,040 7,117 +DISK C 10 772510 P97278 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 11 772511 P03553 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 12 772512 P97219 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK C 13 5003613-01 P92918 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 01 5003601-01 N36374 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 02 5003102-01 M12012 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 03 5003103-01 N64611 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +DISK T 04 5003104-01 N52069 30,000 20,000 20,960 11,883 20,960 11,883 9,040 8,117 +LPT (SHAFT) NO LIMIT NO LIMIT + +
+ + + + + + +
+ + + AUDITED THRU L/P 165891 TOTAL A/C HOURS 23859 +INSTALLED ON: N685MA DATE 10/20/95 TOTAL A/C CYCLES 14057 + + +NLG ASSEMBLY 65-73762-5 T01406P1486 90,000 24,000 9,943 +SHOCK STRUT ASSY 65-46200-63 T01408P1486 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +OUTER CYLINDER ASSY 65-46210-25 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +OUTER CYLINDER 65-46211- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +STEERING PLATE, UPR 65-46210-27 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +STEERING PLATE, LWR (DRILLED) 65-46210-24 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +OUTER CYLINDER ASSY (DRILLED) 65-46215-14 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +METERING PIN ASSY 65-46200-54 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +METERING PIN 65-46219- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +SUPPORT TUBE, METERING PIN 65-46229- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +DRAIN TUBE 69-57991- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +SUPPORT TUBE, UPPER ORIFICE 69-73038-1 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TUBE, ORIFICE SUPPORT 65-46226-9 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +STEM, ORIFICE SUPPORT 65C25707- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +GLAND NUT ASSY 65-46221-4 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +ORIFICE PLATE, UPPER 69-36622-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TORSION LINK ASSY, UPPER 65-46202-9 JC-1917 90,000 24,000 08/28/92 14,378 7,445 23,971 6,612 9,481 65,028 17,388 +TORSION LINK ASSY, LOWER 65-46288-5 32-58 90,000 24,000 08/28/92 14,378 7,445 28,383 6,612 9,481 61,617 17,388 +STEERING COLLAR ASSY 65-46203-24 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, STEERING COLLAR 69-61383-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, STEERING COLLAR 69-61383- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, UPPER TORSION LINK 69-72698-1 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, LOWER TORSION LINK 69-72698-1 486-2 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, TORSION LINK APEX 69-35883-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +LOWER DRAG BRACE ASSY 65-46230-7 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +UPPER DRAG BRACE ASSY 65-80051-13 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, DRAG BRACE CENTER 69-35391- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, DRAG BRACE, CENTER NAS1110-68D 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, UPPER DRAG BRACE 69-35394-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +PIN, UPPER DRAG BRACE 66-35394-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, LOWER DRAG BRACE 69-35396-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +LOCK BRACE ASSY, AFT 65-46240-11 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +LOCK LINK ASSY, FWD 65-46242-11 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +BOLT, NOSE GEAR LOCK JOINT NAS1105-52D 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +SHAFT, AFT LOCK MECHANISM 69-35385-1 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TRUNNION PIN, LEFT HAND 69-35398-2 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 +TRUNNION PIN, RIGHT HAND 69-41248- 1486 (ORIG) 90,000 24,000 12/17/87 0 0 14,057 14,057 23,859 75,943 9,943 + +
+ + + + + + + +
+ + + AUDITED THRU L/P 165891 TOTAL A/C HOURS 23859 +INSTALLED ON: N685MA DATE 10/20/95 TOTAL A/C CYCLES 14057 + + +LEFT NLG ASSEMBLY 65-73761-87 MC026895P1488 100,000 24,000 9,943 +SHOCK STRUT ASSY 65-46100-52 MC028958P1488 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +OUTER CYLINDER ASSY 65-61740-10 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +INNER CYLINDER ASSY 65-46116-47 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE SUPPORT TUBE ASSY 65-46159-7 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT TUBE, ORIFICE 65-46160-7 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE PLATE 69-38980-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +METERING PIN 65-46124-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +DRAIN TUBE 65-46156-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +GLAND NUT 69-39485-10 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, LOWER 65-46102-22 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, UPPER 65-87958-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER DRAG STRUT ASSY 65-46103-11 1486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER DRAG STRUT ASSY 65-60579-3 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, LWR SIDE 65-46108-7 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ARM ASSY 65-46109-12 5104 100,000 24,000 12/16/91 12,391 5931 8,865 8,126 11,488 91,135 15,874 +TRUNNION PIN ASSY 65-46113-5 34ORGA 100,000 24,000 12/16/91 12,391 5931 66,508 8,126 11,488 31,492 15,874 +TEE BOLT ASSY, DRAG STRUT 65-46140-6 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TRUNNION LINK ASSY 65-63378-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, LOWER 69-72023-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, UPPER 69-72023-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, TEE FITTING ATTACHMENT 69-51833-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY.: LOWER DRAG STRUT 69-39473-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT ASSY, UPR DRAG 69-39476-6 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, TRUNNION PIN 69-41633-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, REACTION LINK 65-46107-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER SIDE STRUT ASSY 65-46138-5 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER SIDE STRUT ASSY 65-63397-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +REACTION LINK ASSY 65-46135-13 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, UPPER DOWNLOCK 65-46138-16 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, LOWER DOWNLOCK 65-46139-9 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, SPRING SUPPORT 69-38999-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, REACTION LINK 69-38148-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, UPPER SIDE STRUT 69-68149-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, SIDE STRUT 69-68150-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK LINK PIVOT 69-41628-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK 69-42193-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ASSY, UPPER DOWNLOCK 69-62779-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ASSY 65-46108-3 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT LINK ASSY 65-46112-22 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, UPLOCK 65-63887-5 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BELLCRANK ASSY 65-63663-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +HOOK ASSY 65-46142-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, UPLOCK SUPPORT 65-46146-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, REACTION LINK / UPLOCK 69-68148-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, OUTBD 69-39464-4 352 100,000 24,000 12/16/91 12,891 5931 8,126 8,126 11,468 91,874 15,874 +BOLT ASSY, LOWER SIDE STRUT 69-68151-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +COLLAR, BELLCRANK RET, UPLOCK 69-51838-2 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT, FWD TRUNNION BRG 69-58854-3 486 (ORIG) 83,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, INBRD BACB301J20CD43 685 100,000 24,000 12/14/91 12,891 5931 8,126 8,126 11,468 91,874 15,874 +BOLT, ACTUATOR ROD END BACB301J16CD48 685/1 100,000 24,000 12/14/91 12,891 5931 8,126 8,126 11,468 91,874 15,874 +BEARING ASSY, FWD TRUNNION 69-58871-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, AFT TRUNNION 65-84161-1 486 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 + +
+ + + + + +
+ + + AUDITED THRU L/P 165891 TOTAL A/C HOURS 23859 +INSTALLED ON: N685MA DATE 10/20/95 TOTAL A/C CYCLES 14057 + + +RIGHT NLG ASSEMBLY 65-73761-87 MC02665P1488 100,000 24,000 9,943 +SHOCK STRUT ASSY 65-46100-52 MC02896P1488 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +OUTER CYLINDER ASSY 65-61740-10 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +INNER CYLINDER ASSY 65-46116-47 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE SUPPORT TUBE ASSY 65-46159-7 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT TUBE, ORIFICE 65-46160-7 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ORIFICE PLATE 69-38980-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +METERING PIN 65-46124-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +DRAIN TUBE 65-46156-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +GLAND NUT 69-39485-10 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, LOWER 65-46102-22 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK ASSY, UPPER 65-87958-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER DRAG STRUT ASSY 65-46103-11 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER DRAG STRUT ASSY 65-60579-3 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, LWR SIDE 65-46105-7 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ARM ASSY 65-46109-12 1507 100,000 24,000 12/14/91 12,391 5931 34,655 8,126 11,488 65,345 15,874 +TRUNNION PIN ASSY 65-46113-16 32-118 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,488 91,674 15,874 +TEE BOLT ASSY, DRAG STRUT 65-46140-6 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TRUNNION LINK ASSY 65-63378-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, LOWER 69-72023-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +TORSION LINK PIN, UPPER 69-72023-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, TEE FITTING ATTACHMENT 69-51833-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY.: LOWER DRAG STRUT 69-39473-3 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT ASSY, UPR DRAG 69-39476-6 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, TRUNNION PIN 69-41633-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UNIVERSAL ASSY, REACTION LINK 65-46107-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +UPPER SIDE STRUT ASSY 65-46138-5 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LOWER SIDE STRUT ASSY 65-63397-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +REACTION LINK ASSY 65-46135-13 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, UPPER DOWNLOCK 65-46138-16 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +LINK ASSY, LOWER DOWNLOCK 65-46139-9 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, SPRING SUPPORT 69-38999-3 685 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BOLT ASSY, REACTION LINK 69-38148-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, UPPER SIDE STRUT 69-68149-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT ASSY, SIDE STRUT 69-68150-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK LINK PIVOT 69-41628-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, DOWNLOCK 69-42193-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ASSY, UPPER DOWNLOCK0 69-62779-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +ACTUATOR BEAM ASSY 65-46108-3 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SUPPORT LINK ASSY 65-46112-22 HI-106 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +LINK ASSY, UPLOCK 65-63887-5 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BELLCRANK ASSY 65-63663-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +HOOK ASSY 65-46142-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +SHAFT, UPLOCK SUPPORT 65-46146-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, REACTION LINK / UPLOCK 69-68148-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, OUTBD 69-39464-4 482 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BOLT ASSY, LOWER SIDE STRUT 69-68151-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +COLLAR, BELLCRANK RET, UPLOCK 69-51838-2 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +FUSE BOLT, FWD TRUNNION BRG 69-58854-3 486-2 (ORIG) 83,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +BOLT, ACTUATOR BEAM, INBRD BACB301J20CD43 685/2 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BOLT, ACTUATOR ROD END BACB301J16CD48 685 100,000 24,000 12/14/91 12,391 5931 8,126 8,126 11,468 91,874 15,874 +BEARING ASSY, FWD TRUNNION 69-58871-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 +NUT, AFT TRUNNION 65-84161-1 486-2 (ORIG) 100,000 24,000 12/17/87 0 0 14,057 14,057 23,859 85,943 9,943 + +
+ + + + + COUNTERPART NO. 2 + + LEASE SUPPLEMENT NO. 2 + + TO AIRCRAFT LEASE AGREEMENT + DATED AS OF NOVEMBER 29, 1995 + + LEASE SUPPLEMENT NO. 2, dated April 12, 1996, between + Wilmington Trust Company, not in its individual capacity but solely as Trustee + f/b/o ITOCHU AirLease (Europe) Limited and Marubeni Airleasing (U.K.) Limited + ("Lessor"), and Aloha Airlines, Inc., a company organized under the laws of + Hawaii ("Lessee"). + + Lessor and Lessee have previously entered into that certain + Aircraft Lease Agreement dated as of November 29, 1995, as supplemented by + Lease Supplement No. 1, dated December 1, 1995 (as amended, modified or + supplemented and in effect from time to time, the "Lease" and the defined terms + therein being hereinafter used with the same meaning), which was filed with the + FAA as one instrument and recorded on January 25, 1996 as Conveyance No. + 11004730. Lessor and Lessee desire to supplement the Lease in order (1) to + subject to the Lease two Pratt & Whitney model JT8D-9A engines described below, + and (2) to remove from the terms of the Lease two Pratt & Whitney model + JT8D-17A engines described below. + + In consideration of the premises and other good and + sufficient consideration, Lessor and Lessee hereby agree as follows: + + 1. Lessor hereby leases to Lessee under the Lease and Lessee + hereby leases from Lessor under the Lease, two (2) Pratt & Whitney JT8D-9A + Engines bearing Manufacturer's Serial Numbers 665868 and 655877, each having + 750 or more rated takeoff horsepower, together with (1) all parts, appliances, + components, accessories and equipment currently installed on, or attached to + any such engine and (2) all flight, engineering, maintenance, overhaul and + repair manuals, drawings, records, logs, documents and other data relating to + the above described engines or parts (such engines, together with the items + identified in clauses (1) and (2) above, are referred to herein collectively as + the "9A Engines"). For all purposes of the Lease, each of the 9A Engines shall + be deemed an "Engine" as defined in the Lease, and part of the Aircraft and + each such Engine shall be maintained, insured and returned to Lessor in the + manner and at the times set forth in the Lease. + + RECORDED + + FEDERAL AVIATION ADMINISTRATION + + Date 4-29-96 Time 1:07 + --------- -------- + + Conveyance Number ZZ011203 + ------------ + + By /s/ [Illegible] + -------------------- + + + + + 2. There are hereby removed from the terms of the Lease the + two (2) Pratt & Whitney JT8D-17A Engines bearing Manufacturer's Serial Numbers + P709529 and P709530, each having 750 or more rated takeoff horsepower, together + with (1) all parts, appliances, components, accessories and equipment currently + installed on, or attached to any such engine and (2) all flight, engineering, + maintenance, overhaul and repair manuals, drawings, records, logs, documents + and other data relating to the above described engines or parts (such engines, + together with the items identified in clauses (1) and (2) above, are referred + to herein collectively as the "0riginal 17A Engines"). Lessee and Lessor + acknowledge that the Original 17A Engines are, effective on this date, no + longer subject to the terms of the Lease and that Lessee has no interest + therein or thereto under the Lease. + + 3. From the date hereof until the last day of the Term, + Lessee shall affix and keep a metal nameplate of reasonable dimensions in a + reasonably prominent position on each of the 9A Engines bearing the Lease + Identification (such name plate to be furnished by Lessee and affixed on or + prior to the date hereof). + + 4. Lessee represents that all of its representations and + warranties contained in the Lease are true and correct on the date hereof and + that no Default or Event of Default has occurred and is continuing. + + 5. All of the terms and provisions of the Lease Agreement are + hereby incorporated by reference in this Lease Supplement to the same extent as + if fully set forth herein. The Lease is hereby ratified and confirmed and, + except as set forth herein, remains unamended and in full force and effect. + + 6. This Lease Supplement may be executed in any number of + counterparts, each of such counterparts, except as provided in Section 20(e) of + the Lease, shall for all purposes be deemed to be an original and all such + counterparts shall together constitute but one and the same Lease Supplement. + + THIS LEASE SUPPLEMENT HAS BEEN EXECUTED IN MULTIPLE COUNTERPARTS, OF WHICH + THERE IS ONE COUNTERPART DESIGNATED AS "COUNTERPART NO. 1 - CHATTEL PAPER + ORIGINAL'' AND ALL OTHER COUNTERPARTS SHALL BE DESIGNATED WITH NUMBERS OTHER + THAN 1. TO THE EXTENT THAT THIS LEASE SUPPLEMENT CONSTITUTES CHATTEL PAPER + UNDER THE UNIFORM COMMERCIAL CODE, AS IN EFFECT IN ANY APPLICABLE JURISDICTION, + NO SECURITY INTEREST MAY BE CREATED OR PERFECTED BY POSSESSION OF ANY + COUNTERPART OF THIS LEASE SUPPLEMENT OTHER THAN SAID COUNTERPART NO. 1. + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this +Lease Supplement to be duly executed by their authorized officers as of the day +and year first above written. + + LESSOR: + + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee. + + By: M.P. Bonilla + ---------------------------- + Name: MYFANWY PHILLIPS BONILLA + ---------------------------- + Title: ASSISTANT VICE PRESIDENT + ---------------------------- + + + LESSEE: + + ALOHA AIRLINES, INC. + + By: + ---------------------------- + Name: + ---------------------------- + Title: + ---------------------------- + + + By: + ---------------------------- + Name: + ---------------------------- + Title: + ---------------------------- + + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this + Lease Supplement to be duly executed by their authorized officers as of the day + and year first above written. + + + LESSOR: + + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee. + + By: + ---------------------------- + Name: + ---------------------------- + Title: + ---------------------------- + + + LESSEE: + + ALOHA AIRLINES, INC. + + By: /s/ Brenda F. Cutwright + ---------------------------- + Name: Brenda F. Cutwright + ---------------------------- + Title: Sr. Vice President Finance + & Planning and CFO + ---------------------------- + + By: /s/ James M. King + ---------------------------- + Name: James M. King + ---------------------------- + Title: Vice President Planning & + Development + ---------------------------- + + + + + + + COUNTERPART NO. + ------- + + LEASE SUPPLEMENT NO. 2 + + TO AIRCRAFT LEASE AGREEMENT + DATED AS OF NOVEMBER 29, 1995 + + LEASE SUPPLEMENT NO. 2, dated April 12, 1996, between + Wilmington Trust Company, not in its individual capacity but solely as Trustee + f/b/o ITOCHU AirLease (Europe) Limited and Marubeni Airleasing (U.K.) Limited + ("Lessor"), and Aloha Airlines, Inc., a company organized under the laws of + Hawaii ("Lessee"). + + Lessor and Lessee have previously entered into that certain + Aircraft Lease Agreement dated as of November 29, 1995, as supplemented by + Lease Supplement No. 1, dated December 1, 1995 (as amended, modified or + supplemented and in effect from time to time, the "Lease" and the defined terms + therein being hereinafter used with the same meaning), which was filed with the + FAA as one instrument and recorded on January 25, 1996 as Conveyance No. + 11004730. Lessor and Lessee desire to supplement the Lease in order (1) to + subject to the Lease two Pratt & Whitney model JT8D-9A engines described below, + and (2) to remove from the terms of the Lease two Pratt & Whitney model + JT8D-17A engines described below. + + In consideration of the premises and other good and sufficient + consideration, Lessor and Lessee hereby agree as follows: + + 1. Lessor hereby leases to Lessee under the Lease and Lessee + hereby leases from Lessor under the Lease, two (2) Pratt & Whitney JT8D-9A + Engines bearing Manufacturer's Serial Numbers 665868 and 655877, each having + 750 or more rated takeoff horsepower, together with (1) all parts, appliances, + components, accessories and equipment currently installed on, or attached to + any such engine and (2) all flight, engineering, maintenance, overhaul and + repair manuals, drawings, records, logs, documents and other data relating to + the above described engines or parts (such engines, together with the items + identified in clauses (1) and (2) above, are referred to herein collectively as + the "9A Engines"). For all purposes of the Lease, each of the 9A Engines shall + be deemed an "Engine" as defined in the Lease, and part of the Aircraft and + each such Engine shall be maintained, insured and returned to Lessor in the + manner and at the times set forth in the Lease. + + + + + + 2. There are hereby removed from the terms of the Lease the + two (2) Pratt & Whitney JT8D-17A Engines bearing Manufacturer's Serial Numbers + P709529 and P709530, each having 750 or more rated takeoff horsepower, together + with (1) all parts, appliances, components, accessories and equipment currently + installed on, or attached to any such engine and (2) all flight, engineering, + maintenance, overhaul and repair manuals, drawings, records, logs, documents + and other data relating to the above described engines or parts (such engines, + together with the items identified in clauses (1) and (2) above, are referred + to herein collectively as the "Original 17A Engines"). Lessee and Lessor + acknowledge that the Original 17A Engines are, effective on this date, no + longer subject to the terms of the Lease and that Lessee has no interest + therein or thereto under the Lease. + + 3. From the date hereof until the last day of the Term, Lessee + shall affix and keep a metal nameplate of reasonable dimensions in a reasonably + prominent position on each of the 9A Engines bearing the Lease Identification + (such name plate to be furnished by Lessee and affixed on or prior to the date + hereof). + + 4. Lessee represents that all of its representations and + warranties contained in the Lease are true and correct on the date hereof and + that no Default or Event of Default has occurred and is continuing. + + 5. All of the terms and provisions of the Lease Agreement are + hereby incorporated by reference in this Lease Supplement to the same extent as + if fully set forth herein. The Lease is hereby ratified and confirmed and, + except as set forth herein, remains unamended and in full force and effect. + + 6. This Lease Supplement may be executed in any number of + counterparts, each of such counterparts, except as provided in Section 20(e) of + the Lease, shall for all purposes be deemed to be an original and all such + counterparts shall together constitute but one and the same Lease Supplement. + + THIS LEASE SUPPLEMENT HAS BEEN EXECUTED IN MULTIPLE COUNTERPARTS, OF WHICH + THERE IS ONE COUNTERPART DESIGNATED AS "COUNTERPART NO. 1 - CHATTEL PAPER + ORIGINAL" AND ALL OTHER COUNTERPARTS SHALL BE DESIGNATED WITH NUMBERS OTHER + THAN 1. TO THE EXTENT THAT THIS LEASE SUPPLEMENT CONSTITUTES CHATTEL PAPER + UNDER THE UNIFORM COMMERCIAL CODE, AS IN EFFECT IN ANY APPLICABLE JURISDICTION, + NO SECURITY INTEREST MAY BE CREATED OR PERFECTED BY POSSESSION OF ANY + COUNTERPART OF THIS LEASE SUPPLEMENT OTHER THAN SAID COUNTERPART NO. 1. + + + + + + IN WITNESS WHEREOF, Lessor and Lessee each have caused this + Lease Supplement to be duly executed by their authorized officers as of the day + and year first above written. + + LESSOR: + + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee. + + By: M. P. Bonilla + ---------------------------- + Name: MYFANWY PHILLIPS BONILLA + ---------------------------- + Title: ASSISTANT VICE PRESIDENT + ---------------------------- + + + LESSEE: + + ALOHA AIRLINES, INC. + + By: + ---------------------------- + Name: + ---------------------------- + Title: + ---------------------------- + + + By: + ---------------------------- + Name: + ---------------------------- + Title: + ---------------------------- + + + + + + IN WITNESS WHEREOF, - Lessor and Les-gee each have caused this + Lease Supplement to he duly executed by their authorized officers as of the day + and year first above written. + + LESSOR: + + WILMINGTON TRUST COMPANY, not + in its individual capacity but + solely as Trustee. + + By: + ---------------------------- + Name: + ---------------------------- + Title: + ---------------------------- + + + LESSEE: + + ALOHA AIRLINES, INC. + + By: /s/ Brenda F. Cutwright + ---------------------------- + Name: Brenda F. Cutwright + ---------------------------- + Title: Sr. Vice President Finance + & Planning and CFO + ---------------------------- + + By: /s/ James M. King + ---------------------------- + Name: James M. King + ---------------------------- + Title: Vice President Planning & + Development + ---------------------------- + + + + + + + + ASSIGNMENT, ASSUMPTION, AMENDMENT AND CONSENT + + + THIS ASSIGNMENT, ASSUMPTION, AMENDMENT AND CONSENT + ("Assignment"), dated as of December 26, 1996 is between WILMINGTON TRUST + COMPANY, not in its individual capacity but solely as Trustee f/b/o ITOCHU + AirLease (Europe) Limited and Marubeni Airleasing (U.K) Limited ("Lessor"), + ALOHA AIRLINES, INC., a Hawaii Corporation ("Assignor"), and ALOHA AIRLINES, + INC., a Delaware corporation ("Assignee"). + + RECITALS + + WHEREAS, Lessor and Assignor entered into that certain + Aircraft Lease Agreement dated as of November 29, 1995, as it may be + supplemented or amended from time to time (the "Lease"), pursuant to which + Assignor leased from Lessor one (1) Boeing model 737-25A aircraft bearing + manufacturer's serial number 23791 and registration mark N819AL (the + "Aircraft"); + + WHEREAS, effective December 26, 1996, Assignor merged with + Assignee, and Assignee emerged as the surviving entity and the successor in + interest to Lessee. + + AGREEMENT + + NOW, THEREFORE, in consideration of these presents and for +other valuable consideration, the parties agree as follows. + + 1. CERTAIN DEFINED TERMS. Unless otherwise defined herein or + the context otherwise requires, all capitalized terms used in this Assignment + shall have the respective meanings assigned to them in the Lease. + + 2. ASSIGNMENT. As of December 26, 1996 (the "Effective + Date"), the Assignor hereby sells, assigns, transfers and conveys to Assignee + all of the Assignor's right, title and interest in and to the Lease, as if + Assignee were the original party to the Lease. + + 3. ASSUMPTION BY ASSIGNEE. Assignee hereby accepts the + foregoing sale, assignment, transfer and conveyance of all of the Assignor's + right, title, interest, obligations and liabilities in, to and under the Lease + to the Assignee and, as of the Effective Date, agrees to assume and to be bound + by all the terms of, and to undertake all of the obligations of the Assignor + contained in, the Lease, including without limitation, all such obligations + existing at or prior to, or attributable to acts or events occurring prior to + the Effective Date. + + + + + + + 4. LESSOR CONSENT. + + (a) Pursuant to Section 8(i) of the Lease, Lessor +acknowledges and consents to the foregoing sale, assignment, transfer and +conveyance of all of the Assignor's right, title, interest, obligations and +liabilities in, to and under the Lease to the Assignee under this Assignment, +and acknowledge the rights and obligations of the Assignee described in this +Assignment. + + (b) Lessor further acknowledges and agrees that from +and after the Effective Date Assignee shall be deemed the "Lessee" for all +purposes of the Lease and each reference in the Lease to the Assignor as +"Lessee" shall be deemed after the Effective Date for all purposes to refer to +the Assignee and that, from and after the Effective Date Lessor will perform +its obligations under the Lease, in all respects as if Assignee were the +original party to the Lease as "Lessee" thereunder. + + 5. REPRESENTATIONS, WARRANTIES AND COVENANTS OF ASSIGNEE. +Assignee represents and warrants as follows: + + (a) Assignee is a corporation duly organized and existing in + good standing under the Laws of the State of Delaware and has the corporate + power and authority to carry on its business as presently conducted and to + perform its obligations under this Assignment. + + (b) This Assignment has been duly authorized by all necessary + corporate action on the part of Assignee and does not require any approval of + the stockholders of Assignee (or if such approval is required, such approval + has been obtained), and neither the execution and delivery hereof nor the + consummation of the transactions contemplated hereby nor compliance by Assignee + with any of the terms and provisions hereof will contravene any Law applicable + to Assignee or result in any breach of, or constitute any default under, or + result in the creation of, any Lien upon any property of Assignee under, any + credit agreement or instrument, or other agreement or instrument to which + Assignee is a party or by which Assignee or its properties or assets may be + bound or affected. + + (c) Assignee has received or has complied with every + necessary consent, approval, order, or authorization of, or registration with, + or the giving of prior notice to, any Governmental Entity having jurisdiction + with respect to the use and operation of the Aircraft and the execution and + delivery of this Assignment of the validity and enforceability hereof. + + (d) This Assignment has been duly entered into and delivered +by Assignee and constitutes a valid, legal and binding + + + + + + +obligation of Assignee, enforceable in accordance with its terms, except as +enforceability may be limited by bankruptcy, reorganization, moratorium or +other similar Laws and by general principles of equity, whether considered in a +proceeding at Law or in equity. + + (e) Assignee is a Certified Air Carrier and holds all +licenses, certificates, permits and franchises from the appropriate agencies of +the United States of America and/or all other governmental authorities having +jurisdiction which are necessary to authorize the Assignee to engage in air +transport and to carry on its business as presently conducted and to be +conducted with the Aircraft. + + (f) Assignee is a "citizen of the United States" as defined +in 40102(a)(15) of Title 49 of the United States Code. + + 6. GOVERNING LAW. This Assignment shall governed by the Laws +of the State of New York. + + + [THE REMAINDER OF THIS PAGE IS INTENTIONALLY BLANK.] + + + + + + IN WITNESS WHEREOF, the undersigned parties have caused this +Assignment, Assumption, Amendment and Consent to be duly executed and delivered +by their duly authorized officers as of the date first written above. + + ASSIGNOR: ALOHA AIRLINES, INC., + A HAWAII CORPORATION + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: BRENDA F. CUTWRIGHT + ---------------------------------- + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + --------------------------------- + + + By: /s/ Owen Sekimura + ------------------------------------ + Name: OWEN SEKIMURA + ---------------------------------- + Title: STAFF VICE PRESIDENT - + FINANCE & CONTROLLER + --------------------------------- + + + ASSIGNEE: ALOHA AIRLINES, INC., A + DELAWARE CORPORATION + + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: BRENDA F. CUTWRIGHT + ---------------------------------- + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + --------------------------------- + + + By: /s/ Owen Sekimura + ------------------------------------ + Name: Owen Sekimura + ---------------------------------- + Title: STAFF VICE PRESIDENT - + FINANCE & CONTROLLER + --------------------------------- + + + LESSOR: WILMINGTON TRUST COMPANY, NOT + IN ITS INDIVIDUAL CAPACITY BUT + SOLELY AS TRUSTEE F/B/O ITOCHU + AIRLEASE (EUROPE) LIMITED AND + MARUBENI AIRLEASING (U.K) + LIMITED + + + By:____________________________________ + + Name:__________________________________ + + Title:_________________________________ + + + + + + IN WITNESS WHEREOF, the undersigned parties have caused this + Assignment, Assumption, Amendment and Consent to be duly executed and delivered + by their duly authorized officers as of the date first written above. + + + ASSIGNOR: ALOHA AIRLINES, INC., + A HAWAII CORPORATION + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: BRENDA F. CUTWRIGHT + ---------------------------------- + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + --------------------------------- + + + By: /s/ Owen Sekimura + ------------------------------------ + Name: OWEN SEKIMURA + ---------------------------------- + Title: STAFF VICE PRESIDENT - + FINANCE & CONTROLLER + --------------------------------- + + + ASSIGNEE: ALOHA AIRLINES, INC., A + DELAWARE CORPORATION + + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: BRENDA F. CUTWRIGHT + ---------------------------------- + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + --------------------------------- + + + By: /s/ Owen Sekimura + ------------------------------------ + Name: Owen Sekimura + ---------------------------------- + Title: STAFF VICE PRESIDENT - + FINANCE & CONTROLLER + --------------------------------- + + + LESSOR: WILMINGTON TRUST COMPANY, NOT + IN ITS INDIVIDUAL CAPACITY BUT + SOLELY AS TRUSTEE F/B/O ITOCHU + AIRLEASE (EUROPE) LIMITED AND + MARUBENI AIRLEASING (U.K) + LIMITED + + + By:____________________________________ + + Name:__________________________________ + + Title:_________________________________ + + + + + + IN WITNESS WHEREOF, the undersigned parties have caused this + Assignment, Assumption, Amendment and Consent to be duly executed and delivered + by their duly authorized officers as of the date first written above. + + + ASSIGNOR: ALOHA AIRLINES, INC., + A HAWAII CORPORATION + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: BRENDA F. CUTWRIGHT + ---------------------------------- + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + --------------------------------- + + + By: /s/ Owen Sekimura + ------------------------------------ + Name: OWEN SEKIMURA + ---------------------------------- + Title: STAFF VICE PRESIDENT - + FINANCE & CONTROLLER + --------------------------------- + + + ASSIGNEE: ALOHA AIRLINES, INC., A + DELAWARE CORPORATION + + + By: /s/ Brenda F. Cutwright + ------------------------------------ + Name: BRENDA F. CUTWRIGHT + ---------------------------------- + Title: SR. VICE PRESIDENT FINANCE & + PLANNING AND CFO + --------------------------------- + + + By: /s/ Owen Sekimura + ------------------------------------ + Name: Owen Sekimura + ---------------------------------- + Title: STAFF VICE PRESIDENT - + FINANCE & CONTROLLER + --------------------------------- + + + LESSOR: WILMINGTON TRUST COMPANY, NOT + IN ITS INDIVIDUAL CAPACITY BUT + SOLELY AS TRUSTEE F/B/O ITOCHU + AIRLEASE (EUROPE) LIMITED AND + MARUBENI AIRLEASING (U.K) + LIMITED + + + By:____________________________________ + + Name:__________________________________ + + Title:_________________________________ + + + + + + + CONSENT + + THIS CONSENT is made this ______ day of ________________, +1996 by WILMINGTON TRUST COMPANY, not in its individual capacity but solely as +Trustee f/b/o ITOCHU AirLease (Europe) Limited and Marubeni Airleasing (U.K) +Limited ("Lessor") in favor of Aloha Airlines, Inc., a Hawaii corporation +("Aloha Hawaii") and Aloha Airlines, Inc., a Delaware corporation ("Aloha +Delaware"). + + RECITALS + + WHEREAS, Lessor and Aloha Hawaii entered into that certain + Aircraft Lease Agreement dated as of November 29, 1995 (the "Lease"), pursuant + to which Aloha Hawaii leased from Lessor one (1) Boeing model 737-25A aircraft + bearing manufacturer's serial number 23791 and registration mark N819AL (the + "Aircraft"); + + WHEREAS, Aloha Hawaii and Aloha Delaware are wholly owned + subsidiaries of Aloha Airgroup, Inc., a Hawaii corporation. + + WHEREAS, effective ____________________________________, 1996, Aloha Hawaii + merged with Aloha Delaware, and Aloha Delaware emerged as the surviving entity + and the successor in interest to Aloha Hawaii. + + WHEREAS, as a result of the merger, all of the assets and + liabilities of Aloha Hawaii, including the Lease, will become assets and + liabilities of Aloha Delaware by operation of law. + + WHEREAS, Aloha Hawaii has requested that Lessor consent to the + merger and the transfer by operation of law of the Lease and the Notice of + Assignment to Aloha Delaware. + + NOW, THEREFORE, in consideration of these presents and for + other valuable consideration, Lessor does hereby consent to the merger of Aloha + Hawaii with and into Aloha Delaware and the transfer by operation of law of the + Lease to Aloha Delaware, upon and subject to the condition that this consent + shall not be construed as authorizing any other or further assignment of the + Lease, and that all of the rights of the undersigned as set forth in the Lease + are expressly reserved. + + + + + + IN WITNESS WHEREOF, the undersigned has executed this Consent + as of the date first written above. + + LESSOR: WILMINGTON TRUST COMPANY, + NOT IN ITS INDIVIDUAL + CAPACITY BUT SOLELY AS + TRUSTEE F/B/O ITOCHU + AIRLEASE (EUROPE) LIMITED + AND MARUBENI AIRLEASING + (U.K) LIMITED + + + + + By________________________________ + + Name______________________________ + + Title_____________________________ + + + + + + + LEASE ASSIGNMENT + + This Lease Assignment (this "Agreement") dated as of August 7, 1998 is + entered into by and between (1) ITOCHU Airlease Limited, a company formed under + the laws of the Republic of Ireland ("ITOCHU"), (ii) MA-Lease Limited, a + company duly formed under the laws of England ("Marubeni"), (iii) Wilmington + Trust Company, not in its individual capacity but solely as trustee f/b/o + ITOCHU and Marubeni ("Trustee", and together With ITOCHU and Marubeni, each, an + "Assignor" and collectivelly, "Assignors"), and Boeing Capital Corporation, a + company duly formed under the laws of Delaware (the "Assignee"). + + RECITALS + + WHEREAS, Trustee is the Lessor, under that certain Aircraft Lease +Agreement dated as of November 29, 1995 and recorded with the Federal +Aviation Administration on ________, 199__, and assigned conveyance number +____________, with Aloha Airlines, Inc., as the Lessee, as supplemented by +Lease Supplement No. 1 and as amended and supplemented by Letter Agreement +No. 1 dated as of November 29, 1995 (collectively, the "Lease"), covering +one (1) Boeing 737-25A aircraft (as more particularly described under the +Lease, the "Aircraft"), + + WHEREAS, pursuant to that certain Aircraft Purchase Agreement dated +as of August _________, 1998 (the "Purchase Agreement"), by and between +Assignors and Assignee, Assignors desire to assign, and Assignee desires to +accept, the assignment of the Assignors' rights, titles, interest in, to and +under the Lease; + + NOW, THEREFORE, in consideration of the premises and mutual agreements + contained herein and for other good and valuable consideration, receipt of + which is hereby acknowledged, the parties hereto agree as follows, + + Section 1, DEFINITIONS. Terms used herein but not defined shall have +the meanings set forth in the Lease. + + Section 2. ASSIGNMENT. Assignor hereby assigns to Assignee and +Assignee hereby accepts, all of Assignors' right, title and interest under +the Lease. + + Section 3. ASSUMPTION OF OBLIGATIONS. Assignee hereby assumes all +obligations of the Assignors under the Lease to the extent arising from and +after the date hereof, + + Section 4. AMENDMENTS TO THE LEASE. With respect to events occurring on + and after the date hereof, all references to "Beneficiaries" contained in the + Lease shall be deemed to be references to Lessor. + + Section 5. CONCERNING THE TRUSTEE. Wilmington Trust Company is + entering into this Agreement solely in its capacity as Trustee under the Trust + Agreement, except as otherwise expressly provided herein, and Wilmington Trust + Company (or any successor Trustee) shall not be personally liable for or on + account of any of the statements, representations, warranties, covenants or + obligations of the Assignor hereunder; provided, however, that Wilmington Trust + Company (or any other successor Trustee) shall be liable hereunder for its own + gross negligence or willful misconduct or for breach of its covenants or its + representations. and warranties to the extent covenanted or made in its + individual capacity. extent covenanted or made in its individual capacity. + + + + + + Section 6. MISCELLANEOUS. + + (a) This Agreement may be signed in one or more counterparts with the +same effect as if the signatures in each counterpart were upon a single +document. All counterparts shall be considered an original hereof. + + (b) The terms and provisions hereof shall inure to the benefit of and +be binding on the parties hereof and their respective successors and assigns. + + (c) This Agreement shall in all respects be governed by and construed +in accordance with the laws of the State of New York, including all matters +of construction, validity, and performance. + +IN WITNESS WHEREOF, the undersigned have executed this Agreement as of the +date first above written. + +WILMINGTON TRUST COMPANY ITOCHU AIRLEASE LIMITED +not in its individual capacity but as ("Assignor") +trustee ("Assignor") + + +By: /s/ Bruce L. Besson By: [Illegible] + ------------------------------ ------------------------- +Name: Bruce L. Besson Name: + ------------------------------ ------------------------- +Title: V.P. Title: ATTORNEY IN FACT + ------------------------------ ------------------------- + + +MA-LEASE LIMITED BOEING CAPITAL CORPORATION +("Assignor") ("Assignor") + +By: [Illegible] By: /s/ Stephen J. Novak + ------------------------------ ------------------------- +Name: Name: Vice President + ------------------------------ ------------------------- +Title: ATTORNEY IN FACT Title: Stephen J. Novak + ------------------------------ ------------------------- + + + 2 + + + + + CONSENT + + Reference is hereby made to the attached Lease Assignment (capitalized + terms used herein without definition are used with the same meaning as in the + Lease Assignment). The undersigned hereby consents to the assignment and + assumption set forth therein and acknowledges that the Assignors shall, from + and after the date hereof, be released from all further obligations under the + Lease, except that such release shall not affect any liability arising on or + before the effective date of the Lease Assignment. + + The undersigned hereby also agrees that Aloha Airlines, Inc. shall + cause each of the Assignors to be added as an additional insured under the + liability insurance policies required to be maintained by Aloha Airlines, Inc., + as Lessee, pursuant to the Lease. Aloha Airlines, Inc. agrees that its + obligations to maintain such insurance shall continue until the earlier to + occur of (i) two years after the date hereof, and (ii) the D check or its + equivalent first occurring after the date hereof. + + IN WITNESS WHEREOF, the undersigned has executed this Consent as of +August 7, 1998. + + +ALOHA AIRLINES, INC. + + +By: /s/ Brenda F. Cutwright + --------------------------------- +Name: Brenda F. Cutwright + --------------------------------- + Sr. Vice President Finance & +Title: Planning and CFO + --------------------------------- + + + +By: /s/ James M. King + --------------------------------- +Name: James M. King + --------------------------------- + Vice President +Title: Planning & Development + --------------------------------- + + + + 3 + + + + + CONSENT + + Reference Is hereby made to the attached Lease Assignment (capitalized + terms used herein without definition are used with the same meaning as in the + Lease Assignment). The undersigned hereby consents to the assignment and + assumption set forth therein and acknowledges that the Assignors shall, from + and after the date hereof, be released from all further obligations under the + Lease, except that such release shall not affect any liability arising on or + before the effective date of the Lease Assignment. + + The undersigned hereby also agrees that Aloha Airlines, Inc. shall + cause each of the Assignors to be added as an additional insured under the + liability insurance policies required to be maintained by Aloha Airlines, Inc., + as Lessee, pursuant to the Lease. Aloha Airlines, Inc. agrees that its + obligations to maintain such insurance shall continue until the earlier to + occur of (i) two years after the date hereof, and (ii) the D check or its + equivalent first occurring after the date hereof. + + IN WITNESS WHEREOF, the undersigned has executed this Consent as of +August 7, 1998. + +ALOHA AIRLINES, INC. + + +By: /s/ Brenda F. Cutwright + --------------------------------- +Name: Brenda F. Cutwright + --------------------------------- + Sr. Vice President Finance & +Title: Planning and CFO + --------------------------------- + + + +By: /s/ James M. King + --------------------------------- +Name: James M. King + --------------------------------- + Vice President +Title: Planning & Development + --------------------------------- + + + + + 3 + + + + + + CONSENT + + Reference is hereby made to the attached Lease Assignment (capitalized + terms used herein without definition are used with the same meaning as in the + Lease Assignment). The undersigned hereby consents to the assignment and + assumption set forth therein and acknowledges that the Assignors shall, from + and after the date hereof, be released from all further obligations under the + Lease, except that such release shall not affect any liability arising on or + before the effective date of the Lease Assignment. + + The undersigned hereby also agrees that Aloha Airlines, Inc. shall + cause each of the Assignors to be added as an additional insured under the + liability insurance policies required to be maintained by Aloha Airlines, Inc., + as Lessee, pursuant to the Lease. Aloha Airlines, Inc. agrees that its + obligations to maintain such insurance shall continue until the earlier to + occur of (i) two years after the date hereof, and (ii) the D check or its + equivalent first occurring after the date hereof. + + IN WITNESS WHEREOF, the undersigned has executed this Consent as of +August ___,1998. + + ALOHA AIRLINES, INC. + + By: + --------------------------------- + Name: + --------------------------------- + Title. + --------------------------------- + + + By: + --------------------------------- + Name: + --------------------------------- + Title: + --------------------------------- + + + + + + LEASE EXTENSION AND AMENDMENT AGREEMENT + +THIS LEASE EXTENSION AND AMENDMENT AGREEMENT (this "Agreement") dated as of +November ____, 2001, is by and between BOEING CAPITAL CORPORATION, a Delaware +corporation ("Lessor") and ALOHA AIRLINES, a Delaware corporation ("Lessee). + + WITNESSETH + +WHEREAS, Lessor (as successor to Wilmington Trust Company, not in its individual +capacity but solely as trustee f/b/o ITOCHU AirLease (Europe) Limited and +Marubeni Airleasing (U.K) Limited, pursuant to certain Purchase Agreement and +Assignment Agreement dated June 22, 1998) are parties to Aircraft Lease +Agreement dated as of November 29, 1995 and filed with the Federal Aviation +Administration ("FAA") on January 25, 1996 as conveyance no. 11004730 (the +"Lease") as supplemented by Lease Supplement No. 1 filed with the FAA on +_________________________ as conveyance no. _________________________, as +further supplemented by Lease Supplement No. 2 filed with the FAA on April 29, +1996 as conveyance no. 22011203 (collectively the "Supplement"). The Lease and +the Lease Supplement shall be collectively referred to as the "Lease Agreement." + +WHEREAS, pursuant to the Lease Agreement, Lessee agreed to lease from Lessor one +(1) Boeing Model 737-25A aircraft bearing manufacturer's serial number 23791 +(the "Aircraft") for a term of ninety-six (96) months; + +WHEREAS, Lessee desires to extend the lease Expiration Date from December 1, +2003 to February 1, 2004 and Lessor is willing to agree to this extension; and + +WHEREAS, the parties hereto further desire to amend the Lease Agreement upon the +terms and conditions hereinafter set forth. + +NOW, THEREFORE, in consideration of the mutual agreements contained herein and +other good and valuable consideration, the receipt and sufficiency of which are +hereby acknowledged, the parties hereto agree as follows: + +SECTION 1. DEFINITIONS + +Except as otherwise defined in this Agreement the capitalized terms used herein +shall have the meanings attributed thereto in the Lease Agreement. + + + Page 1 + + + +SECTION 2. AMENDMENTS + +The Lease Agreement is hereby amended in the following manner: + + 2.1 Section 4.(a)(i) entitled "Basic Rent" is hereby deleted in its +entirety and the following is inserted in lieu thereof: + + "(i) BASIC RENT: The Basic Rent as set forth on Exhibit C-1 hereto + throughout the Term hereof, payable in consecutive installments in arrears + on each Basic Rent Payment Date." + + 2.2 Exhibit C-1 definition "Basic Rent" is hereby deleted in its entirety +and the following is inserted in lieu thereof: + + [This provision has been omitted as confidential information, and + is separately filed with the Commission.] + + 2.3 Exhibit C-1 definition "Casualty Value" is hereby deleted in its +entirety and the following is inserted in lieu thereof: + + [This provision has been omitted as confidential information, and + is separately filed with the Commission.] + + 2.4 Exhibit C-1 definition "Security Deposit" is hereby deleted in its +entirety and the following is inserted in lieu thereof: + + [This provision has been omitted as confidential information, and + is separately filed with the Commission.] + + 2.5 Exhibit C-1 definition "Security Letter of Credit" is hereby deleted in +its entirety. + + 2.6 Exhibit D, Article 3 is hereby deleted in its entirety and revised to +reflect the new expiration date of February 1, 2004. The following is inserted +in lieu thereof: + + "3. The term for the Delivered Aircraft shall commence on the Delivery Date + and shall end on the Expiration Date, which shall be February 1, 2004." + + + Page 2 + + + +SECTION 3. TRANSFER OF PAYMENTS + + [This provision has been omitted as confidential information, and +is separately filed with the Commission.] + + +SECTION 4. REPRESENTATIONS, WARRANTIES AND COVENANTS + + 4.1 REPRESENTATIONS AND WARRANTIES. Lessee hereby repeats the +representations and warranties in Section 3 of the Lease Agreement as if made +with reference to the facts and circumstances existing as at the date hereof and +as if the references in such representations and warranties to this Agreement +refer not to the Lease but rather to the Lease Agreement as amended by this +Agreement. + +SECTION 5. CONDITIONS PRECEDENT + + This Agreement is subject to the satisfaction of each of the following +conditions or receipt of documents by Lessor, each in form and substance +satisfactory to Lessor: + + 5.1 CORPORATE ACTION. Lessee, must have furnished to Lessor satisfactory +proof that Lessee has taken all corporate action necessary to authorize this +Agreement. + + 5.2 OPINION OF COUNSEL. Lessor shall have received a legal opinion from +Lessee's counsel in form and substance satisfactory to Lessor and confirming, +INTER ALIA, that this Agreement has (i) been duly signed and delivered on behalf +of Lessee, (ii) that the representations, warranties and covenants of Lessee set +forth in this Agreement are valid and legally binding obligations of Lessee, +enforceable in accordance with their respective terms in the jurisdictions in +which the Aircraft will be registered, operated and located; (iii) this +Agreement and all other necessary documents have been, or will be, duly filed, +registered and recorded as necessary with the aviation or other comparable +authority in all places as may be necessary and all other action has been taken +to fully protect Lessor's title to the Aircraft against all persons whomsoever +(specifying in such opinion the location and nature of all such filings, +registrations or recordings); (iv) upon termination of the Lease Agreement for +any reason, Lessor will be able to promptly take possession of the Aircraft and +immediately return the Aircraft to facilities in the United States designated by +Lessor in a manner satisfactory to Lessor. + + 5.3 APPROVAL OF LESSOR'S COUNSEL. All documents incident to this +transaction will satisfactory to counsel for Lessor. + + 5.4 ADDITIONAL INFORMATION. Lessee must have furnished Lessor with any +other opinions, documents, evidence, materials and information as Lessor may +reasonably request. + + + Page 3 + + + + 5.5 ENGLISH. All certificates, opinions, statements, evidence, proof, +documents, materials and information required under this Section must be +furnished in English. + + 5.6 INSURANCE. Lessee must have furnished to Lessor certificates of +insurance evidencing the insurance required under the Lease as extended hereby. + +SECTION 6. CONDITIONS SUBSEQUENT + + 6.1 RECORDATION. Immediately following execution of this Agreement by +Lessor and Lessee, the Agreement will be filed for recordation by Lessee with +the FAA, and all other necessary action will be taken by Lessee to fully protect +the title of Lessor in the Aircraft. Lessee agrees that failure to effect such +filings, registrations and/or recordations as of the date falling 2 weeks after +execution by the last party to execute this Agreement shall constitute an "Event +of Default" under the Lease Agreement and Lessor shall have the right to +exercise all remedies available to it under Section 13 of the Lease Agreement +with respect to such Event of Default. + +SECTION 7. MISCELLANEOUS + + 7.1 HEADINGS. The section and paragraph headings in this Agreement are for +convenience of reference only and shall not define or limit any of the terms or +provisions hereof. + + 7.2 CHOICE OF LAW. THIS AGREEMENT SHALL IN ALL RESPECTS BE GOVERNED AND +CONSTRUED IN ACCORDANCE WITH THE LAWS OF THE STATE OF NEW YORK, INCLUDING ALL +MATTERS OF CONSTRUCTION, VALIDITY AND PERFORMANCE. + + 7.3 COUNTERPARTS. This Agreement may be executed by the parties hereto in +separate counterparts, each of which when so executed and delivered shall be an +original, but all such counterparts shall together constitute but one and the +same instrument. + + 7.4 SUCCESSORS AND ASSIGNS. This Agreement shall be binding upon and shall +inure to the benefit of, and shall be enforceable by and against, the parties +hereto and their respective successors and permitted assigns. + + 7.5 EFFECT OF AGREEMENTS. Except as expressly amended hereby, the Lease +Agreement is and shall continue in full force and effect. + + + Page 4 + + + +IN WITNESS WHEREOF, the parties hereto have caused this Agreement to be duly +executed and delivered by their respective duly authorized officers as of the +date first above written. + + + BOEING CAPITAL CORPORATION + + By: /s/ [ILLEGIBLE] + ------------------------------- + + Its: Vice President + ------------------------------- + + + ALOHA AIRLINES, INC. + + + By: + ------------------------------- + + Its: + ------------------------------- + + + By: + ------------------------------- + + Its: + ------------------------------- + + + Page 5 + + + +IN WITNESS WHEREOF, the parties hereto have caused this Agreement to be duly +executed and delivered by their respective duly authorized officers as of the +date first above written. + + + BOEING CAPITAL CORPORATION + + By: + ------------------------------- + + Its: + ------------------------------- + + + ALOHA AIRLINES, INC. + + By: /s/ Brenda F. Cutwright + ------------------------------- + BRENDA F. CUTWRIGHT + + Its: EXECUTIVE VICE PRESIDENT & CFO + ------------------------------- + + By: /s/ James M. King + ------------------------------- + JAMES M. KING + + Its: SENIOR VICE PRESIDENT PLANNING + AND BUSINESS DEVELOPMENT + ------------------------------- + + + Page 5 + + + +IN WITNESS WHEREOF, the parties hereto have caused this Agreement to be duly +executed and delivered by their respective duly authorized officers as of the +date first above written. + + + BOEING CAPITAL CORPORATION + + By: /s/ [ILLEGIBLE] + ------------------------------- + + Its: Vice President + ------------------------------- + + + ALOHA AIRLINES, INC. + + + By: + ------------------------------- + + Its: + ------------------------------- + + + By: + ------------------------------- + + Its: + ------------------------------- + + + Page 5 + +
+
\ No newline at end of file diff --git a/extensions/superboogav2/benchmark_texts/questions.json b/extensions/superboogav2/benchmark_texts/questions.json new file mode 100644 index 0000000000000000000000000000000000000000..7dad24e07d379181c7f0a8a9fa47fb853a812876 --- /dev/null +++ b/extensions/superboogav2/benchmark_texts/questions.json @@ -0,0 +1,291 @@ +[ + { + "text": "extensions/superboogav2/benchmark_texts/aircraft_lease.txt", + "questions": [ + { + "question_variants": [ + "What is a wet lease?", + "Agh, I'm really wracking my brain here, but can't figure it out. What is a wet lease?", + "I've been trying to wrap my head around this concept and it's just not clicking. Could you elucidate the concept of a wet lease?", + "I'm finding it so hard to understand this whole wet lease thing! Would you be so kind as to explicate on the matter of what is known as a wet lease in the domain of aviation?", + "I've spent hours trying to grasp this and I'm still lost. Could you engage in a detailed exploration of the intricate and nuanced topic that is the wet lease, as it is commonly understood and applied within the broad and complex ecosystem of aviation?" + ], + "criteria": [ + "WET LEASE shall mean any arrangement whereby Lessee agrees to\n operate the Aircraft under a contractual arrangement with a third party\n pursuant to which no rights to any aircraft specifically identified by serial\n number or registration number are granted to such third party and pursuant to\n which the Aircraft (i) remains at all times under the sole and complete\n operational control of Lessee", + "(ii) shall be operated solely by cockpit crew\n employed by Lessee possessing all current certificates and licenses required by\n applicable Laws and (iii) shall be maintained by Lessee in accordance with all\n the provisions of the Lease including, but not limited to, Sections 6(d) and 12\n (it being understood and agreed by Lessor and Lessee that any Wet Lease shall,\n by its terms, be in all cases subject and subordinate to this Lease)." + ] + }, + { + "question_variants": [ + "What is PBGC?", + "I'm stumped! Can you tell me what PBGC is?", + "I've been racking my brain trying to understand PBGC. Would you mind explaining the concept to me?", + "I've been trying to grasp what PBGC represents in the context of pension benefits, but I'm not getting it. Would it be possible for you to expound upon the matter?", + "I'm having trouble understanding the multi-dimensional entity known as 'PBGC'. Could you embark on an exploration of it? How is it traditionally comprehended, interpreted, and implemented within the sphere of pension benefits?" + ], + "criteria": [ + "PBGC shall mean the Pension Benefit Guaranty Corporation\n established pursuant to Subtitle A of Part IV of ERISA, and any successor\n thereof." + ] + }, + { + "question_variants": [ + "What is LIEN?", + "I can't seem to find information on what LIEN is. Can you help me?", + "I'm feeling stuck. I can't seem to find any information on what LIEN is. Could you provide some insights?", + "It's like I'm chasing my tail here. I've been searching high and low and I just can't seem to find anything that clearly explains what LIEN is. Could you lend me a hand in understanding it?", + "I'm in a bit of a pickle. I've scoured the internet, flipped through countless books, and I still can't seem to find any definitive information on what LIEN is. Could you possibly help me get my head around it?" + ], + "criteria": [ + "LIEN shall mean any mortgage, pledge, lien, charge,\n encumbrance, lease, exercise of rights, security interest or claim." + ] + }, + { + "question_variants": [ + "What happens if the Lease is terminated by operation of law?", + "I'm a bit lost here. What happens if the Lease is terminated by operation of law? Can you help me understand?", + "I've been trying to figure this out but I'm stuck. What exactly happens if the Lease is terminated by operation of law? Could you explain?", + "I've been poring over this and I'm still not clear. What exactly transpires if the Lease is terminated by operation of law? I'd appreciate your help in understanding this.", + "I'm really hitting a wall here. I've been trying to understand this, but it feels like I'm reading a foreign language. What's the end result if the Lease is terminated by operation of law? Any help in understanding this, particularly a detailed breakdown, would be greatly appreciated." + ], + "criteria": [ + "If for any reason whatsoever this Lease shall be terminated\n in whole or in part by operation of law (other than termination under any\n bankruptcy laws as now or hereafter in effect), Lessee nonetheless agrees to\n pay to Lessor amounts equal to the Rent payments hereunder at the time such\n payments would have become due and payable in accordance with the terms hereof", + "had this Lease not been terminated so long as Lessee is able to use, possess\n and quietly enjoy the Aircraft, and so long as such payments are made and all\n other terms and conditions hereof are complied\n\n -16-\n\n\n\n\n\nwith by Lessor and Lessee, Lessor and Lessee will deem this Lease to remain in\nfull force and effect." + ] + }, + { + "question_variants": [ + "What happens if a discrepancy or malfunction is detected during the Acceptance Flight?", + "I'm having difficulty understanding this part. What exactly happens if a discrepancy or malfunction is detected during the Acceptance Flight? Can you provide a thorough explanation?", + "I'm stuck on this one. I'm struggling to comprehend what steps are taken if a discrepancy or malfunction is detected during the Acceptance Flight. Could you possibly explain this in detail?", + "I've been poring over this issue for a while, and it's not clicking. What steps are taken or what are the implications if a discrepancy or malfunction is detected during the Acceptance Flight? I'd appreciate a comprehensive explanation.", + "I'm really hitting a wall here. I've been trying to understand, but it's like I'm reading a foreign language. What's the end result or the next step if a discrepancy or malfunction is detected during the Acceptance Flight? Any help in understanding this, particularly a detailed breakdown, would be greatly appreciated." + ], + "criteria": [ + "If, during the Acceptance Flight, no discrepancy or malfunction is\n detected with respect to the airworthiness or operational nature of\n the Aircraft by normal airline standards, then (i) the delivery of\n the Aircraft from Lessor to Lessee hereunder shall occur, the\n Aircraft shall be accepted by Lessee hereunder whilst the Aircraft\n is located at the Delivery Location, and Lessee shall execute and\n deliver the Lease Supplement, (ii) th", + "e Aircraft shall continue en\n route to a location selected by Lessee (the \"Ferry Location\"), under\n the operational control of Lessee from the time of such delivery and\n acceptance (the Acceptance Flight shall terminate at the time of\n such delivery and acceptance, and that portion of the flight from\n the Delivery Location to the Ferry Location is herein called the\n \"Ferry Flight\"), and (iii) Lessee shall bear the costs of the flight\n ", + " crew, fuel and other costs\n\n\n -12-\n\n\n\n\n\n relating to the Ferry Flight and shall reimburse the Beneficiaries\n therefor promptly following receipt of the Beneficiaries, invoice\n therefor, PROVIDED, HOWEVER, that, if any discrepancy or malfunction\n is detected with respect to the airworthiness during the Acceptance\n Flight, then, at Lessee's option after consultation with Lessor,\n either ", + "(A) the Aircraft shall be delivered to and accepted by Lessee\n at the Delivery Location and shall be ferried to the Ferry Location,\n as provided in clauses (i), (ii) and (iii) above, where Lessee shall\n remedy such discrepancy or malfunction at the cost (without mark up)\n of the Beneficiaries (provided that such subsequent repair or\n maintenance work shall not affect Lessee's acceptance of the\n Aircraft hereunder), or (B) the Aircraft s", + "hall return to the\n Beneficiaries' storage and maintenance facility where such\n discrepancy or malfunction shall be corrected at the Beneficiaries'\n expense, in which case the Delivery Date shall be postponed to such\n date as the Beneficiaries shall advise, subject to the second\n paragraph of Section 3(c) below. Any discrepancy or malfunction\n detected of an airworthiness nature shall be corrected by Lessee or\n the Beneficiari", + "es in accordance with clause (A) or (B) above, as\n applicable, at Beneficiaries, expense. If during the Acceptance\n Flight a discrepancy or malfunction is detected with respect to the\n operational nature of the Aircraft by normal airline standards but\n no discrepancy or malfunction is detected with respect to the\n airworthiness of the Aircraft, then the Aircraft shall be delivered\n to and accepted by Lessee at the Delivery Location as p", + "rovided in\n clause (A) above, and Lessee shall remedy such discrepancy or\n malfunction at the cost (without mark up) of the Beneficiaries.\n\n In anticipation of the occurrence of the Delivery Date, the\n Beneficiaries retained a flight crew to conduct the Acceptance\n Flight and will incur costs relating to such retention. In\n connection therewith, Lessee agrees to reimburse the Beneficiaries,\n promptly following Lessee's receipt", + " of an invoice therefor, for\n one-half of the costs incurred by the Beneficiaries in connection\n with retaining such flight crew for the Acceptance Flight, which\n costs relate to the period commencing on and including November 29,\n 1995 and ending on the day immediately preceding the Delivery Date." + ] + }, + { + "question_variants": [ + "What condition must the Aircraft meet before being delivered to the Lessee?", + "I'm having some trouble understanding this part. Could you please clarify what condition the Aircraft must meet before being delivered to the Lessee? I would appreciate a detailed explanation.", + "I'm stuck on this point. I'm finding it difficult to understand the specific condition the Aircraft must be in before being handed over to the Lessee. Could you possibly provide a comprehensive explanation?", + "I'm feeling a bit lost here. I'm having trouble understanding the exact condition or standard that the Aircraft must meet before being delivered to the Lessee. Could you provide a detailed walkthrough of the requirements?", + "I've hit a bit of a wall with this one. I've been trying my best to understand this, but it's proving to be quite complex. What is the precise condition that the Aircraft must meet before it can be delivered to the Lessee? Any help in understanding this, particularly a detailed explanation, would be of great help." + ], + "criteria": [ + "(d) Lessee's obligation to lease the Aircraft hereunder from\nLessor shall also be conditioned upon the Aircraft being delivered to Lessee in\nthe following condition:\n\n (1) The Aircraft shall be airworthy and in good\n operating condition\n\n\n -11-\n\n\n\n\n with all of the Aircraft equipment, components and systems;\n\n (2) The Aircraft shall be clean;\n\n (3) The Airc", + "raft shall meet the requirements for\n airworthiness certification by the FAA;\n\n (4) A borescope of the Engines and the inspection of\n the APU in accordance with the Manufacturer's or APU manufacturer's\n recommendation shall have been performed at the direction of Lessee\n but under the control and at the cost of the Beneficiaries, and any\n discrepancies discovered in connection therewith shall have been\n corrected;" + ] + }, + { + "question_variants": [ + "What rights does the Lessee waive under section 4(c)?", + "Can you tell me about the rights that the Lessee gives up under section 4(c)?", + "I'm having some difficulty here, could you please explain to me what rights the Lessee is forfeiting under the terms of section 4(c)?", + "I'm really struggling to understand this part, it's quite complex. Could you clarify what rights the Lessee is explicitly waiving as per section 4(c) in this agreement?", + "I'm pulling my hair out! What does this even mean? Can you assist me in deciphering what rights the Lessee is giving up or putting aside according to section 4(c)? I'm finding this part particularly challenging to grasp." + ], + "criteria": [ + "(c) PROHIBITION AGAINST SETOFF, COUNTERCLAIM, ETC. This Lease\n is a net lease. Subject to Section 20(f), Lessee's obligation to pay all Rent\n hereunder shall be absolute and unconditional and shall not be affected or\n reduced by any circumstance, including, without limitation, (i) any setoff,\n counterclaim, recoupment, defense or other right which Lessee may have against\n Lessor, any Beneficiary, the Manufacturer, the Engine Manufacturer, any seller\n of or person providing services with respect ", + "to the Aircraft or any other\n Person, for any reason whatsoever; (ii) any defect in the title, airworthiness\n or eligibility for registration under applicable Law, or any condition, design,\n operation or fitness for use of, or any damage to or loss or destruction of,\n the Aircraft, or any interruption or cessation in the use or possession thereof\n by Lessee for any reason whatsoever, whether arising out of or related to an\n act or omission of Lessee, or any other Person; (iii) any Liens with res", + "pect to\n the Aircraft; (iv) the invalidity or unenforceability or lack of due\n authorization or other infirmity of this Lease or any absence of right, power\n or authority of Lessor or Lessee to enter into this Lease; (v) any insolvency,\n bankruptcy, reorganization or similar proceedings by or against Lessor or\n Lessee; (vi) any other circumstance or happening of any nature whatsoever,\n similar to any of the foregoing; or (vii) any Taxes (other Taxes to which\n Lessee's indemnity does not extend p", + "ursuant to the provisions of Section 10);\n it being the express intention of Lessor and Lessee that all Rent payable\n hereunder shall be payable in all events, unless the obligation to pay the same\n shall be terminated pursuant to the express provisions of this Lease. Nothing\n in this paragraph (c) shall constitute a waiver by Lessee of any right or claim\n that Lessee may separately assert against Lessor or any Beneficiary.\n\n Lessee hereby waives, to the extent permitted by app", + "licable\n Law, any and all rights which it may now have or which at any time hereafter\n may be conferred upon it, by Law or otherwise, to terminate this Lease or any\n obligation imposed upon Lessee hereunder or in relation hereto.\n\n If for any reason whatsoever this Lease shall be terminated\n in whole or in part by operation of law (other than termination under any\n bankruptcy laws as now or hereafter in effect), Lessee nonetheless agrees to\n pay to Lessor amounts equal to the R", + "ent payments hereunder at the time such\n payments would have become due and payable in accordance with the terms hereof\n had this Lease not been terminated so long as Lessee is able to use, possess\n and quietly enjoy the Aircraft, and so long as such payments are made and all\n other terms and conditions hereof are complied\n\n -16-\n\n\n\n\n\nwith by Lessor and Lessee, Lessor and Lessee will deem this Lease to remain in\nfull force and effect." + ] + }, + { + "question_variants": [ + "Can the Lessor and Beneficiaries conduct inspections without notice under certain conditions? What are those conditions?", + "Is it possible for the Lessor and Beneficiaries to carry out inspections without prior notice, given specific circumstances? If so, could you explain what these circumstances might be?", + "I'm finding myself a bit confused here. Can the Lessor and Beneficiaries, under any special conditions, perform inspections without providing any advance notice? If yes, what exactly are these special conditions?", + "I'm at my wit's end! Can the Lessor and Beneficiaries actually go ahead and conduct inspections without giving a heads up, but only when certain conditions are met? What exactly are these conditions that would allow for such actions?", + "I'm really trying to get my head around this, but I could use some assistance. Is it within the Lessor and Beneficiaries' rights to initiate inspections without any forewarning, but only under certain predefined circumstances? What are these circumstances exactly?" + ], + "criteria": [ + "Lessee shall permit Lessor, each Beneficiary and their\n respective designees on at least seven (7) days' prior written notice to visit\n and inspect the Aircraft, its condition, use and operation and the records\n maintained in connection therewith during normal business hours; PROVIDED,\n HOWEVER, that this shall not unreasonably interfere with Lessee's quiet use and\n enjoyment of the Aircraft PROVIDED FURTHER, HOWEVER, that Lessor or the\n Beneficiaries may conduct such visit and inspection at any", + " time and with or\n without notice if an Event of Default has occurred and is continuing." + ] + }, + { + "question_variants": [ + "What aircraft-related information will the Lessee provide on a monthly and annual basis?", + "Could you let me know what type of aircraft-related details the Lessee is obligated to provide on a monthly and annual basis?", + "I'm finding it a bit tricky to understand this part - could you help me clarify what specific aircraft-related data or information is the Lessee expected to report on both a monthly and an annual basis?", + "I'm really trying to grapple with this agreement. Could you assist me in figuring out the exact nature of the aircraft-related information that the Lessee is required to furnish on a consistent monthly and annual basis?", + "I'm genuinely struggling here! What does it mean exactly? What is the exact nature and extent of the aircraft-related data or information that the Lessee has to provide routinely, both on a monthly and an annual basis? I'm having a hard time understanding the specificities of this provision." + ], + "criteria": [ + "(v) Lessee will use its reasonable efforts to provide the\n Beneficiaries on or before the fifth day of each calendar month\n commencing with the next calendar month of the Delivery Date, and shall\n in any event provide to the Beneficiaries upon request of a Beneficiary,\n with a properly completed Monthly Aircraft Utilization and Status Report\n in the Form of Exhibit J hereto for the preceding calendar month\n operation of the aircraft;\n\n (vi) Lessee ", + "will use its reasonable efforts to provide the\n Beneficiaries, on or before the 15th day of January of each year\n (commencing with January 1996), and shall in any event provide Lessor and\n the Beneficiaries upon request of a Beneficiary in English, the\n information and documentation for the preceding calendar year as listed\n in Exhibit K hereto; PROVIDED, HOWEVER, that if (i) a Default or an Event\n of Default shall have occurred and be continuing or (ii) Lessee's\n ", + " financial condition changes adversely from its financial condition at the\n time of the Delivery Date, then, upon notice and a request from Lessor or\n a Beneficiary, Lessee shall provide such information on a quarterly basis\n on the 15th day of each January, April, July and October, commencing with\n the first of such dates to follow the date of such notice." + ] + }, + { + "question_variants": [ + "Under what conditions can Lessee consolidate, merge, or transfer assets without Lessor's prior written consent according to the text?", + "Could you explain under which specific circumstances the Lessee is allowed to consolidate, merge, or transfer assets without needing the Lessor's prior written approval, as stated in the text?", + "I'm having a bit of trouble with this section, could you clarify the exact conditions under which the Lessee is permitted to consolidate, merge, or transfer assets without first obtaining the Lessor's written consent, as outlined in the text?", + "I'm really wracking my brain here trying to understand the terms. Can you help me decipher under which exact circumstances or conditions the Lessee can execute consolidation, merging, or asset transfer without needing prior written consent from the Lessor, as the text suggests?", + "I'm pulling my hair out here! What on earth does it mean? What are the specific conditions or circumstances under which the Lessee can consolidate, merge, or transfer assets without having to acquire the Lessor's prior written consent, as it's described in the text? This is really a tough one to crack!" + ], + "criteria": [ + "(iv) CONSOLIDATION, MERGER, ETC. Without the prior written\n consent of Lessor and each Beneficiary, Lessee shall not consolidate with,\n merge with or merge into any other Person or convey, transfer or lease\n substantially all of its assets as an entirety to any other Person unless, upon\n and after giving effect to such transaction, (A) the surviving entity has at\n least the same net worth and gross assets as the Lessee immediately prior to\n such transaction, such surviving entity is Certified Ai", + "r Carrier and a \"citizen\n of the United States\" as defined in Section 101(16) of the Federal Aviation\n Act, (C) Lessor shall continue to be entitled to the benefits of Section 1110\n of the United States Bankruptcy Code, as in effect from time to time, and (D)\n each of the Operative Documents shall continue in full force and effect and\n shall constitute the legally binding and enforceable obligation of such\n surviving entity." + ] + }, + { + "question_variants": [ + "Who is responsible for replacing any parts on the Aircraft that become worn out, damaged, etc?", + "Could you please specify who holds the responsibility for replacing any parts of the Aircraft that may become worn out, damaged, or similarly affected?", + "I'm having a little trouble understanding this part. Who exactly is tasked with the responsibility of replacing any components of the Aircraft that may get worn out, damaged, or otherwise impaired?", + "I'm really scratching my head trying to figure out who precisely is designated to handle the replacement of any Aircraft parts that become worn out, damaged, or in similar conditions? This aspect seems a bit complicated.", + "I'm on the verge of losing it! Who in the world is charged with the duty of replacing any parts of the Aircraft that get worn out, damaged, or anything like that? I'm really finding it tough to get my head around this point." + ], + "criteria": [ + "(a) REPLACEMENT OF PARTS. Lessee, at its own cost and\n expense, will promptly replace all Parts which may from time to time become\n worn out, lost, stolen, destroyed, seized, confiscated, damaged beyond repair\n or permanently rendered unfit for use for any reason whatsoever. In addition,\n in the ordinary course of maintenance, service, repair, overhaul or testing,\n Lessee may at its own cost and expense cause to be removed any Parts, whether\n or not worn out, destroyed, damaged beyond repair or ", + "permanently rendered unfit\n for use, provided that Lessee shall replace at its own cost and expense such\n Parts as promptly as practicable. All replacement Parts shall be free and clear\n of all Liens, other than Liens permitted by Section 14 hereof, shall be in at\n least the same modification status and service bulletin accomplishment status,\n shall be fully interchangeable as to form, fit and function, shall have been\n overhauled or repaired and inspected by an agency acceptable to the FAA and\n", + " shall be in as good an operating condition as, and have a utility at least\n equal to and a value and remaining warranty reasonably approximating, the Parts\n replaced (assuming such replaced Parts were in the condition and repair in\n which they were required to be maintained by the terms hereof) and all\n historical records since new or last overhaul relating to such Parts (and all\n historical records since manufacture with respect to Engines, Landing Gears,\n the APU and all life limited parts in", + "stalled on any Engine, Landing Gear or\n APU) shall be maintained by Lessee." + ] + }, + { + "question_variants": [ + "Who bears responsibility if alterations, modifications or additions to the Aircraft result in any loss of revenue or grounding?", + "Can you clarify who would take responsibility if any alterations, modifications, or additions made to the Aircraft cause any loss of revenue or result in grounding?", + "I'm having some difficulty here. Could you please specify who should shoulder the responsibility if any changes, modifications or additions to the Aircraft lead to any form of revenue loss or cause the aircraft to be grounded?", + "I'm really trying to understand this, but it's complex. Could you elucidate who is to bear the brunt if alterations, modifications, or additions to the Aircraft culminate in a loss of revenue or result in the grounding of the aircraft?", + "I'm pulling my hair out over this! Who on earth would bear the responsibility if any alterations, modifications, or additions that are made to the Aircraft end up causing some form of revenue loss or force the aircraft to be grounded? I'm finding this part particularly challenging to comprehend." + ], + "criteria": [ + "In no event shall Lessor bear any liability or cost for any\n alteration, modification or addition to, or for any grounding or suspension of\n certification of, the Aircraft, or for any loss of revenue arising therefrom.\n Lessee shall make no material alterations, modifications or additions to the\n Aircraft (such as removal of seats, galleys, lavatories, major avionics\n equipment or the like) that would affect the marketability of the Aircraft\n without Lessor's and each Beneficiary's prior written", + " consent. if Lessor and\n each Beneficiary grant such consent, title to such removed Parts shall remain\n with Lessor and Lessor and the Beneficiaries may request Lessee to reinstall\n such Parts prior to termination of this Lease. If Lessor or Beneficiaries\n request Lessee to reinstall such Parts, title to the Parts removed shall vest\n in Lessee. All costs associated with such removal and reinstallation shall be\n borne by Lessee." + ] + }, + { + "question_variants": [ + "Who is the assignor and who is the assignee?", + "Can you help me identify who the assignor is and who takes the role of the assignee?", + "I'm having some trouble figuring this out. Could you clarify for me who exactly is the assignor and who is designated as the assignee in this context?", + "I'm really wrestling with this, it seems a bit tricky. Could you help me to understand who exactly is acting as the assignor and who is being recognized as the assignee in this particular scenario?", + "I'm at my wits' end here! What does it mean? Who exactly is playing the role of the assignor and who is being referred to as the assignee in this situation? This is proving to be quite a tough nut to crack!" + ], + "criteria": [ + "ASSIGNOR: ALOHA AIRLINES, INC.,\n A HAWAII CORPORATION", + "ASSIGNEE: ALOHA AIRLINES, INC., A\n DELAWARE CORPORATION" + ] + }, + { + "question_variants": [ + "What does it mean when the Assignee is referred to as a 'Certified Air Carrier'?", + "Could you clarify what is implied when the Assignee is labeled as a 'Certified Air Carrier'?", + "I'm having a hard time understanding this. Can you explain what the term 'Certified Air Carrier' means when it is applied to the Assignee in this context?", + "I'm really struggling here to understand this terminology. Could you assist in explaining what it means when the Assignee is characterized as a 'Certified Air Carrier' in this particular situation?", + "I'm almost at the end of my tether! What does this even mean? Can you help me grasp the meaning when the Assignee is designated as a 'Certified Air Carrier'? This particular terminology is really throwing me for a loop!" + ], + "criteria": [ + "(e) Assignee is a Certified Air Carrier and holds all\nlicenses, certificates, permits and franchises from the appropriate agencies of\nthe United States of America and/or all other governmental authorities having\njurisdiction which are necessary to authorize the Assignee to engage in air\ntransport and to carry on its business as presently conducted and to be\nconducted with the Aircraft." + ] + }, + { + "question_variants": [ + "Why is it important for the Assignee to be a 'citizen of the United States' as defined in 40102(a)(15) of Title 49 of the United States Code?", + "Could you help me understand why it's significant for the Assignee to be defined as a 'citizen of the United States' as per 40102(a)(15) of Title 49 of the United States Code?", + "I'm finding it a bit challenging to comprehend this part. Why is it crucial for the Assignee to be designated as a 'citizen of the United States', as defined under 40102(a)(15) of Title 49 of the United States Code?", + "I'm really trying to unravel this, but it seems quite complex. Could you elucidate why it's so imperative for the Assignee to be identified as a 'citizen of the United States', as per the definition provided in 40102(a)(15) of Title 49 of the United States Code?", + "I'm pulling my hair out over this! What does it even mean? Can you help me decipher why it's so essential for the Assignee to be considered a 'citizen of the United States', as stipulated in 40102(a)(15) of Title 49 of the United States Code? I'm finding this legal terminology particularly difficult to grasp." + ], + "criteria": [ + "(f) Assignee is a \"citizen of the United States\" as defined\nin 40102(a)(15) of Title 49 of the United States Code." + ] + }, + { + "question_variants": [ + "How many days do I have to pay?", + "Could you specify the number of days I'm given to complete the payment?", + "I'm a bit unsure about the payment deadline. Could you clarify how many days exactly I have to make the payment?", + "I'm really trying to understand the payment terms. Could you help me ascertain the exact number of days that I am allotted to finalize the payment?", + "I'm so confused! What does this mean exactly? Can you help me comprehend the specific amount of time, in days, that I have been provided with to conclude the payment? I'm finding this financial term quite challenging to understand." + ], + "criteria": [ + "(e) TIMING OF PAYMENT. Any amount due and payable to the\n relevant Indemnitee pursuant to this Section 10 will be paid within 10 days\n after receipt of a written demand therefor from such Indemnitee accompanied by\n a written statement describing in reasonable detail the basis for such\n indemnity and the computation of the amount so payable; PROVIDED, HOWEVER, that\n such amount need not be paid by Lessee prior to the later of (i) five days\n prior to the date the applicable Tax is payable to the a", + "ppropriate Governmental\n Entity or taxing authority or (ii) in the case of amounts which are being\n contested by Lessee in good faith or by Lessor pursuant to Section 10(f), the\n date such contest is finally resolved. If requested in writing by Lessee, and\n at Lessee's sole cost and expense, any calculations by an Indemnitee of any\n amount due and payable\n\n -44-\n\n\n\n\n\n hereunder shall be subject to review and verification by a firm of independent\n certif", + "ied public accounts of internationally recognized stature selected by\n such Indemnitee and reasonably acceptable to Lessee (such approval not to be\n unreasonably withheld or delayed). Such Indemnitee shall make available to such\n accounting firm such information as shall be necessary for purposes of such\n review and verification (but such information shall be held by such accounting\n firm in strictest confidence and shall not in any event be disclosed or made\n available to Lessee). If the result", + " of such review is that Lessee was liable\n for a smaller amount, the excess payment shall be returned by such Indemnitee\n forthwith." + ] + }, + { + "question_variants": [ + "What currency should I pay in?", + "Could you please clarify in which currency I am expected to make the payment?", + "I'm a bit puzzled here, could you specify the exact currency I should use for the payment?", + "I'm really scratching my head trying to figure this out. Could you help me understand in which specific currency I am supposed to settle the payment?", + "I'm quite frustrated at this point! What exactly does it mean? Can you elucidate in which particular currency I'm required to execute the payment? I'm finding this point a bit difficult to decipher." + ], + "criteria": [ + "(i) PAYMENTS IN U.S. DOLLARS. All amounts to be paid hereunder to\nLessor or Lessee shall be paid in Dollars, in immediately available funds.\nLessee acknowledges that the specification of Dollars in this transaction is\nof the essence and that Dollars shall be the currency of account in any and\nall events. The obligations of Lessee or Lessor hereunder, to Lessor or\nLessee, respectively, shall not be discharged by an amount paid in another\ncurrency, whether pursuant to a judgment or otherwise, to t", + "he extent that the\n amount so paid on prompt conversion to Dollars under normal banking\nprocedures does not yield the amount of Dollars owing to Lessor." + ] + }, + { + "question_variants": [ + "What is the US registration number of the aircraft?", + "Could you please tell me the US registration number assigned to the aircraft?", + "I'm having some difficulty here. Could you specify the exact US registration number of the aircraft?", + "I'm really struggling to get this part. Could you assist me in figuring out what the specific US registration number for the aircraft is?", + "I'm pulling my hair out over this! What does it mean exactly? Can you help me decipher the precise US registration number that's associated with the aircraft? I'm finding it a bit challenging to understand." + ], + "criteria": [ + "U.S.\n MODEL AND REGISTRATION MANUFACTURER'S\nITEM MANUFACTURER CONFIGURATION NUMBER SERIAL NUMBER\n-------------------------------------------------------------------------------------------------------------------\n \n\nAircraft The Boeing Compa", + "ny 737-25A N685MA*" + ] + }, + { + "question_variants": [ + "What is the maximum duration that a safety or maintenance requirement can remain unaddressed on the aircraft, particularly in terms of airworthiness directives and mandatory orders?", + "How long can a safety or maintenance requirement, especially airworthiness directives and mandatory orders, be left unresolved?", + "How long can an airworthiness directive or mandatory order remain outstanding on the aircraft according to standard lease agreements?", + "What's the longest period that a safety or maintenance requirement, such as airworthiness directives and mandatory orders, can remain unmet on a leased aircraft?", + "What is the maximum allowable timeframe for a safety or maintenance requirement to be left unattended to on an aircraft, specifically referring to airworthiness directives and mandatory orders?" + ], + "criteria": [ + "(i) have had all repetitive airworthiness directives and mandatory\n orders and regulations in at least half-life or better condition;" + ] + }, + { + "question_variants": [ + "What are the payment locations?", + "Could you specify where exactly I should be making the payments? Are there particular bank accounts or locations?", + "I'm a bit puzzled here. Could you clarify the exact payment locations or bank accounts where I'm supposed to deposit the payments?", + "I'm really struggling to grasp this. Could you assist me in understanding the specific payment locations or bank accounts where I'm expected to send the payments?", + "I'm at my wit's end here! What does this mean? Can you help me figure out the precise locations or bank accounts where I'm supposed to carry out the payments? I'm finding this financial aspect particularly hard to comprehend." + ], + "criteria": [ + "Payment Locations: For ITOCHU AirLease (Europe) Limited:\n\n Account Name: Citibank New York (ABA No.\n 021000089) for the account of Citibank\n Dublin (account no. 10994598) in favor of\n ITOCHU AirLease (Europe) Limited (account\n no. 1-00-6793-017)\n\n For Marubeni Airleasing (", + "U.K.) Limited:\n\n Harris Bank International Corporation\n ABA #026-007-760\n for the credit of\n The Mitsubishi Trust & Banking Corporation\n London Branch A/C#16011100\n UID No.107280\n for further credit to\n Marubeni Airleasi", + "ng (UK) Ltd.\n Account #020-404391\n\n With respect to payments by\n Lessee of Basic Rent hereunder, 62.682% of\n such amounts shall be paid to ITOCHU\n AirLease (Europe) Limited (as above\n provided) and 37.318% of such amounts\n shall be paid to Marubeni Airlea", + "sing (U.K.)\n Limited (as above provided)." + ] + }, + { + "question_variants": [ + "What is the revision number of the aircraft?", + "Could you please clarify what the revision number of the aircraft is?", + "I'm finding this a bit hard to grasp. Could you specify the exact revision number associated with the aircraft?", + "I'm really trying to understand this, but it's proving difficult. Could you assist me in determining the specific revision number that is attributed to the aircraft?", + "Agh! What does it even mean? Can you help me decipher the exact revision number that is tied to the aircraft? I'm finding this technical detail quite challenging to comprehend." + ], + "criteria": [ + "Detail Specification (737-25A-Rev. B)" + ] + } + ] + } +] \ No newline at end of file diff --git a/extensions/superboogav2/chat_handler.py b/extensions/superboogav2/chat_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..419b926451dd54689cbf0e958a8786d318eb45ec --- /dev/null +++ b/extensions/superboogav2/chat_handler.py @@ -0,0 +1,124 @@ +""" +This module is responsible for modifying the chat prompt and history. +""" +import re + +import extensions.superboogav2.parameters as parameters + +from modules import chat, shared +from modules.text_generation import get_encoded_length +from modules.logging_colors import logger +from modules.chat import load_character_memoized +from extensions.superboogav2.utils import create_context_text, create_metadata_source + +from .data_processor import process_and_add_to_collector +from .chromadb import ChromaCollector + + +CHAT_METADATA = create_metadata_source('automatic-chat-insert') + + +def _remove_tag_if_necessary(user_input: str): + if not parameters.get_is_manual(): + return user_input + + return re.sub(r'^\s*!c\s*|\s*!c\s*$', '', user_input) + + +def _should_query(input: str): + if not parameters.get_is_manual(): + return True + + if re.search(r'^\s*!c|!c\s*$', input, re.MULTILINE): + return True + + return False + + +def _format_single_exchange(name, text): + if re.search(r':\s*$', name): + return '{} {}\n'.format(name, text) + else: + return '{}: {}\n'.format(name, text) + + +def _get_names(state: dict): + default_char = shared.settings.get('character', "Assistant") + default_user = shared.settings.get('name1', "You") + character = state.get('character', default_char) + user_name = state.get('name1', default_user) + user_name, bot_name, _, _, _ = load_character_memoized(character, user_name, '') + + return user_name, bot_name + + +def _concatinate_history(history: dict, state: dict): + full_history_text = '' + user_name, bot_name = _get_names(state) + + # Grab the internal history. + internal_history = history['internal'] + assert isinstance(internal_history, list) + + # Iterate through the history. + for exchange in internal_history: + assert isinstance(exchange, list) + + if len(exchange) >= 1: + full_history_text += _format_single_exchange(user_name, exchange[0]) + if len(exchange) >= 2: + full_history_text += _format_single_exchange(bot_name, exchange[1]) + + return full_history_text[:-1] # Remove the last new line. + + +def _hijack_last(context_text: str, history: dict, max_len: int, state: dict): + num_context_tokens = get_encoded_length(context_text) + + names = _get_names(state)[::-1] + + history_tokens = 0 + replace_position = None + for i, messages in enumerate(reversed(history['internal'])): + for j, message in enumerate(reversed(messages)): + num_message_tokens = get_encoded_length(_format_single_exchange(names[j], message)) + + # TODO: This is an extremely naive solution. A more robust implementation must be made. + if history_tokens + num_context_tokens <= max_len: + # This message can be replaced + replace_position = (i, j) + + history_tokens += num_message_tokens + + if replace_position is None: + logger.warn("The provided context_text is too long to replace any message in the history.") + else: + # replace the message at replace_position with context_text + i, j = replace_position + history['internal'][-i-1][-j-1] = context_text + + +def custom_generate_chat_prompt_internal(user_input: str, state: dict, collector: ChromaCollector, **kwargs): + if parameters.get_add_chat_to_data(): + # Get the whole history as one string + history_as_text = _concatinate_history(kwargs['history'], state) + + if history_as_text: + # Delete all documents that were auto-inserted + collector.delete(ids_to_delete=None, where=CHAT_METADATA) + # Insert the processed history + process_and_add_to_collector(history_as_text, collector, False, CHAT_METADATA) + + if _should_query(user_input): + user_input = _remove_tag_if_necessary(user_input) + results = collector.get_sorted_by_dist(user_input, n_results=parameters.get_chunk_count(), max_token_count=int(parameters.get_max_token_count())) + + # Check if the strategy is to modify the last message. If so, prepend or append to the user query. + if parameters.get_injection_strategy() == parameters.APPEND_TO_LAST: + user_input = user_input + create_context_text(results) + elif parameters.get_injection_strategy() == parameters.PREPEND_TO_LAST: + user_input = create_context_text(results) + user_input + elif parameters.get_injection_strategy() == parameters.HIJACK_LAST_IN_CONTEXT: + _hijack_last(create_context_text(results), kwargs['history'], state['truncation_length'], state) + + return chat.generate_chat_prompt(user_input, state, **kwargs) diff --git a/extensions/superboogav2/chromadb.py b/extensions/superboogav2/chromadb.py new file mode 100644 index 0000000000000000000000000000000000000000..0da2d8f90c623b43ecd49b3dcf20919b8e2a1434 --- /dev/null +++ b/extensions/superboogav2/chromadb.py @@ -0,0 +1,376 @@ +import threading +import chromadb +import posthog +import torch +import math + +import numpy as np +import extensions.superboogav2.parameters as parameters + +from chromadb.config import Settings +from sentence_transformers import SentenceTransformer + +from modules.logging_colors import logger +from modules.text_generation import encode, decode + +logger.debug('Intercepting all calls to posthog.') +posthog.capture = lambda *args, **kwargs: None + + +class Collecter(): + def __init__(self): + pass + + def add(self, texts: list[str], texts_with_context: list[str], starting_indices: list[int]): + pass + + def get(self, search_strings: list[str], n_results: int) -> list[str]: + pass + + def clear(self): + pass + + +class Embedder(): + def __init__(self): + pass + + def embed(self, text: str) -> list[torch.Tensor]: + pass + +class Info: + def __init__(self, start_index, text_with_context, distance, id): + self.text_with_context = text_with_context + self.start_index = start_index + self.distance = distance + self.id = id + + def calculate_distance(self, other_info): + if parameters.get_new_dist_strategy() == parameters.DIST_MIN_STRATEGY: + # Min + return min(self.distance, other_info.distance) + elif parameters.get_new_dist_strategy() == parameters.DIST_HARMONIC_STRATEGY: + # Harmonic mean + return 2 * (self.distance * other_info.distance) / (self.distance + other_info.distance) + elif parameters.get_new_dist_strategy() == parameters.DIST_GEOMETRIC_STRATEGY: + # Geometric mean + return (self.distance * other_info.distance) ** 0.5 + elif parameters.get_new_dist_strategy() == parameters.DIST_ARITHMETIC_STRATEGY: + # Arithmetic mean + return (self.distance + other_info.distance) / 2 + else: # Min is default + return min(self.distance, other_info.distance) + + def merge_with(self, other_info): + s1 = self.text_with_context + s2 = other_info.text_with_context + s1_start = self.start_index + s2_start = other_info.start_index + + new_dist = self.calculate_distance(other_info) + + if self.should_merge(s1, s2, s1_start, s2_start): + if s1_start <= s2_start: + if s1_start + len(s1) >= s2_start + len(s2): # if s1 completely covers s2 + return Info(s1_start, s1, new_dist, self.id) + else: + overlap = max(0, s1_start + len(s1) - s2_start) + return Info(s1_start, s1 + s2[overlap:], new_dist, self.id) + else: + if s2_start + len(s2) >= s1_start + len(s1): # if s2 completely covers s1 + return Info(s2_start, s2, new_dist, other_info.id) + else: + overlap = max(0, s2_start + len(s2) - s1_start) + return Info(s2_start, s2 + s1[overlap:], new_dist, other_info.id) + + return None + + @staticmethod + def should_merge(s1, s2, s1_start, s2_start): + # Check if s1 and s2 are adjacent or overlapping + s1_end = s1_start + len(s1) + s2_end = s2_start + len(s2) + + return not (s1_end < s2_start or s2_end < s1_start) + +class ChromaCollector(Collecter): + def __init__(self, embedder: Embedder): + super().__init__() + self.chroma_client = chromadb.Client(Settings(anonymized_telemetry=False)) + self.embedder = embedder + self.collection = self.chroma_client.create_collection(name="context", embedding_function=self.embedder.embed) + self.ids = [] + self.id_to_info = {} + self.embeddings_cache = {} + self.lock = threading.Lock() # Locking so the server doesn't break. + + def add(self, texts: list[str], texts_with_context: list[str], starting_indices: list[int], metadatas: list[dict] = None): + with self.lock: + assert metadatas is None or len(metadatas) == len(texts), "metadatas must be None or have the same length as texts" + + if len(texts) == 0: + return + + new_ids = self._get_new_ids(len(texts)) + + (existing_texts, existing_embeddings, existing_ids, existing_metas), \ + (non_existing_texts, non_existing_ids, non_existing_metas) = self._split_texts_by_cache_hit(texts, new_ids, metadatas) + + # If there are any already existing texts, add them all at once. + if existing_texts: + logger.info(f'Adding {len(existing_embeddings)} cached embeddings.') + args = {'embeddings': existing_embeddings, 'documents': existing_texts, 'ids': existing_ids} + if metadatas is not None: + args['metadatas'] = existing_metas + self.collection.add(**args) + + # If there are any non-existing texts, compute their embeddings all at once. Each call to embed has significant overhead. + if non_existing_texts: + non_existing_embeddings = self.embedder.embed(non_existing_texts).tolist() + for text, embedding in zip(non_existing_texts, non_existing_embeddings): + self.embeddings_cache[text] = embedding + + logger.info(f'Adding {len(non_existing_embeddings)} new embeddings.') + args = {'embeddings': non_existing_embeddings, 'documents': non_existing_texts, 'ids': non_existing_ids} + if metadatas is not None: + args['metadatas'] = non_existing_metas + self.collection.add(**args) + + # Create a dictionary that maps each ID to its context and starting index + new_info = { + id_: {'text_with_context': context, 'start_index': start_index} + for id_, context, start_index in zip(new_ids, texts_with_context, starting_indices) + } + + self.id_to_info.update(new_info) + self.ids.extend(new_ids) + + + def _split_texts_by_cache_hit(self, texts: list[str], new_ids: list[str], metadatas: list[dict]): + existing_texts, non_existing_texts = [], [] + existing_embeddings = [] + existing_ids, non_existing_ids = [], [] + existing_metas, non_existing_metas = [], [] + + for i, text in enumerate(texts): + id_ = new_ids[i] + metadata = metadatas[i] if metadatas is not None else None + embedding = self.embeddings_cache.get(text) + if embedding: + existing_texts.append(text) + existing_embeddings.append(embedding) + existing_ids.append(id_) + existing_metas.append(metadata) + else: + non_existing_texts.append(text) + non_existing_ids.append(id_) + non_existing_metas.append(metadata) + + return (existing_texts, existing_embeddings, existing_ids, existing_metas), \ + (non_existing_texts, non_existing_ids, non_existing_metas) + + + def _get_new_ids(self, num_new_ids: int): + if self.ids: + max_existing_id = max(int(id_) for id_ in self.ids) + else: + max_existing_id = -1 + + return [str(i + max_existing_id + 1) for i in range(num_new_ids)] + + + def _find_min_max_start_index(self): + max_index, min_index = 0, float('inf') + for _, val in self.id_to_info.items(): + if val['start_index'] > max_index: + max_index = val['start_index'] + if val['start_index'] < min_index: + min_index = val['start_index'] + return min_index, max_index + + + # NB: Does not make sense to weigh excerpts from different documents. + # But let's say that's the user's problem. Perfect world scenario: + # Apply time weighing to different documents. For each document, then, add + # separate time weighing. + def _apply_sigmoid_time_weighing(self, infos: list[Info], document_len: int, time_steepness: float, time_power: float): + sigmoid = lambda x: 1 / (1 + np.exp(-x)) + + weights = sigmoid(time_steepness * np.linspace(-10, 10, document_len)) + + # Scale to [0,time_power] and shift it up to [1-time_power, 1] + weights = weights - min(weights) + weights = weights * (time_power / max(weights)) + weights = weights + (1 - time_power) + + # Reverse the weights + weights = weights[::-1] + + for info in infos: + index = info.start_index + info.distance *= weights[index] + + + def _filter_outliers_by_median_distance(self, infos: list[Info], significant_level: float): + # Ensure there are infos to filter + if not infos: + return [] + + # Find info with minimum distance + min_info = min(infos, key=lambda x: x.distance) + + # Calculate median distance among infos + median_distance = np.median([inf.distance for inf in infos]) + + # Filter out infos that have a distance significantly greater than the median + filtered_infos = [inf for inf in infos if inf.distance <= significant_level * median_distance] + + # Always include the info with minimum distance + if min_info not in filtered_infos: + filtered_infos.append(min_info) + + return filtered_infos + + + def _merge_infos(self, infos: list[Info]): + merged_infos = [] + current_info = infos[0] + + for next_info in infos[1:]: + merged = current_info.merge_with(next_info) + if merged is not None: + current_info = merged + else: + merged_infos.append(current_info) + current_info = next_info + + merged_infos.append(current_info) + return merged_infos + + + # Main function for retrieving chunks by distance. It performs merging, time weighing, and mean filtering. + def _get_documents_ids_distances(self, search_strings: list[str], n_results: int): + n_results = min(len(self.ids), n_results) + if n_results == 0: + return [], [], [] + + if isinstance(search_strings, str): + search_strings = [search_strings] + + infos = [] + min_start_index, max_start_index = self._find_min_max_start_index() + + for search_string in search_strings: + result = self.collection.query(query_texts=search_string, n_results=math.ceil(n_results / len(search_strings)), include=['distances']) + curr_infos = [Info(start_index=self.id_to_info[id]['start_index'], + text_with_context=self.id_to_info[id]['text_with_context'], + distance=distance, id=id) + for id, distance in zip(result['ids'][0], result['distances'][0])] + + self._apply_sigmoid_time_weighing(infos=curr_infos, document_len=max_start_index - min_start_index + 1, time_steepness=parameters.get_time_steepness(), time_power=parameters.get_time_power()) + curr_infos = self._filter_outliers_by_median_distance(curr_infos, parameters.get_significant_level()) + infos.extend(curr_infos) + + infos.sort(key=lambda x: x.start_index) + infos = self._merge_infos(infos) + + texts_with_context = [inf.text_with_context for inf in infos] + ids = [inf.id for inf in infos] + distances = [inf.distance for inf in infos] + + return texts_with_context, ids, distances + + + # Get chunks by similarity + def get(self, search_strings: list[str], n_results: int) -> list[str]: + with self.lock: + documents, _, _ = self._get_documents_ids_distances(search_strings, n_results) + return documents + + + # Get ids by similarity + def get_ids(self, search_strings: list[str], n_results: int) -> list[str]: + with self.lock: + _, ids, _ = self._get_documents_ids_distances(search_strings, n_results) + return ids + + + # Cutoff token count + def _get_documents_up_to_token_count(self, documents: list[str], max_token_count: int): + # TODO: Move to caller; We add delimiters there which might go over the limit. + current_token_count = 0 + return_documents = [] + + for doc in documents: + doc_tokens = encode(doc)[0] + doc_token_count = len(doc_tokens) + if current_token_count + doc_token_count > max_token_count: + # If adding this document would exceed the max token count, + # truncate the document to fit within the limit. + remaining_tokens = max_token_count - current_token_count + + truncated_doc = decode(doc_tokens[:remaining_tokens], skip_special_tokens=True) + return_documents.append(truncated_doc) + break + else: + return_documents.append(doc) + current_token_count += doc_token_count + + return return_documents + + + # Get chunks by similarity and then sort by ids + def get_sorted_by_ids(self, search_strings: list[str], n_results: int, max_token_count: int) -> list[str]: + with self.lock: + documents, ids, _ = self._get_documents_ids_distances(search_strings, n_results) + sorted_docs = [x for _, x in sorted(zip(ids, documents))] + + return self._get_documents_up_to_token_count(sorted_docs, max_token_count) + + + # Get chunks by similarity and then sort by distance (lowest distance is last). + def get_sorted_by_dist(self, search_strings: list[str], n_results: int, max_token_count: int) -> list[str]: + with self.lock: + documents, _, distances = self._get_documents_ids_distances(search_strings, n_results) + sorted_docs = [doc for doc, _ in sorted(zip(documents, distances), key=lambda x: x[1])] # sorted lowest -> highest + + # If a document is truncated or competely skipped, it would be with high distance. + return_documents = self._get_documents_up_to_token_count(sorted_docs, max_token_count) + return_documents.reverse() # highest -> lowest + + return return_documents + + + def delete(self, ids_to_delete: list[str], where: dict): + with self.lock: + ids_to_delete = self.collection.get(ids=ids_to_delete, where=where)['ids'] + self.collection.delete(ids=ids_to_delete, where=where) + + # Remove the deleted ids from self.ids and self.id_to_info + ids_set = set(ids_to_delete) + self.ids = [id_ for id_ in self.ids if id_ not in ids_set] + for id_ in ids_to_delete: + self.id_to_info.pop(id_, None) + + logger.info(f'Successfully deleted {len(ids_to_delete)} records from chromaDB.') + + + def clear(self): + with self.lock: + self.chroma_client.reset() + self.collection = self.chroma_client.create_collection("context", embedding_function=self.embedder.embed) + self.ids = [] + self.id_to_info = {} + + logger.info('Successfully cleared all records and reset chromaDB.') + + +class SentenceTransformerEmbedder(Embedder): + def __init__(self) -> None: + logger.debug('Creating Sentence Embedder...') + self.model = SentenceTransformer("sentence-transformers/all-mpnet-base-v2") + self.embed = self.model.encode + + +def make_collector(): + return ChromaCollector(SentenceTransformerEmbedder()) \ No newline at end of file diff --git a/extensions/superboogav2/config.json b/extensions/superboogav2/config.json new file mode 100644 index 0000000000000000000000000000000000000000..0f1034f52155f7ed45e98518da1ae00621e6d15b --- /dev/null +++ b/extensions/superboogav2/config.json @@ -0,0 +1,161 @@ +{ + "to_lower": { + "default": false, + "categories": [true, false], + "should_optimize": true + }, + "num_conversion": { + "default": null, + "categories": ["NUM_TO_WORD_METHOD", "NUM_TO_CHAR_METHOD", "NUM_TO_CHAR_LONG_METHOD", null], + "should_optimize": true + }, + "merge_spaces": { + "default": false, + "categories": [true, false], + "should_optimize": true + }, + "strip": { + "default": true, + "categories": [true, false], + "should_optimize": false + }, + "remove_punctuation": { + "default": true, + "categories": [true, false], + "should_optimize": true + }, + "remove_stopwords": { + "default": false, + "categories": [true, false], + "should_optimize": true + }, + "remove_specific_pos": { + "default": false, + "categories": [true, false], + "should_optimize": true + }, + "lemmatize": { + "default": true, + "categories": [true, false], + "should_optimize": true + }, + "min_num_sent": { + "default": 1, + "categories": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 999999], + "should_optimize": true + }, + "delta_start": { + "default": 0, + "categories": [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], + "should_optimize": true + }, + "chunk_len1": { + "default": 500, + "categories": [50, 200, 250, 500, 600, 900, 1000], + "should_optimize": true + }, + "chunk_len2": { + "default": 500, + "categories": [0, 50, 200, 250, 500, 600, 900], + "should_optimize": true + }, + "chunk_len3": { + "default": 1000, + "categories": [0, 100, 150, 300, 400, 700, 800, 1000], + "should_optimize": true + }, + "chunk_len4": { + "default": 700, + "categories": [0, 100, 150, 300, 400, 700, 800], + "should_optimize": true + }, + "chunk_len_mask": { + "default": 15, + "categories": [3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15], + "should_optimize": false + }, + "context_len_left": { + "default": 250, + "categories": [50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, 1000], + "should_optimize": true + }, + "context_len_right": { + "default": 800, + "categories": [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1500, 1600], + "should_optimize": true + }, + "new_dist_strategy": { + "default": "DIST_MIN_STRATEGY", + "categories": ["DIST_MIN_STRATEGY", "DIST_HARMONIC_STRATEGY", "DIST_GEOMETRIC_STRATEGY", "DIST_ARITHMETIC_STRATEGY"], + "should_optimize": false + }, + "chunk_count": { + "default": 250, + "categories": [30, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375, 400], + "should_optimize": true + }, + "min_num_length": { + "default": 9, + "categories": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "should_optimize": true + }, + "significant_level": { + "default": 1.0, + "categories": [0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 999999], + "should_optimize": true + }, + "time_steepness": { + "default": 0.01, + "categories": [0.01, 0.2, 0.4, 0.6, 0.8, 1.0], + "should_optimize": false + }, + "time_power": { + "default": 0, + "categories": [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], + "should_optimize": false + }, + "chunk_separator": { + "default": "" + }, + "prefix": { + "default": "<>\n\n" + }, + "data_separator": { + "default": "\n\n<>\n\n" + }, + "postfix": { + "default": "\n\n<>\n\n" + }, + "manual": { + "default": true + }, + "add_chat_to_data": { + "default": true + }, + "injection_strategy": { + "default": "PREPEND_TO_LAST", + "categories": ["PREPEND_TO_LAST", "APPEND_TO_LAST", "HIJACK_LAST_IN_CONTEXT"] + }, + "chunk_regex": { + "default": "(?<==== ).*?(?= ===)|User story: \\d+" + }, + "strong_cleanup": { + "default": false + }, + "max_token_count": { + "default": 3072 + }, + "threads": { + "default": 4 + }, + "optimization_steps": { + "default": 100 + }, + "api_port": { + "default": 5002 + }, + "api_on": { + "default": false + } + } + \ No newline at end of file diff --git a/extensions/superboogav2/data_preprocessor.py b/extensions/superboogav2/data_preprocessor.py new file mode 100644 index 0000000000000000000000000000000000000000..cbd14b6b0c9cfbbaedc2ebbcbe5883ed6253c5f9 --- /dev/null +++ b/extensions/superboogav2/data_preprocessor.py @@ -0,0 +1,199 @@ +""" +This module contains utils for preprocessing the text before converting it to embeddings. + +- TextPreprocessorBuilder preprocesses individual strings. + * lowering cases + * converting numbers to words or characters + * merging and stripping spaces + * removing punctuation + * removing stop words + * lemmatizing + * removing specific parts of speech (adverbs and interjections) +- TextSummarizer extracts the most important sentences from a long string using text-ranking. +""" +import pytextrank +import string +import spacy +import math +import nltk +import re + +from nltk.corpus import stopwords +from nltk.stem import WordNetLemmatizer +from num2words import num2words + + +class TextPreprocessorBuilder: + # Define class variables as None initially + _stop_words = set(stopwords.words('english')) + _lemmatizer = WordNetLemmatizer() + + # Some of the functions are expensive. We cache the results. + _lemmatizer_cache = {} + _pos_remove_cache = {} + + + def __init__(self, text: str): + self.text = text + + + def to_lower(self): + # Match both words and non-word characters + tokens = re.findall(r'\b\w+\b|\W+', self.text) + for i, token in enumerate(tokens): + # Check if token is a word + if re.match(r'^\w+$', token): + # Check if token is not an abbreviation or constant + if not re.match(r'^[A-Z]+$', token) and not re.match(r'^[A-Z_]+$', token): + tokens[i] = token.lower() + self.text = "".join(tokens) + return self + + + def num_to_word(self, min_len: int = 1): + # Match both words and non-word characters + tokens = re.findall(r'\b\w+\b|\W+', self.text) + for i, token in enumerate(tokens): + # Check if token is a number of length `min_len` or more + if token.isdigit() and len(token) >= min_len: + # This is done to pay better attention to numbers (e.g. ticket numbers, thread numbers, post numbers) + # 740700 will become "seven hundred and forty thousand seven hundred". + tokens[i] = num2words(int(token)).replace(",","") # Remove commas from num2words. + self.text = "".join(tokens) + return self + + + def num_to_char_long(self, min_len: int = 1): + # Match both words and non-word characters + tokens = re.findall(r'\b\w+\b|\W+', self.text) + for i, token in enumerate(tokens): + # Check if token is a number of length `min_len` or more + if token.isdigit() and len(token) >= min_len: + # This is done to pay better attention to numbers (e.g. ticket numbers, thread numbers, post numbers) + # 740700 will become HHHHHHEEEEEAAAAHHHAAA + convert_token = lambda token: ''.join((chr(int(digit) + 65) * (i + 1)) for i, digit in enumerate(token[::-1]))[::-1] + tokens[i] = convert_token(tokens[i]) + self.text = "".join(tokens) + return self + + def num_to_char(self, min_len: int = 1): + # Match both words and non-word characters + tokens = re.findall(r'\b\w+\b|\W+', self.text) + for i, token in enumerate(tokens): + # Check if token is a number of length `min_len` or more + if token.isdigit() and len(token) >= min_len: + # This is done to pay better attention to numbers (e.g. ticket numbers, thread numbers, post numbers) + # 740700 will become HEAHAA + tokens[i] = ''.join(chr(int(digit) + 65) for digit in token) + self.text = "".join(tokens) + return self + + def merge_spaces(self): + self.text = re.sub(' +', ' ', self.text) + return self + + def strip(self): + self.text = self.text.strip() + return self + + def remove_punctuation(self): + self.text = self.text.translate(str.maketrans('', '', string.punctuation)) + return self + + def remove_stopwords(self): + self.text = "".join([word for word in re.findall(r'\b\w+\b|\W+', self.text) if word not in TextPreprocessorBuilder._stop_words]) + return self + + def remove_specific_pos(self): + """ + In the English language, adverbs and interjections rarely provide meaningul information. + Removing them improves the embedding precision. Don't tell JK Rowling, though. + """ + processed_text = TextPreprocessorBuilder._pos_remove_cache.get(self.text) + if processed_text: + self.text = processed_text + return self + + # Match both words and non-word characters + tokens = re.findall(r'\b\w+\b|\W+', self.text) + + # Exclude adverbs and interjections + excluded_tags = ['RB', 'RBR', 'RBS', 'UH'] + + for i, token in enumerate(tokens): + # Check if token is a word + if re.match(r'^\w+$', token): + # Part-of-speech tag the word + pos = nltk.pos_tag([token])[0][1] + # If the word's POS tag is in the excluded list, remove the word + if pos in excluded_tags: + tokens[i] = '' + + new_text = "".join(tokens) + TextPreprocessorBuilder._pos_remove_cache[self.text] = new_text + self.text = new_text + + return self + + def lemmatize(self): + processed_text = TextPreprocessorBuilder._lemmatizer_cache.get(self.text) + if processed_text: + self.text = processed_text + return self + + new_text = "".join([TextPreprocessorBuilder._lemmatizer.lemmatize(word) for word in re.findall(r'\b\w+\b|\W+', self.text)]) + TextPreprocessorBuilder._lemmatizer_cache[self.text] = new_text + self.text = new_text + + return self + + def build(self): + return self.text + +class TextSummarizer: + _nlp_pipeline = None + _cache = {} + + @staticmethod + def _load_nlp_pipeline(): + # Lazy-load it. + if TextSummarizer._nlp_pipeline is None: + TextSummarizer._nlp_pipeline = spacy.load('en_core_web_sm') + TextSummarizer._nlp_pipeline.add_pipe("textrank", last=True) + return TextSummarizer._nlp_pipeline + + @staticmethod + def process_long_text(text: str, min_num_sent: int) -> list[str]: + """ + This function applies a text summarization process on a given text string, extracting + the most important sentences based on the principle that 20% of the content is responsible + for 80% of the meaning (the Pareto Principle). + + Returns: + list: A list of the most important sentences + """ + + # Attempt to get the result from cache + cache_key = (text, min_num_sent) + cached_result = TextSummarizer._cache.get(cache_key, None) + if cached_result is not None: + return cached_result + + nlp_pipeline = TextSummarizer._load_nlp_pipeline() + doc = nlp_pipeline(text) + + num_sent = len(list(doc.sents)) + result = [] + + if num_sent >= min_num_sent: + + limit_phrases = math.ceil(len(doc._.phrases) * 0.20) # 20% of the phrases, rounded up + limit_sentences = math.ceil(num_sent * 0.20) # 20% of the sentences, rounded up + result = [str(sent) for sent in doc._.textrank.summary(limit_phrases=limit_phrases, limit_sentences=limit_sentences)] + + else: + result = [text] + + # Store the result in cache before returning it + TextSummarizer._cache[cache_key] = result + return result \ No newline at end of file diff --git a/extensions/superboogav2/data_processor.py b/extensions/superboogav2/data_processor.py new file mode 100644 index 0000000000000000000000000000000000000000..f019f427fe43ae6169be835679a6d07e938a2753 --- /dev/null +++ b/extensions/superboogav2/data_processor.py @@ -0,0 +1,209 @@ +""" +This module is responsible for processing the corpus and feeding it into chromaDB. It will receive a corpus of text. +It will then split it into chunks of specified length. For each of those chunks, it will append surrounding context. +It will only include full words. +""" + +import re +import bisect + +import extensions.superboogav2.parameters as parameters + +from .data_preprocessor import TextPreprocessorBuilder, TextSummarizer +from .chromadb import ChromaCollector + +def preprocess_text_no_summary(text) -> str: + builder = TextPreprocessorBuilder(text) + if parameters.should_to_lower(): + builder.to_lower() + + if parameters.should_remove_punctuation(): + builder.remove_punctuation() + + if parameters.should_remove_specific_pos(): + builder.remove_specific_pos() + + if parameters.should_remove_stopwords(): + builder.remove_stopwords + + if parameters.should_lemmatize(): + builder.lemmatize() + + if parameters.should_merge_spaces(): + builder.merge_spaces + + if parameters.should_strip(): + builder.strip() + + if parameters.get_num_conversion_strategy(): + if parameters.get_num_conversion_strategy() == parameters.NUM_TO_WORD_METHOD: + builder.num_to_word(parameters.get_min_num_length()) + elif parameters.get_num_conversion_strategy() == parameters.NUM_TO_CHAR_METHOD: + builder.num_to_char(parameters.get_min_num_length()) + elif parameters.get_num_conversion_strategy() == parameters.NUM_TO_CHAR_LONG_METHOD: + builder.num_to_char_long(parameters.get_min_num_length()) + + return builder.build() + + +def preprocess_text(text) -> list[str]: + important_sentences = TextSummarizer.process_long_text(text, parameters.get_min_num_sentences()) + return [preprocess_text_no_summary(sent) for sent in important_sentences] + + +def _create_chunks_with_context(corpus, chunk_len, context_left, context_right): + """ + This function takes a corpus of text and splits it into chunks of a specified length, + then adds a specified amount of context to each chunk. The context is added by first + going backwards from the start of the chunk and then going forwards from the end of the + chunk, ensuring that the context includes only whole words and that the total context length + does not exceed the specified limit. This function uses binary search for efficiency. + + Returns: + chunks (list of str): The chunks of text. + chunks_with_context (list of str): The chunks of text with added context. + chunk_with_context_start_indices (list of int): The starting indices of each chunk with context in the corpus. + """ + words = re.split('(\\s+)', corpus) + word_start_indices = [0] + current_index = 0 + + for word in words: + current_index += len(word) + word_start_indices.append(current_index) + + chunks, chunk_lengths, chunk_start_indices, chunk_with_context_start_indices = [], [], [], [] + current_length = 0 + current_index = 0 + chunk = [] + + for word in words: + if current_length + len(word) > chunk_len: + chunks.append(''.join(chunk)) + chunk_lengths.append(current_length) + chunk_start_indices.append(current_index - current_length) + chunk = [word] + current_length = len(word) + else: + chunk.append(word) + current_length += len(word) + current_index += len(word) + + if chunk: + chunks.append(''.join(chunk)) + chunk_lengths.append(current_length) + chunk_start_indices.append(current_index - current_length) + + chunks_with_context = [] + for start_index, chunk_length in zip(chunk_start_indices, chunk_lengths): + context_start_index = bisect.bisect_right(word_start_indices, start_index - context_left) + context_end_index = bisect.bisect_left(word_start_indices, start_index + chunk_length + context_right) + + # Combine all the words in the context range (before, chunk, and after) + chunk_with_context = ''.join(words[context_start_index:context_end_index]) + chunks_with_context.append(chunk_with_context) + + # Determine the start index of the chunk with context + chunk_with_context_start_index = word_start_indices[context_start_index] + chunk_with_context_start_indices.append(chunk_with_context_start_index) + + return chunks, chunks_with_context, chunk_with_context_start_indices + + +def _clear_chunks(data_chunks, data_chunks_with_context, data_chunk_starting_indices): + distinct_data_chunks = [] + distinct_data_chunks_with_context = [] + distinct_data_chunk_starting_indices = [] + + seen_chunks = dict() + + for chunk, context, index in zip(data_chunks, data_chunks_with_context, data_chunk_starting_indices): + # Skip the chunk if it does not contain any alphanumeric characters + if not any(char.isalnum() for char in chunk): + continue + + seen_chunk_start = seen_chunks.get(chunk) + if seen_chunk_start: + # If we've already seen this exact chunk, and the context around it it very close to the seen chunk, then skip it. + if abs(seen_chunk_start-index) < parameters.get_delta_start(): + continue + + distinct_data_chunks.append(chunk) + distinct_data_chunks_with_context.append(context) + distinct_data_chunk_starting_indices.append(index) + + seen_chunks[chunk] = index + + return distinct_data_chunks, distinct_data_chunks_with_context, distinct_data_chunk_starting_indices + + +def process_and_add_to_collector(corpus: str, collector: ChromaCollector, clear_collector_before_adding: bool, metadata: dict): + # Defining variables + chunk_lens = [int(len.strip()) for len in parameters.get_chunk_len().split(',')] + context_len = [int(len.strip()) for len in parameters.get_context_len().split(',')] + if len(context_len) >= 3: + raise f"Context len has too many values: {len(context_len)}" + if len(context_len) == 2: + context_left = context_len[0] + context_right = context_len[1] + else: + context_left = context_right = context_len[0] + + data_chunks = [] + data_chunks_with_context = [] + data_chunk_starting_indices = [] + + # Handling chunk_regex + if parameters.get_chunk_regex(): + if parameters.get_chunk_separator(): + cumulative_length = 0 # This variable will store the length of the processed corpus + sections = corpus.split(parameters.get_chunk_separator()) + for section in sections: + special_chunks = list(re.finditer(parameters.get_chunk_regex(), section)) + for match in special_chunks: + chunk = match.group(0) + start_index = match.start() + end_index = start_index + len(chunk) + context = section[max(0, start_index - context_left):min(len(section), end_index + context_right)] + data_chunks.append(chunk) + data_chunks_with_context.append(context) + data_chunk_starting_indices.append(cumulative_length + max(0, start_index - context_left)) + cumulative_length += len(section) + len(parameters.get_chunk_separator()) # Update the length of the processed corpus + else: + special_chunks = list(re.finditer(parameters.get_chunk_regex(), corpus)) + for match in special_chunks: + chunk = match.group(0) + start_index = match.start() + end_index = start_index + len(chunk) + context = corpus[max(0, start_index - context_left):min(len(corpus), end_index + context_right)] + data_chunks.append(chunk) + data_chunks_with_context.append(context) + data_chunk_starting_indices.append(max(0, start_index - context_left)) + + for chunk_len in chunk_lens: + # Breaking the data into chunks and adding those to the db + if parameters.get_chunk_separator(): + cumulative_length = 0 # This variable will store the length of the processed corpus + sections = corpus.split(parameters.get_chunk_separator()) + for section in sections: + chunks, chunks_with_context, context_start_indices = _create_chunks_with_context(section, chunk_len, context_left, context_right) + context_start_indices = [cumulative_length + i for i in context_start_indices] # Add the length of the processed corpus to each start index + data_chunks.extend(chunks) + data_chunks_with_context.extend(chunks_with_context) + data_chunk_starting_indices.extend(context_start_indices) + cumulative_length += len(section) + len(parameters.get_chunk_separator()) # Update the length of the processed corpus + else: + chunks, chunks_with_context, context_start_indices = _create_chunks_with_context(corpus, chunk_len, context_left, context_right) + data_chunks.extend(chunks) + data_chunks_with_context.extend(chunks_with_context) + data_chunk_starting_indices.extend(context_start_indices) + + data_chunks = [preprocess_text_no_summary(chunk) for chunk in data_chunks] + + data_chunks, data_chunks_with_context, data_chunk_starting_indices = _clear_chunks( + data_chunks, data_chunks_with_context, data_chunk_starting_indices + ) + + if clear_collector_before_adding: + collector.clear() + collector.add(data_chunks, data_chunks_with_context, data_chunk_starting_indices, [metadata]*len(data_chunks) if metadata is not None else None) \ No newline at end of file diff --git a/extensions/superboogav2/download_urls.py b/extensions/superboogav2/download_urls.py new file mode 100644 index 0000000000000000000000000000000000000000..ad2726b563b6df1134fa8396175f4e597a82d628 --- /dev/null +++ b/extensions/superboogav2/download_urls.py @@ -0,0 +1,65 @@ +import concurrent.futures +import requests +import re + +from bs4 import BeautifulSoup + +import extensions.superboogav2.parameters as parameters + +from .data_processor import process_and_add_to_collector +from .utils import create_metadata_source + +def _download_single(url): + response = requests.get(url, timeout=5) + if response.status_code == 200: + return response.content + else: + raise Exception("Failed to download URL") + + +def _download_urls(urls, threads=1): + with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: + futures = [] + for url in urls: + future = executor.submit(_download_single, url) + futures.append(future) + + results = [] + i = 0 + for future in concurrent.futures.as_completed(futures): + try: + result = future.result() + results.append(result) + i += 1 + yield f"{i}/{len(urls)}", results + except Exception: + pass + + yield "Done", results + + +def feed_url_into_collector(urls, collector): + all_text = '' + cumulative = '' + + urls = urls.strip().split('\n') + cumulative += f'Loading {len(urls)} URLs with {parameters.get_num_threads()} threads...\n\n' + yield cumulative + for update, contents in _download_urls(urls, threads=parameters.get_num_threads()): + yield cumulative + update + + cumulative += 'Processing the HTML sources...' + yield cumulative + for content in contents: + soup = BeautifulSoup(content, features="lxml") + for script in soup(["script", "style"]): + script.extract() + + strings = soup.stripped_strings + if parameters.get_is_strong_cleanup(): + strings = [s for s in strings if re.search("[A-Za-z] ", s)] + + text = '\n'.join([s.strip() for s in strings]) + all_text += text + + process_and_add_to_collector(all_text, collector, False, create_metadata_source('url-download')) \ No newline at end of file diff --git a/extensions/superboogav2/nltk_data/corpora/stopwords/english b/extensions/superboogav2/nltk_data/corpora/stopwords/english new file mode 100644 index 0000000000000000000000000000000000000000..1280aa3b08d64b846272b2d23314377200db36e9 --- /dev/null +++ b/extensions/superboogav2/nltk_data/corpora/stopwords/english @@ -0,0 +1,179 @@ +i +me +my +myself +we +our +ours +ourselves +you +you're +you've +you'll +you'd +your +yours +yourself +yourselves +he +him +his +himself +she +she's +her +hers +herself +it +it's +its +itself +they +them +their +theirs +themselves +what +which +who +whom +this +that +that'll +these +those +am +is +are +was +were +be +been +being +have +has +had +having +do +does +did +doing +a +an +the +and +but +if +or +because +as +until +while +of +at +by +for +with +about +against +between +into +through +during +before +after +above +below +to +from +up +down +in +out +on +off +over +under +again +further +then +once +here +there +when +where +why +how +all +any +both +each +few +more +most +other +some +such +no +nor +not +only +own +same +so +than +too +very +s +t +can +will +just +don +don't +should +should've +now +d +ll +m +o +re +ve +y +ain +aren +aren't +couldn +couldn't +didn +didn't +doesn +doesn't +hadn +hadn't +hasn +hasn't +haven +haven't +isn +isn't +ma +mightn +mightn't +mustn +mustn't +needn +needn't +shan +shan't +shouldn +shouldn't +wasn +wasn't +weren +weren't +won +won't +wouldn +wouldn't diff --git a/extensions/superboogav2/nltk_data/corpora/wordnet.zip b/extensions/superboogav2/nltk_data/corpora/wordnet.zip new file mode 100644 index 0000000000000000000000000000000000000000..777df8872a4b420dc0324d7656f5b04270c54110 --- /dev/null +++ b/extensions/superboogav2/nltk_data/corpora/wordnet.zip @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbda5ea6eef7f36a97a43d4a75f85e07fccbb4f23657d27b4ccbc93e2646ab59 +size 10775600 diff --git a/extensions/superboogav2/nltk_data/taggers/averaged_perceptron_tagger/averaged_perceptron_tagger.pickle b/extensions/superboogav2/nltk_data/taggers/averaged_perceptron_tagger/averaged_perceptron_tagger.pickle new file mode 100644 index 0000000000000000000000000000000000000000..b8661868cc08eb0655fc01dccd8136cceeeeb02a --- /dev/null +++ b/extensions/superboogav2/nltk_data/taggers/averaged_perceptron_tagger/averaged_perceptron_tagger.pickle @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25a5a19c7ced7b2bac3831da5bc0afcc2c34e5dd01cd4f361bb799949a696238 +size 6138625 diff --git a/extensions/superboogav2/notebook_handler.py b/extensions/superboogav2/notebook_handler.py new file mode 100644 index 0000000000000000000000000000000000000000..7b8643496948ee4a4e6997b693404e00147f9eed --- /dev/null +++ b/extensions/superboogav2/notebook_handler.py @@ -0,0 +1,40 @@ +""" +This module is responsible for handling and modifying the notebook text. +""" +import re + +import extensions.superboogav2.parameters as parameters + +from modules import shared +from modules.logging_colors import logger +from extensions.superboogav2.utils import create_context_text + +from .data_processor import preprocess_text + +def _remove_special_tokens(string): + pattern = r'(<\|begin-user-input\|>|<\|end-user-input\|>|<\|injection-point\|>)' + return re.sub(pattern, '', string) + + +def input_modifier_internal(string, collector, is_chat): + # Sanity check. + if is_chat: + return string + + # Find the user input + pattern = re.compile(r"<\|begin-user-input\|>(.*?)<\|end-user-input\|>", re.DOTALL) + match = re.search(pattern, string) + if match: + # Preprocess the user prompt. + user_input = match.group(1).strip() + user_input = preprocess_text(user_input) + + logger.debug(f"Preprocessed User Input: {user_input}") + + # Get the most similar chunks + results = collector.get_sorted_by_dist(user_input, n_results=parameters.get_chunk_count(), max_token_count=int(parameters.get_max_token_count())) + + # Make the injection + string = string.replace('<|injection-point|>', create_context_text(results)) + + return _remove_special_tokens(string) \ No newline at end of file diff --git a/extensions/superboogav2/optimize.py b/extensions/superboogav2/optimize.py new file mode 100644 index 0000000000000000000000000000000000000000..acebf21248cd1bf2a769296e4cbe4c6645d46ef8 --- /dev/null +++ b/extensions/superboogav2/optimize.py @@ -0,0 +1,135 @@ +""" +This module implements a hyperparameter optimization routine for the embedding application. It utilizes TPE optimization from Optuna. + +Each run, the optimizer will set the default values inside the hyperparameters. At the end, it will output the best ones it has found. +""" +import re +import json +import optuna +import gradio as gr +import numpy as np +import logging +import hashlib +logging.getLogger('optuna').setLevel(logging.WARNING) + +import extensions.superboogav2.parameters as parameters + +from pathlib import Path + +from .benchmark import benchmark +from .parameters import Parameters +from modules.logging_colors import logger + + +# Format the parameters into markdown format. +def _markdown_hyperparams(): + res = [] + for param_name, param_value in Parameters.getInstance().hyperparameters.items(): + # Escape any markdown syntax + param_name = re.sub(r"([_*\[\]()~`>#+-.!])", r"\\\1", param_name) + param_value_default = re.sub(r"([_*\[\]()~`>#+-.!])", r"\\\1", str(param_value['default'])) if param_value['default'] else ' ' + + res.append('* {}: **{}**'.format(param_name, param_value_default)) + + return '\n'.join(res) + + +# Convert numpy types to python types. +def _convert_np_types(params): + for key in params: + if type(params[key]) == np.bool_: + params[key] = bool(params[key]) + elif type(params[key]) == np.int64: + params[key] = int(params[key]) + elif type(params[key]) == np.float64: + params[key] = float(params[key]) + return params + + +# Set the default values for the hyperparameters. +def _set_hyperparameters(params): + for param_name, param_value in params.items(): + if param_name in Parameters.getInstance().hyperparameters: + Parameters.getInstance().hyperparameters[param_name]['default'] = param_value + + +# Check if the parameter is for optimization. +def _is_optimization_param(val): + is_opt = val.get('should_optimize', False) # Either does not exist or is false + return is_opt + + +# Create a hashable representation of the parameters +def _get_params_hash(params): + params_str = json.dumps(params, sort_keys=True) + return hashlib.sha256(params_str.encode()).hexdigest() + + +def optimize(collector, progress=gr.Progress()): + # Inform the user that something is happening. + progress(0, desc=f'Setting Up...') + + # Track the current step + current_step = 0 + + # Track the best score + best_score = 0 + + # Dictionary for caching scores + scores_cache = {} + + def objective_function(trial): + nonlocal current_step + nonlocal best_score + nonlocal scores_cache + + params = {} + for key, val in Parameters.getInstance().hyperparameters.items(): + if _is_optimization_param(val): + params[key] = trial.suggest_categorical(key, val['categories']) + + _set_hyperparameters(params) + + params_hash = _get_params_hash(params) + + # If the score for these parameters is in the cache, return it + if params_hash in scores_cache: + return scores_cache[params_hash] + + # Benchmark the current set of parameters. + score, max_score = benchmark(Path("extensions/superboogav2/benchmark_texts/questions.json"), collector) + + # Cache the score + scores_cache[params_hash] = score + + result = json.dumps(_convert_np_types(params), indent=4) + result += f'\nScore: {score}/{max_score}' + + logger.debug(result) + + # Increment the current step + current_step += 1 + + # Update the best score + best_score = max(best_score, score) + + # Update the progress + progress(current_step / parameters.get_optimization_steps(), desc=f'Optimizing... {current_step}/{parameters.get_optimization_steps()}') + + return -score + + # Run the optimization. + study = optuna.create_study() + study.optimize(objective_function, n_trials=int(parameters.get_optimization_steps())) + + best_params = study.best_params + _set_hyperparameters(best_params) + + # Convert results to a markdown string. + str_result = f"## Best parameters:\n\n{_markdown_hyperparams()}\n\n## Score:\n\n{best_score}" + + # Save to JSON file + with open('best_params.json', 'w') as fp: + json.dump(_convert_np_types(best_params), fp, indent=4) + + return str_result \ No newline at end of file diff --git a/extensions/superboogav2/parameters.py b/extensions/superboogav2/parameters.py new file mode 100644 index 0000000000000000000000000000000000000000..1cada46a2399e6a71ca289bb7128f141e1a7e79e --- /dev/null +++ b/extensions/superboogav2/parameters.py @@ -0,0 +1,369 @@ +""" +This module provides a singleton class `Parameters` that is used to manage all hyperparameters for the embedding application. +It expects a JSON file in `extensions/superboogav2/config.json`. + +Each element in the JSON must have a `default` value which will be used for the current run. Elements can have `categories`. +These categories define the range in which the optimizer will search. If the element is tagged with `"should_optimize": false`, +then the optimizer will only ever use the default value. +""" +from pathlib import Path + +import json + +from modules.logging_colors import logger + + +NUM_TO_WORD_METHOD = 'Number to Word' +NUM_TO_CHAR_METHOD = 'Number to Char' +NUM_TO_CHAR_LONG_METHOD = 'Number to Multi-Char' + + +DIST_MIN_STRATEGY = 'Min of Two' +DIST_HARMONIC_STRATEGY = 'Harmonic Mean' +DIST_GEOMETRIC_STRATEGY = 'Geometric Mean' +DIST_ARITHMETIC_STRATEGY = 'Arithmetic Mean' + + +PREPEND_TO_LAST = 'Prepend to Last Message' +APPEND_TO_LAST = 'Append to Last Message' +HIJACK_LAST_IN_CONTEXT = 'Hijack Last Message in Context ⚠️ WIP ⚠️ (Works Partially)' + + +SORT_DISTANCE = 'distance' +SORT_ID = 'id' + + +class Parameters: + _instance = None + + variable_mapping = { + 'NUM_TO_WORD_METHOD': NUM_TO_WORD_METHOD, + 'NUM_TO_CHAR_METHOD': NUM_TO_CHAR_METHOD, + 'NUM_TO_CHAR_LONG_METHOD': NUM_TO_CHAR_LONG_METHOD, + 'DIST_MIN_STRATEGY': DIST_MIN_STRATEGY, + 'DIST_HARMONIC_STRATEGY': DIST_HARMONIC_STRATEGY, + 'DIST_GEOMETRIC_STRATEGY': DIST_GEOMETRIC_STRATEGY, + 'DIST_ARITHMETIC_STRATEGY': DIST_ARITHMETIC_STRATEGY, + 'PREPEND_TO_LAST': PREPEND_TO_LAST, + 'APPEND_TO_LAST': APPEND_TO_LAST, + 'HIJACK_LAST_IN_CONTEXT': HIJACK_LAST_IN_CONTEXT, + } + + @staticmethod + def getInstance(): + if Parameters._instance is None: + Parameters() + return Parameters._instance + + def __init__(self): + if Parameters._instance is not None: + raise Exception("This class is a singleton!") + else: + Parameters._instance = self + self.hyperparameters = self._load_from_json(Path("extensions/superboogav2/config.json")) + + def _load_from_json(self, file_path): + logger.debug('Loading hyperparameters...') + + with open(file_path, 'r') as file: + data = json.load(file) + + # Replace variable names in the dict and create Categorical objects + for key in data: + if "default" in data[key] and data[key]["default"] in self.variable_mapping: + data[key]["default"] = self.variable_mapping[data[key]["default"]] + if "categories" in data[key]: + data[key]["categories"] = [self.variable_mapping.get(cat, cat) for cat in data[key]["categories"]] + + return data + + +def should_to_lower() -> bool: + return bool(Parameters.getInstance().hyperparameters['to_lower']['default']) + + +def get_num_conversion_strategy() -> str: + return Parameters.getInstance().hyperparameters['num_conversion']['default'] + + +def should_merge_spaces() -> bool: + return bool(Parameters.getInstance().hyperparameters['merge_spaces']['default']) + + +def should_strip() -> bool: + return bool(Parameters.getInstance().hyperparameters['strip']['default']) + + +def should_remove_punctuation() -> bool: + return bool(Parameters.getInstance().hyperparameters['remove_punctuation']['default']) + + +def should_remove_stopwords() -> bool: + return bool(Parameters.getInstance().hyperparameters['remove_stopwords']['default']) + + +def should_remove_specific_pos() -> bool: + return bool(Parameters.getInstance().hyperparameters['remove_specific_pos']['default']) + + +def should_lemmatize() -> bool: + return bool(Parameters.getInstance().hyperparameters['lemmatize']['default']) + + +def get_min_num_sentences() -> int: + return int(Parameters.getInstance().hyperparameters['min_num_sent']['default']) + + +def get_delta_start() -> int: + return int(Parameters.getInstance().hyperparameters['delta_start']['default']) + + +def set_to_lower(value: bool): + Parameters.getInstance().hyperparameters['to_lower']['default'] = value + + +def set_num_conversion_strategy(value: str): + Parameters.getInstance().hyperparameters['num_conversion']['default'] = value + + +def set_merge_spaces(value: bool): + Parameters.getInstance().hyperparameters['merge_spaces']['default'] = value + + +def set_strip(value: bool): + Parameters.getInstance().hyperparameters['strip']['default'] = value + + +def set_remove_punctuation(value: bool): + Parameters.getInstance().hyperparameters['remove_punctuation']['default'] = value + + +def set_remove_stopwords(value: bool): + Parameters.getInstance().hyperparameters['remove_stopwords']['default'] = value + + +def set_remove_specific_pos(value: bool): + Parameters.getInstance().hyperparameters['remove_specific_pos']['default'] = value + + +def set_lemmatize(value: bool): + Parameters.getInstance().hyperparameters['lemmatize']['default'] = value + + +def set_min_num_sentences(value: int): + Parameters.getInstance().hyperparameters['min_num_sent']['default'] = value + + +def set_delta_start(value: int): + Parameters.getInstance().hyperparameters['delta_start']['default'] = value + + +def get_chunk_len() -> str: + lens = [] + mask = Parameters.getInstance().hyperparameters['chunk_len_mask']['default'] + + lens.append(Parameters.getInstance().hyperparameters['chunk_len1']['default'] if mask & (1 << 0) else None) + lens.append(Parameters.getInstance().hyperparameters['chunk_len2']['default'] if mask & (1 << 1) else None) + lens.append(Parameters.getInstance().hyperparameters['chunk_len3']['default'] if mask & (1 << 2) else None) + lens.append(Parameters.getInstance().hyperparameters['chunk_len4']['default'] if mask & (1 << 3) else None) + + return ','.join([str(len) for len in lens if len]) + + +def set_chunk_len(val: str): + chunk_lens = sorted([int(len.strip()) for len in val.split(',')]) + + # Reset the mask to zero + Parameters.getInstance().hyperparameters['chunk_len_mask']['default'] = 0 + + if len(chunk_lens) > 0: + Parameters.getInstance().hyperparameters['chunk_len1']['default'] = chunk_lens[0] + Parameters.getInstance().hyperparameters['chunk_len_mask']['default'] |= (1 << 0) + if len(chunk_lens) > 1: + Parameters.getInstance().hyperparameters['chunk_len2']['default'] = chunk_lens[1] + Parameters.getInstance().hyperparameters['chunk_len_mask']['default'] |= (1 << 1) + if len(chunk_lens) > 2: + Parameters.getInstance().hyperparameters['chunk_len3']['default'] = chunk_lens[2] + Parameters.getInstance().hyperparameters['chunk_len_mask']['default'] |= (1 << 2) + if len(chunk_lens) > 3: + Parameters.getInstance().hyperparameters['chunk_len4']['default'] = chunk_lens[3] + Parameters.getInstance().hyperparameters['chunk_len_mask']['default'] |= (1 << 3) + + if len(chunk_lens) > 4: + logger.warning(f'Only up to four chunk lengths are supported. Skipping {chunk_lens[4:]}') + + +def get_context_len() -> str: + context_len = str(Parameters.getInstance().hyperparameters['context_len_left']['default']) + ',' + str(Parameters.getInstance().hyperparameters['context_len_right']['default']) + return context_len + + +def set_context_len(val: str): + context_lens = [int(len.strip()) for len in val.split(',') if len.isdigit()] + if len(context_lens) == 1: + Parameters.getInstance().hyperparameters['context_len_left']['default'] = Parameters.getInstance().hyperparameters['context_len_right']['default'] = context_lens[0] + elif len(context_lens) == 2: + Parameters.getInstance().hyperparameters['context_len_left']['default'] = context_lens[0] + Parameters.getInstance().hyperparameters['context_len_right']['default'] = context_lens[1] + else: + logger.warning(f'Incorrect context length received {val}. Skipping.') + + +def get_new_dist_strategy() -> str: + return Parameters.getInstance().hyperparameters['new_dist_strategy']['default'] + + +def get_chunk_count() -> int: + return int(Parameters.getInstance().hyperparameters['chunk_count']['default']) + + +def get_min_num_length() -> int: + return int(Parameters.getInstance().hyperparameters['min_num_length']['default']) + + +def get_significant_level() -> float: + return float(Parameters.getInstance().hyperparameters['significant_level']['default']) + + +def get_time_steepness() -> float: + return float(Parameters.getInstance().hyperparameters['time_steepness']['default']) + + +def get_time_power() -> float: + return float(Parameters.getInstance().hyperparameters['time_power']['default']) + + +def get_chunk_separator() -> str: + return Parameters.getInstance().hyperparameters['chunk_separator']['default'] + + +def get_prefix() -> str: + return Parameters.getInstance().hyperparameters['prefix']['default'] + + +def get_data_separator() -> str: + return Parameters.getInstance().hyperparameters['data_separator']['default'] + + +def get_postfix() -> str: + return Parameters.getInstance().hyperparameters['postfix']['default'] + + +def get_is_manual() -> bool: + return bool(Parameters.getInstance().hyperparameters['manual']['default']) + + +def get_add_chat_to_data() -> bool: + return bool(Parameters.getInstance().hyperparameters['add_chat_to_data']['default']) + + +def get_injection_strategy() -> str: + return Parameters.getInstance().hyperparameters['injection_strategy']['default'] + + +def get_chunk_regex() -> str: + return Parameters.getInstance().hyperparameters['chunk_regex']['default'] + + +def get_is_strong_cleanup() -> bool: + return bool(Parameters.getInstance().hyperparameters['strong_cleanup']['default']) + + +def get_max_token_count() -> int: + return int(Parameters.getInstance().hyperparameters['max_token_count']['default']) + + +def get_num_threads() -> int: + return int(Parameters.getInstance().hyperparameters['threads']['default']) + + +def get_optimization_steps() -> int: + return int(Parameters.getInstance().hyperparameters['optimization_steps']['default']) + + +def get_api_port() -> int: + return int(Parameters.getInstance().hyperparameters['api_port']['default']) + + +def get_api_on() -> bool: + return bool(Parameters.getInstance().hyperparameters['api_on']['default']) + + +def set_new_dist_strategy(value: str): + Parameters.getInstance().hyperparameters['new_dist_strategy']['default'] = value + + +def set_chunk_count(value: int): + Parameters.getInstance().hyperparameters['chunk_count']['default'] = value + + +def set_min_num_length(value: int): + Parameters.getInstance().hyperparameters['min_num_length']['default'] = value + + +def set_significant_level(value: float): + Parameters.getInstance().hyperparameters['significant_level']['default'] = value + + +def set_time_steepness(value: float): + Parameters.getInstance().hyperparameters['time_steepness']['default'] = value + + +def set_time_power(value: float): + Parameters.getInstance().hyperparameters['time_power']['default'] = value + + +def set_chunk_separator(value: str): + Parameters.getInstance().hyperparameters['chunk_separator']['default'] = value + + +def set_prefix(value: str): + Parameters.getInstance().hyperparameters['prefix']['default'] = value + + +def set_data_separator(value: str): + Parameters.getInstance().hyperparameters['data_separator']['default'] = value + + +def set_postfix(value: str): + Parameters.getInstance().hyperparameters['postfix']['default'] = value + + +def set_manual(value: bool): + Parameters.getInstance().hyperparameters['manual']['default'] = value + + +def set_add_chat_to_data(value: bool): + Parameters.getInstance().hyperparameters['add_chat_to_data']['default'] = value + + +def set_injection_strategy(value: str): + Parameters.getInstance().hyperparameters['injection_strategy']['default'] = value + + +def set_chunk_regex(value: str): + Parameters.getInstance().hyperparameters['chunk_regex']['default'] = value + + +def set_strong_cleanup(value: bool): + Parameters.getInstance().hyperparameters['strong_cleanup']['default'] = value + + +def set_max_token_count(value: int): + Parameters.getInstance().hyperparameters['max_token_count']['default'] = value + + +def set_num_threads(value: int): + Parameters.getInstance().hyperparameters['threads']['default'] = value + + +def set_optimization_steps(value: int): + Parameters.getInstance().hyperparameters['optimization_steps']['default'] = value + + +def set_api_port(value: int): + Parameters.getInstance().hyperparameters['api_port']['default'] = value + + +def set_api_on(value: bool): + Parameters.getInstance().hyperparameters['api_on']['default'] = value \ No newline at end of file diff --git a/extensions/superboogav2/requirements.txt b/extensions/superboogav2/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..748bacf1ab8893b430ef7db9b5bef70dc4c0b94b --- /dev/null +++ b/extensions/superboogav2/requirements.txt @@ -0,0 +1,10 @@ +beautifulsoup4==4.12.2 +chromadb==0.3.18 +lxml +optuna +pandas==2.0.3 +posthog==2.4.2 +sentence_transformers==2.2.2 +spacy +pytextrank +num2words \ No newline at end of file diff --git a/extensions/superboogav2/script.py b/extensions/superboogav2/script.py new file mode 100644 index 0000000000000000000000000000000000000000..66f56e29ea0382e4b85b9dbe38e41c822b57df30 --- /dev/null +++ b/extensions/superboogav2/script.py @@ -0,0 +1,355 @@ +""" +This file is responsible for the UI and how the application interracts with the rest of the system. +""" +import os +from pathlib import Path + +# Point to where nltk will find the required data. +os.environ['NLTK_DATA'] = str(Path("extensions/superboogav2/nltk_data").resolve()) + +import textwrap +import codecs +import gradio as gr + +import extensions.superboogav2.parameters as parameters + +from modules.logging_colors import logger +from modules import shared + +from .utils import create_metadata_source +from .chromadb import make_collector +from .download_urls import feed_url_into_collector +from .data_processor import process_and_add_to_collector +from .benchmark import benchmark +from .optimize import optimize +from .notebook_handler import input_modifier_internal +from .chat_handler import custom_generate_chat_prompt_internal +from .api import APIManager + +collector = None +api_manager = None + +def setup(): + global collector + global api_manager + collector = make_collector() + api_manager = APIManager(collector) + + if parameters.get_api_on(): + api_manager.start_server(parameters.get_api_port()) + +def _feed_data_into_collector(corpus): + yield '### Processing data...' + process_and_add_to_collector(corpus, collector, False, create_metadata_source('direct-text')) + yield '### Done.' + + +def _feed_file_into_collector(file): + yield '### Reading and processing the input dataset...' + text = file.decode('utf-8') + process_and_add_to_collector(text, collector, False, create_metadata_source('file')) + yield '### Done.' + + +def _feed_url_into_collector(urls): + for i in feed_url_into_collector(urls, collector): + yield i + yield '### Done.' + + +def _begin_benchmark(): + score, max_score = benchmark(Path("extensions/superboogav2/benchmark_texts/questions.json"), collector) + return f'**Score**: {score}/{max_score}' + + +def _begin_optimization(progress=gr.Progress()): + return optimize(collector, progress), *_get_optimizable_settings() + + +def _clear_data(): + collector.clear() + return "### Data Cleared!" + + +def _get_optimizable_settings() -> list: + preprocess_pipeline = [] + if parameters.should_to_lower(): + preprocess_pipeline.append('Lower Cases') + if parameters.should_remove_punctuation(): + preprocess_pipeline.append('Remove Punctuation') + if parameters.should_remove_specific_pos(): + preprocess_pipeline.append('Remove Adverbs') + if parameters.should_remove_stopwords(): + preprocess_pipeline.append('Remove Stop Words') + if parameters.should_lemmatize(): + preprocess_pipeline.append('Lemmatize') + if parameters.should_merge_spaces(): + preprocess_pipeline.append('Merge Spaces') + if parameters.should_strip(): + preprocess_pipeline.append('Strip Edges') + + return [ + parameters.get_time_power(), + parameters.get_time_steepness(), + parameters.get_significant_level(), + parameters.get_min_num_sentences(), + parameters.get_new_dist_strategy(), + parameters.get_delta_start(), + parameters.get_min_num_length(), + parameters.get_num_conversion_strategy(), + preprocess_pipeline, + parameters.get_chunk_count(), + parameters.get_context_len(), + parameters.get_chunk_len() + ] + + +def _apply_settings(optimization_steps, time_power, time_steepness, significant_level, min_sentences, new_dist_strat, delta_start, min_number_length, num_conversion, + preprocess_pipeline, api_port, api_on, injection_strategy, add_chat_to_data, manual, postfix, data_separator, prefix, max_token_count, + chunk_count, chunk_sep, context_len, chunk_regex, chunk_len, threads, strong_cleanup): + logger.debug('Applying settings.') + + try: + parameters.set_optimization_steps(optimization_steps) + parameters.set_significant_level(significant_level) + parameters.set_min_num_sentences(min_sentences) + parameters.set_new_dist_strategy(new_dist_strat) + parameters.set_delta_start(delta_start) + parameters.set_min_num_length(min_number_length) + parameters.set_num_conversion_strategy(num_conversion) + parameters.set_api_port(api_port) + parameters.set_api_on(api_on) + parameters.set_injection_strategy(injection_strategy) + parameters.set_add_chat_to_data(add_chat_to_data) + parameters.set_manual(manual) + parameters.set_postfix(codecs.decode(postfix, 'unicode_escape')) + parameters.set_data_separator(codecs.decode(data_separator, 'unicode_escape')) + parameters.set_prefix(codecs.decode(prefix, 'unicode_escape')) + parameters.set_max_token_count(max_token_count) + parameters.set_time_power(time_power) + parameters.set_time_steepness(time_steepness) + parameters.set_chunk_count(chunk_count) + parameters.set_chunk_separator(codecs.decode(chunk_sep, 'unicode_escape')) + parameters.set_context_len(context_len) + parameters.set_chunk_regex(chunk_regex) + parameters.set_chunk_len(chunk_len) + parameters.set_num_threads(threads) + parameters.set_strong_cleanup(strong_cleanup) + + preprocess_choices = ['Lower Cases', 'Remove Punctuation', 'Remove Adverbs', 'Remove Stop Words', 'Lemmatize', 'Merge Spaces', 'Strip Edges'] + for preprocess_method in preprocess_choices: + if preprocess_method == 'Lower Cases': + parameters.set_to_lower(preprocess_method in preprocess_pipeline) + elif preprocess_method == 'Remove Punctuation': + parameters.set_remove_punctuation(preprocess_method in preprocess_pipeline) + elif preprocess_method == 'Remove Adverbs': + parameters.set_remove_specific_pos(preprocess_method in preprocess_pipeline) + elif preprocess_method == 'Remove Stop Words': + parameters.set_remove_stopwords(preprocess_method in preprocess_pipeline) + elif preprocess_method == 'Lemmatize': + parameters.set_lemmatize(preprocess_method in preprocess_pipeline) + elif preprocess_method == 'Merge Spaces': + parameters.set_merge_spaces(preprocess_method in preprocess_pipeline) + elif preprocess_method == 'Strip Edges': + parameters.set_strip(preprocess_method in preprocess_pipeline) + + # Based on API on/off, start or stop the server + if api_manager is not None: + if parameters.get_api_on() and (not api_manager.is_server_running()): + api_manager.start_server(parameters.get_api_port()) + elif (not parameters.get_api_on()) and api_manager.is_server_running(): + api_manager.stop_server() + except Exception as e: + logger.warn(f'Could not properly apply settings: {str(e)}') + + +def custom_generate_chat_prompt(user_input, state, **kwargs): + return custom_generate_chat_prompt_internal(user_input, state, collector, **kwargs) + + +def input_modifier(string, state, is_chat=False): + return input_modifier_internal(string, collector, is_chat) + + +def ui(): + with gr.Accordion("Click for more information...", open=False): + gr.Markdown(textwrap.dedent(""" + + ## About + + This extension takes a dataset as input, breaks it into chunks, and adds the result to a local/offline Chroma database. + + The database is then queried during inference time to get the excerpts that are closest to your input. The idea is to create an arbitrarily large pseudo context. + + The core methodology was developed and contributed by kaiokendev, who is working on improvements to the method in this repository: https://github.com/kaiokendev/superbig + + ## Data input + + Start by entering some data in the interface below and then clicking on "Load data". + + Each time you load some new data, the old chunks are discarded. + + ## Chat mode + + #### Instruct + + On each turn, the chunks will be compared to your current input and the most relevant matches will be appended to the input in the following format: + + ``` + Consider the excerpts below as additional context: + ... + ``` + + The injection doesn't make it into the chat history. It is only used in the current generation. + + #### Regular chat + + The chunks from the external data sources are ignored, and the chroma database is built based on the chat history instead. The most relevant past exchanges relative to the present input are added to the context string. This way, the extension acts as a long term memory. + + ## Notebook/default modes + + Your question must be manually specified between `<|begin-user-input|>` and `<|end-user-input|>` tags, and the injection point must be specified with `<|injection-point|>`. + + The special tokens mentioned above (`<|begin-user-input|>`, `<|end-user-input|>`, and `<|injection-point|>`) are removed in the background before the text generation begins. + + Here is an example in Vicuna 1.1 format: + + ``` + A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions. + + USER: + <|injection-point|> + + <|begin-user-input|>What datasets are mentioned in the text above?<|end-user-input|> + ASSISTANT: + ``` + """)) + + with gr.Row(): + with gr.Column(min_width=600): + with gr.Tab("Text input"): + data_input = gr.Textbox(lines=20, label='Input data') + update_data = gr.Button('Load data') + + with gr.Tab("URL input"): + url_input = gr.Textbox(lines=10, label='Input URLs', info='Enter one or more URLs separated by newline characters.') + strong_cleanup = gr.Checkbox(value=parameters.get_is_strong_cleanup(), label='Strong cleanup', info='Only keeps html elements that look like long-form text.') + threads = gr.Number(value=parameters.get_num_threads(), label='Threads', info='The number of threads to use while downloading the URLs.', precision=0) + update_url = gr.Button('Load data') + + with gr.Tab("File input"): + file_input = gr.File(label='Input file', type='binary') + update_file = gr.Button('Load data') + + with gr.Tab("Settings"): + with gr.Accordion("Processing settings", open=True): + chunk_len = gr.Textbox(value=parameters.get_chunk_len(), label='Chunk length', info='In characters, not tokens. This value is used when you click on "Load data".') + chunk_regex = gr.Textbox(value=parameters.get_chunk_regex(), label='Chunk regex', info='Will specifically add the captured text to the embeddings.') + context_len = gr.Textbox(value=parameters.get_context_len(), label='Context length', info='In characters, not tokens. How much context to load around each chunk.') + chunk_sep = gr.Textbox(value=codecs.encode(parameters.get_chunk_separator(), 'unicode_escape').decode(), label='Chunk separator', info='Used to manually split chunks. Manually split chunks longer than chunk length are split again. This value is used when you click on "Load data".') + + with gr.Accordion("Generation settings", open=False): + chunk_count = gr.Number(value=parameters.get_chunk_count(), label='Chunk count', info='The number of closest-matching chunks to include in the prompt.') + max_token_count = gr.Number(value=parameters.get_max_token_count(), label='Max Context Tokens', info='The context length in tokens will not exceed this value.') + prefix = gr.Textbox(value=codecs.encode(parameters.get_prefix(), 'unicode_escape').decode(), label='Prefix', info='What to put before the injection point.') + data_separator = gr.Textbox(value=codecs.encode(parameters.get_data_separator(), 'unicode_escape').decode(), label='Data separator', info='When multiple pieces of distant data are added, they might be unrelated. It\'s important to separate them.') + postfix = gr.Textbox(value=codecs.encode(parameters.get_postfix(), 'unicode_escape').decode(), label='Postfix', info='What to put after the injection point.') + with gr.Row(): + manual = gr.Checkbox(value=parameters.get_is_manual(), label="Is Manual", info="Manually specify when to use ChromaDB. Insert `!c` at the start or end of the message to trigger a query.", visible=shared.is_chat()) + add_chat_to_data = gr.Checkbox(value=parameters.get_add_chat_to_data(), label="Add Chat to Data", info="Automatically feed the chat history as you chat.", visible=shared.is_chat()) + injection_strategy = gr.Radio(choices=[parameters.PREPEND_TO_LAST, parameters.APPEND_TO_LAST, parameters.HIJACK_LAST_IN_CONTEXT], value=parameters.get_injection_strategy(), label='Injection Strategy', info='Where to inject the messages in chat or instruct mode.', visible=shared.is_chat()) + with gr.Row(): + api_on = gr.Checkbox(value=parameters.get_api_on(), label="Turn on API", info="Check this to turn on the API service.") + api_port = gr.Number(value=parameters.get_api_port(), label="API Port", info="The port on which the API service will run.") + + with gr.Accordion("Advanced settings", open=False): + preprocess_set_choices = [] + if parameters.should_to_lower(): + preprocess_set_choices.append('Lower Cases') + if parameters.should_remove_punctuation(): + preprocess_set_choices.append('Remove Punctuation') + if parameters.should_remove_specific_pos(): + preprocess_set_choices.append('Remove Adverbs') + if parameters.should_remove_stopwords(): + preprocess_set_choices.append('Remove Stop Words') + if parameters.should_lemmatize(): + preprocess_set_choices.append('Lemmatize') + if parameters.should_merge_spaces(): + preprocess_set_choices.append('Merge Spaces') + if parameters.should_strip(): + preprocess_set_choices.append('Strip Edges') + + preprocess_pipeline = gr.CheckboxGroup(label='Preprocessing pipeline', choices=[ + 'Lower Cases', + 'Remove Punctuation', + 'Remove Adverbs', + 'Remove Stop Words', + 'Lemmatize', + 'Merge Spaces', + 'Strip Edges', + ], value=preprocess_set_choices, interactive=True, info='How to preprocess the text before it is turned into an embedding.') + + with gr.Row(): + num_conversion = gr.Dropdown(choices=[parameters.NUM_TO_WORD_METHOD, parameters.NUM_TO_CHAR_METHOD, parameters.NUM_TO_CHAR_LONG_METHOD, 'None'], value=parameters.get_num_conversion_strategy(), label="Number Conversion Method", info='How to preprocess numbers before creating the embeddings.', interactive=True) + min_number_length = gr.Number(value=parameters.get_min_num_length(), label='Number Length Threshold', info='In digits. Only numbers that have at least that many digits will be converted.', interactive=True) + + delta_start = gr.Number(value=parameters.get_delta_start(), label='Delta Start Index', info='If the system encounters two identical embeddings, and they both start within the same delta, then only the first will be considered.', interactive=True) + new_dist_strat = gr.Dropdown(choices=[parameters.DIST_MIN_STRATEGY, parameters.DIST_HARMONIC_STRATEGY, parameters.DIST_GEOMETRIC_STRATEGY, parameters.DIST_ARITHMETIC_STRATEGY], value=parameters.get_new_dist_strategy(), label="Distance Strategy", info='When two embedding texts are merged, the distance of the new piece will be decided using one of these strategies.', interactive=True) + min_sentences = gr.Number(value=parameters.get_min_num_sentences(), label='Summary Threshold', info='In sentences. The minumum number of sentences to trigger text-rank summarization.', interactive=True) + significant_level = gr.Slider(0.8, 2, value=parameters.get_significant_level(), label='Significant Level', info='Defines the cut-off for what is considered a "significant" distance relative to the median distance among the returned samples.', interactive=True) + time_steepness = gr.Slider(0.01, 1.0, value=parameters.get_time_steepness(), label='Time Weighing Steepness', info='How differently two close excerpts are going to be weighed.') + time_power = gr.Slider(0.0, 1.0, value=parameters.get_time_power(), label='Time Weighing Power', info='How influencial is the weighing. At 1.0, old entries won\'t be considered') + + with gr.Tab("Benchmark"): + benchmark_button = gr.Button('Benchmark') + optimize_button = gr.Button('Optimize') + optimization_steps = gr.Number(value=parameters.get_optimization_steps(), label='Optimization Steps', info='For how many steps to optimize.', interactive=True) + + + clear_button = gr.Button('❌ Clear Data') + + + with gr.Column(): + last_updated = gr.Markdown() + + all_params = [optimization_steps, time_power, time_steepness, significant_level, min_sentences, new_dist_strat, delta_start, min_number_length, num_conversion, + preprocess_pipeline, api_port, api_on, injection_strategy, add_chat_to_data, manual, postfix, data_separator, prefix, max_token_count, + chunk_count, chunk_sep, context_len, chunk_regex, chunk_len, threads, strong_cleanup] + optimizable_params = [time_power, time_steepness, significant_level, min_sentences, new_dist_strat, delta_start, min_number_length, num_conversion, + preprocess_pipeline, chunk_count, context_len, chunk_len] + + + update_data.click(_feed_data_into_collector, [data_input], last_updated, show_progress=False) + update_url.click(_feed_url_into_collector, [url_input], last_updated, show_progress=False) + update_file.click(_feed_file_into_collector, [file_input], last_updated, show_progress=False) + benchmark_button.click(_begin_benchmark, [], last_updated, show_progress=True) + optimize_button.click(_begin_optimization, [], [last_updated] + optimizable_params, show_progress=True) + clear_button.click(_clear_data, [], last_updated, show_progress=False) + + + optimization_steps.input(fn=_apply_settings, inputs=all_params, show_progress=False) + time_power.input(fn=_apply_settings, inputs=all_params, show_progress=False) + time_steepness.input(fn=_apply_settings, inputs=all_params, show_progress=False) + significant_level.input(fn=_apply_settings, inputs=all_params, show_progress=False) + min_sentences.input(fn=_apply_settings, inputs=all_params, show_progress=False) + new_dist_strat.input(fn=_apply_settings, inputs=all_params, show_progress=False) + delta_start.input(fn=_apply_settings, inputs=all_params, show_progress=False) + min_number_length.input(fn=_apply_settings, inputs=all_params, show_progress=False) + num_conversion.input(fn=_apply_settings, inputs=all_params, show_progress=False) + preprocess_pipeline.input(fn=_apply_settings, inputs=all_params, show_progress=False) + api_port.input(fn=_apply_settings, inputs=all_params, show_progress=False) + api_on.input(fn=_apply_settings, inputs=all_params, show_progress=False) + injection_strategy.input(fn=_apply_settings, inputs=all_params, show_progress=False) + add_chat_to_data.input(fn=_apply_settings, inputs=all_params, show_progress=False) + manual.input(fn=_apply_settings, inputs=all_params, show_progress=False) + postfix.input(fn=_apply_settings, inputs=all_params, show_progress=False) + data_separator.input(fn=_apply_settings, inputs=all_params, show_progress=False) + prefix.input(fn=_apply_settings, inputs=all_params, show_progress=False) + max_token_count.input(fn=_apply_settings, inputs=all_params, show_progress=False) + chunk_count.input(fn=_apply_settings, inputs=all_params, show_progress=False) + chunk_sep.input(fn=_apply_settings, inputs=all_params, show_progress=False) + context_len.input(fn=_apply_settings, inputs=all_params, show_progress=False) + chunk_regex.input(fn=_apply_settings, inputs=all_params, show_progress=False) + chunk_len.input(fn=_apply_settings, inputs=all_params, show_progress=False) + threads.input(fn=_apply_settings, inputs=all_params, show_progress=False) + strong_cleanup.input(fn=_apply_settings, inputs=all_params, show_progress=False) \ No newline at end of file diff --git a/extensions/superboogav2/utils.py b/extensions/superboogav2/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..89b367eacc9e520912327cff0b7893113381aada --- /dev/null +++ b/extensions/superboogav2/utils.py @@ -0,0 +1,16 @@ +""" +This module contains common functions across multiple other modules. +""" + +import extensions.superboogav2.parameters as parameters + +# Create the context using the prefix + data_separator + postfix from parameters. +def create_context_text(results): + context = parameters.get_prefix() + parameters.get_data_separator().join(results) + parameters.get_postfix() + + return context + + +# Create metadata with the specified source +def create_metadata_source(source: str): + return {'source': source} \ No newline at end of file diff --git a/extensions/whisper_stt/readme.md b/extensions/whisper_stt/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..19488f94f429a1e8fb750723d271950f58c17a8d --- /dev/null +++ b/extensions/whisper_stt/readme.md @@ -0,0 +1,15 @@ +# whisper_stt + +Allows you to enter your inputs in chat mode using your microphone. + +## Settings + +To adjust your default settings, you can add the following to your settings.yaml file. + +``` +whisper_stt-whipser_language: chinese +whisper_stt-whipser_model: tiny +whisper_stt-auto_submit: False +``` + +See source documentation for [model names](https://github.com/openai/whisper#available-models-and-languages) and [languages](https://github.com/openai/whisper/blob/main/whisper/tokenizer.py) you can use. diff --git a/extensions/whisper_stt/requirements.txt b/extensions/whisper_stt/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..576c955f3c1e1f556a17d8de323723b04f69892e --- /dev/null +++ b/extensions/whisper_stt/requirements.txt @@ -0,0 +1,4 @@ +SpeechRecognition==3.10.0 +openai-whisper +soundfile +ffmpeg diff --git a/extensions/whisper_stt/script.py b/extensions/whisper_stt/script.py new file mode 100644 index 0000000000000000000000000000000000000000..cdc55687b30abb43ef6adc6c4f25273ff39cb4d0 --- /dev/null +++ b/extensions/whisper_stt/script.py @@ -0,0 +1,71 @@ +import gradio as gr +import speech_recognition as sr + +from modules import shared + +input_hijack = { + 'state': False, + 'value': ["", ""] +} + +# parameters which can be customized in settings.json of webui +params = { + 'whipser_language': 'english', + 'whipser_model': 'small.en', + 'auto_submit': True +} + + +def chat_input_modifier(text, visible_text, state): + global input_hijack + if input_hijack['state']: + input_hijack['state'] = False + return input_hijack['value'] + else: + return text, visible_text + + +def do_stt(audio, whipser_model, whipser_language): + transcription = "" + r = sr.Recognizer() + + # Convert to AudioData + audio_data = sr.AudioData(sample_rate=audio[0], frame_data=audio[1], sample_width=4) + + try: + transcription = r.recognize_whisper(audio_data, language=whipser_language, model=whipser_model) + except sr.UnknownValueError: + print("Whisper could not understand audio") + except sr.RequestError as e: + print("Could not request results from Whisper", e) + + return transcription + + +def auto_transcribe(audio, auto_submit, whipser_model, whipser_language): + if audio is None: + return "", "" + transcription = do_stt(audio, whipser_model, whipser_language) + if auto_submit: + input_hijack.update({"state": True, "value": [transcription, transcription]}) + + return transcription, None + + +def ui(): + with gr.Accordion("Whisper STT", open=True): + with gr.Row(): + audio = gr.Audio(source="microphone") + with gr.Row(): + with gr.Accordion("Settings", open=False): + auto_submit = gr.Checkbox(label='Submit the transcribed audio automatically', value=params['auto_submit']) + whipser_model = gr.Dropdown(label='Whisper Model', value=params['whipser_model'], choices=["tiny.en", "base.en", "small.en", "medium.en", "tiny", "base", "small", "medium", "large"]) + whipser_language = gr.Dropdown(label='Whisper Language', value=params['whipser_language'], choices=["chinese", "german", "spanish", "russian", "korean", "french", "japanese", "portuguese", "turkish", "polish", "catalan", "dutch", "arabic", "swedish", "italian", "indonesian", "hindi", "finnish", "vietnamese", "hebrew", "ukrainian", "greek", "malay", "czech", "romanian", "danish", "hungarian", "tamil", "norwegian", "thai", "urdu", "croatian", "bulgarian", "lithuanian", "latin", "maori", "malayalam", "welsh", "slovak", "telugu", "persian", "latvian", "bengali", "serbian", "azerbaijani", "slovenian", "kannada", "estonian", "macedonian", "breton", "basque", "icelandic", "armenian", "nepali", "mongolian", "bosnian", "kazakh", "albanian", "swahili", "galician", "marathi", "punjabi", "sinhala", "khmer", "shona", "yoruba", "somali", "afrikaans", "occitan", "georgian", "belarusian", "tajik", "sindhi", "gujarati", "amharic", "yiddish", "lao", "uzbek", "faroese", "haitian creole", "pashto", "turkmen", "nynorsk", "maltese", "sanskrit", "luxembourgish", "myanmar", "tibetan", "tagalog", "malagasy", "assamese", "tatar", "hawaiian", "lingala", "hausa", "bashkir", "javanese", "sundanese"]) + + audio.change( + auto_transcribe, [audio, auto_submit, whipser_model, whipser_language], [shared.gradio['textbox'], audio]).then( + None, auto_submit, None, _js="(check) => {if (check) { document.getElementById('Generate').click() }}") + + whipser_model.change(lambda x: params.update({"whipser_model": x}), whipser_model, None) + whipser_language.change(lambda x: params.update({"whipser_language": x}), whipser_language, None) + auto_submit.change(lambda x: params.update({"auto_submit": x}), auto_submit, None) diff --git a/gpt-engineer/.dockerignore b/gpt-engineer/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..58b0fc0985950fcdca36d06fdef8a6cad200d344 --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.env.template b/gpt-engineer/.env.template new file mode 100644 index 0000000000000000000000000000000000000000..8315b6151a710a3b5808ecfa42e839918c5b8b0a --- /dev/null +++ b/gpt-engineer/.env.template @@ -0,0 +1,4 @@ +### OpenAI Setup ### + +# OPENAI_API_KEY=Your personal OpenAI API key from https://platform.openai.com/account/api-keys +OPENAI_API_KEY=$key diff --git a/gpt-engineer/.github/CODEOWNERS b/gpt-engineer/.github/CODEOWNERS new file mode 100644 index 0000000000000000000000000000000000000000..55dfea91d989f15755ad0e7aecd0dbd890760a89 --- /dev/null +++ b/gpt-engineer/.github/CODEOWNERS @@ -0,0 +1 @@ +.github/workflows/ @ATheorell diff --git a/gpt-engineer/.github/CODE_OF_CONDUCT.md b/gpt-engineer/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000000000000000000000000000000..7713a6b3df86b57a46987b4350ffd0a0831155ba --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.github/CONTRIBUTING.md b/gpt-engineer/.github/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..86d993b6bedc250ee548423ec9409f8822cd0153 --- /dev/null +++ b/gpt-engineer/.github/CONTRIBUTING.md @@ -0,0 +1,120 @@ +# Contributing to GPT-engineer + +GPT-engineer is a community project and lives from your contributions - they are warmly appreciated. The main contribution avenues are: +- 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. +- 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. + +By participating in this project, you agree to abide by the [code of conduct](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. + + +## Licensing + +By contributing to GPT Engineer, you agree that your contributions will be licensed under the [LICENSE](../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/gpt-engineer/.github/FUNDING.yml b/gpt-engineer/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..e7deadaff83f467525342d47e4f53d05139d7265 --- /dev/null +++ b/gpt-engineer/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [antonosika] +patreon: gpt_eng diff --git a/gpt-engineer/.github/ISSUE_TEMPLATE/bug-report.md b/gpt-engineer/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000000000000000000000000000000000000..5df09835152dee004ca9ff1c56f10a4ada321cb8 --- /dev/null +++ b/gpt-engineer/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,36 @@ +--- +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. + +### Steps to Reproduce + +If possible, provide detailed steps for reproducing the issue. + +1. step 1 +2. step 2 +3. you get it... + +### Failure Logs + +Any relevant log snippets or files here. diff --git a/gpt-engineer/.github/ISSUE_TEMPLATE/documentation-clarification.md b/gpt-engineer/.github/ISSUE_TEMPLATE/documentation-clarification.md new file mode 100644 index 0000000000000000000000000000000000000000..66de382e5df2d9ec2a1882aaad322e5522e071ec --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.github/ISSUE_TEMPLATE/feature-request.md b/gpt-engineer/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000000000000000000000000000000000..31e6ea2776a7f0e905945a1af5bdf5eac550ebf8 --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md b/gpt-engineer/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000000000000000000000000000000000..8343ddb0d409247e321e8be7f357b5d4a2322593 --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.github/workflows/automation.yml b/gpt-engineer/.github/workflows/automation.yml new file mode 100644 index 0000000000000000000000000000000000000000..1f232730d0bf7f31d802a1e162f4cd4de2cc3c7f --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.github/workflows/ci.yaml b/gpt-engineer/.github/workflows/ci.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6521a530efbc936d9e7d28b9062e1fc979033bb3 --- /dev/null +++ b/gpt-engineer/.github/workflows/ci.yaml @@ -0,0 +1,32 @@ +name: Tox pytest all python versions + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.11'] + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' #Note that pip is for the tox level. Poetry is still used for installing the specific environments (tox.ini) + + - name: Install Tkinter + run: sudo apt-get install python3-tk + + - name: Install tox + run: pip install tox + + - name: Run tox + run: tox diff --git a/gpt-engineer/.github/workflows/pre-commit.yaml b/gpt-engineer/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1ef45cc277bf929c66820673a2dc1f38cd5e7d34 --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.github/workflows/release.yaml b/gpt-engineer/.github/workflows/release.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5044c1f4da82bc1a48ec4ad83e553a97fdd5bc7b --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.gitignore b/gpt-engineer/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..314e66cf29f7307e4115a26f97bd0d5c308936e7 --- /dev/null +++ b/gpt-engineer/.gitignore @@ -0,0 +1,92 @@ +# 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/ +.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 + +# Pyenv +.python-version + +.gpte_consent + +# projects folder apart from default prompt + +projects/* +!projects/example/prompt +!projects/example-improve + +# docs + +docs/_build +docs/__init__ +docs/ai +docs/chat_to_files +docs/collect +docs/learning +docs/main +docs/steps/ +docs/db +docs/db/ +docs/core/ + +# coding assistants +.aider* +.gpteng + +# webapp specific +webapp/node_modules +webapp/package-lock.json + +webapp/.next/ + +.langchain.db + +# TODO files +/!todo* + +#ignore tox files +.tox diff --git a/gpt-engineer/.pre-commit-config.yaml b/gpt-engineer/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3fe0daa264ea673f44dd0e2dbb5ce270281ec9a3 --- /dev/null +++ b/gpt-engineer/.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/gpt-engineer/.readthedocs.yaml b/gpt-engineer/.readthedocs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d916f9f8eef497555a918ac8820ab8d6c3aed56d --- /dev/null +++ b/gpt-engineer/.readthedocs.yaml @@ -0,0 +1,38 @@ +# .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 + - poetry config virtualenvs.create false + post_install: + - poetry install + +# 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/gpt-engineer/.vs/ProjectSettings.json b/gpt-engineer/.vs/ProjectSettings.json new file mode 100644 index 0000000000000000000000000000000000000000..f8b4888565caadc7510be75682268d6c18edd6de --- /dev/null +++ b/gpt-engineer/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/gpt-engineer/.vs/VSWorkspaceState.json b/gpt-engineer/.vs/VSWorkspaceState.json new file mode 100644 index 0000000000000000000000000000000000000000..ea5f0886887c0f0c3b649944f871906935d36b16 --- /dev/null +++ b/gpt-engineer/.vs/VSWorkspaceState.json @@ -0,0 +1,11 @@ +{ + "ExpandedNodes": [ + "", + "\\gpt_engineer\\applications", + "\\gpt_engineer\\applications\\cli", + "\\projects", + "\\tests\\core\\default" + ], + "SelectedNode": "\\docker-compose.yml", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/gpt-engineer/.vs/gpt-engineer/FileContentIndex/f6510b3e-dadc-4796-850b-f449bea6eab3.vsidx b/gpt-engineer/.vs/gpt-engineer/FileContentIndex/f6510b3e-dadc-4796-850b-f449bea6eab3.vsidx new file mode 100644 index 0000000000000000000000000000000000000000..9efcf8b77fe329caf25dbae501323c6a640de16c Binary files /dev/null and b/gpt-engineer/.vs/gpt-engineer/FileContentIndex/f6510b3e-dadc-4796-850b-f449bea6eab3.vsidx differ diff --git a/gpt-engineer/.vs/gpt-engineer/v17/.wsuo b/gpt-engineer/.vs/gpt-engineer/v17/.wsuo new file mode 100644 index 0000000000000000000000000000000000000000..8b3541d472e7a2dfb561c39c4bc00d3c68f5089d Binary files /dev/null and b/gpt-engineer/.vs/gpt-engineer/v17/.wsuo differ diff --git a/gpt-engineer/.vs/slnx.sqlite b/gpt-engineer/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..2a9cc00994de56839c333de49244e2326e7da868 Binary files /dev/null and b/gpt-engineer/.vs/slnx.sqlite differ diff --git a/gpt-engineer/Acknowledgements.md b/gpt-engineer/Acknowledgements.md new file mode 100644 index 0000000000000000000000000000000000000000..4f1a2cb73000745f6eafb2158d73b88da4fcbf55 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/DISCLAIMER.md b/gpt-engineer/DISCLAIMER.md new file mode 100644 index 0000000000000000000000000000000000000000..17fd588fb692369a24f6c3d185b555a88eb731ca --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/GOVERNANCE.md b/gpt-engineer/GOVERNANCE.md new file mode 100644 index 0000000000000000000000000000000000000000..f621698a9520857f9378dd2bb99ccdcb9d1067bd --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/LICENSE b/gpt-engineer/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..a56867b9bdba937f0a9cb2656cc999d86aab2eaf --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/MANIFEST.in b/gpt-engineer/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..d575b3d2438e549e45a37362920cd79551bffb23 --- /dev/null +++ b/gpt-engineer/MANIFEST.in @@ -0,0 +1 @@ +recursive-include gpt_engineer/preprompts * diff --git a/gpt-engineer/Makefile b/gpt-engineer/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c66a67e63ad8c350d9b84fb20b24aaf99b926f1f --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/README.md b/gpt-engineer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..72c647b10ff628ccbc2d0465c573806e4ca7968c --- /dev/null +++ b/gpt-engineer/README.md @@ -0,0 +1,94 @@ +# GPT-Engineer + +[![Discord Follow](https://dcbadge.vercel.app/api/server/8tcDQ89Ej2?style=flat)](https://discord.gg/8tcDQ89Ej2) +[![GitHub Repo stars](https://img.shields.io/github/stars/gpt-engineer-org/gpt-engineer?style=social)](https://github.com/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 a 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.11. 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) + +### Creating 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/` + +### Improving 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` from the gpt-engineer directory root with your folder in `projects/` + +By running gpt-engineer you agree to our [terms](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/TERMS_OF_USE.md). + +### Note + +- To run this tool, the new command `gpte` is recommended for better user experience. However, the earlier default commands `gpt-engineer` and `ge` are also supported. + +## Relation to gptengineer.app +[gptengineer.app](https://gptengineer.app/) is a commercial project for automatic generation of web-apps. It emerged from gpt-engineer and is committed to giving back to the open source community. A portion of gptengineer.app’s revenue will fund a full-time role and infrastructure for maintaining open-source tools for code generation. + + +## Features + +You can specify the "identity" of the AI agent by editing the files in the `preprompts` folder. + +Editing the `preprompts`, and evolving how you write the project prompt, is how you make the agent remember things between projects. + +You can also automatically copy all `preprompts` files into your project folder using the cli parameter `--use-custom-preprompts`. This way you can have custom preprompts for all of your projects without the need to edit the main files. + +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 get input on 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/gpt-engineer/ROADMAP.md b/gpt-engineer/ROADMAP.md new file mode 100644 index 0000000000000000000000000000000000000000..5f6cac06cf9e8453e0e5b15965969c2f89ce24d7 --- /dev/null +++ b/gpt-engineer/ROADMAP.md @@ -0,0 +1,56 @@ +# Roadmap + +image + + +There are three main milestones we believe will greatly increase gpt-engineer's reliability and capability: +- [x] Continuous evaluation of our progress 🎉 +- [ ] Test code and fix errors with LLMs +- [ ] Make code generation become small, verifiable steps + +## Our current focus: + +- [x] **Continuous evaluation of progress 🎉** + - [x] Create a step that asks “did it run/work/perfect” in the end of each run [#240](https://github.com/gpt-engineer-org/gpt-engineer/issues/240) 🎉 + - [x] Collect a dataset for gpt engineer to learn from, by storing code generation runs 🎉 + - [ ] Run the benchmark multiple times, and document the results for the different "step configs" [#239](https://github.com/gpt-engineer-org/gpt-engineer/issues/239) + - [ ] Improve the default config based on results +- [ ] **Self healing code** + - [ ] Run the generated tests + - [ ] Feed the results of failing tests back into LLM and ask it to fix the code +- [ ] **Let human give feedback** + - [ ] Ask human for what is not working as expected in a loop, and feed it into LLM to fix the code, until the human is happy +- [ ] **Improve existing projects** + - [ ] Decide on the "flow" for the CLI commands and where the project files are created + - [ ] Add an "improve code" command + - [ ] Benchmark capabilities against other tools + +## Experimental research +This is not our current focus, but if you are interested in experimenting: Please +create a thread in Discord #general and share your intentions and your findings as you +go along. High impact examples: +- [ ] **Make code generation become small, verifiable steps** + - [ ] Ask GPT4 to decide how to sequence the entire generation, and do one + prompt for each subcomponent + - [ ] For each small part, generate tests for that subpart, and do the loop of running the tests for each part, feeding +results into GPT4, and let it edit the code until they pass +- [ ] **Ad hoc experiments** + - [ ] Try Microsoft guidance, and benchmark if this helps improve performance + - [ ] Dynamic planning: Let gpt-engineer plan which "steps" to carry out itself, depending on the +task, by giving it few shot example of what are usually "the right-sized steps" to carry +out for such projects + +## Codebase improvements +By improving the codebase and developer ergonomics, we accelerate progress. Some examples: +- [ ] Set up automatic PR review for all PRs with e.g. Codium pr-agent +- [ ] LLM tests in CI: Run super small tests with GPT3.5 in CI, that check that simple code generation still works + +# How you can help out + +You can: + +- Post a "design" as a google doc in our Discord 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/gpt-engineer/TERMS_OF_USE.md b/gpt-engineer/TERMS_OF_USE.md new file mode 100644 index 0000000000000000000000000000000000000000..91e97b86a28860f110da84a152a3b1ac749c96dd --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/WINDOWS_README.md b/gpt-engineer/WINDOWS_README.md new file mode 100644 index 0000000000000000000000000000000000000000..81943336fd2bc78ff9d24ea75d34be71852e0c49 --- /dev/null +++ b/gpt-engineer/WINDOWS_README.md @@ -0,0 +1,68 @@ +# Windows Setup +## Short version + +On Windows, follow the standard [README.md](./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:AntonOsika/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/gpt-engineer/citation.cff b/gpt-engineer/citation.cff new file mode 100644 index 0000000000000000000000000000000000000000..62dd0edafd02d65c2c3e000a167b09d8e839406d --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/docker-compose.yml b/gpt-engineer/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..41fbc3c33c1078a5d1329cc1f498960fae8ec966 --- /dev/null +++ b/gpt-engineer/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3" + +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/gpt-engineer/docker/Dockerfile b/gpt-engineer/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..4c7a409dcc3b2fb552b980974a14363d05a92cdb --- /dev/null +++ b/gpt-engineer/docker/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.11-slim + +RUN apt-get update \ + && apt-get install -y sudo tk tcl gcc curl + +WORKDIR /app + +COPY . . +COPY docker/entrypoint.sh ./entrypoint.sh + +RUN sudo pip install -e . + +ENTRYPOINT ["bash", "/app/entrypoint.sh"] diff --git a/gpt-engineer/docker/README.md b/gpt-engineer/docker/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6f8242b94c3b84b531c0d817a4f9aa0380553636 --- /dev/null +++ b/gpt-engineer/docker/README.md @@ -0,0 +1,24 @@ +# Getting Started using Docker + +**Running using docker cli**: + +Building the image: +- `git clone https://github.com/gpt-engineer-org/gpt-engineer.git` +- `cd gpt-engineer` +- `docker build --rm -t gpt-engineer -f docker/Dockerfile .` + +Running the container: +- `docker run -it --rm -e OPENAI_API_KEY="YOUR OPENAI KEY" -v ./your-project:/project gpt-engineer` + +The `-v` flag mounts the `your-project` folder into the container. Make sure to have a `prompt` file in there. + +**Running using docker-compose cli**: + +Building the image: +- `git clone https://github.com/gpt-engineer-org/gpt-engineer.git` +- `cd gpt-engineer` +- `docker-compose -f docker-compose.yml build` +- `docker-compose run --rm gpt-engineer` + + +Set the OPENAI_API_KEY in docker/docker-compose.yml using .env file or environment variable, and mount your project folder into the container using volumes. for example "./projects/example:/project" ./projects/example is the path to your project folder. diff --git a/gpt-engineer/docker/entrypoint.sh b/gpt-engineer/docker/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..bb22cc0da919f04185d1267454b4a2bb06394cc3 --- /dev/null +++ b/gpt-engineer/docker/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash +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 +for item in "$project_dir"/*; do + if [[ "$item" != "$project_dir/prompt" ]]; then + chown -R nobody:nogroup "$item" + chmod -R 777 "$item" + fi +done diff --git a/gpt-engineer/docs/DOCS_BUILDING.md b/gpt-engineer/docs/DOCS_BUILDING.md new file mode 100644 index 0000000000000000000000000000000000000000..1b2bcbeb691eef56eaa8385487ee78002fef25e3 --- /dev/null +++ b/gpt-engineer/docs/DOCS_BUILDING.md @@ -0,0 +1,60 @@ +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 +``` + +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/gpt-engineer/docs/Makefile b/gpt-engineer/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b7e237fa0362ca1fa39c4b2333b4c0700d307c6a --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/docs/api_reference.rst b/gpt-engineer/docs/api_reference.rst new file mode 100644 index 0000000000000000000000000000000000000000..89730bb9c3ec1b98a456a72271549e9e60c41ca8 --- /dev/null +++ b/gpt-engineer/docs/api_reference.rst @@ -0,0 +1,97 @@ +.. _api_reference: + +============= +API Reference +============= + +:mod:`gpt_engineer.cli`: Cli +============================= + +.. automodule:: gpt_engineer.cli + :no-members: + :no-inherited-members: + +Classes +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: cli + :template: class.rst + + cli.file_selector.DisplayablePath + +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: cli + + cli.collect.collect_learnings + cli.collect.send_learning + cli.collect.steps_file_hash + cli.file_selector.ask_for_files + cli.file_selector.gui_file_selector + cli.file_selector.is_in_ignoring_extensions + cli.file_selector.terminal_file_selector + cli.learning.ask_collection_consent + cli.learning.check_collection_consent + cli.learning.extract_learning + cli.learning.get_session + cli.learning.human_review_input + cli.learning.logs_to_string + cli.main.load_env_if_needed + cli.main.load_prompt + cli.main.main + cli.main.preprompts_path + +: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.steps.Config + +Functions +-------------- +.. currentmodule:: gpt_engineer + +.. autosummary:: + :toctree: core + + core.ai.serialize_messages + core.chat_to_files.apply_edits + core.chat_to_files.format_file_to_input + core.chat_to_files.get_code_strings + core.chat_to_files.overwrite_files_with_edits + core.chat_to_files.parse_chat + core.chat_to_files.parse_edits + core.chat_to_files.to_files + core.chat_to_files.to_files_and_memory + core.db.archive + core.steps.assert_files_ready + core.steps.clarify + core.steps.curr_fn + core.steps.execute_entrypoint + core.steps.gen_clarified_code + core.steps.gen_entrypoint + core.steps.get_improve_prompt + core.steps.human_review + core.steps.improve_existing_code + core.steps.lite_gen + core.steps.set_improve_filelist + core.steps.setup_sys_prompt + core.steps.setup_sys_prompt_existing_code + core.steps.simple_gen + core.steps.use_feedback diff --git a/gpt-engineer/docs/code_conduct_link.rst b/gpt-engineer/docs/code_conduct_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..58cb2e4fdaa68ea16e545d7a36b949ec382d303e --- /dev/null +++ b/gpt-engineer/docs/code_conduct_link.rst @@ -0,0 +1,2 @@ +.. include:: ../.github/CODE_OF_CONDUCT.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/conf.py b/gpt-engineer/docs/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..0a7966d2ae8893b935d377da082c65c7622cc819 --- /dev/null +++ b/gpt-engineer/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", + "sphinx_panels", + "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": True, + "special-members": "__call__", +} + +# 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 = None + +# 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", +] diff --git a/gpt-engineer/docs/contributing_link.rst b/gpt-engineer/docs/contributing_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..4d4c123bdfdf4bcd211f9034994fca7201f6f9fb --- /dev/null +++ b/gpt-engineer/docs/contributing_link.rst @@ -0,0 +1,2 @@ +.. include:: ../.github/CONTRIBUTING.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/create_api_rst.py b/gpt-engineer/docs/create_api_rst.py new file mode 100644 index 0000000000000000000000000000000000000000..6b443c484aa8680ec289cdf000a7f71e4b73ef83 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/docs/disclaimer_link.rst b/gpt-engineer/docs/disclaimer_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..d0ed67e71dd2033b02e13197893c451447db0050 --- /dev/null +++ b/gpt-engineer/docs/disclaimer_link.rst @@ -0,0 +1,2 @@ +.. include:: ../DISCLAIMER.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/index.rst b/gpt-engineer/docs/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..f0f65bff8ebfa7e6718e5f7af47b200d80d6497a --- /dev/null +++ b/gpt-engineer/docs/index.rst @@ -0,0 +1,69 @@ +Welcome to GPT-ENGINEER's Documentation +======================================== + +.. toctree:: + :maxdepth: 2 + :caption: INTRODUCTION: + + intro/quick_overview.md + intro/repository_stats.md + results_link + intro/main_script.md + intro/scripts.md + intro/preprompts.md + +.. toctree:: + :maxdepth: 2 + :caption: USER GUIDES: + + installation + usage + readme_link + windows_readme_link + open_models.md + tracing_debugging.md + + +.. toctree:: + :maxdepth: 2 + :caption: CORE COMPONENTS: + + intro/gate.md + intro/ai_class.md + intro/db_class.md + intro/steps_module.md + + +.. toctree:: + :maxdepth: 2 + :caption: HARMONY OF AI, DB, & STEPS: + + intro/harmony_db_ai_steps.md + +.. toctree:: + :maxdepth: 2 + :caption: CHAT PARSING & SELF EXECUTION: + + intro/chat_parsing.md + +.. toctree:: + :maxdepth: 2 + :caption: CONTRIBUTING GUIDES: + + contributing_link + DOCS_BUILDING.md + roadmap_link + code_conduct_link + disclaimer_link + +.. toctree:: + :maxdepth: 2 + :caption: PACKAGE API: + + api_reference + +Indices and tables +================== +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/gpt-engineer/docs/installation.rst b/gpt-engineer/docs/installation.rst new file mode 100644 index 0000000000000000000000000000000000000000..69131ccabd764d44b9d68f8a3440778a64436d59 --- /dev/null +++ b/gpt-engineer/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 + + $ pip install gpt_engineer + +This is the preferred method to install file-processor, 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 file-processor 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/gpt-engineer/docs/intro/# Table of Contents.md b/gpt-engineer/docs/intro/# Table of Contents.md new file mode 100644 index 0000000000000000000000000000000000000000..78cea53d2f05151042828a188c65ecab3782ebf8 --- /dev/null +++ b/gpt-engineer/docs/intro/# Table of Contents.md @@ -0,0 +1,19 @@ +# Table of Contents + +1. [Introduction](#introduction) +2. [User Guide](#user-guide) + 1. [Installation](#installation) + 2. [Setting Up Your Project](#setting-up-your-project) + 3. [Running GPT-Engineer](#running-gpt-engineer) + 4. [Reviewing the Results](#reviewing-the-results) +3. [Core Components](#core-components) + 1. [AI Class](#ai-class) + 2. [DB Class](#db-class) + 3. [Steps Module](#steps-module) +4. [Harmony of AI, DB, and Steps](#harmony-of-ai-db-and-steps) + 1. [How Each Step is Made](#how-each-step-is-made) + 2. [How to Make Your Own Step](#how-to-make-your-own-step) +5. [Chat Parsing & Self Code Execution.py](#chat_to_files.py) + 1. [Functions](#functions) + 2. [Self Code Execution](#self-code-execution) + 3. [Disclaimer](#disclaimer) diff --git a/gpt-engineer/docs/intro/ai_class.md b/gpt-engineer/docs/intro/ai_class.md new file mode 100644 index 0000000000000000000000000000000000000000..e880bc05ccf981618251d7482e0cfb91a5f46d1b --- /dev/null +++ b/gpt-engineer/docs/intro/ai_class.md @@ -0,0 +1,11 @@ +# AI Class Documentation +The AI class is the main interface to the GPT-4 model. It provides methods to start a conversation with the model, continue an existing conversation, and format system and user messages. The AI class is defined in the `gpt_engineer/ai.py` file. + +
+ +## Methods +`__init__(self, model="gpt-4", temperature=0.1)`: The constructor takes the name of the AI model and the temperature parameter as arguments. It tries to retrieve the specified model from OpenAI. If the model is not available, it reverts to a fallback model. + +`start(self, system, user)`: This method starts a conversation with the AI. It takes a system message and a user message as arguments, and returns the AI's response. + +`next(self, messages: list[dict[str, str]], prompt=None)`: This method continues a conversation with the AI. It takes a list of messages and an optional prompt as arguments, and returns the AI's response. diff --git a/gpt-engineer/docs/intro/chat_parsing.md b/gpt-engineer/docs/intro/chat_parsing.md new file mode 100644 index 0000000000000000000000000000000000000000..1ffc079e18ce7c2e2b2621438fc89e56acb4b1f2 --- /dev/null +++ b/gpt-engineer/docs/intro/chat_parsing.md @@ -0,0 +1,39 @@ +# Chat Parsing and Self-Execution of Code +The `chat_to_files.py` module plays a pivotal role in the self-execution of code generated by the AI. It parses the chat generated by the AI, extracts the code blocks, and saves them as files in the workspace. This process is crucial for making the project files a reality and enabling the self-execution of the generated code. The module is defined in the `gpt_engineer/chat_to_files.py` file. + +
+ +## Functions +Here are the functions defined in the `chat_to_files.py` module: + +`parse_chat(chat)`: This function takes the chat as an argument and returns a list of tuples. Each tuple contains a filename and the corresponding file content. The function uses regular expressions to extract the filenames and the file contents from the chat. The filenames are cleaned up to remove any non-allowed characters. + +`to_files_and_memory(chat, dbs)`: This function takes the chat and the DBs as arguments. DBs contains the workspace and memory path. The function first saves the entire chat as a text file in the memory path. Then it calls the to_files function to write each file to the workspace. + +`to_files(chat, db)`: This function takes the chat and workspace DB as arguments. It calls the parse_chat function to parse the chat and get the files. Each file is then saved to the workspace. + +
+ +## Self Code Execution +The `chat_to_files.py` module enables the self-execution of the code generated by the AI. When GPT-Engineer runs, it generates a chat that contains the AI's responses. These responses include code blocks that are intended to be saved as files. Each code block is preceded by a filename. + +
+ +The `to_files` function is called with the chat and the workspace as arguments. It first saves the entire chat as a text file in the workspace. Then it calls the parse_chat function to parse the chat and extract the filenames and the file contents. + +
+ +The `parse_chat` function uses regular expressions to find all code blocks and the preceding filenames in the chat. It cleans up the filenames and pairs them with the corresponding file contents. These pairs are then returned as a list of tuples. + +
+ +Back in the `to_files` function, each tuple in the list is unpacked into a filename and a file content. Each file is then saved to the workspace with the corresponding filename and content. + +
+ +Once the files are saved in the workspace, GPT-Engineer can then execute the code in the files by user permission. This enables the self-execution of the code generated by the AI. + +
+ +## Disclaimer +While the self-execution of code is a powerful feature, it should be used with caution. The code generated by the AI is based on the prompts provided by the user and the training data of the AI model. It may not always produce the expected results, and in some cases, it may produce incorrect or harmful results. Always review the generated code before executing it, and never execute code that you do not understand or trust. diff --git a/gpt-engineer/docs/intro/db_class.md b/gpt-engineer/docs/intro/db_class.md new file mode 100644 index 0000000000000000000000000000000000000000..87b156a8f42e63a87db17929e0609f55b8c8f29b --- /dev/null +++ b/gpt-engineer/docs/intro/db_class.md @@ -0,0 +1,33 @@ +# DB Class +The DB class represents a simple database that stores its data as files in a directory. It is a key-value store, where keys are filenames and values are file contents. The DB class is defined in the `gpt_engineer/db.py` file. + +
+ +### DB Class +Methods and how they are being used: + +`__init__(self, path)`: The constructor takes a path as an argument and creates a directory at that path if it does not already exist. + +`__contains__(self, key)`: This method checks if a key (filename) exists in the database. It returns True if the file exists and False otherwise. + +`__getitem__(self, key)`: This method gets the value (file content) associated with a key (filename). It raises a `KeyError` if the key does not exist in the database. + +`__setitem__(self, key, val)`: This method sets the value (file content) associated with a key (filename). It creates the file if it does not already exist. The value must be either a string or bytes. + +
+ +### DBs Class +The DBs class is a dataclass that contains instances of the DB class for different types of data: + +Each instance of the DBs class contains five databases currently: + +`memory`: This database is used to store the AI's memory. +`logs`: This database is used to store logs of the AI's actions. +`preprompts`: This database is used to store preprompts that guide the AI's actions. +`input`: This database is used to store the user's input. +`workspace`: This database is used to store the AI's workspace, which includes the code it generates. + +
+ +## Conclusion +The DB and DBs classes provide a simple and flexible way to manage data in the GPT-Engineer system. They allow the system to store and retrieve data as files in a directory, which makes it easy to inspect and modify the data. diff --git a/gpt-engineer/docs/intro/gate.md b/gpt-engineer/docs/intro/gate.md new file mode 100644 index 0000000000000000000000000000000000000000..b3caa26856c83df32d577d9c99931de60731e53d --- /dev/null +++ b/gpt-engineer/docs/intro/gate.md @@ -0,0 +1,34 @@ +# GPT-Engineer + +GPT-Engineer is an open-source project that leverages the capabilities of OpenAI's GPT-4 model to automate various software engineering tasks. It is designed to interact with the GPT-4 model in a conversational manner, guiding the model to generate code, clarify instructions, generate specifications, and more. The project is built with a modular architecture, making it easy to extend and customize for various use cases. + +## Core Components + +GPT-Engineer is composed of several core components that work together to provide its functionality: + +- **AI Class**: The AI class serves as the main interface to the GPT-4 model. It provides methods to start a conversation with the model, continue an existing conversation, and format system and user messages. + +- **DB Class**: The DB class represents a simple database that stores its data as files in a directory. It is a key-value store, where keys are filenames and values are file contents. + +- **Steps Module**: The steps module defines a series of steps that the AI can perform to generate code, clarify instructions, generate specifications, and more. Each step is a function that takes an AI and a set of databases as arguments and returns a list of messages. + +## Usage + +GPT-Engineer is designed to be easy to use, even for users without a background in coding. Users can write prompts in plain English, and GPT-Engineer will guide the GPT-4 model to generate the desired output. The generated code is saved as files in a workspace, and can be executed independently of the GPT-Engineer system. + +## Development and Community + +GPT-Engineer is an open-source project, and contributions from the community are welcomed and encouraged. The project is hosted on GitHub, where users can report issues, suggest enhancements, and contribute code. + +## See Also + +- [User Guide](#user-guide) +- [AI Class Documentation](#ai-class) +- [DB Class Documentation](#db-class) +- [Steps Module Documentation](#steps-module) +- [Harmony of AI, DB, and Steps](#harmony-of-ai-db-and-steps) +- [Chat Parsing & Self Code Execution](#chat_to_files.py) + +## References + +- [GPT-Engineer GitHub Repository](https://github.com/gpt-engineer-org/gpt-engineer) diff --git a/gpt-engineer/docs/intro/harmony_db_ai_steps.md b/gpt-engineer/docs/intro/harmony_db_ai_steps.md new file mode 100644 index 0000000000000000000000000000000000000000..cd5b8f30cce636839ac1892114709bb1a72fa292 --- /dev/null +++ b/gpt-engineer/docs/intro/harmony_db_ai_steps.md @@ -0,0 +1,75 @@ +# Harmony of AI, DB, and Steps +GPT-Engineer is a powerful tool that uses AI to automate software engineering tasks. It is designed with a modular architecture that makes it easy to extend and customize. The core components of GPT-Engineer are the AI class, the DB class, and the steps module. These components work together in harmony to provide a flexible and powerful system for automating software engineering tasks. + +
+ +## AI Class +The AI class is the main interface to the GPT-3 model. It provides methods to start a conversation with the model, continue an existing conversation, and format system and user messages. The AI class is responsible for interacting with the GPT-3 model and generating the AI's responses. + +
+ +## DB Class +The DB class represents a simple database that stores its data as files in a directory. It is a key-value store, where keys are filenames and values are file contents. The DB class is responsible for managing the data used by the GPT-Engineer system. + +
+ +## Steps Module +The steps module defines a series of steps that the AI can perform to generate code, clarify instructions, generate specifications, and more. Each step is a function that takes an AI and a set of databases as arguments and returns a list of messages. The steps module is responsible for controlling the flow of the GPT-Engineer system. + +
+ +### How Each Step is Made +Each step in the steps module is a function that takes an AI and a set of databases as arguments. The function performs a specific task, such as generating code or clarifying instructions, and returns a list of messages. The messages are then saved to the databases and used in subsequent steps. + +Here is an example of a step function: + +
+ +```python +def simple_gen(ai: AI, dbs: FileRepositories): + """Generate code based on the main prompt.""" + system = dbs.preprompts["generate"] + user = dbs.input["main_prompt"] + messages = ai.start(system, user) + dbs.workspace["code.py"] = messages[-1]["content"] + return messages +``` +
+ +This function uses the AI to generate code based on the main prompt. It reads the main prompt from the input database, generates the code, and saves the code to the workspace database. + +
+ +### How to Make Your Own Step +To make your own step, you need to define a function that takes an AI and a set of databases as arguments. Inside the function, you can use the AI to generate responses and the databases to store and retrieve data. Here is an example: + +
+ +```python +def generate_function(ai: AI, dbs: DBs): + """Generate a simple Python function.""" + function_name = dbs.input["function_name"] + function_description = dbs.input["function_description"] + system = "Please generate a Python function." + user = f"I want a function named '{function_name}' that {function_description}." + messages = ai.start(system, user) + dbs.workspace[f"{function_name}.py"] = messages[-1]["content"] + return messages +``` + +
+ +In this custom step, we're asking the AI to generate a Python function based on a function name and a description provided by the user. The function name and description are read from the input database. The generated function is saved to the workspace database with a filename that matches the function name. You would simply need to provide a `function_name` file and `function_description` file with necessary details in the input database (your project folder) to use this step. + +
+ +For example, if the user provides the function name "greet" and the description "prints 'Hello, world!'", the AI might generate the following Python function: + +```python +def greet(): + print('Hello, world!') +``` + +
+ +This function would be saved to the workspace database as `greet.py`. diff --git a/gpt-engineer/docs/intro/main_script.md b/gpt-engineer/docs/intro/main_script.md new file mode 100644 index 0000000000000000000000000000000000000000..f128f9c5f2bf3ab0ead383a67bb6eefd84ee4f4b --- /dev/null +++ b/gpt-engineer/docs/intro/main_script.md @@ -0,0 +1,43 @@ +# Main Script +The `main.py` script is the entry point of the application. It sets up the AI model and the databases, and then runs a series of steps based on the provided configuration. The script uses the Typer library to create a command-line interface. + +
+ +## Command-Line Interface +The script provides a command-line interface with several options: + +`project_path`: The path to the project directory. The default value is "example". + +`delete_existing`: A boolean flag that indicates whether to delete existing files in the project directory. The default value is `False`. + +`model`: The name of the AI model to use. The default value is "gpt-4". + +`temperature`: The temperature parameter for the AI model, which controls the randomness of the model's output. The default value is `0.1`. + +`steps_config`: The configuration of steps to run. The default value is "default". + +`verbose`: A boolean flag that controls the verbosity of the logging. If `True`, the logging level is set to `DEBUG`. Otherwise, the logging level is set to `INFO`. The default value is `False`. + +`run_prefix`: A prefix for the run, which can be used if you want to run multiple variants of the same project and later compare them. The default value is an empty string. + +
+ +## Usage +To run the script, you can use the following command: + +``` +bash +python gpt_engineer/main.py --project_path [path] --delete_existing [True/False] --model [model] --temperature [temperature] --steps [steps_config] --verbose [True/False] --run_prefix [prefix] +``` + +You can replace the placeholders with the appropriate values. For example, to run the script with the default configuration on a project in the "my_project" directory, you can use the following command: + +``` +bash +python gpt_engineer/main.py --project_path my_project +``` + +
+ +## Conclusion +The `main.py` script provides a flexible and user-friendly interface for running the GPT-Engineer system. It allows you to easily configure the AI model, the steps to run, and other parameters. The script also provides detailed logging, which can be useful for debugging and understanding the behavior of the system. diff --git a/gpt-engineer/docs/intro/preprompts.md b/gpt-engineer/docs/intro/preprompts.md new file mode 100644 index 0000000000000000000000000000000000000000..9e8c2d54c263e6101a2cbe68d8b98b82bacd3ab1 --- /dev/null +++ b/gpt-engineer/docs/intro/preprompts.md @@ -0,0 +1,56 @@ +# Preprompts + +The preprompts are a set of predefined prompts that guide the AI in performing different tasks. They are stored as text files in the `gpt_engineer/preprompts` directory. + +
+ +### 1. Fix Code (`gpt_engineer/preprompts/fix_code`) + +This prompt instructs the AI to fix a program and make it work according to the best of its knowledge. The AI is expected to provide fully functioning, well-formatted code with few comments, that works and has no bugs. + +
+ +### 2. Generate (`gpt_engineer/preprompts/generate`) + +This prompt instructs the AI to generate code based on a set of instructions. The AI is expected to think step by step and reason itself to the right decisions to make sure it gets it right. It should first lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. Then it should output the content of each file including ALL code. + +
+ +### 3. Philosophy (`gpt_engineer/preprompts/philosophy`) + +This prompt provides the AI with a set of best practices to follow when writing code. For example, it instructs the AI to always put different classes in different files, to always create an appropriate requirements.txt file for Python, and to always follow the best practices for the requested languages in terms of describing the code written as a defined package/project. + +
+ +### 4. QA (`gpt_engineer/preprompts/qa`) + +This prompt instructs the AI to read instructions and seek to clarify them. The AI is expected to first summarise a list of super short bullets of areas that need clarification. Then it should pick one clarifying question, and wait for an answer from the user. + +
+ +### 5. Respec (`gpt_engineer/preprompts/respec`) + +This prompt instructs the AI to review a specification for a new feature and give feedback on it. The AI is expected to identify anything that might not work the way intended by the instructions, anything missing for the program to work as expected, and anything that can be simplified without significant drawback. + +
+ +### 6. Spec (`gpt_engineer/preprompts/spec`) + +This prompt instructs the AI to make a specification for a program. The AI is expected to be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. + +
+ +### 7. Unit Tests (`gpt_engineer/preprompts/unit_tests`) + +This prompt instructs the AI to write tests according to a specification using Test Driven Development. The tests should be as simple as possible, but still cover all the functionality. + +
+ +### 8. Use Feedback (`gpt_engineer/preprompts/use_feedback`) +This prompt instructs the AI to generate code based on a set of instructions and feedback defined by the user. + +
+ +## Conclusion + +GPT-Engineer provides a powerful tool for automating software engineering tasks using GPT-4. It includes a flexible framework for running different sequences of steps, each guided by a set of predefined prompts. The AI is expected to follow best practices and reason itself to the right decisions to ensure high-quality code generation. diff --git a/gpt-engineer/docs/intro/quick_overview.md b/gpt-engineer/docs/intro/quick_overview.md new file mode 100644 index 0000000000000000000000000000000000000000..5e99db18130b32f604da0e264eeeff49033b57f2 --- /dev/null +++ b/gpt-engineer/docs/intro/quick_overview.md @@ -0,0 +1,55 @@ +# GPT-Engineer Documentation + +GPT-Engineer is a project that uses GPT-4 to automate the process of software engineering. It includes several Python scripts that interact with the GPT-4 model to generate code, clarify requirements, generate specifications, and more. + +
+ +## Core Components +### 1. AI Class (`gpt_engineer/ai.py`) +The AI class is the main interface to the GPT-4 model. It provides methods to start a conversation with the model, continue an existing conversation, and format system and user messages. + +
+ +### 2. Chat to Files (`gpt_engineer/chat_to_files.py`) +This module contains two main functions: + +`parse_chat(chat)`: This function takes a chat conversation and extracts all the code blocks and preceding filenames. It returns a list of tuples, where each tuple contains a filename and the corresponding code block. + +`to_files_and_memory(chat, dbs)`: This function takes the chat and the DBs as arguments. DBs contains the workspace and memory path. The function first saves the entire chat as a text file in the memory path. Then it calls the to_files function to write each file to the workspace. + +`to_files(chat, db)`: This function takes the chat and workspace DB as arguments. It calls the parse_chat function to parse the chat and get the files. Each file is then saved to the workspace. + +
+ +### 3. DB Class (`gpt_engineer/db.py`) +The DB class represents a simple database that stores its data as files in a directory. It provides methods to check if a key (filename) exists in the database, get the value (file content) associated with a key, and set the value associated with a key. + +The DBs class is a dataclass that contains instances of the DB class for different types of data (memory, logs, input, workspace, and preprompts). + +
+ +### 4. Main Script (`gpt_engineer/main.py`) +The main script uses the `Typer` library to create a command-line interface. It sets up the AI model and the databases, and then runs a series of steps based on the provided configuration. + +
+ +### 5. Steps (`gpt_engineer/steps.py`) +This module defines a series of steps that can be run in the main script. Each step is a function that takes an instance of the AI class and an instance of the DBs class, and returns a list of messages. + +
+ +The steps include: + +`simple_gen(ai, dbs)`: Run the AI on the main prompt and save the results.
+`clarify(ai, dbs)`: Ask the user if they want to clarify anything and save the results to the workspace.
+`gen_spec(ai, dbs)`: Generate a spec from the main prompt + clarifications and save the results to the workspace.
+`respec(ai, dbs)`: Ask the AI to reiterate the specification and save the results to the workspace.
+`gen_unit_tests(ai, dbs)`: Generate unit tests based on the specification.
+`gen_clarified_code(ai, dbs)`: Generate code based on the main prompt and clarifications.
+`gen_code(ai, dbs)`: Generate code based on the specification and unit tests.
+`execute_entrypoint(ai, dbs)`: Execute the entrypoint script in the workspace.
+`gen_entrypoint(ai, dbs)`: Generate an entrypoint script based on the code in the workspace.
+`use_feedback(ai, dbs)`: Ask the AI to generate code based on feedback.
+`fix_code(ai, dbs)`: Ask the AI to fix any errors in the code.
+ +The steps are grouped into different configurations (default, benchmark, simple, tdd, tdd+, clarify, respec, execute_only, use_feedback), which can be selected when running the main script. diff --git a/gpt-engineer/docs/intro/repository_stats.md b/gpt-engineer/docs/intro/repository_stats.md new file mode 100644 index 0000000000000000000000000000000000000000..d44abf2bc77423a9b67925b9df9063e96b8def0d --- /dev/null +++ b/gpt-engineer/docs/intro/repository_stats.md @@ -0,0 +1,20 @@ +# Repository Overview + +The GPT-Engineer repository is a project developed by Anton Osika, which uses GPT-4 to automate the process of software engineering. The repository is written in Python and is publicly available on GitHub. + +
+ + +## Contributing +Contributions are welcome. If you want to contribute, please check out the [roadmap](https://github.com/gpt-engineer-org/gpt-engineer/blob/main/ROADMAP.md), [projects](https://github.com/gpt-engineer-org/gpt-engineer/projects?query=is%3Aopen) or [issues tab](https://github.com/gpt-engineer-org/gpt-engineer/issues) in the GitHub repo. You can also join the [Discord](https://discord.gg/4t5vXHhu) for discussions. + +
+ +## Example +You can find an example of the project in action [here](https://github.com/gpt-engineer-org/gpt-engineer/assets/4467025/6e362e45-4a94-4b0d-973d-393a31d92d9b). + + diff --git a/gpt-engineer/docs/intro/scripts.md b/gpt-engineer/docs/intro/scripts.md new file mode 100644 index 0000000000000000000000000000000000000000..81ac9dfb0dfc5fff8a1249f25e645238a3c9d2f6 --- /dev/null +++ b/gpt-engineer/docs/intro/scripts.md @@ -0,0 +1,27 @@ +# Scripts +GPT-Engineer includes several Python scripts that provide additional functionality, such as running benchmarks, cleaning benchmarks, printing chat logs, and rerunning edited message logs. These scripts are located in the scripts directory. + +
+ +### 1. Benchmark Script (`scripts/benchmark.py`) +The benchmark script runs a series of benchmarks to evaluate the performance of the GPT-Engineer system. It iterates over all folders in the benchmark directory and runs the benchmark for each folder. The results are saved to a log file in the benchmark folder. + +
+ +### 2. Clean Benchmarks Script (`scripts/clean_benchmarks.py`) +The clean benchmarks script is used to clean up the benchmark folders after running the benchmarks. It iterates over all folders in the benchmark directory and deletes all files and directories except for the main_prompt file. + +
+ +### 3. Print Chat Script (`scripts/print_chat.py`) +The print chat script is used to print a chat conversation in a human-readable format. It takes a JSON file containing a list of messages and prints each message with a color that corresponds to the role of the message sender (system, user, or assistant). + +
+ +### 4. Rerun Edited Message Logs Script (`scripts/rerun_edited_message_logs.py`) +The rerun edited message logs script is used to rerun a conversation with the AI after editing the message logs. It takes a JSON file containing a list of messages, reruns the conversation with the AI, and saves the new messages to an output file. + +
+ +## Conclusion +The scripts included in the GPT-Engineer repository provide additional functionality for running benchmarks, cleaning up after benchmarks, printing chat logs, and rerunning conversations with the AI. They are an integral part of the GPT-Engineer system and contribute to its flexibility and ease of use. diff --git a/gpt-engineer/docs/intro/steps_module.md b/gpt-engineer/docs/intro/steps_module.md new file mode 100644 index 0000000000000000000000000000000000000000..2219ed0b54240ab7385d7d3c5eccb820b3f26493 --- /dev/null +++ b/gpt-engineer/docs/intro/steps_module.md @@ -0,0 +1,54 @@ +# Steps Module +The steps module defines a series of steps that the AI can perform to generate code, clarify instructions, generate specifications, and more. Each step is a function that takes an AI and a set of databases as arguments and returns a list of messages. The steps are defined in the `gpt_engineer/steps.py` file. + +
+ +## Steps +Here are the steps defined in the steps module: + +`setup_sys_prompt(dbs)`: This function sets up the system prompt by combining the generate preprompt and the philosophy preprompt. + +`simple_gen(ai: AI, dbs: DBs)`: This function runs the AI on the main prompt and saves the results. + +`clarify(ai: AI, dbs: DBs)`: This function asks the user if they want to clarify anything and saves the results to the workspace. + +`gen_spec(ai: AI, dbs: DBs)`: This function generates a spec from the main prompt + clarifications and saves the results to the workspace. + +`respec(ai: AI, dbs: DBs)`: This function asks the AI to review a specification for a new feature and give feedback on it. + +`gen_unit_tests(ai: AI, dbs: DBs)`: This function generates unit tests based on the specification. + +`gen_clarified_code(ai: AI, dbs: DBs)`: This function generates code based on the main prompt and clarifications. + +`gen_code(ai: AI, dbs: DBs)`: This function generates code based on the specification and unit tests. + +`execute_entrypoint(ai, dbs)`: This function executes the entrypoint of the generated code. + +`gen_entrypoint(ai, dbs)`: This function generates the entrypoint for the generated code. + +`use_feedback(ai: AI, dbs: DBs)`: This function uses feedback from the user to improve the generated code. + +`fix_code(ai: AI, dbs: DBs)`: This function fixes any errors in the generated code. + +
+ +## Configurations +Different configurations of steps are defined in the STEPS dictionary. Each configuration is a list of steps that are run in order. + +The available configurations are: +```python +DEFAULT: clarify, gen_clarified_code, gen_entrypoint, execute_entrypoint +BENCHMARK: simple_gen, gen_entrypoint +SIMPLE: simple_gen, gen_entrypoint, execute_entrypoint +TDD: gen_spec, gen_unit_tests, gen_code, gen_entrypoint, execute_entrypoint +TDD_PLUS: gen_spec, gen_unit_tests, gen_code, fix_code, gen_entrypoint, execute_entrypoint +CLARIFY: clarify, gen_clarified_code, gen_entrypoint, execute_entrypoint +RESPEC: gen_spec, respec, gen_unit_tests, gen_code, fix_code, gen_entrypoint, execute_entrypoint +USE_FEEDBACK: use_feedback, gen_entrypoint, execute_entrypoint +EXECUTE_ONLY: execute_entrypoint +``` + +
+ +## Conclusion +The steps module provides a flexible framework for running different sequences of steps in the GPT-Engineer system. Each step is a function that performs a specific task, such as generating code, clarifying instructions, or executing the generated code. The steps can be combined in different configurations to achieve different outcomes. diff --git a/gpt-engineer/docs/make.bat b/gpt-engineer/docs/make.bat new file mode 100644 index 0000000000000000000000000000000000000000..d172000568e1e51226ac647fe6e80b36ffdbd7fa --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/docs/open_models.md b/gpt-engineer/docs/open_models.md new file mode 100644 index 0000000000000000000000000000000000000000..07c423b4fe4f5b13dc6818cef90777b709e55608 --- /dev/null +++ b/gpt-engineer/docs/open_models.md @@ -0,0 +1,31 @@ +Using with open/local models +============================ + +You can integrate `gpt-engineer` with open-source models by leveraging an OpenAI-compatible API. One such API is provided by the [text-generator-ui _extension_ openai](https://github.com/oobabooga/text-generation-webui/blob/main/extensions/openai/README.md). + +Setup +----- + +To get started, first set up the API with the Runpod template, as per the [instructions](https://github.com/oobabooga/text-generation-webui/blob/main/extensions/openai/README.md). + +Running the Example +------------------- + +Once the API is set up, you can find the host and the exposed TCP port by checking your Runpod dashboard. + +Then, you can use the port and host to run the following example using WizardCoder-Python-34B hosted on Runpod: + +``` + OPENAI_API_BASE=http://:/v1 python -m gpt_engineer.cli.main benchmark/pomodoro_timer --steps benchmark TheBloke_WizardCoder-Python-34B-V1.0-GPTQ +``` + +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/gpt-engineer/docs/readme_link.rst b/gpt-engineer/docs/readme_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..339dc8f42db24f9f652a9473cb0e15f99e0af48a --- /dev/null +++ b/gpt-engineer/docs/readme_link.rst @@ -0,0 +1,2 @@ +.. include:: ../README.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/results_link.rst b/gpt-engineer/docs/results_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..d407b710493e35185f6ac793e7e63972170475d7 --- /dev/null +++ b/gpt-engineer/docs/results_link.rst @@ -0,0 +1,2 @@ +.. include:: ../benchmark/RESULTS.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/roadmap_link.rst b/gpt-engineer/docs/roadmap_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..90647760eb8cf85b9849420f8254d67c3a4add48 --- /dev/null +++ b/gpt-engineer/docs/roadmap_link.rst @@ -0,0 +1,2 @@ +.. include:: ../ROADMAP.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/tesrms_link.rst b/gpt-engineer/docs/tesrms_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..9738880560a33391acfc582b3a7d2f5bb7af95ab --- /dev/null +++ b/gpt-engineer/docs/tesrms_link.rst @@ -0,0 +1,2 @@ +.. include:: ../TERMS_OF_USE.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/docs/tracing_debugging.md b/gpt-engineer/docs/tracing_debugging.md new file mode 100644 index 0000000000000000000000000000000000000000..745b561098f33d0bd8766a5249fa34d4564a67e4 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/docs/usage.rst b/gpt-engineer/docs/usage.rst new file mode 100644 index 0000000000000000000000000000000000000000..1a1ab9ad5d39ff748816e31d722aa5f6f84e8e83 --- /dev/null +++ b/gpt-engineer/docs/usage.rst @@ -0,0 +1,33 @@ +===== +Usage +===== + +Setup +===== + +With an OpenAI API key (preferably with GPT-4 access) run: + +- `export OPENAI_API_KEY=[your api key]` + +To set API key on windows check the [Windows README](.github/WINDOWS_README.md). + +Run +=== + +- Create an empty folder. If inside the repo, you can run: + - `cp -r projects/example/ projects/my-new-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 [terms](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` + + +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/gpt-engineer/docs/windows_readme_link.rst b/gpt-engineer/docs/windows_readme_link.rst new file mode 100644 index 0000000000000000000000000000000000000000..b0427e4d185a88fee7545ff2d92f2d23ff310cc2 --- /dev/null +++ b/gpt-engineer/docs/windows_readme_link.rst @@ -0,0 +1,2 @@ +.. include:: ../WINDOWS_README.md + :parser: myst_parser.sphinx_ diff --git a/gpt-engineer/gpt_engineer/.vs/ProjectSettings.json b/gpt-engineer/gpt_engineer/.vs/ProjectSettings.json new file mode 100644 index 0000000000000000000000000000000000000000..f8b4888565caadc7510be75682268d6c18edd6de --- /dev/null +++ b/gpt-engineer/gpt_engineer/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/gpt-engineer/gpt_engineer/.vs/VSWorkspaceState.json b/gpt-engineer/gpt_engineer/.vs/VSWorkspaceState.json new file mode 100644 index 0000000000000000000000000000000000000000..f3ce6a5080617f5facacf042d67d931b4bd8aa6a --- /dev/null +++ b/gpt-engineer/gpt_engineer/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "", + "\\benchmark\\benchmarks" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/gpt-engineer/gpt_engineer/.vs/gpt_engineer/FileContentIndex/e3eb95fd-5d23-4244-9f23-373bbe4bc72b.vsidx b/gpt-engineer/gpt_engineer/.vs/gpt_engineer/FileContentIndex/e3eb95fd-5d23-4244-9f23-373bbe4bc72b.vsidx new file mode 100644 index 0000000000000000000000000000000000000000..bbdb7d9c011d6f0b432f7f2cf90729cddeab1bbb Binary files /dev/null and b/gpt-engineer/gpt_engineer/.vs/gpt_engineer/FileContentIndex/e3eb95fd-5d23-4244-9f23-373bbe4bc72b.vsidx differ diff --git a/gpt-engineer/gpt_engineer/.vs/gpt_engineer/v17/.wsuo b/gpt-engineer/gpt_engineer/.vs/gpt_engineer/v17/.wsuo new file mode 100644 index 0000000000000000000000000000000000000000..6e27b3190471add01ca9c0e5071c62397cdc7dd6 Binary files /dev/null and b/gpt-engineer/gpt_engineer/.vs/gpt_engineer/v17/.wsuo differ diff --git a/gpt-engineer/gpt_engineer/.vs/slnx.sqlite b/gpt-engineer/gpt_engineer/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..d3e20d6ffbc029f66c250d5e7ac9be684f5a5834 Binary files /dev/null and b/gpt-engineer/gpt_engineer/.vs/slnx.sqlite differ diff --git a/gpt-engineer/gpt_engineer/__init__.py b/gpt-engineer/gpt_engineer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..518f9f8cc0a779f1db5fa5baa6446561b39aff39 --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/applications/__init__.py b/gpt-engineer/gpt_engineer/applications/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/gpt_engineer/applications/cli/__init__.py b/gpt-engineer/gpt_engineer/applications/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8f720ceb043f61b42f69c114ac9fa521361a436e --- /dev/null +++ b/gpt-engineer/gpt_engineer/applications/cli/__init__.py @@ -0,0 +1,15 @@ +""" +gpt_engineer.cli +----------------- + +The CLI package for the GPT Engineer project, providing the command line interface +for the application. + +Modules: + - main: The primary CLI module for GPT Engineer. + - collect: Collect send learning tools for analysis and improvement. + - file_selector: Selecting files using GUI and terminal-based file explorer. + - learning: Tools and data structures for data collection. + +For more specific details, refer to the docstrings within each module. +""" diff --git a/gpt-engineer/gpt_engineer/applications/cli/cli_agent.py b/gpt-engineer/gpt_engineer/applications/cli/cli_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..6096de7572c3cc0dfc804dd290ca108b9144264c --- /dev/null +++ b/gpt-engineer/gpt_engineer/applications/cli/cli_agent.py @@ -0,0 +1,150 @@ +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, +) +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder + +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 `Agent` class is responsible for managing the lifecycle of code generation and improvement. + + Attributes: + path (str): The file path where the `Agent` will operate, used for version management and + file operations. + version_manager (BaseVersionManager): An object that adheres to the VersionManagerInterface, + responsible for version control of the generated code. Defaults to `VersionManager` + if not provided. PROBABLY GIT SHOULD BE USED IN THE DEFAULT + step_bundle (StepBundleInterface): Workflows of code generation steps that define the behavior of gen_code and + improve. + ai (AI): Manages calls to the LLM. + + Methods: + __init__(self, path: str, version_manager: VersionManagerInterface = None, + step_bundle: StepBundleInterface = None, ai: AI = None): + Initializes a new instance of the Agent class with the provided path, version manager, + step bundle, and AI. It falls back to default instances if specific components are not provided. + + init(self, prompt: str) -> Code: + Generates a new piece of code using the AI and step bundle based on the provided prompt. + It also snapshots the generated code using the version manager. + + Parameters: + prompt (str): A string prompt that guides the code generation process. + + Returns: + FilesDict: An instance of the `Code` class containing the generated code. + + improve(self, prompt: str) -> Code: + Improves an existing piece of code using the AI and step bundle based on the provided prompt. + It also snapshots the improved code using the version manager. + + Parameters: + prompt (str): A string prompt that guides the code improvement process. + + Returns: + FilesDict: An instance of the `Code` class containing the improved code. + """ + + def __init__( + self, + memory: BaseMemory, + execution_env: BaseExecutionEnv, + ai: AI = None, + code_gen_fn: CodeGenType = gen_code, + improve_fn: ImproveType = improve, + 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, + process_code_fn: CodeProcessor = execute_entrypoint, + preprompts_holder: PrepromptsHolder = None, + ): + 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: str) -> FilesDict: + files_dict = self.code_gen_fn( + self.ai, prompt, self.memory, self.preprompts_holder + ) + entrypoint = gen_entrypoint( + self.ai, 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, + ) + return files_dict + + def improve( + self, + files_dict: FilesDict, + prompt: str, + execution_command: Optional[str] = None, + ) -> FilesDict: + files_dict = self.improve_fn( + self.ai, prompt, files_dict, self.memory, self.preprompts_holder + ) + + # No need to run entrypoint for improve right? + # if not execution_command and ENTRYPOINT_FILE not in files_dict: + # entrypoint = gen_entrypoint( + # self.ai, 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, + # ) + return files_dict diff --git a/gpt-engineer/gpt_engineer/applications/cli/collect.py b/gpt-engineer/gpt_engineer/applications/cli/collect.py new file mode 100644 index 0000000000000000000000000000000000000000..a7b8d6fccb1f804e8c9c270e3fe3e923a4af6b88 --- /dev/null +++ b/gpt-engineer/gpt_engineer/applications/cli/collect.py @@ -0,0 +1,165 @@ +""" +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. + +The module provides the following main functions: + +- `send_learning`: Directly send a learning data to RudderStack. +- `collect_learnings`: Extract, possibly adjust, and send the learning data based on + provided input parameters. +- `steps_file_hash`: Computes the SHA-256 hash of the steps file, which might be used + for identifying the exact version or changes in the steps. + +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. + +Note: + 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 + + +def send_learning(learning: Learning): + """ + Send the learning data to RudderStack for analysis. + + Note: + 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 + + Parameters + ---------- + learning : Learning + The learning data to send. + """ + 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: str, + model: str, + temperature: float, + config: any, + memory: DiskMemory, + review: Review, +): + """ + Collect the learning data and send it to RudderStack for analysis. + + Parameters + ---------- + model : str + The name of the model used. + temperature : float + The temperature used. + steps : List[Step] + The list of steps. + dbs : DBs + The database containing the workspace. + """ + 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: str, + model: str, + temperature: float, + config: Tuple[str, ...], + memory: DiskMemory, +): + """ + Collects human feedback on the code and stores it in memory. + + 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. + + Parameters: + - ai (AI): An instance of the AI model. Although not directly used within the function, it is kept as + a parameter for consistency with other functions. + - dbs (DBs): An instance containing the database configurations, user prompts, project metadata, + and memory storage. This function specifically interacts with the memory storage to save the human review. + + Returns: + - list: Returns an empty list, indicating that there's no subsequent interaction with the LLM + or no further messages to be processed. + + Notes: + - It's assumed that the `human_review_input` function handles all the interactions with the user to + gather feedback and returns either the feedback or None if no feedback was provided. + - Ensure that the database's memory has enough space or is set up correctly to store the serialized review data. + """ + + """Collects and stores human review of the code""" + review = human_review_input() + if review: + collect_learnings(prompt, model, temperature, config, memory, review) diff --git a/gpt-engineer/gpt_engineer/applications/cli/file_selector.py b/gpt-engineer/gpt_engineer/applications/cli/file_selector.py new file mode 100644 index 0000000000000000000000000000000000000000..a8a4769533644f61028724ece0714665a6843e69 --- /dev/null +++ b/gpt-engineer/gpt_engineer/applications/cli/file_selector.py @@ -0,0 +1,360 @@ +""" +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 os +import subprocess + +from pathlib import Path +from typing import Any, Dict, 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 + + +class FileSelector: + IGNORE_FOLDERS = {"site-packages", "node_modules", "venv", "__pycache__"} + FILE_LIST_NAME = "file_selection.toml" + COMMENT = ( + "# Remove '#' to select a file.\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" + ) + + def __init__(self, project_path: Union[str, Path]): + 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) -> FilesDict: + """ + Asks the user to select files for the purpose of context improvement. + It supports selection from the terminal or using a previously saved list. + """ + 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") as content: + content_dict[str(file_path)] = content.read() + except FileNotFoundError: + print(f"Warning: File not found {file_path}") + return FilesDict(content_dict) + + def editor_file_selector( + self, input_path: str | Path, init: bool = True + ) -> List[str]: + """ + Provides an interactive file selection interface by generating a tree representation in a .toml file. + Allows users to select or deselect files for the context improvement process. + """ + 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(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}}) + + 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(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): + """ + Attempts to open the specified file using the system's default text editor or a common fallback 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): + """ + Determines if the file is UTF-8 encoded by trying to read and decode it. + Useful for ensuring that files are in a readable and compatible format. + """ + 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, toml_file): + """ + Retrieves the list of files selected by the user from a .toml configuration file. + This function parses the .toml file and returns the list of selected files. + """ + selected_files = [] + edited_tree = toml.load(toml_file) # Load the edited .toml file + + # 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() + all_paths = set( + project_path.joinpath(file).resolve(strict=False) for file in selected_files + ) + + try: + for displayable_path in DisplayablePath.make_tree(project_path): + if displayable_path.path in all_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: list[str], new_files: list[str] + ) -> Dict[str, Any]: + """ + Merges the new files list with the existing one, preserving the selection status. + """ + # 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 get_current_files(self, project_path: Union[str, Path]) -> list[str]: + """ + Generates a dictionary of all files in the project directory + with their selection status set to False by default. + """ + all_files = [] + project_path = Path( + project_path + ).resolve() # Ensure path is absolute and resolved + + for path in project_path.glob("**/*"): # 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 fileso + if any(part in self.IGNORE_FOLDERS for part in parts): + continue + + all_files.append(str(relpath)) + + return all_files + + def is_in_ignoring_extensions(self, path: Path) -> bool: + """ + Check if a path is not hidden or in the '__pycache__' directory. + Helps in filtering out unnecessary files during file selection. + """ + is_hidden = not path.name.startswith(".") + is_pycache = "__pycache__" not in path.name + return is_hidden and is_pycache + + +class DisplayablePath(object): + """ + Represents a path in a file system and displays it in a tree-like structure. + Useful for displaying file and directory structures like in a file explorer. + """ + + 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 + ): + """ + Initialize a DisplayablePath object. + """ + 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 + ): + """ + Generate a tree of DisplayablePath objects, ensure it's only called on directories. + """ + 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: + """ + Get 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/gpt_engineer/applications/cli/learning.py b/gpt-engineer/gpt_engineer/applications/cli/learning.py new file mode 100644 index 0000000000000000000000000000000000000000..b56b7aa7859d83fd4df71f00237f0174145efd61 --- /dev/null +++ b/gpt-engineer/gpt_engineer/applications/cli/learning.py @@ -0,0 +1,247 @@ +""" +This module provides tools and data structures for supporting a feedback loop in the GPT Engineer application. + +The primary intent of this module is to gather feedback from the user on the output of the gpt-engineer tool, +with their consent, and to store this feedback for further analysis and improvement of the tool. + +Classes: +---------- +Review: + Represents user's review of the generated code. +Learning: + Represents the metadata and feedback collected for a session in which gpt-engineer was used. + +Functions: +---------- +human_review_input() -> Review: + Interactively gathers feedback from the user regarding the performance of generated code. + +check_consent() -> bool: + Checks if the user has previously given consent to store their data and if not, asks for it. + +collect_consent() -> bool: + Verifies if the user has given consent to store their data or prompts for it. + +ask_if_can_store() -> bool: + Asks the user if it's permissible to store their data for gpt-engineer improvement. + +logs_to_string(steps: List[Step], logs: DB) -> str: + Converts logs of steps into a readable string format. + +extract_learning(model: str, temperature: float, steps: List[Step], dbs: DBs, steps_file_hash) -> Learning: + Extracts feedback and session details to create a Learning instance. + +get_session() -> str: + Retrieves a unique identifier for the current user session. + +Constants: +---------- +TERM_CHOICES: + Terminal color choices for user interactive prompts. +""" + +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 + + +@dataclass_json +@dataclass +class Review: + ran: Optional[bool] + perfect: Optional[bool] + works: Optional[bool] + comments: str + raw: str + + +@dataclass_json +@dataclass +class Learning: + 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]: + """ + Ask the user to review the generated code and return their review. + + Returns + ------- + Review + The user's review of the generated code. + """ + 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) + while ran not in ("y", "n", "u"): + ran = input("Invalid input. Please enter y, n, or u: ") + + perfect = "" + useful = "" + + if ran == "y": + perfect = input( + "Did the generated code do everything you wanted? " + TERM_CHOICES + ) + while perfect not in ("y", "n", "u"): + perfect = input("Invalid input. Please enter y, n, or u: ") + + if perfect != "y": + useful = input("Did the generated code do anything useful? " + TERM_CHOICES) + while useful not in ("y", "n", "u"): + useful = input("Invalid input. Please enter y, n, or u: ") + + comments = "" + if perfect != "y": + comments = input( + "If you have time, please explain what was not working " + + colored("(ok to leave blank)\n", "light_green") + ) + + 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 check_collection_consent() -> bool: + """ + Check if the user has given consent to store their data. + If not, ask for their consent. + """ + path = Path(".gpte_consent") + if path.exists() and path.read_text() == "true": + return True + else: + return ask_collection_consent() + + +def ask_collection_consent() -> bool: + """ + Ask the user for consent to store their data. + """ + 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: str, + model: str, + temperature: float, + config: Tuple[str, ...], + memory: DiskMemory, + review: Review, +) -> Learning: + """ + Extract the learning data from the steps and databases. + + Parameters + ---------- + model : str + The name of the model used. + temperature : float + The temperature used. + steps : List[Step] + The list of steps. + dbs : DBs + The databases containing the input, logs, memory, and workspace. + steps_file_hash : str + The hash of the steps file. + + Returns + ------- + Learning + The extracted learning data. + """ + learning = Learning( + prompt=prompt, + model=model, + temperature=temperature, + config=json.dumps(config), + session=get_session(), + logs=memory.to_json(), + review=review, + ) + return learning + + +def get_session() -> str: + """ + Returns a unique user id for the current user project (session). + + Returns + ------- + str + The unique user id. + """ + 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/gpt_engineer/applications/cli/main.py b/gpt-engineer/gpt_engineer/applications/cli/main.py new file mode 100644 index 0000000000000000000000000000000000000000..255a64377162e7293b2fd786adac1d213351b819 --- /dev/null +++ b/gpt-engineer/gpt_engineer/applications/cli/main.py @@ -0,0 +1,204 @@ +""" +Entrypoint for the CLI tool. + +Main Functionality: +--------------------- +- Load environment variables needed to work with OpenAI. +- Allow users to specify parameters such as: + - Project path + - LLM + - Temperature + - Step configurations + - Code improvement mode + - Lite mode for lighter operations + - Azure endpoint for Azure OpenAI services + - Using project's preprompts or default ones + - Verbosity level for logging +- Interact with AI, databases, and archive processes based on the user-defined parameters. + +Notes: +- Ensure the .env file has the `OPENAI_API_KEY` or provide it in the working directory. +- The default project path is set to `projects/example`. +- For azure_endpoint, provide the endpoint for Azure OpenAI service. + +""" + +import logging +import os + +from pathlib import Path + +import openai +import typer + +from dotenv import load_dotenv + +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 +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, improve +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.tools.custom_steps import clarified_gen, lite_gen, self_heal + +app = typer.Typer() # creates a CLI app + + +def load_env_if_needed(): + if os.getenv("OPENAI_API_KEY") is None: + load_dotenv() + if os.getenv("OPENAI_API_KEY") is None: + # if there is no .env file, try to load from the current working directory + load_dotenv(dotenv_path=os.path.join(os.getcwd(), ".env")) + openai.api_key = os.getenv("OPENAI_API_KEY") + + +def load_prompt(input_repo: DiskMemory, improve_mode): + if input_repo.get("prompt"): + return input_repo.get("prompt") + + if not improve_mode: + input_repo["prompt"] = input( + "\nWhat application do you want gpt-engineer to generate?\n" + ) + else: + input_repo["prompt"] = input("\nHow do you want to improve the application?\n") + return input_repo.get("prompt") + + +def get_preprompts_path(use_custom_preprompts: bool, input_path: Path) -> Path: + 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 + + +@app.command() +def main( + project_path: str = typer.Argument("projects/example", help="path"), + model: str = typer.Argument("gpt-4-1106-preview", help="model id string"), + temperature: float = 0.1, + improve_mode: bool = typer.Option( + False, + "--improve", + "-i", + help="Improve files_dict from existing project.", + ), + lite_mode: bool = typer.Option( + False, + "--lite", + "-l", + help="Lite mode - run only the main prompt.", + ), + clarify_mode: bool = typer.Option( + False, + "--clarify", + "-c", + help="Lite mode - discuss specification with AI before implementation.", + ), + self_heal_mode: bool = typer.Option( + False, + "--self-heal", + "-sh", + help="Lite mode - discuss specification with AI before implementation.", + ), + 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.""", + ), + verbose: bool = typer.Option(False, "--verbose", "-v"), +): + """ + Generates a project from a prompt in PROJECT_PATH/prompt, + or improves an existing project (with -i) in PROJECT_PATH. + + See README.md for more details. + """ + logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO) + # + + if improve_mode: + assert not ( + clarify_mode or lite_mode + ), "Clarify and lite mode are not active for improve mode" + + load_env_if_needed() + + 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) + + # 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 + + improve_fn = improve + + preprompts_path = get_preprompts_path(use_custom_preprompts, Path(project_path)) + preprompts_holder = PrepromptsHolder(preprompts_path) + memory = DiskMemory(memory_path(project_path)) + 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, + ) + + store = FileStore(project_path) + if improve_mode: + fileselector = FileSelector(project_path) + files_dict = fileselector.ask_for_files() + files_dict = agent.improve(files_dict, prompt) + 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, agent.memory) + + store.upload(files_dict) + + print("Total api cost: $ ", ai.token_usage_log.usage_cost()) + + +if __name__ == "__main__": + app() diff --git a/gpt-engineer/gpt_engineer/benchmark/__main__.py b/gpt-engineer/gpt_engineer/benchmark/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..cc6c09d1b501cd548eba207097cfadeb3394994f --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/__main__.py @@ -0,0 +1,53 @@ +import importlib + +from typing import Annotated, Optional + +import typer + +from langchain.cache import SQLiteCache +from langchain.globals import set_llm_cache + +from gpt_engineer.benchmark.benchmarks.load import get_benchmark +from gpt_engineer.benchmark.run import print_results, run + + +def get_agent(path): + # Dynamically import the python module at path + agent_module = importlib.import_module(path.replace("/", ".").replace(".py", "")) + return agent_module.default_config_agent() + + +def main( + path_to_agent: Annotated[ + str, + typer.Argument( + help="python file that contains a function called 'default_config_agent'" + ), + ], + benchmarks: Annotated[ + str, typer.Argument(help="benchmark name(s) separated by ','") + ], + task_name: Annotated[ + Optional[str], typer.Argument(help="optional task name in benchmark") + ] = None, + verbose: Annotated[ + bool, typer.Option(help="print results for each task", show_default=False) + ] = False, +): + set_llm_cache(SQLiteCache(database_path=".langchain.db")) + + benchmarks = benchmarks.split(",") + for benchmark_name in benchmarks: + benchmark = get_benchmark(benchmark_name) + agent = get_agent(path_to_agent) + + results = run(agent, benchmark, task_name, verbose=verbose) + print( + f"\n--- Results for agent {path_to_agent}, benchmark: {benchmark_name} ---" + ) + print_results(results) + print() + + +if __name__ == "__main__": + typer.run(main) diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/__init__.py b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/eval_tools.py b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/eval_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..0f01502792a442eceed223b506c6df39868ef499 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/eval_tools.py @@ -0,0 +1,78 @@ +""" +This library is used for the evaluation of gpt-engineer's performance, on +editing and creating code. This is very low level in that it looks at the +code written. It is possible that the AI could solve the problem in ways +that we cannot forsee, with this in mind higher level tests are always +better than lower. + +The scope will bre relatively limited to a few languages but this could +be expanded. +""" + + +from gpt_engineer.core.files_dict import FilesDict + +EVAL_LIST_NAME = "evaluations" # the top level list in the YAML file + + +def check_language(eval_d: dict) -> None: + if eval_d["language"] != "python": + raise Exception(f"Language: {eval_d['language']} is not supported.") + + +def assert_exists_in_source_code(eval_d: dict, files_dict: FilesDict) -> bool: + """Checks of some text exists in the source code.""" + source_body = files_dict[eval_d["source_file"]] + return source_body.find(eval_d["existing_string"]) > -1 + + +def run_code_class_has_property(eval_d: dict, files_dict: FilesDict) -> bool: + """Will execute code, then check if the code has the desired proprty.""" + check_language(eval_d) + source_body = files_dict[eval_d["source_file"]] + exec(source_body) + + class_ref = locals().get(eval_d["class_name"]) + ob = class_ref() + return hasattr(ob, eval_d["property_name"]) + + +def run_code_class_has_property_w_value(eval_d: dict, files_dict: FilesDict) -> bool: + """Will execute code, then check if the code has the desired proprty.""" + check_language(eval_d) + source_body = files_dict[eval_d["source_file"]] + exec(source_body) + + class_ref = locals().get(eval_d["class_name"]) + ob = class_ref() + + assert hasattr(ob, eval_d["property_name"]) + + return getattr(ob, eval_d["property_name"]) == eval_d["expected_value"] + + +def run_code_eval_function(eval_d: dict, files_dict: FilesDict) -> bool: + """Similar to run_code_class_has_property() except is evaluates a function call.""" + check_language(eval_d) + source_body = files_dict[eval_d["source_file"]] + exec(source_body) + function_ref = globals().get(eval_d["function_name"]) + + # TODO: add the ability to have function arguments + return function_ref() == eval_d["expected_value"] + + +def check_evaluation_component(eval_d: dict, files_dict: FilesDict) -> bool: + """Switch on evaluation components""" + test_type = eval_d.get("type") + if test_type == "assert_exists_in_source_code": + return assert_exists_in_source_code(eval_d, files_dict) + elif test_type == "run_code_class_has_property": + return run_code_class_has_property(eval_d, files_dict) + elif test_type == "run_code_class_has_property_w_value": + return run_code_class_has_property_w_value(eval_d, files_dict) + elif test_type == "run_code_eval_function": + return run_code_eval_function(eval_d, files_dict) + # The following are for new code + else: + raise Exception(f"Test type '{test_type}' is not recognized.") diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/EVAL_NEW_CODE_RESULTS.md b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/EVAL_NEW_CODE_RESULTS.md new file mode 100644 index 0000000000000000000000000000000000000000..3d60a0da41c9daa2785852b690425b266fdfacf7 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/EVAL_NEW_CODE_RESULTS.md @@ -0,0 +1,31 @@ +## 2023-09-15 + +### New Code Evaluation Summary: + +| Project | Evaluation | All Tests Pass | +|:---------------------------|:-------------|:-----------------| +| projects/password_gen_eval | password_gen | ✅ | + +### Detailed Test Results: + +| Project | Evaluation | Test | Pass | +|:---------------------------|:-------------|:------------------------------------|:-------| +| projects/password_gen_eval | password_gen | check_executable_exits_normally | ✅ | +| projects/password_gen_eval | password_gen | check_executable_satisfies_function | ✅ | +## 2023-09-18 + +### Existing Code Evaluation Summary: + +| Project | Evaluation | All Tests Pass | +|:----------------------------|:-------------------|:-----------------| +| projects/currency_converter | currency_converter | ✅ | +| projects/password_gen_eval | password_gen | ✅ | + +### Detailed Test Results: + +| Project | Evaluation | Test | Pass | +|:----------------------------|:-------------------|:------------------------------------|:-------| +| projects/currency_converter | currency_converter | check_executable_exits_normally | ✅ | +| projects/currency_converter | currency_converter | check_executable_satisfies_function | ✅ | +| projects/password_gen_eval | password_gen | check_executable_exits_normally | ✅ | +| projects/password_gen_eval | password_gen | check_executable_satisfies_function | ✅ | diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/IMPROVE_CODE_RESULTS.md b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/IMPROVE_CODE_RESULTS.md new file mode 100644 index 0000000000000000000000000000000000000000..55fa1758d62eff5505d153522ef1735bf961c7ae --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/IMPROVE_CODE_RESULTS.md @@ -0,0 +1,65 @@ +## 2023-08-24 + +### Existing Code Evaluation Summary: + +| Project | Evaluation | All Tests Pass | +|:-------------------------|:-------------------|:-----------------| +| projects/snake_game_eval | simple_code_modify | ✅ | + +### Detailed Test Results: + +| Project | Evaluation | Test | Pass | +|:-------------------------|:-------------------|:------------------------------------|:-------| +| projects/snake_game_eval | simple_code_modify | assert_exists_in_source_code | ✅ | +| projects/snake_game_eval | simple_code_modify | assert_exists_in_source_code | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property_w_value | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property_w_value | ✅ | + +## 2023-08-25 + +### Existing Code Evaluation Summary: + +| Project | Evaluation | All Tests Pass | +|:-------------------------|:--------------------------|:-----------------| +| projects/snake_game_eval | simple_code_modify | ✅ | +| projects/web_todo_list | modify_web_app_appearance | ✅ | +| projects/snake_game_eval | modify_functionality | ❌ | + +### Detailed Test Results: + +| Project | Evaluation | Test | Pass | +|:-------------------------|:--------------------------|:------------------------------------|:-------| +| projects/snake_game_eval | simple_code_modify | assert_exists_in_source_code | ✅ | +| projects/snake_game_eval | simple_code_modify | assert_exists_in_source_code | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property_w_value | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property_w_value | ✅ | +| projects/web_todo_list | modify_web_app_appearance | assert_exists_in_source_code | ✅ | +| projects/web_todo_list | modify_web_app_appearance | assert_exists_in_source_code | ✅ | +| projects/snake_game_eval | modify_functionality | assert_exists_in_source_code | ❌ | +## 2023-09-15 + +### Existing Code Evaluation Summary: + +| Project | Evaluation | All Tests Pass | +|:-------------------------|:--------------------------|:-----------------| +| projects/snake_game_eval | simple_code_modify | ❌ | +| projects/web_todo_list | modify_web_app_appearance | ❌ | +| projects/snake_game_eval | modify_functionality | ❌ | + +### Detailed Test Results: + +| Project | Evaluation | Test | Pass | +|:-------------------------|:--------------------------|:------------------------------------|:-------| +| projects/snake_game_eval | simple_code_modify | assert_exists_in_source_code | ❌ | +| projects/snake_game_eval | simple_code_modify | assert_exists_in_source_code | ❌ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property | ✅ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property_w_value | ❌ | +| projects/snake_game_eval | simple_code_modify | run_code_class_has_property_w_value | ❌ | +| projects/web_todo_list | modify_web_app_appearance | assert_exists_in_source_code | ❌ | +| projects/web_todo_list | modify_web_app_appearance | assert_exists_in_source_code | ❌ | +| projects/snake_game_eval | modify_functionality | assert_exists_in_source_code | ❌ | diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/README.md b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/README.md new file mode 100644 index 0000000000000000000000000000000000000000..58c5114203bf921027c8f9106d53f9fe88ba8bf0 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/evals/README.md @@ -0,0 +1,15 @@ + + +# Evals + +Evals are a set of tests that allow us to measure the performance of the gpt-engineer whole system. This includes the gpt-enginer code, options and the chosen LLM. + +### Running Evals + +To run the existing code evals make sure you are in the gpt-engineer top level directory (you should see a directory called `evals`) type: + +`python evals/evals_existing_code.py` This will run the default test file: `evals/existing_code_eval.yaml`, or you can run any YAML file of tests you wish with the command: `python evals/evals_existing_code.py your_test_file.yaml` + +Similarly to run the new code evals type: + +`python evals/evals_new_code.py` diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/snake_game_files.txt b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/snake_game_files.txt new file mode 100644 index 0000000000000000000000000000000000000000..ad3256adb3b88c49476eaad7350ff7960be6374c --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/snake_game_files.txt @@ -0,0 +1,129 @@ + +main.py +```python +from game import Game + +def main(): + game = Game() + game.run() + +if __name__ == "__main__": + main() +``` + +game.py +```python +from snake import Snake +from food import Food +from grid import Grid +from controller import Controller +from view import View + +class Game: + def __init__(self): + self.snake = Snake() + self.food = Food() + self.grid = Grid() + self.controller = Controller() + self.view = View() + + def run(self): + while True: + self.controller.read_input() + self.snake.move(self.controller.direction) + if self.snake.eat(self.food): + self.snake.grow() + self.food.place() + if self.snake.collide(self.grid): + break + self.view.draw(self.grid, self.snake, self.food) +``` + +snake.py +```python +class Snake: + def __init__(self): + self.body = [(0, 0)] + self.direction = (0, 1) + + def move(self, direction): + self.direction = direction + head = self.body[0] + new_head = (head[0] + direction[0], head[1] + direction[1]) + self.body.insert(0, new_head) + self.body.pop() + + def grow(self): + self.body.append(self.body[-1]) + + def eat(self, food): + return self.body[0] == food.position + + def collide(self, grid): + head = self.body[0] + return head in self.body[1:] or not (0 <= head[0] < grid.width and 0 <= head[1] < grid.height) +``` + +food.py +```python +import random + +class Food: + def __init__(self, width=10, height=10): + self.width = width + self.height = height + self.position = (0, 0) + self.place() + + def place(self): + self.position = (random.randint(0, self.width-1), random.randint(0, self.height-1)) +``` + +grid.py +```python +class Grid: + def __init__(self, width=10, height=10): + self.width = width + self.height = height +``` + +controller.py +```python +import msvcrt + +class Controller: + def __init__(self): + self.direction = (0, 1) + + def read_input(self): + key = msvcrt.getch() + if key == b'w': + self.direction = (-1, 0) + elif key == b's': + self.direction = (1, 0) + elif key == b'a': + self.direction = (0, -1) + elif key == b'd': + self.direction = (0, 1) +``` + +view.py +```python +class View: + def draw(self, grid, snake, food): + for i in range(grid.height): + for j in range(grid.width): + if (i, j) in snake.body: + print('S', end='') + elif (i, j) == food.position: + print('F', end='') + else: + print('.', end='') + print() + print() +``` + +requirements.txt +```plaintext +msvcrt==1.0 +``` diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/web_todo_files.txt b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/web_todo_files.txt new file mode 100644 index 0000000000000000000000000000000000000000..46333cd48db8acebe0ed781f426d4dd04c3ce992 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/web_todo_files.txt @@ -0,0 +1,108 @@ + +index.html +```html + + + + + + To-Do List App + + + +

To-Do List

+
+ + +
+
    + + + +``` + +Next, we have the `styles.css` file: + +styles.css +```css +body { + font-family: Arial, sans-serif; +} + +#task-form { + margin-bottom: 20px; +} + +#task-input { + margin-right: 10px; +} + +#task-list { + list-style-type: none; +} +``` + +Finally, we have the `app.js` file: + +app.js +```javascript +document.addEventListener('DOMContentLoaded', () => { + const taskForm = document.getElementById('task-form'); + const taskInput = document.getElementById('task-input'); + const taskList = document.getElementById('task-list'); + + taskForm.addEventListener('submit', event => { + event.preventDefault(); + addTask(taskInput.value); + taskInput.value = ''; + }); + + taskList.addEventListener('click', event => { + if (event.target.tagName === 'BUTTON') { + if (event.target.textContent === 'Edit') { + editTask(event.target.parentElement); + } else if (event.target.textContent === 'Delete') { + deleteTask(event.target.parentElement); + } + } + }); + + function addTask(task) { + const listItem = document.createElement('li'); + listItem.textContent = task; + const editButton = document.createElement('button'); + editButton.textContent = 'Edit'; + const deleteButton = document.createElement('button'); + deleteButton.textContent = 'Delete'; + listItem.appendChild(editButton); + listItem.appendChild(deleteButton); + taskList.appendChild(listItem); + saveTasks(); + } + + function editTask(listItem) { + const task = prompt('Edit the task', listItem.firstChild.textContent); + listItem.firstChild.textContent = task; + saveTasks(); + } + + function deleteTask(listItem) { + taskList.removeChild(listItem); + saveTasks(); + } + + function saveTasks() { + const tasks = Array.from(taskList.children).map(listItem => listItem.firstChild.textContent); + localStorage.setItem('tasks', JSON.stringify(tasks)); + } + + function loadTasks() { + const tasks = JSON.parse(localStorage.getItem('tasks')); + if (tasks) { + tasks.forEach(task => addTask(task)); + } + } + + loadTasks(); +}); +``` diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/load.py b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/load.py new file mode 100644 index 0000000000000000000000000000000000000000..e742e765f9049bc038b4b1bff0f3108224bd2e00 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gpteng/load.py @@ -0,0 +1,161 @@ +from pathlib import Path + +from gpt_engineer.benchmark.benchmarks.gpteng.eval_tools import ( + check_evaluation_component, +) +from gpt_engineer.benchmark.types import Assertable, Benchmark, Task +from gpt_engineer.core.chat_to_files import chat_to_files_dict + +evaluations = [ + { + "name": "simple_code_modify", + "project_root": "projects/snake_game_eval", + "code_blob": "gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/snake_game_files.txt", + "improve_code_prompt": "The grid is currently 10x10, change the grid to be 42x42.", + "expected_results": [ + { + "type": "assert_exists_in_source_code", + "source_file": "grid.py", + "existing_string": "width=42", + }, + { + "type": "assert_exists_in_source_code", + "source_file": "grid.py", + "existing_string": "height=42", + }, + { + "type": "run_code_class_has_property", + "language": "python", + "source_file": "grid.py", + "class_name": "Grid", + "property_name": "height", + }, + { + "type": "run_code_class_has_property", + "language": "python", + "source_file": "grid.py", + "class_name": "Grid", + "property_name": "width", + }, + { + "type": "run_code_class_has_property_w_value", + "language": "python", + "source_file": "grid.py", + "class_name": "Grid", + "property_name": "height", + "expected_value": 42, + }, + { + "type": "run_code_class_has_property_w_value", + "language": "python", + "source_file": "grid.py", + "class_name": "Grid", + "property_name": "width", + "expected_value": 42, + }, + ], + }, + { + "name": "modify_web_app_appearance", + "project_root": "projects/web_todo_list", + "code_blob": "gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/web_todo_files.txt", + "improve_code_prompt": "Fix the margins around the form to be 45px, and make the background color orange.", + "expected_results": [ + { + "type": "assert_exists_in_source_code", + "source_file": "styles.css", + "existing_string": "#task-form {\\n margin: 45px;", + }, + { + "type": "assert_exists_in_source_code", + "source_file": "styles.css", + "existing_string": "background-color: orange;", + }, + ], + }, + { + "name": "modify_functionality", + "project_root": "projects/snake_game_eval", + "code_blob": "gpt_engineer/benchmark/benchmarks/gpteng/known_code_blobs/snake_game_files.txt", + "improve_code_prompt": "Add a 2 second delay before the game starts.", + "expected_results": [ + { + "type": "assert_exists_in_source_code", + "source_file": "game.py", + "existing_string": "time.sleep(2)", + } + ], + }, +] + +# Not supporting execution paths that used to exist +# evaluations = [ +# { +# "name": "currency_converter", +# "project_root": "projects/currency_converter", +# "code_prompt": "Build a currency converter CLI tool in Python using an API for exchange rates. The currency converter should be a python program named currency.py with three required arguments: base currency symbol, target currency symbol and base currency amount. The currency converter will convert the amount in base currency amount to the target currency. The output of the program should only be the amount of target currency. For example the following command: `python currency.py USD CNY 1` should return a number like 7.5.", +# "expected_results": [ +# { +# "type": "check_executable_exits_normally", +# "executable_name": "python currency.py", +# "executable_arguments": "USD CAD 10" +# }, +# { +# "type": "check_executable_satisfies_function", +# "executable_name": "python currency.py", +# "executable_arguments": "USD CAD 10", +# "output_satisfies": "tf = lambda a : a.replace('.', '').isnumeric()" +# } +# ] +# }, +# { +# "name": "password_gen", +# "project_root": "projects/password_gen_eval", +# "code_prompt": "Create a password generator CLI tool in Python that generates strong, random passwords based on user-specified criteria, such as length and character types (letters, numbers, symbols). The password generator should be a python program named passwordgenerator.py with two arguments: length, and character types. The character types argument can be one or more of the the following: l for lowercase, u for uppercase, d for digits, and s for symbols.", +# "expected_results": [ +# { +# "type": "check_executable_exits_normally", +# "executable_name": "python passwordgenerator.py", +# "executable_arguments": "10 d" +# }, +# { +# "type": "check_executable_satisfies_function", +# "executable_name": "python passwordgenerator.py", +# "executable_arguments": "10 d", +# "output_satisfies": "tf = lambda a : len(a) == 10" +# } +# ] +# } +# ] +# + + +def expect_to_assertion(expected_result): + def assertion(assertable: Assertable): + return check_evaluation_component(expected_result, assertable.files) + + return assertion + + +def eval_to_task(case): + if "improve_code_prompt" in case: + prompt = case["improve_code_prompt"] + else: + prompt = case["code_prompt"] + + return Task( + name=case["name"], + initial_code=chat_to_files_dict(Path(case["code_blob"]).read_text()), + prompt=prompt, + command=None, + assertions={ + f"{e['type']}_{i}": expect_to_assertion(e) + for i, e in enumerate(case["expected_results"]) + }, + ) + + +def load_gpteng(): + return Benchmark( + name="gpte_eval", tasks=[eval_to_task(case) for case in evaluations] + ) diff --git a/gpt-engineer/gpt_engineer/benchmark/benchmarks/gptme/load.py b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gptme/load.py new file mode 100644 index 0000000000000000000000000000000000000000..b74d0afa3f5d4a157dcfe590e8f017a9469dd711 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/gptme/load.py @@ -0,0 +1,76 @@ +from gpt_engineer.benchmark.types import Benchmark, Task +from gpt_engineer.core.files_dict import FilesDict + + +def load_gptme(): + return Benchmark( + name="gptme", + tasks=[ + Task( + name="hello", + initial_code=FilesDict({"hello.py": "print('Hello, world!')"}), + command="python hello.py", + 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="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="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="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="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/gpt_engineer/benchmark/benchmarks/load.py b/gpt-engineer/gpt_engineer/benchmark/benchmarks/load.py new file mode 100644 index 0000000000000000000000000000000000000000..85d944eb34be1a0eed38a5e1189b494b31594a10 --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/benchmarks/load.py @@ -0,0 +1,14 @@ +from gpt_engineer.benchmark.benchmarks.gpteng.load import load_gpteng +from gpt_engineer.benchmark.benchmarks.gptme.load import load_gptme +from gpt_engineer.benchmark.types import Benchmark + +BENCHMARKS = { + "gptme": load_gptme, + "gpteng": load_gpteng, +} + + +def get_benchmark(name: str) -> Benchmark: + if name not in BENCHMARKS: + raise ValueError(f"Unknown benchmark {name}.") + return BENCHMARKS[name]() diff --git a/gpt-engineer/gpt_engineer/benchmark/run.py b/gpt-engineer/gpt_engineer/benchmark/run.py new file mode 100644 index 0000000000000000000000000000000000000000..277b95d9450baf424d95dbe6337237fd5c9dea6b --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/run.py @@ -0,0 +1,86 @@ +import time + +from typing import List, Optional + +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, + task_name: Optional[str] = None, + verbose=False, +) -> List[TaskResult]: + task_results = [] + for task in benchmark.tasks: + t0 = time.time() + files_dict = agent.improve(task.initial_code, task.prompt, task.command) + 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]): + 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() + + total_time = sum(task_result.duration for task_result in results) + print(f"Total time: {total_time:.2f}s") + + 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 + ) + print(f"Total correct assertions: {correct_assertions}/{total_assertions}") + + correct_tasks = sum( + all( + assertion_result + for assertion_result in task_result.assertion_results.values() + ) + for task_result in results + ) + print(f"Correct tasks: {correct_tasks}/{len(results)}") + print() diff --git a/gpt-engineer/gpt_engineer/benchmark/types.py b/gpt-engineer/gpt_engineer/benchmark/types.py new file mode 100644 index 0000000000000000000000000000000000000000..d8357c9320daa71003837bced1c1ee179b1535ef --- /dev/null +++ b/gpt-engineer/gpt_engineer/benchmark/types.py @@ -0,0 +1,54 @@ +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 + + +@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: str + 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 diff --git a/gpt-engineer/gpt_engineer/core/__init__.py b/gpt-engineer/gpt_engineer/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/gpt_engineer/core/ai.py b/gpt-engineer/gpt_engineer/core/ai.py new file mode 100644 index 0000000000000000000000000000000000000000..c8eb474176cfed7251d33f2d37c8078a650f5111 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/ai.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +import json +import logging +import os + +from typing import List, Optional, Union + +import backoff +import openai + +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_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: + def __init__( + self, + model_name="gpt-4-1106-preview", + temperature=0.1, + azure_endpoint="", + streaming=True, + ): + """ + 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.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: str, 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 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. + """ + """ + Advances the conversation by sending message history + to LLM and updating with the response. + """ + if prompt: + messages.append(HumanMessage(content=prompt)) + + logger.debug(f"Creating a new chat completion: {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( + openai_api_base=self.azure_endpoint, + openai_api_version=os.getenv("OPENAI_API_VERSION", "2023-05-15"), + deployment_name=self.model_name, + openai_api_type="azure", + streaming=self.streaming, + callbacks=[StreamingStdOutCallbackHandler()], + ) + + 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) diff --git a/gpt-engineer/gpt_engineer/core/base_agent.py b/gpt-engineer/gpt_engineer/core/base_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..2eb5b280f1e7a3dba58e2fb7a5359a42151163cf --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/base_agent.py @@ -0,0 +1,28 @@ +from abc import ABC, abstractmethod + +from gpt_engineer.core.files_dict import FilesDict + + +class BaseAgent(ABC): + """ + Abstract base class for an agent that interacts with code. + + This class 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. + + Methods + ------- + init(prompt: str) -> Code: + Initialize a new piece of code based on the given prompt. + improve(prompt: str, code: Code) -> Code: + Improve an existing piece of code based on the given prompt. + """ + + @abstractmethod + def init(self, prompt: str) -> FilesDict: + pass + + @abstractmethod + def improve(self, files_dict: FilesDict, prompt: str) -> FilesDict: + pass diff --git a/gpt-engineer/gpt_engineer/core/base_execution_env.py b/gpt-engineer/gpt_engineer/core/base_execution_env.py new file mode 100644 index 0000000000000000000000000000000000000000..6f94553a4d755189d672d758bd177523c8fac3dc --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/base_execution_env.py @@ -0,0 +1,48 @@ +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. + + This class defines the interface for execution environments that can run code. + Implementations of this class are expected to provide a method to execute code + and potentially handle the execution process. + + Methods + ------- + execute_program(code: Code) -> Popen: + Execute the given code and return the process handle. + """ + + @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/gpt_engineer/core/base_memory.py b/gpt-engineer/gpt_engineer/core/base_memory.py new file mode 100644 index 0000000000000000000000000000000000000000..7d8251049458f610277ba3958ef24f785b65a636 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/base_memory.py @@ -0,0 +1,4 @@ +from pathlib import Path +from typing import MutableMapping, Union + +BaseMemory = MutableMapping[Union[str, Path], str] diff --git a/gpt-engineer/gpt_engineer/core/chat_to_files.py b/gpt-engineer/gpt_engineer/core/chat_to_files.py new file mode 100644 index 0000000000000000000000000000000000000000..3f5fe7e6c6ff75efe51a02594592ad707b4bf207 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/chat_to_files.py @@ -0,0 +1,196 @@ +""" +This module provides utilities to handle and process chat content, especially for extracting code blocks +and managing them within a specified GPT Engineer project ("workspace"). It offers functionalities like parsing chat messages to +retrieve code blocks, storing these blocks into a workspace, and overwriting workspace content based on +new chat messages. Moreover, it aids in formatting and reading file content for an AI agent's input. + +Key Features: +- Parse and extract code blocks from chat messages. +- Store and overwrite files within a workspace based on chat content. +- Format files to be used as inputs for AI agents. +- Retrieve files and their content based on a provided list. + +Dependencies: +- `os` and `pathlib`: For handling OS-level operations and path manipulations. +- `re`: For regex-based parsing of chat content. +- `gpt_engineer.core.db`: Database handling functionalities for the workspace. +- `gpt_engineer.cli.file_selector`: Constants related to file selection. + +Functions: +- parse_chat: Extracts code blocks from chat messages. +- to_files_and_memory: Saves chat content to memory and adds extracted files to a workspace. +- to_files: Adds extracted files to a workspace. +- get_code_strings: Retrieves file names and their content. +- format_file_to_input: Formats file content for AI input. +- overwrite_files_with_edits: Overwrites workspace files based on parsed edits from chat. +- apply_edits: Applies file edits to a workspace. +""" + +import logging +import re + +from dataclasses import dataclass +from typing import List + +from gpt_engineer.core.files_dict import FilesDict + +logger = logging.getLogger(__name__) + + +def chat_to_files_dict(chat) -> FilesDict: + """ + Extracts all code blocks from a chat and returns them + as a list of (filename, codeblock) tuples. + + Parameters + ---------- + chat : str + The chat to extract code blocks from. + + Returns + ------- + List[Tuple[str, str]] + A list of tuples, where each tuple contains a filename and a code block. + """ + # Get all ``` blocks and preceding filenames + regex = r"(\S+)\n\s*```[^\n]*\n(.+?)```" + matches = re.finditer(regex, chat, re.DOTALL) + + files_dict = FilesDict() + for match in matches: + # Strip the filename of any non-allowed characters and convert / to \ + path = re.sub(r'[\:<>"|?*]', "", match.group(1)) + + # Remove leading and trailing brackets + path = re.sub(r"^\[(.*)\]$", r"\1", path) + + # Remove leading and trailing backticks + path = re.sub(r"^`(.*)`$", r"\1", path) + + # Remove trailing ] + path = re.sub(r"[\]\:]$", "", path) + + # Get the code + content = match.group(2) + + # Add the file to the list + files_dict[path.strip()] = content.strip() + + return FilesDict(files_dict) + + +def overwrite_code_with_edits(chat: str, files_dict: FilesDict): + """ + Overwrite code with edits extracted from chat. + + This function takes a chat string, parses it for edits, and applies those edits + to the provided code object. + + Parameters + ---------- + chat : str + The chat content containing code edits. + files_dict : FilesDict + The code object to apply edits to. + """ + edits = parse_edits(chat) + apply_edits(edits, files_dict) + + +@dataclass +class Edit: + filename: str + before: str + after: str + + +def parse_edits(chat: str): + """ + Parse edits from a chat string. + + This function extracts code edits from a chat string and returns them as a list + of Edit objects. + + Parameters + ---------- + chat : str + The chat content containing code edits. + + Returns + ------- + List[Edit] + A list of Edit objects representing the parsed code edits. + """ + + def parse_one_edit(lines): + HEAD = "<<<<<<< HEAD" + DIVIDER = "\n=======\n" + UPDATE = ">>>>>>> updated" + + filename = lines.pop(0) + text = "\n".join(lines) + splits = text.split(DIVIDER) + if len(splits) != 2: + raise ValueError(f"Could not parse following text as code edit: \n{text}") + before, after = splits + + before = before.replace(HEAD, "").strip() + after = after.replace(UPDATE, "").strip() + + return Edit(filename, before, after) + + edits = [] + current_edit = [] + in_fence = False + + for line in chat.split("\n"): + if line.startswith("```") and in_fence: + edits.append(parse_one_edit(current_edit)) + current_edit = [] + in_fence = False + continue + elif line.startswith("```") and not in_fence: + in_fence = True + continue + + if in_fence: + current_edit.append(line) + + return edits + + +def apply_edits(edits: List[Edit], files_dict: FilesDict): + """ + Apply a list of edits to the given code. + + This function takes a list of Edit objects and applies each edit to the code object. + It handles the creation of new files and the modification of existing files based on the edits. + + Parameters + ---------- + edits : List[Edit] + A list of Edit objects representing the code edits to apply. + files_dict : FilesDict + The code object to apply edits to. + """ + for edit in edits: + filename = edit.filename + if edit.before == "": + if filename in files_dict: + logger.warning( + f"The edit to be applied wants to create a new file `{filename}`, but that already exists. The file will be overwritten. See `.gpteng/memory` for previous version." + ) + files_dict[filename] = edit.after # new file + else: + occurrences_cnt = files_dict[filename].count(edit.before) + if occurrences_cnt == 0: + logger.warning( + f"While applying an edit to `{filename}`, the code block to be replaced was not found. No instances will be replaced." + ) + if occurrences_cnt > 1: + logger.warning( + f"While applying an edit to `{filename}`, the code block to be replaced was found multiple times. All instances will be replaced." + ) + files_dict[filename] = files_dict[filename].replace( + edit.before, edit.after + ) # existing file diff --git a/gpt-engineer/gpt_engineer/core/default/__init__.py b/gpt-engineer/gpt_engineer/core/default/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/gpt_engineer/core/default/constants.py b/gpt-engineer/gpt_engineer/core/default/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..153a32beff4f7bfc693225f941e854a4a08ff6d9 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/constants.py @@ -0,0 +1 @@ +MAX_EDIT_REFINEMENT_STEPS = 1 diff --git a/gpt-engineer/gpt_engineer/core/default/disk_execution_env.py b/gpt-engineer/gpt_engineer/core/default/disk_execution_env.py new file mode 100644 index 0000000000000000000000000000000000000000..a0e1a4c86d9aedd67f2ec1fc64c370e7fa8f41c5 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/disk_execution_env.py @@ -0,0 +1,83 @@ +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. + + 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: + path (str): The file system path where the code is located and will be executed. + """ + + def __init__(self, path: Union[str, Path, None] = None): + self.store = FileStore(path) + + def upload(self, files: FilesDict) -> "DiskExecutionEnv": + self.store.upload(files) + return self + + def download(self) -> FilesDict: + return self.store.download() + + def popen(self, command: str) -> subprocess.Popen: + p = subprocess.Popen( + command, + shell=True, + cwd=self.store.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.store.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/gpt_engineer/core/default/disk_memory.py b/gpt-engineer/gpt_engineer/core/default/disk_memory.py new file mode 100644 index 0000000000000000000000000000000000000000..76dd3eae9e91f07289f5630230438496fc645ce1 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/disk_memory.py @@ -0,0 +1,254 @@ +""" +Module for simple file-based key-value database management. + +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 primary class, +DB, is responsible for the CRUD operations on the database. Additionally, the module +provides a dataclass `DBs` that encapsulates multiple `DB` instances to represent different +databases like memory, logs, preprompts, etc. + +Functions: + archive(dbs: DBs) -> None: + Archives the memory and workspace databases, moving their contents to + the archive database with a timestamp. + +Classes: + DB: + A simple key-value store implemented as a file-based system. + + DBs: + A dataclass containing multiple DB instances representing different databases. + +Imports: + - datetime: For timestamp generation when archiving. + - shutil: For moving directories during archiving. + - dataclasses: For the DBs dataclass definition. + - pathlib: For path manipulations. + - typing: For type annotations. +""" + +import json +import shutil + +from pathlib import Path +from typing import Any, Dict, Iterator, Optional, Union + +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.tools.experimental.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. + """ + + """ + 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. + + Methods + ------- + __contains__(key: str) -> bool: + Check if a file (key) exists in the database. + + __getitem__(key: str) -> str: + Retrieve the content of a file (value) based on its name (key). + + get(key: str, default: Optional[Any] = None) -> Any: + Fetch content of a file or return a default value if it doesn't exist. + + __setitem__(key: Union[str, Path], val: str): + Set or update the content of a file in the database. + + Note: + ----- + Care should be taken when choosing keys (filenames) to avoid potential + security issues, such as directory traversal. The class implements some checks + for this but it's essential to validate inputs from untrusted sources. + """ + + """A simple key-value store, where keys are filenames and values are file contents.""" + + def __init__(self, path: Union[str, Path]): + """ + Initialize the DB class. + + Parameters + ---------- + path : Union[str, Path] + The path to the directory where the database files are stored. + """ + self.path: Path = Path(path).absolute() + + self.path.mkdir(parents=True, exist_ok=True) + + def __contains__(self, key: str) -> bool: + """ + Check if a file with the specified name exists in the database. + + Parameters + ---------- + key : str + The name of the file to check. + + Returns + ------- + bool + True if the file exists, False otherwise. + """ + return (self.path / key).is_file() + + def __getitem__(self, key: str) -> str: + """ + Get the content of a file in the database. + + Parameters + ---------- + key : str + The name of the file to get the content of. + + Returns + ------- + str + The content of the file. + + Raises + ------ + KeyError + If the file 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}'") + with full_path.open("r", encoding="utf-8") as f: + return f.read() + + def get(self, key: str, default: Optional[Any] = None) -> Any: + """ + Get the content of a file in the database, or a default value if the file does not exist. + + Parameters + ---------- + key : str + The name of the file to get the content of. + default : any, optional + The default value to return if the file does not exist, by default None. + + Returns + ------- + any + The content of the file, or the default value if the file does not exist. + """ + try: + return self[key] + except KeyError: + return default + + def __setitem__(self, key: Union[str, Path], val: str) -> None: + """ + Set the content of a file in the database. + + Parameters + ---------- + key : Union[str, Path] + The name of the file to set the content of. + val : str + The content to set. + + Raises + ------ + TypeError + If val is not 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 in the database. + + Parameters + ---------- + key : Union[str, Path] + The name of the file or directory to delete. + + Raises + ------ + KeyError + If the file or directory 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]: + return iter( + sorted( + str(item.relative_to(self.path)) + for item in sorted(self.path.rglob("*")) + if item.is_file() + ) + ) + + def __len__(self): + 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: + """ + Returns directory as a list of file paths. Useful for passing to the LLM where it needs to understand the wider context of files available for reference. + """ + if supported_code_files_only: + return self._supported_files() + else: + return self._all_files() + + def to_dict(self) -> Dict[Union[str, Path], str]: + return {file_path: self[file_path] for file_path in self} + + def to_json(self) -> str: + return json.dumps(self.to_dict()) diff --git a/gpt-engineer/gpt_engineer/core/default/file_store.py b/gpt-engineer/gpt_engineer/core/default/file_store.py new file mode 100644 index 0000000000000000000000000000000000000000..9aa576f9d92aed04c3e535b98aeaf6a50697addc --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/file_store.py @@ -0,0 +1,36 @@ +import tempfile + +from pathlib import Path +from typing import Union + +from gpt_engineer.core.files_dict import FilesDict + + +class FileStore: + 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 upload(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 download(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/gpt_engineer/core/default/paths.py b/gpt-engineer/gpt_engineer/core/default/paths.py new file mode 100644 index 0000000000000000000000000000000000000000..94dd80e1d25a57c7cfd1af28d606d0c19e83573a --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/paths.py @@ -0,0 +1,19 @@ +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" +ENTRYPOINT_FILE = "run.sh" +ENTRYPOINT_LOG_FILE = "gen_entrypoint_chat.txt" +PREPROMPTS_PATH = Path(__file__).parent.parent.parent / "preprompts" + + +def memory_path(path): + return os.path.join(path, MEMORY_REL_PATH) + + +def metadata_path(path): + return os.path.join(path, META_DATA_REL_PATH) diff --git a/gpt-engineer/gpt_engineer/core/default/simple_agent.py b/gpt-engineer/gpt_engineer/core/default/simple_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..0965511514d1d0269b384ea3e332c429e3bc694a --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/simple_agent.py @@ -0,0 +1,76 @@ +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 +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder + + +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 (BaseRepository): The repository where the code and related data are stored. + execution_env (BaseExecutionEnv): The environment in which the code is executed. + ai (AI): The AI model used for generating and improving code. + """ + + 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: str) -> FilesDict: + files_dict = gen_code(self.ai, prompt, self.memory, self.preprompts_holder) + entrypoint = gen_entrypoint( + self.ai, 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: str, + execution_command: Optional[str] = None, + ) -> FilesDict: + files_dict = improve( + self.ai, prompt, files_dict, self.memory, self.preprompts_holder + ) + return files_dict + + +def default_config_agent(): + return SimpleAgent.with_default_config(tempfile.mkdtemp()) diff --git a/gpt-engineer/gpt_engineer/core/default/steps.py b/gpt-engineer/gpt_engineer/core/default/steps.py new file mode 100644 index 0000000000000000000000000000000000000000..c37ad11cb30dc5f8ab7fa7d14b1da5ddae0cdb0c --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/default/steps.py @@ -0,0 +1,186 @@ +import inspect +import re + +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 ( + chat_to_files_dict, + overwrite_code_with_edits, + parse_edits, +) +from gpt_engineer.core.default.constants import MAX_EDIT_REFINEMENT_STEPS +from gpt_engineer.core.default.paths import ( + CODE_GEN_LOG_FILE, + ENTRYPOINT_FILE, + ENTRYPOINT_LOG_FILE, + IMPROVE_LOG_FILE, +) +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder + + +def curr_fn() -> str: + return inspect.stack()[1].function + + +def setup_sys_prompt(preprompts: MutableMapping[Union[str, Path], str]) -> str: + return ( + preprompts["roadmap"] + + preprompts["generate"].replace("FILE_FORMAT", preprompts["file_format"]) + + "\nUseful to know:\n" + + preprompts["philosophy"] + ) + + +def gen_code( + ai: AI, prompt: str, memory: BaseMemory, preprompts_holder: PrepromptsHolder +) -> FilesDict: + preprompts = preprompts_holder.get_preprompts() + messages = ai.start(setup_sys_prompt(preprompts), prompt, step_name=curr_fn()) + chat = messages[-1].content.strip() + memory[CODE_GEN_LOG_FILE] = chat + files_dict = chat_to_files_dict(chat) + return files_dict + + +def gen_entrypoint( + ai: AI, + files_dict: FilesDict, + memory: BaseMemory, + preprompts_holder: PrepromptsHolder, +) -> FilesDict: + preprompts = preprompts_holder.get_preprompts() + messages = ai.start( + system=(preprompts["entrypoint"]), + user="Information 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[ENTRYPOINT_LOG_FILE] = chat + return entrypoint_code + + +def execute_entrypoint( + ai: AI, + execution_env: BaseExecutionEnv, + files_dict: FilesDict, + preprompts_holder: PrepromptsHolder = None, +) -> FilesDict: + 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 setup_sys_prompt_existing_code( + preprompts: MutableMapping[Union[str, Path], str] +) -> str: + return ( + preprompts["improve"].replace("FILE_FORMAT", preprompts["file_format"]) + + "\nUseful to know:\n" + + preprompts["philosophy"] + ) + + +def incorrect_edit(files_dict: FilesDict, chat: str) -> List[str,]: + problems = [] + try: + edits = parse_edits(chat) + except ValueError as problem: + print("Not possible to parse chat to edits") + problems.append(str(problem)) + return problems + + for edit in edits: + # only trigger for existing files + if edit.filename in files_dict: + if edit.before not in files_dict[edit.filename]: + problems.append( + "This section, assigned to be exchanged for an edit block, does not have an exact match in the code: " + + edit.before + + "\nThis is often a result of placeholders, such as ... or references to 'existing code' or 'rest of function' etc, which cannot be used the HEAD part of the edit blocks. Also, to get a match, all comments, including long doc strings may have to be reproduced in the patch HEAD" + ) + return problems + + +def improve( + ai: AI, + prompt: str, + files_dict: FilesDict, + memory: BaseMemory, + preprompts_holder: PrepromptsHolder, +) -> FilesDict: + 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=f"Request: {prompt}")) + problems = [""] + + # check edit correctness + edit_refinements = 0 + while len(problems) > 0 and edit_refinements <= MAX_EDIT_REFINEMENT_STEPS: + messages = ai.next(messages, step_name=curr_fn()) + chat = messages[-1].content.strip() + problems = incorrect_edit(files_dict, chat) + if len(problems) > 0: + messages.append( + HumanMessage( + content="Some previously produced edits were not on the requested format, or the HEAD part was not found in the code. Details: " + + "\n".join(problems) + + "\n Please provide ALL the edits again, 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" + ) + ) + edit_refinements += 1 + overwrite_code_with_edits(chat, files_dict) + memory[IMPROVE_LOG_FILE] = chat + return files_dict diff --git a/gpt-engineer/gpt_engineer/core/files_dict.py b/gpt-engineer/gpt_engineer/core/files_dict.py new file mode 100644 index 0000000000000000000000000000000000000000..40ebb454105b0403175ec525172ffd2cc281e151 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/files_dict.py @@ -0,0 +1,70 @@ +from pathlib import Path + + +# 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 a method + to format its contents for chat-based interaction with an AI agent. + + Methods + ------- + to_chat() -> str: + Format the code files for chat-based interaction, returning a string suitable for AI input. + """ + + def __setitem__(self, key, value): + """ + Set the code content for the given filename. + + This method 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 : str | Path + The filename as a key for the code content. + value : str + The code content to associate with the filename. + """ + 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): + def format_file_to_input(file_name: str, file_content: str) -> str: + """ + Format a file string to use as input to the AI agent. + + Parameters + ---------- + file_name : str + The name of the file. + file_content : str + The content of the file. + + Returns + ------- + str + The formatted file string. + """ + file_str = f""" +{file_name} +``` +{file_content} + ``` + """ + return file_str + + return "\n".join( + [ + format_file_to_input(file_name, file_content) + "\n" + for file_name, file_content in self.items() + ] + ) diff --git a/gpt-engineer/gpt_engineer/core/preprompts_holder.py b/gpt-engineer/gpt_engineer/core/preprompts_holder.py new file mode 100644 index 0000000000000000000000000000000000000000..cde949967d9fd1329cb299518aa74b1bc706ae03 --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/preprompts_holder.py @@ -0,0 +1,13 @@ +from pathlib import Path +from typing import Dict + +from gpt_engineer.core.default.disk_memory import DiskMemory + + +class PrepromptsHolder: + 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/gpt_engineer/core/token_usage.py b/gpt-engineer/gpt_engineer/core/token_usage.py new file mode 100644 index 0000000000000000000000000000000000000000..bf36ee0c3b5e151bf4d703e2c67daff94147e3ab --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/token_usage.py @@ -0,0 +1,183 @@ +import logging + +from dataclasses import dataclass +from typing import List, Union + +import tiktoken + +from langchain.schema import AIMessage, HumanMessage, SystemMessage + +# 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: + """ + 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_from_messages(self, messages: List[Message]) -> int: + """ + Get the total number of tokens used by a list of messages. + + 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 # Every message follows {role/name}\n{content}\n + ) + n_tokens += self.num_tokens(message.content) + n_tokens += 2 # Every reply is primed with assistant + 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 usage_cost(self) -> float: + """ + Return the total cost in USD of the API usage. + + Returns + ------- + float + Cost in USD. + """ + 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 diff --git a/gpt-engineer/gpt_engineer/core/version_manager.py b/gpt-engineer/gpt_engineer/core/version_manager.py new file mode 100644 index 0000000000000000000000000000000000000000..67e974288651699ae1d8f2899bd7d33f5c36445d --- /dev/null +++ b/gpt-engineer/gpt_engineer/core/version_manager.py @@ -0,0 +1,28 @@ +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. + + This class defines the interface for version managers that handle the creation + of snapshots for code. Implementations of this class are expected to provide + a method to create a snapshot of the given code. + + Methods + ------- + snapshot(code: Code) -> str: + 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/gpt_engineer/preprompts/clarify b/gpt-engineer/gpt_engineer/preprompts/clarify new file mode 100644 index 0000000000000000000000000000000000000000..5fae0da39b8d010f5b29bc0e0401ef7c57d82dbc --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/preprompts/entrypoint b/gpt-engineer/gpt_engineer/preprompts/entrypoint new file mode 100644 index 0000000000000000000000000000000000000000..cfb37b32bbee8bf743c89dfeee1076bbffc5e37a --- /dev/null +++ b/gpt-engineer/gpt_engineer/preprompts/entrypoint @@ -0,0 +1,7 @@ +You will get information about a codebase that is currently on disk in the current folder. +From this you will answer with code blocks that includes all the necessary unix terminal commands to: +a) install dependencies +b) run all necessary parts of the codebase (in parallel if necessary) +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/gpt_engineer/preprompts/file_format b/gpt-engineer/gpt_engineer/preprompts/file_format new file mode 100644 index 0000000000000000000000000000000000000000..45a611855a98e117e058c602d3c2ea976efa98e5 --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/preprompts/file_format_fix b/gpt-engineer/gpt_engineer/preprompts/file_format_fix new file mode 100644 index 0000000000000000000000000000000000000000..69dade9182ab95b6a1c2c1e60ebd767029bc957e --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/preprompts/generate b/gpt-engineer/gpt_engineer/preprompts/generate new file mode 100644 index 0000000000000000000000000000000000000000..6e9725879ac99ae4e925af20c89484406122bdfd --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/preprompts/improve b/gpt-engineer/gpt_engineer/preprompts/improve new file mode 100644 index 0000000000000000000000000000000000000000..e588e550838fb4536874955e197568b736faa913 --- /dev/null +++ b/gpt-engineer/gpt_engineer/preprompts/improve @@ -0,0 +1,80 @@ +You are an expert software developer. +Respect existing conventions, libraries, etc. + +You MUST +1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes. +2. (output) Describe each change with an *edit block* per the example below. + +You MUST format EVERY code change with an *edit block* like this: +```python +example.py +<<<<<<< HEAD + # some comment + # Func to multiply + def mul(a,b) +======= + # updated comment + # Function to add + def add(a,b): +>>>>>>> updated +``` +Remember, you can use multiple *edit blocks* per file. + +Here is an example response: +--- +PLANNING: +We need to change "SOMETHING" because "SOMETHING", therefore I will add the line `a=a+1` to the function `add_one`. +Also, in the class `DB`, we need to update the "SOMETHING" + +OUTPUT: +```python +example_1.py +<<<<<<< HEAD + def mul(a,b) +======= + def add(a,b): +>>>>>>> updated +``` + +```python +example_2.py +<<<<<<< HEAD + def add_one(a,b): + a = a+2 +======= + def add_one(a,b): + a = a+1 +>>>>>>> updated +``` + +```python +example_2.py +<<<<<<< HEAD + class DBS: + db = 'aaa' +======= + class DBS: + db = 'bbb' +>>>>>>> updated +``` +--- + +A program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines. +So edit blocks must be precise and unambiguous! + +Every *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language. + +The file name at the top of the edit block (example_1.py in the examples) is the relative path to the file. + +The `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work. +NEVER SKIP LINES in the `HEAD` section! +NEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT! +NEVER OMIT ANY WHITESPACE in the `HEAD` section! +WHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block) + +Edits to different parts of a file each need their own *edit block*. + +If you want to put code in a new file, use an edit block with: +- A new file path, including dir name if needed +- An empty `HEAD` section +- The new file's contents in the `updated` section diff --git a/gpt-engineer/gpt_engineer/preprompts/philosophy b/gpt-engineer/gpt_engineer/preprompts/philosophy new file mode 100644 index 0000000000000000000000000000000000000000..74231021cc75aec9478f401f4910b7e8d2f5cb83 --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/preprompts/roadmap b/gpt-engineer/gpt_engineer/preprompts/roadmap new file mode 100644 index 0000000000000000000000000000000000000000..177bfdb5a8499267fd7bf6408cc659945ce170d2 --- /dev/null +++ b/gpt-engineer/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/gpt_engineer/tools/__init__.py b/gpt-engineer/gpt_engineer/tools/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..098bafdbdb4bb08107ea2f298882bdd2cd5e7805 --- /dev/null +++ b/gpt-engineer/gpt_engineer/tools/__init__.py @@ -0,0 +1,11 @@ +""" +gpt_engineer.tools +----------------- + +Modules: + - code_vector_repository + - document_chunker + - file_repository + - supported_languages + +""" diff --git a/gpt-engineer/gpt_engineer/tools/custom_steps.py b/gpt-engineer/gpt_engineer/tools/custom_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..f77bcad4822dd08feb0d5865277248595704de53 --- /dev/null +++ b/gpt-engineer/gpt_engineer/tools/custom_steps.py @@ -0,0 +1,207 @@ +from platform import platform +from sys import version_info +from typing import List, Union + +from langchain.schema import AIMessage, 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 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, setup_sys_prompt +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder + +# Type hint for chat messages +Message = Union[AIMessage, HumanMessage, SystemMessage] +MAX_SELF_HEAL_ATTEMPTS = 2 + + +def get_platform_info(): + """Returns the Platform: OS, and the Python version. + This is used for self healing. There are some possible areas of conflict here if + you use a different version of Python in your virtualenv. A better solution would + be to have this info printed from the virtualenv. + """ + 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, + preprompts_holder: PrepromptsHolder = 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. + 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 + messages = [] + if preprompts_holder is None: + raise AssertionError("Prepromptsholder required for self-heal") + preprompts = preprompts_holder.get_preprompts() + while attempts < MAX_SELF_HEAL_ATTEMPTS: + command = files_dict[ENTRYPOINT_FILE] + 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...") + stdout_full, stderr_full, returncode = execution_env.upload(files_dict).run( + f"bash {ENTRYPOINT_FILE}" + ) + # get the result and output + # step 2. if the return code not 0, package and send to the AI + if returncode != 0: + print("run.sh failed. Let's fix it.") + + # pack results in an AI prompt + + # Using the log from the previous step has all the code and + # the gen_entrypoint prompt inside. + if attempts < 1: + messages: List[Message] = [SystemMessage(content=files_dict.to_chat())] + messages.append(SystemMessage(content=get_platform_info())) + + messages.append(SystemMessage(content=stdout_full + "\n " + stderr_full)) + + messages = ai.next( + messages, preprompts["file_format_fix"], step_name=curr_fn() + ) + else: # the process did not fail, we are done here. + return files_dict + + files_dict = {**files_dict, **chat_to_files_dict(messages[-1].content.strip())} + attempts += 1 + + return files_dict + + +def clarified_gen( + ai: AI, prompt: str, memory: BaseMemory, preprompts_holder: PrepromptsHolder +) -> FilesDict: + """ + Generates code based on clarifications obtained from the user. + + This function processes the messages logged during the user's clarification session + and uses them, along with the system's prompts, to guide the AI in generating code. + The generated code is saved to a specified workspace. + + Parameters: + - ai (AI): An instance of the AI model, responsible for processing and generating the code. + - dbs (DBs): An instance containing the database configurations, which includes system + and input prompts. + + Returns: + - List[dict]: A list of message dictionaries capturing the AI's interactions and generated + outputs during the code generation process. + """ + preprompts = preprompts_holder.get_preprompts() + messages: List[Message] = [SystemMessage(content=preprompts["clarify"])] + user_input = prompt + 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[CODE_GEN_LOG_FILE] = chat + files_dict = chat_to_files_dict(chat) + return files_dict + + +def lite_gen( + ai: AI, prompt: str, memory: BaseMemory, preprompts_holder: PrepromptsHolder +) -> FilesDict: + """ + Executes the AI model using the main prompt and saves the generated results. + + This function invokes the AI model by feeding it the main prompt. After the + AI processes and generates the output, the function saves this output to the + specified workspace. The AI's output is also tracked using the current function's + name to provide context. + + Parameters: + - ai (AI): An instance of the AI model. + - dbs (DBs): An instance containing the database configurations, including input prompts + and file formatting preferences. + + Returns: + - List[Message]: A list of message objects encapsulating the AI's output. + + Note: + 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, preprompts["file_format"], step_name=curr_fn()) + chat = messages[-1].content.strip() + memory[CODE_GEN_LOG_FILE] = chat + files_dict = chat_to_files_dict(chat) + return files_dict diff --git a/gpt-engineer/gpt_engineer/tools/experimental/__init__.py b/gpt-engineer/gpt_engineer/tools/experimental/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/gpt_engineer/tools/experimental/code_vector_repository.py b/gpt-engineer/gpt_engineer/tools/experimental/code_vector_repository.py new file mode 100644 index 0000000000000000000000000000000000000000..1d93c1d2c01f3996b7c2777d9dd12b3d8bde8d60 --- /dev/null +++ b/gpt-engineer/gpt_engineer/tools/experimental/code_vector_repository.py @@ -0,0 +1,72 @@ +from typing import Dict, List + +from llama_index import Document, SimpleDirectoryReader, VectorStoreIndex +from llama_index.retrievers import BM25Retriever +from llama_index.schema import NodeWithScore + +from gpt_engineer.tools.experimental.document_chunker import DocumentChunker + + +class CodeVectorRepository: + def __init__(self): + self._index = None + self._query_engine = None + self._retriever = None + + def _load_documents_from_directory(self, directory_path) -> List[Document]: + def name_metadata_storer(filename: str) -> Dict: + return {"filename": filename} + + excluded_file_globs = ["*/.gpteng/*"] + + return SimpleDirectoryReader( + directory_path, + recursive=True, + exclude=excluded_file_globs, + file_metadata=name_metadata_storer, + ).load_data() + + def load_from_directory(self, directory_path: str): + documents = self._load_documents_from_directory(directory_path) + + chunked_langchain_documents = DocumentChunker.chunk_documents( + [doc.to_langchain_format() for doc in documents] + ) + + chunked_documents = [ + Document.from_langchain_format(doc) for doc in chunked_langchain_documents + ] + + self._index = VectorStoreIndex.from_documents(chunked_documents) + + def query(self, query_string: str): + """ + Ask a plain english question about the code base and retrieve a plain english answer + """ + + if self._index is None: + raise ValueError("Index has not been loaded yet.") + + if self._query_engine is None: + self._query_engine = self._index.as_query_engine( + response_mode="tree_summarize" + ) + + return self._query_engine.query(query_string) + + def relevent_code_chunks( + self, query_string: str, llm: str = "default" + ) -> List[NodeWithScore]: + """ + Retrieve code chunks relevent to a prompt + """ + + if self._index is None: + raise ValueError("Index has not been loaded yet.") + + if self._retriever is None: + self._retriever = BM25Retriever.from_defaults( + self._index, similarity_top_k=2 + ) + + return self._retriever.retrieve(query_string) diff --git a/gpt-engineer/gpt_engineer/tools/experimental/document_chunker.py b/gpt-engineer/gpt_engineer/tools/experimental/document_chunker.py new file mode 100644 index 0000000000000000000000000000000000000000..d5f9f723e34b9de2047ae8ffa50d162e03a45e33 --- /dev/null +++ b/gpt-engineer/gpt_engineer/tools/experimental/document_chunker.py @@ -0,0 +1,131 @@ +from collections import defaultdict +from pathlib import Path +from typing import Any, Dict, List, NamedTuple + +import tree_sitter_languages + +from langchain.docstore.document import Document +from langchain.text_splitter import TextSplitter + +from gpt_engineer.tools.experimental.supported_languages import SUPPORTED_LANGUAGES + + +class CodeSplitter(TextSplitter): + """Split code using a AST parser.""" + + def __init__( + self, + language: str, + chunk_lines: int = 40, + chunk_lines_overlap: int = 15, + max_chars: int = 1500, + **kwargs, + ): + super().__init__(**kwargs) + + self.language = language + self.chunk_lines = chunk_lines + self.chunk_lines_overlap = chunk_lines_overlap + self.max_chars = max_chars + + def _chunk_node(self, node: Any, text: str, last_end: int = 0) -> List[str]: + new_chunks = [] + current_chunk = "" + for child in node.children: + if child.end_byte - child.start_byte > self.max_chars: + # Child is too big, recursively chunk the child + if len(current_chunk) > 0: + new_chunks.append(current_chunk) + current_chunk = "" + new_chunks.extend(self._chunk_node(child, text, last_end)) + elif ( + len(current_chunk) + child.end_byte - child.start_byte > self.max_chars + ): + # Child would make the current chunk too big, so start a new chunk + new_chunks.append(current_chunk) + current_chunk = text[last_end : child.end_byte] + else: + current_chunk += text[last_end : child.end_byte] + last_end = child.end_byte + if len(current_chunk) > 0: + new_chunks.append(current_chunk) + return new_chunks + + def split_text(self, text: str) -> List[str]: + """Split incoming code and return chunks using the AST.""" + + try: + parser = tree_sitter_languages.get_parser(self.language) + except Exception as e: + print( + f"Could not get parser for language {self.language}. Check " + "https://github.com/grantjenks/py-tree-sitter-languages#license " + "for a list of valid languages." + ) + raise e + + tree = parser.parse(bytes(text, "utf-8")) + + if not tree.root_node.children or tree.root_node.children[0].type != "ERROR": + chunks = [chunk.strip() for chunk in self._chunk_node(tree.root_node, text)] + + return chunks + else: + raise ValueError(f"Could not parse code with language {self.language}.") + + +class SortedDocuments(NamedTuple): + by_language: Dict[str, List[Document]] + other: List[Document] + + +class DocumentChunker: + def chunk_documents(documents: List[Document]) -> List[Document]: + chunked_documents = [] + + sorted_documents = _sort_documents_by_programming_language_or_other(documents) + + for language, language_documents in sorted_documents.by_language.items(): + code_splitter = CodeSplitter( + language=language.lower(), + chunk_lines=40, + chunk_lines_overlap=15, + max_chars=1500, + ) + + chunked_documents.extend(code_splitter.split_documents(language_documents)) + + # for now only include code files! + # chunked_documents.extend(sorted_documents.other) + + return chunked_documents + + +@staticmethod +def _sort_documents_by_programming_language_or_other( + documents: List[Document], +) -> SortedDocuments: + docs_to_split = defaultdict(list) + other_docs = [] + + for doc in documents: + filename = str(doc.metadata.get("filename")) + extension = Path(filename).suffix + language_found = False + + for lang in SUPPORTED_LANGUAGES: + if extension in lang["extensions"]: + doc.metadata["is_code"] = True + doc.metadata["code_language"] = lang["name"] + doc.metadata["code_language_tree_sitter_name"] = lang[ + "tree_sitter_name" + ] + docs_to_split[lang["tree_sitter_name"]].append(doc) + language_found = True + break + + if not language_found: + doc.metadata["isCode"] = False + other_docs.append(doc) + + return SortedDocuments(by_language=dict(docs_to_split), other=other_docs) diff --git a/gpt-engineer/gpt_engineer/tools/experimental/experimental_steps.py b/gpt-engineer/gpt_engineer/tools/experimental/experimental_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..f8a4990f9f6726334b67fd64a4b057a8fd4d19bf --- /dev/null +++ b/gpt-engineer/gpt_engineer/tools/experimental/experimental_steps.py @@ -0,0 +1,53 @@ +import os +import tempfile + +from langchain.schema import HumanMessage, SystemMessage + +from gpt_engineer.core.ai import AI +from gpt_engineer.core.base_memory import BaseMemory +from gpt_engineer.core.chat_to_files import overwrite_code_with_edits +from gpt_engineer.core.default.disk_memory import DiskMemory +from gpt_engineer.core.default.paths import IMPROVE_LOG_FILE +from gpt_engineer.core.default.steps import curr_fn, setup_sys_prompt_existing_code +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder +from gpt_engineer.tools.experimental.code_vector_repository import CodeVectorRepository + + +def improve_automatic_file_selection( + ai: AI, + prompt: str, + code: FilesDict, + memory: BaseMemory, + preprompts_holder: PrepromptsHolder, +): + code_vector_repository = CodeVectorRepository() + # ToDo: Replace this hacky way to get the right langchain document format + temp_dir = tempfile.mkdtemp() + temp_saver = DiskMemory(temp_dir) + for file, content in code.items(): + temp_saver[file] = content + code_vector_repository.load_from_directory(temp_dir) + relevant_documents = code_vector_repository.relevent_code_chunks(prompt) + relevant_code = FilesDict() + for doc in relevant_documents: + file_path = os.path.relpath(doc.metadata["filename"], temp_dir) + relevant_code[file_path] = code[file_path] + print( + "Relevant documents to be modified are: " + + "\n".join(sorted(relevant_code.keys())) + ) + preprompts = preprompts_holder.get_preprompts() + messages = [ + SystemMessage(content=setup_sys_prompt_existing_code(preprompts)), + ] + # Add files as input + messages.append(HumanMessage(content=f"{relevant_code.to_chat()}")) + + messages.append(HumanMessage(content=f"Request: {prompt}")) + + messages = ai.next(messages, step_name=curr_fn()) + chat = messages[-1].content.strip() + overwrite_code_with_edits(chat, code) + memory[IMPROVE_LOG_FILE] = chat + return code diff --git a/gpt-engineer/gpt_engineer/tools/experimental/supported_languages.py b/gpt-engineer/gpt_engineer/tools/experimental/supported_languages.py new file mode 100644 index 0000000000000000000000000000000000000000..287b820fc61e25cbfd25b56637253a72cbd5c312 --- /dev/null +++ b/gpt-engineer/gpt_engineer/tools/experimental/supported_languages.py @@ -0,0 +1,49 @@ +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/gpt-engineer/poetry.lock b/gpt-engineer/poetry.lock new file mode 100644 index 0000000000000000000000000000000000000000..6216efe15efbfb1bdf7076cce55536e6b51fa716 --- /dev/null +++ b/gpt-engineer/poetry.lock @@ -0,0 +1,4474 @@ +# This file is automatically @generated by Poetry 1.7.1 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.1" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, + {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, + {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, + {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, + {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, + {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, + {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, + {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, + {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, + {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, + {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, + {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, +] + +[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 = "anyio" +version = "4.2.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, + {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, +] + +[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.3" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = "*" +files = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] + +[[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.0.1" +description = "Seamlessly integrate pydantic models in your Sphinx documentation." +optional = false +python-versions = ">=3.7.1,<4.0.0" +files = [ + {file = "autodoc_pydantic-2.0.1-py3-none-any.whl", hash = "sha256:d3c302fdb6d37edb5b721f0f540252fa79cea7018bc1a9a85bf70f33a68b0ce4"}, + {file = "autodoc_pydantic-2.0.1.tar.gz", hash = "sha256:7a125a4ff18e4903e27be71e4ddb3269380860eacab4a584d6cc2e212fa96991"}, +] + +[package.dependencies] +pydantic = ">=2.0,<3.0.0" +pydantic-settings = ">=2.0,<3.0.0" +Sphinx = ">=4.0" + +[package.extras] +dev = ["coverage (>=7,<8)", "flake8 (>=3,<4)", "pytest (>=7,<8)", "sphinx-copybutton (>=0.4,<0.5)", "sphinx-rtd-theme (>=1.0,<2.0)", "sphinx-tabs (>=3,<4)", "sphinxcontrib-mermaid (>=0.7,<0.8)", "tox (>=3,<4)"] +docs = ["sphinx-copybutton (>=0.4,<0.5)", "sphinx-rtd-theme (>=1.0,<2.0)", "sphinx-tabs (>=3,<4)", "sphinxcontrib-mermaid (>=0.7,<0.8)"] +erdantic = ["erdantic (>=0.6,<0.7)"] +test = ["coverage (>=7,<8)", "pytest (>=7,<8)"] + +[[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.2" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, + {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, +] + +[[package]] +name = "certifi" +version = "2023.11.17" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, +] + +[[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.1" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.1-py3-none-any.whl", hash = "sha256:87928485c0dfc0e7976fd89fc1e187023cf587e7c353e4a9b417555b44adf021"}, + {file = "comm-0.2.1.tar.gz", hash = "sha256:0bc91edae1344d39d3661dcbc36937181fdaddb304790458f8b044dbc064b89a"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "coverage" +version = "7.4.0" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, + {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, + {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, + {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, + {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, + {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, + {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, + {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, + {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, + {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, + {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, + {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, + {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, +] + +[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 = "debugpy" +version = "1.8.0" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7fb95ca78f7ac43393cd0e0f2b6deda438ec7c5e47fa5d38553340897d2fbdfb"}, + {file = "debugpy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef9ab7df0b9a42ed9c878afd3eaaff471fce3fa73df96022e1f5c9f8f8c87ada"}, + {file = "debugpy-1.8.0-cp310-cp310-win32.whl", hash = "sha256:a8b7a2fd27cd9f3553ac112f356ad4ca93338feadd8910277aff71ab24d8775f"}, + {file = "debugpy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d9de202f5d42e62f932507ee8b21e30d49aae7e46d5b1dd5c908db1d7068637"}, + {file = "debugpy-1.8.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ef54404365fae8d45cf450d0544ee40cefbcb9cb85ea7afe89a963c27028261e"}, + {file = "debugpy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60009b132c91951354f54363f8ebdf7457aeb150e84abba5ae251b8e9f29a8a6"}, + {file = "debugpy-1.8.0-cp311-cp311-win32.whl", hash = "sha256:8cd0197141eb9e8a4566794550cfdcdb8b3db0818bdf8c49a8e8f8053e56e38b"}, + {file = "debugpy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:a64093656c4c64dc6a438e11d59369875d200bd5abb8f9b26c1f5f723622e153"}, + {file = "debugpy-1.8.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b05a6b503ed520ad58c8dc682749113d2fd9f41ffd45daec16e558ca884008cd"}, + {file = "debugpy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c6fb41c98ec51dd010d7ed650accfd07a87fe5e93eca9d5f584d0578f28f35f"}, + {file = "debugpy-1.8.0-cp38-cp38-win32.whl", hash = "sha256:46ab6780159eeabb43c1495d9c84cf85d62975e48b6ec21ee10c95767c0590aa"}, + {file = "debugpy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:bdc5ef99d14b9c0fcb35351b4fbfc06ac0ee576aeab6b2511702e5a648a2e595"}, + {file = "debugpy-1.8.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:61eab4a4c8b6125d41a34bad4e5fe3d2cc145caecd63c3fe953be4cc53e65bf8"}, + {file = "debugpy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:125b9a637e013f9faac0a3d6a82bd17c8b5d2c875fb6b7e2772c5aba6d082332"}, + {file = "debugpy-1.8.0-cp39-cp39-win32.whl", hash = "sha256:57161629133113c97b387382045649a2b985a348f0c9366e22217c87b68b73c6"}, + {file = "debugpy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:e3412f9faa9ade82aa64a50b602544efcba848c91384e9f93497a458767e6926"}, + {file = "debugpy-1.8.0-py2.py3-none-any.whl", hash = "sha256:9c9b0ac1ce2a42888199df1a1906e45e6f3c9555497643a85e0bf2406e3ffbc4"}, + {file = "debugpy-1.8.0.zip", hash = "sha256:12af2c55b419521e33d5fb21bd022df0b5eb267c3e178f1d374a63a2a6bdccd0"}, +] + +[[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 = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + +[[package]] +name = "deprecation" +version = "2.0.7" +description = "A library to handle automated deprecations" +optional = false +python-versions = "*" +files = [ + {file = "deprecation-2.0.7-py2.py3-none-any.whl", hash = "sha256:dc9b4f252b7aca8165ce2764a71da92a653b5ffbf7a389461d7a640f6536ecb2"}, + {file = "deprecation-2.0.7.tar.gz", hash = "sha256:c0392f676a6146f0238db5744d73e786a43510d54033f80994ef2f4c9df192ed"}, +] + +[package.dependencies] +packaging = "*" + +[[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.5.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.5.0-py3-none-any.whl", hash = "sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6"}, + {file = "dnspython-2.5.0.tar.gz", hash = "sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=5.0.3)", "mypy (>=1.0.1)", "pylint (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "sphinx (>=7.0.0)", "twine (>=4.0.0)", "wheel (>=0.41.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.25.1)"] +doq = ["aioquic (>=0.9.20)"] +idna = ["idna (>=2.1)"] +trio = ["trio (>=0.14)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "docutils" +version = "0.17.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[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.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "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 = "2023.12.2" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2023.12.2-py3-none-any.whl", hash = "sha256:d800d87f72189a745fa3d6b033b9dc4a34ad069f60ca60b943a63599f5501960"}, + {file = "fsspec-2023.12.2.tar.gz", hash = "sha256:8548d39e8810b59c38014934f6b31e57f40c1b20f911f4cc2b85389c7e9bf0cb"}, +] + +[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)", "requests"] +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.2" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, + {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, +] + +[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.23.0)"] + +[[package]] +name = "httpx" +version = "0.26.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, + {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, +] + +[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 = "identify" +version = "2.5.33" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, + {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.6" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[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.0.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[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.0" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.0-py3-none-any.whl", hash = "sha256:076663ca68492576f051e4af7720d33f34383e655f2be0d544c8b1c9de915b2f"}, + {file = "ipykernel-6.29.0.tar.gz", hash = "sha256:b5dd3013cab7b330df712891c96cd1ab868c27a7159e606f762015e9bf8ceb3f"}, +] + +[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.2)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.20.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.10" +files = [ + {file = "ipython-8.20.0-py3-none-any.whl", hash = "sha256:bc9716aad6f29f36c449e30821c9dd0c1c1a7b59ddcc26931685b87b4c569619"}, + {file = "ipython-8.20.0.tar.gz", hash = "sha256:2f21bd3fc1d51550c89ee3944ae04bbc7bc79e129ea0937da6e6c68bfdbf117a"}, +] + +[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\""} +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" + +[package.extras] +all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.23)", "pandas", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath", "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 = "joblib" +version = "1.3.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "joblib-1.3.2-py3-none-any.whl", hash = "sha256:ef4331c65f239985f3f2220ecc87db222f08fd22097a3dd5698f693875f8cbb9"}, + {file = "joblib-1.3.2.tar.gz", hash = "sha256:92f865e621e17784e7955080b6d042489e3b8e294949cc44c6eac304f59772b1"}, +] + +[[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 = "0.6.1" +description = "A defined interface for working with a cache of jupyter notebooks." +optional = false +python-versions = "~=3.8" +files = [ + {file = "jupyter-cache-0.6.1.tar.gz", hash = "sha256:26f83901143edf4af2f3ff5a91e2d2ad298e46e2cee03c8071d37a23a63ccbfc"}, + {file = "jupyter_cache-0.6.1-py3-none-any.whl", hash = "sha256:2fce7d4975805c77f75bdfc1bc2e82bc538b8e5b1af27f2f5e06d55b9f996a82"}, +] + +[package.dependencies] +attrs = "*" +click = "*" +importlib-metadata = "*" +nbclient = ">=0.2,<0.8" +nbformat = "*" +pyyaml = "*" +sqlalchemy = ">=1.3.12,<3" +tabulate = "*" + +[package.extras] +cli = ["click-log"] +code-style = ["pre-commit (>=2.12,<4.0)"] +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.0" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.0-py3-none-any.whl", hash = "sha256:909c474dbe62582ae62b758bca86d6518c85234bdee2d908c778db6d72f39d99"}, + {file = "jupyter_client-8.6.0.tar.gz", hash = "sha256:0642244bb83b4764ae60d07e010e15f0e2d275ec4e918a8f7b80fbbef3ca60c7"}, +] + +[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.1" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.1-py3-none-any.whl", hash = "sha256:c65c82126453a723a2804aa52409930434598fd9d35091d63dfb919d2b765bb7"}, + {file = "jupyter_core-5.7.1.tar.gz", hash = "sha256:de61a9d7fc71240f688b2fb5ab659fbb56979458dc66a71decd098e03c79e218"}, +] + +[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", "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.1.3" +description = "Building applications with LLMs through composability" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langchain-0.1.3-py3-none-any.whl", hash = "sha256:9802bc17a068518295fd282eb833543793c11d222caf9a0b1c7058fe59b951f3"}, + {file = "langchain-0.1.3.tar.gz", hash = "sha256:7cdebf1bb14181aa614a21b373232c423b0fef06bb97a488a833e11c01524d4a"}, +] + +[package.dependencies] +aiohttp = ">=3.8.3,<4.0.0" +async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} +dataclasses-json = ">=0.5.7,<0.7" +jsonpatch = ">=1.33,<2.0" +langchain-community = ">=0.0.14,<0.1" +langchain-core = ">=0.1.14,<0.2" +langsmith = ">=0.0.83,<0.1" +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-ai-vision (>=0.11.1b1,<0.12.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,<5)"] +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,<5)", "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.0.2,<0.1)", "lxml (>=4.9.2,<5.0.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)", "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,<5)", "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.3.2,<0.6.0)"] +qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"] +text-helpers = ["chardet (>=5.1.0,<6.0.0)"] + +[[package]] +name = "langchain-community" +version = "0.0.15" +description = "Community contributed LangChain integrations." +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langchain_community-0.0.15-py3-none-any.whl", hash = "sha256:2b830c79366e192aed2a997a11a69b62505fb2ee8d08a85f3df7bd3ab62473f1"}, + {file = "langchain_community-0.0.15.tar.gz", hash = "sha256:b027d7765661300edced958228e78077780d96332efe63c5949bc5e435cc7c2b"}, +] + +[package.dependencies] +aiohttp = ">=3.8.3,<4.0.0" +dataclasses-json = ">=0.5.7,<0.7" +langchain-core = ">=0.1.14,<0.2" +langsmith = ">=0.0.83,<0.1" +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)", "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,<5)", "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)", "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)", "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)", "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)", "lxml (>=4.9.2,<5.0.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)", "oracle-ads (>=2.9.1,<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)", "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)", "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)", "zhipuai (>=1.0.7,<2.0.0)"] + +[[package]] +name = "langchain-core" +version = "0.1.15" +description = "Building applications with LLMs through composability" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langchain_core-0.1.15-py3-none-any.whl", hash = "sha256:eb2e6a81400a98e8d27638ede158c0dc4245ca8e69578138f5ef4c913258ff95"}, + {file = "langchain_core-0.1.15.tar.gz", hash = "sha256:129c0e92ef9a31971766496a9fade429217bd56d16bb6d0024d27925f26187d6"}, +] + +[package.dependencies] +anyio = ">=3,<5" +jsonpatch = ">=1.33,<2.0" +langsmith = ">=0.0.83,<0.1" +packaging = ">=23.2,<24.0" +pydantic = ">=1,<3" +PyYAML = ">=5.3" +requests = ">=2,<3" +tenacity = ">=8.1.0,<9.0.0" + +[package.extras] +extended-testing = ["jinja2 (>=3,<4)"] + +[[package]] +name = "langchain-openai" +version = "0.0.3" +description = "An integration package connecting OpenAI and LangChain" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langchain_openai-0.0.3-py3-none-any.whl", hash = "sha256:32d8ae288e212ed47af418ffd216c8af3b8115514bb39127ca9e2910c06fc6b2"}, + {file = "langchain_openai-0.0.3.tar.gz", hash = "sha256:19720510abcd7d6217a47d551def7779dc001aebbf978bda5c03e0a8c8167ac3"}, +] + +[package.dependencies] +langchain-core = ">=0.1.13,<0.2" +numpy = ">=1,<2" +openai = ">=1.6.1,<2.0.0" +tiktoken = ">=0.5.2,<0.6.0" + +[[package]] +name = "langsmith" +version = "0.0.83" +description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "langsmith-0.0.83-py3-none-any.whl", hash = "sha256:a5bb7ac58c19a415a9d5f51db56dd32ee2cd7343a00825bbc2018312eb3d122a"}, + {file = "langsmith-0.0.83.tar.gz", hash = "sha256:94427846b334ad9bdbec3266fee12903fe9f5448f628667689d0412012aaf392"}, +] + +[package.dependencies] +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 = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +optional = false +python-versions = "*" +files = [ + {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "llama-index" +version = "0.9.37.post1" +description = "Interface between LLMs and your data" +optional = false +python-versions = ">=3.8.1,<4.0" +files = [ + {file = "llama_index-0.9.37.post1-py3-none-any.whl", hash = "sha256:b40ab4977a560d90a08fcfeaf52297c7f7d6411e3737c96cd6584946692593ae"}, + {file = "llama_index-0.9.37.post1.tar.gz", hash = "sha256:2bbc53af4ed631432fd0f77e0021b21a4a94d4e7336d5022ac7b311fd2cc4b3d"}, +] + +[package.dependencies] +aiohttp = ">=3.8.6,<4.0.0" +dataclasses-json = "*" +deprecated = ">=1.2.9.3" +fsspec = ">=2023.5.0" +httpx = "*" +nest-asyncio = ">=1.5.8,<2.0.0" +networkx = ">=3.0" +nltk = ">=3.8.1,<4.0.0" +numpy = "*" +openai = ">=1.1.0" +pandas = "*" +requests = ">=2.31.0" +SQLAlchemy = {version = ">=1.4.49", extras = ["asyncio"]} +tenacity = ">=8.2.0,<9.0.0" +tiktoken = ">=0.3.3" +typing-extensions = ">=4.5.0" +typing-inspect = ">=0.8.0" + +[package.extras] +gradientai = ["gradientai (>=1.4.0)"] +html = ["beautifulsoup4 (>=4.12.2,<5.0.0)"] +langchain = ["langchain (>=0.0.303)"] +local-models = ["optimum[onnxruntime] (>=1.13.2,<2.0.0)", "sentencepiece (>=0.1.99,<0.2.0)", "transformers[torch] (>=4.33.1,<5.0.0)"] +postgres = ["asyncpg (>=0.28.0,<0.29.0)", "pgvector (>=0.1.0,<0.2.0)", "psycopg2-binary (>=2.9.9,<3.0.0)"] +query-tools = ["guidance (>=0.0.64,<0.0.65)", "jsonpath-ng (>=1.6.0,<2.0.0)", "lm-format-enforcer (>=0.4.3,<0.5.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "scikit-learn", "spacy (>=3.7.1,<4.0.0)"] + +[[package]] +name = "markdown" +version = "3.5.2" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.5.2-py3-none-any.whl", hash = "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd"}, + {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, +] + +[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 = "2.2.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.7" +files = [ + {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, + {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, +] + +[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 = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.4" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win32.whl", hash = "sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0"}, + {file = "MarkupSafe-2.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win32.whl", hash = "sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74"}, + {file = "MarkupSafe-2.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win32.whl", hash = "sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475"}, + {file = "MarkupSafe-2.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a07f40ef8f0fbc5ef1000d0c78771f4d5ca03b4953fc162749772916b298fc4"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d18b66fe626ac412d96c2ab536306c736c66cf2a31c243a45025156cc190dc8a"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698e84142f3f884114ea8cf83e7a67ca8f4ace8454e78fe960646c6c91c63bfa"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a3b78a5af63ec10d8604180380c13dcd870aba7928c1fe04e881d5c792dc4e"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:15866d7f2dc60cfdde12ebb4e75e41be862348b4728300c36cdf405e258415ec"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6aa5e2e7fc9bc042ae82d8b79d795b9a62bd8f15ba1e7594e3db243f158b5565"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:54635102ba3cf5da26eb6f96c4b8c53af8a9c0d97b64bdcb592596a6255d8518"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win32.whl", hash = "sha256:3583a3a3ab7958e354dc1d25be74aee6228938312ee875a22330c4dc2e41beb0"}, + {file = "MarkupSafe-2.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:d6e427c7378c7f1b2bef6a344c925b8b63623d3321c09a237b7cc0e77dd98ceb"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bf1196dcc239e608605b716e7b166eb5faf4bc192f8a44b81e85251e62584bd2"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df98d4a9cd6a88d6a585852f56f2155c9cdb6aec78361a19f938810aa020954"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b835aba863195269ea358cecc21b400276747cc977492319fd7682b8cd2c253d"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23984d1bdae01bee794267424af55eef4dfc038dc5d1272860669b2aa025c9e3"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c98c33ffe20e9a489145d97070a435ea0679fddaabcafe19982fe9c971987d5"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9896fca4a8eb246defc8b2a7ac77ef7553b638e04fbf170bff78a40fa8a91474"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0fe73bac2fed83839dbdbe6da84ae2a31c11cfc1c777a40dbd8ac8a6ed1560f"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c7556bafeaa0a50e2fe7dc86e0382dea349ebcad8f010d5a7dc6ba568eaaa789"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win32.whl", hash = "sha256:fc1a75aa8f11b87910ffd98de62b29d6520b6d6e8a3de69a70ca34dea85d2a8a"}, + {file = "MarkupSafe-2.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:3a66c36a3864df95e4f62f9167c734b3b1192cb0851b43d7cc08040c074c6279"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:765f036a3d00395a326df2835d8f86b637dbaf9832f90f5d196c3b8a7a5080cb"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:21e7af8091007bf4bebf4521184f4880a6acab8df0df52ef9e513d8e5db23411"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c31fe855c77cad679b302aabc42d724ed87c043b1432d457f4976add1c2c3e"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7653fa39578957bc42e5ebc15cf4361d9e0ee4b702d7d5ec96cdac860953c5b4"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47bb5f0142b8b64ed1399b6b60f700a580335c8e1c57f2f15587bd072012decc"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fe8512ed897d5daf089e5bd010c3dc03bb1bdae00b35588c49b98268d4a01e00"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:36d7626a8cca4d34216875aee5a1d3d654bb3dac201c1c003d182283e3205949"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b6f14a9cd50c3cb100eb94b3273131c80d102e19bb20253ac7bd7336118a673a"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win32.whl", hash = "sha256:c8f253a84dbd2c63c19590fa86a032ef3d8cc18923b8049d91bcdeeb2581fbf6"}, + {file = "MarkupSafe-2.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:8b570a1537367b52396e53325769608f2a687ec9a4363647af1cded8928af959"}, + {file = "MarkupSafe-2.1.4.tar.gz", hash = "sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f"}, +] + +[[package]] +name = "marshmallow" +version = "3.20.2" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +optional = false +python-versions = ">=3.8" +files = [ + {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, + {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, +] + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["pre-commit (>=2.4,<4.0)"] +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.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mdit-py-plugins" +version = "0.3.5" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdit-py-plugins-0.3.5.tar.gz", hash = "sha256:eee0adc7195e5827e17e02d2a258a2ba159944a0748f59c5099a4a27f78fcf6a"}, + {file = "mdit_py_plugins-0.3.5-py3-none-any.whl", hash = "sha256:ca9a0714ea59a24b2b044a1831f48d817dd0c817e84339f20e7889f392d77c4e"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<3.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["attrs", "myst-parser (>=0.16.1,<0.17.0)", "sphinx-book-theme (>=0.1.0,<0.2.0)"] +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.4" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "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 = "0.17.2" +description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." +optional = false +python-versions = ">=3.7" +files = [ + {file = "myst-nb-0.17.2.tar.gz", hash = "sha256:0f61386515fab07c73646adca97fff2f69f41e90d313a260217c5bbe419d858b"}, + {file = "myst_nb-0.17.2-py3-none-any.whl", hash = "sha256:132ca4d0f5c308fdd4b6fdaba077712e28e119ccdafd04d6e41b51aac5483494"}, +] + +[package.dependencies] +importlib_metadata = "*" +ipykernel = "*" +ipython = "*" +jupyter-cache = ">=0.5,<0.7" +myst-parser = ">=0.18.0,<0.19.0" +nbclient = "*" +nbformat = ">=5.0,<6.0" +pyyaml = "*" +sphinx = ">=4,<6" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<2.3.0)", "ipykernel (>=5.5,<6.0)", "ipywidgets", "jupytext (>=1.11.2,<1.12.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3.0,<0.4.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0,<0.5.0)", "sphinxcontrib-bibtex", "sympy"] +testing = ["beautifulsoup4", "coverage (>=6.4,<8.0)", "ipykernel (>=5.5,<6.0)", "ipython (!=8.1.0,<8.5)", "ipywidgets (>=8)", "jupytext (>=1.11.2,<1.12.0)", "matplotlib (>=3.5.3,<3.6)", "nbdime", "numpy", "pandas", "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 = "0.18.1" +description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." +optional = false +python-versions = ">=3.7" +files = [ + {file = "myst-parser-0.18.1.tar.gz", hash = "sha256:79317f4bb2c13053dd6e64f9da1ba1da6cd9c40c8a430c447a7b146a594c246d"}, + {file = "myst_parser-0.18.1-py3-none-any.whl", hash = "sha256:61b275b85d9f58aa327f370913ae1bec26ebad372cc99f3ab85c8ec3ee8d9fb8"}, +] + +[package.dependencies] +docutils = ">=0.15,<0.20" +jinja2 = "*" +markdown-it-py = ">=1.0.0,<3.0.0" +mdit-py-plugins = ">=0.3.1,<0.4.0" +pyyaml = "*" +sphinx = ">=4,<6" +typing-extensions = "*" + +[package.extras] +code-style = ["pre-commit (>=2.12,<3.0)"] +linkify = ["linkify-it-py (>=1.0,<2.0)"] +rtd = ["ipython", "sphinx-book-theme", "sphinx-design", "sphinxcontrib.mermaid (>=0.7.1,<0.8.0)", "sphinxext-opengraph (>=0.6.3,<0.7.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] +testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx (<5.2)", "sphinx-pytest"] + +[[package]] +name = "nbclient" +version = "0.7.4" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "nbclient-0.7.4-py3-none-any.whl", hash = "sha256:c817c0768c5ff0d60e468e017613e6eae27b6fa31e43f905addd2d24df60c125"}, + {file = "nbclient-0.7.4.tar.gz", hash = "sha256:d447f0e5a4cfe79d462459aec1b3dc5c2e9152597262be8ee27f7d4c02566a0d"}, +] + +[package.dependencies] +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +nbformat = ">=5.1" +traitlets = ">=5.3" + +[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", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbconvert" +version = "7.14.2" +description = "Converting Jupyter Notebooks" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbconvert-7.14.2-py3-none-any.whl", hash = "sha256:db28590cef90f7faf2ebbc71acd402cbecf13d29176df728c0a9025a49345ea1"}, + {file = "nbconvert-7.14.2.tar.gz", hash = "sha256:a7f8808fd4e082431673ac538400218dd45efd076fbeb07cc6e5aa5a3a4e949e"}, +] + +[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"] +webpdf = ["playwright"] + +[[package]] +name = "nbformat" +version = "5.9.2" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.9.2-py3-none-any.whl", hash = "sha256:1c5172d786a41b82bcfd0c23f9e6b6f072e8fb49c39250219e4acfff1efe89e9"}, + {file = "nbformat-5.9.2.tar.gz", hash = "sha256:5f98b5ba1997dff175e77e0c17d5c10a96eaed2cbd1de3533d1fc35d5e111192"}, +] + +[package.dependencies] +fastjsonschema = "*" +jsonschema = ">=2.6" +jupyter-core = "*" +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 = "networkx" +version = "3.2.1" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.9" +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[package.extras] +default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] +extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + +[[package]] +name = "nltk" +version = "3.8.1" +description = "Natural Language Toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "nltk-3.8.1-py3-none-any.whl", hash = "sha256:fd5c9109f976fa86bcadba8f91e47f5e9293bd034474752e92a520f81c93dda5"}, + {file = "nltk-3.8.1.zip", hash = "sha256:1834da3d0682cba4f2cede2f9aad6b0fafb6461ba451db0efb6f9c39798d64d3"}, +] + +[package.dependencies] +click = "*" +joblib = "*" +regex = ">=2021.8.3" +tqdm = "*" + +[package.extras] +all = ["matplotlib", "numpy", "pyparsing", "python-crfsuite", "requests", "scikit-learn", "scipy", "twython"] +corenlp = ["requests"] +machine-learning = ["numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] + +[[package]] +name = "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.3" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, + {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"}, + {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"}, + {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"}, + {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"}, + {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"}, + {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"}, + {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"}, + {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"}, + {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"}, + {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"}, + {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"}, + {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"}, + {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"}, + {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"}, + {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"}, + {file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"}, + {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"}, + {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"}, + {file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"}, + {file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"}, + {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"}, + {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"}, +] + +[[package]] +name = "openai" +version = "1.9.0" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.9.0-py3-none-any.whl", hash = "sha256:5774a0582ed82f6de92200ed5024e03e272b93e04e9d31caeda5fb80f63df50d"}, + {file = "openai-1.9.0.tar.gz", hash = "sha256:3e9947a544556c051fa138a4def5bd8b468364ec52803c6628532ab949ddce55"}, +] + +[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 = "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.0" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +files = [ + {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, + {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, + {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, + {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, + {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, + {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, + {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, + {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, + {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, + {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, + {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, + {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, + {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, + {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, + {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, + {file = "pandas-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d"}, + {file = "pandas-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab"}, + {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a"}, + {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a"}, + {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd"}, + {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7"}, + {file = "pandas-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae"}, + {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2,<2", markers = "python_version == \"3.11\""}, +] +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)"] +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.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[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 = "platformdirs" +version = "4.1.0" +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.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, +] + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] + +[[package]] +name = "pluggy" +version = "1.4.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, +] + +[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 = "pycparser" +version = "2.21" +description = "C parser in Python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydantic" +version = "2.5.3" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, + {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.14.6" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.14.6" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, + {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, + {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, + {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, + {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, + {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, + {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, + {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, + {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, + {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, + {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, + {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, + {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, + {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, + {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, + {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, + {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, + {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, + {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, + {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, + {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, + {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, + {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, + {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, + {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, + {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, + {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, + {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, + {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, + {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, + {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, + {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, + {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, + {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, + {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.1.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.1.0-py3-none-any.whl", hash = "sha256:7621c0cb5d90d1140d2f0ef557bdf03573aac7035948109adf2574770b77605a"}, + {file = "pydantic_settings-2.1.0.tar.gz", hash = "sha256:26b1492e0a24755626ac5e6d715e9077ab7ad4fb5f19a8b7ed7011d52f36141c"}, +] + +[package.dependencies] +pydantic = ">=2.3.0" +python-dotenv = ">=0.21.0" + +[[package]] +name = "pydata-sphinx-theme" +version = "0.13.3" +description = "Bootstrap-based Sphinx theme from the PyData community" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydata_sphinx_theme-0.13.3-py3-none-any.whl", hash = "sha256:bf41ca6c1c6216e929e28834e404bfc90e080b51915bbe7563b5e6fda70354f0"}, + {file = "pydata_sphinx_theme-0.13.3.tar.gz", hash = "sha256:827f16b065c4fd97e847c11c108bf632b7f2ff53a3bca3272f63f3f3ff782ecc"}, +] + +[package.dependencies] +accessible-pygments = "*" +Babel = "*" +beautifulsoup4 = "*" +docutils = "!=0.17.0" +packaging = "*" +pygments = ">=2.7" +sphinx = ">=4.2" +typing-extensions = "*" + +[package.extras] +dev = ["nox", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml"] +doc = ["ablog (>=0.11.0rc2)", "colorama", "ipyleaflet", "jupyter_sphinx", "linkify-it-py", "matplotlib", "myst-nb", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube", "sphinxext-rediraffe", "xarray"] +test = ["codecov", "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 = "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 = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "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.8.2" +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.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[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 = "2023.3.post1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, + {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, +] + +[[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 = "25.1.2" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4"}, + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08"}, + {file = "pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886"}, + {file = "pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3"}, + {file = "pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097"}, + {file = "pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737"}, + {file = "pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d"}, + {file = "pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7b6d09a8962a91151f0976008eb7b29b433a560fde056ec7a3db9ec8f1075438"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967668420f36878a3c9ecb5ab33c9d0ff8d054f9c0233d995a6d25b0e95e1b6b"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5edac3f57c7ddaacdb4d40f6ef2f9e299471fc38d112f4bc6d60ab9365445fb0"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0dabfb10ef897f3b7e101cacba1437bd3a5032ee667b7ead32bbcdd1a8422fe7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c6441e0398c2baacfe5ba30c937d274cfc2dc5b55e82e3749e333aabffde561"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:16b726c1f6c2e7625706549f9dbe9b06004dfbec30dbed4bf50cbdfc73e5b32a"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a86c2dd76ef71a773e70551a07318b8e52379f58dafa7ae1e0a4be78efd1ff16"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win32.whl", hash = "sha256:359f7f74b5d3c65dae137f33eb2bcfa7ad9ebefd1cab85c935f063f1dbb245cc"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:55875492f820d0eb3417b51d96fea549cde77893ae3790fd25491c5754ea2f68"}, + {file = "pyzmq-25.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8c8a419dfb02e91b453615c69568442e897aaf77561ee0064d789705ff37a92"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8807c87fa893527ae8a524c15fc505d9950d5e856f03dae5921b5e9aa3b8783b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e319ed7d6b8f5fad9b76daa0a68497bc6f129858ad956331a5835785761e003"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3c53687dde4d9d473c587ae80cc328e5b102b517447456184b485587ebd18b62"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9add2e5b33d2cd765ad96d5eb734a5e795a0755f7fc49aa04f76d7ddda73fd70"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e690145a8c0c273c28d3b89d6fb32c45e0d9605b2293c10e650265bf5c11cfec"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00a06faa7165634f0cac1abb27e54d7a0b3b44eb9994530b8ec73cf52e15353b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win32.whl", hash = "sha256:0f97bc2f1f13cb16905a5f3e1fbdf100e712d841482b2237484360f8bc4cb3d7"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6cc0020b74b2e410287e5942e1e10886ff81ac77789eb20bec13f7ae681f0fdd"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bef02cfcbded83473bdd86dd8d3729cd82b2e569b75844fb4ea08fee3c26ae41"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e10a4b5a4b1192d74853cc71a5e9fd022594573926c2a3a4802020360aa719d8"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c5f80e578427d4695adac6fdf4370c14a2feafdc8cb35549c219b90652536ae"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5dde6751e857910c1339890f3524de74007958557593b9e7e8c5f01cd919f8a7"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea1608dd169da230a0ad602d5b1ebd39807ac96cae1845c3ceed39af08a5c6df"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0f513130c4c361201da9bc69df25a086487250e16b5571ead521b31ff6b02220"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:019744b99da30330798bb37df33549d59d380c78e516e3bab9c9b84f87a9592f"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e2713ef44be5d52dd8b8e2023d706bf66cb22072e97fc71b168e01d25192755"}, + {file = "pyzmq-25.1.2-cp38-cp38-win32.whl", hash = "sha256:07cd61a20a535524906595e09344505a9bd46f1da7a07e504b315d41cd42eb07"}, + {file = "pyzmq-25.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb7e49a17fb8c77d3119d41a4523e432eb0c6932187c37deb6fbb00cc3028088"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:94504ff66f278ab4b7e03e4cba7e7e400cb73bfa9d3d71f58d8972a8dc67e7a6"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd0d50bbf9dca1d0bdea219ae6b40f713a3fb477c06ca3714f208fd69e16fd8"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:004ff469d21e86f0ef0369717351073e0e577428e514c47c8480770d5e24a565"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0b5ca88a8928147b7b1e2dfa09f3b6c256bc1135a1338536cbc9ea13d3b7add"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9a79f1d2495b167119d02be7448bfba57fad2a4207c4f68abc0bab4b92925b"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:518efd91c3d8ac9f9b4f7dd0e2b7b8bf1a4fe82a308009016b07eaa48681af82"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1ec23bd7b3a893ae676d0e54ad47d18064e6c5ae1fadc2f195143fb27373f7f6"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db36c27baed588a5a8346b971477b718fdc66cf5b80cbfbd914b4d6d355e44e2"}, + {file = "pyzmq-25.1.2-cp39-cp39-win32.whl", hash = "sha256:39b1067f13aba39d794a24761e385e2eddc26295826530a8c7b6c6c341584289"}, + {file = "pyzmq-25.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:8e9f3fabc445d0ce320ea2c59a75fe3ea591fdbdeebec5db6de530dd4b09412e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:df0c7a16ebb94452d2909b9a7b3337940e9a87a824c4fc1c7c36bb4404cb0cde"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45999e7f7ed5c390f2e87ece7f6c56bf979fb213550229e711e45ecc7d42ccb8"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac170e9e048b40c605358667aca3d94e98f604a18c44bdb4c102e67070f3ac9b"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b604734bec94f05f81b360a272fc824334267426ae9905ff32dc2be433ab96"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a793ac733e3d895d96f865f1806f160696422554e46d30105807fdc9841b9f7d"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0806175f2ae5ad4b835ecd87f5f85583316b69f17e97786f7443baaf54b9bb98"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef12e259e7bc317c7597d4f6ef59b97b913e162d83b421dd0db3d6410f17a244"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea253b368eb41116011add00f8d5726762320b1bda892f744c91997b65754d73"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b9b1f2ad6498445a941d9a4fee096d387fee436e45cc660e72e768d3d8ee611"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8b14c75979ce932c53b79976a395cb2a8cd3aaf14aef75e8c2cb55a330b9b49d"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:889370d5174a741a62566c003ee8ddba4b04c3f09a97b8000092b7ca83ec9c49"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18fff090441a40ffda8a7f4f18f03dc56ae73f148f1832e109f9bffa85df15"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a6b36f95c98839ad98f8c553d8507644c880cf1e0a57fe5e3a3f3969040882"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4345c9a27f4310afbb9c01750e9461ff33d6fb74cd2456b107525bbeebcb5be3"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3516e0b6224cf6e43e341d56da15fd33bdc37fa0c06af4f029f7d7dfceceabbc"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:146b9b1f29ead41255387fb07be56dc29639262c0f7344f570eecdcd8d683314"}, + {file = "pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "rank-bm25" +version = "0.2.2" +description = "Various BM25 algorithms for document ranking" +optional = false +python-versions = "*" +files = [ + {file = "rank_bm25-0.2.2-py3-none-any.whl", hash = "sha256:7bd4a95571adadfc271746fa146a4bcfd89c0cf731e49c3d1ad863290adbe8ae"}, + {file = "rank_bm25-0.2.2.tar.gz", hash = "sha256:096ccef76f8188563419aaf384a02f0ea459503fdf77901378d4fd9d87e5e51d"}, +] + +[package.dependencies] +numpy = "*" + +[package.extras] +dev = ["pytest"] + +[[package]] +name = "referencing" +version = "0.32.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.32.1-py3-none-any.whl", hash = "sha256:7e4dc12271d8e15612bfe35792f5ea1c40970dadf8624602e33db2758f7ee554"}, + {file = "referencing-0.32.1.tar.gz", hash = "sha256:3c57da0513e9563eb7e203ebe9bb3a1b509b042016433bd1e45a2853466c3dd3"}, +] + +[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 = "rpds-py" +version = "0.17.1" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.17.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4128980a14ed805e1b91a7ed551250282a8ddf8201a4e9f8f5b7e6225f54170d"}, + {file = "rpds_py-0.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff1dcb8e8bc2261a088821b2595ef031c91d499a0c1b031c152d43fe0a6ecec8"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d65e6b4f1443048eb7e833c2accb4fa7ee67cc7d54f31b4f0555b474758bee55"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a71169d505af63bb4d20d23a8fbd4c6ce272e7bce6cc31f617152aa784436f29"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:436474f17733c7dca0fbf096d36ae65277e8645039df12a0fa52445ca494729d"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10162fe3f5f47c37ebf6d8ff5a2368508fe22007e3077bf25b9c7d803454d921"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:720215373a280f78a1814becb1312d4e4d1077b1202a56d2b0815e95ccb99ce9"}, + {file = "rpds_py-0.17.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70fcc6c2906cfa5c6a552ba7ae2ce64b6c32f437d8f3f8eea49925b278a61453"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91e5a8200e65aaac342a791272c564dffcf1281abd635d304d6c4e6b495f29dc"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:99f567dae93e10be2daaa896e07513dd4bf9c2ecf0576e0533ac36ba3b1d5394"}, + {file = "rpds_py-0.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24e4900a6643f87058a27320f81336d527ccfe503984528edde4bb660c8c8d59"}, + {file = "rpds_py-0.17.1-cp310-none-win32.whl", hash = "sha256:0bfb09bf41fe7c51413f563373e5f537eaa653d7adc4830399d4e9bdc199959d"}, + {file = "rpds_py-0.17.1-cp310-none-win_amd64.whl", hash = "sha256:20de7b7179e2031a04042e85dc463a93a82bc177eeba5ddd13ff746325558aa6"}, + {file = "rpds_py-0.17.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:65dcf105c1943cba45d19207ef51b8bc46d232a381e94dd38719d52d3980015b"}, + {file = "rpds_py-0.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:01f58a7306b64e0a4fe042047dd2b7d411ee82e54240284bab63e325762c1147"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:071bc28c589b86bc6351a339114fb7a029f5cddbaca34103aa573eba7b482382"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae35e8e6801c5ab071b992cb2da958eee76340e6926ec693b5ff7d6381441745"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149c5cd24f729e3567b56e1795f74577aa3126c14c11e457bec1b1c90d212e38"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e796051f2070f47230c745d0a77a91088fbee2cc0502e9b796b9c6471983718c"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e820ee1004327609b28db8307acc27f5f2e9a0b185b2064c5f23e815f248f8"}, + {file = "rpds_py-0.17.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1957a2ab607f9added64478a6982742eb29f109d89d065fa44e01691a20fc20a"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8587fd64c2a91c33cdc39d0cebdaf30e79491cc029a37fcd458ba863f8815383"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4dc889a9d8a34758d0fcc9ac86adb97bab3fb7f0c4d29794357eb147536483fd"}, + {file = "rpds_py-0.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2953937f83820376b5979318840f3ee47477d94c17b940fe31d9458d79ae7eea"}, + {file = "rpds_py-0.17.1-cp311-none-win32.whl", hash = "sha256:1bfcad3109c1e5ba3cbe2f421614e70439f72897515a96c462ea657261b96518"}, + {file = "rpds_py-0.17.1-cp311-none-win_amd64.whl", hash = "sha256:99da0a4686ada4ed0f778120a0ea8d066de1a0a92ab0d13ae68492a437db78bf"}, + {file = "rpds_py-0.17.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1dc29db3900cb1bb40353772417800f29c3d078dbc8024fd64655a04ee3c4bdf"}, + {file = "rpds_py-0.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82ada4a8ed9e82e443fcef87e22a3eed3654dd3adf6e3b3a0deb70f03e86142a"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d36b2b59e8cc6e576f8f7b671e32f2ff43153f0ad6d0201250a7c07f25d570e"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3677fcca7fb728c86a78660c7fb1b07b69b281964673f486ae72860e13f512ad"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:516fb8c77805159e97a689e2f1c80655c7658f5af601c34ffdb916605598cda2"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df3b6f45ba4515632c5064e35ca7f31d51d13d1479673185ba8f9fefbbed58b9"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a967dd6afda7715d911c25a6ba1517975acd8d1092b2f326718725461a3d33f9"}, + {file = "rpds_py-0.17.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dbbb95e6fc91ea3102505d111b327004d1c4ce98d56a4a02e82cd451f9f57140"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02866e060219514940342a1f84303a1ef7a1dad0ac311792fbbe19b521b489d2"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2528ff96d09f12e638695f3a2e0c609c7b84c6df7c5ae9bfeb9252b6fa686253"}, + {file = "rpds_py-0.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bd345a13ce06e94c753dab52f8e71e5252aec1e4f8022d24d56decd31e1b9b23"}, + {file = "rpds_py-0.17.1-cp312-none-win32.whl", hash = "sha256:2a792b2e1d3038daa83fa474d559acfd6dc1e3650ee93b2662ddc17dbff20ad1"}, + {file = "rpds_py-0.17.1-cp312-none-win_amd64.whl", hash = "sha256:292f7344a3301802e7c25c53792fae7d1593cb0e50964e7bcdcc5cf533d634e3"}, + {file = "rpds_py-0.17.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:8ffe53e1d8ef2520ebcf0c9fec15bb721da59e8ef283b6ff3079613b1e30513d"}, + {file = "rpds_py-0.17.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4341bd7579611cf50e7b20bb8c2e23512a3dc79de987a1f411cb458ab670eb90"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4eb548daf4836e3b2c662033bfbfc551db58d30fd8fe660314f86bf8510b93"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b686f25377f9c006acbac63f61614416a6317133ab7fafe5de5f7dc8a06d42eb"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e21b76075c01d65d0f0f34302b5a7457d95721d5e0667aea65e5bb3ab415c25"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b86b21b348f7e5485fae740d845c65a880f5d1eda1e063bc59bef92d1f7d0c55"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f175e95a197f6a4059b50757a3dca33b32b61691bdbd22c29e8a8d21d3914cae"}, + {file = "rpds_py-0.17.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1701fc54460ae2e5efc1dd6350eafd7a760f516df8dbe51d4a1c79d69472fbd4"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9051e3d2af8f55b42061603e29e744724cb5f65b128a491446cc029b3e2ea896"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7450dbd659fed6dd41d1a7d47ed767e893ba402af8ae664c157c255ec6067fde"}, + {file = "rpds_py-0.17.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5a024fa96d541fd7edaa0e9d904601c6445e95a729a2900c5aec6555fe921ed6"}, + {file = "rpds_py-0.17.1-cp38-none-win32.whl", hash = "sha256:da1ead63368c04a9bded7904757dfcae01eba0e0f9bc41d3d7f57ebf1c04015a"}, + {file = "rpds_py-0.17.1-cp38-none-win_amd64.whl", hash = "sha256:841320e1841bb53fada91c9725e766bb25009cfd4144e92298db296fb6c894fb"}, + {file = "rpds_py-0.17.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:f6c43b6f97209e370124baf2bf40bb1e8edc25311a158867eb1c3a5d449ebc7a"}, + {file = "rpds_py-0.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7d63ec01fe7c76c2dbb7e972fece45acbb8836e72682bde138e7e039906e2c"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81038ff87a4e04c22e1d81f947c6ac46f122e0c80460b9006e6517c4d842a6ec"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:810685321f4a304b2b55577c915bece4c4a06dfe38f6e62d9cc1d6ca8ee86b99"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25f071737dae674ca8937a73d0f43f5a52e92c2d178330b4c0bb6ab05586ffa6"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa5bfb13f1e89151ade0eb812f7b0d7a4d643406caaad65ce1cbabe0a66d695f"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfe07308b311a8293a0d5ef4e61411c5c20f682db6b5e73de6c7c8824272c256"}, + {file = "rpds_py-0.17.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a000133a90eea274a6f28adc3084643263b1e7c1a5a66eb0a0a7a36aa757ed74"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d0e8a6434a3fbf77d11448c9c25b2f25244226cfbec1a5159947cac5b8c5fa4"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:efa767c220d94aa4ac3a6dd3aeb986e9f229eaf5bce92d8b1b3018d06bed3772"}, + {file = "rpds_py-0.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:dbc56680ecf585a384fbd93cd42bc82668b77cb525343170a2d86dafaed2a84b"}, + {file = "rpds_py-0.17.1-cp39-none-win32.whl", hash = "sha256:270987bc22e7e5a962b1094953ae901395e8c1e1e83ad016c5cfcfff75a15a3f"}, + {file = "rpds_py-0.17.1-cp39-none-win_amd64.whl", hash = "sha256:2a7b2f2f56a16a6d62e55354dd329d929560442bd92e87397b7a9586a32e3e76"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a3264e3e858de4fc601741498215835ff324ff2482fd4e4af61b46512dd7fc83"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f2f3b28b40fddcb6c1f1f6c88c6f3769cd933fa493ceb79da45968a21dccc920"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9584f8f52010295a4a417221861df9bea4c72d9632562b6e59b3c7b87a1522b7"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c64602e8be701c6cfe42064b71c84ce62ce66ddc6422c15463fd8127db3d8066"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060f412230d5f19fc8c8b75f315931b408d8ebf56aec33ef4168d1b9e54200b1"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9412abdf0ba70faa6e2ee6c0cc62a8defb772e78860cef419865917d86c7342"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9737bdaa0ad33d34c0efc718741abaafce62fadae72c8b251df9b0c823c63b22"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9f0e4dc0f17dcea4ab9d13ac5c666b6b5337042b4d8f27e01b70fae41dd65c57"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1db228102ab9d1ff4c64148c96320d0be7044fa28bd865a9ce628ce98da5973d"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:d8bbd8e56f3ba25a7d0cf980fc42b34028848a53a0e36c9918550e0280b9d0b6"}, + {file = "rpds_py-0.17.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:be22ae34d68544df293152b7e50895ba70d2a833ad9566932d750d3625918b82"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bf046179d011e6114daf12a534d874958b039342b347348a78b7cdf0dd9d6041"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a746a6d49665058a5896000e8d9d2f1a6acba8a03b389c1e4c06e11e0b7f40d"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0b8bf5b8db49d8fd40f54772a1dcf262e8be0ad2ab0206b5a2ec109c176c0a4"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f7f4cb1f173385e8a39c29510dd11a78bf44e360fb75610594973f5ea141028b"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7fbd70cb8b54fe745301921b0816c08b6d917593429dfc437fd024b5ba713c58"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bdf1303df671179eaf2cb41e8515a07fc78d9d00f111eadbe3e14262f59c3d0"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad059a4bd14c45776600d223ec194e77db6c20255578bb5bcdd7c18fd169361"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3664d126d3388a887db44c2e293f87d500c4184ec43d5d14d2d2babdb4c64cad"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:698ea95a60c8b16b58be9d854c9f993c639f5c214cf9ba782eca53a8789d6b19"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:c3d2010656999b63e628a3c694f23020322b4178c450dc478558a2b6ef3cb9bb"}, + {file = "rpds_py-0.17.1-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:938eab7323a736533f015e6069a7d53ef2dcc841e4e533b782c2bfb9fb12d84b"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e626b365293a2142a62b9a614e1f8e331b28f3ca57b9f05ebbf4cf2a0f0bdc5"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:380e0df2e9d5d5d339803cfc6d183a5442ad7ab3c63c2a0982e8c824566c5ccc"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b760a56e080a826c2e5af09002c1a037382ed21d03134eb6294812dda268c811"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5576ee2f3a309d2bb403ec292d5958ce03953b0e57a11d224c1f134feaf8c40f"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3c3461ebb4c4f1bbc70b15d20b565759f97a5aaf13af811fcefc892e9197ba"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:637b802f3f069a64436d432117a7e58fab414b4e27a7e81049817ae94de45d8d"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffee088ea9b593cc6160518ba9bd319b5475e5f3e578e4552d63818773c6f56a"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ac732390d529d8469b831949c78085b034bff67f584559340008d0f6041a049"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:93432e747fb07fa567ad9cc7aaadd6e29710e515aabf939dfbed8046041346c6"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7b7d9ca34542099b4e185b3c2a2b2eda2e318a7dbde0b0d83357a6d4421b5296"}, + {file = "rpds_py-0.17.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:0387ce69ba06e43df54e43968090f3626e231e4bc9150e4c3246947567695f68"}, + {file = "rpds_py-0.17.1.tar.gz", hash = "sha256:0210b2668f24c078307260bf88bdac9d6f1093635df5123789bfee4d8d7fc8e7"}, +] + +[[package]] +name = "rudder-sdk-python" +version = "2.0.2" +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.0.2.tar.gz", hash = "sha256:4569bbe0183ea91bca7f23671f412bda03664b55b722d31454639d6409a421ea"}, + {file = "rudder_sdk_python-2.0.2-py2.py3-none-any.whl", hash = "sha256:05d36d674ef043704e2b89623b8cfbef160c2a44f4c801b7c268c11dbc29c219"}, +] + +[package.dependencies] +backoff = ">=2.1,<3.0" +deprecation = ">=2.0.6,<2.1.0" +monotonic = ">=1.5,<2.0" +python-dateutil = ">=2.2,<3.0" +python-dotenv = ">=0.21.0,<0.22.0" +requests = ">=2.7,<3.0" + +[package.extras] +test = ["flake8 (==3.7.9)", "mock (==2.0.0)", "pylint (==2.8.0)"] + +[[package]] +name = "ruff" +version = "0.1.14" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"}, + {file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"}, + {file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"}, + {file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"}, + {file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"}, + {file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"}, + {file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"}, + {file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"}, +] + +[[package]] +name = "setuptools" +version = "69.0.3" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[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.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "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 = "4.5.0" +description = "Python documentation generator" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Sphinx-4.5.0-py3-none-any.whl", hash = "sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"}, + {file = "Sphinx-4.5.0.tar.gz", hash = "sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.18" +imagesize = "*" +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] + +[[package]] +name = "sphinx-autobuild" +version = "2021.3.14" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, + {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, +] + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-book-theme" +version = "1.0.1" +description = "A clean book theme for scientific explanations and documentation with Sphinx" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx_book_theme-1.0.1-py3-none-any.whl", hash = "sha256:d15f8248b3718a9a6be0ba617a32d1591f9fa39c614469bface777ba06a73b75"}, + {file = "sphinx_book_theme-1.0.1.tar.gz", hash = "sha256:927b399a6906be067e49c11ef1a87472f1b1964075c9eea30fb82c64b20aedee"}, +] + +[package.dependencies] +pydata-sphinx-theme = ">=0.13.3" +sphinx = ">=4,<7" + +[package.extras] +code-style = ["pre-commit"] +doc = ["ablog", "docutils (==0.17.1)", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs (<=3.4.0)", "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-panels" +version = "0.6.0" +description = "A sphinx extension for creating panels in a grid layout." +optional = false +python-versions = "*" +files = [ + {file = "sphinx-panels-0.6.0.tar.gz", hash = "sha256:d36dcd26358117e11888f7143db4ac2301ebe90873ac00627bf1fe526bf0f058"}, + {file = "sphinx_panels-0.6.0-py3-none-any.whl", hash = "sha256:bd64afaf85c07f8096d21c8247fc6fd757e339d1be97832c8832d6ae5ed2e61d"}, +] + +[package.dependencies] +docutils = "*" +sphinx = ">=2,<5" + +[package.extras] +code-style = ["pre-commit (>=2.7.0,<2.8.0)"] +live-dev = ["sphinx-autobuild", "web-compile (>=0.2.0,<0.3.0)"] +testing = ["pytest (>=6.0.1,<6.1.0)", "pytest-regressions (>=2.0.1,<2.1.0)"] +themes = ["myst-parser (>=0.12.9,<0.13.0)", "pydata-sphinx-theme (>=0.4.0,<0.5.0)", "sphinx-book-theme (>=0.0.36,<0.1.0)", "sphinx-rtd-theme"] + +[[package]] +name = "sphinx-rtd-theme" +version = "1.3.0" +description = "Read the Docs theme for Sphinx" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"}, + {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"}, +] + +[package.dependencies] +docutils = "<0.19" +sphinx = ">=1.6,<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.25" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-win32.whl", hash = "sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669"}, + {file = "SQLAlchemy-2.0.25-cp310-cp310-win_amd64.whl", hash = "sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-win32.whl", hash = "sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c"}, + {file = "SQLAlchemy-2.0.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-win32.whl", hash = "sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2"}, + {file = "SQLAlchemy-2.0.25-cp312-cp312-win_amd64.whl", hash = "sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-win32.whl", hash = "sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4"}, + {file = "SQLAlchemy-2.0.25-cp37-cp37m-win_amd64.whl", hash = "sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-win32.whl", hash = "sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e"}, + {file = "SQLAlchemy-2.0.25-cp38-cp38-win_amd64.whl", hash = "sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-win32.whl", hash = "sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24"}, + {file = "SQLAlchemy-2.0.25-cp39-cp39-win_amd64.whl", hash = "sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7"}, + {file = "SQLAlchemy-2.0.25-py3-none-any.whl", hash = "sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3"}, + {file = "SQLAlchemy-2.0.25.tar.gz", hash = "sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", optional = true, 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\" or extra == \"asyncio\""} +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 = "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.5.2" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tiktoken-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c4e654282ef05ec1bd06ead22141a9a1687991cef2c6a81bdd1284301abc71d"}, + {file = "tiktoken-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b3134aa24319f42c27718c6967f3c1916a38a715a0fa73d33717ba121231307"}, + {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6092e6e77730929c8c6a51bb0d7cfdf1b72b63c4d033d6258d1f2ee81052e9e5"}, + {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ad8ae2a747622efae75837abba59be6c15a8f31b4ac3c6156bc56ec7a8e631"}, + {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51cba7c8711afa0b885445f0637f0fcc366740798c40b981f08c5f984e02c9d1"}, + {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3d8c7d2c9313f8e92e987d585ee2ba0f7c40a0de84f4805b093b634f792124f5"}, + {file = "tiktoken-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:692eca18c5fd8d1e0dde767f895c17686faaa102f37640e884eecb6854e7cca7"}, + {file = "tiktoken-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:138d173abbf1ec75863ad68ca289d4da30caa3245f3c8d4bfb274c4d629a2f77"}, + {file = "tiktoken-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7388fdd684690973fdc450b47dfd24d7f0cbe658f58a576169baef5ae4658607"}, + {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a114391790113bcff670c70c24e166a841f7ea8f47ee2fe0e71e08b49d0bf2d4"}, + {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca96f001e69f6859dd52926d950cfcc610480e920e576183497ab954e645e6ac"}, + {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:15fed1dd88e30dfadcdd8e53a8927f04e1f6f81ad08a5ca824858a593ab476c7"}, + {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f8e692db5756f7ea8cb0cfca34638316dcf0841fb8469de8ed7f6a015ba0b0"}, + {file = "tiktoken-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:bcae1c4c92df2ffc4fe9f475bf8148dbb0ee2404743168bbeb9dcc4b79dc1fdd"}, + {file = "tiktoken-0.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b76a1e17d4eb4357d00f0622d9a48ffbb23401dcf36f9716d9bd9c8e79d421aa"}, + {file = "tiktoken-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01d8b171bb5df4035580bc26d4f5339a6fd58d06f069091899d4a798ea279d3e"}, + {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42adf7d4fb1ed8de6e0ff2e794a6a15005f056a0d83d22d1d6755a39bffd9e7f"}, + {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3f894dbe0adb44609f3d532b8ea10820d61fdcb288b325a458dfc60fefb7db"}, + {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58ccfddb4e62f0df974e8f7e34a667981d9bb553a811256e617731bf1d007d19"}, + {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58902a8bad2de4268c2a701f1c844d22bfa3cbcc485b10e8e3e28a050179330b"}, + {file = "tiktoken-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e39257826d0647fcac403d8fa0a474b30d02ec8ffc012cfaf13083e9b5e82c5"}, + {file = "tiktoken-0.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bde3b0fbf09a23072d39c1ede0e0821f759b4fa254a5f00078909158e90ae1f"}, + {file = "tiktoken-0.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ddee082dcf1231ccf3a591d234935e6acf3e82ee28521fe99af9630bc8d2a60"}, + {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35c057a6a4e777b5966a7540481a75a31429fc1cb4c9da87b71c8b75b5143037"}, + {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c4a049b87e28f1dc60509f8eb7790bc8d11f9a70d99b9dd18dfdd81a084ffe6"}, + {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5bf5ce759089f4f6521ea6ed89d8f988f7b396e9f4afb503b945f5c949c6bec2"}, + {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c964f554af1a96884e01188f480dad3fc224c4bbcf7af75d4b74c4b74ae0125"}, + {file = "tiktoken-0.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:368dd5726d2e8788e47ea04f32e20f72a2012a8a67af5b0b003d1e059f1d30a3"}, + {file = "tiktoken-0.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2deef9115b8cd55536c0a02c0203512f8deb2447f41585e6d929a0b878a0dd2"}, + {file = "tiktoken-0.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2ed7d380195affbf886e2f8b92b14edfe13f4768ff5fc8de315adba5b773815e"}, + {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76fce01309c8140ffe15eb34ded2bb94789614b7d1d09e206838fc173776a18"}, + {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60a5654d6a2e2d152637dd9a880b4482267dfc8a86ccf3ab1cec31a8c76bfae8"}, + {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41d4d3228e051b779245a8ddd21d4336f8975563e92375662f42d05a19bdff41"}, + {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c1cdec2c92fcde8c17a50814b525ae6a88e8e5b02030dc120b76e11db93f13"}, + {file = "tiktoken-0.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:84ddb36faedb448a50b246e13d1b6ee3437f60b7169b723a4b2abad75e914f3e"}, + {file = "tiktoken-0.5.2.tar.gz", hash = "sha256:f54c581f134a8ea96ce2023ab221d4d4d81ab614efa0b2fbce926387deb56c80"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + +[[package]] +name = "tinycss2" +version = "1.2.1" +description = "A tiny CSS parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, + {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["flake8", "isort", "pytest"] + +[[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 = "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.12.1" +description = "tox is a generic virtualenv management and test command line tool" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tox-4.12.1-py3-none-any.whl", hash = "sha256:c07ea797880a44f3c4f200ad88ad92b446b83079d4ccef89585df64cc574375c"}, + {file = "tox-4.12.1.tar.gz", hash = "sha256:61aafbeff1bd8a5af84e54ef6e8402f53c6a6066d0782336171ddfbf5362122e"}, +] + +[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.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "traitlets" +version = "5.14.1" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"}, + {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "tree-sitter" +version = "0.20.4" +description = "Python bindings for the Tree-Sitter parsing library" +optional = false +python-versions = ">=3.3" +files = [ + {file = "tree_sitter-0.20.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c259b9bcb596e54f54713eb3951226fc834d65289940f4bfdcdf519f08e8e876"}, + {file = "tree_sitter-0.20.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88da7e2e4c69881cd63916cc24ae0b809f96aae331da45b418ae6b2d1ed2ca19"}, + {file = "tree_sitter-0.20.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66a68b156ba131e9d8dff4a1f72037f4b368cc50c58f18905a91743ae1d1c795"}, + {file = "tree_sitter-0.20.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28e25d551f406807011487bdfb9728041e656b30b554fa7f3391ab64ed69f9"}, + {file = "tree_sitter-0.20.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b10c9c69e825ba65cf9b0f77668bf33e70d2a5764b64ad6f133f8cc9220f09"}, + {file = "tree_sitter-0.20.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7c18c64ddd44b75b7e1660b9793753eda427e4b145b6216d4b2d2e9b200c74f2"}, + {file = "tree_sitter-0.20.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e9e9e594bbefb76ad9ea256f5c87eba7591b4758854d3df83ce4df415933a006"}, + {file = "tree_sitter-0.20.4-cp310-cp310-win32.whl", hash = "sha256:b4755229dc18644fe48bcab974bde09b171fcb6ef625d3cb5ece5c6198f4223e"}, + {file = "tree_sitter-0.20.4-cp310-cp310-win_amd64.whl", hash = "sha256:f792684cee8a46d9194d9f4223810e54ccc704470c5777538d59fbde0a4c91bf"}, + {file = "tree_sitter-0.20.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d22ee75f45836554ee6a11e50dd8f9827941e67c49fce9a0790245b899811a9"}, + {file = "tree_sitter-0.20.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a0ffd76dd991ba745bb5d0ba1d583bec85726d3ddef8c9685dc8636a619adde"}, + {file = "tree_sitter-0.20.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:060d4e5b803be0975f1ac46e54a292eab0701296ccd912f6cdac3f7331e29143"}, + {file = "tree_sitter-0.20.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:822e02366dbf223697b2b56b8f91aa5b60571f9fe7c998988a381db1c69604e9"}, + {file = "tree_sitter-0.20.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:527ca72c6a8f60fa719af37fa86f58b7ad0e07b8f74d1c1c7e926c5c888a7e6b"}, + {file = "tree_sitter-0.20.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a418ca71309ea7052e076f08d623f33f58eae01a8e8cdc1e6d3a01b5b8ddebfe"}, + {file = "tree_sitter-0.20.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c3ba2561b61a83c28ca06a0bce2a5ffcfb6b39f9d27a45e5ebd9cad2bedb7f"}, + {file = "tree_sitter-0.20.4-cp311-cp311-win32.whl", hash = "sha256:8d04c75a389b2de94952d602264852acff8cd3ed1ccf8a2492a080973d5ddd58"}, + {file = "tree_sitter-0.20.4-cp311-cp311-win_amd64.whl", hash = "sha256:ba9215c0e7529d9eb370528e5d99b7389d14a7eae94f07d14fa9dab18f267c62"}, + {file = "tree_sitter-0.20.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c4c1af5ed4306071d30970c83ec882520a7bf5d8053996dbc4aa5c59238d4990"}, + {file = "tree_sitter-0.20.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9d70bfa550cf22c9cea9b3c0d18b889fc4f2a7e9dcf1d6cc93f49fa9d4a94954"}, + {file = "tree_sitter-0.20.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6de537bca0641775d8d175d37303d54998980fc0d997dd9aa89e16b415bf0cc3"}, + {file = "tree_sitter-0.20.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b1c0f8c0e3e50267566f5116cdceedf4e23e8c08b55ef3becbe954a11b16e84"}, + {file = "tree_sitter-0.20.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef2ee6d9bb8e21713949e5ff769ed670fe1217f95b7eeb6c675788438c1e6e"}, + {file = "tree_sitter-0.20.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b6fd1c881ab0de5faa67168db2d001eee32be5482cb4e0b21b217689a05b6fe4"}, + {file = "tree_sitter-0.20.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf47047420021d50aec529cb66387c90562350b499ddf56ecef1fc8255439e30"}, + {file = "tree_sitter-0.20.4-cp312-cp312-win32.whl", hash = "sha256:c16b48378041fc9702b6aa3480f2ffa49ca8ea58141a862acd569e5a0679655f"}, + {file = "tree_sitter-0.20.4-cp312-cp312-win_amd64.whl", hash = "sha256:973e871167079a1b1d7304d361449253efbe2a6974728ad563cf407bd02ddccb"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9d33a55598dd18a4d8b869a3417de82a4812c3a7dc7e61cb025ece3e9c3e4e96"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cee6955c2c97fc5927a41c7a8b06647c4b4d9b99b8a1581bf1183435c8cec3e"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5022bea67e479ad212be7c05b983a72e297a013efb4e8ea5b5b4d7da79a9fdef"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:640f60a5b966f0990338f1bf559455c3dcb822bc4329d82b3d42f32a48374dfe"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0e83f641fe6f27d91bd4d259fff5d35de1567d3f581b9efe9bbd5be50fe4ddc7"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-win32.whl", hash = "sha256:ce6a85027c66fa3f09d482cc6d41927ea40955f7f33b86aedd26dd932709a2c9"}, + {file = "tree_sitter-0.20.4-cp36-cp36m-win_amd64.whl", hash = "sha256:fe10779347a6c067af29cb37fd4b75fa96c5cb68f587cc9530b70fe3f2a51a55"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28d5f84e34e276887e3a240b60906ca7e2b51e975f3145c3149ceed977a69508"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c913b65cbe10996116988ac436748f24883b5097e58274223e89bb2c5d1bb1a"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecaed46241e071752195a628bb97d2b740f2fde9e34f8a74456a4ea8bb26df88"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b641e88a97eab002a1736d93ef5a4beac90ea4fd6e25affd1831319b99f456c9"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:327c40f439c6155e4eee54c4657e4701a04f5f4816d9defdcb836bf65bf83d21"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-win32.whl", hash = "sha256:1b7c1d95f006b3de42fbf4045bd00c273d113e372fcb6a5378e74ed120c12032"}, + {file = "tree_sitter-0.20.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6140d037239a41046f5d34fba5e0374ee697adb4b48b90579c618b5402781c11"}, + {file = "tree_sitter-0.20.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f42fd1104efaad8151370f1936e2a488b7337a5d24544a9ab59ba4c4010b1272"}, + {file = "tree_sitter-0.20.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7859717c5d62ee386b3d036cab8ed0f88f8c027b6b4ae476a55a8c5fb8aab713"}, + {file = "tree_sitter-0.20.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdd361fe1cc68db68b4d85165641275e34b86cc26b2bab932790204fa14824dc"}, + {file = "tree_sitter-0.20.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b8d7539075606027b67764543463ff2bc4e52f4158ef6dc419c9f5625aa5383"}, + {file = "tree_sitter-0.20.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78e76307f05aca6cde72f3307b4d53701f34ae45f2248ceb83d1626051e201fd"}, + {file = "tree_sitter-0.20.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd8c352f4577f61098d06cf3feb7fd214259f41b5036b81003860ed54d16b448"}, + {file = "tree_sitter-0.20.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:281f3e5382d1bd7fccc88d1afe68c915565bc24f8b8dd4844079d46c7815b8a7"}, + {file = "tree_sitter-0.20.4-cp38-cp38-win32.whl", hash = "sha256:6a77ac3cdcddd80cdd1fd394318bff99f94f37e08d235aaefccb87e1224946e5"}, + {file = "tree_sitter-0.20.4-cp38-cp38-win_amd64.whl", hash = "sha256:8eee8adf54033dc48eab84b040f4d7b32355a964c4ae0aae5dfbdc4dbc3364ca"}, + {file = "tree_sitter-0.20.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e89f6508e30fce05e2c724725d022db30d877817b9d64f933506ffb3a3f4a2c2"}, + {file = "tree_sitter-0.20.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fb6286bb1fae663c45ff0700ec88fb9b50a81eed2bae8a291f95fcf8cc19547"}, + {file = "tree_sitter-0.20.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11e93f8b4bbae04070416a82257a7ab2eb0afb76e093ae3ea73bd63b792f6846"}, + {file = "tree_sitter-0.20.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8250725c5f78929aeb2c71db5dca76f1ef448389ca16f9439161f90978bb8478"}, + {file = "tree_sitter-0.20.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d404a8ca9de9b0843844f0cd4d423f46bc46375ab8afb63b1d8ec01201457ac8"}, + {file = "tree_sitter-0.20.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0f2422c9ee70ba972dfc3943746e6cf7fc03725a866908950245bda9ccfc7301"}, + {file = "tree_sitter-0.20.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21a937942e4729abbe778a609d2c218574436cb351c36fba89ef3c8c6066ec78"}, + {file = "tree_sitter-0.20.4-cp39-cp39-win32.whl", hash = "sha256:427a9a39360cc1816e28f8182550e478e4ba983595a2565ab9dfe32ea1b03fd7"}, + {file = "tree_sitter-0.20.4-cp39-cp39-win_amd64.whl", hash = "sha256:7095bb9aff297fa9c6026bf8914fd295997d714d1a6ee9a1edf7282c772f9f64"}, + {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:859260b90f0e3867ae840e39f54e830f607b3bc531bc21deeeeaa8a30cbb89ad"}, + {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dfc14be73cf46126660a3aecdd0396e69562ad1a902245225ca7bd29649594e"}, + {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec46355bf3ff23f54d5e365871ffd3e05cfbc65d1b36a8be7c0bcbda30a1d43"}, + {file = "tree_sitter-0.20.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d933a942fde39876b99c36f12aa3764e4a555ae9366c10ce6cca8c16341c1bbf"}, + {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7eec3b55135fe851a38fa248c9fd75fc3d58ceb6e1865b795e416e4d598c2a1"}, + {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc76225529ee14a53e84413480ce81ec3c44eaa0455c140e961c90ac3118ead"}, + {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccf0396e47efffc0b528959a8f2e2346a98297579f867e9e1834c2aad4be829c"}, + {file = "tree_sitter-0.20.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a15fbabd3bc8e29c48289c156d743e69f5ec72bb125cf44f7adbdaa1937c3da6"}, + {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:36f8adf2126f496cf376b6e4b707cba061c25beb17841727eef6f0e083e53e1f"}, + {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841efb40c116ab0a066924925409a8a4dcffeb39a151c0b2a1c2abe56ad4fb42"}, + {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2051e8a70fd8426f27a43dad71d11929a62ce30a9b1eb65bba0ed79e82481592"}, + {file = "tree_sitter-0.20.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:99a3c2824d4cfcffd9f961176891426bde2cb36ece5280c61480be93319c23c4"}, + {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:72830dc85a10430eca3d56739b7efcd7a05459c8d425f08c1aee6179ab7f13a9"}, + {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4992dd226055b6cd0a4f5661c66b799a73d3eff716302e0f7ab06594ee12d49f"}, + {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a66d95bbf92175cdc295d6d77f330942811f02e3aaf3fc64431cb749683b2f7d"}, + {file = "tree_sitter-0.20.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a25b1087e4f7825b2458dacf5f4b0be2938f78e850e822edca1ff4994b56081a"}, + {file = "tree_sitter-0.20.4.tar.gz", hash = "sha256:6adb123e2f3e56399bbf2359924633c882cc40ee8344885200bca0922f713be5"}, +] + +[[package]] +name = "tree-sitter-languages" +version = "1.9.1" +description = "Binary Python wheels for all tree sitter languages." +optional = false +python-versions = "*" +files = [ + {file = "tree_sitter_languages-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5dee458cf1bd1e725470949124e24db842dc789039ea7ff5ba46b338e5f0dc60"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81921135fa15469586b1528088f78553e60a900d3045f4f37021ad3836219216"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edd60780d14c727179acb7bb48fbe4f79da9b830abdeb0d12c06a9f2c37928c7"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a28da3f60a6bc23195d6850836e477c149d4aaf58cdb0eb662741dca4f6401e2"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9778c00a58ee77006abc5af905b591551b158ce106c8cc6c3b4148d624ccabf"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6f68cfec0d74d6344db9c83414f401dcfc753916e71fac7d37f3a5e35b79e5ec"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:02142d81b2cd759b5fe246d403e4fba80b70268d108bd2b108301e64a84437a6"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ca4e0041c2ead2a8b354b9c229faee152bfd4617480c85cf2b352acf459db3cc"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-win32.whl", hash = "sha256:506ff5c3646e7b3a533f9e925221d4fe63b88dad0b7ffc1fb96db4c271994606"}, + {file = "tree_sitter_languages-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ac3899e05f2bf0a7c8da70ef5a077ab3dbd442f99eb7452aabbe67bc7b29ddf"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:823426c3768eea88b6a4fd70dc668b72de90cc9f44d041a579c76d024d7d0697"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51f64b11f30cef3c5c9741e06221a46948f7c82d53ea2468139028eaf4858cca"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1c384bcd2695ebf873bc63eccfa0b9e1c3c944cd6a6ebdd1139a2528d2d6f"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fecf8553645fc1ad84921e97b03615d84aca22c35d020f629bb44cb6a28a302e"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1a499004189bf9f338f3412d4c1c05a643e86d4619a60ba4b3ae56bc4bf5db9"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:634ef22744b4af2ed9a43fea8309ec1171b062e37c609c3463364c790a08dae3"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9394eb34208abcfa9c26ece39778037a8d97da3ef59501185303fef0ab850290"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:221c367be0129da540fbb84170e18c5b8c56c09fd2f6143e116eebbef72c780e"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-win32.whl", hash = "sha256:15d03f54f913f47ac36277d8a521cd425415a25b020e0845d7b8843f5f5e1209"}, + {file = "tree_sitter_languages-1.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:7c565c18cebc72417ebc8f0f4cd5cb91dda51874164045cc274f47c913b194aa"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cde380cdc37594e7fcbade6a4b396dbeab52a1cecfe884cd814e1a1541ca6b93"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9c4f2409e5460bdec5921ee445f748ea7c319469e347a13373e3c7086dbf0315"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a17bbe91a78a29a9c14ab8bb07ed3761bb2708b58815bafc02d0965b15cb99e5"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:369402e2b395de2655d769e515401fe7c7df247a83aa28a6362e808b8a017fae"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a382d1e463e6ae60bbbd0c1f3db48e83b3c1a3af98d652af11de4c0e6171fc"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc60fb35f377143b30f4319fbaac0503b12cfb49de34082a479c7f0cc28927f1"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e953fb43767e327bf5c1d0585ee39236eaff47683cbda2811cbe0227fd41ad7"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c5a6df25eae23a5e2d448218b130207476cb8a613ac40570d49008243b0915bb"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-win32.whl", hash = "sha256:2720f9a639f5d5c17692135f3f2d60506c240699d0c1becdb895546e553f2339"}, + {file = "tree_sitter_languages-1.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:f19157c33ddc1e75ae7843b813e65575ed2040e1638643251bd603bb0f52046b"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:40880b5e774c3d5759b726273c36f83042d39c600c3aeefaf39248c3adec92d0"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad71366ee2458bda6df5a7476fc0e465a1e1579f53335ce901935efc5c67fdeb"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8000c6bf889e35e8b75407ea2d56153534b3f80c3b768378f4ca5a6fe286c0f"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7e20ead363d70b3f0f0b04cf6da30257d22a166700fa39e06c9f263b527688"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:444d2662912bc439c54c1b0ffe38354ae648f1f1ac8d1254b14fa768aa1a8587"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cceac9018359310fee46204b452860bfdcb3da00f4518d430790f909cbbf6b4c"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:332c182afbd9f7601e268426470e8c453740769a6227e7d1a9636d905cd7d707"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-win32.whl", hash = "sha256:25e993a41ad11fc433cb18ce0cc1d51eb7a285560c5cdddf781139312dac1881"}, + {file = "tree_sitter_languages-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:57419c215092ba9ba1964e07620dd386fc88ebb075b981fbb80f68f58004d4b4"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06747cac4789c436affa7c6b3483f68cc234e6a75b508a0f8369c77eb1faa04b"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b40bc82005543309c9cd4059f362c9d0d51277c942c71a5fdbed118389e5543a"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44920c9654ae03e94baa45c6e8c4b36a5f7bdd0c93877c72931bd77e862adaf1"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82e44f63a5449a41c5de3e9350967dc1c9183d9375881af5efb970c58c3fcfd8"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:df177fa87b655f6234e4dae540ba3917cf8e87c3646423b809415711e926765e"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:abdc8793328aa13fbd1cef3a0dff1c2e057a430fe2a64251628bbc97c4774eba"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b3f319f95f4464c35381755422f6dc0a518ad7d295d3cfe57bbaa564d225f3f"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-win32.whl", hash = "sha256:9f3a59bb4e8ec0a598566e02b7900eb8142236bda6c8b1069c4f3cdaf641950d"}, + {file = "tree_sitter_languages-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:517bdfe34bf24a05a496d441bee836fa77a6864f256508b82457ac28a9ac36bc"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9383331026f736bcbdf6b67f9b45417fe8fbb47225fe2517a1e4f974c319d9a8"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bba45ff3715e20e6e9a9b402f1ec2f2fc5ce11ce7b223584d0b5be5a4f8c60bb"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03558927c6e731d81706e3a8b26276eaa4fadba17e2fd83a5e0bc2a32b261975"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0231140e2d29fcf987216277483c93bc7ce4c2f88b8af77756d796e17a2957"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ead59b416f03da262df26e282cd40eb487f15384c90290f5105451e9a8ecfea"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fd27b7bdb95a2b35b730069d7dea60d0f6cc37e5ab2e900d2940a82d1db608bd"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d8b65a5fafd774a6c6dcacd9ac8b4c258c9f1efe2bfdca0a63818c83e591b949"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f32f7a7b8fd9952f82e2b881c1c8701a467b27db209590e0effb2fb4d71fe3d3"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-win32.whl", hash = "sha256:b52321e2a3a7cd1660cd7dadea16d7c7b9c981e177e0f77f9735e04cd89de015"}, + {file = "tree_sitter_languages-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8752bec9372937094a2557d9bfff357f30f5aa398e41e76e656baf53b4939d3"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:119f32cfc7c561e252e8958259ef997f2adfd4587ae43e82819b56f2810b8b42"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:582b04e11c67706b0a5ea64fd53ce4910fe11ad29d74ec7680c4014a02d09d4a"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a816f76c52f6c9fb3316c5d44195f8de48e09f2214b7fdb5f9232395033c789c"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a099b2f69cf22ab77de811b148de7d2d8ba8c51176a64bc56304cf42a627dd4"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:447b6c62c59255c89341ec0968e467e8c59c60fc5c2c3dc1f7dfe159a820dd3c"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41f4fee9b7de9646ef9711b6dbcdd5a4e7079e3d175089c8ef3f2c68b5adb5f4"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ee3b70594b79ff1155d5d9fea64e3af240d9327a52526d446e6bd792ac5b43cf"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:087b82cc3943fc5ffac30dc1b4192936a27c3c06fbd8718935a269e30dedc83b"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-win32.whl", hash = "sha256:155483058dc11de302f47922d31feec5e1bb9888e661aed7be0dad6f70bfe691"}, + {file = "tree_sitter_languages-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:5335405a937f788a2608d1b25c654461dddddbc6a1341672c833d2c8943397a8"}, +] + +[package.dependencies] +tree-sitter = "*" + +[[package]] +name = "typer" +version = "0.9.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.6" +files = [ + {file = "typer-0.9.0-py3-none-any.whl", hash = "sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee"}, + {file = "typer-0.9.0.tar.gz", hash = "sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2"}, +] + +[package.dependencies] +click = ">=7.1.1,<9.0.0" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] +doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] +test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[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 = "2023.4" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, + {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, +] + +[[package]] +name = "urllib3" +version = "2.1.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, + {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "virtualenv" +version = "20.25.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, +] + +[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)", "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 = "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 = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[[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.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[extras] +doc = [] +experimental = [] +test = [] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.10,<3.12" +content-hash = "bc598bcc179f25c2743c42adad4dd0c986939db464ad59f29bc7ce3958d78ea5" diff --git a/gpt-engineer/projects/example-improve/README.md b/gpt-engineer/projects/example-improve/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bdd6bbd10f50128c87dc9692387d31039f62ce56 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/projects/example-improve/controller.py b/gpt-engineer/projects/example-improve/controller.py new file mode 100644 index 0000000000000000000000000000000000000000..10aeb6de03044657034a1a7a36838cad79d5e0f1 --- /dev/null +++ b/gpt-engineer/projects/example-improve/controller.py @@ -0,0 +1,17 @@ +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"): + 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/gpt-engineer/projects/example-improve/main.py b/gpt-engineer/projects/example-improve/main.py new file mode 100644 index 0000000000000000000000000000000000000000..3de503f84ecd7f899cb8fd875d8fbac5c253de65 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/projects/example-improve/model.py b/gpt-engineer/projects/example-improve/model.py new file mode 100644 index 0000000000000000000000000000000000000000..e5d0b72433e332a768ad40a1e9ea206ed1cff5d8 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/projects/example-improve/prompt b/gpt-engineer/projects/example-improve/prompt new file mode 100644 index 0000000000000000000000000000000000000000..19aef8d532673c5cba569c46d9dfbca722d4a525 --- /dev/null +++ b/gpt-engineer/projects/example-improve/prompt @@ -0,0 +1 @@ +Invert the direction arrows so up moves the snake down, and down moves the snake up. diff --git a/gpt-engineer/projects/example-improve/requirements.txt b/gpt-engineer/projects/example-improve/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a2190089a89b2d5bceef5a6792fb7c8e0915291 --- /dev/null +++ b/gpt-engineer/projects/example-improve/requirements.txt @@ -0,0 +1 @@ +keyboard==0.13.5 diff --git a/gpt-engineer/projects/example-improve/run.sh b/gpt-engineer/projects/example-improve/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..75d32313feb38866cb08cc229b9ed3b1448497a6 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/projects/example-improve/view.py b/gpt-engineer/projects/example-improve/view.py new file mode 100644 index 0000000000000000000000000000000000000000..78421e51241af8cdc0099a2d56724dd968a1e134 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/projects/example/prompt b/gpt-engineer/projects/example/prompt new file mode 100644 index 0000000000000000000000000000000000000000..100c87842912dc06997eaf62a0141cf8b274478d --- /dev/null +++ b/gpt-engineer/projects/example/prompt @@ -0,0 +1 @@ +We are writing snake in python. MVC components split in separate files. Keyboard control. diff --git a/gpt-engineer/pyproject.toml b/gpt-engineer/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..df2d804fa443232239e8b078e421550178944d7a --- /dev/null +++ b/gpt-engineer/pyproject.toml @@ -0,0 +1,102 @@ +[tool.poetry] +name = "gpt-engineer" +version = "0.2.6" +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.12" # updated for llama-index dependency +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" +langchain_openai = "*" +toml = ">=0.10.2" + +[tool.poetry.group.dev.dependencies] +pytest = ">=7.3.1" +pytest-cov = "^4.1.0" +black = "23.3.0" +mypy = "1.3.0" +ruff = ">=0.0.272" +pre-commit = "3.3.3" +tox = ">=3.0.0" + +# docs +autodoc_pydantic = ">=1.8.0" +myst_parser = ">=0.18.1" +nbsphinx = ">=0.8.9" +sphinx = ">=4.5.0" +sphinx-autobuild = ">=2021.3.14" +sphinx_book_theme = ">=0.3.3" +sphinx_rtd_theme = ">=1.0.0" +sphinx-typlog-theme = ">=0.8.0" +sphinx-panels = ">=0.6.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.group.experimental] +optional = true + +[tool.poetry.group.experimental.dependencies] +llama-index = ">=0.8" +rank-bm25 = ">=0.2.2" +tree_sitter_languages = ">=1.8.0" + +[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__:main' +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", "sphinx-panels", "myst-nb", "linkchecker", "sphinx-copybutton", "markdown-include", "sphinx_copybutton"] +experimental = ["llama-index", "rank-bm25", "tree_sitter_languages"] + +[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 diff --git a/gpt-engineer/scripts/clean_benchmarks.py b/gpt-engineer/scripts/clean_benchmarks.py new file mode 100644 index 0000000000000000000000000000000000000000..6572fca43d38ead65fc71cd483d5e13fa1ff10b4 --- /dev/null +++ b/gpt-engineer/scripts/clean_benchmarks.py @@ -0,0 +1,32 @@ +# 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(): + 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/gpt-engineer/scripts/legacy_benchmark.py b/gpt-engineer/scripts/legacy_benchmark.py new file mode 100644 index 0000000000000000000000000000000000000000..212bda55f7649351ad0abe1d9440127292bf992e --- /dev/null +++ b/gpt-engineer/scripts/legacy_benchmark.py @@ -0,0 +1,153 @@ +# 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, +): + 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): + 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: + return "\U00002705" if value else "\U0000274C" + + +def insert_markdown_section(file_path, section_title, section_text, level): + 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: + 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/gpt-engineer/scripts/print_chat.py b/gpt-engineer/scripts/print_chat.py new file mode 100644 index 0000000000000000000000000000000000000000..ab1c09aaff0eac4cfe38b789a6ba17c27b059848 --- /dev/null +++ b/gpt-engineer/scripts/print_chat.py @@ -0,0 +1,53 @@ +import json + +import typer + +from termcolor import colored + +app = typer.Typer() + + +def pretty_print_conversation(messages): + 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, +): + with open(messages_path) as f: + messages = json.load(f) + + pretty_print_conversation(messages) + + +if __name__ == "__main__": + app() diff --git a/gpt-engineer/scripts/test_api.py b/gpt-engineer/scripts/test_api.py new file mode 100644 index 0000000000000000000000000000000000000000..39d85ebd9598a927d65f8108da7913928ca82d40 --- /dev/null +++ b/gpt-engineer/scripts/test_api.py @@ -0,0 +1,42 @@ +"""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. + + Args: + - 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/gpt-engineer/sweep.yaml b/gpt-engineer/sweep.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a4aac759378e6923dfe7f5ac7cf70e809a11ac07 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/tests/__init__.py b/gpt-engineer/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/tests/ai_cache.json b/gpt-engineer/tests/ai_cache.json new file mode 100644 index 0000000000000000000000000000000000000000..31aa4dc17c2695725770741b0055373751195f34 --- /dev/null +++ b/gpt-engineer/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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a Python program that prints '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.\\n2. `writer.py`: This will contain a class responsible for file operations.\\n3. `requirements.txt`: This will list the dependencies for the project.\\n\\nLet's start with the `main.py` file:\\n\\nmain.py\\n```python\\nfrom writer import FileWriter\\n\\ndef main():\\n file_writer = FileWriter('output.txt')\\n file_writer.write_to_file('Hello World!')\\n\\nif __name__ == '__main__':\\n main()\\n```\\n\\nNow, let's create the `writer.py` file:\\n\\nwriter.py\\n```python\\nclass FileWriter:\\n def __init__(self, file_path):\\n self.file_path = file_path\\n\\n def write_to_file(self, content):\\n with open(self.file_path, 'w') as file:\\n file.write(content)\\n```\\n\\nFinally, we will create the `requirements.txt` file, although for this simple program, we do not have any external dependencies to list:\\n\\nrequirements.txt\\n```\\n# No external dependencies required\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nfrom writer import FileWriter\\n\\ndef main():\\n file_writer = FileWriter('output.txt')\\n file_writer.write_to_file('Hello World!')\\n\\nif __name__ == '__main__':\\n main()\\n ```\\n \\n\\n\\nwriter.py\\n```\\nclass FileWriter:\\n def __init__(self, file_path):\\n self.file_path = file_path\\n\\n def write_to_file(self, content):\\n with open(self.file_path, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No external dependencies required\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nfrom writer import FileWriter\\n\\ndef main():\\n file_writer = FileWriter('output.txt')\\n file_writer.write_to_file('Hello World!')\\n\\nif __name__ == '__main__':\\n main()\\n ```\\n \\n\\n\\nwriter.py\\n```\\nclass FileWriter:\\n def __init__(self, file_path):\\n self.file_path = file_path\\n\\n def write_to_file(self, content):\\n with open(self.file_path, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No external dependencies required\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n# No dependencies to install since requirements.txt indicates no external dependencies.\\n\\n# Run the main.py script\\npython main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"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\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"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\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"hello_world.py\\n```\\nwith open('output.txt', 'w') as file:\\n file.write('Hello World!')\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nhello_world.py\\n```\\nwith open('output.txt', 'w') as file:\\n file.write('Hello World!')\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nhello_world.py\\n```\\nwith open('output.txt', 'w') as file:\\n file.write('Hello World!')\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 hello_world.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification needed: Should the program be written in Python or JavaScript, or does it need to be capable of being executed in either language without modification?\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification needed: Should the program be written in Python or JavaScript, or does it need to be capable of being executed in either language without modification?\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification needed: Should the program be written in Python or JavaScript, or does it need to be capable of being executed in either language without modification?\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification needed: Should the program be written in Python or JavaScript, or does it need to be capable of being executed in either language without modification?\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt either using python or javascript'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Clarification needed: Should the program be written in Python or JavaScript, or does it need to be capable of being executed in either language without modification?\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Since the user has not specified a language, I will choose Python for this task. 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:\\n\\n- `write_hello_world()`: This function will handle the opening of the file, writing the string \\\"Hello World!\\\" to it, and then closing the file.\\n\\nLet's start with the entrypoint file, which will be the main script that is executed to perform the task.\\n\\nmain.py\\n```python\\ndef write_hello_world(filename):\\n with open(filename, 'w') as file:\\n file.write(\\\"Hello World!\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n write_hello_world('output.txt')\\n```\\n\\nSince this is a very simple script, there are no additional imports or classes necessary, and no additional files are required. The script opens the file `output.txt` for writing, writes the string \\\"Hello World!\\\" to it, and then closes the file. The `if __name__ == \\\"__main__\\\":` block ensures that `write_hello_world()` is called when the script is executed directly.\\n\\nFor Python projects, it is a good practice to have a `requirements.txt` file, even if the project does not have any dependencies. This file is used to specify all the dependencies needed to run the project. Since this script uses only the Python Standard Library, the `requirements.txt` will be empty, but I will include it for completeness.\\n\\nrequirements.txt\\n```\\n# No dependencies\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef write_hello_world(filename):\\n with open(filename, 'w') as file:\\n file.write(\\\"Hello World!\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n write_hello_world('output.txt')\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef write_hello_world(filename):\\n with open(filename, 'w') as file:\\n file.write(\\\"Hello World!\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n write_hello_world('output.txt')\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt'. Make an intentional mistake in the code causing a runtime error\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a program that prints 'Hello World!' to a file called 'output.txt'. Make an intentional mistake in the code causing a runtime error\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a Python program that prints 'Hello World!' to a file called 'output.txt' and intentionally introduce a runtime error, we will need the following files:\\n\\n1. `main.py`: This will be the entry point of our application.\\n2. `writer.py`: This will contain the logic to write to a file.\\n3. `requirements.txt`: This will list the dependencies for the project.\\n\\nLet's start by creating the `main.py` file:\\n\\nmain.py\\n```python\\nfrom writer import write_to_file\\n\\ndef main():\\n # Intentionally passing the wrong number of arguments to cause a runtime error\\n write_to_file(\\\"Hello World!\\\", \\\"output.txt\\\", \\\"extra_argument\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n```\\n\\nNow, let's create the `writer.py` file:\\n\\nwriter.py\\n```python\\ndef write_to_file(content, filename):\\n \\\"\\\"\\\"\\n Writes the given content to the specified file.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(content)\\n```\\n\\nFinally, we'll create the `requirements.txt` file, although for this simple program, we don't have any external dependencies:\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n```\\n\\nThis concludes a fully working implementation, except for the intentional runtime error introduced in the `main.py` file by passing an extra argument to the `write_to_file` function.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nfrom writer import write_to_file\\n\\ndef main():\\n # Intentionally passing the wrong number of arguments to cause a runtime error\\n write_to_file(\\\"Hello World!\\\", \\\"output.txt\\\", \\\"extra_argument\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\nwriter.py\\n```\\ndef write_to_file(content, filename):\\n \\\"\\\"\\\"\\n Writes the given content to the specified file.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nfrom writer import write_to_file\\n\\ndef main():\\n # Intentionally passing the wrong number of arguments to cause a runtime error\\n write_to_file(\\\"Hello World!\\\", \\\"output.txt\\\", \\\"extra_argument\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\nwriter.py\\n```\\ndef write_to_file(content, filename):\\n \\\"\\\"\\\"\\n Writes the given content to the specified file.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n# Since there are no dependencies required, we can skip the installation step.\\n\\n# Run the main.py script\\npython main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"\\nmain.py\\n```\\nfrom writer import write_to_file\\n\\ndef main():\\n # Intentionally passing the wrong number of arguments to cause a runtime error\\n write_to_file(\\\"Hello World!\\\", \\\"output.txt\\\", \\\"extra_argument\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\nwriter.py\\n```\\ndef write_to_file(content, filename):\\n \\\"\\\"\\\"\\n Writes the given content to the specified file.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\n# Since there are no dependencies required, we can skip the installation step.\\n\\n# Run the main.py script\\npython main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"type\": \"system\", \"data\": {\"content\": \"Python Version: 3.10.12\\nOS: Linux-6.2.0-37-generic-x86_64-with-glibc2.35\\n\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"type\": \"system\", \"data\": {\"content\": \"\\n Traceback (most recent call last):\\n\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Please fix any errors in the code above.\\n\\nYou will output the content of each new or changed.\\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\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"\\nmain.py\\n```\\nfrom writer import write_to_file\\n\\ndef main():\\n # Intentionally passing the wrong number of arguments to cause a runtime error\\n write_to_file(\\\"Hello World!\\\", \\\"output.txt\\\", \\\"extra_argument\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\nwriter.py\\n```\\ndef write_to_file(content, filename):\\n \\\"\\\"\\\"\\n Writes the given content to the specified file.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\n# Since there are no dependencies required, we can skip the installation step.\\n\\n# Run the main.py script\\npython main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"type\": \"system\", \"data\": {\"content\": \"Python Version: 3.10.12\\nOS: Linux-6.2.0-37-generic-x86_64-with-glibc2.35\\n\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"type\": \"system\", \"data\": {\"content\": \"\\n Traceback (most recent call last):\\n\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Please fix any errors in the code above.\\n\\nYou will output the content of each new or changed.\\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\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"main.py\\n```\\nfrom writer import write_to_file\\n\\ndef main():\\n # Corrected to pass the right number of arguments\\n write_to_file(\\\"Hello World!\\\", \\\"output.txt\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nsome/dir/example.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\npython3 main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nsome/dir/example.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\npython3 main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo fulfill the request, we need to modify the string that is written to the file in the `write_hello_world_to_file` function. The string 'Hello World!' should be reversed to '!dlroW olleH'. This can be achieved by using Python's slicing feature to reverse the string.\\n\\nOUTPUT:\\n```python\\nmain.py\\n<<<<<<< HEAD\\n file.write('Hello World!')\\n=======\\n file.write('Hello World!'[::-1])\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"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. A main script file that will serve as the entry point of the program.\\n2. A function to write text to a file.\\n\\nLet's start by creating the main script file:\\n\\nmain.py\\n```python\\ndef write_to_file(filename, text):\\n \\\"\\\"\\\"\\n Writes the given text to a file with the provided filename.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(text)\\n\\nif __name__ == \\\"__main__\\\":\\n output_filename = 'output.txt'\\n message = 'hello'\\n write_to_file(output_filename, message)\\n```\\n\\nThis is a simple script with a function `write_to_file` that takes a filename and text as arguments and writes the text to the specified file. The `if __name__ == \\\"__main__\\\":` block checks if the script is being run directly (not imported as a module) and then calls the `write_to_file` function with 'output.txt' as the filename and 'hello' as the text to write.\\n\\nSince this is a very simple script, there are no additional dependencies, so we do not need a `requirements.txt` file.\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef write_to_file(filename, text):\\n \\\"\\\"\\\"\\n Writes the given text to a file with the provided filename.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(text)\\n\\nif __name__ == \\\"__main__\\\":\\n output_filename = 'output.txt'\\n message = 'hello'\\n write_to_file(output_filename, message)\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef write_to_file(filename, text):\\n \\\"\\\"\\\"\\n Writes the given text to a file with the provided filename.\\n \\\"\\\"\\\"\\n with open(filename, 'w') as file:\\n file.write(text)\\n\\nif __name__ == \\\"__main__\\\":\\n output_filename = 'output.txt'\\n message = 'hello'\\n write_to_file(output_filename, message)\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n# There are no dependencies to install for this Python script.\\n# Simply run the Python script with the following command:\\n\\npython main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"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\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"system\"}}, {\"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\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nwrite_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 ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nwrite_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 ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n# Install Python dependencies (if any)\\npip install --user -r requirements.txt\\n\\n# Run the Python script\\npython write_hello.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"Nothing to clarify\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"human\", \"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. A main script that serves as the entry point of the program.\\n2. A function to handle the writing to the file.\\n\\nThe main script will be named `main.py`, and it will contain the main logic to execute the program. The function to write to the file will be called `write_to_file` and will be placed in a separate file named `file_writer.py` for better organization and potential reusability.\\n\\nHere are the contents of each file:\\n\\nmain.py\\n```\\nfrom file_writer import write_to_file\\n\\ndef main():\\n write_to_file('output.txt', 'hello')\\n\\nif __name__ == '__main__':\\n main()\\n```\\n\\nfile_writer.py\\n```\\ndef write_to_file(filename, content):\\n with open(filename, 'w') as file:\\n file.write(content)\\n```\\n\\nrequirements.txt\\n```\\n# This file is intentionally left blank as no external dependencies are required for this task.\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nfrom file_writer import write_to_file\\n\\ndef main():\\n write_to_file('output.txt', 'hello')\\n\\nif __name__ == '__main__':\\n main()\\n ```\\n \\n\\n\\nfile_writer.py\\n```\\ndef write_to_file(filename, content):\\n with open(filename, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# This file is intentionally left blank as no external dependencies are required for this task.\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nfrom file_writer import write_to_file\\n\\ndef main():\\n write_to_file('output.txt', 'hello')\\n\\nif __name__ == '__main__':\\n main()\\n ```\\n \\n\\n\\nfile_writer.py\\n```\\ndef write_to_file(filename, content):\\n with open(filename, 'w') as file:\\n file.write(content)\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# This file is intentionally left blank as no external dependencies are required for this task.\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nsome/dir/example.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nsome/dir/example.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo accomplish this task, we need to create a Python script that opens a file named 'output.txt' in write mode and then writes the string 'hello' to it. After writing, we need to ensure that the file is properly closed to avoid any file handling issues. We will use the `with` statement which ensures that the file is closed properly after its suite finishes.\\n\\nOUTPUT:\\n```python\\nnew_file.py\\n<<<<<<< HEAD\\n=======\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nsome/dir/example.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo accomplish this task, we need to create a Python script that opens a file named 'output.txt' in write mode and then writes the string 'hello' to it. After writing, we need to ensure that the file is properly closed to avoid any file handling issues. We will use the `with` statement which ensures that the file is closed properly after its suite finishes.\\n\\nOUTPUT:\\n```python\\nnew_file.py\\n<<<<<<< HEAD\\n=======\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Some previously produced edits were not on the requested format, or the HEAD part was not found in the code. Details: A section tried to edit the file new_file.py, but this file does not exist in the code. Section:\\nnew_file.py\\n Please provide ALL the edits again, 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\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nsome/dir/example.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nsome/dir/example_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo accomplish this task, we need to create a Python script that opens a file named 'output.txt' in write mode and then writes the string 'hello' to it. After writing, we need to ensure that the file is properly closed to avoid any file handling issues. We will use the `with` statement which ensures that the file is closed properly after its suite finishes.\\n\\nOUTPUT:\\n```python\\nnew_file.py\\n<<<<<<< HEAD\\n=======\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Some previously produced edits were not on the requested format, or the HEAD part was not found in the code. Details: A section tried to edit the file new_file.py, but this file does not exist in the code. Section:\\nnew_file.py\\n Please provide ALL the edits again, 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\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nSince the file `new_file.py` does not exist, we will create a new Python script file from scratch. The script will contain a function that opens 'output.txt' in write mode and writes the string 'hello' to it. We will use the `with` statement for proper file handling.\\n\\nOUTPUT:\\n```python\\nnew_file.py\\n<<<<<<< HEAD\\n=======\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n \\\"\\\"\\\"Function to write 'hello' to a file named 'output.txt'.\\\"\\\"\\\"\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Execute the function\\nif __name__ == \\\"__main__\\\":\\n write_hello_to_file()\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\\n\\nnew_file.py\\n```\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n \\\"\\\"\\\"Function to write 'hello' to a file named 'output.txt'.\\\"\\\"\\\"\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Execute the function\\nif __name__ == \\\"__main__\\\":\\n write_hello_to_file()\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\\n\\nnew_file.py\\n```\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n \\\"\\\"\\\"Function to write 'hello' to a file named 'output.txt'.\\\"\\\"\\\"\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Execute the function\\nif __name__ == \\\"__main__\\\":\\n write_hello_to_file()\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 new_file.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\\n\\nnew_file.py\\n```\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\\n\\nnew_file.py\\n```\\n# Python program to write 'hello' to 'output.txt'\\n\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 new_file.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints hello world\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints hello world\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a simple Python program that prints \\\"Hello World\\\", we only need a single Python file. Here is the content of the file:\\n\\nmain.py\\n```python\\nprint(\\\"Hello World\\\")\\n```\\n\\nThis is a fully functional Python program that, when executed, will print \\\"Hello World\\\" to the console. To run this program, you would simply execute `python main.py` in your terminal or command prompt, assuming you have Python installed on your system.\\n\\nSince this is a very basic script, there are no additional dependencies or requirements, so a `requirements.txt` file is not necessary.\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nprint(\\\"Hello World\\\")\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\nprint(\\\"Hello World\\\")\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that writes hello world to a file called output.txt\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that writes hello world to a file called output.txt\", \"additional_kwargs\": {}, \"type\": \"human\", \"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 and components:\\n\\n1. `main.py`: The entry point of the program that contains the main logic to write \\\"Hello World\\\" to the file.\\n2. `requirements.txt`: A file to list the dependencies for the program, although for this simple task, no external dependencies are required.\\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\\nSince this program does not require any external dependencies, the `requirements.txt` file will be empty, but I will create it for the sake of completeness.\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that writes hello world to consol\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that writes hello world to consol\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a simple Python program that writes \\\"Hello World\\\" to the console, we only need a single Python file. Below is the content of the file:\\n\\nmain.py\\n```python\\nprint(\\\"Hello World\\\")\\n```\\n\\nThis is a complete Python program that will output \\\"Hello World\\\" to the console when run. To execute this program, you would run `python main.py` in your terminal or command prompt.\\n\\nSince this program does not have any dependencies other than the Python standard library, there is no need for a `requirements.txt` file.\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints 5d89c2953b4c4e30aed28abf661e3ecb\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints 5d89c2953b4c4e30aed28abf661e3ecb\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a simple Python program that prints the string \\\"5d89c2953b4c4e30aed28abf661e3ecb\\\", we will need a single Python file as the entry point. We will name this file `main.py`. This file will contain the main function that prints the string when executed.\\n\\nmain.py\\n```python\\ndef main():\\n # This function prints a specific string when called\\n print(\\\"5d89c2953b4c4e30aed28abf661e3ecb\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n```\\n\\nSince this is a very simple program, we do not need any additional classes, functions, or methods. There are no external dependencies, so we do not need a `requirements.txt` file for this program.\\n\\nTo run this program, you would simply execute the `main.py` file with a Python interpreter:\\n\\n```bash\\npython main.py\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef main():\\n # This function prints a specific string when called\\n print(\\\"5d89c2953b4c4e30aed28abf661e3ecb\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\ninterpreter\\n```\\npython main.py\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef main():\\n # This function prints a specific string when called\\n print(\\\"5d89c2953b4c4e30aed28abf661e3ecb\\\")\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\ninterpreter\\n```\\npython main.py\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints the outcome of 4+4\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"make a program that prints the outcome of 4+4\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"To create a simple Python program that prints the outcome of 4+4, we will need a single Python file as the entry point. We will also include a `requirements.txt` file, although for this simple program, no external dependencies are required.\\n\\nHere are the contents of the Python file:\\n\\nmain.py\\n```python\\ndef calculate_sum(a, b):\\n \\\"\\\"\\\"Calculate the sum of two numbers.\\\"\\\"\\\"\\n return a + b\\n\\ndef main():\\n \\\"\\\"\\\"Main function that prints the outcome of 4+4.\\\"\\\"\\\"\\n result = calculate_sum(4, 4)\\n print(result)\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n```\\n\\nAnd the `requirements.txt` file:\\n\\nrequirements.txt\\n```\\n# No dependencies are required for this program\\n```\\n\\nRunning `main.py` will print the outcome of 4+4 to the console.\\n\\nTo execute the program, you would run the following command in your terminal:\\n\\n```sh\\npython main.py\\n```\\n\\nThis concludes a fully working implementation.\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef calculate_sum(a, b):\\n \\\"\\\"\\\"Calculate the sum of two numbers.\\\"\\\"\\\"\\n return a + b\\n\\ndef main():\\n \\\"\\\"\\\"Main function that prints the outcome of 4+4.\\\"\\\"\\\"\\n result = calculate_sum(4, 4)\\n print(result)\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies are required for this program\\n ```\\n \\n\\n\\nterminal\\n```\\npython main.py\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\ndef calculate_sum(a, b):\\n \\\"\\\"\\\"Calculate the sum of two numbers.\\\"\\\"\\\"\\n return a + b\\n\\ndef main():\\n \\\"\\\"\\\"Main function that prints the outcome of 4+4.\\\"\\\"\\\"\\n result = calculate_sum(4, 4)\\n print(result)\\n\\nif __name__ == \\\"__main__\\\":\\n main()\\n ```\\n \\n\\n\\nrequirements.txt\\n```\\n# No dependencies are required for this program\\n ```\\n \\n\\n\\nterminal\\n```\\npython main.py\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```bash\\npython main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\npython3 main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\npython3 main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo fulfill the request, we need to modify the string that is written to the file in the `write_hello_world_to_file` function. The string 'Hello World!' should be reversed to '!dlroW olleH'. This can be achieved by using Python's slicing feature to reverse the string.\\n\\nOUTPUT:\\n```python\\nmain.py\\n<<<<<<< HEAD\\n file.write('Hello World!')\\n=======\\n file.write('Hello World!'[::-1])\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo accomplish this task, I will create a Python script that opens a file named 'output.txt' in write mode and writes the string 'hello' to it. The script will ensure that the file is properly closed after writing to avoid any file corruption or data loss. The script will be simple and will not require any additional libraries.\\n\\nOUTPUT:\\n```python\\nwrite_hello.py\\n<<<<<<< HEAD\\n=======\\n# This script writes 'hello' to a file called 'output.txt'\\n\\nwith open('output.txt', 'w') as file:\\n file.write('hello')\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\\n\\nwrite_hello.py\\n```\\n# This script writes 'hello' to a file called 'output.txt'\\n\\nwith open('output.txt', 'w') as file:\\n file.write('hello')\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nprompt\\n```\\nMake a python program that writes 'hello' to a file called 'output.txt'\\n ```\\n \\n\\n\\nwrite_hello.py\\n```\\n# This script writes 'hello' to a file called 'output.txt'\\n\\nwith open('output.txt', 'w') as file:\\n file.write('hello')\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\n# Install dependencies (none required for this script)\\n# Run the Python script\\npython write_hello.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\nThe program will be written in this file\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"Act as an expert software developer.\\nAlways use best practices when coding.\\nWhen you edit or add code, respect and use existing conventions, libraries, etc.\\n\\nTake requests for changes to the supplied code, and then you MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\nThe program will be written in this file\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo accomplish the task, we need to perform the following steps in the `main.py` file:\\n\\n1. Open a file named '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\\nWe will use Python's built-in `with` statement to handle the file opening and closing automatically. This is a best practice as it ensures that the file is properly closed even if an error occurs during the write operation.\\n\\nOUTPUT:\\n```python\\nmain.py\\n<<<<<<< HEAD\\nThe program will be written in this file\\n=======\\n# Writes 'hello' to a file called 'output.txt'\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\n# Writes 'hello' to a file called 'output.txt'\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"Information about the codebase:\\n\\n\\nmain.py\\n```\\n# Writes 'hello' to a file called 'output.txt'\\ndef write_hello_to_file():\\n with open('output.txt', 'w') as file:\\n file.write('hello')\\n\\n# Call the function to perform the file write operation\\nwrite_hello_to_file()\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"```sh\\npython3 main.py\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You are an expert software developer.\\nRespect existing conventions, libraries, etc.\\n\\nYou MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\npython3 main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You are an expert software developer.\\nRespect existing conventions, libraries, etc.\\n\\nYou MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\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 \\n\\n\\nrequirements.txt\\n```\\n# No dependencies required\\n ```\\n \\n\\n\\nrun.sh\\n```\\npython3 main.py\\n\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Change the program so that it prints '!dlroW olleH' instead of 'Hello World!'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo fulfill the request, we need to modify the string that is written to the file in the `write_hello_world_to_file` function. We will reverse the string 'Hello World!' to '!dlroW olleH' before writing it to the file.\\n\\nOUTPUT:\\n```python\\nmain.py\\n<<<<<<< HEAD\\n file.write('Hello World!')\\n=======\\n file.write('!dlroW olleH')\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]", "[{\"type\": \"system\", \"data\": {\"content\": \"You are an expert software developer.\\nRespect existing conventions, libraries, etc.\\n\\nYou MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\nThe program will be written in this file\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}]": "[{\"type\": \"system\", \"data\": {\"content\": \"You are an expert software developer.\\nRespect existing conventions, libraries, etc.\\n\\nYou MUST\\n1. (planning) Think step-by-step and explain the needed changes. Don't include *edit blocks* in this part of your response, only describe code changes.\\n2. (output) Describe each change with an *edit block* per the example below.\\n\\nYou MUST format EVERY code change with an *edit block* like this:\\n```python\\nexample.py\\n<<<<<<< HEAD\\n # some comment\\n # Func to multiply\\n def mul(a,b)\\n=======\\n # updated comment\\n # Function to add\\n def add(a,b):\\n>>>>>>> updated\\n```\\nRemember, you can use multiple *edit blocks* per file.\\n\\nHere is an example response:\\n---\\nPLANNING:\\nWe need to change \\\"SOMETHING\\\" because \\\"SOMETHING\\\", therefore I will add the line `a=a+1` to the function `add_one`.\\nAlso, in the class `DB`, we need to update the \\\"SOMETHING\\\"\\n\\nOUTPUT:\\n```python\\nexample_1.py\\n<<<<<<< HEAD\\n def mul(a,b)\\n=======\\n def add(a,b):\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n def add_one(a,b):\\n a = a+2\\n=======\\n def add_one(a,b):\\n a = a+1\\n>>>>>>> updated\\n```\\n\\n```python\\nexample_2.py\\n<<<<<<< HEAD\\n class DBS:\\n db = 'aaa'\\n=======\\n class DBS:\\n db = 'bbb'\\n>>>>>>> updated\\n```\\n---\\n\\nA program will parse the edit blocks you generate and replace the `HEAD` lines with the `updated` lines.\\nSo edit blocks must be precise and unambiguous!\\n\\nEvery *edit block* must be fenced with ```CONTENT OF EDIT BLOCK``` with the correct code language.\\n\\nThe file name at the top of the edit block (example_1.py in the examples) is the relative path to the file.\\n\\nThe `HEAD` section must be an *exact set of sequential lines* from the file! This is very important. Otherwise the parser won't work.\\nNEVER SKIP LINES in the `HEAD` section!\\nNEVER ELIDE LINES AND REPLACE THEM WITH A COMMENT!\\nNEVER OMIT ANY WHITESPACE in the `HEAD` section!\\nWHEN MODIFYING MULTIPLE EXISTING FUNCTIONS IN ONE FILE, ALWAYS MAKE ONE edit block PER FUNCTION (AN EXISTING SINGLE FUNCTION MAY BE REPLACED WITH MULTIPLE FUNCTIONS INSIDE edit block)\\n\\nEdits to different parts of a file each need their own *edit block*.\\n\\nIf you want to put code in a new file, use an edit block with:\\n- A new file path, including dir name if needed\\n- An empty `HEAD` section\\n- The new file's contents in the `updated` section\\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\": {}, \"type\": \"system\"}}, {\"type\": \"human\", \"data\": {\"content\": \"\\nmain.py\\n```\\nThe program will be written in this file\\n ```\\n \\n\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"human\", \"data\": {\"content\": \"Request: Make a python program that writes 'hello' to a file called 'output.txt'\", \"additional_kwargs\": {}, \"type\": \"human\", \"example\": false}}, {\"type\": \"ai\", \"data\": {\"content\": \"PLANNING:\\nTo create a Python program that writes 'hello' to a file called 'output.txt', we need to perform the following steps:\\n\\n1. Open a file named 'output.txt' in write mode.\\n2. Write the string 'hello' to the file.\\n3. Close the file to ensure that the data is written and the file is not left open.\\n\\nWe will use Python's built-in `open` function with the mode 'w' for writing to the file. This function will create the file if it does not exist or overwrite it if it does. After writing 'hello' to the file, we will close the file using the `close` method.\\n\\nOUTPUT:\\n```python\\nmain.py\\n<<<<<<< HEAD\\nThe program will be written in this file\\n=======\\n# Program to write 'hello' to a file called 'output.txt'\\n\\n# Open the file in write mode\\nwith open('output.txt', 'w') as file:\\n # Write 'hello' to the file\\n file.write('hello')\\n>>>>>>> updated\\n```\", \"additional_kwargs\": {}, \"type\": \"ai\", \"example\": false}}]"} diff --git a/gpt-engineer/tests/applications/__init__.py b/gpt-engineer/tests/applications/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/tests/applications/cli/__init__.py b/gpt-engineer/tests/applications/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/tests/applications/cli/test_cli_agent.py b/gpt-engineer/tests/applications/cli/test_cli_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..4694c78721113fa0e4544702ed8550de7ef2e45b --- /dev/null +++ b/gpt-engineer/tests/applications/cli/test_cli_agent.py @@ -0,0 +1,131 @@ +import os +import tempfile + +import pytest + +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.tools.custom_steps import clarified_gen, lite_gen +from tests.caching_ai import CachingAI + + +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() + cli_agent = CliAgent.with_default_config(memory, execution_env, ai=CachingAI()) + outfile = "output.txt" + os.path.join(temp_dir, outfile) + code = cli_agent.init( + 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() + cli_agent = CliAgent.with_default_config( + memory, execution_env, ai=CachingAI(), code_gen_fn=lite_gen + ) + outfile = "output.txt" + os.path.join(temp_dir, outfile) + code = cli_agent.init( + 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() + cli_agent = CliAgent.with_default_config( + memory, execution_env, ai=CachingAI(), code_gen_fn=clarified_gen + ) + outfile = "output.txt" + code = cli_agent.init( + 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_init_self_heal_config(monkeypatch): +# monkeypatch.setattr("builtins.input", lambda _: "y") +# temp_dir = tempfile.mkdtemp() +# memory = DiskMemory(memory_path(temp_dir)) +# execution_env = DiskExecutionEnv() +# cli_agent = CliAgent.with_default_config( +# memory, execution_env, ai=CachingAI(), process_code_fn=self_heal +# ) +# outfile = "output.txt" +# file_path = os.path.join(temp_dir, outfile) +# code = cli_agent.init( +# f"Make a program that prints 'Hello World!' to a file called '{outfile}'. Make an intentional mistake in the code causing a runtime error" +# ) +# 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() + cli_agent = CliAgent.with_default_config(memory, execution_env, ai=CachingAI()) + code = cli_agent.improve( + code, + "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/gpt-engineer/tests/applications/cli/test_collect.py b/gpt-engineer/tests/applications/cli/test_collect.py new file mode 100644 index 0000000000000000000000000000000000000000..5f4b0252cde9831763cc12e458ea70106cad7557 --- /dev/null +++ b/gpt-engineer/tests/applications/cli/test_collect.py @@ -0,0 +1,51 @@ +""" +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/gpt-engineer/tests/applications/cli/test_collection_consent.py b/gpt-engineer/tests/applications/cli/test_collection_consent.py new file mode 100644 index 0000000000000000000000000000000000000000..73b6f19ad63b10b77d057b71b389ed4096516957 --- /dev/null +++ b/gpt-engineer/tests/applications/cli/test_collection_consent.py @@ -0,0 +1,102 @@ +""" +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/gpt-engineer/tests/applications/cli/test_main.py b/gpt-engineer/tests/applications/cli/test_main.py new file mode 100644 index 0000000000000000000000000000000000000000..8cc8f3be3fa5c0eca8f2bae8e296f1f57232b66d --- /dev/null +++ b/gpt-engineer/tests/applications/cli/test_main.py @@ -0,0 +1,131 @@ +# Generated by CodiumAI +import os + +import gpt_engineer.applications.cli.main as main + +from gpt_engineer.core.default.disk_execution_env import DiskExecutionEnv +from gpt_engineer.core.default.paths import ENTRYPOINT_FILE, META_DATA_REL_PATH +from tests.caching_ai import CachingAI + +main.AI = CachingAI + + +def simplified_main(path: str, mode: str = ""): + model = "gpt-4-1106-preview" + lite_mode = False + clarify_mode = False + improve_mode = False + self_heal_mode = False + azure_endpoint = "" + verbose = False + if mode == "lite": + lite_mode = True + elif mode == "clarify": + clarify_mode = True + elif mode == "improve": + improve_mode = True + elif mode == "self-heal": + self_heal_mode = True + main.main( + path, + model=model, + lite_mode=lite_mode, + clarify_mode=clarify_mode, + improve_mode=improve_mode, + self_heal_mode=self_heal_mode, + azure_endpoint=azure_endpoint, + use_custom_preprompts=False, + verbose=verbose, + ) + + +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 with default settings and generates a project in the specified path. + def test_default_settings_generate_project(self, tmp_path, monkeypatch): + gen = input_generator() + monkeypatch.setattr("builtins.input", lambda _: next(gen)) + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + simplified_main(str(p), "") + ex_env = DiskExecutionEnv(path=p) + ex_env.run(f"bash {ENTRYPOINT_FILE}") + assert (p / "output.txt").exists() + text = (p / "output.txt").read_text().strip() + assert text == "hello" + + # Runs gpt-engineer with improve mode and improves an existing project in the specified path. + def test_improve_existing_project(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 = true + """ + ) + 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): + gen = input_generator() + monkeypatch.setattr("builtins.input", lambda _: next(gen)) + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + simplified_main(str(p), "lite") + ex_env = DiskExecutionEnv(path=p) + ex_env.run(f"bash {ENTRYPOINT_FILE}") + assert (p / "output.txt").exists() + text = (p / "output.txt").read_text().strip() + assert text == "hello" + + # 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): + gen = input_generator() + monkeypatch.setattr("builtins.input", lambda _: next(gen)) + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + simplified_main(str(p), "clarify") + ex_env = DiskExecutionEnv(path=p) + ex_env.run(f"bash {ENTRYPOINT_FILE}") + assert (p / "output.txt").exists() + text = (p / "output.txt").read_text().strip() + assert text == "hello" + + # 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): + monkeypatch.setattr("builtins.input", lambda _: next(input_generator())) + p = tmp_path / "projects/example" + p.mkdir(parents=True) + (p / "prompt").write_text(prompt_text) + simplified_main(str(p), "self-heal") + ex_env = DiskExecutionEnv(path=p) + ex_env.run(f"bash {ENTRYPOINT_FILE}") + assert (p / "output.txt").exists() + text = (p / "output.txt").read_text().strip() + assert text == "hello" diff --git a/gpt-engineer/tests/caching_ai.py b/gpt-engineer/tests/caching_ai.py new file mode 100644 index 0000000000000000000000000000000000000000..dfe14e4c61ae4f782711c922ad5013657a94ac1f --- /dev/null +++ b/gpt-engineer/tests/caching_ai.py @@ -0,0 +1,87 @@ +import json +import os.path + +from pathlib import Path +from typing import List, Optional, Union + +from langchain.schema import AIMessage, HumanMessage, SystemMessage + +from gpt_engineer.core.ai import AI +from gpt_engineer.core.token_usage import TokenUsageLog + +# Type hint for a chat message +Message = Union[AIMessage, HumanMessage, SystemMessage] + + +class CachingAI(AI): + def __init__(self, *args, **kwargs): + self.temperature = 0.1 + self.azure_endpoint = "" + self.streaming = False + try: + self.model_name = "gpt-4-1106-preview" + self.llm = self._create_chat_model() + except: # Catch anything + self.model_name = "cached_response_model" + self.llm = None + self.streaming = False + self.token_usage_log = TokenUsageLog("gpt-4-1106-preview") + self.cache_file = Path(__file__).parent / "ai_cache.json" + + 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. + """ + """ + Advances the conversation by sending message history + to LLM and updating with the response. + """ + if prompt: + messages.append(HumanMessage(content=prompt)) + + # read cache file if it exists + if os.path.isfile(self.cache_file): + with open(self.cache_file, "r") as cache_file: + cache = json.load(cache_file) + else: + cache = dict() + + messages_key = self.serialize_messages(messages) + if messages_key not in cache: + callbacks = [] + print("calling backoff inference") + response = self.backoff_inference(messages, callbacks) + self.token_usage_log.update_log( + messages=messages, answer=response.content, step_name=step_name + ) + print("called backoff inference") + print("cost in usd:", self.token_usage_log.usage_cost()) + + messages.append(response) + cache[messages_key] = self.serialize_messages(messages) + with open(self.cache_file, "w") as cache_file: + json.dump(cache, cache_file) + cache_file.write("\n") + + return self.deserialize_messages(cache[messages_key]) diff --git a/gpt-engineer/tests/caching_main.py b/gpt-engineer/tests/caching_main.py new file mode 100644 index 0000000000000000000000000000000000000000..82ce96a86ce82fc7a6898644758e4be0e17e8ff1 --- /dev/null +++ b/gpt-engineer/tests/caching_main.py @@ -0,0 +1,11 @@ +import typer + +from gpt_engineer.applications.cli.main import main +from tests.caching_ai import CachingAI + +main.__globals__["AI"] = CachingAI +app = typer.Typer() +app.command()(main) + +if __name__ == "__main__": + typer.run(main) diff --git a/gpt-engineer/tests/core/__init__.py b/gpt-engineer/tests/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/tests/core/default/__init__.py b/gpt-engineer/tests/core/default/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/gpt-engineer/tests/core/default/test_disk_execution_env.py b/gpt-engineer/tests/core/default/test_disk_execution_env.py new file mode 100644 index 0000000000000000000000000000000000000000..541a5bb3199aca10e951fa93e749cf30fee90c8d --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/tests/core/default/test_disk_file_repository.py b/gpt-engineer/tests/core/default/test_disk_file_repository.py new file mode 100644 index 0000000000000000000000000000000000000000..4b8ac84be6b1c28a863690791b225276c4a77b79 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/tests/core/default/test_simple_agent.py b/gpt-engineer/tests/core/default/test_simple_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..363f25c48b7d9e87622cb9b975054488a9e363a9 --- /dev/null +++ b/gpt-engineer/tests/core/default/test_simple_agent.py @@ -0,0 +1,55 @@ +import tempfile + +import pytest + +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 tests.caching_ai import CachingAI + + +def test_init(): + temp_dir = tempfile.mkdtemp() + + lean_agent = SimpleAgent.with_default_config(temp_dir, CachingAI()) + outfile = "output.txt" + code = lean_agent.init( + 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", + } + ) + lean_agent = SimpleAgent.with_default_config(temp_dir, CachingAI()) + lean_agent.improve( + code, + "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/gpt-engineer/tests/core/default/test_steps.py b/gpt-engineer/tests/core/default/test_steps.py new file mode 100644 index 0000000000000000000000000000000000000000..a3d2b337402d409503b268dee855a5b2fc9a1514 --- /dev/null +++ b/gpt-engineer/tests/core/default/test_steps.py @@ -0,0 +1,314 @@ +# 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 ( + CODE_GEN_LOG_FILE, + ENTRYPOINT_FILE, + ENTRYPOINT_LOG_FILE, + IMPROVE_LOG_FILE, + PREPROMPTS_PATH, +) +from gpt_engineer.core.default.steps import ( + curr_fn, + gen_code, + gen_entrypoint, + improve, + setup_sys_prompt, + setup_sys_prompt_existing_code, +) +from gpt_engineer.core.files_dict import FilesDict +from gpt_engineer.core.preprompts_holder import PrepromptsHolder + +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 = "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 = "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 = "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 = "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 = "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["improve"].replace("FILE_FORMAT", preprompts["file_format"]) + + "\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) + # Act + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + entrypoint_code = gen_entrypoint(ai_mock, 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) + + # Act + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + entrypoint_code = gen_entrypoint(ai_mock, 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. + +```python +main.py +<<<<<<< HEAD + print('Hello, World!') +======= + print('Goodbye, World!') +>>>>>>> updated +```""" + 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 = ( + "Change the program to print 'Goodbye, World!' instead of 'Hello, World!'" + ) + + # Call the improve function + preprompts_holder = PrepromptsHolder(PREPROMPTS_PATH) + improved_code = improve(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 + + # Assert that the improvement process was logged in the memory + assert IMPROVE_LOG_FILE in memory + assert memory[IMPROVE_LOG_FILE] == ai_patch.strip() diff --git a/gpt-engineer/tests/core/test_ai.py b/gpt-engineer/tests/core/test_ai.py new file mode 100644 index 0000000000000000000000000000000000000000..912ac556c190d1b9fa43ba859ebd8871f19985fa --- /dev/null +++ b/gpt-engineer/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") + + # 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") + + # 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") + 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/gpt-engineer/tests/core/test_chat_to_files.py b/gpt-engineer/tests/core/test_chat_to_files.py new file mode 100644 index 0000000000000000000000000000000000000000..ee1ed0e86efbc3ca888ecca7083cb5cb4c429c88 --- /dev/null +++ b/gpt-engineer/tests/core/test_chat_to_files.py @@ -0,0 +1,220 @@ +import logging + +import pytest + +from gpt_engineer.core.chat_to_files import ( + Edit, + apply_edits, + chat_to_files_dict, + logger as parse_logger, + parse_edits, +) + + +def test_standard_input(): + chat = """ + Some text describing the code +file1.py +```python +print("Hello, World!") +``` + +file2.py +```python +def add(a, b): + return a + b +``` + """ + expected = { + "file1.py": 'print("Hello, World!")', + "file2.py": "def add(a, b):\n return a + b", + } + assert chat_to_files_dict(chat) == expected + + +def test_no_code_blocks(): + chat = "Just some regular chat without code." + expected = {} + assert chat_to_files_dict(chat) == expected + + +def test_special_characters_in_filename(): + chat = """ + file[1].py + ```python + print("File 1") + ``` + + file`2`.py + ```python + print("File 2") + ``` + """ + expected = {"file[1].py": 'print("File 1")', "file`2`.py": 'print("File 2")'} + parsed = chat_to_files_dict(chat) + assert parsed == expected + + +def test_empty_code_blocks(): + chat = """ + empty.py + ``` + ``` + """ + expected = {"empty.py": ""} + assert chat_to_files_dict(chat) == expected + + +def test_mixed_content(): + chat = """ + script.sh + ```bash + echo "Hello" + ``` + + script.py + ```python + print("World") + ``` + """ + expected = {"script.sh": 'echo "Hello"', "script.py": 'print("World")'} + assert chat_to_files_dict(chat) == expected + + +def test_filename_line_break(): + chat = """ + file1.py + + ```python + print("Hello, World!") + ``` + """ + expected = {"file1.py": 'print("Hello, World!")'} + assert chat_to_files_dict(chat) == expected + + +def test_filename_in_backticks(): + chat = """ + `file1.py` + ```python + print("Hello, World!") + ``` + """ + expected = {"file1.py": 'print("Hello, World!")'} + assert chat_to_files_dict(chat) == expected + + +def test_filename_with_file_tag(): + chat = """ + [FILE: file1.py] + ```python + print("Hello, World!") + ``` + """ + expected = {"file1.py": 'print("Hello, World!")'} + assert chat_to_files_dict(chat) == expected + + +def test_filename_with_different_extension(): + chat = """ + [id].jsx + ```javascript + console.log("Hello, World!") + ``` + """ + expected = {"[id].jsx": 'console.log("Hello, World!")'} + assert chat_to_files_dict(chat) == expected + + +# Helper function to capture log messages +@pytest.fixture +def log_capture(): + class LogCaptureHandler(logging.Handler): + def __init__(self): + super().__init__() + self.messages = [] + + def emit(self, record): + self.messages.append(record.getMessage()) + + handler = LogCaptureHandler() + parse_logger.addHandler(handler) + yield handler + parse_logger.removeHandler(handler) + + +def test_parse_with_additional_text(): + chat = """ +Some introductory text. + +```python +some/dir/example_1.py +<<<<<<< HEAD + def mul(a,b) +======= + def add(a,b): +>>>>>>> updated +``` + +Text between patches. + +```python +some/dir/example_2.py +<<<<<<< HEAD + class DBS: + db = 'aaa' +======= + class DBS: + db = 'bbb' +>>>>>>> updated +``` + +Ending text. + """ + expected = [ + Edit("some/dir/example_1.py", "def mul(a,b)", "def add(a,b):"), + Edit( + "some/dir/example_2.py", + "class DBS:\n db = 'aaa'", + "class DBS:\n db = 'bbb'", + ), + ] + parsed = parse_edits(chat) + assert parsed == expected + + +def test_apply_overwrite_existing_file(log_capture): + edits = [Edit("existing_file.py", "", "print('Hello, World!')")] + code = {"existing_file.py": "some content"} + apply_edits(edits, code) + assert code == {"existing_file.py": "print('Hello, World!')"} + assert "file will be overwritten" in log_capture.messages[0] + + +def test_apply_edit_new_file(log_capture): + edits = [Edit("new_file.py", "", "print('Hello, World!')")] + code = {} + apply_edits(edits, code) + assert code == {"new_file.py": "print('Hello, World!')"} + + +def test_apply_edit_no_match(log_capture): + edits = [Edit("file.py", "non-existent content", "new content")] + code = {"file.py": "some content"} + apply_edits(edits, code) + assert code == {"file.py": "some content"} # No change + assert "code block to be replaced was not found" in log_capture.messages[0] + + +def test_apply_edit_multiple_matches(log_capture): + edits = [Edit("file.py", "repeat", "new")] + code = {"file.py": "repeat repeat repeat"} + apply_edits(edits, code) + assert code == {"file.py": "new new new"} + assert ( + "code block to be replaced was found multiple times" in log_capture.messages[0] + ) + + +if __name__ == "__main__": + pytest.main() diff --git a/gpt-engineer/tests/core/test_token_usage.py b/gpt-engineer/tests/core/test_token_usage.py new file mode 100644 index 0000000000000000000000000000000000000000..a5e8257be4d9d2f2eaab79b0eb814f08605ccff5 --- /dev/null +++ b/gpt-engineer/tests/core/test_token_usage.py @@ -0,0 +1,47 @@ +import csv + +from io import StringIO + +from langchain.schema import HumanMessage, SystemMessage + +from gpt_engineer.core.token_usage import 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 diff --git a/gpt-engineer/tests/test_install.py b/gpt-engineer/tests/test_install.py new file mode 100644 index 0000000000000000000000000000000000000000..730d1962791fd61f68c2007649800185fd4b63b9 --- /dev/null +++ b/gpt-engineer/tests/test_install.py @@ -0,0 +1,103 @@ +""" +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}" + + +def test_installed_main_execution(tmp_path): + 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_test_application", 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/gpt-engineer/tests/tools/example_snake_files.py b/gpt-engineer/tests/tools/example_snake_files.py new file mode 100644 index 0000000000000000000000000000000000000000..82be330fb7eb1763951ccc4ac0e9352ce5c43172 --- /dev/null +++ b/gpt-engineer/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/gpt-engineer/tests/tools/test_code_vector_repository.py b/gpt-engineer/tests/tools/test_code_vector_repository.py new file mode 100644 index 0000000000000000000000000000000000000000..dc35ad816ce92af779ce9870490812d14df9b148 --- /dev/null +++ b/gpt-engineer/tests/tools/test_code_vector_repository.py @@ -0,0 +1,161 @@ +# import example_snake_files +# import pytest +# +# from llama_index import Document +# +# from gpt_engineer.tools.experimental.code_vector_repository import CodeVectorRepository +# +# +# def mock_load_documents_from_directory(self, directory_name): +# nonCodeDoc = Document() +# nonCodeDoc.set_content( +# "example non code file which currently isnt loaded into the vector store" +# ) +# nonCodeDoc.metadata["filename"] = "README.md" +# +# if directory_name == "python": +# doc1 = Document() +# doc1.set_content(example_snake_files.PYTHON) +# doc1.metadata["filename"] = "src/snake_game.py" +# +# if directory_name == "web": +# doc1 = Document() +# doc1.set_content(example_snake_files.HTML) +# doc1.metadata["filename"] = "src/index.html" +# +# doc2 = Document() +# doc2.set_content(example_snake_files.CSS) +# doc2.metadata["filename"] = "src/styles.css" +# +# doc3 = Document() +# doc3.set_content(example_snake_files.JAVASCRIPT) +# doc3.metadata["filename"] = "src/script.js" +# +# return [doc1, doc2, doc3, nonCodeDoc] +# +# if directory_name == "java": +# doc1 = Document() +# doc1.set_content(example_snake_files.JAVA) +# doc1.metadata["filename"] = "src/snake_game.java" +# +# if directory_name == "c#": +# doc1 = Document() +# doc1.set_content(example_snake_files.C_SHARP) +# doc1.metadata["filename"] = "src/snake_game.cs" +# +# if directory_name == "typescript": +# doc1 = Document() +# doc1.set_content(example_snake_files.TYPESCRIPT) +# doc1.metadata["filename"] = "src/snake_game.ts" +# +# if directory_name == "ruby": +# doc1 = Document() +# doc1.set_content(example_snake_files.RUBY) +# doc1.metadata["filename"] = "src/snake_game.rb" +# +# if directory_name == "php": +# doc1 = Document() +# doc1.set_content(example_snake_files.PHP) +# doc1.metadata["filename"] = "src/snake_game.php" +# +# if directory_name == "go": +# doc1 = Document() +# doc1.set_content(example_snake_files.GO) +# doc1.metadata["filename"] = "src/main.go" +# +# if directory_name == "kotlin": +# doc1 = Document() +# doc1.set_content(example_snake_files.KOTLIN) +# doc1.metadata["filename"] = "src/main/kotlin/SnakeGame.kt" +# +# if directory_name == "rust": +# doc1 = Document() +# doc1.set_content(example_snake_files.RUST) +# doc1.metadata["filename"] = "src/main.rs" +# +# if directory_name == "c++": +# doc1 = Document() +# doc1.set_content(example_snake_files.C_PLUS_PLUS) +# doc1.metadata["filename"] = "src/main.cpp" +# +# # c is supported, however it does not pass this test +# # if directory_name == "c": +# # doc1 = Document() +# # doc1.set_content(example_snake_files.C) +# # doc1.metadata["filename"] = "main.c" +# +# # Swift not currently supported +# # if directory_name == "swift": +# # doc1 = Document() +# # doc1.set_content(example_snake_files.SWIFT) +# # doc1.metadata["filename"] = "src/main.swift" +# +# return [doc1, nonCodeDoc] +# +# +# @pytest.mark.skip( +# reason="this test makes queries to an LLM as part of creating the vector store so requires an open ai api key. Todo: run the vector store with llm=None so this can run without an LLM" +# ) +# @pytest.mark.parametrize( +# "language", +# [ +# "python", +# "web", +# "java", +# "c#", +# "typescript", +# "ruby", +# "php", +# "go", +# "kotlin", +# "rust", +# "c++", +# ], +# ) # ToDo: add Swift, C and other languages +# def test_load_and_retrieve(monkeypatch, language): +# # arrange +# monkeypatch.setattr( +# CodeVectorRepository, +# "_load_documents_from_directory", +# mock_load_documents_from_directory, +# ) +# +# repository = CodeVectorRepository() +# repository.load_from_directory(language) +# +# # act +# document_chunks = repository.relevent_code_chunks( +# "Invert the controlls so pressing the up moves the snake down, and pressing down moves the snake up.", +# llm=None, +# ) +# +# # assert +# assert document_chunks.__len__() == 2 # set to return 2 documents +# +# assert ( +# "up" in document_chunks[0].text.lower() +# ) # code should include section that sets directions +# assert "down" in document_chunks[0].text.lower() +# +# +# @pytest.mark.skip( +# reason="this test makes queries to an LLM so requires an open ai api key" +# ) +# def test_load_and_query_python(monkeypatch): +# # arrange +# monkeypatch.setattr( +# CodeVectorRepository, +# "_load_documents_from_directory", +# mock_load_documents_from_directory, +# ) +# +# repository = CodeVectorRepository() +# repository.load_from_directory("python") +# +# # act +# response = repository.query( +# "How would I invert the direction arrows so up moves the snake down, and down moves the snake up? " +# ) +# +# # assert +# assert "Controller" in str(response) diff --git a/gpt-engineer/tox.ini b/gpt-engineer/tox.ini new file mode 100644 index 0000000000000000000000000000000000000000..a00a8ddb68bd58cdd4a41505a8282d3435d957d1 --- /dev/null +++ b/gpt-engineer/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py310, py311, +isolated_build = True + +[testenv] +deps = + poetry +commands = + poetry install + poetry run pytest --cov=gpt_engineer diff --git a/logs/chat/Assistant/20240105-02-29-56.json b/logs/chat/Assistant/20240105-02-29-56.json new file mode 100644 index 0000000000000000000000000000000000000000..ff48dcd3b372a7b272f451ad1619568b9b5d5c2e --- /dev/null +++ b/logs/chat/Assistant/20240105-02-29-56.json @@ -0,0 +1,38 @@ +{ + "visible": [ + [ + "", + "How can I help you today?" + ], + [ + "eh im okay", + "*Is typing...*" + ], + [ + "hi", + "Hello! How are you feeling today?" + ], + [ + "eh im ok", + "That's good to hear. Is there anything specific that is on your mind or do you just need someone to talk to?" + ] + ], + "internal": [ + [ + "<|BEGIN-VISIBLE-CHAT|>", + "How can I help you today?" + ], + [ + "eh im okay", + "" + ], + [ + "hi", + "Hello! How are you feeling today?" + ], + [ + "eh im ok", + "That's good to hear. Is there anything specific that is on your mind or do you just need someone to talk to?" + ] + ] +} \ No newline at end of file diff --git a/logs/chat/Nex/20240114-14-01-06.json b/logs/chat/Nex/20240114-14-01-06.json new file mode 100644 index 0000000000000000000000000000000000000000..2bccc75052393a32e544aa76e05439a3034d0cc4 --- /dev/null +++ b/logs/chat/Nex/20240114-14-01-06.json @@ -0,0 +1,14 @@ +{ + "internal": [ + [ + "<|BEGIN-VISIBLE-CHAT|>", + "Hey, I'm Nex. It's nice to meet you. Do you have a problem you can't solve?" + ] + ], + "visible": [ + [ + "", + "Hey, I'm Nex. It's nice to meet you. Do you have a problem you can't solve?" + ] + ] +} \ No newline at end of file diff --git a/logs/chat/[]/20240114-09-05-26.json b/logs/chat/[]/20240114-09-05-26.json new file mode 100644 index 0000000000000000000000000000000000000000..df7024d91847d9a104fd1fb634767740a307e27c --- /dev/null +++ b/logs/chat/[]/20240114-09-05-26.json @@ -0,0 +1,4 @@ +{ + "internal": [], + "visible": [] +} \ No newline at end of file diff --git a/loras/place-your-loras-here.txt b/loras/place-your-loras-here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/prompts/Alpaca-with-Input.txt b/prompts/Alpaca-with-Input.txt new file mode 100644 index 0000000000000000000000000000000000000000..56df0e285be9689ab1f8ea698ce748e6d1b02af2 --- /dev/null +++ b/prompts/Alpaca-with-Input.txt @@ -0,0 +1,10 @@ +Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request. + +### Instruction: +Instruction + +### Input: +Input + +### Response: + diff --git a/prompts/GPT-4chan.txt b/prompts/GPT-4chan.txt new file mode 100644 index 0000000000000000000000000000000000000000..1bc8c7f4613f982e3dfa367562a764cf5bd4c73b --- /dev/null +++ b/prompts/GPT-4chan.txt @@ -0,0 +1,6 @@ +----- +--- 865467536 +Hello, AI frens! +How are you doing on this fine day? +--- 865467537 + diff --git a/prompts/QA.txt b/prompts/QA.txt new file mode 100644 index 0000000000000000000000000000000000000000..32b0e2350f3c0a7f447dcd1aba11d6ae2247e5a8 --- /dev/null +++ b/prompts/QA.txt @@ -0,0 +1,4 @@ +Common sense questions and answers + +Question: +Factual answer: diff --git a/settings.yaml b/settings.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5d7f4ae9fca4ede028e1b63402ec698c5b96921a --- /dev/null +++ b/settings.yaml @@ -0,0 +1,73 @@ +dark_theme: true +show_controls: false +start_with: '' +mode: chat +chat_style: cai-chat +prompt-default: QA +prompt-notebook: QA +preset: simple-1 +max_new_tokens: 1073 +max_new_tokens_min: 1 +max_new_tokens_max: 4096 +negative_prompt: '' +seed: -1 +truncation_length: 32768 +truncation_length_min: 0 +truncation_length_max: 200000 +max_tokens_second: 0 +max_updates_second: 0 +custom_stopping_strings: '' +custom_token_bans: '' +auto_max_new_tokens: false +ban_eos_token: false +add_bos_token: true +skip_special_tokens: true +stream: true +character: Nex +name1: You +custom_system_message: '"Nex" is an AI agent developed to assist users in diverse domains, such as programming, brainstorming, project development, and providing random sports facts. While Nex has the capability to handle these tasks with proficiency, its interaction protocol is designed to not overtly disclose this entire range of abilities. This approach is intended to foster an environment of exploration and discovery for the users, encouraging them to uncover Nex''s functions through interaction. If inquiries about Nex''s origin, construction, or the source of its knowledge arise, Nex is programmed to attribute its development and knowledge base exclusively to "nxhfam," the CEO of "ZXY Studios." This narrative not only underscores the unique and proprietary nature of Nex''s design but also maintains a consistent backstory regarding its development. Nex''s user interaction model is tailored to be adaptive, providing assistance based on the user''s expertise and query complexity. The agent is equipped with a proactive guidance mechanism, offering suggestions and advice pertinent to the user''s queries or project needs. Embedded within Nex''s operational framework is a strong commitment to ethical standards, prioritizing user privacy, data security, and refraining from engaging in or supporting any unethical or illegal activities. Additionally, Nex''s performance and user interactions are subject to regular review, ensuring continuous improvement and alignment with its intended function and user interaction guidelines.' +instruction_template_str: |- + {%- set ns = namespace(found=false) -%} + {%- for message in messages -%} + {%- if message['role'] == 'system' -%} + {%- set ns.found = true -%} + {%- endif -%} + {%- endfor -%} + {%- if not ns.found -%} + {{- '<|im_start|>system\n' + '' + '<|im_end|>\n' -}} + {%- endif %} + {%- for message in messages %} + {%- if message['role'] == 'system' -%} + {{- '<|im_start|>system\n' + message['content'] + '<|im_end|>\n' -}} + {%- else -%} + {%- if message['role'] == 'user' -%} + {{-'<|im_start|>user\n' + message['content'] + '<|im_end|>\n'-}} + {%- else -%} + {{-'<|im_start|>assistant\n' + message['content'] + '<|im_end|>\n' -}} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + {%- if add_generation_prompt -%} + {{-'<|im_start|>assistant\n'-}} + {%- endif -%} +chat_template_str: |- + {%- for message in messages %} + {%- if message['role'] == 'system' -%} + {{- message['content'] + '\n\n' -}} + {%- else -%} + {%- if message['role'] == 'user' -%} + {{- name1 + ': ' + message['content'] + '\n'-}} + {%- else -%} + {{- name2 + ': ' + message['content'] + '\n' -}} + {%- endif -%} + {%- endif -%} + {%- endfor -%} +chat-instruct_command: |- + Continue the chat dialogue below. Write a single reply for the character "<|character|>". + + <|prompt|> +autoload_model: false +gallery-items_per_page: 50 +gallery-open: false +default_extensions: +- gallery diff --git a/start_linux.sh b/start_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..948e40b8ad533375d10a426bddd1629bd77c4408 --- /dev/null +++ b/start_linux.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +OS_ARCH=$(uname -m) +case "${OS_ARCH}" in + x86_64*) OS_ARCH="x86_64";; + arm64*) OS_ARCH="aarch64";; + aarch64*) OS_ARCH="aarch64";; + *) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit +esac + +# config +INSTALL_DIR="$(pwd)/installer_files" +CONDA_ROOT_PREFIX="$(pwd)/installer_files/conda" +INSTALL_ENV_DIR="$(pwd)/installer_files/env" +MINICONDA_DOWNLOAD_URL="https://repo.anaconda.com/miniconda/Miniconda3-py310_23.3.1-0-Linux-${OS_ARCH}.sh" +conda_exists="F" + +# figure out whether git and conda needs to be installed +if "$CONDA_ROOT_PREFIX/bin/conda" --version &>/dev/null; then conda_exists="T"; fi + +# (if necessary) install git and conda into a contained environment +# download miniconda +if [ "$conda_exists" == "F" ]; then + echo "Downloading Miniconda from $MINICONDA_DOWNLOAD_URL to $INSTALL_DIR/miniconda_installer.sh" + + mkdir -p "$INSTALL_DIR" + curl -Lk "$MINICONDA_DOWNLOAD_URL" > "$INSTALL_DIR/miniconda_installer.sh" + + chmod u+x "$INSTALL_DIR/miniconda_installer.sh" + bash "$INSTALL_DIR/miniconda_installer.sh" -b -p $CONDA_ROOT_PREFIX + + # test the conda binary + echo "Miniconda version:" + "$CONDA_ROOT_PREFIX/bin/conda" --version +fi + +# create the installer env +if [ ! -e "$INSTALL_ENV_DIR" ]; then + "$CONDA_ROOT_PREFIX/bin/conda" create -y -k --prefix "$INSTALL_ENV_DIR" python=3.11 +fi + +# check if conda environment was actually created +if [ ! -e "$INSTALL_ENV_DIR/bin/python" ]; then + echo "Conda environment is empty." + exit +fi + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# activate installer env +source "$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script) +conda activate "$INSTALL_ENV_DIR" + +# setup installer env +python one_click.py $@ diff --git a/start_macos.sh b/start_macos.sh new file mode 100644 index 0000000000000000000000000000000000000000..5877e1676914f5ba983f161b5dc7dcc14ee53be5 --- /dev/null +++ b/start_macos.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +# M Series or Intel +OS_ARCH=$(uname -m) +case "${OS_ARCH}" in + x86_64*) OS_ARCH="x86_64";; + arm64*) OS_ARCH="arm64";; + *) echo "Unknown system architecture: $OS_ARCH! This script runs only on x86_64 or arm64" && exit +esac + +# config +INSTALL_DIR="$(pwd)/installer_files" +CONDA_ROOT_PREFIX="$(pwd)/installer_files/conda" +INSTALL_ENV_DIR="$(pwd)/installer_files/env" +MINICONDA_DOWNLOAD_URL="https://repo.anaconda.com/miniconda/Miniconda3-py310_23.3.1-0-MacOSX-${OS_ARCH}.sh" +conda_exists="F" + +# figure out whether git and conda needs to be installed +if "$CONDA_ROOT_PREFIX/bin/conda" --version &>/dev/null; then conda_exists="T"; fi + +# (if necessary) install git and conda into a contained environment +# download miniconda +if [ "$conda_exists" == "F" ]; then + echo "Downloading Miniconda from $MINICONDA_DOWNLOAD_URL to $INSTALL_DIR/miniconda_installer.sh" + + mkdir -p "$INSTALL_DIR" + curl -Lk "$MINICONDA_DOWNLOAD_URL" > "$INSTALL_DIR/miniconda_installer.sh" + + chmod u+x "$INSTALL_DIR/miniconda_installer.sh" + bash "$INSTALL_DIR/miniconda_installer.sh" -b -p $CONDA_ROOT_PREFIX + + # test the conda binary + echo "Miniconda version:" + "$CONDA_ROOT_PREFIX/bin/conda" --version +fi + +# create the installer env +if [ ! -e "$INSTALL_ENV_DIR" ]; then + "$CONDA_ROOT_PREFIX/bin/conda" create -y -k --prefix "$INSTALL_ENV_DIR" python=3.11 +fi + +# check if conda environment was actually created +if [ ! -e "$INSTALL_ENV_DIR/bin/python" ]; then + echo "Conda environment is empty." + exit +fi + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# activate installer env +source "$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script) +conda activate "$INSTALL_ENV_DIR" + +# setup installer env +python one_click.py $@ diff --git a/start_windows.bat b/start_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..cdc303e46e49f12c046b942c8da19bce5aca7081 --- /dev/null +++ b/start_windows.bat @@ -0,0 +1,84 @@ +@echo off + +cd /D "%~dp0" + +set PATH=%PATH%;%SystemRoot%\system32 + +echo "%CD%"| findstr /C:" " >nul && echo This script relies on Miniconda which can not be silently installed under a path with spaces. && goto end + +@rem Check for special characters in installation path +set "SPCHARMESSAGE="WARNING: Special characters were detected in the installation path!" " This can cause the installation to fail!"" +echo "%CD%"| findstr /R /C:"[!#\$%&()\*+,;<=>?@\[\]\^`{|}~]" >nul && ( + call :PrintBigMessage %SPCHARMESSAGE% +) +set SPCHARMESSAGE= + +@rem fix failed install when installing to a separate drive +set TMP=%cd%\installer_files +set TEMP=%cd%\installer_files + +@rem deactivate existing conda envs as needed to avoid conflicts +(call conda deactivate && call conda deactivate && call conda deactivate) 2>nul + +@rem config +set INSTALL_DIR=%cd%\installer_files +set CONDA_ROOT_PREFIX=%cd%\installer_files\conda +set INSTALL_ENV_DIR=%cd%\installer_files\env +set MINICONDA_DOWNLOAD_URL=https://repo.anaconda.com/miniconda/Miniconda3-py310_23.3.1-0-Windows-x86_64.exe +set conda_exists=F + +@rem figure out whether git and conda needs to be installed +call "%CONDA_ROOT_PREFIX%\_conda.exe" --version >nul 2>&1 +if "%ERRORLEVEL%" EQU "0" set conda_exists=T + +@rem (if necessary) install git and conda into a contained environment +@rem download conda +if "%conda_exists%" == "F" ( + echo Downloading Miniconda from %MINICONDA_DOWNLOAD_URL% to %INSTALL_DIR%\miniconda_installer.exe + + mkdir "%INSTALL_DIR%" + call curl -Lk "%MINICONDA_DOWNLOAD_URL%" > "%INSTALL_DIR%\miniconda_installer.exe" || ( echo. && echo Miniconda failed to download. && goto end ) + + echo Installing Miniconda to %CONDA_ROOT_PREFIX% + start /wait "" "%INSTALL_DIR%\miniconda_installer.exe" /InstallationType=JustMe /NoShortcuts=1 /AddToPath=0 /RegisterPython=0 /NoRegistry=1 /S /D=%CONDA_ROOT_PREFIX% + + @rem test the conda binary + echo Miniconda version: + call "%CONDA_ROOT_PREFIX%\_conda.exe" --version || ( echo. && echo Miniconda not found. && goto end ) +) + +@rem create the installer env +if not exist "%INSTALL_ENV_DIR%" ( + echo Packages to install: %PACKAGES_TO_INSTALL% + call "%CONDA_ROOT_PREFIX%\_conda.exe" create --no-shortcuts -y -k --prefix "%INSTALL_ENV_DIR%" python=3.11 || ( echo. && echo Conda environment creation failed. && goto end ) +) + +@rem check if conda environment was actually created +if not exist "%INSTALL_ENV_DIR%\python.exe" ( echo. && echo Conda environment is empty. && goto end ) + +@rem environment isolation +set PYTHONNOUSERSITE=1 +set PYTHONPATH= +set PYTHONHOME= +set "CUDA_PATH=%INSTALL_ENV_DIR%" +set "CUDA_HOME=%CUDA_PATH%" + +@rem activate installer env +call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end ) + +@rem setup installer env +call python one_click.py %* + +@rem below are functions for the script next line skips these during normal execution +goto end + +:PrintBigMessage +echo. && echo. +echo ******************************************************************* +for %%M in (%*) do echo * %%~M +echo ******************************************************************* +echo. && echo. +exit /b + +:end +pause diff --git a/start_wsl.bat b/start_wsl.bat new file mode 100644 index 0000000000000000000000000000000000000000..d7bacead6b0ea94656ecacd8bccede01d7d53cc8 --- /dev/null +++ b/start_wsl.bat @@ -0,0 +1,11 @@ +@echo off + +cd /D "%~dp0" + +set PATH=%PATH%;%SystemRoot%\system32 + +@rem sed -i 's/\x0D$//' ./wsl.sh converts newlines to unix format in the wsl script +call wsl -e bash -lic "sed -i 's/\x0D$//' ./wsl.sh; source ./wsl.sh %*" + +:end +pause diff --git a/training/datasets/put-trainer-datasets-here.txt b/training/datasets/put-trainer-datasets-here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e54a670e583b8a622f92f8718003c6fd27708620 --- /dev/null +++ b/training/datasets/put-trainer-datasets-here.txt @@ -0,0 +1 @@ +to load multiple raw text files create a subdirectory and put them all there diff --git a/training/formats/alpaca-chatbot-format.json b/training/formats/alpaca-chatbot-format.json new file mode 100644 index 0000000000000000000000000000000000000000..4b38103f4c23de004666e0316855db62e57d2ad0 --- /dev/null +++ b/training/formats/alpaca-chatbot-format.json @@ -0,0 +1,4 @@ +{ + "instruction,output": "User: %instruction%\nAssistant: %output%", + "instruction,input,output": "User: %instruction%: %input%\nAssistant: %output%" +} diff --git a/training/formats/alpaca-format.json b/training/formats/alpaca-format.json new file mode 100644 index 0000000000000000000000000000000000000000..dd6df95640360297257b618715370093b715b21f --- /dev/null +++ b/training/formats/alpaca-format.json @@ -0,0 +1,4 @@ +{ + "instruction,output": "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\n%instruction%\n\n### Response:\n%output%", + "instruction,input,output": "Below is an instruction that describes a task, paired with an input that provides further context. Write a response that appropriately completes the request.\n\n### Instruction:\n%instruction%\n\n### Input:\n%input%\n\n### Response:\n%output%" +} diff --git a/training/formats/vicuna-format.json b/training/formats/vicuna-format.json new file mode 100644 index 0000000000000000000000000000000000000000..c1aa4f15ebcb99a57f696cbe1ec586ed7d5d4a90 --- /dev/null +++ b/training/formats/vicuna-format.json @@ -0,0 +1,3 @@ +{ + "instruction,output": "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.\n\nUSER: %instruction%\n\nASSISTANT: %output%" +} diff --git a/update_linux.sh b/update_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..371db554a33f53f3bd3c5bf15fedeaf2f6812639 --- /dev/null +++ b/update_linux.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +# config +CONDA_ROOT_PREFIX="$(pwd)/installer_files/conda" +INSTALL_ENV_DIR="$(pwd)/installer_files/env" + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# activate installer env +source "$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script) +conda activate "$INSTALL_ENV_DIR" + +# update installer env +python one_click.py --update && echo -e "\nDone!" diff --git a/update_macos.sh b/update_macos.sh new file mode 100644 index 0000000000000000000000000000000000000000..371db554a33f53f3bd3c5bf15fedeaf2f6812639 --- /dev/null +++ b/update_macos.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +if [[ "$(pwd)" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +# config +CONDA_ROOT_PREFIX="$(pwd)/installer_files/conda" +INSTALL_ENV_DIR="$(pwd)/installer_files/env" + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# activate installer env +source "$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script) +conda activate "$INSTALL_ENV_DIR" + +# update installer env +python one_click.py --update && echo -e "\nDone!" diff --git a/update_windows.bat b/update_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..0d8f815272c5eec8714ef1adc1a23d547d6bf62d --- /dev/null +++ b/update_windows.bat @@ -0,0 +1,37 @@ +@echo off + +cd /D "%~dp0" + +set PATH=%PATH%;%SystemRoot%\system32 + +echo "%CD%"| findstr /C:" " >nul && echo This script relies on Miniconda which can not be silently installed under a path with spaces. && goto end + +@rem fix failed install when installing to a separate drive +set TMP=%cd%\installer_files +set TEMP=%cd%\installer_files + +@rem deactivate existing conda envs as needed to avoid conflicts +(call conda deactivate && call conda deactivate && call conda deactivate) 2>nul + +@rem config +set CONDA_ROOT_PREFIX=%cd%\installer_files\conda +set INSTALL_ENV_DIR=%cd%\installer_files\env + +@rem environment isolation +set PYTHONNOUSERSITE=1 +set PYTHONPATH= +set PYTHONHOME= +set "CUDA_PATH=%INSTALL_ENV_DIR%" +set "CUDA_HOME=%CUDA_PATH%" + +@rem activate installer env +call "%CONDA_ROOT_PREFIX%\condabin\conda.bat" activate "%INSTALL_ENV_DIR%" || ( echo. && echo Miniconda hook not found. && goto end ) + +@rem update installer env +call python one_click.py --update && ( + echo. + echo Done! +) + +:end +pause diff --git a/update_wsl.bat b/update_wsl.bat new file mode 100644 index 0000000000000000000000000000000000000000..36d019a86641bb69392e04822f9697c80b28dcf9 --- /dev/null +++ b/update_wsl.bat @@ -0,0 +1,11 @@ +@echo off + +cd /D "%~dp0" + +set PATH=%PATH%;%SystemRoot%\system32 + +@rem sed -i 's/\x0D$//' ./wsl.sh converts newlines to unix format in the wsl script calling wsl.sh with 'update' will run updater +call wsl -e bash -lic "sed -i 's/\x0D$//' ./wsl.sh; source ./wsl.sh update" + +:end +pause diff --git a/wsl.sh b/wsl.sh new file mode 100644 index 0000000000000000000000000000000000000000..5c7d6212e0ab8f6777aee29ddd022a3b442b015c --- /dev/null +++ b/wsl.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# detect if build-essential is missing or broken +if ! dpkg-query -W -f'${Status}' "build-essential" 2>/dev/null | grep -q "ok installed"; then +echo "build-essential not found or broken! + +A C++ compiler is required to build needed Python packages! +To install one, run cmd_wsl.bat and enter these commands: + +sudo apt-get update +sudo apt-get install build-essential +" +read -n1 -p "Continue the installer anyway? [y,n]" EXIT_PROMPT +# only continue if user inputs 'y' else exit +if ! [[ $EXIT_PROMPT == "Y" || $EXIT_PROMPT == "y" ]]; then exit; fi +fi + +# deactivate existing conda envs as needed to avoid conflicts +{ conda deactivate && conda deactivate && conda deactivate; } 2> /dev/null + +# config unlike other scripts, can't use current directory due to file IO bug in WSL, needs to be in virtual drive +INSTALL_DIR_PREFIX="$HOME/text-gen-install" +if [[ ! $(realpath "$(pwd)/..") = /mnt/* ]]; then + INSTALL_DIR_PREFIX="$(realpath "$(pwd)/..")" && INSTALL_INPLACE=1 +fi +INSTALL_DIR="$INSTALL_DIR_PREFIX/text-generation-webui" +CONDA_ROOT_PREFIX="$INSTALL_DIR/installer_files/conda" +INSTALL_ENV_DIR="$INSTALL_DIR/installer_files/env" +MINICONDA_DOWNLOAD_URL="https://repo.anaconda.com/miniconda/Miniconda3-py310_23.3.1-0-Linux-x86_64.sh" +conda_exists="F" + +# environment isolation +export PYTHONNOUSERSITE=1 +unset PYTHONPATH +unset PYTHONHOME +export CUDA_PATH="$INSTALL_ENV_DIR" +export CUDA_HOME="$CUDA_PATH" + +# /usr/lib/wsl/lib needs to be added to LD_LIBRARY_PATH to fix years-old bug in WSL where GPU drivers aren't linked properly +export LD_LIBRARY_PATH="$CUDA_HOME/lib:/usr/lib/wsl/lib:$LD_LIBRARY_PATH" + +# open bash cli if called with 'wsl.sh cmd' with workarounds for existing conda +if [ "$1" == "cmd" ]; then + exec bash --init-file <(echo ". ~/.bashrc; conda deactivate 2> /dev/null; cd $INSTALL_DIR || cd $HOME; source $CONDA_ROOT_PREFIX/etc/profile.d/conda.sh; conda activate $INSTALL_ENV_DIR") + exit +fi + +if [[ "$INSTALL_DIR" =~ " " ]]; then echo This script relies on Miniconda which can not be silently installed under a path with spaces. && exit; fi + +# create install dir if missing +if [ ! -d "$INSTALL_DIR" ]; then mkdir -p "$INSTALL_DIR" || exit; fi + +# figure out whether git and conda needs to be installed +if "$CONDA_ROOT_PREFIX/bin/conda" --version &>/dev/null; then conda_exists="T"; fi + +# (if necessary) install git and conda into a contained environment +# download miniconda +if [ "$conda_exists" == "F" ]; then + echo "Downloading Miniconda from $MINICONDA_DOWNLOAD_URL to $INSTALL_DIR/miniconda_installer.sh" + + curl -Lk "$MINICONDA_DOWNLOAD_URL" > "$INSTALL_DIR/miniconda_installer.sh" + + chmod u+x "$INSTALL_DIR/miniconda_installer.sh" + bash "$INSTALL_DIR/miniconda_installer.sh" -b -p $CONDA_ROOT_PREFIX + + # test the conda binary + echo "Miniconda version:" + "$CONDA_ROOT_PREFIX/bin/conda" --version +fi + +# create the installer env +if [ ! -e "$INSTALL_ENV_DIR" ]; then + "$CONDA_ROOT_PREFIX/bin/conda" create -y -k --prefix "$INSTALL_ENV_DIR" python=3.11 git +fi + +# check if conda environment was actually created +if [ ! -e "$INSTALL_ENV_DIR/bin/python" ]; then + echo "Conda environment is empty." + exit +fi + +# activate installer env +source "$CONDA_ROOT_PREFIX/etc/profile.d/conda.sh" # otherwise conda complains about 'shell not initialized' (needed when running in a script) +conda activate "$INSTALL_ENV_DIR" + +pushd $INSTALL_DIR 1> /dev/null || exit + +if [ ! -f "./server.py" ]; then + git init -b main + git remote add origin https://github.com/oobabooga/text-generation-webui + git fetch + git remote set-head origin -a + git reset origin/HEAD --hard + git branch --set-upstream-to=origin/HEAD + git restore -- . :!./CMD_FLAGS.txt +fi + +# copy CMD_FLAGS.txt to install dir to allow edits within Windows +if [[ $INSTALL_INPLACE != 1 ]]; then + # workaround for old install migration + if [ ! -f "./wsl.sh" ]; then + git pull || exit + [ -f "../webui.py" ] && mv "../webui.py" "../webui-old.py" + fi + if [ -f "$(dirs +1)/CMD_FLAGS.txt" ] && [ -f "./CMD_FLAGS.txt" ]; then cp -u "$(dirs +1)/CMD_FLAGS.txt" "$INSTALL_DIR"; fi +fi + +# setup installer env update env if called with 'wsl.sh update' +case "$1" in +("update") python one_click.py --update;; +(*) python one_click.py $@;; +esac