From 81a900dcfa56ae2b0ca6b31c332209ea37c54679 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 25 Oct 2023 11:13:33 +0100 Subject: [PATCH] Introducing Adala, Autonomous Data Labeling Agent --- .github/workflows/tests.yml | 116 ++ CONTRIBUTION.md | 75 + LICENSE | 201 +++ README.md | 309 +++- adala/__init__.py | 0 adala/agents/__init__.py | 1 + adala/agents/base.py | 284 ++++ adala/datasets/__init__.py | 3 + adala/datasets/base.py | 82 ++ adala/datasets/dataframe.py | 53 + adala/datasets/label_studio.py | 129 ++ adala/environments/__init__.py | 1 + adala/environments/base.py | 126 ++ adala/memories/__init__.py | 2 + adala/memories/base.py | 75 + adala/memories/file_memory.py | 21 + adala/runtimes/__init__.py | 2 + adala/runtimes/base.py | 301 ++++ adala/runtimes/openai.py | 74 + adala/skills/__init__.py | 6 + adala/skills/base.py | 375 +++++ adala/skills/generation/__init__.py | 0 adala/skills/generation/base.py | 15 + adala/skills/generation/qa.py | 23 + adala/skills/generation/summarization.py | 22 + adala/skills/generation/translation.py | 27 + adala/skills/labeling/__init__.py | 0 adala/skills/labeling/classification.py | 35 + adala/skills/labeling/sequence_labeling.py | 26 + adala/skills/skillset.py | 137 ++ adala/utils/__init__.py | 0 adala/utils/internal_data.py | 16 + adala/utils/logs.py | 48 + docs/README.md | 24 + docs/mkdocs.yml | 46 + docs/requirements.txt | 4 + docs/src/agents.md | 2 + docs/src/datasets.md | 4 + docs/src/environments.md | 2 + docs/src/img/diagram.png | Bin 0 -> 142044 bytes docs/src/img/logo.png | Bin 0 -> 154625 bytes docs/src/index.md | 109 ++ docs/src/memories.md | 4 + docs/src/runtimes.md | 4 + docs/src/skills.md | 14 + docs/src/utils.md | 2 + examples/classification_skill.ipynb | 907 ++++++++++++ examples/classification_skill_with_CoT.ipynb | 263 ++++ examples/question_answering_skill.ipynb | 244 ++++ examples/quickstart.ipynb | 1264 ++++++++++++++++ examples/summarization_skill.ipynb | 192 +++ examples/text_generation_skill.ipynb | 338 +++++ examples/translation_skill.ipynb | 312 ++++ pdm.lock | 1347 ++++++++++++++++++ pyproject.toml | 34 + requirements.txt | 7 + setup.py | 28 + tests/requirements-test.txt | 5 + tests/test_classification.py | 89 ++ 59 files changed, 7828 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/tests.yml create mode 100644 CONTRIBUTION.md create mode 100644 LICENSE create mode 100644 adala/__init__.py create mode 100644 adala/agents/__init__.py create mode 100644 adala/agents/base.py create mode 100644 adala/datasets/__init__.py create mode 100644 adala/datasets/base.py create mode 100644 adala/datasets/dataframe.py create mode 100644 adala/datasets/label_studio.py create mode 100644 adala/environments/__init__.py create mode 100644 adala/environments/base.py create mode 100644 adala/memories/__init__.py create mode 100644 adala/memories/base.py create mode 100644 adala/memories/file_memory.py create mode 100644 adala/runtimes/__init__.py create mode 100644 adala/runtimes/base.py create mode 100644 adala/runtimes/openai.py create mode 100644 adala/skills/__init__.py create mode 100644 adala/skills/base.py create mode 100644 adala/skills/generation/__init__.py create mode 100644 adala/skills/generation/base.py create mode 100644 adala/skills/generation/qa.py create mode 100644 adala/skills/generation/summarization.py create mode 100644 adala/skills/generation/translation.py create mode 100644 adala/skills/labeling/__init__.py create mode 100644 adala/skills/labeling/classification.py create mode 100644 adala/skills/labeling/sequence_labeling.py create mode 100644 adala/skills/skillset.py create mode 100644 adala/utils/__init__.py create mode 100644 adala/utils/internal_data.py create mode 100644 adala/utils/logs.py create mode 100644 docs/README.md create mode 100644 docs/mkdocs.yml create mode 100644 docs/requirements.txt create mode 100644 docs/src/agents.md create mode 100644 docs/src/datasets.md create mode 100644 docs/src/environments.md create mode 100644 docs/src/img/diagram.png create mode 100644 docs/src/img/logo.png create mode 100644 docs/src/index.md create mode 100644 docs/src/memories.md create mode 100644 docs/src/runtimes.md create mode 100644 docs/src/skills.md create mode 100644 docs/src/utils.md create mode 100644 examples/classification_skill.ipynb create mode 100644 examples/classification_skill_with_CoT.ipynb create mode 100644 examples/question_answering_skill.ipynb create mode 100644 examples/quickstart.ipynb create mode 100644 examples/summarization_skill.ipynb create mode 100644 examples/text_generation_skill.ipynb create mode 100644 examples/translation_skill.ipynb create mode 100644 pdm.lock create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 setup.py create mode 100644 tests/requirements-test.txt create mode 100644 tests/test_classification.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..ea5f567a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,116 @@ +name: pytests +on: + push: + branches: + - master + paths: + - 'adala/**' + - '.github/workflows/tests.yml' + - 'requirements**' + tags-ignore: + - '**' + pull_request: + types: + - opened + - synchronize + - reopened + - ready_for_review + branches: + - master + - 'release/**' + +env: + CACHE_NAME_PREFIX: v1 + OPENAI_API_KEY: test + +jobs: + run_pytest_ubuntu: + name: Ubuntu + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - '3.8' + - '3.9' + - '3.10' + - '3.11' + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4.7.0 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v3.3.1 + name: Configure pip cache + id: pip-cache + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements.txt') }}-${{ hashFiles('**/requirements-test.txt') }} + restore-keys: | + ${{ env.CACHE_NAME_PREFIX }}-${{ runner.os }}-pip-${{ matrix.python-version }}- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools + pip install -U pip + pip install -r requirements.txt -r tests/requirements-test.txt + pip install -e . + + - name: Run functional tests + run: | + cd tests/ + pytest --junitxml report.xml --cov=. -vv + + + + + run_pytests_windows: + name: Windows + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + python-version: + - '3.8' + - '3.9' + - '3.10' + - '3.11' + + steps: + - uses: hmarr/debug-action@v2.1.0 + + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.head_sha }} + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v3 + name: Configure pip cache + with: + path: ~\AppData\Local\pip\Cache + key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/requirements.txt') }}-${{ hashFiles('**/requirements-test.txt') }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.python-version }}- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools + pip install --upgrade cython + if (Test-Path -Path '.\requirements.txt' -PathType Leaf) + {pip install -r requirements.txt} + if (Test-Path -Path '.\tests\requirements-test.txt' -PathType Leaf) + {pip install -r tests/requirements-test.txt} + pip install -e . + + - name: Test with pytest + run: | + cd tests/ + python -m pytest -vv diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md new file mode 100644 index 00000000..e00341de --- /dev/null +++ b/CONTRIBUTION.md @@ -0,0 +1,75 @@ +# Adala Project Contribution Guide: Agent and Skill Development + +Thank you for your interest in contributing to the Adala Project's agent development! The robustness and versatility of our system primarily stem from the diverse agents and skills we deploy. This guide focuses on agent-related contributions, highlighting the importance of domain and task specificity. + +## Areas of Contribution: + +### Diverse Skills Contributions: + +Adala welcomes agents equipped with a wide range of skills, each offering unique capabilities. From tasks such as classification, anomaly detection, and regression to specialized roles like sentiment analysis or recommendation systems, there's endless potential to broaden our agent spectrum. Skills designed for specific domains (like medical, finance, or nature) or tailored tasks within these areas can considerably amplify the system's efficacy. + +### Extending Skills: + +Start with the foundational Skill class and extend it to facilitate Adala in acquiring new skills. To understand better, examine how the Classification skills were implemented. + +Example: + +```python +class +``` + +### Domain-Specific Skills + +Customize skills to particular domains, providing more profound insights and actionable feedback. + +Example: + +```python +``` + +#### Guidelines for New Skills: + +- Uniqueness: Focus on specificity. What unique problem does your skill resolve? +- Integration: Ensure your skill aligns well with the existing Adala framework. +- Documentation: Offer comprehensive documentation, usage instances for your agent, and a testing environment (with a ground truth dataset). +- Testing: Incorporate both unit and integration tests to guarantee a seamless integration with the Adala system. + +### New Runtimes + +Introduce runtimes utilizing varying language models or even distinct model types for labeling tasks. Enhancing current implementations through performance optimization or new feature introduction is also encouraged. + +#### Adding a New Runtime: +To introduce a new runtime, adhere to the structure delineated by the Runtime abstract class. Below is a rudimentary example: + +```python + +``` + +### Environments + +The environment offers a unique method for collecting user feedback, which assists Adala agents in learning. For instance, you can create a setting where it attempts to call your phone using Twilio integration, seeking your oversight. + +```python + +``` + +### Roadmap Driven + +Contributions that align with the items detailed in our roadmap, found in the main README, are not only welcome but are greatly encouraged. Adhering to this roadmap ensures that all efforts are in synergy with project's vision. + +## How to Contribute: + +- Fork the Repository: Create a fork of the Adala repository on your GitHub account. +- Clone, Branch, and Develop: Clone your fork, spawn a new branch for your contribution, and commence development. +- Test and Commit: After modifications, conduct comprehensive testing. Once content, commit with an informative message. +- Push and Pull Request: Push your amendments and formulate a pull request detailing your contribution's value. + +## Development Environment + +Adala uses [PDM](https://pdm.fming.dev/latest) to manage dependencies. To create an environment, [install PDM](https://pdm.fming.dev/latest/#recommended-installation-method) and run `pdm install` from the root of the repository. To activate the environment, run `source .venv/bin/activate` from the root of the repo after creating the environment. + +## Code of Conduct: +While diverse contributions invigorate our project, it's paramount to sustain a harmonious and cooperative environment. Please adhere to our code of conduct. + +## Questions or Discussions: +For inquiries or discussions concerning particular features, agents, or modifications, please initiate an issue. Your feedback propels the project's advancement. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..6ec15332 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2023 HumanSignal, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index c0e9ca76..f97be241 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,307 @@ -# ADALA -ADALA: Autonomous Data Labeling Agent +[![PyPI version](https://badge.fury.io/py/adala-pk-test.svg)](https://badge.fury.io/py/adala-pk-test) +![GitHub](https://img.shields.io/github/license/HumanSignal/Adala) +![GitHub Repo stars](https://img.shields.io/github/stars/HumanSignal/Adala) +[![](https://img.shields.io/discord/1166330284300570624?label=Discord&logo=discord)](https://discord.gg/QBtgTbXTgU) + +ADALA logo + +Adala is an **A**utonomous **DA**ta (**L**abeling) **A**gent framework. + +Adala offers a robust framework for implementing agents specialized in data processing, with an emphasis on +diverse data labeling tasks. These agents are autonomous, meaning they can independently acquire one or more skills +through iterative learning. This learning process is influenced by their operating environment, observations, and +reflections. Users define the environment by providing a ground truth dataset. Every agent learns and applies its skills +in what we refer to as a "runtime", synonymous with LLM. + +![Diagram of components](./docs/src/img/diagram.png "Diagram of components") + + + +## ๐Ÿ“ข Why choose Adala? + +- ๐ŸŒŸ **Reliable agents**: Agents are built upon a foundation of ground + truth data. This ensures consistent and trustworthy results, making Adala a + reliable choice for your data processing needs. + +- ๐ŸŽฎ **Controllable output**: For every skill, you can configure the + desired output and set specific constraints with varying degrees of + flexibility. Whether you want strict adherence to particular + guidelines or more adaptive outputs based on the agent's learning, + Adala allows you to tailor results to your exact needs. + +- ๐ŸŽฏ **Specialized in data processing**: While agents excel in diverse + data labeling tasks, they can be customized for a wide range of data + processing needs. + +- ๐Ÿง  **Autonomous learning**: Adala agents aren't just automated; + they're intelligent. They iteratively and independently develop + skills based on environment, observations, and reflections. + +- โœ… **Flexible and extensible runtime**: Adala's runtime environment is + adaptable. A single skill can be deployed across multiple runtimes, + facilitating dynamic scenarios like the student/teacher + architecture. Moreover, the openness of framework invites the + community to extend and tailor runtimes, ensuring continuous + evolution and adaptability to diverse needs. + +- ๐Ÿš€ **Easily customizable**: Quickly customize and develop agents to address + challenges specific to your needs, without facing a steep learning curve. + +## ๐Ÿซต Who is Adala for? + +Adala is a versatile framework designed for individuals and professionals in the field of AI and machine learning. Here's who can benefit: + +- ๐Ÿงก **AI engineers:** Architect and design AI agent systems with modular, interconnected skills. Build production-level agent systems, abstracting low-level ML to Adala and LLMs. +- ๐Ÿ’ป **Machine learning researchers:** Experiment with complex problem decomposition and causal reasoning. +- ๐Ÿ“ˆ **Data scientists:** Apply agents to preprocess and postprocess your data. Interact with Adala natively through Python notebooks when working with large Dataframes. +- ๐Ÿซ **Educators and students:** Use Adala as a teaching tool or as a base for advanced projects and research. + +While the roles highlighted above are central, it's pivotal to note that Adala is intricately designed to streamline and elevate the AI development journey, +catering to all enthusiasts, irrespective of their specific niche in the field. ๐Ÿฅฐ + +## ๐Ÿ”ŒInstallation + +Install Adala: + +```sh +pip install adala +``` + + +## ๐Ÿ“ Prerequisites + +Set OPENAI_API_KEY ([see instructions here](https://platform.openai.com/docs/quickstart/step-2-setup-your-api-key)) + +``` +export OPENAI_API_KEY='your-openai-api-key' +``` + +## ๐ŸŽฌ Quickstart + +In this example we will use Adala as a standalone library directly inside Python notebook. + +Click [here](./examples/quickstart.ipynb) to see an extended quickstart example. + +```python +import pandas as pd + +from adala.agents import Agent +from adala.datasets import DataFrameDataset +from adala.environments import BasicEnvironment +from adala.skills import ClassificationSkill +from adala.runtimes import OpenAIRuntime +from rich import print + +# Train dataset +ground_truth_df = pd.DataFrame([ + ["It was the negative first impressions, and then it started working.", "Positive"], + ["Not loud enough and doesn't turn on like it should.", "Negative"], + ["I don't know what to say.", "Neutral"], + ["Manager was rude, but the most important that mic shows very flat frequency response.", "Positive"], + ["The phone doesn't seem to accept anything except CBR mp3s.", "Negative"], + ["I tried it before, I bought this device for my son.", "Neutral"], +], columns=["text", "ground_truth"]) + +# Test dataset +predict_df = pd.DataFrame([ + "All three broke within two months of use.", + "The device worked for a long time, can't say anything bad.", + "Just a random line of text." +], columns=["text"]) + +ground_truth_dataset = DataFrameDataset(df=ground_truth_df) +predict_dataset = DataFrameDataset(df=predict_df) + +agent = Agent( + # connect to a dataset + environment=BasicEnvironment( + ground_truth_dataset=ground_truth_dataset, + ground_truth_column="ground_truth" + ), + + # define a skill + skills=ClassificationSkill( + name='sentiment_classification', + instructions="Label text as subjective or objective.", + labels=["Positive", "Negative", "Neutral"], + input_data_field='text' + ), + + # define all the different runtimes your skills may use + runtimes = { + # You can specify your OPENAI API KEY here via `OpenAIRuntime(..., api_key='your-api-key')` + 'openai': OpenAIRuntime(model='gpt-3.5-turbo-instruct'), + 'openai-gpt3': OpenAIRuntime(model='gpt-3.5-turbo'), + # 'openai-gpt4': OpenAIRuntime(model='gpt-4'), + }, + default_runtime='openai', + + # NOTE! If you don't have an access to gpt4 - replace it with "openai-gpt3" + # default_teacher_runtime='openai-gpt4' +) + +print(agent) +print(agent.skills) + +agent.learn(learning_iterations=3, accuracy_threshold=0.95) + +print('\n=> Run tests ...') +run = agent.apply_skills(predict_dataset) +print('\n => Test results:') +print(run) +``` + +### ๐Ÿ‘‰ Available skills +- [ClassificationSkill](./examples/classification_skill.ipynb) โ€“ Classify text into a set of predefined labels. +- [ClassificationSkillWithCoT](./examples/classification_skill_with_CoT.ipynb) โ€“ Classify text into a set of predefined labels, using Chain-of-Thoughts reasoning. +- [SummarizationSkill](./examples/summarization_skill.ipynb) โ€“ Summarize text into a shorter text. +- [QuestionAnsweringSkill](./examples/question_answering_skill.ipynb) โ€“ Answer questions based on a given context. +- [TranslationSkill](./examples/translation_skill.ipynb) โ€“ Translate text from one language to another. +- [TextGenerationSkill](./examples/text_generation_skill.ipynb) โ€“ Generate text based on a given prompt. + + + +## ๐Ÿ—บ Roadmap + +- [ ] Low-level skill management (i.e. agent.get_skill("name")) +- [ ] Calculate and store top line Agent metrics (predictions created, runtime executions, learning loops, etc) +- [ ] Create Named Entity Recognition Skill +- [ ] Extend environment with one more example +- [ ] Command line utility (see the source for this readme for example) +- [ ] REST API to interact with Adala +- [ ] Multi-task learning (learn multiple skills at once) +- [ ] Vision and multi-modal agent skills + +## ๐Ÿคฉ Contributing to Adala + +Enhance skills, optimize runtimes, or pioneer new agent types. Whether you're +crafting nuanced tasks, refining computational environments, or sculpting specialized agents for unique domains, your +contributions will power Adala's evolution. Join us in shaping the future of intelligent systems and making Adala more +versatile and impactful for users across the globe. + +[Read more](./CONTRIBUTION.md) here. + +## ๐Ÿ’ฌ Support + +Do you need help or are you looking to engage with community? Check out [Discord channel](https://discord.gg/QBtgTbXTgU)! +Whether you have questions, need clarification, or simply want to discuss topics related to the project, the Discord community is welcoming! diff --git a/adala/__init__.py b/adala/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adala/agents/__init__.py b/adala/agents/__init__.py new file mode 100644 index 00000000..ebbc5c75 --- /dev/null +++ b/adala/agents/__init__.py @@ -0,0 +1 @@ +from .base import Agent \ No newline at end of file diff --git a/adala/agents/base.py b/adala/agents/base.py new file mode 100644 index 00000000..334f4f1f --- /dev/null +++ b/adala/agents/base.py @@ -0,0 +1,284 @@ +from pydantic import BaseModel, Field, SkipValidation, field_validator, model_validator, ValidationError +from abc import ABC, abstractmethod +from typing import Any, Optional, List, Dict, Union +from adala.environments.base import Environment, BasicEnvironment +from adala.datasets import Dataset, DataFrameDataset +from adala.runtimes.base import Runtime, LLMRuntime, LLMRuntimeType, LLMRuntimeModelType +from adala.runtimes.openai import OpenAIRuntime +from adala.memories.base import ShortTermMemory, LongTermMemory +from adala.skills.base import BaseSkill +from adala.skills.skillset import SkillSet, LinearSkillSet +from adala.utils.logs import print_dataframe, print_text, print_error +from adala.utils.internal_data import InternalDataFrame + + +class Agent(BaseModel, ABC): + """ + Represents a customizable agent that can interact with environments, + employ skills, and leverage memory and runtimes. + + Attributes: + environment (Union[Dataset, Environment]): The environment with which the agent interacts. + skills (Union[SkillSet, BaseSkill, List[BaseSkill], Dict[str, BaseSkill]]): The skills possessed by the agent. + memory (LongTermMemory, optional): The agent's long-term memory. Defaults to None. + runtimes (Dict[str, Runtime], optional): The runtimes available to the agent. Defaults to predefined runtimes. + default_runtime (str): The default runtime used by the agent. Defaults to 'openai'. + """ + + environment: Union[InternalDataFrame, Dataset, Environment] = Field(default_factory=DataFrameDataset) + skills: Union[SkillSet, BaseSkill, List[BaseSkill], Dict[str, BaseSkill]] + + memory: LongTermMemory = Field(default=None) + runtimes: Optional[Dict[str, Runtime]] = Field( + default_factory=lambda: { + 'openai': OpenAIRuntime(model='gpt-3.5-turbo-instruct'), + # 'llama2': LLMRuntime( + # llm_runtime_type=LLMRuntimeModelType.Transformers, + # llm_params={ + # 'model': 'meta-llama/Llama-2-7b', + # 'device': 'cuda:0', + # } + # ) + } + ) + teacher_runtimes: Optional[Dict[str, Runtime]] = Field( + default_factory=lambda: { + 'openai-gpt3': OpenAIRuntime(model='gpt-3.5-turbo'), + 'openai-gpt4': OpenAIRuntime(model='gpt-4') + } + ) + default_runtime: str = 'openai' + default_teacher_runtime: str = 'openai-gpt3' + + class Config: + arbitrary_types_allowed = True + + def __rich__(self): + """ + Returns a colorized and formatted representation of the Agent instance. + + Returns: + str: A rich-formatted representation of the agent. + """ + + skill_names = ", ".join([skill.name for skill in self.skills.skills.values()]) + runtime_names = ", ".join(self.runtimes.keys()) + + return ( + f"[bold blue]Agent Instance[/bold blue]\n\n" + f"Environment: {self.environment.__class__.__name__}\n" + f"Skills: {skill_names}\n" + f"Runtimes: {runtime_names}\n" + f"Default Runtime: {self.default_runtime}\n" + f"Default Teacher Runtime: {self.default_teacher_runtime}" + ) + + @field_validator('environment') + def environment_validator(cls, v): + """ + Validates and possibly transforms the environment attribute. + + Args: + v (Union[Dataset, Environment]): The environment value to validate. + + Returns: + Environment: The validated environment. + """ + if isinstance(v, InternalDataFrame): + v = DataFrameDataset(df=v) + if isinstance(v, Dataset): + v = BasicEnvironment(dataset=v) + return v + + @field_validator('skills') + def skills_validator(cls, v): + """ + Validates and possibly transforms the skills attribute. + + Args: + v (Union[SkillSet, BaseSkill, List[BaseSkill], Dict[str, BaseSkill]]): The skills value to validate. + + Returns: + SkillSet: The validated set of skills. + """ + + if isinstance(v, SkillSet): + pass + elif isinstance(v, BaseSkill): + v = LinearSkillSet(skills={'skill_0': v}) + elif isinstance(v, list): + v = LinearSkillSet(skills={f'skill_{i}': skill for i, skill in enumerate(v)}) + elif isinstance(v, dict): + v = LinearSkillSet(skills=v) + return v + + @model_validator(mode='after') + def verify_input_parameters(self): + def _raise_default_runtime_error(val, runtime, runtimes, default_value): + print_error(f"The Agent.{runtime} is set to {val}, " + f"but this runtime is not available in the list: {list(runtimes)}. " + f"Please choose one of the available runtimes and initialize the agent again, for example:\n\n" + f"agent = Agent(..., {runtime}='{default_value}')\n\n" + f"Make sure the default runtime is available in the list of runtimes. For example:\n\n" + f"agent = Agent(..., runtimes={{'{default_value}': OpenAIRuntime(model='gpt-4')}})\n\n") + raise ValueError(f"default runtime {val} not found in provided runtimes.") + + if self.default_runtime not in self.runtimes: + _raise_default_runtime_error(self.default_runtime, 'default_runtime', self.runtimes, 'openai') + if self.default_teacher_runtime not in self.teacher_runtimes: + _raise_default_runtime_error(self.default_teacher_runtime, 'default_teacher_runtime', self.teacher_runtimes, 'openai-gpt4') + return self + + def get_runtime(self, runtime: Optional[str] = None) -> Runtime: + """ + Retrieves the specified runtime or the default runtime if none is specified. + + Args: + runtime (str, optional): The name of the runtime to retrieve. Defaults to None. + + Returns: + Runtime: The requested runtime. + + Raises: + ValueError: If the specified runtime is not found. + """ + + if runtime is None: + runtime = self.default_runtime + if runtime not in self.runtimes: + raise ValueError(f'Runtime "{runtime}" not found.') + return self.runtimes[runtime] + + def get_teacher_runtime(self, runtime: Optional[str] = None) -> Runtime: + """ + Retrieves the specified teacher runtime or the default runtime if none is specified. + + Args: + runtime (str, optional): The name of the runtime to retrieve. Defaults to None. + + Returns: + Runtime: The requested runtime. + + Raises: + ValueError: If the specified runtime is not found. + """ + + if runtime is None: + runtime = self.default_teacher_runtime + if runtime not in self.teacher_runtimes: + raise ValueError(f'Teacher Runtime "{runtime}" not found.') + return self.teacher_runtimes[runtime] + + def apply_skills( + self, + dataset: Union[Dataset, InternalDataFrame], + runtime: Optional[Union[str, Runtime]] = None, + experience: Optional[ShortTermMemory] = None, + ) -> ShortTermMemory: + """ + Applies the agent's skills to a given dataset using the specified runtime. + + Args: + dataset (Dataset): The dataset to apply skills on. + runtime (str, optional): The runtime to use. Defaults to None. + experience (ShortTermMemory, optional): The agent's short-term memory. Defaults to None. + + Returns: + ShortTermMemory: The short-term memory resulting from the application of skills. + """ + runtime = runtime or self.default_runtime + if isinstance(dataset, InternalDataFrame): + dataset = DataFrameDataset(df=dataset) + if isinstance(runtime, str): + runtime = self.get_runtime(runtime=runtime) + return self.skills.apply(dataset=dataset, runtime=runtime, experience=experience) + + def learn( + self, + learning_iterations: int = 3, + accuracy_threshold: float = 0.9, + update_skills: bool = True, + update_memory: bool = True, + request_environment_feedback: bool = True, + experience: Optional[ShortTermMemory] = None, + runtime: Optional[str] = None, + ) -> ShortTermMemory: + """ + Enables the agent to learn and improve its skills based on interactions with its environment. + + Args: + learning_iterations (int, optional): The number of iterations for learning. Defaults to 3. + accuracy_threshold (float, optional): The desired accuracy threshold to reach. Defaults to 0.9. + update_skills (bool, optional): Flag to determine if skills should be updated after learning. Defaults to True. + update_memory (bool, optional): Flag to determine if memory should be updated after learning. Defaults to True. + request_environment_feedback (bool, optional): Flag to determine if feedback should be requested from the environment. Defaults to True. + experience (ShortTermMemory, optional): Initial experience for the learning process. Defaults to None. + runtime (str, optional): The runtime to be used for the learning process. Defaults to None. + + Returns: + ShortTermMemory: The short-term memory after the learning process. + """ + + runtime = self.get_runtime(runtime=runtime) + # TODO: support teacher runtime input, not default + teacher_runtime = self.get_teacher_runtime(runtime=self.default_teacher_runtime) + + skills = self.skills.model_copy(deep=True) + dataset = self.environment.as_dataset() + + # Apply agent skills to dataset and get experience with predictions + experience = self.apply_skills(dataset=dataset, runtime=runtime, experience=experience) + + # Agent select one skill to improve + learned_skill = skills.select_skill_to_improve(experience) + + # Request feedback from environment is necessary + if request_environment_feedback: + self.environment.request_feedback(learned_skill, experience) + + for iteration in range(learning_iterations): + print_text(f'\n\n=> Iteration #{iteration}: Comparing to ground truth, analyzing and improving ...') + + # 1. EVALUATION PHASE: Compare predictions to ground truth + experience = self.environment.compare_to_ground_truth(learned_skill, experience) + print_text(f'Comparing predictions to ground truth data ...') + print_dataframe(experience.evaluations) + + # 2. ANALYSIS PHASE: Analyze evaluation experience, optionally use long term memory + print_text(f'Analyze evaluation experience ...') + experience = learned_skill.analyze( + experience=experience, + student_runtime=runtime, + teacher_runtime=teacher_runtime, + memory=self.memory + ) + print_text(f'Number of errors: {len(experience.errors)}') + + print_text(f'Accuracy = {experience.accuracy*100:0.2f}%', style='bold red') + if experience.accuracy >= accuracy_threshold: + print_text(f'Accuracy threshold reached ({experience.accuracy} >= {accuracy_threshold})') + break + + # 3. IMPROVEMENT PHASE: Improve skills based on analysis + print_text(f"Improve \"{learned_skill.name}\" skill based on analysis ...") + experience = learned_skill.improve( + experience=experience, + runtime=teacher_runtime, + update_instructions=True + ) + print_text(f'Updated instructions for skill "{learned_skill.name}":\n') + print_text(learned_skill.instructions, style='bold green') + + # 4. RE-APPLY PHASE: Re-apply skills to dataset + print_text(f"Re-apply {learned_skill.name} skill to dataset ...") + experience = learned_skill.apply(dataset, runtime, experience=experience) + + # Update skills and memory based on experience + if update_skills: + self.skills = skills + + if self.memory and update_memory: + self.memory.remember(experience, self.skills) + + print_text('Train is done!') + return experience diff --git a/adala/datasets/__init__.py b/adala/datasets/__init__.py new file mode 100644 index 00000000..2783af7e --- /dev/null +++ b/adala/datasets/__init__.py @@ -0,0 +1,3 @@ +from .base import Dataset, InternalDataFrame +from .dataframe import DataFrameDataset +# from .label_studio import LabelStudioDataset, LabelStudioFileDataset diff --git a/adala/datasets/base.py b/adala/datasets/base.py new file mode 100644 index 00000000..a85dcf6f --- /dev/null +++ b/adala/datasets/base.py @@ -0,0 +1,82 @@ +from abc import ABC, abstractmethod +from pydantic import BaseModel, field_validator +from typing import List, Optional, Any, Dict, Union + +from adala.utils.internal_data import InternalDataFrame + + +class Dataset(BaseModel, ABC): + """ + Abstract base class representing a dataset. + + Provides methods to interact with and obtain information about datasets. + Concrete implementations should provide functionality for batch iteration, + getting dataset size, and displaying dataset information. + """ + + @abstractmethod + def batch_iterator(self, batch_size: int = 100) -> InternalDataFrame: + """ + Yields batches of data records from the dataset. + + Args: + batch_size (int, optional): Size of each batch to be yielded. Defaults to 100. + + Returns: + InternalDataFrame: A data frame containing a batch of records. + """ + + @abstractmethod + def __len__(self) -> int: + """ + Provides the number of records in the dataset. + + Returns: + int: Total number of records in the dataset. + """ + + @abstractmethod + def info(self) -> None: + """ + Displays information about the dataset. + """ + + +class BlankDataset(Dataset): + """ + Represents an empty dataset with no records. + + This class can be used in situations where a dataset is required, + but no actual data is available or needed. + All methods return defaults representing an empty state. + """ + + def batch_iterator(self, batch_size: int = 100) -> InternalDataFrame: + """ + Yields an empty data frame as there are no records in a blank dataset. + + Args: + batch_size (int, optional): This argument is ignored for BlankDataset. Defaults to 100. + + Returns: + InternalDataFrame: An empty data frame. + """ + + return InternalDataFrame() + + def __len__(self) -> int: + """ + Provides the number of records in the blank dataset (which is always 0). + + Returns: + int: Total number of records in the dataset (0 for BlankDataset). + """ + + return 0 + + def info(self) -> None: + """ + Displays information about the blank dataset. + """ + + print('Blank dataset') diff --git a/adala/datasets/dataframe.py b/adala/datasets/dataframe.py new file mode 100644 index 00000000..e7dae40f --- /dev/null +++ b/adala/datasets/dataframe.py @@ -0,0 +1,53 @@ +from typing import Iterable +from .base import Dataset +from adala.utils.internal_data import InternalDataFrame +from pydantic import Field + + +class DataFrameDataset(Dataset): + """ + Represents a dataset backed by an internal data frame. + + Provides methods to interact with and obtain information about the dataset stored + as an internal data frame. This class wraps around `InternalDataFrame` to make it + compatible with the dataset abstraction. + + Attributes: + df (InternalDataFrame): The internal data frame storing the dataset. + """ + + df: InternalDataFrame = Field(default_factory=InternalDataFrame) + + class Config: + arbitrary_types_allowed = True + + def __len__(self): + """ + Provides the number of records in the dataset. + + Returns: + int: Total number of records in the dataset. + """ + + return len(self.df) + + def batch_iterator(self, batch_size: int = 100) -> Iterable[InternalDataFrame]: + """ + Yields batches of data records from the dataset. + + Args: + batch_size (int, optional): Size of each batch to be yielded. Defaults to 100. + + Yields: + Iterable[InternalDataFrame]: An iterator that yields data frames containing batches of records. + """ + + for i in range(0, len(self.df), batch_size): + yield self.df.iloc[i:i+batch_size] + + def info(self) -> None: + """ + Displays information (statistical description) about the dataset. + """ + + print(self.df.describe()) diff --git a/adala/datasets/label_studio.py b/adala/datasets/label_studio.py new file mode 100644 index 00000000..20eccb62 --- /dev/null +++ b/adala/datasets/label_studio.py @@ -0,0 +1,129 @@ +import json +import label_studio_sdk + +from pprint import pprint +from .base import Dataset, InternalDataFrame +from pydantic import model_validator, SkipValidation +from label_studio_sdk.project import LabelStudioException, Project +from typing import Optional, List, Dict + + +class LabelStudioFormatMixin: + + def _tasks_to_df( + self, + tasks, + include_annotations: bool = True, + only_annotated: bool = False, + ground_truth_column: str = 'ground_truth' + ): + indices, records = [], [] + for task in tasks: + record = task['data'] + if only_annotated and not task['annotations']: + continue + + if (only_annotated or include_annotations) and task['annotations']: + # TODO: expand more complex annotations + if len(task['annotations']) > 1: + raise NotImplementedError('Multiple annotations are not supported yet') + annotation = task['annotations'][0] + annotation_type = annotation['result'][0]['type'] + if annotation_type == 'textarea': + annotation_type = 'text' + if len(annotation['result']) > 1: + raise NotImplementedError('Multiple results per annotation are not supported yet') + label = annotation['result'][0]['value'][annotation_type] + if isinstance(label, list): + if len(label) == 1: + label = label[0] + else: + label = ','.join(sorted(label)) + else: + label = str(label) + record[ground_truth_column] = label + + index = task['id'] + records.append(record) + indices.append(index) + return InternalDataFrame(records, index=indices) + + +class LabelStudioDataset(Dataset, LabelStudioFormatMixin): + + label_studio_url: str + label_studio_api_key: str + label_studio_project_id: int + + ground_truth_column: str = 'ground_truth' + + _project_client: SkipValidation[Project] = None + + @model_validator(mode='after') + def init_client(self): + if self._project_client is None: + client = label_studio_sdk.Client( + url=self.label_studio_url, + api_key=self.label_studio_api_key + ) + self._project_client = client.get_project(id=self.label_studio_project_id) + return self + + def get_project_info(self): + return self._project_client.get_params() + + def info(self) -> None: + pprint(self.get_project_info()) + + def __len__(self): + info = self.get_project_info() + return info['task_number'] + + def batch_iterator(self, batch_size: int = 100) -> InternalDataFrame: + page = 1 + while True: + try: + data = self._project_client.get_paginated_tasks(page=page, page_size=batch_size) + yield self._tasks_to_df(data['tasks'], include_annotations=False) + page += 1 + # we'll get 404 from API on empty page + except LabelStudioException as e: + break + + def get_ground_truth(self, batch: Optional[InternalDataFrame] = None) -> InternalDataFrame: + if batch is None: + labeled_tasks = self._project_client.get_labeled_tasks() + gt = self._tasks_to_df(labeled_tasks, only_annotated=True, ground_truth_column='ground_truth') + return gt + else: + # TODO: not the most effective method - better to send subset of indices to LS API + labeled_tasks = self._project_client.get_labeled_tasks() + gt = self._tasks_to_df(labeled_tasks, only_annotated=True, ground_truth_column='ground_truth') + return gt[gt.index.isin(batch.index)] + + +class LabelStudioFileDataset(Dataset, LabelStudioFormatMixin): + label_studio_file: str + ground_truth_column: str = 'ground_truth' + + _data: List[Dict] = None + + @model_validator(mode='after') + def load_data(self): + with open(self.label_studio_file) as f: + self._data = json.load(f) + return self + + def batch_iterator(self, batch_size: int = 100) -> InternalDataFrame: + for i in range(0, len(self._data), batch_size): + batch = self._data[i:i+batch_size] + yield self._tasks_to_df(batch, include_annotations=False) + + def get_ground_truth(self, batch: Optional[InternalDataFrame]) -> InternalDataFrame: + return self._tasks_to_df(self._data, only_annotated=True, ground_truth_column='ground_truth') + + def __len__(self): + return len(self._data) + + def info(self) -> None: + print(f'Total Label Studio tasks loaded: {len(self)}') diff --git a/adala/environments/__init__.py b/adala/environments/__init__.py new file mode 100644 index 00000000..7448fa4d --- /dev/null +++ b/adala/environments/__init__.py @@ -0,0 +1 @@ +from .base import Environment, BasicEnvironment \ No newline at end of file diff --git a/adala/environments/base.py b/adala/environments/base.py new file mode 100644 index 00000000..4f4a3f0c --- /dev/null +++ b/adala/environments/base.py @@ -0,0 +1,126 @@ +from pydantic import BaseModel, dataclasses, Field, field_validator +from abc import ABC, abstractmethod +from typing import Any, Optional, Dict, Union, Callable + +from adala.utils.internal_data import InternalDataFrame, InternalDataFrameConcat +from adala.skills.base import BaseSkill +from adala.memories.base import ShortTermMemory +from adala.datasets import Dataset, DataFrameDataset + + +class Environment(BaseModel, ABC): + """Abstract base class for environments. + + The environment provides a mechanism to obtain ground truth information from raw data and predictions, + and also facilitates comparison of ground truth with predictions. + + Attributes: + Config (class): Configuration for the environment class, allows arbitrary types. + """ + + @abstractmethod + def request_feedback(self, skill: BaseSkill, experience: ShortTermMemory): + """Request user feedback using predictions and update internal ground truth set.""" + + @abstractmethod + def compare_to_ground_truth(self, skill: BaseSkill, experience: ShortTermMemory) -> ShortTermMemory: + """Compare predictions with ground truth and return the results.""" + + @abstractmethod + def as_dataset(self) -> Dataset: + """Convert the environment to a dataset.""" + + @abstractmethod + def save(self): + """Persist the state of the environment.""" + + @abstractmethod + def restore(self): + """Retrieve and set the state of the environment.""" + + class Config: + arbitrary_types_allowed = True + + +class BasicEnvironment(Environment): + """Basic environment implementation. + + This environment assumes the ground truth is provided explicitly with the input data. + For comparison with ground truth, exact matching is used. + + Attributes: + ground_truth_dataset (DataFrameDataset): Dataset containing the ground truth data. + Defaults to an empty DataFrameDataset. + ground_truth_column (str): Name of the column containing ground truth in the dataset. + Defaults to 'ground_truth'. + _prediction_column (str): Name of the column containing predictions. + + """ + + ground_truth_dataset: Union[InternalDataFrame, DataFrameDataset] = Field(default_factory=DataFrameDataset) + ground_truth_column: str = 'ground_truth' + + _prediction_column: str + + @field_validator('ground_truth_dataset') + def _validate_ground_truth_dataset(cls, v): + if isinstance(v, InternalDataFrame): + return DataFrameDataset(df=v) + return v + + def request_feedback(self, skill: BaseSkill, experience: ShortTermMemory): + """In the BasicEnvironment, ground truth is already provided with the input data.""" + + def compare_to_ground_truth(self, skill: BaseSkill, experience: ShortTermMemory) -> ShortTermMemory: + """Compare the predictions with the ground truth using exact matching. + + Args: + skill (BaseSkill): The skill being evaluated. + experience (ShortTermMemory): The experience memory containing predictions. + + Returns: + ShortTermMemory: Updated memory containing evaluation results against ground truth. + """ + + experience = experience.model_copy() + + gt = self.ground_truth_dataset.df[self.ground_truth_column] + pred = experience.predictions + # select + gt = gt[gt.index.isin(pred.index)] + if gt.empty: + # return empty memory + return experience + + gt = gt.to_frame(self.ground_truth_column) + + # compare ground truth with predictions using exact matching + match_column_name = f'{self.ground_truth_column}__x__{skill.name}' + evaluations = InternalDataFrameConcat([ + pred, + (gt[self.ground_truth_column] == pred[skill.name]).rename(match_column_name) + ], axis=1) + experience.evaluations = evaluations + # remember the last column names used in evaluations + experience.ground_truth_column_name = self.ground_truth_column + experience.match_column_name = match_column_name + return experience + + def as_dataset(self) -> Dataset: + """Return the ground truth dataset. + + Returns: + Dataset: The dataset containing ground truth data. + """ + + return self.ground_truth_dataset + + def save(self): + """Save method for BasicEnvironment. Not implemented.""" + + raise NotImplementedError + + def restore(self): + """Restore method for BasicEnvironment. Not implemented.""" + + raise NotImplementedError diff --git a/adala/memories/__init__.py b/adala/memories/__init__.py new file mode 100644 index 00000000..8f218bff --- /dev/null +++ b/adala/memories/__init__.py @@ -0,0 +1,2 @@ +from .file_memory import FileMemory +from .base import ShortTermMemory, LongTermMemory \ No newline at end of file diff --git a/adala/memories/base.py b/adala/memories/base.py new file mode 100644 index 00000000..822f9ddf --- /dev/null +++ b/adala/memories/base.py @@ -0,0 +1,75 @@ +from __future__ import annotations +from abc import ABC, abstractmethod +from typing import Any, Optional, TYPE_CHECKING + +from pydantic import BaseModel +from adala.datasets.base import Dataset, InternalDataFrame +from rich import print + +if TYPE_CHECKING: + from adala.skills.skillset import SkillSet + + +class ShortTermMemory(BaseModel): + """ + Base class for short term memory storage + """ + dataset: Dataset = None + predictions: InternalDataFrame = None + evaluations: InternalDataFrame = None + ground_truth_column_name: str = None + match_column_name: str = None + errors: InternalDataFrame = None + accuracy: float = None + initial_instructions: str = None + updated_instructions: str = None + + class Config: + arbitrary_types_allowed = True + + def reset(self): + self.predictions = None + self.evaluations = None + self.errors = None + self.accuracy = None + self.initial_instructions = None + self.updated_instructions = None + + def __rich__(self): + text = '[bold blue]Agent Experience:[/bold blue]\n\n' + if self.predictions is not None: + text += f'\n[bold]Predictions[/bold]\n{self.predictions}' + if self.evaluations is not None: + text += f'\n[bold]Evaluations[/bold]\n{self.evaluations}' + if self.errors is not None: + text += f'\n[bold]Errors[/bold]\n{self.errors}' + if self.accuracy is not None: + text += f'\n[bold]Accuracy[/bold]\n{self.accuracy}' + if self.initial_instructions is not None: + text += f'\n[bold]Initial Instructions[/bold]\n{self.initial_instructions}' + if self.updated_instructions is not None: + text += f'\n[bold]Updated Instructions[/bold]\n{self.updated_instructions}' + return text + + def display(self): + print(self) + + +class LongTermMemory(BaseModel, ABC): + + """ + Base class for long-term memories. + Long-term memories are used to store acquired knowledge and can be shared between agents. + """ + + @abstractmethod + def remember(self, experience: ShortTermMemory, skills: SkillSet): + """ + Base method for remembering experiences in long term memory. + """ + + @abstractmethod + def retrieve(self, observations: ShortTermMemory) -> ShortTermMemory: + """ + Base method for retrieving past experiences from long term memory, based on current observations + """ diff --git a/adala/memories/file_memory.py b/adala/memories/file_memory.py new file mode 100644 index 00000000..f2972667 --- /dev/null +++ b/adala/memories/file_memory.py @@ -0,0 +1,21 @@ +from .base import LongTermMemory, ShortTermMemory +from typing import Any + + +class FileMemory(LongTermMemory): + + filepath: str + + def remember(self, experience: ShortTermMemory): + """ + Serialize experience in JSON and append to file + """ + experience_json = experience.model_dump_json() + with open(self.filepath, 'a') as f: + f.write(experience_json + '\n') + + def retrieve(self, observations: ShortTermMemory) -> ShortTermMemory: + """ + Retrieve experience from file + """ + raise NotImplementedError diff --git a/adala/runtimes/__init__.py b/adala/runtimes/__init__.py new file mode 100644 index 00000000..807e8176 --- /dev/null +++ b/adala/runtimes/__init__.py @@ -0,0 +1,2 @@ +from .base import Runtime, LLMRuntime, LLMRuntimeModelType +from .openai import OpenAIRuntime diff --git a/adala/runtimes/base.py b/adala/runtimes/base.py new file mode 100644 index 00000000..641c7449 --- /dev/null +++ b/adala/runtimes/base.py @@ -0,0 +1,301 @@ +import enum +import guidance +import re + +from tqdm import tqdm +from abc import ABC, abstractmethod +from pydantic import BaseModel, model_validator +from typing import List, Dict, Optional, Tuple, Any +from adala.datasets.base import InternalDataFrame + +tqdm.pandas() + + +class Runtime(BaseModel, ABC): + """ + Base class representing a generic runtime environment. + + Attributes: + verbose (bool): Flag indicating if runtime outputs should be verbose. Defaults to False. + """ + verbose: bool = False + + @model_validator(mode='after') + def init_runtime(self): + """Initializes the runtime. + + This method should be used to validate and potentially initialize the runtime instance. + + Returns: + Runtime: The initialized runtime instance. + """ + return self + + +class LLMRuntimeType(enum.Enum): + STUDENT = 'student' + TEACHER = 'teacher' + + +class LLMRuntimeModelType(enum.Enum): + """Enumeration for LLM runtime model types.""" + OpenAI = 'OpenAI' + Transformers = 'Transformers' + + +class LLMRuntime(Runtime): + """ + Class representing an LLM runtime environment. + + Attributes: + llm_runtime_type (LLMRuntimeModelType): Type of the LLM runtime. Defaults to OpenAI. + llm_params (Dict[str, str]): Parameters for the LLM runtime. Defaults to a basic GPT-3.5 configuration. + + _llm: Internal instance for the LLM model. Initialized in `init_runtime`. + _program: Program instance used for guidance. Initialized in `init_runtime`. + _llm_template (str): Template string for LLM guidance. + """ + llm_runtime_type: LLMRuntimeType = LLMRuntimeType.STUDENT + llm_runtime_model_type: LLMRuntimeModelType = LLMRuntimeModelType.OpenAI + llm_params: Dict[str, str] = { + 'model': 'gpt-3.5-turbo-instruct', + # 'max_tokens': 10, + # 'temperature': 0, + } + _llm = None + _program = None + # do not override this template + _llm_template: str = '''\ +{{>instructions_program}} + +{{>input_program}} +{{>output_program}}''' + + class Config: + arbitrary_types_allowed = True + + def _create_program(self): + # create an LLM instance + if self.llm_runtime_model_type.value == LLMRuntimeModelType.OpenAI.value: + self._llm = guidance.llms.OpenAI(**self.llm_params) + elif self.llm_runtime_model_type.value == LLMRuntimeModelType.Transformers.value: + self._llm = guidance.llms.Transformers(**self.llm_params) + else: + raise NotImplementedError(f'LLM runtime type {self.llm_runtime_model_type} is not implemented.') + self._program = guidance(self._llm_template, llm=self._llm, silent=not self.verbose) + + def init_runtime(self): + """Initializes the LLM runtime environment. + + Creates an LLM instance based on the runtime type and parameters. + + Returns: + LLMRuntime: Initialized runtime instance. + """ + self._create_program() + return self + + def get_outputs(self, output_template: str) -> List[str]: + """Extracts output fields from the output template. + + Args: + output_template (str): The template string to extract output fields from. + + Returns: + List[str]: List of extracted output fields. + """ + # search for all occurrences of {{...'output'...}} + # TODO: this is a very naive regex implementation - likely to fail in many cases + outputs = re.findall(r'\'(.*?)\'', output_template) + return outputs + + def _process_record( + self, + record, + program, + extra_fields, + outputs=None + ): + """Processes a single record using the guidance program. + + Args: + record (dict or InternalDataFrame): The record to be processed. + program (callable): The guidance program for processing. + extra_fields (dict, optional): Additional fields to include in the processed record. + outputs (list of str, optional): Specific output fields to extract from the result. + + Returns: + dict: Processed output for the record. + """ + + if not isinstance(record, dict): + record = record.to_dict() + else: + record = record.copy() + verified_input = record + # exclude guidance parameter from input + if 'text' in verified_input: + verified_input['text_'] = verified_input['text'] + del verified_input['text'] + + verified_input.update(extra_fields) + result = program( + silent=not self.verbose, + **verified_input + ) + if outputs is None: + verified_output = {'': str(result)} + else: + verified_output = {field: result[field] for field in outputs} + + return verified_output + + def get_input_program(self, input_template): + """Generates an input program from the provided template. + + Args: + input_template (str): Template to generate the input program. + + Returns: + callable: The generated input program. + """ + + # fix input template in case "text" is presented there - there might be other paramater names as well... + fixed_input_template = input_template + if '{{text}}' in fixed_input_template: + fixed_input_template = fixed_input_template.replace('{{text}}', '{{text_}}') + input_program = guidance(fixed_input_template, llm=self._llm, silent=not self.verbose) + return input_program + + def get_output_program(self, output_template): + """Generates an output program from the provided template. + + Args: + output_template (str): Template to generate the output program. + + Returns: + callable: The generated output program. + """ + + return guidance(output_template, llm=self._llm) + + def get_instructions_program(self, instructions): + """Generates an instructions program from the provided template. + + Args: + instructions (str): The instructions to generate the program. + + Returns: + callable: The generated instructions program. + """ + + return guidance(instructions, llm=self._llm) + + def process_record( + self, + record: Dict[str, Any], + input_template: str, + output_template: str, + instructions: str, + extra_fields: Optional[Dict[str, Any]] = None, + ) -> Dict[str, Any]: + """Processes a record using the provided templates and instructions. + + Args: + record (Dict[str, Any]): The record data to be processed. + input_template (str): Template for input processing. + output_template (str): Template for output processing. + instructions (str): Instructions for guidance. + extra_fields (Dict[str, Any], optional): Additional fields to include during processing. + + Returns: + Dict[str, Any]: The processed record. + """ + + outputs = re.findall(r'\'(.*?)\'', output_template) + + input = record.copy() + input.update({ + 'input_program': self.get_input_program(input_template), + 'output_program': self.get_output_program(output_template), + 'instructions_program': self.get_instructions_program(instructions), + }) + output = self._process_record( + record=input, + program=self._program, + outputs=outputs, + extra_fields=extra_fields + ) + return output + + def process_batch( + self, + batch: InternalDataFrame, + input_template: str, + output_template: str, + instructions: str, + extra_fields: Optional[Dict[str, Any]] = None, + ) -> InternalDataFrame: + """Processes a batch of records using the provided templates and instructions. + + Args: + batch (InternalDataFrame): The batch of records to be processed. + input_template (str): Template for input processing. + output_template (str): Template for output processing. + instructions (str): Instructions for guidance. + extra_fields (Dict[str, Any], optional): Additional fields to include during batch processing. + + Returns: + InternalDataFrame: The processed batch of records. + """ + + outputs = self.get_outputs(output_template) + + extra_fields = extra_fields or {} + # copy extra fields to avoid modification of the original dict + extra_fields = extra_fields.copy() + # TODO: it's not efficient way to initialize the program here - should be done once + extra_fields.update({ + 'input_program': self.get_input_program(input_template), + 'output_program': self.get_output_program(output_template), + 'instructions_program': self.get_instructions_program(instructions), + }) + output = batch.progress_apply( + self._process_record, + axis=1, + result_type='expand', + program=self._program, + outputs=outputs, + extra_fields=extra_fields + ) + return output + + def process_batch_inputs( + self, + batch: InternalDataFrame, + input_template: str, + extra_fields: Optional[Dict[str, Any]] = None, + ) -> InternalDataFrame: + """Processes inputs for a batch of records using the provided input template. + + Args: + batch (InternalDataFrame): The batch of records for input processing. + input_template (str): The template for input processing. + extra_fields (Dict[str, Any], optional): Additional fields to include during input processing. + + Returns: + InternalDataFrame: The processed inputs for the batch of records. + """ + + output = batch.progress_apply( + self._process_record, + axis=1, + result_type='expand', + program=self.get_input_program(input_template), + extra_fields=extra_fields or {} + ) + return output + + +class CodeRuntime(Runtime): + """Base class representing a runtime designed for executing code.""" diff --git a/adala/runtimes/openai.py b/adala/runtimes/openai.py new file mode 100644 index 00000000..94c308cd --- /dev/null +++ b/adala/runtimes/openai.py @@ -0,0 +1,74 @@ +import os +import openai +from pydantic import model_validator, field_validator, ValidationError, ValidationInfo, Field +from typing import Optional, Dict +from .base import LLMRuntime, LLMRuntimeType, LLMRuntimeModelType +from adala.utils.logs import print_error + + +class OpenAIRuntime(LLMRuntime): + """Runtime class specifically designed for OpenAI models. + + This class is tailored to use OpenAI models, particularly GPT models. + It inherits from the `LLMRuntime` class and thus can utilize its functionalities but specializes + for the OpenAI ecosystem. + + Attributes: + api_key (str): The API key required to access OpenAI's API. + gpt_model_name (str): Name of the GPT model. Defaults to 'gpt-3.5-turbo-instruct'. + temperature (float): Sampling temperature for the GPT model's output. + A higher value makes output more random, while a lower value makes it more deterministic. + Defaults to 0.0. + """ + + api_key: Optional[str] = None + gpt_model_name: Optional[str] = Field(default='gpt-3.5-turbo-instruct', alias='model') + temperature: Optional[float] = 0.0 + + def _check_api_key(self): + if self.api_key: + return + self.api_key = os.getenv('OPENAI_API_KEY') + if not self.api_key: + print_error( + 'OpenAI API key is not provided. Please set the OPENAI_API_KEY environment variable:\n\n' + 'export OPENAI_API_KEY=your-openai-api-key\n\n' + 'or set the `api_key` attribute of the `OpenAIRuntime` python class:\n\n' + f'{self.__class__.__name__}(..., api_key="your-openai-api-key")\n\n' + f'Read more about OpenAI API keys at https://platform.openai.com/docs/quickstart/step-2-setup-your-api-key') + raise ValidationError('OpenAI API key is not provided.') + + def _check_model_availability(self): + models = openai.Model.list(api_key=self.api_key) + models = set(model['id'] for model in models['data']) + if self.gpt_model_name not in models: + print_error( + f'Requested model "{self.gpt_model_name}" is not available in your OpenAI account. ' + f'Available models are: {models}\n\n' + f'Try to change the runtime settings for {self.__class__.__name__}, for example:\n\n' + f'{self.__class__.__name__}(..., model="gpt-3.5-turbo")\n\n' + ) + raise ValidationError(f'Requested model {self.gpt_model_name} is not available in your OpenAI account.') + + def init_runtime(self): + self._check_api_key() + self._check_model_availability() + + student_models = {'gpt-3.5-turbo-instruct', 'text-davinci-003'} + teacher_models = {'gpt-4', 'gpt-3.5-turbo', 'gpt-3.5-turbo-16k'} + + if self.gpt_model_name in student_models: + self.llm_runtime_type = LLMRuntimeType.STUDENT + elif self.gpt_model_name in teacher_models: + self.llm_runtime_type = LLMRuntimeType.TEACHER + else: + raise NotImplementedError(f'Not supported model: {self.gpt_model_name}.') + + self.llm_runtime_model_type = LLMRuntimeModelType.OpenAI + self.llm_params = { + 'model': self.gpt_model_name, + 'temperature': self.temperature, + 'api_key': self.api_key + } + self._create_program() + return self diff --git a/adala/skills/__init__.py b/adala/skills/__init__.py new file mode 100644 index 00000000..2b1c62d5 --- /dev/null +++ b/adala/skills/__init__.py @@ -0,0 +1,6 @@ +from .labeling.classification import LLMSkill, ClassificationSkill, ClassificationSkillWithCoT +from .labeling.sequence_labeling import SequenceLabelingSkill +from .generation.base import TextGenerationSkill +from .generation.qa import QuestionAnsweringSkill +from .generation.summarization import SummarizationSkill + diff --git a/adala/skills/base.py b/adala/skills/base.py new file mode 100644 index 00000000..c4404ff8 --- /dev/null +++ b/adala/skills/base.py @@ -0,0 +1,375 @@ +import openai +import pandas as pd +import re + +from pydantic import BaseModel +from typing import List, Optional, Any, Dict, Tuple +from abc import ABC, abstractmethod +from pydantic import Field, model_validator + +from typing import Optional +from adala.runtimes.base import LLMRuntime +from adala.datasets.base import Dataset +from adala.runtimes.base import Runtime +from adala.memories.base import ShortTermMemory, LongTermMemory +from adala.utils.internal_data import InternalDataFrame, InternalDataFrameConcat +from adala.utils.logs import print_error + + +class BaseSkill(BaseModel, ABC): + """ + A foundational abstract class representing a skill. This class sets the foundation + for all skills and provides common attributes and methods for skill-based operations. + """ + name: str = Field( + title='Skill name', + description='Unique name of the skill', + default='', + examples=['labeling', 'classification', 'text-generation'] + ) + instructions: str = Field( + title='Skill instructions', + description='Instructs agent what to do with the input data. ' + 'Can use templating to refer to input fields.', + default='', + examples=['Label the input text with the following labels: {{labels}}'] + ) + description: Optional[str] = Field( + default='', + title='Skill description', + description='Description of the skill. Can be used to retrieve skill from the library.', + examples=['The skill to perform sentiment analysis on the input text.'] + ) + input_template: Optional[str] = Field( + title='Input template', + description='Template for the input data. ' + 'Can use templating to refer to input parameters and perform data transformations.', + default="Input: {{{{{input}}}}}", + examples=["Text: {{{{{input}}}}}, Date: {{{{date_column}}}}, Sentiment: {{{{gen 'sentiment'}}}}"] + ) + input_data_field: Optional[str] = Field( + title='Input data field', + description='Input data field name that will be used to match input data.', + examples=['text'], + # TODO: either make it required, or `input_template` required + default=None + ) + output_template: Optional[str] = Field( + title='Output template', + description='Template for the output data. ' + 'Can use templating to refer to input parameters and perform data transformations. ' + 'Should contain at least one field matching `validation_fields`.', + default="Output: {{gen 'predictions'}}", + examples=["Output: {{select 'predictions' options=labels logprobs='score'}}"] + ) + prediction_field: Optional[str] = Field( + title='Prediction field', + description='Prediction field name that will be used to match ground truth labels.' + 'Should match at least one output field in `output_template`, e.g. \'predictions\'', + examples=['predictions'], + default='predictions' + ) + + @model_validator(mode='after') + def validate_inputs(self): + """ + Validates the input_template, updating it if necessary. + + Returns: + BaseSkill: Updated instance of the BaseSkill class. + """ + if '{{{{{input}}}}}' in self.input_template: + if self.input_data_field is None: + print_error(f'You provided skill "{self.name}" with input template:\n\n' + f'{self.__class__.__name__}.input_template = "{self.input_template}"\n\n' + 'that contains "{{{{{input}}}}}" placeholder. (yes... 5 curly braces!) \n\n' + 'In this case, you have to provide skill with `skill.input_data_field` to match the input data.' + f'\nFor example, if your input data stored in `"text"` column, ' + f'you can set\n\nskill = {self.__class__.__name__}(..., input_data_field="text")') + raise ValueError(f'`input_data_field` is not provided for skill {self.name}') + self.input_template = self.input_template.format(input=self.input_data_field) + return self + + def __call__(self, input: InternalDataFrame, runtime: Runtime, dataset: Dataset) -> InternalDataFrame: + """Calls the runtime to process a batch of inputs. Input and + output shapes can be varying, and it should also take care of + data types validation + + Args: + input (InternalDataFrame): Input data in the form of an InternalDataFrame. + runtime (Runtime): The runtime instance to be used for processing. + dataset (Dataset): The dataset containing the data to be processed. + + Returns: + InternalDataFrame: Concatenated dataframe with the original input and the predictions from the runtime. + + """ + + # get user defined dataset input fields + + runtime_predictions = runtime.process_batch( + batch=input, + input_template=self.input_template, + output_template=self.output_template, + instructions=self.instructions, + extra_fields=self._get_extra_fields() + ) + return InternalDataFrameConcat((input, runtime_predictions), axis=1) + + def _get_extra_fields(self): + """ + Retrieves fields that are not categorized as system fields. + + Returns: + dict: A dictionary containing fields that are not system fields. + """ + + # TODO: more robust way to exclude system fields + system_fields = { + 'name', 'description', 'input_template', 'output_template', 'instructions', 'validation_fields'} + extra_fields = self.model_dump(exclude=system_fields) + return extra_fields + + @abstractmethod + def apply( + self, dataset: Dataset, + runtime: Runtime, + experience: ShortTermMemory + ) -> ShortTermMemory: + """ + Applies the skill to a dataset and returns the results. + + Args: + dataset (Dataset): The dataset on which the skill is to be applied. + runtime (Runtime): The runtime instance to be used for processing. + experience (ShortTermMemory): Previous experiences or results. + + Returns: + ShortTermMemory: The updated experience after applying the skill. + """ + + @abstractmethod + def analyze( + self, experience: ShortTermMemory, + student_runtime: Runtime, + teacher_runtime: Optional[Runtime] = None, + memory: Optional[LongTermMemory] = None, + ) -> ShortTermMemory: + """ + Analyzes the results to derive new experiences. + + Args: + experience (ShortTermMemory): The current experience. + student_runtime (Runtime): The student runtime instance. Defaults to None. + teacher_runtime (Runtime, optional): The teacher runtime instance. Defaults to None. + memory (LongTermMemory, optional): Previous long term memories. Defaults to None. + + Returns: + ShortTermMemory: The updated experience after analysis. + """ + + @abstractmethod + def improve( + self, + experience: ShortTermMemory, + runtime: Runtime, + update_instructions: bool = True, + ) -> ShortTermMemory: + """ + Refines the current state of the skill based on its experiences. + + Args: + experience (ShortTermMemory): The current experience. + runtime (Runtime): The runtime instance to be used for processing. + update_instructions (bool, optional): Flag to decide if instructions should be updated. Defaults to True. + + Returns: + ShortTermMemory: The updated experience after improvements. + """ + + +class LLMSkill(BaseSkill): + """ + A skill specialized for Language Models (LLM). Inherits from the BaseSkill + class and provides specific implementations for handling LLM predictions based + on given instructions. + """ + + def apply( + self, + dataset: Dataset, + runtime: LLMRuntime, + experience: ShortTermMemory + ) -> ShortTermMemory: + """ + Applies the LLM skill on a dataset and returns the results. + + Args: + dataset (Dataset): The dataset on which the skill is to be applied. + runtime (LLMRuntime): The runtime instance to be used for processing. + experience (ShortTermMemory): Previous experiences or results. + + Returns: + ShortTermMemory: The updated experience after applying the skill. + """ + + experience = experience.model_copy() + + predictions = [] + + for batch in dataset.batch_iterator(): + runtime_predictions = self(batch, runtime, dataset) + predictions.append(runtime_predictions) + + if not predictions: + predictions = InternalDataFrame() + else: + predictions = InternalDataFrameConcat(predictions, copy=False) + predictions.rename(columns={self.prediction_field: self.name}, inplace=True) + + # append predictions to existing experience, to chain skills + # TODO: implement predictions chaining + experience.predictions = predictions + # if experience.predictions is None: + # experience.predictions = predictions + # else: + # experience.predictions = InternalDataFrameConcat([ + # experience.predictions.drop(columns=[col for col in experience.predictions.columns if col in predictions.columns]), + # predictions + # ], axis=1) + # raise NotImplementedError + + return experience + + def analyze( + self, experience: ShortTermMemory, + student_runtime: Runtime, + teacher_runtime: Optional[Runtime] = None, + memory: Optional[LongTermMemory] = None + ) -> ShortTermMemory: + """ + Analyzes the results to identify any discrepancies and returns the observed experience. + + Args: + experience (ShortTermMemory): The current experience. + student_runtime (Runtime): The student runtime instance. Defaults to None. + teacher_runtime (Runtime, optional): The teacher runtime instance. Defaults to None. + memory (LongTermMemory, optional): Previous long term memories. Defaults to None. + + Returns: + ShortTermMemory: The updated experience after analysis. + """ + + experience = experience.model_copy() + + # TODO: can be multiple prediction validation fields + match = experience.match_column_name + errors = experience.evaluations[~experience.evaluations[match]] + experience.accuracy = experience.evaluations[match].mean() + if errors.empty: + # No errors - nothing to analyze + experience.errors = errors + return experience + + # collect errors and create error report + # first sample errors - make it uniform, but more sophisticated sampling can be implemented + errors = errors.sample(n=min(3, errors.shape[0])) + + # collect error inputs from runtime + extra_fields = self._get_extra_fields() + inputs = student_runtime.process_batch_inputs( + batch=errors, + input_template=self.input_template, + extra_fields=extra_fields + ) + + # construct error report + errors = pd.concat([ + inputs, + errors[[self.name, experience.ground_truth_column_name]] + ], axis=1) + errors.columns = ['input', 'prediction', 'ground_truth'] + if not teacher_runtime: + teacher_runtime = student_runtime + + error_reasons = teacher_runtime.process_batch( + errors, + instructions="{{#system~}}\n" + "LLM prompt was created by concatenating instructions with text input:\n\n" + "Prediction = LLM(Input, Instructions)\n\n" + "We expect the prediction to be equal to the ground truth.\n" + "Your task is to provide a reason for the error due to the original instruction.\n" + "Be concise and specific.\n\n" + f"Instructions: {self.instructions}\n" + "{{~/system}}", + input_template="{{#user~}}\n" + "{{input}}\n" + "Prediction: {{prediction}}\n" + "Ground truth: {{ground_truth}}\n" + "Explanation:\n" + "{{~/user}}", + output_template="{{#assistant~}}{{gen 'reason'}}{{~/assistant}}", + extra_fields=extra_fields + ) + errors['reason'] = error_reasons['reason'] + + experience.errors = errors + return experience + + def improve( + self, + experience: ShortTermMemory, + runtime: Runtime, + update_instructions: bool = True, + ) -> ShortTermMemory: + """ + Refines the LLM skill based on its recent experiences. + + Args: + experience (ShortTermMemory): The current experience. + runtime (Runtime): The runtime instance to be used for processing. + update_instructions (bool, optional): Flag to decide if instructions should be updated. Defaults to True. + + Returns: + ShortTermMemory: The updated experience after improvements. + """ + + experience = experience.model_copy() + + errors = experience.errors.to_dict(orient='records') + result = runtime.process_record( + record={ + 'errors': errors + }, + instructions="{{#system~}}\n" + "LLM prompt was created by concatenating instructions with text input:\n\n" + "Prediction = LLM(Input, Instructions)\n\n" + "We expect the prediction to be equal to the ground truth.\n" + "Your task is to craft a revised concise instruction for the LLM. " + "Follow best practices for LLM prompt engineering.\n" + "Include 2-3 examples at the end of your response to demonstrate how the new instruction would be applied.\n" + "Use the following format for your examples:\n" + "Input: ...\n" + "Output: ...\n\n" + "{{~/system}}\n", + input_template="{{#user~}}\n" + f"Old instruction: {self.instructions}\n\n" + "Errors:\n{{#each errors}}" + "\n{{this.input}}\n" + "Prediction: {{this.prediction}}\n" + "Ground truth: {{this.ground_truth}}\n" + "{{/each}}\n" + "New instruction:\n" + "{{~/user}}", + output_template="{{#assistant~}}{{gen 'new_instruction'}}{{~/assistant}}", + extra_fields=self._get_extra_fields() + ) + new_instruction = result['new_instruction'] + + experience.initial_instructions = self.instructions + experience.updated_instructions = new_instruction + + if update_instructions: + self.instructions = new_instruction + + return experience diff --git a/adala/skills/generation/__init__.py b/adala/skills/generation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adala/skills/generation/base.py b/adala/skills/generation/base.py new file mode 100644 index 00000000..d943aff0 --- /dev/null +++ b/adala/skills/generation/base.py @@ -0,0 +1,15 @@ +from ..base import LLMSkill + + +class TextGenerationSkill(LLMSkill): + """ + Skill specialized for generating text based on the provided input. + + This involves tasks where the LLM is expected to produce creative, coherent, and contextually + relevant textual content based on the given input. + + Attributes: + instructions (str): Instruction to guide the LLM in text generation. + """ + + instructions: str = 'Generate text based on the provided input.' diff --git a/adala/skills/generation/qa.py b/adala/skills/generation/qa.py new file mode 100644 index 00000000..48a75cf1 --- /dev/null +++ b/adala/skills/generation/qa.py @@ -0,0 +1,23 @@ +from .base import TextGenerationSkill + + +class QuestionAnsweringSkill(TextGenerationSkill): + """ + Skill specialized for answering questions based on the provided input. + + Inherits from the TextGenerationSkill and focuses on generating answers to the questions + posed in the input. The class customizes the instructions, input, and output templates + specifically for question-answering tasks. + + Attributes: + instructions (str): Instruction to guide the LLM in answering the question. + input_template (str): Format in which the question is presented to the LLM. + output_template (str): Expected format of the LLM's answer. + prediction_field (str): Field name for the generated answer. + """ + + instructions: str = 'Answer the question.' + input_template: str = "Question: {{{{{input}}}}}" + output_template: str = "Answer: {{gen 'answer'}}" + prediction_field: str = 'answer' + diff --git a/adala/skills/generation/summarization.py b/adala/skills/generation/summarization.py new file mode 100644 index 00000000..a2c980ab --- /dev/null +++ b/adala/skills/generation/summarization.py @@ -0,0 +1,22 @@ +from .base import TextGenerationSkill + + +class SummarizationSkill(TextGenerationSkill): + """ + Skill specialized for summarizing lengthy texts based on the provided input. + + Inherits from the TextGenerationSkill and focuses on generating concise summaries + for the input texts. The class customizes the instructions, input, and output templates + specifically for text summarization tasks. + + Attributes: + instructions (str): Instruction to guide the LLM in summarizing the text. + input_template (str): Format in which the full text is presented to the LLM. + output_template (str): Expected format of the LLM's summary. + prediction_field (str): Field name for the generated summary. + """ + + instructions: str = 'Summarize the text.' + input_template: str = "Text: {{{{{input}}}}}" + output_template: str = "Summary: {{gen 'summary'}}" + prediction_field: str = 'summary' diff --git a/adala/skills/generation/translation.py b/adala/skills/generation/translation.py new file mode 100644 index 00000000..319f7442 --- /dev/null +++ b/adala/skills/generation/translation.py @@ -0,0 +1,27 @@ +from .base import TextGenerationSkill + + +class TranslationSkill(TextGenerationSkill): + """ + Skill specialized for translating text from one language to another. + + Inherits from the TextGenerationSkill and focuses on translating the input text to the + specified target language. The class customizes the instructions, input, and output templates + specifically for translation tasks. + + Attributes: + instructions (str): Instruction to guide the LLM in translating the text. + input_template (str): Format in which the full text is presented to the LLM. + output_template (str): Expected format of the LLM's translation. + prediction_field (str): Field name for the generated translation. + target_language (str): Language to which the input text is translated. + """ + + name: str = 'translation' + description: str = 'Translate text from one language to another.' + instructions: str = 'Identify the language of the given text and translate it to {{target_language}}.' + input_template: str = "Text: {{{{{input}}}}}" + # output_template: str = "Input language: {{gen 'detected_language'}}\nTranslation: {{gen 'translation'}}" + output_template: str = "Translation: {{gen 'translation'}}" + prediction_field: str = 'translation' + target_language: str = 'English' diff --git a/adala/skills/labeling/__init__.py b/adala/skills/labeling/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adala/skills/labeling/classification.py b/adala/skills/labeling/classification.py new file mode 100644 index 00000000..c80204e1 --- /dev/null +++ b/adala/skills/labeling/classification.py @@ -0,0 +1,35 @@ +from ..base import LLMSkill +from typing import List + + +class ClassificationSkill(LLMSkill): + """ + Skill specialized for classifying text inputs based on a predefined set of labels. + + Args: + instructions (str): Templated instruction to guide the LLM in classification. + labels (List[str]): A list of valid labels for the classification task. + output_template (str): Templated string to format the output from the LLM. + prediction_field (str): Specifies the field in which predictions will be stored. + """ + + instructions: str = 'Label the input text with the following labels: {{labels}}' + labels: List[str] + output_template: str = "Output: {{select 'predictions' options=labels logprobs='score'}}" + prediction_field: str = 'predictions' + + +class ClassificationSkillWithCoT(ClassificationSkill): + """ + Skill specialized for classifying text inputs with the addition of generating a Chain of Thought. + + Args: + instructions (str): Templated instruction to guide the LLM in classification and to generate a rationale. + labels (List[str]): A list of valid labels for the classification task. + input_template (str): Templated string to format the input, which includes a rationale (thoughts). + output_template (str): Templated string to format the output from the LLM. + prediction_field (str): Specifies the field in which predictions will be stored. + """ + + instructions: str = 'Label the input text with the following labels: {{labels}}. Provide a rationale for your answer.' + output_template: str = "Thoughts: {{gen 'rationale'}}\nOutput: {{select 'predictions' options=labels logprobs='score'}}" diff --git a/adala/skills/labeling/sequence_labeling.py b/adala/skills/labeling/sequence_labeling.py new file mode 100644 index 00000000..81611858 --- /dev/null +++ b/adala/skills/labeling/sequence_labeling.py @@ -0,0 +1,26 @@ +from ..base import LLMSkill +from typing import List + + +class SequenceLabelingSkill(LLMSkill): + """ + Skill specialized for sequence labeling on text inputs based on a predefined set of labels. + + This involves tasks like named entity recognition where each word/token in the sequence + might be assigned a label. + + Args: + instructions (str): Templated instruction to guide the LLM in sequence labeling. + labels (List[str]): A list of valid labels for the sequence labeling task. + input_template (str): Templated string to format the input for the LLM. + output_template (str): Templated string to format the output from the LLM. + prediction_field (str): Specifies the field in which predictions will be stored. + + Note: + This class is still a work in progress. + """ + instructions: str = 'Label the input text with the following labels: {{labels}}' + labels: List[str] + input_template: str = "Input: {{{{{input}}}}}" + output_template: str = "Output: {{select 'predictions' options=labels logprobs='score'}}" + prediction_field: str = 'predictions' diff --git a/adala/skills/skillset.py b/adala/skills/skillset.py new file mode 100644 index 00000000..eff7cb47 --- /dev/null +++ b/adala/skills/skillset.py @@ -0,0 +1,137 @@ +from pydantic import BaseModel, model_validator +from abc import ABC, abstractmethod +from typing import List, Union, Dict, Any, Optional +from adala.datasets.base import Dataset +from adala.runtimes.base import Runtime +from adala.memories.base import ShortTermMemory +from .base import BaseSkill + + +class SkillSet(BaseModel, ABC): + """ + Represents a collection of interdependent skills aiming to achieve a specific goal. + + A skill set breaks down the path to achieve a goal into necessary precursor skills. + Agents can evolve these skills either in parallel for tasks like self-consistency or + sequentially for complex problem decompositions and causal reasoning. In the most generic + cases, task decomposition can involve a graph-based approach. + + Args: + skills (Dict[str, BaseSkill]): Dictionary mapping skill names to their corresponding + BaseSkill instances. + """ + + skills: Dict[str, BaseSkill] + + @abstractmethod + def apply(self, dataset: Dataset, runtime: Runtime, experience: Optional[ShortTermMemory] = None) -> ShortTermMemory: + """ + Apply the skill set to a dataset using a specified runtime. + + Args: + dataset (Dataset): The dataset to apply the skill set to. + runtime (Runtime): The runtime environment in which to apply the skills. + experience (Optional[ShortTermMemory], optional): Existing experience data. Defaults to None. + + Returns: + ShortTermMemory: Updated experience after applying the skill set. + """ + + @abstractmethod + def select_skill_to_improve(self, experience: ShortTermMemory) -> BaseSkill: + """ + Select the next skill to enhance based on the current experience. + + Args: + experience (ShortTermMemory): Current experience data. + + Returns: + BaseSkill: Skill selected for improvement. + """ + + +class LinearSkillSet(SkillSet): + """ + Represents a sequence of skills that are acquired in a specific order to achieve a goal. + + LinearSkillSet ensures that skills are developed in a sequential manner, determined either + by the provided skill_sequence or by the lexicographical order of skill names. + + Args: + skill_sequence (List[str], optional): Ordered list of skill names indicating the order + in which they should be acquired. + """ + + skill_sequence: List[str] = None + + @model_validator(mode='after') + def skill_sequence_validator(self): + """ + Validates and sets the default order for the skill sequence if not provided. + + Returns: + LinearSkillSet: The current instance with updated skill_sequence attribute. + """ + + if self.skill_sequence is None: + # use default skill sequence defined by lexicographical order + self.skill_sequence = sorted(self.skills.keys()) + return self + + def apply( + self, dataset: Dataset, + runtime: Runtime, + experience: Optional[ShortTermMemory] = None + ) -> ShortTermMemory: + """ + Sequentially applies each skill on the dataset, enhancing the agent's experience. + + Args: + dataset (Dataset): The dataset to apply the skills on. + runtime (Runtime): The runtime environment in which to apply the skills. + experience (Optional[ShortTermMemory], optional): Existing experience data. Defaults to None. + + Returns: + ShortTermMemory: Updated experience after sequentially applying the skills. + """ + if experience is None: + experience = ShortTermMemory(dataset=dataset) + else: + experience = experience.model_copy() + + for skill_name in self.skill_sequence: + skill = self.skills[skill_name] + experience = skill.apply(dataset, runtime, experience) + + return experience + + def select_skill_to_improve(self, experience: ShortTermMemory) -> BaseSkill: + """ + Picks the next skill for improvement in the sequence. + + Args: + experience (ShortTermMemory): Current experience data. + + Returns: + BaseSkill: The next skill selected for improvement. + """ + + # TODO: implement real logic for skill selection + return self.skills[self.skill_sequence[0]] + + def __rich__(self): + """Returns a rich representation of the skill.""" + # TODO: move it to a base class and use repr derived from Skills + text = f"[bold blue]Total Agent Skills: {len(self.skills)}[/bold blue]\n\n" + for skill in self.skills.values(): + text += f'[bold underline green]{skill.name}[/bold underline green]\n' \ + f'[green]{skill.instructions}[green]\n' + return text + + +class ParallelSkillSet(SkillSet): + """ + Represents a set of skills that are acquired simultaneously to reach a goal. + """ + + pass diff --git a/adala/utils/__init__.py b/adala/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adala/utils/internal_data.py b/adala/utils/internal_data.py new file mode 100644 index 00000000..8c8f6699 --- /dev/null +++ b/adala/utils/internal_data.py @@ -0,0 +1,16 @@ +import pandas as pd +from typing import List, Dict, Any, Union, Iterable + +RawRecord = Dict[str, Any] +RawRecords = List[RawRecord] + +# Internal data tables representation. Replace this with Dask or Polars in the future. +InternalDataFrame = pd.DataFrame + + +def InternalDataFrame_encoder(df: InternalDataFrame) -> List: + return df.to_dict(orient='records') + + +def InternalDataFrameConcat(dfs: Iterable[InternalDataFrame], **kwargs) -> InternalDataFrame: + return pd.concat(dfs, **kwargs) diff --git a/adala/utils/logs.py b/adala/utils/logs.py new file mode 100644 index 00000000..d9cca2a3 --- /dev/null +++ b/adala/utils/logs.py @@ -0,0 +1,48 @@ +import pandas as pd +import time + +from rich import print +from rich.table import Table +from rich import box +from rich.console import Console +from typing import Optional +from .internal_data import InternalDataFrame + +console = Console() +error_console = Console(stderr=True, style="bold red") + + +def print_text(text: str, style=None, streaming_style=False): + if streaming_style: + for char in text: + console.print(char, sep='', end='', style=style) + time.sleep(0.01) + console.print() + else: + console.print(text, style=style) + + +def print_error(text: str): + error_console.print(text) + + +def print_dataframe(dataframe: InternalDataFrame): + num_rows = 5 + table = Table(show_header=True, header_style="bold magenta") + # index_name = dataframe.index.name or 'index' + # table.add_column(index_name) + + for column in dataframe.columns: + table.add_column(str(column)) + + for index, value_list in enumerate(dataframe.iloc[:num_rows].values.tolist()): + # row = [str(index)] + row = [] + row += [str(x) for x in value_list] + table.add_row(*row) + + # Update the style of the table + table.row_styles = ["none", "dim"] + table.box = box.SIMPLE_HEAD + + console.print(table) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..43dd0a3c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,24 @@ +# Adala documentation + +To develop the documentation, you need to install [MkDocs](https://www.mkdocs.org/). + +``` +pip install requirements.txt +``` + +For full documentation visit [mkdocs.org](https://www.mkdocs.org). + +## Commands + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +## Project layout + + mkdocs.yml # The configuration file. + docs/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. + diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 00000000..472e8f82 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,46 @@ +site_name: Adala Docs + +logo: src/img/logo.png +favicon: src/img/logo.png + +repo_url: https://github.com/humansignal/Adala +edit_uri: https://github.com/humansignal/Adala/tree/main/docs/src + +repo_name: humansignal/Adala +docs_dir: src + +plugins: + - search + - autorefs + - mkdocs-jupyter + - mkdocstrings + + +theme: + name: "material" + features: + - content.code.copy + - content.tabs.link + - content.action.edit + - toc.follow + - toc.integrate + - navigation.top + - navigation.tabs + - navigation.tabs.sticky + - navigation.footer + - navigation.tracking + - navigation.instant + - navigation.indexes + - navigation.expand + - navigation.sections + + +nav: + - Home: 'index.md' + - agents.md + - datasets.md + - environments.md + - memories.md + - runtimes.md + - skills.md + # - utils.md diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..2f00bce9 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +mkdocs +mkdocs-jupyter +mkdocs-material +mkdocstrings[python] diff --git a/docs/src/agents.md b/docs/src/agents.md new file mode 100644 index 00000000..5b6e9da9 --- /dev/null +++ b/docs/src/agents.md @@ -0,0 +1,2 @@ + +::: adala.agents.base diff --git a/docs/src/datasets.md b/docs/src/datasets.md new file mode 100644 index 00000000..a96ed85a --- /dev/null +++ b/docs/src/datasets.md @@ -0,0 +1,4 @@ + +::: adala.datasets.base + +::: adala.datasets.dataframe diff --git a/docs/src/environments.md b/docs/src/environments.md new file mode 100644 index 00000000..27c5e608 --- /dev/null +++ b/docs/src/environments.md @@ -0,0 +1,2 @@ + +::: adala.environments.base diff --git a/docs/src/img/diagram.png b/docs/src/img/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e430937ef2ca62338885480ff6df17bc6b58778f GIT binary patch literal 142044 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 SVi&J8Q&-Vau6Sq__WuEy6E_b4 literal 0 HcmV?d00001 diff --git a/docs/src/img/logo.png b/docs/src/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b37a89a8642d85414d2701e7903f709098ac8f42 GIT binary patch literal 154625 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%0A9E1hd_ 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

lA()aJU@w-THL6g`v8Q( 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

m|o#tCT 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 literal 0 HcmV?d00001 diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 00000000..90a68d3c --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,109 @@ +# Quickstart + +Adala is an **A**utonomous **DA**ta (**L**abeling) **A**gent framework. + +Adala offers a robust framework for implementing agents specialized in data processing, with an emphasis on +diverse data labeling tasks. These agents are autonomous, meaning they can independently acquire one or more skills +through iterative learning. This learning process is influenced by their operating environment, observations, and +reflections. Users define the environment by providing a ground truth dataset. Every agent learns and applies its skills +in what we refer to as a "runtime", synonymous with LLM. + +![Diagram of components](img/diagram.png) + + +## Installation + +Install Adala: + +```sh +pip install adala +``` + +## Prerequisites + +Set OPENAI_API_KEY ([see instructions here](https://platform.openai.com/docs/quickstart/step-2-setup-your-api-key)) + + +## ๐ŸŽฌ Quickstart + +In this example we will use Adala as a standalone library directly inside Python notebook. + +Click [here](https://github.com/HumanSignal/Adala/blob/master/examples/quickstart.ipynb) to see an extended quickstart example. + +```python +import pandas as pd + +from adala.agents import Agent +from adala.datasets import DataFrameDataset +from adala.environments import BasicEnvironment +from adala.skills import ClassificationSkill +from adala.runtimes import OpenAIRuntime +from rich import print + +# Train dataset +ground_truth_df = pd.DataFrame([ + ["It was the negative first impressions, and then it started working.", "Positive"], + ["Not loud enough and doesn't turn on like it should.", "Negative"], + ["I don't know what to say.", "Neutral"], + ["Manager was rude, but the most important that mic shows very flat frequency response.", "Positive"], + ["The phone doesn't seem to accept anything except CBR mp3s.", "Negative"], + ["I tried it before, I bought this device for my son.", "Neutral"], +], columns=["text", "ground_truth"]) + +# Test dataset +predict_df = pd.DataFrame([ + "All three broke within two months of use.", + "The device worked for a long time, can't say anything bad.", + "Just a random line of text." +], columns=["text"]) + +ground_truth_dataset = DataFrameDataset(df=ground_truth_df) +predict_dataset = DataFrameDataset(df=predict_df) + +agent = Agent( + # connect to a dataset + environment=BasicEnvironment( + ground_truth_dataset=ground_truth_dataset, + ground_truth_column="ground_truth" + ), + + # define a skill + skills=ClassificationSkill( + name='sentiment_classification', + instructions="Label text as subjective or objective.", + labels=["Positive", "Negative", "Neutral"], + input_data_field='text' + ), + + # define all the different runtimes your skills may use + runtimes = { + # You can specify your OPENAI API KEY here via `OpenAIRuntime(..., api_key='your-api-key')` + 'openai': OpenAIRuntime(model='gpt-3.5-turbo-instruct'), + 'openai-gpt3': OpenAIRuntime(model='gpt-3.5-turbo'), + # 'openai-gpt4': OpenAIRuntime(model='gpt-4'), + }, + default_runtime='openai', + + # NOTE! If you don't have an access to gpt4 - replace it with "openai-gpt3" + # default_teacher_runtime='openai-gpt4' +) + +print(agent) +print(agent.skills) + +agent.learn(learning_iterations=3, accuracy_threshold=0.95) + +print('\n=> Run tests ...') +run = agent.apply_skills(predict_dataset) +print('\n => Test results:') +print(run) +``` + +## Reference + +- [**Agents**](agents.md) - main interface for interacting with environment +- [**Datasets**](datasets.md) - data inputs for agents +- [**Environments**](environments.md) - environments for agents, where it collects ground truth signal +- [**Memories**](memories.md) - agent's memory for storing and retrieving data +- [**Runtimes**](runtimes.md) - agent's execution runtime (e.g. LLMs providers) +- [**Skills**](skills.md) - agent skills for data labeling diff --git a/docs/src/memories.md b/docs/src/memories.md new file mode 100644 index 00000000..cda68d25 --- /dev/null +++ b/docs/src/memories.md @@ -0,0 +1,4 @@ + +::: adala.memories.base + +::: adala.memories.file_memory diff --git a/docs/src/runtimes.md b/docs/src/runtimes.md new file mode 100644 index 00000000..098f44fc --- /dev/null +++ b/docs/src/runtimes.md @@ -0,0 +1,4 @@ + +::: adala.runtimes.base + +::: adala.runtimes.openai diff --git a/docs/src/skills.md b/docs/src/skills.md new file mode 100644 index 00000000..3b770dce --- /dev/null +++ b/docs/src/skills.md @@ -0,0 +1,14 @@ + +::: adala.skills.base + +::: adala.skills.skillset + +::: adala.skills.generation.base + +::: adala.skills.generation.qa + +::: adala.skills.generation.summarization + +::: adala.skills.labeling.classification + +::: adala.skills.labeling.sequence_labeling diff --git a/docs/src/utils.md b/docs/src/utils.md new file mode 100644 index 00000000..6db4d6e2 --- /dev/null +++ b/docs/src/utils.md @@ -0,0 +1,2 @@ + +::: adala.utils.internal_data diff --git a/examples/classification_skill.ipynb b/examples/classification_skill.ipynb new file mode 100644 index 00000000..f5acd9cb --- /dev/null +++ b/examples/classification_skill.ipynb @@ -0,0 +1,907 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "94ad15ac", + "metadata": {}, + "source": [ + "# Classification skill" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "a2f6d99b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textcategory
0Apple product with a sleek design.Electronics
1Laptop stand for the kitchen.Furniture/Home Decor
2Chocolate leather boots.Footwear/Clothing
3Wooden cream for surfaces.Furniture/Home Decor
4Natural finish for your lips.Beauty/Personal Care
\n", + "
" + ], + "text/plain": [ + " text category\n", + "0 Apple product with a sleek design. Electronics\n", + "1 Laptop stand for the kitchen. Furniture/Home Decor\n", + "2 Chocolate leather boots. Footwear/Clothing\n", + "3 Wooden cream for surfaces. Furniture/Home Decor\n", + "4 Natural finish for your lips. Beauty/Personal Care" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame([\n", + " {\"text\": \"Apple product with a sleek design.\", \"category\": \"Electronics\"},\n", + " {\"text\": \"Laptop stand for the kitchen.\", \"category\": \"Furniture/Home Decor\"},\n", + " {\"text\": \"Chocolate leather boots.\", \"category\": \"Footwear/Clothing\"},\n", + " {\"text\": \"Wooden cream for surfaces.\", \"category\": \"Furniture/Home Decor\"},\n", + " {\"text\": \"Natural finish for your lips.\", \"category\": \"Beauty/Personal Care\"}\n", + "])\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "6ee2cebf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:00<00:00, 45.32it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n",
+       "\n",
+       "=> Iteration #0: Comparing to ground truth, analyzing and improving ...\n",
+       "
\n" + ], + "text/plain": [ + "\n", + "\n", + "=> Iteration #\u001b[1;36m0\u001b[0m: Comparing to ground truth, analyzing and improving \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Comparing predictions to ground truth data ...\n",
+       "
\n" + ], + "text/plain": [ + "Comparing predictions to ground truth data \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
                                                                                                                   \n",
+       "  text                   category               product_category_clโ€ฆ   score                  category__x__produโ€ฆ  \n",
+       " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n",
+       "  Apple product with a   Electronics            Electronics            {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  sleek design.                                                        -7.4104013,                                 \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -0.0006187928400000โ€ฆ                        \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -12.842141,                                 \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -13.004693,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -11.657418}                          \n",
+       "  Laptop stand for the   Furniture/Home Decor   Electronics            {'Footwear/Clothingโ€ฆ   False                \n",
+       "  kitchen.                                                             -5.046604,                                  \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -0.0079155900000000โ€ฆ                        \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -12.865566,                                 \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -6.5376244,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -12.865139}                          \n",
+       "  Chocolate leather      Footwear/Clothing      Footwear/Clothing      {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  boots.                                                               -0.0410600639999999โ€ฆ                        \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -11.113914,                                 \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -3.3290619999999995,                        \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -5.6644883,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -6.991324}                           \n",
+       "  Wooden cream for       Furniture/Home Decor   Furniture/Home Decor   {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  surfaces.                                                            -5.391147,                                  \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -12.019984,                                 \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -8.380702,                                  \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor':                                     \n",
+       "                                                                       -0.0170855020000000โ€ฆ                        \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -4.4105563}                          \n",
+       "  Natural finish for     Beauty/Personal Care   Beauty/Personal Care   {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  your lips.                                                           -3.9375494,                                 \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -10.5326805,                                \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -9.545025,                                  \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -10.485508,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care':                                      \n",
+       "                                                                       -0.0198170879999999โ€ฆ                        \n",
+       "                                                                                                                   \n",
+       "
\n" + ], + "text/plain": [ + " \n", + " \u001b[1;35m \u001b[0m\u001b[1;35mtext \u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mcategory \u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mproduct_category_clโ€ฆ\u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mscore \u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mcategory__x__produโ€ฆ\u001b[0m\u001b[1;35m \u001b[0m \n", + " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n", + " Apple product with a Electronics Electronics {'Footwear/Clothingโ€ฆ True \n", + " sleek design. -7.4104013, \n", + " 'Electronics': \n", + " -0.0006187928400000โ€ฆ \n", + " 'Food/Beverages': \n", + " -12.842141, \n", + " 'Furniture/Home \n", + " Decor': -13.004693, \n", + " 'Beauty/Personal \n", + " Care': -11.657418} \n", + " \u001b[2m \u001b[0m\u001b[2mLaptop stand for the\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mElectronics \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m{'Footwear/Clothingโ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFalse \u001b[0m\u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m\u001b[2mkitchen. \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-5.046604, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Electronics': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-0.0079155900000000โ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Food/Beverages': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-12.865566, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Furniture/Home \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mDecor': -6.5376244, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Beauty/Personal \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mCare': -12.865139} \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " Chocolate leather Footwear/Clothing Footwear/Clothing {'Footwear/Clothingโ€ฆ True \n", + " boots. -0.0410600639999999โ€ฆ \n", + " 'Electronics': \n", + " -11.113914, \n", + " 'Food/Beverages': \n", + " -3.3290619999999995, \n", + " 'Furniture/Home \n", + " Decor': -5.6644883, \n", + " 'Beauty/Personal \n", + " Care': -6.991324} \n", + " \u001b[2m \u001b[0m\u001b[2mWooden cream for \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m{'Footwear/Clothingโ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mTrue \u001b[0m\u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m\u001b[2msurfaces. \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-5.391147, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Electronics': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-12.019984, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Food/Beverages': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-8.380702, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Furniture/Home \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mDecor': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-0.0170855020000000โ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Beauty/Personal \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mCare': -4.4105563} \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " Natural finish for Beauty/Personal Care Beauty/Personal Care {'Footwear/Clothingโ€ฆ True \n", + " your lips. -3.9375494, \n", + " 'Electronics': \n", + " -10.5326805, \n", + " 'Food/Beverages': \n", + " -9.545025, \n", + " 'Furniture/Home \n", + " Decor': -10.485508, \n", + " 'Beauty/Personal \n", + " Care': \n", + " -0.0198170879999999โ€ฆ \n", + " \n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Analyze evaluation experience ...\n",
+       "
\n" + ], + "text/plain": [ + "Analyze evaluation experience \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 1/1 [00:00<00:00, 137.21it/s]\n", + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 1/1 [00:04<00:00, 4.85s/it]\n" + ] + }, + { + "data": { + "text/html": [ + "
Number of errors: 1\n",
+       "
\n" + ], + "text/plain": [ + "Number of errors: \u001b[1;36m1\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy = 80.00%\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;31mAccuracy = \u001b[0m\u001b[1;36m80.00\u001b[0m\u001b[1;31m%\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Improve \"product_category_classification\" skill based on analysis ...\n",
+       "
\n" + ], + "text/plain": [ + "Improve \u001b[32m\"product_category_classification\"\u001b[0m skill based on analysis \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Updated instructions for skill \"product_category_classification\":\n",
+       "\n",
+       "
\n" + ], + "text/plain": [ + "Updated instructions for skill \u001b[32m\"product_category_classification\"\u001b[0m:\n", + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Categorize the input text into one of the following labels: ['Footwear/Clothing', 'Electronics', 'Food/Beverages', \n",
+       "'Furniture/Home Decor', 'Beauty/Personal Care']. Choose the label that best represents the main category of the \n",
+       "input text.\n",
+       "\n",
+       "Examples:\n",
+       "\n",
+       "Input: Laptop stand for the kitchen.\n",
+       "Output: Furniture/Home Decor\n",
+       "\n",
+       "Input: Running shoes for men.\n",
+       "Output: Footwear/Clothing\n",
+       "\n",
+       "Input: Organic shampoo for dry hair.\n",
+       "Output: Beauty/Personal Care\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;32mCategorize the input text into one of the following labels: \u001b[0m\u001b[1;32m[\u001b[0m\u001b[32m'Footwear/Clothing'\u001b[0m\u001b[1;32m, \u001b[0m\u001b[32m'Electronics'\u001b[0m\u001b[1;32m, \u001b[0m\u001b[32m'Food/Beverages'\u001b[0m\u001b[1;32m, \u001b[0m\n", + "\u001b[32m'Furniture/Home Decor'\u001b[0m\u001b[1;32m, \u001b[0m\u001b[32m'Beauty/Personal Care'\u001b[0m\u001b[1;32m]\u001b[0m\u001b[1;32m. Choose the label that best represents the main category of the \u001b[0m\n", + "\u001b[1;32minput text.\u001b[0m\n", + "\n", + "\u001b[1;32mExamples:\u001b[0m\n", + "\n", + "\u001b[1;32mInput: Laptop stand for the kitchen.\u001b[0m\n", + "\u001b[1;32mOutput: Furniture/Home Decor\u001b[0m\n", + "\n", + "\u001b[1;32mInput: Running shoes for men.\u001b[0m\n", + "\u001b[1;32mOutput: Footwear/Clothing\u001b[0m\n", + "\n", + "\u001b[1;32mInput: Organic shampoo for dry hair.\u001b[0m\n", + "\u001b[1;32mOutput: Beauty/Personal Care\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Re-apply product_category_classification skill to dataset ...\n",
+       "
\n" + ], + "text/plain": [ + "Re-apply product_category_classification skill to dataset \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:03<00:00, 1.48it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n",
+       "\n",
+       "=> Iteration #1: Comparing to ground truth, analyzing and improving ...\n",
+       "
\n" + ], + "text/plain": [ + "\n", + "\n", + "=> Iteration #\u001b[1;36m1\u001b[0m: Comparing to ground truth, analyzing and improving \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Comparing predictions to ground truth data ...\n",
+       "
\n" + ], + "text/plain": [ + "Comparing predictions to ground truth data \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
                                                                                                                   \n",
+       "  text                   category               product_category_clโ€ฆ   score                  category__x__produโ€ฆ  \n",
+       " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n",
+       "  Apple product with a   Electronics            Electronics            {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  sleek design.                                                        -13.669698,                                 \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -4.4849444000336985โ€ฆ                        \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -14.937825,                                 \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -13.595754,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -13.327497}                          \n",
+       "  Laptop stand for the   Furniture/Home Decor   Furniture/Home Decor   {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  kitchen.                                                             -9.9471035,                                 \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -4.787397,                                  \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -12.115164,                                 \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor':                                     \n",
+       "                                                                       -0.0084281690000000โ€ฆ                        \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -12.145201}                          \n",
+       "  Chocolate leather      Footwear/Clothing      Footwear/Clothing      {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  boots.                                                               -0.0003247375000000โ€ฆ                        \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -17.322811,                                 \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -8.062444,                                  \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -12.040547,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -12.584134}                          \n",
+       "  Wooden cream for       Furniture/Home Decor   Furniture/Home Decor   {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  surfaces.                                                            -15.480099,                                 \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -17.015057,                                 \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -13.499149,                                 \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor':                                     \n",
+       "                                                                       -0.0001718358800000โ€ฆ                        \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care': -8.679317}                           \n",
+       "  Natural finish for     Beauty/Personal Care   Beauty/Personal Care   {'Footwear/Clothingโ€ฆ   True                 \n",
+       "  your lips.                                                           -11.842119,                                 \n",
+       "                                                                       'Electronics':                              \n",
+       "                                                                       -14.539164,                                 \n",
+       "                                                                       'Food/Beverages':                           \n",
+       "                                                                       -13.285265,                                 \n",
+       "                                                                       'Furniture/Home                             \n",
+       "                                                                       Decor': -14.923815,                         \n",
+       "                                                                       'Beauty/Personal                            \n",
+       "                                                                       Care':                                      \n",
+       "                                                                       -9.72990600003512e-โ€ฆ                        \n",
+       "                                                                                                                   \n",
+       "
\n" + ], + "text/plain": [ + " \n", + " \u001b[1;35m \u001b[0m\u001b[1;35mtext \u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mcategory \u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mproduct_category_clโ€ฆ\u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mscore \u001b[0m\u001b[1;35m \u001b[0m \u001b[1;35m \u001b[0m\u001b[1;35mcategory__x__produโ€ฆ\u001b[0m\u001b[1;35m \u001b[0m \n", + " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n", + " Apple product with a Electronics Electronics {'Footwear/Clothingโ€ฆ True \n", + " sleek design. -13.669698, \n", + " 'Electronics': \n", + " -4.4849444000336985โ€ฆ \n", + " 'Food/Beverages': \n", + " -14.937825, \n", + " 'Furniture/Home \n", + " Decor': -13.595754, \n", + " 'Beauty/Personal \n", + " Care': -13.327497} \n", + " \u001b[2m \u001b[0m\u001b[2mLaptop stand for the\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m{'Footwear/Clothingโ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mTrue \u001b[0m\u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m\u001b[2mkitchen. \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-9.9471035, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Electronics': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-4.787397, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Food/Beverages': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-12.115164, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Furniture/Home \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mDecor': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-0.0084281690000000โ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Beauty/Personal \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mCare': -12.145201} \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " Chocolate leather Footwear/Clothing Footwear/Clothing {'Footwear/Clothingโ€ฆ True \n", + " boots. -0.0003247375000000โ€ฆ \n", + " 'Electronics': \n", + " -17.322811, \n", + " 'Food/Beverages': \n", + " -8.062444, \n", + " 'Furniture/Home \n", + " Decor': -12.040547, \n", + " 'Beauty/Personal \n", + " Care': -12.584134} \n", + " \u001b[2m \u001b[0m\u001b[2mWooden cream for \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mFurniture/Home Decor\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m{'Footwear/Clothingโ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mTrue \u001b[0m\u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m\u001b[2msurfaces. \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-15.480099, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Electronics': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-17.015057, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Food/Beverages': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-13.499149, \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Furniture/Home \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mDecor': \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m-0.0001718358800000โ€ฆ\u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2m'Beauty/Personal \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m \u001b[2m \u001b[0m\u001b[2mCare': -8.679317} \u001b[0m\u001b[2m \u001b[0m \u001b[2m \u001b[0m \n", + " Natural finish for Beauty/Personal Care Beauty/Personal Care {'Footwear/Clothingโ€ฆ True \n", + " your lips. -11.842119, \n", + " 'Electronics': \n", + " -14.539164, \n", + " 'Food/Beverages': \n", + " -13.285265, \n", + " 'Furniture/Home \n", + " Decor': -14.923815, \n", + " 'Beauty/Personal \n", + " Care': \n", + " -9.72990600003512e-โ€ฆ \n", + " \n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Analyze evaluation experience ...\n",
+       "
\n" + ], + "text/plain": [ + "Analyze evaluation experience \u001b[33m...\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Number of errors: 0\n",
+       "
\n" + ], + "text/plain": [ + "Number of errors: \u001b[1;36m0\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy = 100.00%\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;31mAccuracy = \u001b[0m\u001b[1;36m100.00\u001b[0m\u001b[1;31m%\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy threshold reached (1.0 >= 0.9)\n",
+       "
\n" + ], + "text/plain": [ + "Accuracy threshold reached \u001b[1m(\u001b[0m\u001b[1;36m1.0\u001b[0m >= \u001b[1;36m0.9\u001b[0m\u001b[1m)\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Train is done!\n",
+       "
\n" + ], + "text/plain": [ + "Train is done!\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "ShortTermMemory(dataset=DataFrameDataset(df= text category\n", + "0 Apple product with a sleek design. Electronics\n", + "1 Laptop stand for the kitchen. Furniture/Home Decor\n", + "2 Chocolate leather boots. Footwear/Clothing\n", + "3 Wooden cream for surfaces. Furniture/Home Decor\n", + "4 Natural finish for your lips. Beauty/Personal Care), predictions= text category \\\n", + "0 Apple product with a sleek design. Electronics \n", + "1 Laptop stand for the kitchen. Furniture/Home Decor \n", + "2 Chocolate leather boots. Footwear/Clothing \n", + "3 Wooden cream for surfaces. Furniture/Home Decor \n", + "4 Natural finish for your lips. Beauty/Personal Care \n", + "\n", + " product_category_classification \\\n", + "0 Electronics \n", + "1 Furniture/Home Decor \n", + "2 Footwear/Clothing \n", + "3 Furniture/Home Decor \n", + "4 Beauty/Personal Care \n", + "\n", + " score \n", + "0 {'Footwear/Clothing': -13.669698, 'Electronics... \n", + "1 {'Footwear/Clothing': -9.9471035, 'Electronics... \n", + "2 {'Footwear/Clothing': -0.0003247375000000436, ... \n", + "3 {'Footwear/Clothing': -15.480099, 'Electronics... \n", + "4 {'Footwear/Clothing': -11.842119, 'Electronics... , evaluations= text category \\\n", + "0 Apple product with a sleek design. Electronics \n", + "1 Laptop stand for the kitchen. Furniture/Home Decor \n", + "2 Chocolate leather boots. Footwear/Clothing \n", + "3 Wooden cream for surfaces. Furniture/Home Decor \n", + "4 Natural finish for your lips. Beauty/Personal Care \n", + "\n", + " product_category_classification \\\n", + "0 Electronics \n", + "1 Furniture/Home Decor \n", + "2 Footwear/Clothing \n", + "3 Furniture/Home Decor \n", + "4 Beauty/Personal Care \n", + "\n", + " score \\\n", + "0 {'Footwear/Clothing': -13.669698, 'Electronics... \n", + "1 {'Footwear/Clothing': -9.9471035, 'Electronics... \n", + "2 {'Footwear/Clothing': -0.0003247375000000436, ... \n", + "3 {'Footwear/Clothing': -15.480099, 'Electronics... \n", + "4 {'Footwear/Clothing': -11.842119, 'Electronics... \n", + "\n", + " category__x__product_category_classification \n", + "0 True \n", + "1 True \n", + "2 True \n", + "3 True \n", + "4 True , ground_truth_column_name='category', match_column_name='category__x__product_category_classification', errors=Empty DataFrame\n", + "Columns: [text, category, product_category_classification, score, category__x__product_category_classification]\n", + "Index: [], accuracy=1.0, initial_instructions='Label the input text with the following labels: {{labels}}', updated_instructions=\"Categorize the input text into one of the following labels: ['Footwear/Clothing', 'Electronics', 'Food/Beverages', 'Furniture/Home Decor', 'Beauty/Personal Care']. Choose the label that best represents the main category of the input text.\\n\\nExamples:\\n\\nInput: Laptop stand for the kitchen.\\nOutput: Furniture/Home Decor\\n\\nInput: Running shoes for men.\\nOutput: Footwear/Clothing\\n\\nInput: Organic shampoo for dry hair.\\nOutput: Beauty/Personal Care\")" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.environments import BasicEnvironment\n", + "from adala.skills.labeling.classification import ClassificationSkill\n", + "\n", + "agent = Agent(\n", + " skills=ClassificationSkill(\n", + " name='product_category_classification',\n", + " input_data_field='text',\n", + " labels=[\n", + " \"Footwear/Clothing\",\n", + " \"Electronics\",\n", + " \"Food/Beverages\",\n", + " \"Furniture/Home Decor\",\n", + " \"Beauty/Personal Care\"\n", + " ],\n", + " ),\n", + " environment=BasicEnvironment(\n", + " ground_truth_dataset=df,\n", + " ground_truth_column='category'\n", + " )\n", + ")\n", + "\n", + "agent.learn()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4a876f3d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Total Agent Skills: 1\n",
+       "\n",
+       "product_category_classification\n",
+       "Categorize the input text into one of the following labels: ['Footwear/Clothing', 'Electronics', 'Food/Beverages', \n",
+       "'Furniture/Home Decor', 'Beauty/Personal Care']. Choose the label that best represents the main category of the \n",
+       "input text.\n",
+       "\n",
+       "Examples:\n",
+       "\n",
+       "Input: Laptop stand for the kitchen.\n",
+       "Output: Furniture/Home Decor\n",
+       "\n",
+       "Input: Running shoes for men.\n",
+       "Output: Footwear/Clothing\n",
+       "\n",
+       "Input: Organic shampoo for dry hair.\n",
+       "Output: Beauty/Personal Care\n",
+       "\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1;34mTotal Agent Skills: \u001b[0m\u001b[1;34m1\u001b[0m\n", + "\n", + "\u001b[1;4;32mproduct_category_classification\u001b[0m\n", + "\u001b[32mCategorize the input text into one of the following labels: \u001b[0m\u001b[1;32m[\u001b[0m\u001b[32m'Footwear/Clothing'\u001b[0m\u001b[32m, \u001b[0m\u001b[32m'Electronics'\u001b[0m\u001b[32m, \u001b[0m\u001b[32m'Food/Beverages'\u001b[0m\u001b[32m, \u001b[0m\n", + "\u001b[32m'Furniture/Home Decor'\u001b[0m\u001b[32m, \u001b[0m\u001b[32m'Beauty/Personal Care'\u001b[0m\u001b[1;32m]\u001b[0m\u001b[32m. Choose the label that best represents the main category of the \u001b[0m\n", + "\u001b[32minput text.\u001b[0m\n", + "\n", + "\u001b[32mExamples:\u001b[0m\n", + "\n", + "\u001b[32mInput: Laptop stand for the kitchen.\u001b[0m\n", + "\u001b[32mOutput: Furniture/Home Decor\u001b[0m\n", + "\n", + "\u001b[32mInput: Running shoes for men.\u001b[0m\n", + "\u001b[32mOutput: Footwear/Clothing\u001b[0m\n", + "\n", + "\u001b[32mInput: Organic shampoo for dry hair.\u001b[0m\n", + "\u001b[32mOutput: Beauty/Personal Care\u001b[0m\n", + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from rich import print\n", + "\n", + "print(agent.skills)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "ee97ee22", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:02<00:00, 2.37it/s]\n" + ] + } + ], + "source": [ + "test_df = pd.DataFrame([\n", + " \"Stainless steel apple peeler.\", # Potential categories: Electronics or Food/Beverages\n", + " \"Silk finish touch screen.\", # Potential categories: Electronics or Beauty/Personal Care\n", + " \"Chocolate coated boots.\", # Potential categories: Footwear/Clothing or Food/Beverages\n", + " \"Natural wood fragrance.\", # Potential categories: Beauty/Personal Care or Furniture/Home Decor\n", + " \"Leather grain snack bar.\" # Potential categories: Footwear/Clothing or Food/Beverages\n", + "], columns=['text'])\n", + "\n", + "run = agent.apply_skills(test_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "03cce2a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textproduct_category_classificationscore
0Stainless steel apple peeler.Food/Beverages{'Footwear/Clothing': -5.903179, 'Electronics'...
1Silk finish touch screen.Electronics{'Footwear/Clothing': -11.517515, 'Electronics...
2Chocolate coated boots.Footwear/Clothing{'Footwear/Clothing': -0.074807025, 'Electroni...
3Natural wood fragrance.Furniture/Home Decor{'Footwear/Clothing': -15.117043, 'Electronics...
4Leather grain snack bar.Food/Beverages{'Footwear/Clothing': -9.763915, 'Electronics'...
\n", + "
" + ], + "text/plain": [ + " text product_category_classification \\\n", + "0 Stainless steel apple peeler. Food/Beverages \n", + "1 Silk finish touch screen. Electronics \n", + "2 Chocolate coated boots. Footwear/Clothing \n", + "3 Natural wood fragrance. Furniture/Home Decor \n", + "4 Leather grain snack bar. Food/Beverages \n", + "\n", + " score \n", + "0 {'Footwear/Clothing': -5.903179, 'Electronics'... \n", + "1 {'Footwear/Clothing': -11.517515, 'Electronics... \n", + "2 {'Footwear/Clothing': -0.074807025, 'Electroni... \n", + "3 {'Footwear/Clothing': -15.117043, 'Electronics... \n", + "4 {'Footwear/Clothing': -9.763915, 'Electronics'... " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run.predictions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/classification_skill_with_CoT.ipynb b/examples/classification_skill_with_CoT.ipynb new file mode 100644 index 00000000..d6e2a613 --- /dev/null +++ b/examples/classification_skill_with_CoT.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "94ad15ac", + "metadata": {}, + "source": [ + "# Classification skill with Chain-of-Thoughts" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a2f6d99b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textcategory
0Apple product with a sleek design.Electronics
1Laptop stand for the kitchen.Furniture/Home Decor
2Chocolate leather boots.Footwear/Clothing
3Wooden cream for surfaces.Furniture/Home Decor
4Natural finish for your lips.Beauty/Personal Care
\n", + "
" + ], + "text/plain": [ + " text category\n", + "0 Apple product with a sleek design. Electronics\n", + "1 Laptop stand for the kitchen. Furniture/Home Decor\n", + "2 Chocolate leather boots. Footwear/Clothing\n", + "3 Wooden cream for surfaces. Furniture/Home Decor\n", + "4 Natural finish for your lips. Beauty/Personal Care" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame([\n", + " {\"text\": \"Apple product with a sleek design.\", \"category\": \"Electronics\"},\n", + " {\"text\": \"Laptop stand for the kitchen.\", \"category\": \"Furniture/Home Decor\"},\n", + " {\"text\": \"Chocolate leather boots.\", \"category\": \"Footwear/Clothing\"},\n", + " {\"text\": \"Wooden cream for surfaces.\", \"category\": \"Furniture/Home Decor\"},\n", + " {\"text\": \"Natural finish for your lips.\", \"category\": \"Beauty/Personal Care\"}\n", + "])\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6ee2cebf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:00<00:00, 7.41it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textcategoryrationaleproduct_category_classificationscore
0Apple product with a sleek design.Electronics\\nElectronics - The input mentions an \"Apple p...Electronics{'Footwear/Clothing': -10.126126, 'Electronics...
1Laptop stand for the kitchen.Furniture/Home Decor\\nElectronics - The input mentions a specific ...Electronics{'Footwear/Clothing': -11.511877, 'Electronics...
2Chocolate leather boots.Footwear/Clothing\\nFootwear/Clothing - The input text mentions ...Footwear/Clothing{'Footwear/Clothing': -0.05364191500000001, 'E...
3Wooden cream for surfaces.Furniture/Home Decor\\nLabel: Furniture/Home Decor\\nRationale: The ...Furniture/Home Decor{'Footwear/Clothing': -12.323324, 'Electronics...
4Natural finish for your lips.Beauty/Personal Care\\nLabel: Beauty/Personal Care\\n\\nRationale: Th...Beauty/Personal Care{'Footwear/Clothing': -8.5240965, 'Electronics...
\n", + "
" + ], + "text/plain": [ + " text category \\\n", + "0 Apple product with a sleek design. Electronics \n", + "1 Laptop stand for the kitchen. Furniture/Home Decor \n", + "2 Chocolate leather boots. Footwear/Clothing \n", + "3 Wooden cream for surfaces. Furniture/Home Decor \n", + "4 Natural finish for your lips. Beauty/Personal Care \n", + "\n", + " rationale \\\n", + "0 \\nElectronics - The input mentions an \"Apple p... \n", + "1 \\nElectronics - The input mentions a specific ... \n", + "2 \\nFootwear/Clothing - The input text mentions ... \n", + "3 \\nLabel: Furniture/Home Decor\\nRationale: The ... \n", + "4 \\nLabel: Beauty/Personal Care\\n\\nRationale: Th... \n", + "\n", + " product_category_classification \\\n", + "0 Electronics \n", + "1 Electronics \n", + "2 Footwear/Clothing \n", + "3 Furniture/Home Decor \n", + "4 Beauty/Personal Care \n", + "\n", + " score \n", + "0 {'Footwear/Clothing': -10.126126, 'Electronics... \n", + "1 {'Footwear/Clothing': -11.511877, 'Electronics... \n", + "2 {'Footwear/Clothing': -0.05364191500000001, 'E... \n", + "3 {'Footwear/Clothing': -12.323324, 'Electronics... \n", + "4 {'Footwear/Clothing': -8.5240965, 'Electronics... " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.skills.labeling.classification import ClassificationSkillWithCoT\n", + "\n", + "agent = Agent(\n", + " skills=ClassificationSkillWithCoT(\n", + " name='product_category_classification',\n", + " input_data_field='text',\n", + " labels=[\n", + " \"Footwear/Clothing\",\n", + " \"Electronics\",\n", + " \"Food/Beverages\",\n", + " \"Furniture/Home Decor\",\n", + " \"Beauty/Personal Care\"\n", + " ],\n", + " )\n", + ")\n", + "\n", + "run = agent.apply_skills(df)\n", + "run.predictions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/question_answering_skill.ipynb b/examples/question_answering_skill.ipynb new file mode 100644 index 00000000..dd517618 --- /dev/null +++ b/examples/question_answering_skill.ipynb @@ -0,0 +1,244 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "94ad15ac", + "metadata": {}, + "source": [ + "# Question-answering skill" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a2f6d99b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
questionexpected_answer
0In quantum mechanics, what principle asserts t...Heisenberg Uncertainty Principle
1Which famous poet wrote 'The Love Song of J. A...T.S. Eliot
2What mathematical theorem states that in any r...Pythagorean Theorem
3Which philosophical paradox involves a ship wh...Ship of Theseus
4In the world of programming, what is the desig...Open/Closed Principle
\n", + "
" + ], + "text/plain": [ + " question \\\n", + "0 In quantum mechanics, what principle asserts t... \n", + "1 Which famous poet wrote 'The Love Song of J. A... \n", + "2 What mathematical theorem states that in any r... \n", + "3 Which philosophical paradox involves a ship wh... \n", + "4 In the world of programming, what is the desig... \n", + "\n", + " expected_answer \n", + "0 Heisenberg Uncertainty Principle \n", + "1 T.S. Eliot \n", + "2 Pythagorean Theorem \n", + "3 Ship of Theseus \n", + "4 Open/Closed Principle " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame([\n", + " {\"question\": \"In quantum mechanics, what principle asserts that it's impossible to simultaneously know the exact position and momentum of a particle?\", \"expected_answer\": \"Heisenberg Uncertainty Principle\"},\n", + " {\"question\": \"Which famous poet wrote 'The Love Song of J. Alfred Prufrock'?\", \"expected_answer\": \"T.S. Eliot\"},\n", + " {\"question\": \"What mathematical theorem states that in any right-angled triangle, the area of the square whose side is the hypotenuse is equal to the sum of the areas of the squares whose sides are the two legs?\", \"expected_answer\": \"Pythagorean Theorem\"},\n", + " {\"question\": \"Which philosophical paradox involves a ship where all of its wooden parts are replaced with metal parts?\", \"expected_answer\": \"Ship of Theseus\"},\n", + " {\"question\": \"In the world of programming, what is the design principle that suggests a system should be open for extension but closed for modification?\", \"expected_answer\": \"Open/Closed Principle\"}\n", + "])\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6ee2cebf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:02<00:00, 1.91it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
questionexpected_answerqa_skill
0In quantum mechanics, what principle asserts t...Heisenberg Uncertainty PrincipleThe Heisenberg uncertainty principle.
1Which famous poet wrote 'The Love Song of J. A...T.S. EliotT.S. Eliot
2What mathematical theorem states that in any r...Pythagorean TheoremThe Pythagorean theorem.
3Which philosophical paradox involves a ship wh...Ship of TheseusThe Ship of Theseus paradox.
4In the world of programming, what is the desig...Open/Closed PrincipleThe design principle is called the Open-Closed...
\n", + "
" + ], + "text/plain": [ + " question \\\n", + "0 In quantum mechanics, what principle asserts t... \n", + "1 Which famous poet wrote 'The Love Song of J. A... \n", + "2 What mathematical theorem states that in any r... \n", + "3 Which philosophical paradox involves a ship wh... \n", + "4 In the world of programming, what is the desig... \n", + "\n", + " expected_answer \\\n", + "0 Heisenberg Uncertainty Principle \n", + "1 T.S. Eliot \n", + "2 Pythagorean Theorem \n", + "3 Ship of Theseus \n", + "4 Open/Closed Principle \n", + "\n", + " qa_skill \n", + "0 The Heisenberg uncertainty principle. \n", + "1 T.S. Eliot \n", + "2 The Pythagorean theorem. \n", + "3 The Ship of Theseus paradox. \n", + "4 The design principle is called the Open-Closed... " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.skills.generation.qa import QuestionAnsweringSkill\n", + "\n", + "agent = Agent(\n", + " skills=QuestionAnsweringSkill(\n", + " name='qa_skill',\n", + " input_data_field='question'\n", + " )\n", + ")\n", + "\n", + "run = agent.apply_skills(df)\n", + "run.predictions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/quickstart.ipynb b/examples/quickstart.ipynb new file mode 100644 index 00000000..3eab4978 --- /dev/null +++ b/examples/quickstart.ipynb @@ -0,0 +1,1264 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a6c119c3", + "metadata": {}, + "source": [ + "# ADALA Quickstart\n", + "\n", + "In this notebook, we are going to run through some of the common tasks for creating data labeling agents with ADALA. In this example, we're going to create a data labeling agent for a text classification task - labeling our text samples as either \"Subjective or \"Objective\" statements. \n", + "\n", + "This agent will be LLM-based, so we will use [OpenAI's API](https://platform.openai.com/). You will to generate an API key and set it as an environment variable as follows: \n", + "\n", + "```\n", + "export OPENAI_API_KEY=your_openai_api_key\n", + "```\n", + "\n", + "Now, let's begin. " + ] + }, + { + "cell_type": "markdown", + "id": "55c19afc", + "metadata": {}, + "source": [ + "## Dataset Creation\n", + "First, let's use a dataset of product reviews stored in pandas dataframe. This will help us manage our data as we add more attributes, like predictions and labels for subjectivity and objectivity over time. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "5d5b37a3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textground_truth
0The mic is great.Subjective
1Will order from them again!Subjective
2Not loud enough and doesn't turn on like it sh...Objective
3The phone doesn't seem to accept anything exce...Objective
4All three broke within two months of use.Objective
\n", + "
" + ], + "text/plain": [ + " text ground_truth\n", + "0 The mic is great. Subjective\n", + "1 Will order from them again! Subjective\n", + "2 Not loud enough and doesn't turn on like it sh... Objective\n", + "3 The phone doesn't seem to accept anything exce... Objective\n", + "4 All three broke within two months of use. Objective" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.DataFrame([\n", + " [\"The mic is great.\", \"Subjective\"],\n", + " [\"Will order from them again!\", \"Subjective\"],\n", + " [\"Not loud enough and doesn't turn on like it should.\", \"Objective\"],\n", + " [\"The phone doesn't seem to accept anything except CBR mp3s\", \"Objective\"],\n", + " [\"All three broke within two months of use.\", \"Objective\"]\n", + "], columns=[\"text\", \"ground_truth\"])\n", + "\n", + "df" + ] + }, + { + "cell_type": "markdown", + "id": "9ce6651b", + "metadata": {}, + "source": [ + "We instantiate Dataset that uses this pandas dataframe as a data source. Dataset object takes care of input data schema and data streaming:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "93a31f60", + "metadata": {}, + "outputs": [], + "source": [ + "from adala.datasets import DataFrameDataset\n", + "\n", + "dataset = DataFrameDataset(df=df)" + ] + }, + { + "cell_type": "markdown", + "id": "0dc201b3", + "metadata": {}, + "source": [ + "## Create Agent\n", + "\n", + "To create Agent, we need to to define 2 things:\n", + "\n", + "**Skills** - Agent's abilities are defined as _Skills_. Each agent can possess many different skills. In our case, this agent only has one labeling skill, to produce a classification of Subjective or Objective for a given piece of text. To define this skill, we will leverage an LLM, passing it instructions and the set of labeles we expect to receive back. \n", + "\n", + "**Environment** - that is where the Agent receives ground truth signal to improve its skill. Since we already created ground truth dataset, we can simply refer to the column from the dataframe. In the real world scenario, you may consider using a different environment where ground truth signal can be obtained asynchoronously by gathering real human feedback during agent's learning phase." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a1310fce", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
Agent Instance\n",
+       "\n",
+       "Environment: BasicEnvironment\n",
+       "Skills: subjectivity_detection\n",
+       "Runtimes: openai, openai-gpt3, openai-gpt4\n",
+       "Default Runtime: openai\n",
+       "Default Teacher Runtime: openai-gpt4\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;34mAgent Instance\u001B[0m\n", + "\n", + "Environment: BasicEnvironment\n", + "Skills: subjectivity_detection\n", + "Runtimes: openai, openai-gpt3, openai-gpt4\n", + "Default Runtime: openai\n", + "Default Teacher Runtime: openai-gpt4\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.environments import BasicEnvironment\n", + "from adala.skills import ClassificationSkill\n", + "from adala.runtimes import OpenAIRuntime\n", + "from rich import print\n", + "\n", + "\n", + "agent = Agent(\n", + " # define the agent's labeling skill that should classify text onto 2 categories\n", + " skills=ClassificationSkill(\n", + " name='subjectivity_detection',\n", + " description='Understanding subjective and objective statements from text.',\n", + " instructions='Classify a product review as either expressing \"Subjective\" or \"Objective\" statements.',\n", + " labels=['Subjective', 'Objective'],\n", + " input_data_field='text'\n", + " ),\n", + " \n", + " # basic environment extracts ground truth signal from the input records\n", + " environment=BasicEnvironment(\n", + " ground_truth_dataset=dataset,\n", + " ground_truth_column='ground_truth'\n", + " ),\n", + " \n", + " runtimes = {\n", + " # You can specify your OPENAI API KEY here via `OpenAIRuntime(..., api_key='your-api-key')`\n", + " 'openai': OpenAIRuntime(model='gpt-3.5-turbo-instruct'),\n", + " 'openai-gpt3': OpenAIRuntime(model='gpt-3.5-turbo'),\n", + " 'openai-gpt4': OpenAIRuntime(model='gpt-4'),\n", + " },\n", + " default_runtime='openai',\n", + " \n", + " # NOTE! If you don't have an access to gpt4 - replace it with \"openai-gpt3\"\n", + " default_teacher_runtime='openai-gpt4'\n", + ")\n", + "\n", + "print(agent)" + ] + }, + { + "cell_type": "markdown", + "id": "8340dde8", + "metadata": {}, + "source": [ + "## Learning Agent\n", + "\n", + "We will now let Agent learn from the ground truth. After every action, Agent returns its _Experience_, where it stores various observations like predicted data, errors, accuracy, etc." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "666c8d0f", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:00<00:00, 39.18it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n",
+       "\n",
+       "=> Iteration #0: Comparing to ground truth, analyzing and improving ...\n",
+       "
\n" + ], + "text/plain": [ + "\n", + "\n", + "=> Iteration #\u001B[1;36m0\u001B[0m: Comparing to ground truth, analyzing and improving \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Comparing predictions to ground truth data ...\n",
+       "
\n" + ], + "text/plain": [ + "Comparing predictions to ground truth data \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
                                                                                                                   \n",
+       "  text                     ground_truth   subjectivity_detection   score                    ground_truth__x__subโ€ฆ  \n",
+       " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n",
+       "  The mic is great.        Subjective     Subjective               {'Subjective':           True                   \n",
+       "                                                                   -0.02697588099999997,                           \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -3.6262724}                                     \n",
+       "  Will order from them     Subjective     Subjective               {'Subjective':           True                   \n",
+       "  again!                                                           -0.11282212000000001,                           \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -2.2378219999999995}                            \n",
+       "  Not loud enough and      Objective      Subjective               {'Subjective':           False                  \n",
+       "  doesn't turn on like                                             -0.014163457000000034,                          \n",
+       "  it should.                                                       'Objective':                                    \n",
+       "                                                                   -4.2641635}                                     \n",
+       "  The phone doesn't seem   Objective      Objective                {'Subjective':           True                   \n",
+       "  to accept anything                                               -2.0720863,                                     \n",
+       "  except CBR mp3s                                                  'Objective':                                    \n",
+       "                                                                   -0.13458653999999995}                           \n",
+       "  All three broke within   Objective      Objective                {'Subjective':           True                   \n",
+       "  two months of use.                                               -2.1821797,                                     \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -0.11967964500000007}                           \n",
+       "                                                                                                                   \n",
+       "
\n" + ], + "text/plain": [ + " \n", + " \u001B[1;35m \u001B[0m\u001B[1;35mtext \u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mground_truth\u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35msubjectivity_detection\u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mscore \u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mground_truth__x__subโ€ฆ\u001B[0m\u001B[1;35m \u001B[0m \n", + " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n", + " The mic is great. Subjective Subjective {'Subjective': True \n", + " -0.02697588099999997, \n", + " 'Objective': \n", + " -3.6262724} \n", + " \u001B[2m \u001B[0m\u001B[2mWill order from them \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mSubjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mSubjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m{'Subjective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mTrue \u001B[0m\u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2magain! \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.11282212000000001, \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m'Objective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-2.2378219999999995} \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " Not loud enough and Objective Subjective {'Subjective': False \n", + " doesn't turn on like -0.014163457000000034, \n", + " it should. 'Objective': \n", + " -4.2641635} \n", + " \u001B[2m \u001B[0m\u001B[2mThe phone doesn't seem\u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m{'Subjective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mTrue \u001B[0m\u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2mto accept anything \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-2.0720863, \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2mexcept CBR mp3s \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m'Objective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.13458653999999995} \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " All three broke within Objective Objective {'Subjective': True \n", + " two months of use. -2.1821797, \n", + " 'Objective': \n", + " -0.11967964500000007} \n", + " \n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Analyze evaluation experience ...\n",
+       "
\n" + ], + "text/plain": [ + "Analyze evaluation experience \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 1/1 [00:00<00:00, 153.47it/s]\n", + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 1/1 [00:00<00:00, 31.21it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
Number of errors: 1\n",
+       "
\n" + ], + "text/plain": [ + "Number of errors: \u001B[1;36m1\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy = 80.00%\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;31mAccuracy = \u001B[0m\u001B[1;36m80.00\u001B[0m\u001B[1;31m%\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Improve \"subjectivity_detection\" skill based on analysis ...\n",
+       "
\n" + ], + "text/plain": [ + "Improve \u001B[32m\"subjectivity_detection\"\u001B[0m skill based on analysis \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Updated instructions for skill \"subjectivity_detection\":\n",
+       "\n",
+       "
\n" + ], + "text/plain": [ + "Updated instructions for skill \u001B[32m\"subjectivity_detection\"\u001B[0m:\n", + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Determine whether the given product review contains \"Subjective\" (based on personal feelings, tastes, or opinions) \n",
+       "or \"Objective\" (based on facts) statements.\n",
+       "\n",
+       "Examples:\n",
+       "\n",
+       "Input: Not loud enough and doesn't turn on like it should.\n",
+       "Output: Objective\n",
+       "\n",
+       "Input: I personally think the sound quality is not up to the mark.\n",
+       "Output: Subjective\n",
+       "\n",
+       "Input: The phone's battery lasts for 10 hours.\n",
+       "Output: Objective\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;32mDetermine whether the given product review contains \u001B[0m\u001B[32m\"Subjective\"\u001B[0m\u001B[1;32m \u001B[0m\u001B[1;32m(\u001B[0m\u001B[1;32mbased on personal feelings, tastes, or opinions\u001B[0m\u001B[1;32m)\u001B[0m\u001B[1;32m \u001B[0m\n", + "\u001B[1;32mor \u001B[0m\u001B[32m\"Objective\"\u001B[0m\u001B[1;32m \u001B[0m\u001B[1;32m(\u001B[0m\u001B[1;32mbased on facts\u001B[0m\u001B[1;32m)\u001B[0m\u001B[1;32m statements.\u001B[0m\n", + "\n", + "\u001B[1;32mExamples:\u001B[0m\n", + "\n", + "\u001B[1;32mInput: Not loud enough and doesn't turn on like it should.\u001B[0m\n", + "\u001B[1;32mOutput: Objective\u001B[0m\n", + "\n", + "\u001B[1;32mInput: I personally think the sound quality is not up to the mark.\u001B[0m\n", + "\u001B[1;32mOutput: Subjective\u001B[0m\n", + "\n", + "\u001B[1;32mInput: The phone's battery lasts for \u001B[0m\u001B[1;36m10\u001B[0m\u001B[1;32m hours.\u001B[0m\n", + "\u001B[1;32mOutput: Objective\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Re-apply subjectivity_detection skill to dataset ...\n",
+       "
\n" + ], + "text/plain": [ + "Re-apply subjectivity_detection skill to dataset \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:00<00:00, 48.32it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n",
+       "\n",
+       "=> Iteration #1: Comparing to ground truth, analyzing and improving ...\n",
+       "
\n" + ], + "text/plain": [ + "\n", + "\n", + "=> Iteration #\u001B[1;36m1\u001B[0m: Comparing to ground truth, analyzing and improving \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Comparing predictions to ground truth data ...\n",
+       "
\n" + ], + "text/plain": [ + "Comparing predictions to ground truth data \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
                                                                                                                   \n",
+       "  text                     ground_truth   subjectivity_detection   score                    ground_truth__x__subโ€ฆ  \n",
+       " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n",
+       "  The mic is great.        Subjective     Objective                {'Subjective':           False                  \n",
+       "                                                                   -2.2253392,                                     \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -0.11432376000000005}                           \n",
+       "  Will order from them     Subjective     Objective                {'Subjective':           False                  \n",
+       "  again!                                                           -0.8573844400000001,                            \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -0.5521171}                                     \n",
+       "  Not loud enough and      Objective      Objective                {'Subjective':           True                   \n",
+       "  doesn't turn on like                                             -4.0895286,                                     \n",
+       "  it should.                                                       'Objective':                                    \n",
+       "                                                                   -0.01688896000000003}                           \n",
+       "  The phone doesn't seem   Objective      Objective                {'Subjective':           True                   \n",
+       "  to accept anything                                               -2.8614092,                                     \n",
+       "  except CBR mp3s                                                  'Objective':                                    \n",
+       "                                                                   -0.058888500000000066}                          \n",
+       "  All three broke within   Objective      Objective                {'Subjective':           True                   \n",
+       "  two months of use.                                               -4.7739024,                                     \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -0.008483256000000052}                          \n",
+       "                                                                                                                   \n",
+       "
\n" + ], + "text/plain": [ + " \n", + " \u001B[1;35m \u001B[0m\u001B[1;35mtext \u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mground_truth\u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35msubjectivity_detection\u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mscore \u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mground_truth__x__subโ€ฆ\u001B[0m\u001B[1;35m \u001B[0m \n", + " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n", + " The mic is great. Subjective Objective {'Subjective': False \n", + " -2.2253392, \n", + " 'Objective': \n", + " -0.11432376000000005} \n", + " \u001B[2m \u001B[0m\u001B[2mWill order from them \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mSubjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m{'Subjective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mFalse \u001B[0m\u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2magain! \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.8573844400000001, \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m'Objective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.5521171} \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " Not loud enough and Objective Objective {'Subjective': True \n", + " doesn't turn on like -4.0895286, \n", + " it should. 'Objective': \n", + " -0.01688896000000003} \n", + " \u001B[2m \u001B[0m\u001B[2mThe phone doesn't seem\u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m{'Subjective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mTrue \u001B[0m\u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2mto accept anything \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-2.8614092, \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2mexcept CBR mp3s \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m'Objective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.058888500000000066}\u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " All three broke within Objective Objective {'Subjective': True \n", + " two months of use. -4.7739024, \n", + " 'Objective': \n", + " -0.008483256000000052} \n", + " \n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Analyze evaluation experience ...\n",
+       "
\n" + ], + "text/plain": [ + "Analyze evaluation experience \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 2/2 [00:00<00:00, 229.64it/s]\n", + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 2/2 [00:00<00:00, 24.71it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
Number of errors: 2\n",
+       "
\n" + ], + "text/plain": [ + "Number of errors: \u001B[1;36m2\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy = 60.00%\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;31mAccuracy = \u001B[0m\u001B[1;36m60.00\u001B[0m\u001B[1;31m%\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Improve \"subjectivity_detection\" skill based on analysis ...\n",
+       "
\n" + ], + "text/plain": [ + "Improve \u001B[32m\"subjectivity_detection\"\u001B[0m skill based on analysis \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Updated instructions for skill \"subjectivity_detection\":\n",
+       "\n",
+       "
\n" + ], + "text/plain": [ + "Updated instructions for skill \u001B[32m\"subjectivity_detection\"\u001B[0m:\n", + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Identify if the provided product review is \"Subjective\" (expressing personal feelings, tastes, or opinions) or \n",
+       "\"Objective\" (based on factual information). Consider a statement as subjective if it reflects personal judgment or \n",
+       "preference, and as objective if it states verifiable facts or features.\n",
+       "\n",
+       "Examples:\n",
+       "\n",
+       "Input: Not loud enough and doesn't turn on like it should.\n",
+       "Output: Objective\n",
+       "\n",
+       "Input: I personally think the sound quality is not up to the mark.\n",
+       "Output: Subjective\n",
+       "\n",
+       "Input: The phone's battery lasts for 10 hours.\n",
+       "Output: Objective\n",
+       "\n",
+       "Input: The mic is great.\n",
+       "Output: Subjective\n",
+       "\n",
+       "Input: Will order from them again!\n",
+       "Output: Subjective\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;32mIdentify if the provided product review is \u001B[0m\u001B[32m\"Subjective\"\u001B[0m\u001B[1;32m \u001B[0m\u001B[1;32m(\u001B[0m\u001B[1;32mexpressing personal feelings, tastes, or opinions\u001B[0m\u001B[1;32m)\u001B[0m\u001B[1;32m or \u001B[0m\n", + "\u001B[32m\"Objective\"\u001B[0m\u001B[1;32m \u001B[0m\u001B[1;32m(\u001B[0m\u001B[1;32mbased on factual information\u001B[0m\u001B[1;32m)\u001B[0m\u001B[1;32m. Consider a statement as subjective if it reflects personal judgment or \u001B[0m\n", + "\u001B[1;32mpreference, and as objective if it states verifiable facts or features.\u001B[0m\n", + "\n", + "\u001B[1;32mExamples:\u001B[0m\n", + "\n", + "\u001B[1;32mInput: Not loud enough and doesn't turn on like it should.\u001B[0m\n", + "\u001B[1;32mOutput: Objective\u001B[0m\n", + "\n", + "\u001B[1;32mInput: I personally think the sound quality is not up to the mark.\u001B[0m\n", + "\u001B[1;32mOutput: Subjective\u001B[0m\n", + "\n", + "\u001B[1;32mInput: The phone's battery lasts for \u001B[0m\u001B[1;36m10\u001B[0m\u001B[1;32m hours.\u001B[0m\n", + "\u001B[1;32mOutput: Objective\u001B[0m\n", + "\n", + "\u001B[1;32mInput: The mic is great.\u001B[0m\n", + "\u001B[1;32mOutput: Subjective\u001B[0m\n", + "\n", + "\u001B[1;32mInput: Will order from them again!\u001B[0m\n", + "\u001B[1;32mOutput: Subjective\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Re-apply subjectivity_detection skill to dataset ...\n",
+       "
\n" + ], + "text/plain": [ + "Re-apply subjectivity_detection skill to dataset \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 5/5 [00:00<00:00, 35.93it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n",
+       "\n",
+       "=> Iteration #2: Comparing to ground truth, analyzing and improving ...\n",
+       "
\n" + ], + "text/plain": [ + "\n", + "\n", + "=> Iteration #\u001B[1;36m2\u001B[0m: Comparing to ground truth, analyzing and improving \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Comparing predictions to ground truth data ...\n",
+       "
\n" + ], + "text/plain": [ + "Comparing predictions to ground truth data \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
                                                                                                                   \n",
+       "  text                     ground_truth   subjectivity_detection   score                    ground_truth__x__subโ€ฆ  \n",
+       " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n",
+       "  The mic is great.        Subjective     Subjective               {'Subjective':           True                   \n",
+       "                                                                   -0.022607480000000055,                          \n",
+       "                                                                   'Objective': -3.80076}                          \n",
+       "  Will order from them     Subjective     Subjective               {'Subjective':           True                   \n",
+       "  again!                                                           -0.05627503599999997,                           \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -2.9055107}                                     \n",
+       "  Not loud enough and      Objective      Objective                {'Subjective':           True                   \n",
+       "  doesn't turn on like                                             -2.897738,                                      \n",
+       "  it should.                                                       'Objective':                                    \n",
+       "                                                                   -0.05672692499999995}                           \n",
+       "  The phone doesn't seem   Objective      Objective                {'Subjective':           True                   \n",
+       "  to accept anything                                               -3.8168292,                                     \n",
+       "  except CBR mp3s                                                  'Objective':                                    \n",
+       "                                                                   -0.022242965000000038}                          \n",
+       "  All three broke within   Objective      Objective                {'Subjective':           True                   \n",
+       "  two months of use.                                               -4.800799,                                      \n",
+       "                                                                   'Objective':                                    \n",
+       "                                                                   -0.008257226000000043}                          \n",
+       "                                                                                                                   \n",
+       "
\n" + ], + "text/plain": [ + " \n", + " \u001B[1;35m \u001B[0m\u001B[1;35mtext \u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mground_truth\u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35msubjectivity_detection\u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mscore \u001B[0m\u001B[1;35m \u001B[0m \u001B[1;35m \u001B[0m\u001B[1;35mground_truth__x__subโ€ฆ\u001B[0m\u001B[1;35m \u001B[0m \n", + " โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ \n", + " The mic is great. Subjective Subjective {'Subjective': True \n", + " -0.022607480000000055, \n", + " 'Objective': -3.80076} \n", + " \u001B[2m \u001B[0m\u001B[2mWill order from them \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mSubjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mSubjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m{'Subjective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mTrue \u001B[0m\u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2magain! \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.05627503599999997, \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m'Objective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-2.9055107} \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " Not loud enough and Objective Objective {'Subjective': True \n", + " doesn't turn on like -2.897738, \n", + " it should. 'Objective': \n", + " -0.05672692499999995} \n", + " \u001B[2m \u001B[0m\u001B[2mThe phone doesn't seem\u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mObjective \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m{'Subjective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2mTrue \u001B[0m\u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2mto accept anything \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-3.8168292, \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m\u001B[2mexcept CBR mp3s \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m'Objective': \u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m \u001B[2m \u001B[0m\u001B[2m-0.022242965000000038}\u001B[0m\u001B[2m \u001B[0m \u001B[2m \u001B[0m \n", + " All three broke within Objective Objective {'Subjective': True \n", + " two months of use. -4.800799, \n", + " 'Objective': \n", + " -0.008257226000000043} \n", + " \n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Analyze evaluation experience ...\n",
+       "
\n" + ], + "text/plain": [ + "Analyze evaluation experience \u001B[33m...\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Number of errors: 0\n",
+       "
\n" + ], + "text/plain": [ + "Number of errors: \u001B[1;36m0\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy = 100.00%\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;31mAccuracy = \u001B[0m\u001B[1;36m100.00\u001B[0m\u001B[1;31m%\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Accuracy threshold reached (1.0 >= 0.95)\n",
+       "
\n" + ], + "text/plain": [ + "Accuracy threshold reached \u001B[1m(\u001B[0m\u001B[1;36m1.0\u001B[0m >= \u001B[1;36m0.95\u001B[0m\u001B[1m)\u001B[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Train is done!\n",
+       "
\n" + ], + "text/plain": [ + "Train is done!\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "learning_experience = agent.learn(learning_iterations=3, accuracy_threshold=0.95)" + ] + }, + { + "cell_type": "markdown", + "id": "ee1573e3", + "metadata": {}, + "source": [ + "Let's see the final instructions:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f5b67bd4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Total Agent Skills: 1\n",
+       "\n",
+       "subjectivity_detection\n",
+       "Identify if the provided product review is \"Subjective\" (expressing personal feelings, tastes, or opinions) or \n",
+       "\"Objective\" (based on factual information). Consider a statement as subjective if it reflects personal judgment or \n",
+       "preference, and as objective if it states verifiable facts or features.\n",
+       "\n",
+       "Examples:\n",
+       "\n",
+       "Input: Not loud enough and doesn't turn on like it should.\n",
+       "Output: Objective\n",
+       "\n",
+       "Input: I personally think the sound quality is not up to the mark.\n",
+       "Output: Subjective\n",
+       "\n",
+       "Input: The phone's battery lasts for 10 hours.\n",
+       "Output: Objective\n",
+       "\n",
+       "Input: The mic is great.\n",
+       "Output: Subjective\n",
+       "\n",
+       "Input: Will order from them again!\n",
+       "Output: Subjective\n",
+       "\n",
+       "
\n" + ], + "text/plain": [ + "\u001B[1;34mTotal Agent Skills: \u001B[0m\u001B[1;34m1\u001B[0m\n", + "\n", + "\u001B[1;4;32msubjectivity_detection\u001B[0m\n", + "\u001B[32mIdentify if the provided product review is \u001B[0m\u001B[32m\"Subjective\"\u001B[0m\u001B[32m \u001B[0m\u001B[1;32m(\u001B[0m\u001B[32mexpressing personal feelings, tastes, or opinions\u001B[0m\u001B[1;32m)\u001B[0m\u001B[32m or \u001B[0m\n", + "\u001B[32m\"Objective\"\u001B[0m\u001B[32m \u001B[0m\u001B[1;32m(\u001B[0m\u001B[32mbased on factual information\u001B[0m\u001B[1;32m)\u001B[0m\u001B[32m. Consider a statement as subjective if it reflects personal judgment or \u001B[0m\n", + "\u001B[32mpreference, and as objective if it states verifiable facts or features.\u001B[0m\n", + "\n", + "\u001B[32mExamples:\u001B[0m\n", + "\n", + "\u001B[32mInput: Not loud enough and doesn't turn on like it should.\u001B[0m\n", + "\u001B[32mOutput: Objective\u001B[0m\n", + "\n", + "\u001B[32mInput: I personally think the sound quality is not up to the mark.\u001B[0m\n", + "\u001B[32mOutput: Subjective\u001B[0m\n", + "\n", + "\u001B[32mInput: The phone's battery lasts for \u001B[0m\u001B[1;32m10\u001B[0m\u001B[32m hours.\u001B[0m\n", + "\u001B[32mOutput: Objective\u001B[0m\n", + "\n", + "\u001B[32mInput: The mic is great.\u001B[0m\n", + "\u001B[32mOutput: Subjective\u001B[0m\n", + "\n", + "\u001B[32mInput: Will order from them again!\u001B[0m\n", + "\u001B[32mOutput: Subjective\u001B[0m\n", + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(agent.skills)" + ] + }, + { + "cell_type": "markdown", + "id": "54ec4568", + "metadata": {}, + "source": [ + "... and predictions created by the skill:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "baa69db8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textground_truthsubjectivity_detectionscore
0The mic is great.SubjectiveSubjective{'Subjective': -0.022607480000000055, 'Objecti...
1Will order from them again!SubjectiveSubjective{'Subjective': -0.05627503599999997, 'Objectiv...
2Not loud enough and doesn't turn on like it sh...ObjectiveObjective{'Subjective': -2.897738, 'Objective': -0.0567...
3The phone doesn't seem to accept anything exce...ObjectiveObjective{'Subjective': -3.8168292, 'Objective': -0.022...
4All three broke within two months of use.ObjectiveObjective{'Subjective': -4.800799, 'Objective': -0.0082...
\n", + "
" + ], + "text/plain": [ + " text ground_truth \\\n", + "0 The mic is great. Subjective \n", + "1 Will order from them again! Subjective \n", + "2 Not loud enough and doesn't turn on like it sh... Objective \n", + "3 The phone doesn't seem to accept anything exce... Objective \n", + "4 All three broke within two months of use. Objective \n", + "\n", + " subjectivity_detection score \n", + "0 Subjective {'Subjective': -0.022607480000000055, 'Objecti... \n", + "1 Subjective {'Subjective': -0.05627503599999997, 'Objectiv... \n", + "2 Objective {'Subjective': -2.897738, 'Objective': -0.0567... \n", + "3 Objective {'Subjective': -3.8168292, 'Objective': -0.022... \n", + "4 Objective {'Subjective': -4.800799, 'Objective': -0.0082... " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "learning_experience.predictions" + ] + }, + { + "cell_type": "markdown", + "id": "b8d49385", + "metadata": {}, + "source": [ + "## Applying learned skills to the real data\n", + "\n", + "Now as we have our Agent with evolved \"subjectivity detection\" skill, we can apply it to the real dataset without ground truth data:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "60a79462", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
text
0Doesn't hold charge.
1Excellent bluetooth headset
2I love this thing!
3VERY DISAPPOINTED.
\n", + "
" + ], + "text/plain": [ + " text\n", + "0 Doesn't hold charge.\n", + "1 Excellent bluetooth headset\n", + "2 I love this thing!\n", + "3 VERY DISAPPOINTED." + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_df = pd.DataFrame([\n", + " \"Doesn't hold charge.\",\n", + " \"Excellent bluetooth headset\",\n", + " \"I love this thing!\",\n", + " \"VERY DISAPPOINTED.\"\n", + "], columns=['text'])\n", + "test_df" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "2f2bf273", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 4/4 [00:00<00:00, 32.32it/s]\n" + ] + } + ], + "source": [ + "result = agent.apply_skills(test_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e6c50ede", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textsubjectivity_detectionscore
0Doesn't hold charge.Objective{'Subjective': -4.9062243, 'Objective': -0.007...
1Excellent bluetooth headsetObjective{'Subjective': -1.450324, 'Objective': -0.2672...
2I love this thing!Subjective{'Subjective': -0.0014673689999999905, 'Object...
3VERY DISAPPOINTED.Subjective{'Subjective': -0.17851222999999997, 'Objectiv...
\n", + "
" + ], + "text/plain": [ + " text subjectivity_detection \\\n", + "0 Doesn't hold charge. Objective \n", + "1 Excellent bluetooth headset Objective \n", + "2 I love this thing! Subjective \n", + "3 VERY DISAPPOINTED. Subjective \n", + "\n", + " score \n", + "0 {'Subjective': -4.9062243, 'Objective': -0.007... \n", + "1 {'Subjective': -1.450324, 'Objective': -0.2672... \n", + "2 {'Subjective': -0.0014673689999999905, 'Object... \n", + "3 {'Subjective': -0.17851222999999997, 'Objectiv... " + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result.predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0922915b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/examples/summarization_skill.ipynb b/examples/summarization_skill.ipynb new file mode 100644 index 00000000..27c47923 --- /dev/null +++ b/examples/summarization_skill.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "94ad15ac", + "metadata": {}, + "source": [ + "# Summarization skill" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a2f6d99b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
text
0Caffeine comes from coffee beans, but it can a...
1Vitamin C is a water-soluble essential vitamin...
2Vitamin D is a fat-soluble nutrient. It is one...
\n", + "
" + ], + "text/plain": [ + " text\n", + "0 Caffeine comes from coffee beans, but it can a...\n", + "1 Vitamin C is a water-soluble essential vitamin...\n", + "2 Vitamin D is a fat-soluble nutrient. It is one..." + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame([\n", + " \"Caffeine comes from coffee beans, but it can also be synthesized in a laboratory. It has the same structure whether itโ€™s in coffee, energy drinks, tea, or pills. Caffeine is a powerful stimulant, and it can be used to improve physical strength and endurance. It is classified as a nootropic because it sensitizes neurons and provides mental stimulation. Habitual caffeine use is also associated with a reduced risk of Alzheimer's disease, cirrhosis, and liver cancer. Caffeineโ€™s main mechanism concerns antagonizing adenosine receptors. Adenosine causes sedation and relaxation when it acts upon its receptors, located in the brain. Caffeine prevents this action and causes alertness and wakefulness. This inhibition of adenosine can influence the dopamine, serotonin, acetylcholine, and adrenaline systems. For practical tips on the optimal use of caffeine, check out our Supplement Guides.\",\n", + " \"Vitamin C is a water-soluble essential vitamin that can be found in fruits and vegetables, especially citrus. Humans are unable to synthesize vitamin C from their bodies, so it must be acquired through dietary intake. Vitamin C is important for immune system function and is a powerful antioxidant. It also acts as a cofactor for collagen synthesis.[2]. People often supplement with vitamin C when they have a cold. According to various studies, vitamin C may be effective in reducing the duration of a cold, but does not seem to reduce the frequency of colds in a population.[3][4] The available literature suggests that a dose ranging from 200 mg to 2,000 mg could be beneficial for reducing cold duration.Often utilized for its antioxidant effects, vitamin C has been studied for its potential role in Alzheimerโ€™s disease and cancer. Lower vitamin C levels are present in people with Alzheimerโ€™s, even with adequate dietary intake.[5] It is thought that oxidative stress plays a major role in the pathogenesis of the disease, so vitamin Cโ€™s antioxidative effects could be beneficial.[6][7] In rodent studies, oral vitamin C was able to reduce oxidative and inflammatory biomarkers.[8] In recent cancer research, vitamin C was found to promote oxidative stress in cancer cells, leading to cytotoxic effects at high doses in mice.[9] While promising, further research and human studies are required to determine efficacy.\",\n", + " \"Vitamin D is a fat-soluble nutrient. It is one of the 24 micronutrients critical for human survival. The sun is the major natural source through eliciting vitamin D production in the skin, but vitamin D is also found naturally in oily fish and eggs and is added to milk and milk alternatives. Supplemental vitamin D is associated with a range of benefits, including improved immune health, bone health, and well-being. Supplementation may also reduce the risk of cancer mortality, diabetes, and multiple sclerosis.The effects of vitamin D likely depend on a personโ€™s circulating levels of 25-hydroxyvitamin D (25(OH)D; a form of vitamin D that is measured in blood samples to determine vitamin D status), and many of its benefits will only be seen when a deficiency is reversed.\"\n", + "], columns=['text'])\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "6ee2cebf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 3/3 [00:05<00:00, 1.73s/it]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textsummarization
0Caffeine comes from coffee beans, but it can a...\\nCaffeine is a stimulant found in coffee bean...
1Vitamin C is a water-soluble essential vitamin...\\nVitamin C is an essential water-soluble vita...
2Vitamin D is a fat-soluble nutrient. It is one...\\nVitamin D is a fat-soluble nutrient that is ...
\n", + "
" + ], + "text/plain": [ + " text \\\n", + "0 Caffeine comes from coffee beans, but it can a... \n", + "1 Vitamin C is a water-soluble essential vitamin... \n", + "2 Vitamin D is a fat-soluble nutrient. It is one... \n", + "\n", + " summarization \n", + "0 \\nCaffeine is a stimulant found in coffee bean... \n", + "1 \\nVitamin C is an essential water-soluble vita... \n", + "2 \\nVitamin D is a fat-soluble nutrient that is ... " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.skills.generation.summarization import SummarizationSkill\n", + "\n", + "agent = Agent(\n", + " skills=SummarizationSkill(\n", + " name='summarization',\n", + " input_data_field='text'\n", + " )\n", + ")\n", + "\n", + "run = agent.apply_skills(df)\n", + "run.predictions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/text_generation_skill.ipynb b/examples/text_generation_skill.ipynb new file mode 100644 index 00000000..88eb374a --- /dev/null +++ b/examples/text_generation_skill.ipynb @@ -0,0 +1,338 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "94ad15ac", + "metadata": {}, + "source": [ + "# Text generation skill" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a2f6d99b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textcompletion_target
0Imagine a fusion of traditional retail with vi...In this futuristic retail scenario, customers ...
1Draft a message to shareholders highlighting o...Dear valued shareholders, As we look ahead, ou...
2Describe the office of the future in a post-pa...The office of the future is a hybrid space tha...
3How can we integrate AI into our customer serv...Integrating AI into customer service can be ac...
4Provide a vision statement for a tech company ...Empowering every individual, no matter their l...
5Suggest a strategy to improve employee wellnes...Promote regular digital detox hours, provide e...
6How can businesses benefit from embracing bloc...By adopting blockchain, businesses can achieve...
7Draft a mission statement for a startup focuse...Transforming discarded materials into timeless...
8Propose an innovative method for businesses to...Harness augmented reality (AR) to create immer...
9How can companies ensure data privacy in the a...Companies can adopt zero-knowledge proofs, dec...
\n", + "
" + ], + "text/plain": [ + " text \\\n", + "0 Imagine a fusion of traditional retail with vi... \n", + "1 Draft a message to shareholders highlighting o... \n", + "2 Describe the office of the future in a post-pa... \n", + "3 How can we integrate AI into our customer serv... \n", + "4 Provide a vision statement for a tech company ... \n", + "5 Suggest a strategy to improve employee wellnes... \n", + "6 How can businesses benefit from embracing bloc... \n", + "7 Draft a mission statement for a startup focuse... \n", + "8 Propose an innovative method for businesses to... \n", + "9 How can companies ensure data privacy in the a... \n", + "\n", + " completion_target \n", + "0 In this futuristic retail scenario, customers ... \n", + "1 Dear valued shareholders, As we look ahead, ou... \n", + "2 The office of the future is a hybrid space tha... \n", + "3 Integrating AI into customer service can be ac... \n", + "4 Empowering every individual, no matter their l... \n", + "5 Promote regular digital detox hours, provide e... \n", + "6 By adopting blockchain, businesses can achieve... \n", + "7 Transforming discarded materials into timeless... \n", + "8 Harness augmented reality (AR) to create immer... \n", + "9 Companies can adopt zero-knowledge proofs, dec... " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame([\n", + " {\"text\": \"Imagine a fusion of traditional retail with virtual reality. Describe the customer experience.\", \"completion_target\": \"In this futuristic retail scenario, customers wear VR headsets as they enter physical stores. They can instantly see personalized recommendations, virtual try-ons, and immersive product demos, blending the tactile benefits of in-person shopping with the endless possibilities of the digital realm.\"},\n", + " \n", + " {\"text\": \"Draft a message to shareholders highlighting our commitment to sustainable practices.\", \"completion_target\": \"Dear valued shareholders, As we look ahead, our commitment to sustainable and eco-friendly practices remains unwavering. We understand the critical importance of conserving our planet for future generations and are continually implementing innovative solutions to reduce our carbon footprint and promote ethical operations.\"},\n", + " \n", + " {\"text\": \"Describe the office of the future in a post-pandemic world.\", \"completion_target\": \"The office of the future is a hybrid space that seamlessly integrates remote and in-person work. Enhanced with smart technology, it's equipped for virtual collaborations and is adaptable to individual needs, prioritizing health, safety, and flexibility.\"},\n", + " \n", + " {\"text\": \"How can we integrate AI into our customer service without losing the human touch?\", \"completion_target\": \"Integrating AI into customer service can be achieved by using chatbots for routine queries and ensuring human agents handle more complex or emotional issues. Training AI with empathy in responses, while always providing an option for customers to connect with a human, can strike the right balance.\"},\n", + " \n", + " {\"text\": \"Provide a vision statement for a tech company aiming to bridge the digital divide in rural areas.\", \"completion_target\": \"Empowering every individual, no matter their location, with equal and unhindered access to the digital realm, we strive to erase boundaries and create a universally connected world.\"},\n", + " \n", + " {\"text\": \"Suggest a strategy to improve employee wellness in remote working conditions.\", \"completion_target\": \"Promote regular digital detox hours, provide ergonomic home office equipment subsidies, organize virtual fitness sessions, and ensure frequent mental health check-ins and resources to cultivate a culture of well-being.\"},\n", + " \n", + " {\"text\": \"How can businesses benefit from embracing blockchain technology?\", \"completion_target\": \"By adopting blockchain, businesses can achieve enhanced data security, improved supply chain transparency, efficient and transparent transactions, reduced fraud, and can tap into new models like token economies.\"},\n", + " \n", + " {\"text\": \"Draft a mission statement for a startup focused on upcycling waste materials into high-end fashion.\", \"completion_target\": \"Transforming discarded materials into timeless fashion, we're not just designing clothing, but pioneering a movement. Our mission is to redefine luxury with sustainability at its heart.\"},\n", + " \n", + " {\"text\": \"Propose an innovative method for businesses to retain customers in an increasingly competitive digital market.\", \"completion_target\": \"Harness augmented reality (AR) to create immersive product experiences. Allow customers to virtually 'experience' products in their real environment before purchasing, bridging the gap between the digital and physical worlds.\"},\n", + " \n", + " {\"text\": \"How can companies ensure data privacy in the age of big data and AI?\", \"completion_target\": \"Companies can adopt zero-knowledge proofs, decentralized data storage, regularly audit AI algorithms for bias, maintain transparent data policies, and educate users about their data rights to ensure robust data privacy.\"}\n", + "])\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "6ee2cebf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 10/10 [00:24<00:00, 2.48s/it]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textcompletion_targettext_generation
0Imagine a fusion of traditional retail with vi...In this futuristic retail scenario, customers ...The customer walks into a store that looks lik...
1Draft a message to shareholders highlighting o...Dear valued shareholders, As we look ahead, ou...Dear Shareholders,\\n\\nI am pleased to announce...
2Describe the office of the future in a post-pa...The office of the future is a hybrid space tha...The office of the future in a post-pandemic wo...
3How can we integrate AI into our customer serv...Integrating AI into customer service can be ac...One way to integrate AI into customer service ...
4Provide a vision statement for a tech company ...Empowering every individual, no matter their l...Our vision is to create a world where technolo...
5Suggest a strategy to improve employee wellnes...Promote regular digital detox hours, provide e...One strategy to improve employee wellness in r...
6How can businesses benefit from embracing bloc...By adopting blockchain, businesses can achieve...\\nBusinesses can benefit greatly from embracin...
7Draft a mission statement for a startup focuse...Transforming discarded materials into timeless...\\nAt our startup, we are passionate about crea...
8Propose an innovative method for businesses to...Harness augmented reality (AR) to create immer...One potential solution could be implementing a...
9How can companies ensure data privacy in the a...Companies can adopt zero-knowledge proofs, dec...In order to ensure data privacy in the age of ...
\n", + "
" + ], + "text/plain": [ + " text \\\n", + "0 Imagine a fusion of traditional retail with vi... \n", + "1 Draft a message to shareholders highlighting o... \n", + "2 Describe the office of the future in a post-pa... \n", + "3 How can we integrate AI into our customer serv... \n", + "4 Provide a vision statement for a tech company ... \n", + "5 Suggest a strategy to improve employee wellnes... \n", + "6 How can businesses benefit from embracing bloc... \n", + "7 Draft a mission statement for a startup focuse... \n", + "8 Propose an innovative method for businesses to... \n", + "9 How can companies ensure data privacy in the a... \n", + "\n", + " completion_target \\\n", + "0 In this futuristic retail scenario, customers ... \n", + "1 Dear valued shareholders, As we look ahead, ou... \n", + "2 The office of the future is a hybrid space tha... \n", + "3 Integrating AI into customer service can be ac... \n", + "4 Empowering every individual, no matter their l... \n", + "5 Promote regular digital detox hours, provide e... \n", + "6 By adopting blockchain, businesses can achieve... \n", + "7 Transforming discarded materials into timeless... \n", + "8 Harness augmented reality (AR) to create immer... \n", + "9 Companies can adopt zero-knowledge proofs, dec... \n", + "\n", + " text_generation \n", + "0 The customer walks into a store that looks lik... \n", + "1 Dear Shareholders,\\n\\nI am pleased to announce... \n", + "2 The office of the future in a post-pandemic wo... \n", + "3 One way to integrate AI into customer service ... \n", + "4 Our vision is to create a world where technolo... \n", + "5 One strategy to improve employee wellness in r... \n", + "6 \\nBusinesses can benefit greatly from embracin... \n", + "7 \\nAt our startup, we are passionate about crea... \n", + "8 One potential solution could be implementing a... \n", + "9 In order to ensure data privacy in the age of ... " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.skills.generation.base import TextGenerationSkill\n", + "\n", + "agent = Agent(\n", + " skills=TextGenerationSkill(\n", + " name='text_generation',\n", + " input_data_field='text'\n", + " )\n", + ")\n", + "\n", + "run = agent.apply_skills(df)\n", + "run.predictions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/translation_skill.ipynb b/examples/translation_skill.ipynb new file mode 100644 index 00000000..d830f364 --- /dev/null +++ b/examples/translation_skill.ipynb @@ -0,0 +1,312 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "94ad15ac", + "metadata": {}, + "source": [ + "# Translation skill" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "a2f6d99b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textlanguage
0El sol brilla siempreSpanish
1La vie est belleFrench
2Der Wald ruft michGerman
3Amo la pizza napoletanaItalian
4ๆ˜ฅๅคฉ็š„่Šฑๅพˆ็พŽChinese
5ะ—ะฒะตะทะดั‹ ัะฒะตั€ะบะฐัŽั‚ ะฝะพั‡ัŒัŽRussian
6้›จใฎๅพŒใฎ่™นJapanese
7์ปคํ”ผ๊ฐ€ ํ•„์š”ํ•ดKorean
8A mรบsica toca a almaPortuguese
9เคธเคชเคจเฅ‡ เคธเคš เคนเฅ‹เคคเฅ‡ เคนเฅˆเค‚Hindi
\n", + "
" + ], + "text/plain": [ + " text language\n", + "0 El sol brilla siempre Spanish\n", + "1 La vie est belle French\n", + "2 Der Wald ruft mich German\n", + "3 Amo la pizza napoletana Italian\n", + "4 ๆ˜ฅๅคฉ็š„่Šฑๅพˆ็พŽ Chinese\n", + "5 ะ—ะฒะตะทะดั‹ ัะฒะตั€ะบะฐัŽั‚ ะฝะพั‡ัŒัŽ Russian\n", + "6 ้›จใฎๅพŒใฎ่™น Japanese\n", + "7 ์ปคํ”ผ๊ฐ€ ํ•„์š”ํ•ด Korean\n", + "8 A mรบsica toca a alma Portuguese\n", + "9 เคธเคชเคจเฅ‡ เคธเคš เคนเฅ‹เคคเฅ‡ เคนเฅˆเค‚ Hindi" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "df = pd.DataFrame([\n", + " {\"text\": \"El sol brilla siempre\", \"language\": \"Spanish\"},\n", + " {\"text\": \"La vie est belle\", \"language\": \"French\"}, \n", + " {\"text\": \"Der Wald ruft mich\", \"language\": \"German\"},\n", + " {\"text\": \"Amo la pizza napoletana\", \"language\": \"Italian\"},\n", + " {\"text\": \"ๆ˜ฅๅคฉ็š„่Šฑๅพˆ็พŽ\", \"language\": \"Chinese\"}, \n", + " {\"text\": \"ะ—ะฒะตะทะดั‹ ัะฒะตั€ะบะฐัŽั‚ ะฝะพั‡ัŒัŽ\", \"language\": \"Russian\"},\n", + " {\"text\": \"้›จใฎๅพŒใฎ่™น\", \"language\": \"Japanese\"},\n", + " {\"text\": \"์ปคํ”ผ๊ฐ€ ํ•„์š”ํ•ด\", \"language\": \"Korean\"},\n", + " {\"text\": \"A mรบsica toca a alma\", \"language\": \"Portuguese\"},\n", + " {\"text\": \"เคธเคชเคจเฅ‡ เคธเคš เคนเฅ‹เคคเฅ‡ เคนเฅˆเค‚\", \"language\": \"Hindi\"}\n", + "])\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6ee2cebf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 10/10 [00:08<00:00, 1.13it/s]\n" + ] + } + ], + "source": [ + "from adala.agents import Agent\n", + "from adala.environments import BasicEnvironment\n", + "from adala.skills.generation.translation import TranslationSkill\n", + "from rich import print\n", + "\n", + "agent = Agent(skills=TranslationSkill(input_data_field='text', target_language='Swahili'))\n", + "\n", + "run = agent.apply_skills(df)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "ee97ee22", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textlanguagetranslation
0El sol brilla siempreSpanishJua huangaza daima
1La vie est belleFrenchMaisha ni mazuri
2Der Wald ruft michGermanMsitu unaniita
3Amo la pizza napoletanaItalianNapenda pizza ya Napoli
4ๆ˜ฅๅคฉ็š„่Šฑๅพˆ็พŽChineseMaua ya msimu wa kuchipua ni mazuri sana
5ะ—ะฒะตะทะดั‹ ัะฒะตั€ะบะฐัŽั‚ ะฝะพั‡ัŒัŽRussianNyota zinang'aa usiku
6้›จใฎๅพŒใฎ่™นJapaneseMvua baada ya upinde wa mvua
7์ปคํ”ผ๊ฐ€ ํ•„์š”ํ•ดKoreanNinahitaji kahawa
8A mรบsica toca a almaPortugueseMuziki hucheza roho
9เคธเคชเคจเฅ‡ เคธเคš เคนเฅ‹เคคเฅ‡ เคนเฅˆเค‚HindiNdoto zinatimia
\n", + "
" + ], + "text/plain": [ + " text language \\\n", + "0 El sol brilla siempre Spanish \n", + "1 La vie est belle French \n", + "2 Der Wald ruft mich German \n", + "3 Amo la pizza napoletana Italian \n", + "4 ๆ˜ฅๅคฉ็š„่Šฑๅพˆ็พŽ Chinese \n", + "5 ะ—ะฒะตะทะดั‹ ัะฒะตั€ะบะฐัŽั‚ ะฝะพั‡ัŒัŽ Russian \n", + "6 ้›จใฎๅพŒใฎ่™น Japanese \n", + "7 ์ปคํ”ผ๊ฐ€ ํ•„์š”ํ•ด Korean \n", + "8 A mรบsica toca a alma Portuguese \n", + "9 เคธเคชเคจเฅ‡ เคธเคš เคนเฅ‹เคคเฅ‡ เคนเฅˆเค‚ Hindi \n", + "\n", + " translation \n", + "0 Jua huangaza daima \n", + "1 Maisha ni mazuri \n", + "2 Msitu unaniita \n", + "3 Napenda pizza ya Napoli \n", + "4 Maua ya msimu wa kuchipua ni mazuri sana \n", + "5 Nyota zinang'aa usiku \n", + "6 Mvua baada ya upinde wa mvua \n", + "7 Ninahitaji kahawa \n", + "8 Muziki hucheza roho \n", + "9 Ndoto zinatimia " + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run.predictions" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 00000000..795173d3 --- /dev/null +++ b/pdm.lock @@ -0,0 +1,1347 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default"] +cross_platform = true +static_urls = false +lock_version = "4.3" +content_hash = "sha256:393586f004c3f00bb1aaa5d0c7f71d670c4491e579daaf0e2df40762dfbd660a" + +[[package]] +name = "aiohttp" +version = "3.8.6" +requires_python = ">=3.6" +summary = "Async http client/server framework (asyncio)" +dependencies = [ + "aiosignal>=1.1.2", + "async-timeout<5.0,>=4.0.0a3", + "attrs>=17.3.0", + "charset-normalizer<4.0,>=2.0", + "frozenlist>=1.1.1", + "multidict<7.0,>=4.5", + "yarl<2.0,>=1.0", +] +files = [ + {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41d55fc043954cddbbd82503d9cc3f4814a40bcef30b3569bc7b5e34130718c1"}, + {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d84166673694841d8953f0a8d0c90e1087739d24632fe86b1a08819168b4566"}, + {file = "aiohttp-3.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:253bf92b744b3170eb4c4ca2fa58f9c4b87aeb1df42f71d4e78815e6e8b73c9e"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd194939b1f764d6bb05490987bfe104287bbf51b8d862261ccf66f48fb4096"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c5f938d199a6fdbdc10bbb9447496561c3a9a565b43be564648d81e1102ac22"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2817b2f66ca82ee699acd90e05c95e79bbf1dc986abb62b61ec8aaf851e81c93"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa375b3d34e71ccccf172cab401cd94a72de7a8cc01847a7b3386204093bb47"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9de50a199b7710fa2904be5a4a9b51af587ab24c8e540a7243ab737b45844543"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e1d8cb0b56b3587c5c01de3bf2f600f186da7e7b5f7353d1bf26a8ddca57f965"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8e31e9db1bee8b4f407b77fd2507337a0a80665ad7b6c749d08df595d88f1cf5"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7bc88fc494b1f0311d67f29fee6fd636606f4697e8cc793a2d912ac5b19aa38d"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ec00c3305788e04bf6d29d42e504560e159ccaf0be30c09203b468a6c1ccd3b2"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad1407db8f2f49329729564f71685557157bfa42b48f4b93e53721a16eb813ed"}, + {file = "aiohttp-3.8.6-cp310-cp310-win32.whl", hash = "sha256:ccc360e87341ad47c777f5723f68adbb52b37ab450c8bc3ca9ca1f3e849e5fe2"}, + {file = "aiohttp-3.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:93c15c8e48e5e7b89d5cb4613479d144fda8344e2d886cf694fd36db4cc86865"}, + {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e2f9cc8e5328f829f6e1fb74a0a3a939b14e67e80832975e01929e320386b34"}, + {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6a00ffcc173e765e200ceefb06399ba09c06db97f401f920513a10c803604ca"}, + {file = "aiohttp-3.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:41bdc2ba359032e36c0e9de5a3bd00d6fb7ea558a6ce6b70acedf0da86458321"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14cd52ccf40006c7a6cd34a0f8663734e5363fd981807173faf3a017e202fec9"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d5b785c792802e7b275c420d84f3397668e9d49ab1cb52bd916b3b3ffcf09ad"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1bed815f3dc3d915c5c1e556c397c8667826fbc1b935d95b0ad680787896a358"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96603a562b546632441926cd1293cfcb5b69f0b4159e6077f7c7dbdfb686af4d"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d76e8b13161a202d14c9584590c4df4d068c9567c99506497bdd67eaedf36403"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e3f1e3f1a1751bb62b4a1b7f4e435afcdade6c17a4fd9b9d43607cebd242924a"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:76b36b3124f0223903609944a3c8bf28a599b2cc0ce0be60b45211c8e9be97f8"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a2ece4af1f3c967a4390c284797ab595a9f1bc1130ef8b01828915a05a6ae684"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:16d330b3b9db87c3883e565340d292638a878236418b23cc8b9b11a054aaa887"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42c89579f82e49db436b69c938ab3e1559e5a4409eb8639eb4143989bc390f2f"}, + {file = "aiohttp-3.8.6-cp311-cp311-win32.whl", hash = "sha256:efd2fcf7e7b9d7ab16e6b7d54205beded0a9c8566cb30f09c1abe42b4e22bdcb"}, + {file = "aiohttp-3.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:3b2ab182fc28e7a81f6c70bfbd829045d9480063f5ab06f6e601a3eddbbd49a0"}, + {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e2ee0ac5a1f5c7dd3197de309adfb99ac4617ff02b0603fd1e65b07dc772e4b"}, + {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01770d8c04bd8db568abb636c1fdd4f7140b284b8b3e0b4584f070180c1e5c62"}, + {file = "aiohttp-3.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3c68330a59506254b556b99a91857428cab98b2f84061260a67865f7f52899f5"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89341b2c19fb5eac30c341133ae2cc3544d40d9b1892749cdd25892bbc6ac951"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71783b0b6455ac8f34b5ec99d83e686892c50498d5d00b8e56d47f41b38fbe04"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f628dbf3c91e12f4d6c8b3f092069567d8eb17814aebba3d7d60c149391aee3a"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04691bc6601ef47c88f0255043df6f570ada1a9ebef99c34bd0b72866c217ae"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee912f7e78287516df155f69da575a0ba33b02dd7c1d6614dbc9463f43066e3"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9c19b26acdd08dd239e0d3669a3dddafd600902e37881f13fbd8a53943079dbc"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:99c5ac4ad492b4a19fc132306cd57075c28446ec2ed970973bbf036bcda1bcc6"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f0f03211fd14a6a0aed2997d4b1c013d49fb7b50eeb9ffdf5e51f23cfe2c77fa"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:8d399dade330c53b4106160f75f55407e9ae7505263ea86f2ccca6bfcbdb4921"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ec4fd86658c6a8964d75426517dc01cbf840bbf32d055ce64a9e63a40fd7b771"}, + {file = "aiohttp-3.8.6-cp38-cp38-win32.whl", hash = "sha256:33164093be11fcef3ce2571a0dccd9041c9a93fa3bde86569d7b03120d276c6f"}, + {file = "aiohttp-3.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:bdf70bfe5a1414ba9afb9d49f0c912dc524cf60141102f3a11143ba3d291870f"}, + {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d52d5dc7c6682b720280f9d9db41d36ebe4791622c842e258c9206232251ab2b"}, + {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ac39027011414dbd3d87f7edb31680e1f430834c8cef029f11c66dad0670aa5"}, + {file = "aiohttp-3.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3f5c7ce535a1d2429a634310e308fb7d718905487257060e5d4598e29dc17f0b"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b30e963f9e0d52c28f284d554a9469af073030030cef8693106d918b2ca92f54"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:918810ef188f84152af6b938254911055a72e0f935b5fbc4c1a4ed0b0584aed1"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:002f23e6ea8d3dd8d149e569fd580c999232b5fbc601c48d55398fbc2e582e8c"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcf3eabd3fd1a5e6092d1242295fa37d0354b2eb2077e6eb670accad78e40e1"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:255ba9d6d5ff1a382bb9a578cd563605aa69bec845680e21c44afc2670607a95"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d67f8baed00870aa390ea2590798766256f31dc5ed3ecc737debb6e97e2ede78"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:86f20cee0f0a317c76573b627b954c412ea766d6ada1a9fcf1b805763ae7feeb"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:39a312d0e991690ccc1a61f1e9e42daa519dcc34ad03eb6f826d94c1190190dd"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e827d48cf802de06d9c935088c2924e3c7e7533377d66b6f31ed175c1620e05e"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd111d7fc5591ddf377a408ed9067045259ff2770f37e2d94e6478d0f3fc0c17"}, + {file = "aiohttp-3.8.6-cp39-cp39-win32.whl", hash = "sha256:caf486ac1e689dda3502567eb89ffe02876546599bbf915ec94b1fa424eeffd4"}, + {file = "aiohttp-3.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3f0e27e5b733803333bb2371249f41cf42bae8884863e8e8965ec69bebe53132"}, + {file = "aiohttp-3.8.6.tar.gz", hash = "sha256:b0cf2a4501bff9330a8a5248b4ce951851e415bdcce9dc158e76cfd55e15085c"}, +] + +[[package]] +name = "aiosignal" +version = "1.3.1" +requires_python = ">=3.7" +summary = "aiosignal: a list of registered asynchronous callbacks" +dependencies = [ + "frozenlist>=1.1.0", +] +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[[package]] +name = "annotated-types" +version = "0.6.0" +requires_python = ">=3.8" +summary = "Reusable constraint types to use with typing.Annotated" +dependencies = [ + "typing-extensions>=4.0.0; python_version < \"3.9\"", +] +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 = "async-timeout" +version = "4.0.3" +requires_python = ">=3.7" +summary = "Timeout context manager for asyncio programs" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +requires_python = ">=3.7" +summary = "Classes Without Boilerplate" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[[package]] +name = "cachetools" +version = "5.3.2" +requires_python = ">=3.7" +summary = "Extensible memoizing collections and decorators" +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.7.22" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "cffi" +version = "1.16.0" +requires_python = ">=3.8" +summary = "Foreign Function Interface for Python calling C code." +dependencies = [ + "pycparser", +] +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]] +name = "charset-normalizer" +version = "3.3.1" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +files = [ + {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, + {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +dependencies = [ + "colorama; platform_system == \"Windows\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +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 = "cryptography" +version = "41.0.5" +requires_python = ">=3.7" +summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +dependencies = [ + "cffi>=1.12", +] +files = [ + {file = "cryptography-41.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797"}, + {file = "cryptography-41.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da"}, + {file = "cryptography-41.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20"}, + {file = "cryptography-41.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548"}, + {file = "cryptography-41.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d"}, + {file = "cryptography-41.0.5-cp37-abi3-win32.whl", hash = "sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936"}, + {file = "cryptography-41.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88"}, + {file = "cryptography-41.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179"}, + {file = "cryptography-41.0.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723"}, + {file = "cryptography-41.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84"}, + {file = "cryptography-41.0.5.tar.gz", hash = "sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7"}, +] + +[[package]] +name = "diskcache" +version = "5.6.3" +requires_python = ">=3" +summary = "Disk Cache -- Disk and file backed persistent cache." +files = [ + {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, + {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, +] + +[[package]] +name = "frozenlist" +version = "1.4.0" +requires_python = ">=3.8" +summary = "A list-like structure which implements collections.abc.MutableSequence" +files = [ + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, + {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, + {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, + {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, + {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, + {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, + {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, + {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, + {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, + {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, +] + +[[package]] +name = "gptcache" +version = "0.1.42" +requires_python = ">=3.8.1" +summary = "GPTCache, a powerful caching library that can be used to speed up and lower the cost of chat applications that rely on the LLM service. GPTCache works as a memcache for AIGC applications, similar to how Redis works for traditional applications." +dependencies = [ + "cachetools", + "numpy", + "requests", +] +files = [ + {file = "gptcache-0.1.42-py3-none-any.whl", hash = "sha256:8da93cd9fdc3a1c09aae25b688823b4a5bc28dcfa4522e33617f3f7a9e5b8bb0"}, + {file = "gptcache-0.1.42.tar.gz", hash = "sha256:17339c41d992bd47c623c716be3bd915dba2687a0fa52aa4ab4ed9cc7cc2b256"}, +] + +[[package]] +name = "guidance" +version = "0.0.64" +summary = "A guidance language for controlling large language models." +dependencies = [ + "aiohttp", + "diskcache", + "gptcache", + "msal", + "nest-asyncio", + "numpy", + "openai>=0.27.8", + "platformdirs", + "pygtrie", + "pyparsing>=3.0.0", + "requests", + "tiktoken>=0.3", +] +files = [ + {file = "guidance-0.0.64-py3-none-any.whl", hash = "sha256:b15b3bc667bb5b6e9b574781cab7c1ce7fa0a6e705651595bbd8630d124c045a"}, + {file = "guidance-0.0.64.tar.gz", hash = "sha256:baaee2c791fe853c920b5964661bb63155feb58f84c25e45f83c47f63c4e58dd"}, +] + +[[package]] +name = "hiredis" +version = "2.2.3" +requires_python = ">=3.7" +summary = "Python wrapper for hiredis" +files = [ + {file = "hiredis-2.2.3-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:9a1a80a8fa767f2fdc3870316a54b84fe9fc09fa6ab6a2686783de6a228a4604"}, + {file = "hiredis-2.2.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3f006c28c885deb99b670a5a66f367a175ab8955b0374029bad7111f5357dcd4"}, + {file = "hiredis-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffaf841546905d90ff189de7397aa56413b1ce5e54547f17a98f0ebf3a3b0a3b"}, + {file = "hiredis-2.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cadb0ac7ba3babfd804e425946bec9717b320564a1390f163a54af9365a720a"}, + {file = "hiredis-2.2.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33bc4721632ef9708fa44e5df0066053fccc8e65410a2c48573192517a533b48"}, + {file = "hiredis-2.2.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:227c5b4bcb60f89008c275d596e4a7b6625a6b3c827b8a66ae582eace7051f71"}, + {file = "hiredis-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61995eb826009d99ed8590747bc0da683a5f4fbb4faa8788166bf3810845cd5c"}, + {file = "hiredis-2.2.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f969edc851efe23010e0f53a64269f2629a9364135e9ec81c842e8b2277d0c1"}, + {file = "hiredis-2.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27e560eefb57914d742a837f1da98d3b29cb22eff013c8023b7cf52ae6e051d"}, + {file = "hiredis-2.2.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3759f4789ae1913b7df278dfc9e8749205b7a106f888cd2903d19461e24a7697"}, + {file = "hiredis-2.2.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c6cb613148422c523945cdb8b6bed617856f2602fd8750e33773ede2616e55d5"}, + {file = "hiredis-2.2.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:1d274d5c511dfc03f83f997d3238eaa9b6ee3f982640979f509373cced891e98"}, + {file = "hiredis-2.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b7fe075e91b9d9cff40eba4fb6a8eff74964d3979a39be9a9ef58b1b4cb3604"}, + {file = "hiredis-2.2.3-cp310-cp310-win32.whl", hash = "sha256:77924b0d32fd1f493d3df15d9609ddf9d94c31a364022a6bf6b525ce9da75bea"}, + {file = "hiredis-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:dcb0569dd5bfe6004658cd0f229efa699a3169dcb4f77bd72e188adda302063d"}, + {file = "hiredis-2.2.3-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:d115790f18daa99b5c11a506e48923b630ef712e9e4b40482af942c3d40638b8"}, + {file = "hiredis-2.2.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c3b8be557e08b234774925622e196f0ee36fe4eab66cd19df934d3efd8f3743"}, + {file = "hiredis-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f5446068197b35a11ccc697720c41879c8657e2e761aaa8311783aac84cef20"}, + {file = "hiredis-2.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa17a3b22b3726d54d7af20394f65d4a1735a842a4e0f557dc67a90f6965c4bc"}, + {file = "hiredis-2.2.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7df645b6b7800e8b748c217fbd6a4ca8361bcb9a1ae6206cc02377833ec8a1aa"}, + {file = "hiredis-2.2.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fb9300959a0048138791f3d68359d61a788574ec9556bddf1fec07f2dbc5320"}, + {file = "hiredis-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d7e459fe7313925f395148d36d9b7f4f8dac65be06e45d7af356b187cef65fc"}, + {file = "hiredis-2.2.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8eceffca3941775b646cd585cd19b275d382de43cc3327d22f7c75d7b003d481"}, + {file = "hiredis-2.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b17baf702c6e5b4bb66e1281a3efbb1d749c9d06cdb92b665ad81e03118f78fc"}, + {file = "hiredis-2.2.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e43e2b5acaad09cf48c032f7e4926392bb3a3f01854416cf6d82ebff94d5467"}, + {file = "hiredis-2.2.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a7205497d7276a81fe92951a29616ef96562ed2f91a02066f72b6f93cb34b40e"}, + {file = "hiredis-2.2.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:126623b03c31cb6ac3e0d138feb6fcc36dd43dd34fc7da7b7a0c38b5d75bc896"}, + {file = "hiredis-2.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:071c5814b850574036506a8118034f97c3cbf2fe9947ff45a27b07a48da56240"}, + {file = "hiredis-2.2.3-cp311-cp311-win32.whl", hash = "sha256:d1be9e30e675f5bc1cb534633324578f6f0944a1bcffe53242cf632f554f83b6"}, + {file = "hiredis-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9a7c987e161e3c58f992c63b7e26fea7fe0777f3b975799d23d65bbb8cb5899"}, + {file = "hiredis-2.2.3-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:5a4bcef114fc071d5f52c386c47f35aae0a5b43673197b9288a15b584da8fa3a"}, + {file = "hiredis-2.2.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:232d0a70519865741ba56e1dfefd160a580ae78c30a1517bad47b3cf95a3bc7d"}, + {file = "hiredis-2.2.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9076ce8429785c85f824650735791738de7143f61f43ae9ed83e163c0ca0fa44"}, + {file = "hiredis-2.2.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec58fb7c2062f835595c12f0f02dcda76d0eb0831423cc191d1e18c9276648de"}, + {file = "hiredis-2.2.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f2b34a6444b8f9c1e9f84bd2c639388e5d14f128afd14a869dfb3d9af893aa2"}, + {file = "hiredis-2.2.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:818dfd310aa1020a13cd08ee48e116dd8c3bb2e23b8161f8ac4df587dd5093d7"}, + {file = "hiredis-2.2.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d9ea6c8d4cbdeee2e0d43379ce2881e4af0454b00570677c59f33f2531cd38"}, + {file = "hiredis-2.2.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1eadbcd3de55ac42310ff82550d3302cb4efcd4e17d76646a17b6e7004bb42b"}, + {file = "hiredis-2.2.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:477c34c4489666dc73cb5e89dafe2617c3e13da1298917f73d55aac4696bd793"}, + {file = "hiredis-2.2.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:14824e457e4f5cda685c3345d125da13949bcf3bb1c88eb5d248c8d2c3dee08f"}, + {file = "hiredis-2.2.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9cd32326dfa6ce87edf754153b0105aca64486bebe93b9600ccff74fa0b224df"}, + {file = "hiredis-2.2.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:51341e70b467004dcbec3a6ce8c478d2d6241e0f6b01e4c56764afd5022e1e9d"}, + {file = "hiredis-2.2.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2443659c76b226267e2a04dbbb21bc2a3f91aa53bdc0c22964632753ae43a247"}, + {file = "hiredis-2.2.3-cp38-cp38-win32.whl", hash = "sha256:4e3e3e31423f888d396b1fc1f936936e52af868ac1ec17dd15e3eeba9dd4de24"}, + {file = "hiredis-2.2.3-cp38-cp38-win_amd64.whl", hash = "sha256:20f509e3a1a20d6e5f5794fc37ceb21f70f409101fcfe7a8bde783894d51b369"}, + {file = "hiredis-2.2.3-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:d20891e3f33803b26d54c77fd5745878497091e33f4bbbdd454cf6e71aee8890"}, + {file = "hiredis-2.2.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:50171f985e17970f87d5a29e16603d1e5b03bdbf5c2691a37e6c912942a6b657"}, + {file = "hiredis-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9944a2cac25ffe049a7e89f306e11b900640837d1ef38d9be0eaa4a4e2b73a52"}, + {file = "hiredis-2.2.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a5c8019ff94988d56eb49b15de76fe83f6b42536d76edeb6565dbf7fe14b973"}, + {file = "hiredis-2.2.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a286ded34eb16501002e3713b3130c987366eee2ba0d58c33c72f27778e31676"}, + {file = "hiredis-2.2.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b3e974ad15eb32b1f537730dea70b93a4c3db7b026de3ad2b59da49c6f7454d"}, + {file = "hiredis-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08415ea74c1c29b9d6a4ca3dd0e810dc1af343c1d1d442e15ba133b11ab5be6a"}, + {file = "hiredis-2.2.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e17d04ea58ab8cf3f2dc52e875db16077c6357846006780086fff3189fb199d"}, + {file = "hiredis-2.2.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6ccdcb635dae85b006592f78e32d97f4bc7541cb27829d505f9c7fefcef48298"}, + {file = "hiredis-2.2.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:69536b821dd1bc78058a6e7541743f8d82bf2d981b91280b14c4daa6cdc7faba"}, + {file = "hiredis-2.2.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:3753df5f873d473f055e1f8837bfad0bd3b277c86f3c9bf058c58f14204cd901"}, + {file = "hiredis-2.2.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6f88cafe46612b6fa68e6dea49e25bebf160598bba00101caa51cc8c1f18d597"}, + {file = "hiredis-2.2.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33ee3ea5cad3a8cb339352cd230b411eb437a2e75d7736c4899acab32056ccdb"}, + {file = "hiredis-2.2.3-cp39-cp39-win32.whl", hash = "sha256:b4f3d06dc16671b88a13ae85d8ca92534c0b637d59e49f0558d040a691246422"}, + {file = "hiredis-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4f674e309cd055ee7a48304ceb8cf43265d859faf4d7d01d270ce45e976ae9d3"}, + {file = "hiredis-2.2.3-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8f280ab4e043b089777b43b4227bdc2035f88da5072ab36588e0ccf77d45d058"}, + {file = "hiredis-2.2.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15c2a551f3b8a26f7940d6ee10b837810201754b8d7e6f6b1391655370882c5a"}, + {file = "hiredis-2.2.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60c4e3c258eafaab21b174b17270a0cc093718d61cdbde8c03f85ec4bf835343"}, + {file = "hiredis-2.2.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc36a9dded458d4e37492fe3e619c6c83caae794d26ad925adbce61d592f8428"}, + {file = "hiredis-2.2.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4ed68a3b1ccb4313d2a42546fd7e7439ad4745918a48b6c9bcaa61e1e3e42634"}, + {file = "hiredis-2.2.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3bf4b5bae472630c229518e4a814b1b68f10a3d9b00aeaec45f1a330f03a0251"}, + {file = "hiredis-2.2.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33a94d264e6e12a79d9bb8af333b01dc286b9f39c99072ab5fef94ce1f018e17"}, + {file = "hiredis-2.2.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fa6811a618653164f918b891a0fa07052bd71a799defa5c44d167cac5557b26"}, + {file = "hiredis-2.2.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af33f370be90b48bbaf0dab32decbdcc522b1fa95d109020a963282086518a8e"}, + {file = "hiredis-2.2.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b9953d87418ac228f508d93898ab572775e4d3b0eeb886a1a7734553bcdaf291"}, + {file = "hiredis-2.2.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5e7bb4dd524f50b71c20ef5a12bd61da9b463f8894b18a06130942fe31509881"}, + {file = "hiredis-2.2.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89a258424158eb8b3ed9f65548d68998da334ef155d09488c5637723eb1cd697"}, + {file = "hiredis-2.2.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f4a65276f6ecdebe75f2a53f578fbc40e8d2860658420d5e0611c56bbf5054c"}, + {file = "hiredis-2.2.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:334f2738700b20faa04a0d813366fb16ed17287430a6b50584161d5ad31ca6d7"}, + {file = "hiredis-2.2.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d194decd9608f11c777946f596f31d5aacad13972a0a87829ae1e6f2d26c1885"}, + {file = "hiredis-2.2.3.tar.gz", hash = "sha256:e75163773a309e56a9b58165cf5a50e0f84b755f6ff863b2c01a38918fe92daa"}, +] + +[[package]] +name = "idna" +version = "3.4" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +requires_python = ">=3.8" +summary = "Python port of markdown-it. Markdown parsing, done right!" +dependencies = [ + "mdurl~=0.1", +] +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +requires_python = ">=3.7" +summary = "Markdown URL utilities" +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 = "more-itertools" +version = "9.1.0" +requires_python = ">=3.7" +summary = "More routines for operating on iterables, beyond itertools" +files = [ + {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, + {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, +] + +[[package]] +name = "msal" +version = "1.24.1" +requires_python = ">=2.7" +summary = "The Microsoft Authentication Library (MSAL) for Python library" +dependencies = [ + "PyJWT[crypto]<3,>=1.0.0", + "cryptography<44,>=0.6", + "requests<3,>=2.0.0", +] +files = [ + {file = "msal-1.24.1-py2.py3-none-any.whl", hash = "sha256:ce4320688f95c301ee74a4d0e9dbcfe029a63663a8cc61756f40d0d0d36574ad"}, + {file = "msal-1.24.1.tar.gz", hash = "sha256:aa0972884b3c6fdec53d9a0bd15c12e5bd7b71ac1b66d746f54d128709f3f8f8"}, +] + +[[package]] +name = "multidict" +version = "6.0.4" +requires_python = ">=3.7" +summary = "multidict implementation" +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-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 = "nest-asyncio" +version = "1.5.8" +requires_python = ">=3.5" +summary = "Patch asyncio to allow nested event loops" +files = [ + {file = "nest_asyncio-1.5.8-py3-none-any.whl", hash = "sha256:accda7a339a70599cb08f9dd09a67e0c2ef8d8d6f4c07f96ab203f2ae254e48d"}, + {file = "nest_asyncio-1.5.8.tar.gz", hash = "sha256:25aa2ca0d2a5b5531956b9e273b45cf664cae2b145101d73b86b199978d48fdb"}, +] + +[[package]] +name = "numpy" +version = "1.24.4" +requires_python = ">=3.8" +summary = "Fundamental package for array computing in Python" +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + +[[package]] +name = "openai" +version = "0.28.1" +requires_python = ">=3.7.1" +summary = "Python client library for the OpenAI API" +dependencies = [ + "aiohttp", + "requests>=2.20", + "tqdm", +] +files = [ + {file = "openai-0.28.1-py3-none-any.whl", hash = "sha256:d18690f9e3d31eedb66b57b88c2165d760b24ea0a01f150dd3f068155088ce68"}, + {file = "openai-0.28.1.tar.gz", hash = "sha256:4be1dad329a65b4ce1a660fe6d5431b438f429b5855c883435f0f7fcb6d2dcc8"}, +] + +[[package]] +name = "pandas" +version = "2.0.3" +requires_python = ">=3.8" +summary = "Powerful data structures for data analysis, time series, and statistics" +dependencies = [ + "numpy>=1.20.3; python_version < \"3.10\"", + "numpy>=1.21.0; python_version >= \"3.10\"", + "numpy>=1.23.2; python_version >= \"3.11\"", + "python-dateutil>=2.8.2", + "pytz>=2020.1", + "tzdata>=2022.1", +] +files = [ + {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, + {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, + {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, + {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, + {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, + {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, + {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, + {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, + {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, + {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, + {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, + {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, + {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, + {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, + {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, + {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, + {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, + {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, +] + +[[package]] +name = "platformdirs" +version = "3.11.0" +requires_python = ">=3.7" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +files = [ + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "C parser in Python" +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.0.3" +requires_python = ">=3.7" +summary = "Data validation using Python type hints" +dependencies = [ + "annotated-types>=0.4.0", + "pydantic-core==2.3.0", + "typing-extensions>=4.6.1", +] +files = [ + {file = "pydantic-2.0.3-py3-none-any.whl", hash = "sha256:614eb3321eb600c81899a88fa9858b008e3c79e0d4f1b49ab1f516b4b0c27cfb"}, + {file = "pydantic-2.0.3.tar.gz", hash = "sha256:94f13e0dcf139a5125e88283fc999788d894e14ed90cf478bcc2ee50bd4fc630"}, +] + +[[package]] +name = "pydantic-core" +version = "2.3.0" +requires_python = ">=3.7" +summary = "" +dependencies = [ + "typing-extensions!=4.7.0,>=4.6.0", +] +files = [ + {file = "pydantic_core-2.3.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:4542c98b8364b976593703a2dda97377433b102f380b61bc3a2cbc2fbdae1d1f"}, + {file = "pydantic_core-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9342de50824b40f55d2600f66c6f9a91a3a24851eca39145a749a3dc804ee599"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:539432f911686cb80284c30b33eaf9f4fd9a11e1111fe0dc98fdbdce69b49821"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38a0e7ee65c8999394d92d9c724434cb629279d19844f2b69d9bbc46dc8b8b61"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_armv7l.whl", hash = "sha256:e3ed6834cc005798187a56c248a2240207cb8ffdda1c89e9afda4c3d526c2ea0"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:e72ac299a6bf732a60852d052acf3999d234686755a02ba111e85e7ebf8155b1"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_24_s390x.whl", hash = "sha256:616b3451b05ca63b8f433c627f68046b39543faeaa4e50d8c6699a2a1e4b85a5"}, + {file = "pydantic_core-2.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:adcb9c8848e15c613e483e0b99767ae325af27fe0dbd866df01fe5849d06e6e1"}, + {file = "pydantic_core-2.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:464bf799b422be662e5e562e62beeffc9eaa907d381a9d63a2556615bbda286d"}, + {file = "pydantic_core-2.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4638ebc17de08c2f3acba557efeb6f195c88b7299d8c55c0bb4e20638bbd4d03"}, + {file = "pydantic_core-2.3.0-cp310-none-win32.whl", hash = "sha256:9ff322c7e1030543d35d83bb521b69114d3d150750528d7757544f639def9ad6"}, + {file = "pydantic_core-2.3.0-cp310-none-win_amd64.whl", hash = "sha256:4824eb018f0a4680b1e434697a9bf3f41c7799b80076d06530cbbd212e040ccc"}, + {file = "pydantic_core-2.3.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:0aa429578e23885b3984c49d687cd05ab06f0b908ea1711a8bf7e503b7f97160"}, + {file = "pydantic_core-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20d710c1f79af930b8891bcebd84096798e4387ab64023ef41521d58f21277d3"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f45d4d7481d6f09cb9e35c72caa0e50add4a30bb08c04c5fe5956a0158633"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bcfb7be905aa849bd882262e1df3f75b564e2f708b4b4c7ad2d3deaf5410562"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_armv7l.whl", hash = "sha256:85cd9c0af34e371390e3cb2f3a470b0b40cc07568c1e966c638c49062be6352d"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:37c5028cebdf731298724070838fb3a71ef1fbd201d193d311ac2cbdbca25a23"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_24_s390x.whl", hash = "sha256:e4208f23f12d0ad206a07a489ef4cb15722c10b62774c4460ee4123250be938e"}, + {file = "pydantic_core-2.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c24465dd11b65c8510f251b095fc788c7c91481c81840112fe3f76c30793a455"}, + {file = "pydantic_core-2.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3cd7ee8bbfab277ab56e272221886fd33a1b5943fbf45ae9195aa6a48715a8a0"}, + {file = "pydantic_core-2.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0fc7e0b056b66cc536e97ef60f48b3b289f6b3b62ac225afd4b22a42434617bf"}, + {file = "pydantic_core-2.3.0-cp311-none-win32.whl", hash = "sha256:4788135db4bd83a5edc3522b11544b013be7d25b74b155e08dd3b20cd6663bbb"}, + {file = "pydantic_core-2.3.0-cp311-none-win_amd64.whl", hash = "sha256:f93c867e5e85584a28c6a6feb6f2086d717266eb5d1210d096dd717b7f4dec04"}, + {file = "pydantic_core-2.3.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:73f62bb7fd862d9bcd886e10612bade6fe042eda8b47e8c129892bcfb7b45e84"}, + {file = "pydantic_core-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d889d498fce64bfcd8adf1a78579a7f626f825cbeb2956a24a29b35f9a1df32"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d55e38a89ec2ae17b2fa7ffeda6b70f63afab1888bd0d57aaa7b7879760acb4"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1aefebb506bc1fe355d91d25f12bcdea7f4d7c2d9f0f6716dd025543777c99a5"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_armv7l.whl", hash = "sha256:6441a29f42585f085db0c04cd0557d4cbbb46fa68a0972409b1cfe9f430280c1"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_ppc64le.whl", hash = "sha256:47e8f034be31390a8f525431eb5e803a78ce7e2e11b32abf5361a972e14e6b61"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_24_s390x.whl", hash = "sha256:ad814864aba263be9c83ada44a95f72d10caabbf91589321f95c29c902bdcff0"}, + {file = "pydantic_core-2.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9eff3837d447fccf2ac38c259b14ab9cbde700df355a45a1f3ff244d5e78f8b6"}, + {file = "pydantic_core-2.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:534f3f63c000f08050c6f7f4378bf2b52d7ba9214e9d35e3f60f7ad24a4d6425"}, + {file = "pydantic_core-2.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ef6a222d54f742c24f6b143aab088702db3a827b224e75b9dd28b38597c595fe"}, + {file = "pydantic_core-2.3.0-cp312-none-win32.whl", hash = "sha256:4e26944e64ecc1d7b19db954c0f7b471f3b141ec8e1a9f57cfe27671525cd248"}, + {file = "pydantic_core-2.3.0-cp312-none-win_amd64.whl", hash = "sha256:019c5c41941438570dfc7d3f0ae389b2425add1775a357ce1e83ed1434f943d6"}, + {file = "pydantic_core-2.3.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:0b3d781c71b8bfb621ef23b9c874933e2cd33237c1a65cc20eeb37437f8e7e18"}, + {file = "pydantic_core-2.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad46027dbd5c1db87dc0b49becbe23093b143a20302028d387dae37ee5ef95f5"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39aa09ed7ce2a648c904f79032d16dda29e6913112af8465a7bf710eef23c7ca"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05b4bf8c58409586a7a04c858a86ab10f28c6c1a7c33da65e0326c59d5b0ab16"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_armv7l.whl", hash = "sha256:ba2b807d2b62c446120906b8580cddae1d76d3de4efbb95ccc87f5e35c75b4b2"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:ea955e4ed21f4bbb9b83fea09fc6af0bed82e69ecf6b35ec89237a0a49633033"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_24_s390x.whl", hash = "sha256:06884c07956526ac9ebfef40fe21a11605569b8fc0e2054a375fb39c978bf48f"}, + {file = "pydantic_core-2.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f868e731a18b403b88aa434d960489ceeed0ddeb44ebc02389540731a67705e0"}, + {file = "pydantic_core-2.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cb08fab0fc1db15c277b72e33ac74ad9c0c789413da8984a3eacb22a94b42ef4"}, + {file = "pydantic_core-2.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6ca34c29fbd6592de5fd39e80c1993634d704c4e7e14ba54c87b2c7c53da68fe"}, + {file = "pydantic_core-2.3.0-cp38-none-win32.whl", hash = "sha256:cd782807d35c8a41aaa7d30b5107784420eefd9fdc1c760d86007d43ae00b15d"}, + {file = "pydantic_core-2.3.0-cp38-none-win_amd64.whl", hash = "sha256:01f56d5ee70b1d39c0fd08372cc5142274070ab7181d17c86035f130eebc05b8"}, + {file = "pydantic_core-2.3.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:78b1ac0151271ce62bc2b33755f1043eda6a310373143a2f27e2bcd3d5fc8633"}, + {file = "pydantic_core-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:64bfd2c35a2c350f73ac52dc134d8775f93359c4c969280a6fe5301b5b6e7431"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:937c0fe9538f1212b62df6a68f8d78df3572fe3682d9a0dd8851eac8a4e46063"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d965c7c4b40d1cedec9188782e98bd576f9a04868835604200c3a6e817b824f"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_armv7l.whl", hash = "sha256:ad442b8585ed4a3c2d22e4bf7b465d9b7d281e055b09719a8aeb5b576422dc9b"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:4bf20c9722821fce766e685718e739deeccc60d6bc7be5029281db41f999ee0c"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_24_s390x.whl", hash = "sha256:f3dd5333049b5b3faa739e0f40b77cc8b7a1aded2f2da0e28794c81586d7b08a"}, + {file = "pydantic_core-2.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dc5f516b24d24bc9e8dd9305460899f38302b3c4f9752663b396ef9848557bf"}, + {file = "pydantic_core-2.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:055f7ea6b1fbb37880d66d70eefd22dd319b09c79d2cb99b1dbfeb34b653b0b2"}, + {file = "pydantic_core-2.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:af693a89db6d6ac97dd84dd7769b3f2bd9007b578127d0e7dda03053f4d3b34b"}, + {file = "pydantic_core-2.3.0-cp39-none-win32.whl", hash = "sha256:f60e31e3e15e8c294bf70c60f8ae4d0c3caf3af8f26466e9aa8ea4c01302749b"}, + {file = "pydantic_core-2.3.0-cp39-none-win_amd64.whl", hash = "sha256:2b79f3681481f4424d7845cc7a261d5a4baa810d656b631fa844dc9967b36a7b"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:a666134b41712e30a71afaa26deeb4da374179f769fa49784cdf0e7698880fab"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c119e9227487ad3d7c3c737d896afe548a6be554091f9745da1f4b489c40561"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73929a2fb600a2333fce2efd92596cff5e6bf8946e20e93c067b220760064862"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bbc2678a5b6a19371b2cb51f30ccea71f0c14b26477d2d884fed761cea42c7"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dcbff997f47d45bf028bda4c3036bb3101e89a3df271281d392b6175f71c71d1"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:afa8808159169368b66e4fbeafac6c6fd8f26246dc4d0dcc2caf94bd9cf1b828"}, + {file = "pydantic_core-2.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:12be3b5f54f8111ca38e6b7277f26c23ba5cb3344fae06f879a0a93dfc8b479e"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ed5babdcd3d052ba5cf8832561f18df20778c7ccf12587b2d82f7bf3bf259a0e"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d642e5c029e2acfacf6aa0a7a3e822086b3b777c70d364742561f9ca64c1ffc"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba3073eb38a1294e8c7902989fb80a7a147a69db2396818722bd078476586a0"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5146a6749b1905e04e62e0ad4622f079e5582f8b3abef5fb64516c623127908"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:deeb64335f489c3c11949cbd1d1668b3f1fb2d1c6a5bf40e126ef7bf95f9fa40"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:31acc37288b8e69e4849f618c3d5cf13b58077c1a1ff9ade0b3065ba974cd385"}, + {file = "pydantic_core-2.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e09d9f6d722de9d4c1c5f122ea9bc6b25a05f975457805af4dcab7b0128aacbf"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ba6a8cf089222a171b8f84e6ec2d10f7a9d14f26be3a347b14775a8741810676"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1fd1b24e9bcddcb168437686677104e205c8e25b066e73ffdf331d3bb8792b"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eda1a89c4526826c0a87d33596a4cd15b8f58e9250f503e39af1699ba9c878e8"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3e9a18401a28db4358da2e191508702dbf065f2664c710708cdf9552b9fa50c"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a439fd0d45d51245bbde799726adda5bd18aed3fa2b01ab2e6a64d6d13776fa3"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:bf6a1d2c920cc9528e884850a4b2ee7629e3d362d5c44c66526d4097bbb07a1a"}, + {file = "pydantic_core-2.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e33fcbea3b63a339dd94de0fc442fefacfe681cc7027ce63f67af9f7ceec7422"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:bf3ed993bdf4754909f175ff348cf8f78d4451215b8aa338633f149ca3b1f37a"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7584171eb3115acd4aba699bc836634783f5bd5aab131e88d8eeb8a3328a4a72"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1624baa76d1740711b2048f302ae9a6d73d277c55a8c3e88b53b773ebf73a971"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:06f33f695527f5a86e090f208978f9fd252c9cfc7e869d3b679bd71f7cb2c1fa"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7ecf0a67b212900e92f328181fed02840d74ed39553cdb38d27314e2b9c89dfa"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:45fa1e8ad6f4367ad73674ca560da8e827cc890eaf371f3ee063d6d7366a207b"}, + {file = "pydantic_core-2.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8d0dbcc57839831ae79fd24b1b83d42bc9448d79feaf3ed3fb5cbf94ffbf3eb7"}, + {file = "pydantic_core-2.3.0.tar.gz", hash = "sha256:5cfb5ac4e82c47d5dc25b209dd4c3989e284b80109f9e08b33c895080c424b4f"}, +] + +[[package]] +name = "pygments" +version = "2.16.1" +requires_python = ">=3.7" +summary = "Pygments is a syntax highlighting package written in Python." +files = [ + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, +] + +[[package]] +name = "pygtrie" +version = "2.5.0" +summary = "A pure Python trie data structure implementation." +files = [ + {file = "pygtrie-2.5.0-py3-none-any.whl", hash = "sha256:8795cda8105493d5ae159a5bef313ff13156c5d4d72feddefacaad59f8c8ce16"}, + {file = "pygtrie-2.5.0.tar.gz", hash = "sha256:203514ad826eb403dab1d2e2ddd034e0d1534bbe4dbe0213bb0593f66beba4e2"}, +] + +[[package]] +name = "pyjwt" +version = "2.8.0" +requires_python = ">=3.7" +summary = "JSON Web Token implementation in Python" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[[package]] +name = "pyjwt" +version = "2.8.0" +extras = ["crypto"] +requires_python = ">=3.7" +summary = "JSON Web Token implementation in Python" +dependencies = [ + "PyJWT==2.8.0", + "cryptography>=3.4.0", +] +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[[package]] +name = "pyparsing" +version = "3.1.1" +requires_python = ">=3.6.8" +summary = "pyparsing module - Classes and methods to define and execute parsing grammars" +files = [ + {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"}, + {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"}, +] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +dependencies = [ + "six>=1.5", +] +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]] +name = "python-ulid" +version = "1.1.0" +requires_python = ">=3.7" +summary = "Universally Unique Lexicographically Sortable Identifier" +files = [ + {file = "python-ulid-1.1.0.tar.gz", hash = "sha256:5fb5e4a91db8ca93e8938a613360b3def299b60d41f847279a8c39c9b2e9c65e"}, + {file = "python_ulid-1.1.0-py3-none-any.whl", hash = "sha256:88c952f6be133dbede19c907d72d26717d2691ec8421512b573144794d891e24"}, +] + +[[package]] +name = "pytz" +version = "2023.3.post1" +summary = "World timezone definitions, modern and historical" +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 = "redis" +version = "4.6.0" +requires_python = ">=3.7" +summary = "Python client for Redis database and key-value store" +dependencies = [ + "async-timeout>=4.0.2; python_full_version <= \"3.11.2\"", +] +files = [ + {file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"}, + {file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"}, +] + +[[package]] +name = "redis-om" +version = "0.2.1" +requires_python = ">=3.7,<4.0" +summary = "Object mappings, and more, for Redis." +dependencies = [ + "click<9.0.0,>=8.0.1", + "hiredis<3.0.0,>=2.2.3", + "more-itertools<10.0,>=8.14", + "pydantic<2.1.0,>=1.10.2", + "python-ulid<2.0.0,>=1.0.3", + "redis<5.0.0,>=3.5.3", + "types-redis<5.0.0,>=3.5.9", + "typing-extensions<5.0.0,>=4.4.0", +] +files = [ + {file = "redis_om-0.2.1-py3-none-any.whl", hash = "sha256:31313a3027a014608b3a4d44ecd1d3000c7d0fe3a25060db19b42225e636cd53"}, + {file = "redis_om-0.2.1.tar.gz", hash = "sha256:150c9cb5238d6003f35e9b6394aab30a0df35b00e955eb7dc508f4345e0a0ccc"}, +] + +[[package]] +name = "regex" +version = "2023.10.3" +requires_python = ">=3.7" +summary = "Alternative regular expression module, to replace re." +files = [ + {file = "regex-2023.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c34d4f73ea738223a094d8e0ffd6d2c1a1b4c175da34d6b0de3d8d69bee6bcc"}, + {file = "regex-2023.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8f4e49fc3ce020f65411432183e6775f24e02dff617281094ba6ab079ef0915"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cd1bccf99d3ef1ab6ba835308ad85be040e6a11b0977ef7ea8c8005f01a3c29"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81dce2ddc9f6e8f543d94b05d56e70d03a0774d32f6cca53e978dc01e4fc75b8"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c6b4d23c04831e3ab61717a707a5d763b300213db49ca680edf8bf13ab5d91b"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c15ad0aee158a15e17e0495e1e18741573d04eb6da06d8b84af726cfc1ed02ee"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6239d4e2e0b52c8bd38c51b760cd870069f0bdf99700a62cd509d7a031749a55"}, + {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4a8bf76e3182797c6b1afa5b822d1d5802ff30284abe4599e1247be4fd6b03be"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9c727bbcf0065cbb20f39d2b4f932f8fa1631c3e01fcedc979bd4f51fe051c5"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3ccf2716add72f80714b9a63899b67fa711b654be3fcdd34fa391d2d274ce767"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:107ac60d1bfdc3edb53be75e2a52aff7481b92817cfdddd9b4519ccf0e54a6ff"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:00ba3c9818e33f1fa974693fb55d24cdc8ebafcb2e4207680669d8f8d7cca79a"}, + {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0a47efb1dbef13af9c9a54a94a0b814902e547b7f21acb29434504d18f36e3a"}, + {file = "regex-2023.10.3-cp310-cp310-win32.whl", hash = "sha256:36362386b813fa6c9146da6149a001b7bd063dabc4d49522a1f7aa65b725c7ec"}, + {file = "regex-2023.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:c65a3b5330b54103e7d21cac3f6bf3900d46f6d50138d73343d9e5b2900b2353"}, + {file = "regex-2023.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90a79bce019c442604662d17bf69df99090e24cdc6ad95b18b6725c2988a490e"}, + {file = "regex-2023.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c7964c2183c3e6cce3f497e3a9f49d182e969f2dc3aeeadfa18945ff7bdd7051"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef80829117a8061f974b2fda8ec799717242353bff55f8a29411794d635d964"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5addc9d0209a9afca5fc070f93b726bf7003bd63a427f65ef797a931782e7edc"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c148bec483cc4b421562b4bcedb8e28a3b84fcc8f0aa4418e10898f3c2c0eb9b"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d1f21af4c1539051049796a0f50aa342f9a27cde57318f2fc41ed50b0dbc4ac"}, + {file = "regex-2023.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b9ac09853b2a3e0d0082104036579809679e7715671cfbf89d83c1cb2a30f58"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ebedc192abbc7fd13c5ee800e83a6df252bec691eb2c4bedc9f8b2e2903f5e2a"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d8a993c0a0ffd5f2d3bda23d0cd75e7086736f8f8268de8a82fbc4bd0ac6791e"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:be6b7b8d42d3090b6c80793524fa66c57ad7ee3fe9722b258aec6d0672543fd0"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4023e2efc35a30e66e938de5aef42b520c20e7eda7bb5fb12c35e5d09a4c43f6"}, + {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d47840dc05e0ba04fe2e26f15126de7c755496d5a8aae4a08bda4dd8d646c54"}, + {file = "regex-2023.10.3-cp311-cp311-win32.whl", hash = "sha256:9145f092b5d1977ec8c0ab46e7b3381b2fd069957b9862a43bd383e5c01d18c2"}, + {file = "regex-2023.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:b6104f9a46bd8743e4f738afef69b153c4b8b592d35ae46db07fc28ae3d5fb7c"}, + {file = "regex-2023.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff507ae210371d4b1fe316d03433ac099f184d570a1a611e541923f78f05037"}, + {file = "regex-2023.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be5e22bbb67924dea15039c3282fa4cc6cdfbe0cbbd1c0515f9223186fc2ec5f"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a992f702c9be9c72fa46f01ca6e18d131906a7180950958f766c2aa294d4b41"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7434a61b158be563c1362d9071358f8ab91b8d928728cd2882af060481244c9e"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2169b2dcabf4e608416f7f9468737583ce5f0a6e8677c4efbf795ce81109d7c"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9e908ef5889cda4de038892b9accc36d33d72fb3e12c747e2799a0e806ec841"}, + {file = "regex-2023.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12bd4bc2c632742c7ce20db48e0d99afdc05e03f0b4c1af90542e05b809a03d9"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc72c231f5449d86d6c7d9cc7cd819b6eb30134bb770b8cfdc0765e48ef9c420"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bce8814b076f0ce5766dc87d5a056b0e9437b8e0cd351b9a6c4e1134a7dfbda9"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ba7cd6dc4d585ea544c1412019921570ebd8a597fabf475acc4528210d7c4a6f"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b0c7d2f698e83f15228ba41c135501cfe7d5740181d5903e250e47f617eb4292"}, + {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5a8f91c64f390ecee09ff793319f30a0f32492e99f5dc1c72bc361f23ccd0a9a"}, + {file = "regex-2023.10.3-cp312-cp312-win32.whl", hash = "sha256:ad08a69728ff3c79866d729b095872afe1e0557251da4abb2c5faff15a91d19a"}, + {file = "regex-2023.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:39cdf8d141d6d44e8d5a12a8569d5a227f645c87df4f92179bd06e2e2705e76b"}, + {file = "regex-2023.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b98b7681a9437262947f41c7fac567c7e1f6eddd94b0483596d320092004533"}, + {file = "regex-2023.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:91dc1d531f80c862441d7b66c4505cd6ea9d312f01fb2f4654f40c6fdf5cc37a"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82fcc1f1cc3ff1ab8a57ba619b149b907072e750815c5ba63e7aa2e1163384a4"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7979b834ec7a33aafae34a90aad9f914c41fd6eaa8474e66953f3f6f7cbd4368"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef71561f82a89af6cfcbee47f0fabfdb6e63788a9258e913955d89fdd96902ab"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd829712de97753367153ed84f2de752b86cd1f7a88b55a3a775eb52eafe8a94"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00e871d83a45eee2f8688d7e6849609c2ca2a04a6d48fba3dff4deef35d14f07"}, + {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:706e7b739fdd17cb89e1fbf712d9dc21311fc2333f6d435eac2d4ee81985098c"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cc3f1c053b73f20c7ad88b0d1d23be7e7b3901229ce89f5000a8399746a6e039"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f85739e80d13644b981a88f529d79c5bdf646b460ba190bffcaf6d57b2a9863"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:741ba2f511cc9626b7561a440f87d658aabb3d6b744a86a3c025f866b4d19e7f"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e77c90ab5997e85901da85131fd36acd0ed2221368199b65f0d11bca44549711"}, + {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:979c24cbefaf2420c4e377ecd1f165ea08cc3d1fbb44bdc51bccbbf7c66a2cb4"}, + {file = "regex-2023.10.3-cp38-cp38-win32.whl", hash = "sha256:58837f9d221744d4c92d2cf7201c6acd19623b50c643b56992cbd2b745485d3d"}, + {file = "regex-2023.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:c55853684fe08d4897c37dfc5faeff70607a5f1806c8be148f1695be4a63414b"}, + {file = "regex-2023.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c54e23836650bdf2c18222c87f6f840d4943944146ca479858404fedeb9f9af"}, + {file = "regex-2023.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69c0771ca5653c7d4b65203cbfc5e66db9375f1078689459fe196fe08b7b4930"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ac965a998e1388e6ff2e9781f499ad1eaa41e962a40d11c7823c9952c77123e"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c0e8fae5b27caa34177bdfa5a960c46ff2f78ee2d45c6db15ae3f64ecadde14"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c56c3d47da04f921b73ff9415fbaa939f684d47293f071aa9cbb13c94afc17d"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef1e014eed78ab650bef9a6a9cbe50b052c0aebe553fb2881e0453717573f52"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d29338556a59423d9ff7b6eb0cb89ead2b0875e08fe522f3e068b955c3e7b59b"}, + {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9c6d0ced3c06d0f183b73d3c5920727268d2201aa0fe6d55c60d68c792ff3588"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:994645a46c6a740ee8ce8df7911d4aee458d9b1bc5639bc968226763d07f00fa"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:66e2fe786ef28da2b28e222c89502b2af984858091675044d93cb50e6f46d7af"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:11175910f62b2b8c055f2b089e0fedd694fe2be3941b3e2633653bc51064c528"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:06e9abc0e4c9ab4779c74ad99c3fc10d3967d03114449acc2c2762ad4472b8ca"}, + {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fb02e4257376ae25c6dd95a5aec377f9b18c09be6ebdefa7ad209b9137b73d48"}, + {file = "regex-2023.10.3-cp39-cp39-win32.whl", hash = "sha256:3b2c3502603fab52d7619b882c25a6850b766ebd1b18de3df23b2f939360e1bd"}, + {file = "regex-2023.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:adbccd17dcaff65704c856bd29951c58a1bd4b2b0f8ad6b826dbd543fe740988"}, + {file = "regex-2023.10.3.tar.gz", hash = "sha256:3fef4f844d2290ee0ba57addcec17eec9e3df73f10a2748485dfd6a3a188cc0f"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +requires_python = ">=3.7" +summary = "Python HTTP for Humans." +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[[package]] +name = "rich" +version = "13.6.0" +requires_python = ">=3.7.0" +summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +dependencies = [ + "markdown-it-py>=2.2.0", + "pygments<3.0.0,>=2.13.0", + "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", +] +files = [ + {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, + {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, +] + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +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 = "tiktoken" +version = "0.5.1" +requires_python = ">=3.8" +summary = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +dependencies = [ + "regex>=2022.1.18", + "requests>=2.26.0", +] +files = [ + {file = "tiktoken-0.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2b0bae3fd56de1c0a5874fb6577667a3c75bf231a6cef599338820210c16e40a"}, + {file = "tiktoken-0.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e529578d017045e2f0ed12d2e00e7e99f780f477234da4aae799ec4afca89f37"}, + {file = "tiktoken-0.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edd2ffbb789712d83fee19ab009949f998a35c51ad9f9beb39109357416344ff"}, + {file = "tiktoken-0.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4c73d47bdc1a3f1f66ffa019af0386c48effdc6e8797e5e76875f6388ff72e9"}, + {file = "tiktoken-0.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46b8554b9f351561b1989157c6bb54462056f3d44e43aa4e671367c5d62535fc"}, + {file = "tiktoken-0.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92ed3bbf71a175a6a4e5fbfcdb2c422bdd72d9b20407e00f435cf22a68b4ea9b"}, + {file = "tiktoken-0.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:714efb2f4a082635d9f5afe0bf7e62989b72b65ac52f004eb7ac939f506c03a4"}, + {file = "tiktoken-0.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a10488d1d1a5f9c9d2b2052fdb4cf807bba545818cb1ef724a7f5d44d9f7c3d4"}, + {file = "tiktoken-0.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8079ac065572fe0e7c696dbd63e1fdc12ce4cdca9933935d038689d4732451df"}, + {file = "tiktoken-0.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ef730db4097f5b13df8d960f7fdda2744fe21d203ea2bb80c120bb58661b155"}, + {file = "tiktoken-0.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:426e7def5f3f23645dada816be119fa61e587dfb4755de250e136b47a045c365"}, + {file = "tiktoken-0.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:323cec0031358bc09aa965c2c5c1f9f59baf76e5b17e62dcc06d1bb9bc3a3c7c"}, + {file = "tiktoken-0.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5abd9436f02e2c8eda5cce2ff8015ce91f33e782a7423de2a1859f772928f714"}, + {file = "tiktoken-0.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:1fe99953b63aabc0c9536fbc91c3c9000d78e4755edc28cc2e10825372046a2d"}, + {file = "tiktoken-0.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dcdc630461927718b317e6f8be7707bd0fc768cee1fdc78ddaa1e93f4dc6b2b1"}, + {file = "tiktoken-0.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1f2b3b253e22322b7f53a111e1f6d7ecfa199b4f08f3efdeb0480f4033b5cdc6"}, + {file = "tiktoken-0.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43ce0199f315776dec3ea7bf86f35df86d24b6fcde1babd3e53c38f17352442f"}, + {file = "tiktoken-0.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a84657c083d458593c0235926b5c993eec0b586a2508d6a2020556e5347c2f0d"}, + {file = "tiktoken-0.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c008375c0f3d97c36e81725308699116cd5804fdac0f9b7afc732056329d2790"}, + {file = "tiktoken-0.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:779c4dea5edd1d3178734d144d32231e0b814976bec1ec09636d1003ffe4725f"}, + {file = "tiktoken-0.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:b5dcfcf9bfb798e86fbce76d40a1d5d9e3f92131aecfa3d1e5c9ea1a20f1ef1a"}, + {file = "tiktoken-0.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b180a22db0bbcc447f691ffc3cf7a580e9e0587d87379e35e58b826ebf5bc7b"}, + {file = "tiktoken-0.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b756a65d98b7cf760617a6b68762a23ab8b6ef79922be5afdb00f5e8a9f4e76"}, + {file = "tiktoken-0.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba9873c253ca1f670e662192a0afcb72b41e0ba3e730f16c665099e12f4dac2d"}, + {file = "tiktoken-0.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74c90d2be0b4c1a2b3f7dde95cd976757817d4df080d6af0ee8d461568c2e2ad"}, + {file = "tiktoken-0.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:709a5220891f2b56caad8327fab86281787704931ed484d9548f65598dea9ce4"}, + {file = "tiktoken-0.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5d5a187ff9c786fae6aadd49f47f019ff19e99071dc5b0fe91bfecc94d37c686"}, + {file = "tiktoken-0.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:e21840043dbe2e280e99ad41951c00eff8ee3b63daf57cd4c1508a3fd8583ea2"}, + {file = "tiktoken-0.5.1.tar.gz", hash = "sha256:27e773564232004f4f810fd1f85236673ec3a56ed7f1206fc9ed8670ebedb97a"}, +] + +[[package]] +name = "tqdm" +version = "4.66.1" +requires_python = ">=3.7" +summary = "Fast, Extensible Progress Meter" +dependencies = [ + "colorama; platform_system == \"Windows\"", +] +files = [ + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, +] + +[[package]] +name = "types-pyopenssl" +version = "23.2.0.2" +summary = "Typing stubs for pyOpenSSL" +dependencies = [ + "cryptography>=35.0.0", +] +files = [ + {file = "types-pyOpenSSL-23.2.0.2.tar.gz", hash = "sha256:6a010dac9ecd42b582d7dd2cc3e9e40486b79b3b64bb2fffba1474ff96af906d"}, + {file = "types_pyOpenSSL-23.2.0.2-py3-none-any.whl", hash = "sha256:19536aa3debfbe25a918cf0d898e9f5fbbe6f3594a429da7914bf331deb1b342"}, +] + +[[package]] +name = "types-redis" +version = "4.6.0.7" +summary = "Typing stubs for redis" +dependencies = [ + "cryptography>=35.0.0", + "types-pyOpenSSL", +] +files = [ + {file = "types-redis-4.6.0.7.tar.gz", hash = "sha256:28c4153ddb5c9d4f10def44a2454673c361d2d5fc3cd867cf3bb1520f3f59a38"}, + {file = "types_redis-4.6.0.7-py3-none-any.whl", hash = "sha256:05b1bf92879b25df20433fa1af07784a0d7928c616dc2ebf9087618db77ccbd0"}, +] + +[[package]] +name = "typing-extensions" +version = "4.8.0" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +files = [ + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, +] + +[[package]] +name = "tzdata" +version = "2023.3" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + +[[package]] +name = "urllib3" +version = "2.0.7" +requires_python = ">=3.7" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +files = [ + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, +] + +[[package]] +name = "yarl" +version = "1.9.2" +requires_python = ">=3.7" +summary = "Yet another URL library" +dependencies = [ + "idna>=2.0", + "multidict>=4.0", +] +files = [ + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, + {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, + {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, + {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, + {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, + {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, + {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, + {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, + {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, + {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..06219952 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +[project] +name = "adala" +version = "0.0.1" +description = "Adala: Autonomous Data Labeling Agent" +authors = [ + {name = "Human Signal", email = "hello@humansignal.com"}, +] +dependencies = [ + "pandas", + "openai", + "guidance", + "pydantic>=2", + "rich>=13", + "redis-om", +] +requires-python = ">=3.8.8" +readme = "README.md" +# [project.optional-dependencies] +# label-studio = [ +# "label-studio-sdk @ git+https://github.com/HumanSignal/label-studio-sdk.git@pd-support", +# ] +# jupyter = [ +# "jupyter", +# ] +# docs = [ +# "sphinx>=7.1.2", +# "sphinx-rtd-theme>=1.3.0", +# "myst-parser>=2.0.0", +# ] + +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..620d7ee1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +pandas +openai +guidance +label-studio-sdk @ git+https://github.com/HumanSignal/label-studio-sdk.git@pd-support +rich~=13.6 +pydantic +redis-om diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..0cd185b4 --- /dev/null +++ b/setup.py @@ -0,0 +1,28 @@ +"""This file and its contents are licensed under the Apache License 2.0. Please see the included NOTICE for copyright information and LICENSE for a copy of the license. +""" +import re +import setuptools + +# Module dependencies +requirements, dependency_links = [], [] +with open('requirements.txt') as f: + for line in f.read().splitlines(): + requirements.append(line) + +setuptools.setup( + name='adala', + version='0.0.1', + author='Heartex', + author_email="hello@humansignal.com", + description='ADALA: Automated Data Labeling Agent', + url='https://github.com/HumanSignal/ADALA', + packages=setuptools.find_packages(), + include_package_data=True, + classifiers=[ + 'Programming Language :: Python :: 3', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + ], + python_requires='>=3.8', + install_requires=requirements +) diff --git a/tests/requirements-test.txt b/tests/requirements-test.txt new file mode 100644 index 00000000..c63eb541 --- /dev/null +++ b/tests/requirements-test.txt @@ -0,0 +1,5 @@ +pytest==6.2.5 +pytest-cov==3.0.0 +pytest-env==0.6.2 +# fakeredis==1.5.0 +# pytest-xdist \ No newline at end of file diff --git a/tests/test_classification.py b/tests/test_classification.py new file mode 100644 index 00000000..92a2b1e1 --- /dev/null +++ b/tests/test_classification.py @@ -0,0 +1,89 @@ +import pandas as pd +from unittest.mock import MagicMock, patch +from adala.runtimes.openai import OpenAIRuntime + +from adala.agents import Agent +from adala.datasets import DataFrameDataset +from adala.environments import BasicEnvironment +from adala.skills import ClassificationSkill +from adala.utils.logs import print_dataframe + + +def process_record_generator(*args, **kwargs): + # train + for i in range(3): + # predictions for gt comparison + yield {'sentiment': 'Neutral' if i < 2 else 'Positive'} + yield {'sentiment': 'Neutral' if i < 2 else 'Negative'} + yield {'sentiment': 'Neutral'} + + # errors + yield {'reason': 'Test reason'} + yield {'reason': 'Test reason'} + yield {'reason': 'Test reason'} + yield {'reason': 'Test reason'} + + # instruction generation + yield {'new_instruction': 'Test instruction'} + + # test + yield {'sentiment': 'Positive'} + yield {'sentiment': 'Negative'} + yield {'sentiment': 'Neutral'} + + +@patch.object(OpenAIRuntime, '_check_api_key', return_value=None) +@patch.object(OpenAIRuntime, '_check_model_availability', return_value=None) +@patch.object(OpenAIRuntime, '_process_record', side_effect=process_record_generator()) +def test_classification_skill( + mock_check_api_key, + mock_check_model_availability, + mock_process_record +): + print("=> Initialize datasets ...") + + # Train dataset + train_df = pd.DataFrame([ + ["It was the negative first impressions, and then it started working.", "Positive"], + ["Not loud enough and doesn't turn on like it should.", "Negative"], + ["I don't know what to say.", "Neutral"], + ], columns=["text", "ground_truth"]) + + # Test dataset + test_df = pd.DataFrame([ + "All three broke within two months of use.", + "The device worked for a long time, can't say anything bad.", + "Just a random line of text.", + ], columns=["text"]) + + train_dataset = DataFrameDataset(df=train_df) + test_dataset = DataFrameDataset(df=test_df) + + print("=> Initialize and train ADALA agent ...") + agent = Agent( + # connect to a dataset + environment=BasicEnvironment( + ground_truth_dataset=train_dataset, + ground_truth_column="ground_truth" + ), + # define a skill + skills=ClassificationSkill( + name='sentiment', + instructions="Label text as subjective or objective.", + labels=["Positive", "Negative", "Neutral"], + input_data_field='text' + ), + ) + run = agent.learn(learning_iterations=3, accuracy_threshold=0.95) + assert run.accuracy > 0.8 + + print('\n\n=> Final instructions:') + print('=====================') + print(f'{run.updated_instructions}') + print('=====================') + + print('\n=> Run test ...') + run = agent.apply_skills(test_dataset) + print_dataframe(run.predictions) + + assert not run.predictions.empty