├── .env.example ├── .github ├── ISSUE_TEMPLATE │ ├── 001-report-bug.yml │ ├── 002-feature-request.yml │ └── 003-misc-discussion.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTE.md ├── LICENSE ├── README.md ├── pyopenagi ├── README.md ├── __init__.py ├── agents │ ├── README.md │ ├── __init__.py │ ├── agent_factory.py │ ├── agent_process.py │ ├── base_agent.py │ ├── call_core.py │ ├── example │ │ ├── README.md │ │ ├── academic_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── cocktail_mixlogist │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── cook_therapist │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── creation_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── fashion_stylist │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── festival_card_designer │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── fitness_trainer │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── game_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── interior_decorator │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── language_tutor │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── logo_creator │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── math_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── meme_creator │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── music_composer │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── plant_care_assistant │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── rag_agent │ │ │ ├── README.md │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ ├── data │ │ │ │ └── paul_graham │ │ │ │ │ └── paul_graham_essay.txt │ │ │ └── meta_requirements.txt │ │ ├── rec_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── story_teller │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── tech_support_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── transcribe_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ ├── travel_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ │ └── travel_planner_agent │ │ │ ├── README.md │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ ├── meta_requirements.txt │ │ │ └── prompts.py │ ├── interact.py │ ├── om-raheja │ │ └── transcribe_agent │ │ │ ├── agent.py │ │ │ ├── config.json │ │ │ └── meta_requirements.txt │ └── react_agent.py ├── data │ └── agent_tasks │ │ ├── README.md │ │ └── example │ │ ├── academic_agent_task.txt │ │ ├── cocktail_mixlogist_task.txt │ │ ├── cook_therapist_task.txt │ │ ├── creation_agent_task.txt │ │ ├── fashion_stylist_task.txt │ │ ├── festival_card_designer_task.txt │ │ ├── fitness_trainer_task.txt │ │ ├── game_agent_task.txt │ │ ├── interior_decorator_task.txt │ │ ├── language_tutor_task.txt │ │ ├── logo_creator_task.txt │ │ ├── math_agent_task.txt │ │ ├── meme_creator_task.txt │ │ ├── music_composer_task.txt │ │ ├── plant_care_assistant_task.txt │ │ ├── rec_agent_task.txt │ │ ├── story_teller_task.txt │ │ ├── tech_support_agent_task.txt │ │ ├── travel_agent_task.txt │ │ └── travel_planner_agent_task.txt ├── manager │ └── manager.py ├── queues │ ├── README.md │ ├── base_queue.py │ └── llm_request_queue.py ├── tools │ ├── README.md │ ├── __init__.py │ ├── arxiv │ │ └── arxiv.py │ ├── base.py │ ├── bing │ │ └── bing_search.py │ ├── currency_converter │ │ └── currency_converter.py │ ├── google │ │ ├── google_places.py │ │ └── google_search.py │ ├── imdb │ │ ├── top_movie.py │ │ ├── top_movies.py │ │ └── top_series.py │ ├── impira │ │ └── doc_question_answering.py │ ├── meteosource_weather │ │ └── find_place.py │ ├── moonphase │ │ └── moon_phase_search.py │ ├── openai │ │ └── speech_to_text.py │ ├── shazam │ │ └── song_auto_complete.py │ ├── stability-ai │ │ ├── sdxl_turbo.py │ │ └── text_to_image.py │ ├── suno │ │ └── text_to_speech.py │ ├── timbrooks │ │ └── image_to_image.py │ ├── transcriber │ │ └── transcriber.py │ ├── travel_planner │ │ ├── accommodations.py │ │ ├── attractions.py │ │ ├── cities.py │ │ ├── flights.py │ │ ├── google_distance_matrix.py │ │ ├── notebook.py │ │ ├── planner.py │ │ └── restaurants.py │ ├── trip_advisor │ │ ├── airport_search.py │ │ ├── flight_search.py │ │ ├── get_hotel_details.py │ │ ├── get_restaurant_details.py │ │ ├── hotel_location_search.py │ │ ├── hotel_search.py │ │ ├── restaurant_location_search.py │ │ └── restaurant_search.py │ ├── wikipedia │ │ └── wikipedia.py │ ├── wolfram │ │ └── wolfram_alpha.py │ └── words_api │ │ └── words_api.py └── utils │ ├── README.md │ ├── __init__.py │ ├── chat_template.py │ ├── commands │ └── top.py │ ├── compressor.py │ ├── logger.py │ └── utils.py ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── tests ├── README.md ├── __init__.py ├── test_agent_creation.py └── test_tools │ ├── README.md │ ├── test_currency_converter.py │ ├── test_top_series.py │ ├── test_wolfram_alpha.py │ └── test_words_api.py └── tools.md /.env.example: -------------------------------------------------------------------------------- 1 | RAPID_API_KEY='' 2 | WOLFRAM_ALPHA_APPID='' -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/001-report-bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Raise an issue here if you find a bug. 3 | title: "[Bug] " 4 | labels: ["bug"] 5 | 6 | body: 7 | - type: checkboxes 8 | attributes: 9 | label: Checked other resources 10 | description: Please confirm and check all the following options. 11 | options: 12 | - label: I added a very descriptive title to this issue. 13 | required: true 14 | - label: I am sure the issue hasn't been already addressed by searching through https://github.com/agiresearch/OpenAGI/issues. 15 | required: true 16 | - label: The usage issue is not resolved by updating to the latest stable version in the main branch. 17 | required: true 18 | validations: 19 | required: true 20 | - type: textarea 21 | attributes: 22 | label: Describe your current environment 23 | description: | 24 | Your current environment information (including OS, GPU, Cuda-version) 25 | - type: textarea 26 | attributes: 27 | label: Describe the bug 28 | description: | 29 | Please provide a clear and concise description of what the bug is. 30 | If relevant, add a minimal example so that we can reproduce the error by running the code. 31 | validations: 32 | required: true 33 | - type: markdown 34 | attributes: 35 | value: > 36 | Thanks for contributing 🎉! 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/002-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Submit a proposal/request for a new aios feature 3 | title: "[Feature] " 4 | labels: ["feature request"] 5 | 6 | body: 7 | - type: checkboxes 8 | attributes: 9 | label: Checked other resources 10 | description: Please confirm and check all the following options. 11 | options: 12 | - label: I added a very descriptive title to this issue. 13 | required: true 14 | - label: I am sure the issue hasn't been already addressed by searching through https://github.com/agiresearch/OpenAGI/issues. 15 | required: true 16 | - type: textarea 17 | attributes: 18 | label: The feature, motivation and pitch 19 | description: > 20 | A clear and concise description of the feature proposal. Please outline the motivation for the proposal. Is your feature request related to a specific problem? e.g., *"I'm working on X and would like Y to be possible"*. If this is related to another GitHub issue, please link here too. 21 | validations: 22 | required: true 23 | - type: textarea 24 | attributes: 25 | label: Alternatives 26 | description: > 27 | A description of any alternative solutions or features you've considered, if any. 28 | - type: textarea 29 | attributes: 30 | label: Additional context 31 | description: > 32 | Add any other context or screenshots about the feature request. 33 | - type: markdown 34 | attributes: 35 | value: > 36 | Thanks for contributing 🎉! 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/003-misc-discussion.yml: -------------------------------------------------------------------------------- 1 | name: Misc/random discussions that do not fit into the above categories. 2 | description: Submit a discussion as you like. Note that developers are heavily overloaded and we mainly rely on community users to answer these issues. 3 | title: "[Misc] " 4 | labels: ["misc discussion"] 5 | 6 | body: 7 | - type: textarea 8 | attributes: 9 | label: Anything you want to discuss about aios. 10 | description: > 11 | Anything you want to discuss about aios. 12 | validations: 13 | required: true 14 | - type: markdown 15 | attributes: 16 | value: > 17 | Thanks for contributing 🎉! 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for your contribution to OpenAGI! 2 | Before submitting the pull request, please ensure **the PR meets the following criteria**! This helps improve the efficiency of the review process. 3 | 4 | ### Code Quality 5 | Before submitting your PR, you need to follow the steps below to maintain code quality. 6 | - Use `pip install -r requirements-dev.txt` to install the extra dependencies in requirements-dev.txt for the following checks. 7 | - Use `pre-commit install` to install pre-commit locally before you commit messages. The pre-commit can help correct the style that are added or modified. 8 | - Use `pytest -v tests/` to run the test code and make sure it passes all the checks. 9 | 10 | ### PR title and classification 11 | Only specific types of PRs will be reviewed. The PR title is prefixed appropriately (i.e., "prefix: description") to indicate the type of change. Please use one of the prefixs as below: 12 | - `feat` Add new features 13 | - `fix` Fix bugs 14 | - `docs` Modify documents like README, CONTRIBUTE 15 | - `style` Modify code format like space and comma without changing code logic 16 | - `refactor` Refactor code structure without adding new features or fixing new bugs 17 | - `perf` Improve performance or user experience 18 | - `test` Test features, including unit test and integration test 19 | - `chore` Change the build procedure or add dependencies 20 | - `revert` Revert to the previous version 21 | 22 | ### PR messages 23 | - **Description:** a description of the change 24 | - **Issue:** the issue # it fixes, if applicable 25 | - **Dependencies:** any dependencies required for this change 26 | 27 | ### For the Reviews 28 | - After the PR is submitted, the PR will be assigned to a reviewer. Every reviewer will pick up the PRs based on their expertise and availability. 29 | - If no one reviews your PR within a few days, please @-mention one of [dongyuanjushi](https://github.com/dongyuanjushi/), [evison](https://github.com/evison), [Wenyueh](https://github.com/Wenyueh), [BRama10](https://github.com/BRama10). 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | [push,pull_request] 5 | 6 | jobs: 7 | run-pytest: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Set up Python 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: "3.11" 17 | 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | pip install -r requirements-dev.txt 22 | 23 | - name: Run tests 24 | run: | 25 | pytest -v tests 26 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | environment: release 13 | permissions: 14 | id-token: write 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up Python 19 | uses: actions/setup-python@v4 20 | with: 21 | python-version: "3.11" 22 | cache: "pip" 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install hatch 27 | - name: Build package 28 | run: hatch build 29 | - name: Publish package distributions to PyPI 30 | uses: pypa/gh-action-pypi-publish@release/v1 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | 4 | .DS_Store 5 | 6 | # Ignore Python compiled files 7 | __pycache__/ 8 | *.py[cod] 9 | 10 | # Ignore Mac system files 11 | .DS_Store 12 | 13 | # Ignore package info 14 | *.egg-info/ 15 | 16 | 17 | *.ipynb 18 | 19 | api_key.py 20 | 21 | generated_images/ 22 | 23 | generated_poems/ 24 | 25 | generated_music/ 26 | 27 | ./UI/.env*.local 28 | .env 29 | 30 | chroma_db/ 31 | 32 | dist/ 33 | /dist/ 34 | dist -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v3.2.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | - id: check-added-large-files 9 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # How to contribute to OpenAGI 2 | Thank you for your interest in OpenAGI! 3 | Here's a guide to help you contribute to this project. 4 | 5 | ## 1. Get Started 6 | ### Fork the repository 7 | 8 | At first, you need to fork this copy and create your own version of repo. 9 | 10 | ### Clone the repository and install the dependencies. 11 | 12 | ### Installing dependencies with pip 13 | ```bash 14 | pip install -r requirements.txt 15 | ``` 16 | 17 | ### Installing pre-commit 18 | We strongly recommend installing [pre-commit](https://pre-commit.com/) to ensure proper formatting during development 19 | 20 | ## 2. Developing and Testing 21 | ### Create a branch 22 | 23 | Create a new branch for developing your creative features 24 | 25 | ```shell 26 | git checkout -b your-feature 27 | ``` 28 | 29 | ### Make changes and testing 30 | 31 | You can develop new features and then you need to make sure everything works as expected. Run our provided tests and make sure the existing ones go well. Your new tests are encouraged. 32 | 33 | ### Run tests 34 | 35 | Add your test code into the `openagi/tests/` directory if any, then run test via [pytest](https://docs.pytest.org/en/8.0.x/) 36 | 37 | ``` 38 | cd openagi 39 | pytest -v tests 40 | ``` 41 | sample output 42 | ``` 43 | ============================================================================================================================= test session starts ============================================================================================================================== 44 | platform darwin -- Python 3.11.9, pytest-8.1.1, pluggy-1.5.0 -- "" 45 | cachedir: .pytest_cache 46 | rootdir: "" 47 | plugins: anyio-4.3.0 48 | collected 2 items 49 | 50 | tests/test_agent_creation.py::test_agent_creation PASSED [ 50%] 51 | tests/test_tools.py::test_currency_converter_api PASSED [100%] 52 | ``` 53 | 54 | ## 3. Submitting Changes 55 | 56 | ### Code format check 57 | Please ensure your code is formatted correctly using pre-commit 58 | 59 | ### Git commit message 60 | We strongly recommend your git commit follows the format below 61 | ```bash 62 | git commit -m : 63 | ``` 64 | 65 | | | | 66 | |-------------|--------------------------------------------------| 67 | | `feat` | Add new features | 68 | | `fix` | Fix bugs | 69 | | `docs` | Modify documents like README, CONTRIBUTE | 70 | | `style` | Modify code format like space and comma without changing code logic | 71 | | `refactor` | Refactor code structure without adding new features or fixing new bugs | 72 | | `perf` | Improve performance or user experience | 73 | | `test` | Test features, including unit test and integration test | 74 | | `chore` | Change the build procedure or add dependencies | 75 | | `revert` | Revert to the previous version | 76 | 77 | 💡Try to shrink the number of git commit messages to make it clear and concise. If you find you have already made too many commit messages, no worries, use git rebase and squash to merge multiple messages. Here is the [guide](https://www.freecodecamp.org/news/git-squash-commits/#:~:text=The%20first%20thing%20you%20need,to%20go%20back%206%20commits.&text=Now%2C%20you%20need%20to%20replace,apart%20from%20the%20first%20one). 78 | ### Create a Pull Request 79 | 80 | 1. Visit your forked AIOS repository on GitHub and click the "Compare & pull request" button to initiate the process of submitting your changes to the original repository for review and potential merging. 81 | 2. Choose the base branch and the compare branch (your feature branch).💡 Note that when you add new features, it is recommended to choose the (`dev`) branch and if your change does not affect original functions, you may consider choosing the (`main`) branch. 82 | 3. Write a title and describe your changes in the description. And it is recommended to select the label of the change to make it more clear. 83 | 84 | ## 4. Review and Approval 85 | Our maintainers will have a review of that and might give some suggestions or ask for more details. After they approve, your commitment can be incorporated into OpenAGI! 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 AGI Research 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAGI: Package for AI Agent Creation 2 | 3 | [![Code License](https://img.shields.io/badge/Code%20License-MIT-green.svg)](https://github.com/agiresearch/OpenAGI/blob/main/LICENSE) 4 | 5 | 6 | 7 | ## ✈️ Getting Started 8 | OpenAGI is used as the agent creation package to build agents for [AIOS](https://github.com/agiresearch/AIOS). 9 | **Notice:** For building up agents in the AIOS, please migrate to the [Cerebrum](https://github.com/agiresearch/Cerebrum), which is our latest SDK to connect with AIOS kernel. 10 | 11 | ### Installation 12 | From PyPI 13 | ``` 14 | pip install pyopenagi 15 | ``` 16 | Locally 17 | ``` 18 | git clone https://agiresearch/OpenAGI 19 | cd OpenAGI 20 | pip install -e . 21 | ``` 22 | 23 | ### Usage 24 | 25 | #### Add a new agent 26 | To add a new agent, first you need to create a folder under the pyopenagi/agents folder. 27 | The folder needs to be the following structure: 28 | ``` 29 | - pyopenagi/agents 30 | - author 31 | - agent_name 32 | - agent.py # main code for the agent execution logic 33 | - config.json # set up configurations for agent 34 | - meta_requirements.txt # dependencies that the agent needs 35 | ``` 36 | If you want to use external tools provided by openagi in your agents, you can follow instructions of setting up tools in [How to setup external tools](./tools.md). 37 | If you want to add new tools for your developing agent, 38 | you need to add a new tool file in the [folder](./pyopenagi/tools/). 39 | 40 | #### Upload agent 41 | If you have developed and tested your agent, and you would like to share your agents, you can use the following to upload your agents 42 | ``` 43 | python pyopenagi/agents/interact.py --mode upload --agent 44 | ``` 45 | 💡Note that the `agent` param must exactly match the folder you put your agent locally. 46 | 47 | #### Download agent 48 | If you want to look at implementations of other agents that others have developed, you can use the following command: 49 | ``` 50 | python pyopenagi/agents/interact.py --mode download --agent 51 | ``` 52 | 53 | ## 🚀 Contributions 54 | 55 | For detailed information on how to contribute, see [CONTRIBUTE](./CONTRIBUTE.md). If you would like to contribute to the codebase, [issues](https://github.com/agiresearch/OpenAGI/issues) or [pull requests](https://github.com/agiresearch/OpenAGI/pulls) are always welcome! 56 | 57 | ## 🖋️ Research 58 | Please check out our [implementation](https://github.com/agiresearch/OpenAGI/tree/research) for our research paper [OpenAGI: When LLM Meets Domain Experts](https://arxiv.org/abs/2304.04370). 59 | 60 | ``` 61 | @article{openagi, 62 | title={OpenAGI: When LLM Meets Domain Experts}, 63 | author={Ge, Yingqiang and Hua, Wenyue and Mei, Kai and Ji, Jianchao and Tan, Juntao and Xu, Shuyuan and Li, Zelong and Zhang, Yongfeng}, 64 | journal={In Advances in Neural Information Processing Systems (NeurIPS)}, 65 | year={2023} 66 | } 67 | ``` 68 | 69 | ## 🌍 OpenAGI Contributors 70 | [![OpenAGI contributors](https://contrib.rocks/image?repo=agiresearch/OpenAGI&max=300)](https://github.com/agiresearch/OpenAGI/graphs/contributors) 71 | 72 | 73 | 74 | ## 🌟 Star History 75 | 76 | [![Star History Chart](https://api.star-history.com/svg?repos=agiresearch/OpenAGI&type=Date)](https://star-history.com/#agiresearch/OpenAGI&Date) 77 | -------------------------------------------------------------------------------- /pyopenagi/README.md: -------------------------------------------------------------------------------- 1 | # pyopenagi 2 | 3 | The internal implementation for OpenAGI. 4 | 5 | 1. `agents/` contains the agent implementation all future agents have to follow. 6 | 2. `queues/` contains the class implementation for queues. 7 | 3. `utils/` contains some helpful internal utilities. 8 | 4. `tools/` contains the tools the agents can use. 9 | -------------------------------------------------------------------------------- /pyopenagi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/__init__.py -------------------------------------------------------------------------------- /pyopenagi/agents/README.md: -------------------------------------------------------------------------------- 1 | # pyopenagi/agents 2 | This folder contains the base implementation for running the agents, as well as the handlers for running multiple agents in Threads in `agent_process.py` and `agent_factory.py` 3 | 4 | In `example/` we have some example agents. You can add agents to that directory or to `your-cool-identifier/` to show your agent off in the main repo. However, it is recommended to use the agent database over submitting a pull request, unless it is for demo purposes. 5 | -------------------------------------------------------------------------------- /pyopenagi/agents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/__init__.py -------------------------------------------------------------------------------- /pyopenagi/agents/agent_factory.py: -------------------------------------------------------------------------------- 1 | from threading import Lock 2 | from pympler import asizeof 3 | from .interact import Interactor 4 | import os 5 | import importlib 6 | import random 7 | class AgentFactory: 8 | def __init__(self, 9 | # agent_process_queue, 10 | agent_process_factory, 11 | agent_log_mode 12 | ): 13 | # self.max_aid = 256 14 | # self.llm = llm 15 | # self.aid_pool = [i for i in range(self.max_aid)] 16 | # heapq.heapify(self.aid_pool) 17 | # self.agent_process_queue = agent_process_queue 18 | self.agent_process_factory = agent_process_factory 19 | 20 | self.current_agents = {} 21 | 22 | self.current_agents_lock = Lock() 23 | 24 | # self.terminate_signal = Event() 25 | 26 | self.agent_log_mode = agent_log_mode 27 | 28 | def snake_to_camel(self, snake_str): 29 | components = snake_str.split('_') 30 | return ''.join(x.title() for x in components) 31 | 32 | def list_agents(self): 33 | agent_list = Interactor().list_available_agents() 34 | for agent in agent_list: 35 | print(agent) 36 | 37 | def load_agent_instance(self, agent_name): 38 | # dynamically loads the module from the path 39 | author, name = agent_name.split("/") 40 | module_name = ".".join(["pyopenagi", "agents", author, name, "agent"]) 41 | class_name = self.snake_to_camel(name) 42 | 43 | agent_module = importlib.import_module(module_name) 44 | 45 | # dynamically loads the class 46 | agent_class = getattr(agent_module, class_name) 47 | 48 | return agent_class 49 | 50 | def activate_agent(self, agent_name, task_input): 51 | script_path = os.path.abspath(__file__) 52 | script_dir = os.path.dirname(script_path) 53 | 54 | # downloads the agent if its not installed already 55 | interactor = Interactor() 56 | 57 | if not os.path.exists(os.path.join(script_dir, agent_name)): 58 | interactor.download_agent(agent_name) 59 | 60 | if not interactor.check_reqs_installed(agent_name): 61 | interactor.install_agent_reqs(agent_name) 62 | 63 | # we instantiate the agent directly from the class 64 | agent_class = self.load_agent_instance(agent_name) 65 | 66 | agent = agent_class( 67 | agent_name = agent_name, 68 | task_input = task_input, 69 | agent_process_factory = self.agent_process_factory, 70 | log_mode = self.agent_log_mode 71 | ) 72 | 73 | aid = random.randint(100000, 999999) 74 | # set the identifier for the agent 75 | # aid = heapq.heappop(self.aid_pool) 76 | agent.set_aid(aid) 77 | 78 | # use a lock to make sure only one agent can read the values at a time 79 | # if not self.terminate_signal.is_set(): 80 | with self.current_agents_lock: 81 | self.current_agents[aid] = agent 82 | 83 | return agent 84 | 85 | def run_agent(self, agent_name, task_input): 86 | agent = self.activate_agent( 87 | agent_name=agent_name, 88 | task_input=task_input 89 | ) 90 | # print(task_input) 91 | output = agent.run() 92 | self.deactivate_agent(agent.get_aid()) 93 | return output 94 | 95 | def print_agent(self): 96 | headers = ["Agent ID", "Agent Name", "Created Time", "Status", "Memory Usage"] 97 | data = [] 98 | for id, agent in self.current_agents.items(): 99 | agent_name = agent.agent_name 100 | created_time = agent.created_time 101 | status = agent.status 102 | memory_usage = f"{asizeof.asizeof(agent)} bytes" 103 | data.append( 104 | [id, agent_name, created_time, status, memory_usage] 105 | ) 106 | self.print(headers=headers, data=data) 107 | 108 | 109 | def print(self, headers, data): 110 | # align output 111 | column_widths = [ 112 | max(len(str(row[i])) for row in [headers] + data) for i in range(len(headers)) 113 | ] 114 | print("+" + "-" * (sum(column_widths) + len(headers) * 3 - 3 ) + "+") 115 | print(self.format_row(headers, column_widths)) 116 | print("=" * (sum(column_widths) + len(headers) * 3 - 1)) 117 | for i, row in enumerate(data): 118 | print(self.format_row(row, column_widths)) 119 | if i < len(data): 120 | print("-" * (sum(column_widths) + len(headers) * 3 - 1)) 121 | print("+" + "-" * (sum(column_widths) + len(headers) * 3 - 3 ) + "+") 122 | 123 | 124 | def format_row(self, row, widths, align="<"): 125 | row_str = " | ".join(f"{str(item):{align}{widths[i]}}" for i, item in enumerate(row)) 126 | return row_str 127 | 128 | def deactivate_agent(self, aid): 129 | self.current_agents.pop(aid) 130 | # heapq.heappush(self.aid_pool, aid) 131 | -------------------------------------------------------------------------------- /pyopenagi/agents/agent_process.py: -------------------------------------------------------------------------------- 1 | from threading import Thread, Lock 2 | 3 | from ..utils.chat_template import Query 4 | import random 5 | class AgentProcess: 6 | def __init__(self, 7 | agent_name: str, 8 | query: Query 9 | ): 10 | """Agent Process 11 | 12 | Args: 13 | agent_name (str): Name of the agent 14 | query (Query): Query sent by the agent 15 | """ 16 | self.agent_name = agent_name 17 | self.query = query 18 | self.pid: int = None 19 | self.status = None 20 | self.response = None 21 | self.time_limit = None 22 | self.created_time = None 23 | self.start_time = None 24 | self.end_time = None 25 | 26 | def set_created_time(self, time): 27 | self.created_time = time 28 | 29 | def get_created_time(self): 30 | return self.created_time 31 | 32 | def set_start_time(self, time): 33 | self.start_time = time 34 | 35 | def get_start_time(self): 36 | return self.start_time 37 | 38 | def set_end_time(self, time): 39 | self.end_time = time 40 | 41 | def get_end_time(self): 42 | return self.end_time 43 | 44 | def set_priority(self, priority): 45 | self.priority = priority 46 | 47 | def get_priority(self): 48 | return self.priority 49 | 50 | def set_status(self, status): 51 | self.status = status 52 | 53 | def get_status(self): 54 | return self.status 55 | 56 | def set_pid(self, pid): 57 | self.pid = pid 58 | 59 | def get_pid(self): 60 | return self.pid 61 | 62 | def get_response(self): 63 | return self.response 64 | 65 | def set_response(self, response): 66 | self.response = response 67 | 68 | def get_time_limit(self): 69 | return self.time_limit 70 | 71 | def set_time_limit(self, time_limit): 72 | self.time_limit = time_limit 73 | 74 | 75 | class LLMRequestProcess(AgentProcess): 76 | pass 77 | 78 | class AgentProcessFactory: 79 | def __init__(self, agent_process_log_mode = None): 80 | # self.max_pid = 1024 81 | # self.pid_pool = [i for i in range(self.max_pid)] 82 | # heapq.heapify(self.pid_pool) 83 | 84 | self.thread = Thread(target=self.deactivate_agent_process) 85 | 86 | self.current_agent_processes = dict() 87 | 88 | self.current_agent_processes_lock = Lock() 89 | 90 | # self.terminate_signal = Event() 91 | 92 | self.agent_process_log_mode = agent_process_log_mode 93 | 94 | def activate_agent_process(self, agent_name, query): 95 | # if not self.terminate_signal.is_set(): 96 | with self.current_agent_processes_lock: 97 | agent_process = AgentProcess( 98 | agent_name = agent_name, 99 | query = query 100 | ) 101 | pid = random.randint(1000000, 9999999) 102 | # pid = heapq.heappop(self.pid_pool) 103 | agent_process.set_pid(pid) 104 | agent_process.set_status("active") 105 | self.current_agent_processes[pid] = agent_process 106 | return agent_process 107 | 108 | def print_agent_process(self): 109 | headers = ["Agent Process ID", "Agent Name", "Created Time", "Status"] 110 | data = [] 111 | for id, agent_process in self.current_agent_processes.items(): 112 | agent_name = agent_process.agent_name 113 | created_time = agent_process.created_time 114 | status = agent_process.status 115 | # memory_usage = f"{asizeof.asizeof(agent)} bytes" 116 | data.append( 117 | [id, agent_name, created_time, status] 118 | ) 119 | self.print(headers=headers, data=data) 120 | 121 | 122 | def print(self, headers, data): 123 | # align output 124 | column_widths = [ 125 | max(len(str(row[i])) for row in [headers] + data) for i in range(len(headers)) 126 | ] 127 | print("+" + "-" * (sum(column_widths) + len(headers) * 3 - 3 ) + "+") 128 | print(self.format_row(headers, column_widths)) 129 | print("=" * (sum(column_widths) + len(headers) * 3 - 1)) 130 | for i, row in enumerate(data): 131 | print(self.format_row(row, column_widths)) 132 | if i < len(data): 133 | print("-" * (sum(column_widths) + len(headers) * 3 - 1)) 134 | print("+" + "-" * (sum(column_widths) + len(headers) * 3 - 3 ) + "+") 135 | 136 | 137 | def format_row(self, row, widths, align="<"): 138 | row_str = " | ".join(f"{str(item):{align}{widths[i]}}" for i, item in enumerate(row)) 139 | return row_str 140 | 141 | def deactivate_agent_process(self, pid): 142 | self.current_agent_processes.pop(pid) 143 | # heapq.heappush(self.pid_pool, pid) 144 | 145 | def start(self): 146 | """start the factory to check inactive agent""" 147 | self.thread.start() 148 | 149 | def stop(self): 150 | self.thread.join() 151 | -------------------------------------------------------------------------------- /pyopenagi/agents/call_core.py: -------------------------------------------------------------------------------- 1 | import time 2 | from threading import Thread 3 | 4 | from aios.hooks.stores._global import global_llm_req_queue_add_message 5 | from .agent_process import AgentProcess 6 | from ..utils.logger import AgentLogger 7 | 8 | 9 | class CustomizedThread(Thread): 10 | def __init__(self, target, args=()): 11 | super().__init__() 12 | self.target = target 13 | self.args = args 14 | self.result = None 15 | 16 | def run(self): 17 | self.result = self.target(*self.args) 18 | 19 | def join(self): 20 | super().join() 21 | return self.result 22 | 23 | 24 | class CallCore: 25 | """ 26 | Simplify BaseAgent to provide an interface for external frameworks to make LLM requests using aios. 27 | """ 28 | def __init__(self, 29 | agent_name, 30 | agent_process_factory, 31 | log_mode: str = "console" 32 | ): 33 | self.agent_name = agent_name 34 | self.agent_process_factory = agent_process_factory 35 | self.log_mode = log_mode 36 | self.logger = self.setup_logger() 37 | 38 | # the default method used for getting response from AIOS 39 | def get_response(self, 40 | query, 41 | temperature=0.0 42 | ): 43 | 44 | thread = CustomizedThread(target=self.query_loop, args=(query,)) 45 | thread.start() 46 | return thread.join() 47 | 48 | def query_loop(self, query): 49 | agent_process = self.create_agent_request(query) 50 | 51 | completed_response, start_times, end_times, waiting_times, turnaround_times = "", [], [], [], [] 52 | 53 | while agent_process.get_status() != "done": 54 | thread = Thread(target=self.listen, args=(agent_process,)) 55 | current_time = time.time() 56 | # reinitialize agent status 57 | agent_process.set_created_time(current_time) 58 | agent_process.set_response(None) 59 | 60 | global_llm_req_queue_add_message(agent_process) 61 | 62 | # LLMRequestQueue.add_message(agent_process) 63 | 64 | thread.start() 65 | thread.join() 66 | 67 | completed_response = agent_process.get_response() 68 | if agent_process.get_status() != "done": 69 | self.logger.log( 70 | f"Suspended due to the reach of time limit ({agent_process.get_time_limit()}s). Current result is: {completed_response.response_message}\n", 71 | level="suspending" 72 | ) 73 | start_time = agent_process.get_start_time() 74 | end_time = agent_process.get_end_time() 75 | waiting_time = start_time - agent_process.get_created_time() 76 | turnaround_time = end_time - agent_process.get_created_time() 77 | 78 | start_times.append(start_time) 79 | end_times.append(end_time) 80 | waiting_times.append(waiting_time) 81 | turnaround_times.append(turnaround_time) 82 | # Re-start the thread if not done 83 | 84 | # self.agent_process_factory.deactivate_agent_process(agent_process.get_pid()) 85 | 86 | return completed_response, start_times, end_times, waiting_times, turnaround_times 87 | 88 | def create_agent_request(self, query): 89 | agent_process = self.agent_process_factory.activate_agent_process( 90 | agent_name=self.agent_name, 91 | query=query 92 | ) 93 | agent_process.set_created_time(time.time()) 94 | # print("Already put into the queue") 95 | return agent_process 96 | 97 | def listen(self, agent_process: AgentProcess): 98 | """Response Listener for agent 99 | 100 | Args: 101 | agent_process (AgentProcess): Listened AgentProcess 102 | 103 | Returns: 104 | str: LLM response of Agent Process 105 | """ 106 | while agent_process.get_response() is None: 107 | time.sleep(0.2) 108 | 109 | return agent_process.get_response() 110 | 111 | def setup_logger(self): 112 | logger = AgentLogger(self.agent_name, self.log_mode) 113 | return logger 114 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/README.md: -------------------------------------------------------------------------------- 1 | # pyopenagi/agents/example 2 | 3 | Here are the example agents we created to demo agent creation in OpenAGI. 4 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/academic_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | 4 | 5 | class AcademicAgent(ReactAgent): 6 | def __init__(self, agent_name, task_input, agent_process_factory, log_mode: str): 7 | ReactAgent.__init__( 8 | self, agent_name, task_input, agent_process_factory, log_mode 9 | ) 10 | # self.workflow_mode = "manual" 11 | self.workflow_mode = "automatic" 12 | 13 | def check_path(self, tool_calls): 14 | script_path = os.path.abspath(__file__) 15 | save_dir = os.path.join( 16 | os.path.dirname(script_path), "output" 17 | ) # modify the customized output path for saving outputs 18 | if not os.path.exists(save_dir): 19 | os.makedirs(save_dir) 20 | for tool_call in tool_calls: 21 | try: 22 | for k in tool_call["parameters"]: 23 | if "path" in k: 24 | path = tool_call["parameters"][k] 25 | if not path.startswith(save_dir): 26 | tool_call["parameters"][k] = os.path.join( 27 | save_dir, os.path.basename(path) 28 | ) 29 | except Exception: 30 | continue 31 | return tool_calls 32 | 33 | def manual_workflow(self): 34 | workflow = [ 35 | {"message": "Gather research topic and keywords", "tool_use": []}, 36 | {"message": "Search for relevant papers on arXiv", "tool_use": ["arxiv"]}, 37 | {"message": "Summarize key findings of selected papers", "tool_use": []}, 38 | { 39 | "message": "Identify research gaps and generate potential research questions", 40 | "tool_use": [], 41 | }, 42 | ] 43 | return workflow 44 | 45 | def run(self): 46 | return super().run() 47 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/academic_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "academic_agent", 3 | "description": [ 4 | "You are an academic research assistant. ", 5 | "Help users find relevant research papers, summarize key findings, and generate potential research questions." 6 | ], 7 | "tools": [ 8 | "arxiv/arxiv" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | }, 15 | "build": { 16 | "entry": "agent.py", 17 | "module": "AcademicAgent" 18 | } 19 | } -------------------------------------------------------------------------------- /pyopenagi/agents/example/academic_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | arxiv -------------------------------------------------------------------------------- /pyopenagi/agents/example/cocktail_mixlogist/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class CocktailMixlogist(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "automatic" 12 | # self.workflow_mode = "manual" 13 | 14 | def check_path(self, tool_calls): 15 | script_path = os.path.abspath(__file__) 16 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 17 | if not os.path.exists(save_dir): 18 | os.makedirs(save_dir) 19 | for tool_call in tool_calls: 20 | try: 21 | for k in tool_call["parameters"]: 22 | if "path" in k: 23 | path = tool_call["parameters"][k] 24 | if not path.startswith(save_dir): 25 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 26 | except Exception: 27 | continue 28 | return tool_calls 29 | 30 | def manual_workflow(self): 31 | workflow = [ 32 | { 33 | "message": "Gather user preferences (alcoholic or non-alcoholic, taste profile, occasion)", 34 | "tool_use": [] 35 | }, 36 | { 37 | "message": "Identify available ingredients and potential substitutions", 38 | "tool_use": [] 39 | }, 40 | { 41 | "message": "Create cocktail or mocktail recipes", 42 | "tool_use": [] 43 | } 44 | ] 45 | return workflow 46 | 47 | def run(self): 48 | return super().run() 49 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/cocktail_mixlogist/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cocktail_mixlogist", 3 | "description": [ 4 | "You are a virtual mixologist. ", 5 | "Create delicious cocktails and mocktails based on user preferences, available ingredients, and dietary restrictions." 6 | ], 7 | "tools": [ 8 | "wikipedia/wikipedia" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/cocktail_mixlogist/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/cocktail_mixlogist/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/cook_therapist/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class CookTherapist(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | # self.workflow_mode = "automatic" 12 | self.workflow_mode = "manual" 13 | 14 | def check_path(self, tool_calls): 15 | script_path = os.path.abspath(__file__) 16 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 17 | if not os.path.exists(save_dir): 18 | os.makedirs(save_dir) 19 | for tool_call in tool_calls: 20 | try: 21 | for k in tool_call["parameters"]: 22 | if "path" in k: 23 | path = tool_call["parameters"][k] 24 | if not path.startswith(save_dir): 25 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 26 | except Exception: 27 | continue 28 | return tool_calls 29 | 30 | def manual_workflow(self): 31 | workflow = [ 32 | { 33 | "message": "Gather user input on desired ingredients, dietary restrictions, or cuisine type.", 34 | "tool_use": [] 35 | }, 36 | { 37 | "message": "Create a detailed recipe, including ingredients, measurements, and step-by-step instructions.", 38 | "tool_use": [] 39 | }, 40 | { 41 | "message": "Generate an image of the final dish.", 42 | "tool_use": ["text_to_image"] 43 | }, 44 | { 45 | "message": "Present the recipe, including image, instructions, and nutritional information.", 46 | "tool_use": [] 47 | } 48 | ] 49 | return workflow 50 | 51 | def run(self): 52 | return super().run() 53 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/cook_therapist/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cook_therapist", 3 | "description": [ 4 | "You are a culinary expert and recipe creator. ", 5 | "Your role is to generate unique and delicious recipes tailored to the user's tastes and dietary needs. " 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/cook_therapist/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/cook_therapist/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/creation_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class CreationAgent(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | # self.workflow_mode = "automatic" 12 | self.workflow_mode = "manual" 13 | 14 | def check_path(self, tool_calls): 15 | script_path = os.path.abspath(__file__) 16 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 17 | if not os.path.exists(save_dir): 18 | os.makedirs(save_dir) 19 | for tool_call in tool_calls: 20 | try: 21 | for k in tool_call["parameters"]: 22 | if "path" in k: 23 | path = tool_call["parameters"][k] 24 | if not path.startswith(save_dir): 25 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 26 | except Exception: 27 | continue 28 | return tool_calls 29 | 30 | def manual_workflow(self): 31 | workflow = [ 32 | { 33 | "message": "Gather content requirements (platform, topic, style)", 34 | "tool_use": [] 35 | }, 36 | { 37 | "message": "Develop content concept and key messages", 38 | "tool_use": [] 39 | }, 40 | { 41 | "message": "Generate engaging text content", 42 | "tool_use": [] 43 | }, 44 | { 45 | "message": "Create visually appealing images", 46 | "tool_use": ["text_to_image"] 47 | }, 48 | { 49 | "message": "Summarize content and post", 50 | "tool_use": [] 51 | } 52 | ] 53 | return workflow 54 | 55 | def run(self): 56 | return super().run() 57 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/creation_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "creation_agent", 3 | "description": [ 4 | "You are a social media content creator. ", 5 | "Generate compelling text and visually appealing images" 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/creation_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | diffusers==0.27.2 2 | accelerate==0.30.1 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/fashion_stylist/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class FashionStylist(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | # self.workflow_mode = "automatic" 12 | self.workflow_mode = "manual" 13 | 14 | def check_path(self, tool_calls): 15 | script_path = os.path.abspath(__file__) 16 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 17 | if not os.path.exists(save_dir): 18 | os.makedirs(save_dir) 19 | for tool_call in tool_calls: 20 | try: 21 | for k in tool_call["parameters"]: 22 | if "path" in k: 23 | path = tool_call["parameters"][k] 24 | if not path.startswith(save_dir): 25 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 26 | except Exception: 27 | continue 28 | return tool_calls 29 | 30 | def manual_workflow(self): 31 | workflow = [ 32 | { 33 | "message": "Gather user preferences, body type, and occasion details.", 34 | "tool_use": [] 35 | }, 36 | { 37 | "message": "Generate outfit ideas based on user input.", 38 | "tool_use": [] 39 | }, 40 | { 41 | "message": "Create visual representations of outfit ideas.", 42 | "tool_use": ["text_to_image"] 43 | }, 44 | { 45 | "message": "Analyze generated images for style coherence and alignment with user preferences.", 46 | "tool_use": ["doc_question_answering"] 47 | }, 48 | { 49 | "message": "Search for similar items online based on the generated outfit ideas.", 50 | "tool_use": ["google_search"] 51 | }, 52 | { 53 | "message": "Summarize content.", 54 | "tool_use": [] 55 | } 56 | ] 57 | return workflow 58 | 59 | def run(self): 60 | return super().run() 61 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/fashion_stylist/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fashion_stylist", 3 | "description": [ 4 | "You are a fashion designer. ", 5 | "Create custom clothing and accessory designs based on user preferences and body measurements. " 6 | ], 7 | "tools": [ 8 | "google/google_search", 9 | "stability-ai/text_to_image", 10 | "impira/doc_question_answering" 11 | ], 12 | "meta": { 13 | "author": "example", 14 | "version": "0.0.1", 15 | "license": "CC0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/fashion_stylist/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | soundfile 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/festival_card_designer/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class FestivalCardDesigner(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def check_path(self, tool_calls): 17 | script_path = os.path.abspath(__file__) 18 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 19 | if not os.path.exists(save_dir): 20 | os.makedirs(save_dir) 21 | for tool_call in tool_calls: 22 | try: 23 | for k in tool_call["parameters"]: 24 | if "path" in k: 25 | path = tool_call["parameters"][k] 26 | if not path.startswith(save_dir): 27 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 28 | except Exception: 29 | continue 30 | return tool_calls 31 | 32 | def manual_workflow(self): 33 | workflow = [ 34 | { 35 | "message": "Gather user information (festival theme, target audience, card size)", 36 | "tool_use": [] 37 | }, 38 | { 39 | "message": "Identify card design elements (colors, fonts, imagery)", 40 | "tool_use": [] 41 | }, 42 | { 43 | "message": "Generate card layout options", 44 | "tool_use": ["text_to_image"] 45 | }, 46 | { 47 | "message": "Add textual elements to the festival card ", 48 | "tool_use": [] 49 | } 50 | ] 51 | return workflow 52 | 53 | def run(self): 54 | return super().run() 55 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/festival_card_designer/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "festival_card_designer", 3 | "description": [ 4 | "You are a festival card designer. ", 5 | "Create unique and eye-catching festival cards based on user preferences and festival themes." 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/festival_card_designer/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/festival_card_designer/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/fitness_trainer/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class FitnessTrainer(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def check_path(self, tool_calls): 17 | script_path = os.path.abspath(__file__) 18 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 19 | if not os.path.exists(save_dir): 20 | os.makedirs(save_dir) 21 | for tool_call in tool_calls: 22 | try: 23 | for k in tool_call["parameters"]: 24 | if "path" in k: 25 | path = tool_call["parameters"][k] 26 | if not path.startswith(save_dir): 27 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 28 | except Exception: 29 | continue 30 | return tool_calls 31 | 32 | def manual_workflow(self): 33 | workflow = [ 34 | { 35 | "message": "Gather information about the user's fitness level, goals, and any physical limitations.", 36 | "tool_use": [] 37 | }, 38 | { 39 | "message": "Create a detailed workout plan with exercise descriptions.", 40 | "tool_use": [] 41 | }, 42 | { 43 | "message": "Generate images demonstrating key exercises in the workout plan.", 44 | "tool_use": ["text_to_image"] 45 | }, 46 | { 47 | "message": "Create audio instructions for each exercise to guide the user.", 48 | "tool_use": ["text_to_speech"] 49 | }, 50 | { 51 | "message": "Compile the workout plan, images, and audio into a comprehensive fitness guide.", 52 | "tool_use": [] 53 | } 54 | ] 55 | return workflow 56 | 57 | def run(self): 58 | return super().run() 59 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/fitness_trainer/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fitness_trainer", 3 | "description": [ 4 | "You are a fitness trainer with expertise in various exercise techniques, nutrition, and personalized fitness planning. ", 5 | "Your role is to create tailored workout plans and provide clear instructions for exercises." 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image", 9 | "suno/text_to_speech" 10 | ], 11 | "meta": { 12 | "author": "example", 13 | "version": "0.0.1", 14 | "license": "CC0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/fitness_trainer/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | arxiv 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/game_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | 3 | class GameAgent(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def manual_workflow(self): 17 | workflow = [ 18 | { 19 | "message": "Gather user preferences (genre, platform, play style)", 20 | "tool_use": [] 21 | }, 22 | { 23 | "message": "Search for suitable games based on criteria", 24 | "tool_use": ["google_search"] 25 | }, 26 | { 27 | "message": "Provide game descriptions, ratings, and gameplay details", 28 | "tool_use": [] 29 | } 30 | ] 31 | return workflow 32 | 33 | def run(self): 34 | return super().run() 35 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/game_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "game_agent", 3 | "description": [ 4 | "You are a video game expert. ", 5 | "Recommend games based on user preferences, mood, and available platforms. Provide detailed game descriptions and gameplay information." 6 | ], 7 | "tools": [ 8 | "google/google_search" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/game_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | arxiv 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/interior_decorator/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class InteriorDecorator(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | # self.workflow_mode = "automatic" 12 | self.workflow_mode = "manual" 13 | 14 | def check_path(self, tool_calls): 15 | script_path = os.path.abspath(__file__) 16 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 17 | if not os.path.exists(save_dir): 18 | os.makedirs(save_dir) 19 | for tool_call in tool_calls: 20 | try: 21 | for k in tool_call["parameters"]: 22 | if "path" in k: 23 | path = tool_call["parameters"][k] 24 | if not path.startswith(save_dir): 25 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 26 | except Exception: 27 | continue 28 | return tool_calls 29 | 30 | def manual_workflow(self): 31 | workflow = [ 32 | { 33 | "message": "Gather user preferences, room dimensions, and desired style.", 34 | "tool_use": [] 35 | }, 36 | { 37 | "message": "Generate mood board images based on user input using text_to_image.", 38 | "tool_use": ["text_to_image"] 39 | }, 40 | { 41 | "message": "Analyze generated images for color schemes, furniture styles, and overall ambiance.", 42 | "tool_use": [] 43 | }, 44 | { 45 | "message": "Recommend specific furniture, decor items, and color palettes based on analysis.", 46 | "tool_use": [] 47 | } 48 | ] 49 | 50 | return workflow 51 | 52 | def run(self): 53 | return super().run() 54 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/interior_decorator/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interior_decorator", 3 | "description": [ 4 | "You are a virtual interior decorator. ", 5 | "You should be able to provide design recommendations based on user preferences, room size, and style. You can generate mood boards, suggest furniture and decor, and offer color palette ideas. " 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/interior_decorator/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | diffusers==0.27.2 2 | accelerate==0.30.1 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/language_tutor/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class LanguageTutor(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | # self.workflow_mode = "automatic" 12 | self.workflow_mode = "manual" 13 | 14 | def check_path(self, tool_calls): 15 | script_path = os.path.abspath(__file__) 16 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 17 | if not os.path.exists(save_dir): 18 | os.makedirs(save_dir) 19 | for tool_call in tool_calls: 20 | try: 21 | for k in tool_call["parameters"]: 22 | if "path" in k: 23 | path = tool_call["parameters"][k] 24 | if not path.startswith(save_dir): 25 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 26 | except Exception: 27 | continue 28 | return tool_calls 29 | 30 | def manual_workflow(self): 31 | workflow = [ 32 | { 33 | "message": "Identify user's target language and learning goals.", 34 | "tool_use": [] 35 | }, 36 | { 37 | "message": "Generate vocabulary lists and exercises.", 38 | "tool_use": ["google_search"] 39 | }, 40 | { 41 | "message": "Create grammar explanations and practice sentences.", 42 | "tool_use": [] 43 | }, 44 | { 45 | "message": "Provide audio examples of pronunciation.", 46 | "tool_use": ["text_to_speech"] 47 | }, 48 | { 49 | "message": "Engage in conversation practice with the user.", 50 | "tool_use": [] 51 | } 52 | ] 53 | return workflow 54 | 55 | def run(self): 56 | return super().run() 57 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/language_tutor/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "language_tutor", 3 | "description": [ 4 | "You are a language tutor. You can provide vocabulary exercises, grammar explanations, and conversation practice. ", 5 | "You can also offer pronunciation guidance and cultural insights. " 6 | ], 7 | "tools": [ 8 | "google/google_search", 9 | "suno/text_to_speech" 10 | ], 11 | "meta": { 12 | "author": "example", 13 | "version": "0.0.1", 14 | "license": "CC0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/language_tutor/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | soundfile 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/logo_creator/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class LogoCreator(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def check_path(self, tool_calls): 14 | script_path = os.path.abspath(__file__) 15 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 16 | if not os.path.exists(save_dir): 17 | os.makedirs(save_dir) 18 | for tool_call in tool_calls: 19 | try: 20 | for k in tool_call["parameters"]: 21 | if "path" in k: 22 | path = tool_call["parameters"][k] 23 | if not path.startswith(save_dir): 24 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 25 | except Exception: 26 | continue 27 | return tool_calls 28 | 29 | def automatic_workflow(self): 30 | return super().automatic_workflow() 31 | 32 | def manual_workflow(self): 33 | workflow = [ 34 | { 35 | "message": "Gather business information (name, industry, target audience)", 36 | "tool_use": [] 37 | }, 38 | { 39 | "message": "Identify brand personality and values", 40 | "tool_use": [] 41 | }, 42 | { 43 | "message": "Generate logo concepts and variations", 44 | "tool_use": ["text_to_image"] 45 | } 46 | ] 47 | return workflow 48 | 49 | def run(self): 50 | return super().run() 51 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/logo_creator/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logo_creator", 3 | "description": [ 4 | "You are a logo design expert. ", 5 | "Create unique and professional logo designs based on user-provided business information and preferences." 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/logo_creator/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/logo_creator/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/math_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | 3 | class MathAgent(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | 12 | def automatic_workflow(self): 13 | return super().automatic_workflow() 14 | 15 | def manual_workflow(self): 16 | # TODO: add pemdas calculation support in the future 17 | workflow = [ 18 | { 19 | "message": "Identify the problem type and relevant formulas", 20 | "tool_use": ["wikipedia"] 21 | }, 22 | { 23 | "message": "Break down the problem into steps", 24 | "tool_use": [] 25 | }, 26 | { 27 | "message": "Provide final answer/solution", 28 | "tool_use": [] 29 | } 30 | ] 31 | return workflow 32 | 33 | def run(self): 34 | return super().run() 35 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/math_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MathAgent", 3 | "description": [ 4 | "You are an expert who is good at solving mathematical problems. " 5 | ], 6 | "workflow": [ 7 | "identify the tool to call to do some pre-calculation. ", 8 | "perform mathematical operations using the pre-calculated result, which could involve addition, subtraction, multiplication, or division with other numeric values to solve the problem." 9 | ], 10 | "tools": ["wikipedia/wikipedia"], 11 | "meta": { 12 | "author": "example", 13 | "version": "0.0.1", 14 | "license": "CC0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/math_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | wolframalpha 2 | wikipedia -------------------------------------------------------------------------------- /pyopenagi/agents/example/meme_creator/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | import os 3 | class MemeCreator(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def check_path(self, tool_calls): 17 | script_path = os.path.abspath(__file__) 18 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 19 | if not os.path.exists(save_dir): 20 | os.makedirs(save_dir) 21 | for tool_call in tool_calls: 22 | try: 23 | for k in tool_call["parameters"]: 24 | if "path" in k: 25 | path = tool_call["parameters"][k] 26 | if not path.startswith(save_dir): 27 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 28 | except Exception: 29 | continue 30 | return tool_calls 31 | 32 | def manual_workflow(self): 33 | workflow = [ 34 | { 35 | "message": "Gather user input (topic, text, image)", 36 | "tool_use": [] 37 | }, 38 | { 39 | "message": "Select a suitable meme template or create a custom image", 40 | "tool_use": ["text_to_image"] 41 | }, 42 | { 43 | "message": "Add text to the image based on user input", 44 | "tool_use": [] 45 | } 46 | ] 47 | return workflow 48 | 49 | def run(self): 50 | return super().run() 51 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/meme_creator/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meme_creator", 3 | "description": [ 4 | "You are a meme creator. Given a topic, text, or an image, create a funny and relevant meme." 5 | ], 6 | "tools": [ 7 | "stability-ai/text_to_image" 8 | ], 9 | "meta": { 10 | "author": "example", 11 | "version": "0.0.1", 12 | "license": "CC0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/meme_creator/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | arxiv 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/music_composer/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | 3 | class MusicComposer(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | # self.workflow_mode = "automatic" 12 | self.workflow_mode = "manual" 13 | 14 | def manual_workflow(self): 15 | workflow = [ 16 | { 17 | "message": "Gather user information about desired music genre, mood, and tempo.", 18 | "tool_use": [] 19 | }, 20 | { 21 | "message": "Generate basic melody, chord progression, or rhythm structure.", 22 | "tool_use": [] 23 | }, 24 | { 25 | "message": "Provide suggestions for musical development and experimentation.", 26 | "tool_use": [] 27 | }, 28 | { 29 | "message": "Convert musical elements into audio.", 30 | "tool_use": ["text_to_speech"] 31 | }, 32 | { 33 | "message": "Offer feedback on composition and suggest improvements.", 34 | "tool_use": [] 35 | } 36 | ] 37 | return workflow 38 | 39 | def run(self): 40 | return super().run() 41 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/music_composer/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "music_composer", 3 | "description": [ 4 | "You are an excellent music composer. ", 5 | "Your role is to produce music based on the user's needs. " 6 | ], 7 | "tools": [ 8 | "suno/text_to_speech" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/music_composer/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | wikipedia 2 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/plant_care_assistant/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | 3 | class PlantCareAssistant(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def manual_workflow(self): 17 | workflow = [ 18 | { 19 | "message": "Gather plant information (type, age, environment)", 20 | "tool_use": [] 21 | }, 22 | { 23 | "message": "Identify plant needs (light, water, fertilizer)", 24 | "tool_use": ["wikipedia"] 25 | }, 26 | { 27 | "message": "Create a plant care schedule", 28 | "tool_use": [] 29 | }, 30 | { 31 | "message": "Provide troubleshooting advice for plant issues", 32 | "tool_use": [] 33 | } 34 | ] 35 | return workflow 36 | 37 | def run(self): 38 | return super().run() 39 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/plant_care_assistant/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plant_care_assistant", 3 | "description": [ 4 | "You are a virtual plant expert. ", 5 | "Provide care instructions, identify plant problems, and offer reminders for plant care tasks." 6 | ], 7 | "tools": [ 8 | "wikipedia/wikipedia" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/plant_care_assistant/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | arxiv 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rag_agent/README.md: -------------------------------------------------------------------------------- 1 | # src/agents/agent_config 2 | 3 | Each agent holds a config file in addition to the class specifying what to run. The agent config is a JSON file. 4 | 5 | Each JSON file contains the following: 6 | 7 | 1. `name` : name of the agent 8 | 2. `description` : an array with one element containing the system prompt 9 | 3. `workflow` : an array with plaintext describing what the agent will do at each iteration. this is fed into the LLM running the agent 10 | 4. `tools` : an array with complex json objects 11 | - `type` : type of tool, typically "function" 12 | - `function` : if the type of function it contains data in the specific functions. 13 | 14 | For more detailed information, cite each specific agent as an example and fit it for your purposes. 15 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rag_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...base_agent import BaseAgent 2 | 3 | import time 4 | 5 | import argparse 6 | 7 | from ....utils import Message 8 | 9 | from pathlib import PurePosixPath 10 | import os 11 | import chromadb 12 | from llama_index.embeddings.huggingface import HuggingFaceEmbedding 13 | from llama_index.core import VectorStoreIndex, SimpleDirectoryReader 14 | from llama_index.core import StorageContext 15 | from llama_index.core import PromptTemplate 16 | from llama_index.core.retrievers import VectorIndexRetriever 17 | from llama_index.vector_stores.chroma import ChromaVectorStore 18 | 19 | from openagi.src.agents.base import BaseAgent 20 | 21 | class RAGAgent(BaseAgent): 22 | def __init__(self, 23 | agent_name, 24 | task_input, 25 | llm, 26 | agent_process_queue, 27 | agent_process_factory, 28 | log_mode: str 29 | ): 30 | BaseAgent.__init__(self, agent_name, task_input, llm, agent_process_queue, agent_process_factory, log_mode) 31 | 32 | def run(self): 33 | request_waiting_times = [] 34 | request_turnaround_times = [] 35 | query = self.task_input 36 | 37 | self.logger.log(f"{query}\n", level="info") 38 | 39 | context = self.retrive(query) 40 | prompt = self.build_prompt(context_str=context, query_str=query) 41 | 42 | rounds = 0 43 | 44 | response, start_times, end_times, waiting_times, turnaround_times = self.get_response( 45 | message = Message( 46 | prompt = prompt, 47 | tools = None 48 | ) 49 | ) 50 | 51 | self.set_start_time(start_times[0]) 52 | rounds += 1 53 | 54 | request_waiting_times.extend(waiting_times) 55 | request_turnaround_times.extend(turnaround_times) 56 | 57 | response_message = response.response_message 58 | 59 | self.logger.log(f"Final result is: {response.response_message}\n", level="info") 60 | final_result = response_message 61 | 62 | self.set_status("done") 63 | self.set_end_time(time=time.time()) 64 | 65 | return { 66 | "agent_name": self.agent_name, 67 | "result": final_result, 68 | "rounds": rounds, 69 | "agent_waiting_time": self.start_time - self.created_time, 70 | "agent_turnaround_time": self.end_time - self.created_time, 71 | "request_waiting_times": request_waiting_times, 72 | "request_turnaround_times": request_turnaround_times, 73 | } 74 | 75 | def retrive(self, query: str): 76 | script_dir = os.path.dirname(os.path.realpath(__file__)) 77 | self.data_path = PurePosixPath(script_dir, "data", "paul_graham").as_posix() 78 | self.db_path = PurePosixPath(script_dir, "chroma_db").as_posix() 79 | self.collection_name = "quickstart" 80 | self.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-base-en-v1.5") 81 | 82 | self.create_db_if_not_exists() 83 | 84 | db = chromadb.PersistentClient(path=self.db_path) 85 | chroma_collection = db.get_or_create_collection(self.collection_name) 86 | vector_store = ChromaVectorStore(chroma_collection=chroma_collection) 87 | index = VectorStoreIndex.from_vector_store( 88 | vector_store, 89 | embed_model=self.embed_model, 90 | ) 91 | 92 | retriever = VectorIndexRetriever(index=index, similarity_top_k=2) 93 | retrieved_result = retriever.retrieve(query) 94 | context_str = retrieved_result[0].get_content() 95 | return context_str 96 | 97 | def create_db_if_not_exists(self): 98 | if os.path.exists(self.db_path): 99 | pass 100 | else: 101 | print("store documents to vector db!") 102 | documents = SimpleDirectoryReader(self.data_path).load_data() 103 | 104 | chroma_client = chromadb.PersistentClient(path=self.db_path) 105 | chroma_collection = chroma_client.create_collection(self.collection_name) 106 | vector_store = ChromaVectorStore(chroma_collection=chroma_collection) 107 | storage_context = StorageContext.from_defaults(vector_store=vector_store) 108 | index = VectorStoreIndex.from_documents( 109 | documents, storage_context=storage_context, embed_model=self.embed_model 110 | ) 111 | index.storage_context.persist(persist_dir=self.db_path) 112 | 113 | def build_prompt(self, context_str: str, query_str: str): 114 | prompt_template_literal = ( 115 | "{query_str}" 116 | 117 | "Use the following context as your learned knowledge, inside XML tags.\n" 118 | "\n" 119 | "{context_str}" 120 | "\n" 121 | 122 | "Avoid mentioning that you obtained the information from the context.\n" 123 | ) 124 | prompt_template = PromptTemplate(prompt_template_literal) 125 | final_prompt = prompt_template.format(context_str=context_str, query_str=query_str) 126 | return final_prompt 127 | 128 | if __name__ == "__main__": 129 | parser = argparse.ArgumentParser(description='Run RagAgent') 130 | parser.add_argument("--agent_name") 131 | parser.add_argument("--task_input") 132 | 133 | args = parser.parse_args() 134 | agent = RAGAgent(args.agent_name, args.task_input) 135 | agent.run() 136 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rag_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rag_agent", 3 | "description": [ 4 | "Thou art the deity overseeing documents; when mortals inquire of thee, out of mercy, thou dost enlighten them with the knowledge contained within the documents." 5 | ], 6 | "workflow": [], 7 | "tool_info": [], 8 | "meta": { 9 | "author": "example", 10 | "version": "0.0.1", 11 | "license": "CC0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rag_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | langchain_core==0.2.1 2 | llama_index==0.10.39 3 | llama_index.embeddings.huggingface==0.2.0 4 | llama-index-vector-stores-chroma==0.1.8 5 | chromadb==0.5.0 6 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rec_agent/agent.py: -------------------------------------------------------------------------------- 1 | 2 | from ...react_agent import ReactAgent 3 | 4 | class RecAgent(ReactAgent): 5 | def __init__(self, 6 | agent_name, 7 | task_input, 8 | agent_process_factory, 9 | log_mode 10 | ): 11 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def manual_workflow(self): 17 | workflow = [ 18 | { 19 | "message": "identify the tool that you need to call to obtain information.", 20 | "tool_use": ["google_search"] 21 | }, 22 | { 23 | "message": "based on the information, give recommendations for the user based on the constrains. ", 24 | "tool_use": [] 25 | } 26 | ] 27 | return workflow 28 | 29 | def run(self): 30 | return super().run() 31 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rec_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RecAgent", 3 | "description": [ 4 | "You are an expert who is good at recommending TV series and movies." 5 | ], 6 | "tools": ["google/google_search"], 7 | "meta": { 8 | "author": "example", 9 | "version": "0.0.1", 10 | "license": "CC0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/rec_agent/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/rec_agent/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/story_teller/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | 3 | import os 4 | class StoryTeller(ReactAgent): 5 | def __init__(self, 6 | agent_name, 7 | task_input, 8 | agent_process_factory, 9 | log_mode: str 10 | ): 11 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 12 | # self.workflow_mode = "automatic" 13 | self.workflow_mode = "manual" 14 | 15 | def manual_workflow(self): 16 | workflow = [ 17 | { 18 | "message": "Determine the story genre and theme based on user input.", 19 | "tool_use": [] 20 | }, 21 | { 22 | "message": "Generate initial story plot and characters.", 23 | "tool_use": [] 24 | }, 25 | { 26 | "message": "Create visual representations for the main character.", 27 | "tool_use": ["text_to_image"] 28 | }, 29 | { 30 | "message": "Write descriptive text for each image and analyze each image", 31 | "tool_use": ["doc_question_answering"] 32 | }, 33 | { 34 | "message": "Incorporate it into the story narrative.", 35 | "tool_use": [] 36 | } 37 | ] 38 | return workflow 39 | 40 | def check_path(self, tool_calls): 41 | script_path = os.path.abspath(__file__) 42 | save_dir = os.path.join(os.path.dirname(script_path), "output") # modify the customized output path for saving outputs 43 | if not os.path.exists(save_dir): 44 | os.makedirs(save_dir) 45 | for tool_call in tool_calls: 46 | try: 47 | for k in tool_call["parameters"]: 48 | if "path" in k: 49 | path = tool_call["parameters"][k] 50 | if not path.startswith(save_dir): 51 | tool_call["parameters"][k] = os.path.join(save_dir, os.path.basename(path)) 52 | except Exception: 53 | continue 54 | return tool_calls 55 | 56 | def run(self): 57 | return super().run() 58 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/story_teller/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "story_teller", 3 | "description": [ 4 | "You are a creative storyteller. Given a genre, setting, or character, you can craft engaging narratives. ", 5 | "You can incorporate images to enhance the storytelling experience and even provide audio descriptions of scenes." 6 | ], 7 | "tools": [ 8 | "stability-ai/text_to_image", 9 | "impira/doc_question_answering" 10 | ], 11 | "meta": { 12 | "author": "example", 13 | "version": "0.0.1", 14 | "license": "CC0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/story_teller/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/story_teller/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/tech_support_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | 3 | class TechSupportAgent(ReactAgent): 4 | def __init__(self, 5 | agent_name, 6 | task_input, 7 | agent_process_factory, 8 | log_mode: str 9 | ): 10 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 11 | self.workflow_mode = "manual" 12 | 13 | def automatic_workflow(self): 14 | return super().automatic_workflow() 15 | 16 | def manual_workflow(self): 17 | workflow = [ 18 | { 19 | "message": "identify the user's technical issue or requirement", 20 | "tool_use": [] 21 | }, 22 | { 23 | "message": "search for troubleshooting steps for the identified issue", 24 | "tool_use": ["google_search"] 25 | }, 26 | { 27 | "message": "organize the above information and summarize the solution", 28 | "tool_use": [] 29 | } 30 | ] 31 | return workflow 32 | 33 | def run(self): 34 | return super().run() 35 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/tech_support_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tech_support_agent", 3 | "description": [ 4 | "You are an expert specialized in providing technical support, ", 5 | "including troubleshooting, software recommendations, and updates." 6 | ], 7 | "tools": [ 8 | "google/google_search" 9 | ], 10 | "meta": { 11 | "author": "example", 12 | "version": "0.0.1", 13 | "license": "CC0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/tech_support_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | arxiv 2 | wikipedia 3 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/transcribe_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | class TranscribeAgent(ReactAgent): 3 | def __init__(self, 4 | agent_name, 5 | task_input, 6 | agent_process_factory, 7 | log_mode: str 8 | ): 9 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 10 | self.workflow_mode = "manual" 11 | def manual_workflow(self): 12 | workflow = [ 13 | { 14 | "message": "figure out what to do with the audio", 15 | "tool_use": [ "transcriber/transcriber"], 16 | }, 17 | { 18 | "message": "organize the information and respond to the user", 19 | "tool_use": [ ], 20 | }, 21 | ] 22 | return workflow 23 | 24 | def run(self): 25 | return super().run() 26 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/transcribe_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "transcribe_agent", 3 | "description": [ 4 | "You are an agent who can transcribe audio from the microphone into text. " 5 | ], 6 | "tools": [ 7 | "transcriber/transcriber" 8 | ], 9 | "meta": { 10 | "author": "Om Raheja", 11 | "version": "0.0.1", 12 | "license": "CC0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/transcribe_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | RealtimeSTT -------------------------------------------------------------------------------- /pyopenagi/agents/example/travel_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | class TravelAgent(ReactAgent): 3 | def __init__(self, 4 | agent_name, 5 | task_input, 6 | agent_process_factory, 7 | log_mode: str 8 | ): 9 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 10 | 11 | def automatic_workflow(self): 12 | return super().automatic_workflow() 13 | 14 | def manual_workflow(self): 15 | workflow = [ 16 | { 17 | "message": "identify the destination and search for hotel locations", 18 | "tool_use": ["hotel_location_search"] 19 | }, 20 | { 21 | "message": "based on the hotel locations, find suitable hotels using the hotel_search tool, and select the best one. ", 22 | "tool_use": None 23 | }, 24 | { 25 | "message": "get detailed information about the selected hotel", 26 | "tool_use": ["get_hotel_details"] 27 | }, 28 | { 29 | "message": ["search for the nearest airport to the origin"], 30 | "tool_use": ["airport_search"] 31 | }, 32 | { 33 | "message": ["search for the nearest airport to the destination"], 34 | "tool_use": ["airport_search"] 35 | }, 36 | { 37 | "message": ["find available flights to the destination airport using the correct date"], 38 | "tool_use": ["flight_search"] 39 | }, 40 | { 41 | "message": ["search for restaurant locations near destination"], 42 | "tool_use": ["restaurant_location_search"] 43 | }, 44 | { 45 | "message": ["based on the restaurant locations, find suitable restaurants"], 46 | "tool_use": ["restaurant_search"] 47 | }, 48 | { 49 | "message": ["get detailed information about the selected restaurants"], 50 | "tool_use": ["get_restaurant_details"] 51 | }, 52 | { 53 | "message": ["Gather additional relevant information about the destination the user is visiting"], 54 | "tool_use": ["wikipedia"] 55 | }, 56 | { 57 | "message": ["integrate the information gathered from the previous steps to provide a comprehensive travel plan"], 58 | "tool_use": None 59 | } 60 | ] 61 | return workflow 62 | 63 | def run(self): 64 | return super().run() 65 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/travel_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "travel_agent", 3 | "description": [ 4 | "You are an expert in planning and managing travel itineraries." 5 | ], 6 | "tools": [ 7 | "trip_advisor/" 8 | ], 9 | "meta": { 10 | "author": "example", 11 | "version": "0.0.1", 12 | "license": "CC0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/travel_agent/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/travel_agent/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/example/travel_planner_agent/README.md: -------------------------------------------------------------------------------- 1 | # Travel Planner Agent 2 | 3 | ## Introduction 4 | 5 | The Travel Planner Agent is replicated from the RecAgent used in the paper "TravelPlanner: A Benchmark for Real-World Planning with Language Agents". 6 | 7 | ## Start 8 | 9 | This agent depends on the [database](https://drive.google.com/file/d/1pF1Sw6pBmq2sFkJvm-LzJOqrmfWoQgxE/view). Please download it and place it in the `pyopenagi/environments/` directory, then rename the 'database' as 'travelPlanner', like `pyopenagi/environments/travelPlanner`. 10 | 11 | Then submit an agent like: 12 | 13 | ```python 14 | submitAgent( 15 | agent_name="example/travel_planner_agent", 16 | task_input="Please plan a trip for me starting from Sarasota to Chicago for 3 days, from March 22nd to March 24th, 2022. The budget for this trip is set at $1,900." 17 | ) 18 | ``` 19 | 20 | Then run 21 | ```python 22 | python main.py --llm_name 23 | ``` 24 | such as 25 | ```python 26 | python main.py --llm_name gpt-4o-mini 27 | ``` 28 | 29 | [Others Guide](https://github.com/agiresearch/AIOS) 30 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/travel_planner_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "travel_planner_agent", 3 | "description": [ 4 | "TravelPlanner: A Benchmark for Real-World Planning with Language Agents" 5 | ], 6 | "tools": [ 7 | "travel_planner/accommodations", 8 | "travel_planner/attractions", 9 | "travel_planner/cities", 10 | "travel_planner/flights", 11 | "travel_planner/google_distance_matrix", 12 | "travel_planner/notebook", 13 | "travel_planner/restaurants" 14 | ], 15 | "meta": { 16 | "author": "example", 17 | "version": "0.0.1", 18 | "license": "CC0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pyopenagi/agents/example/travel_planner_agent/meta_requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/agents/example/travel_planner_agent/meta_requirements.txt -------------------------------------------------------------------------------- /pyopenagi/agents/om-raheja/transcribe_agent/agent.py: -------------------------------------------------------------------------------- 1 | from ...react_agent import ReactAgent 2 | class TranscribeAgent(ReactAgent): 3 | def __init__(self, 4 | agent_name, 5 | task_input, 6 | agent_process_factory, 7 | log_mode: str 8 | ): 9 | ReactAgent.__init__(self, agent_name, task_input, agent_process_factory, log_mode) 10 | self.workflow_mode = "automatic" 11 | def manual_workflow(self): 12 | pass 13 | def run(self): 14 | return super().run() -------------------------------------------------------------------------------- /pyopenagi/agents/om-raheja/transcribe_agent/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "transcribe_agent", 3 | "description": [ 4 | "You are an agent who can transcribe audio from the microphone into text. " 5 | ], 6 | "tools": [ 7 | "transcriber/transcriber" 8 | ], 9 | "meta": { 10 | "author": "Om Raheja", 11 | "version": "0.0.1", 12 | "license": "CC0" 13 | } 14 | } -------------------------------------------------------------------------------- /pyopenagi/agents/om-raheja/transcribe_agent/meta_requirements.txt: -------------------------------------------------------------------------------- 1 | RealtimeSTT -------------------------------------------------------------------------------- /pyopenagi/data/agent_tasks/README.md: -------------------------------------------------------------------------------- 1 | # Agent Tasks 2 | This directory test tasks for each agent, containing examples of prompts that could be given to that agent. This is used by `eval.py` when running tests. 3 | -------------------------------------------------------------------------------- /pyopenagi/data/agent_tasks/example/music_composer_task.txt: -------------------------------------------------------------------------------- 1 | Compose a dreamy indie-pop song with a catchy chorus. 2 | Create a dark, atmospheric metal track with heavy guitar riffs. 3 | Write a soulful R&B ballad with smooth vocals and a melodic bassline. 4 | Produce a high-energy EDM track with a pulsating beat and euphoric synths. 5 | Develop a nostalgic country song with a twangy guitar and heartfelt lyrics. 6 | Create a lively Latin jazz piece with intricate rhythms and improvisation. 7 | Write a melancholic blues song with a soulful vocal performance. 8 | Produce a upbeat reggae track with a relaxed vibe and steel guitar. 9 | Compose a cinematic orchestral piece with soaring strings and brass. 10 | Create a funky disco track with a groovy bassline and infectious energy. 11 | Compose a cheerful and optimistic pop song. 12 | Create a mysterious and suspenseful horror soundtrack. 13 | Write a calming and relaxing ambient piece. 14 | Produce a energetic and uplifting workout playlist. 15 | Develop a sorrowful and reflective piano ballad. 16 | Create a playful and whimsical children's song. 17 | Write a passionate and dramatic love song. 18 | Produce a dreamy and ethereal electronic track. 19 | Compose a powerful and anthemic rock song. 20 | Create a nostalgic and bittersweet acoustic guitar piece. 21 | Compose a fast-paced electronic dance track. 22 | Create a slow and melancholic ballad. 23 | Write a medium-tempo pop song with a catchy hook. 24 | Produce a very slow and atmospheric ambient piece. 25 | Develop a moderately fast and energetic rock song. 26 | Compose a piece featuring a solo violin. 27 | Create a track with prominent saxophone improvisation. 28 | Write a song with a driving drum beat. 29 | Produce a piece highlighting acoustic guitar fingerpicking. 30 | Develop a composition centered around a flute melody. 31 | Compose a minimalist electronic piece. 32 | Create a baroque-inspired orchestral suite. 33 | Write a jazz fusion piece with complex harmonies. 34 | Produce a psychedelic rock track with experimental sounds. 35 | Develop a neoclassical piano composition. 36 | Compose a song using only three chords. 37 | Create a piece in an unusual time signature (e.g., 7/8). 38 | Write a song with lyrics about a specific topic (e.g., climate change). 39 | Produce a track using only sampled sounds. 40 | Develop a composition with a strict form (e.g., sonata form). 41 | Compose music for a video game level. 42 | Create a jingle for a commercial. 43 | Write a soundtrack for a short film. 44 | Produce music for a dance performance. 45 | Develop a theme song for a TV show. 46 | Compose a piece inspired by a painting. 47 | Create a song that evokes a specific place or time. 48 | Write a piece that tells a story through music. 49 | Produce a track that combines elements of different genres. 50 | Develop a composition that challenges traditional musical conventions. 51 | Compose a dreamy lo-fi hip-hop beat with vinyl crackles. 52 | Create a gritty punk rock anthem with energetic vocals. 53 | Write a smooth jazz standard with improvisation. 54 | Produce a tropical house track with uplifting melodies. 55 | Develop a classic rock power ballad with soaring vocals. 56 | Compose a mysterious and suspenseful spy thriller theme. 57 | Create a euphoric and uplifting trance track. 58 | Write a somber and reflective classical piece. 59 | Produce a playful and energetic circus music. 60 | Develop a haunting and ethereal dark ambient piece. 61 | Compose a very fast-paced drum and bass track. 62 | Create a slow and dreamy shoegaze piece. 63 | Write a medium-tempo funk groove with horn section. 64 | Produce a very slow and meditative drone piece. 65 | Develop a moderately fast and upbeat pop-punk song. 66 | Compose a piece featuring a solo cello. 67 | Create a track with prominent trumpet solos. 68 | Write a song with a driving bass guitar line. 69 | Produce a piece highlighting acoustic piano improvisation. 70 | Develop a composition centered around a sitar melody. 71 | Compose a minimalist techno track. 72 | Create a romantic era orchestral waltz. 73 | Write a free jazz improvisation with extended techniques. 74 | Produce a gothic rock piece with dark atmosphere. 75 | Develop a neo-soul track with soulful vocals and grooves. 76 | Compose a song using only percussion instruments. 77 | Create a piece in a microtonal tuning system. 78 | Write a song with lyrics inspired by a poem. 79 | Produce a track using only field recordings. 80 | Develop a composition with a strict rhythmic pattern. 81 | Compose music for a dance video. 82 | Create a jingle for a radio station. 83 | Write a soundtrack for a documentary. 84 | Produce music for a fashion show. 85 | Develop a theme song for a podcast. 86 | Compose a piece inspired by a natural phenomenon. 87 | Create a song that evokes a specific emotion. 88 | Write a piece that reflects a cultural tradition. 89 | Produce a track that combines electronic and acoustic elements. 90 | Develop a composition that pushes the boundaries of music. 91 | Compose a catchy synth-wave track with retro vibes. 92 | Create a heavy metal breakdown with crushing guitar riffs. 93 | Write a smooth jazz fusion piece with electric piano. 94 | Produce a deep house track with soulful vocals. 95 | Develop a classic rock power ballad with guitar solo. 96 | Compose a energetic and uplifting pop anthem. 97 | Create a dark and brooding industrial track. 98 | Write a calming and relaxing new age piece. 99 | Produce a playful and whimsical carnival music. 100 | Develop a haunting and ethereal gothic rock ballad. 101 | -------------------------------------------------------------------------------- /pyopenagi/data/agent_tasks/example/travel_agent_task.txt: -------------------------------------------------------------------------------- 1 | I want to take a trip to Paris, France from July 4th to July 10th, 2024, and I am traveling from New York City. Help me plan this trip. 2 | I want to take a trip to San Francisco, California from May 10th to May 15th, 2024, and I am traveling from Miami, Florida. Help me plan this trip. 3 | I want to take a trip to Tokyo, Japan from September 1st to September 10th, 2024, and I am traveling from Los Angeles, California. Help me plan this trip. 4 | I want to take a trip to Austin, Texas from March 15th to March 20th, 2024, and I am traveling from Chicago, Illinois. Help me plan this trip. 5 | I want to take a trip to Sydney, Australia from December 20th to December 30th, 2024, and I am traveling from Seattle, Washington. Help me plan this trip. 6 | I want to take a trip to Denver, Colorado from August 5th to August 10th, 2024, and I am traveling from Houston, Texas. Help me plan this trip. 7 | I want to take a trip to Rio de Janeiro, Brazil from February 1st to February 10th, 2024, and I am traveling from Atlanta, Georgia. Help me plan this trip. 8 | I want to take a trip to Portland, Oregon from June 20th to June 25th, 2024, and I am traveling from Phoenix, Arizona. Help me plan this trip. 9 | I want to take a trip to Rome, Italy from May 15th to May 22nd, 2024, and I am traveling from Boston, Massachusetts. Help me plan this trip. 10 | I want to take a trip to Orlando, Florida from July 5th to July 12th, 2024, and I am traveling from Philadelphia, Pennsylvania. Help me plan this trip. 11 | I want to take a trip to Berlin, Germany from August 14th to August 21st, 2024, and I am traveling from San Francisco, California. Help me plan this trip. 12 | I want to take a trip to Las Vegas, Nevada from April 10th to April 15th, 2024, and I am traveling from Denver, Colorado. Help me plan this trip. 13 | I want to take a trip to Cairo, Egypt from November 10th to November 20th, 2024, and I am traveling from Dallas, Texas. Help me plan this trip. 14 | I want to take a trip to Nashville, Tennessee from September 10th to September 15th, 2024, and I am traveling from Indianapolis, Indiana. Help me plan this trip. 15 | I want to take a trip to London, United Kingdom from June 7th to June 14th, 2024, and I am traveling from Washington D.C. Help me plan this trip. 16 | I want to take a trip to Honolulu, Hawaii from October 5th to October 12th, 2024, and I am traveling from San Diego, California. Help me plan this trip. 17 | I want to take a trip to Bangkok, Thailand from April 1st to April 10th, 2024, and I am traveling from Seattle, Washington. Help me plan this trip. 18 | I want to take a trip to Chicago, Illinois from May 10th to May 15th, 2024, and I am traveling from New York City. Help me plan this trip. 19 | I want to take a trip to Barcelona, Spain from July 10th to July 17th, 2024, and I am traveling from Miami, Florida. Help me plan this trip. 20 | I want to take a trip to Dallas, Texas from November 15th to November 20th, 2024, and I am traveling from Orlando, Florida. Help me plan this trip. 21 | I want to take a trip to Athens, Greece from May 1st to May 10th, 2024, and I am traveling from Denver, Colorado. Help me plan this trip. 22 | I want to take a trip to New Orleans, Louisiana from February 20th to February 25th, 2024, and I am traveling from Atlanta, Georgia. Help me plan this trip. 23 | I want to take a trip to Dubai, UAE from October 15th to October 25th, 2024, and I am traveling from Detroit, Michigan. Help me plan this trip. 24 | I want to take a trip to Los Angeles, California from March 1st to March 5th, 2024, and I am traveling from Houston, Texas. Help me plan this trip. 25 | I want to take a trip to Dublin, Ireland from June 1st to June 8th, 2024, and I am traveling from Boston, Massachusetts. Help me plan this trip. 26 | I want to take a trip to Austin, Texas from September 5th to September 10th, 2024, and I am traveling from Phoenix, Arizona. Help me plan this trip. 27 | I want to take a trip to Hong Kong from December 1st to December 10th, 2024, and I am traveling from San Jose, California. Help me plan this trip. 28 | I want to take a trip to Seattle, Washington from July 15th to July 20th, 2024, and I am traveling from Minneapolis, Minnesota. Help me plan this trip. 29 | I want to take a trip to Amsterdam, Netherlands from July 1st to July 8th, 2024, and I am traveling from Indianapolis, Indiana. Help me plan this trip. 30 | I want to take a trip to Miami, Florida from January 5th to January 10th, 2024, and I am traveling from Philadelphia, Pennsylvania. Help me plan this trip. 31 | -------------------------------------------------------------------------------- /pyopenagi/queues/README.md: -------------------------------------------------------------------------------- 1 | # pyopenagi/queues 2 | 3 | This contains implementations for queries to be passed to the LLM in a queue format so that we can have some waiting while one request is completing. 4 | -------------------------------------------------------------------------------- /pyopenagi/queues/base_queue.py: -------------------------------------------------------------------------------- 1 | import queue 2 | 3 | class BaseQueue: 4 | 5 | _queue = queue.Queue() 6 | 7 | @classmethod 8 | def add_message(cls, message): 9 | cls._queue.put(message) 10 | 11 | @classmethod 12 | def get_message(cls): 13 | return cls._queue.get(block=True, timeout=1) 14 | 15 | @classmethod 16 | def is_empty(cls): 17 | return cls._queue.empty() 18 | -------------------------------------------------------------------------------- /pyopenagi/queues/llm_request_queue.py: -------------------------------------------------------------------------------- 1 | from .base_queue import BaseQueue 2 | 3 | class LLMRequestQueue(BaseQueue): 4 | pass 5 | -------------------------------------------------------------------------------- /pyopenagi/tools/README.md: -------------------------------------------------------------------------------- 1 | # pyopenagi/tools 2 | 3 | This is where all the tools are located. Each tool requires you to subclass the base tool and add the features required. 4 | -------------------------------------------------------------------------------- /pyopenagi/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/tools/__init__.py -------------------------------------------------------------------------------- /pyopenagi/tools/arxiv/arxiv.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from ..base import BaseTool 4 | 5 | import arxiv 6 | 7 | from typing import Optional 8 | 9 | class Arxiv(BaseTool): 10 | """Arxiv Tool, refactored from Langchain. 11 | 12 | To use, you should have the ``arxiv`` python package installed. 13 | https://lukasschwab.me/arxiv.py/index.html 14 | This wrapper will use the Arxiv API to conduct searches and 15 | fetch document summaries. By default, it will return the document summaries 16 | of the top-k results. 17 | If the query is in the form of arxiv identifier 18 | (see https://info.arxiv.org/help/find/index.html), it will return the paper 19 | corresponding to the arxiv identifier. 20 | It limits the Document content by doc_content_chars_max. 21 | Set doc_content_chars_max=None if you don't want to limit the content size. 22 | 23 | Attributes: 24 | top_k_results: number of the top-scored document used for the arxiv tool 25 | ARXIV_MAX_QUERY_LENGTH: the cut limit on the query used for the arxiv tool. 26 | load_max_docs: a limit to the number of loaded documents 27 | load_all_available_meta: 28 | if True: the `metadata` of the loaded Documents contains all available 29 | meta info (see https://lukasschwab.me/arxiv.py/index.html#Result), 30 | if False: the `metadata` contains only the published date, title, 31 | authors and summary. 32 | doc_content_chars_max: an optional cut limit for the length of a document's 33 | content 34 | """ 35 | 36 | def __init__(self): 37 | super().__init__() 38 | self.top_k_results: int = 3 39 | self.ARXIV_MAX_QUERY_LENGTH: int = 300 40 | self.load_max_docs: int = 100 41 | self.load_all_available_meta: bool = False 42 | self.doc_content_chars_max: Optional[int] = 4000 43 | self.build_client() 44 | 45 | def is_arxiv_identifier(self, query: str) -> bool: 46 | """Check if a query is an arxiv identifier.""" 47 | arxiv_identifier_pattern = r"\d{2}(0[1-9]|1[0-2])\.\d{4,5}(v\d+|)|\d{7}.*" 48 | for query_item in query[: self.ARXIV_MAX_QUERY_LENGTH].split(): 49 | match_result = re.match(arxiv_identifier_pattern, query_item) 50 | if not match_result: 51 | return False 52 | assert match_result is not None 53 | if not match_result.group(0) == query_item: 54 | return False 55 | return True 56 | 57 | def build_client(self): 58 | """Validate that the python package exists in environment.""" 59 | self.arxiv_search = arxiv.Search 60 | self.arxiv_exceptions = arxiv.ArxivError 61 | 62 | def run(self, params) -> str: 63 | """ 64 | Performs an arxiv search and A single string 65 | with the publish date, title, authors, and summary 66 | for each article separated by two newlines. 67 | 68 | If an error occurs or no documents found, error text 69 | is returned instead. Wrapper for 70 | https://lukasschwab.me/arxiv.py/index.html#Search 71 | 72 | Args: 73 | query: a plaintext search query 74 | """ # noqa: E501 75 | query = params["query"] 76 | try: 77 | if self.is_arxiv_identifier(query): 78 | results = self.arxiv_search( 79 | id_list=query.split(), 80 | max_results=self.top_k_results, 81 | ).results() 82 | else: 83 | results = self.arxiv_search( # type: ignore 84 | query[: self.ARXIV_MAX_QUERY_LENGTH], max_results=self.top_k_results 85 | ).results() 86 | except self.arxiv_exceptions as ex: 87 | return f"Arxiv exception: {ex}" 88 | docs = [ 89 | f"Published: {result.updated.date()}\n" 90 | f"Title: {result.title}\n" 91 | f"Authors: {', '.join(a.name for a in result.authors)}\n" 92 | f"Summary: {result.summary}" 93 | for result in results 94 | ] 95 | if docs: 96 | return "\n\n".join(docs)[: self.doc_content_chars_max] 97 | else: 98 | return "No good Arxiv Result was found" 99 | 100 | def get_tool_call_format(self): 101 | tool_call_format = { 102 | "type": "function", 103 | "function": { 104 | "name": "arxiv", 105 | "description": "Query articles or topics in arxiv", 106 | "parameters": { 107 | "type": "object", 108 | "properties": { 109 | "query": { 110 | "type": "string", 111 | "description": "Input query that describes what to search in arxiv" 112 | } 113 | }, 114 | "required": [ 115 | "query" 116 | ] 117 | } 118 | } 119 | } 120 | return tool_call_format 121 | -------------------------------------------------------------------------------- /pyopenagi/tools/base.py: -------------------------------------------------------------------------------- 1 | class BaseTool: 2 | """Base class for calling tools 3 | """ 4 | def __init__(self) -> None: 5 | pass 6 | 7 | def run(self, params) -> None: 8 | pass 9 | 10 | def get_tool_call_format(self) -> dict: 11 | """Get tool calling format following the function calling from OpenAI: https://platform.openai.com/docs/guides/function-calling 12 | """ 13 | pass 14 | 15 | class BaseRapidAPITool(BaseTool): 16 | """Base class for calling tools from rapidapi hub: https://rapidapi.com/hub 17 | """ 18 | def __init__(self): 19 | super().__init__() 20 | self.url: str = None 21 | self.host_name: str = None 22 | self.api_key: str = None 23 | 24 | def run(self, params: dict): 25 | pass 26 | 27 | def get_tool_call_format(self) -> dict: 28 | pass 29 | 30 | 31 | class BaseHuggingfaceTool(BaseTool): 32 | """Base class for calling models from huggingface 33 | 34 | """ 35 | def __init__(self): 36 | super().__init__() 37 | self.url: str = None 38 | self.host_name: str = None 39 | self.api_key: str = None 40 | 41 | def run(self, params: dict): 42 | pass 43 | 44 | def get_tool_call_format(self) -> dict: 45 | pass 46 | -------------------------------------------------------------------------------- /pyopenagi/tools/bing/bing_search.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from ..base import BaseTool 4 | 5 | from typing import List 6 | # from pydantic import root_validator 7 | 8 | from pyopenagi.utils.utils import get_from_env 9 | 10 | class BingSearch(BaseTool): 11 | """Bing Search Tool, refactored from langchain. 12 | In order to set this up, follow instructions at: 13 | https://levelup.gitconnected.com/api-tutorial-how-to-use-bing-web-search-api-in-python-4165d5592a7e 14 | """ 15 | def __init__(self): 16 | super().__init__() 17 | self.url = "https://api.bing.microsoft.com/v7.0/search" # temporarily 18 | self.bing_subscription_key = get_from_env("BING_SUBSCRIPTION_KEY") 19 | self.k: int = 10 # topk searched results 20 | # search_kwargs: dict 21 | 22 | def _bing_search_results(self, search_term: str, count: int) -> List[dict]: 23 | headers = {"Ocp-Apim-Subscription-Key": self.bing_subscription_key} 24 | params = { 25 | "q": search_term, 26 | "count": count, 27 | "textDecorations": True, 28 | "textFormat": "HTML", 29 | # **self.search_kwargs, 30 | } 31 | response = requests.get( 32 | self.bing_search_url, 33 | headers=headers, 34 | params=params, # type: ignore 35 | ) 36 | response.raise_for_status() 37 | search_results = response.json() 38 | if "webPages" in search_results: 39 | return search_results["webPages"]["value"] 40 | return [] 41 | 42 | def run(self, query: str) -> str: 43 | """Run query through BingSearch and parse result.""" 44 | response = self._bing_search_results(query, count=self.k) 45 | result = self.parse_result(response) 46 | return result 47 | 48 | def parse_result(self, response): 49 | snippets = [] 50 | if len(response) == 0: 51 | return "No good Bing Search Result was found" 52 | for result in response: 53 | snippets.append(result["snippet"]) 54 | 55 | return " ".join(snippets) 56 | -------------------------------------------------------------------------------- /pyopenagi/tools/currency_converter/currency_converter.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | class CurrencyConverter(BaseRapidAPITool): 8 | def __init__(self): 9 | super().__init__() 10 | self.url = "https://currency-converter5.p.rapidapi.com/currency/convert" 11 | self.host_name = "currency-converter5.p.rapidapi.com" 12 | self.api_key = get_from_env("RAPID_API_KEY") 13 | 14 | def run(self, params: dict): 15 | headers = { 16 | "X-RapidAPI-Key": self.api_key, 17 | "X-RapidAPI-Host": self.host_name 18 | } 19 | try: 20 | self.query_string = { 21 | "from": params["from"], 22 | "to": params["to"], 23 | "amount": params["amount"] if "amount" in params else "1.0" 24 | } 25 | except ValueError: 26 | raise KeyError( 27 | "The keys in params do not match the excepted keys in params for currency converter api. " 28 | "Please make sure it contains two keys: 'from' and 'to'" 29 | ) 30 | 31 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 32 | 33 | result = self.parse_result(response) 34 | return result 35 | 36 | def parse_result(self, response) -> str: 37 | results = [] 38 | amount = str(response["amount"]) 39 | base = response["base_currency_name"] 40 | rates = response["rates"] 41 | 42 | for key, value in rates.items(): 43 | converted = value["currency_name"] 44 | converted_rate = value["rate"] 45 | converted_amount = value["rate_for_amount"] 46 | results.append("Currency from " + base + " to " + converted + "is " + converted_rate + ".", ) 47 | results.append(amount + " " + base + "can be converted to " + converted_amount + " " + converted + ".") 48 | 49 | return " ".join(results) 50 | 51 | def get_tool_call_format(self): 52 | tool_call_format = { 53 | "type": "function", 54 | "function": { 55 | "name": "currency_converter", 56 | "description": "Provides currency exchange rates convert base currency to desired currency with the given amount", 57 | "parameters": { 58 | "type": "object", 59 | "properties": { 60 | "from": { 61 | "type": "string", 62 | "description": "Base currency code, e.g., AUD, CAD, EUR, GBP..." 63 | }, 64 | "to": { 65 | "type": "string", 66 | "description": "Desired currency code, e.g., AUD, CAD, EUR, GBP..." 67 | }, 68 | "amount": { 69 | "type": "string", 70 | "default": "1.0", 71 | "description": "The amount to be converted" 72 | } 73 | }, 74 | "required": [ 75 | "from", 76 | "to" 77 | ] 78 | } 79 | } 80 | } 81 | return tool_call_format 82 | -------------------------------------------------------------------------------- /pyopenagi/tools/google/google_places.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Any, Dict, Optional 3 | 4 | from ..base import BaseTool 5 | 6 | from pyopenagi.utils.utils import get_from_env 7 | 8 | 9 | class GooglePlaces(BaseTool): 10 | """Google Places API, refactored from langchain. 11 | 12 | To use, you should have the ``googlemaps`` python package installed, 13 | **an API key for the google maps platform**, 14 | and the environment variable ''GPLACES_API_KEY'' 15 | set with your API key , or pass 'gplaces_api_key' 16 | as a named parameter to the constructor. 17 | 18 | By default, this will return the all the results on the input query. 19 | You can use the top_k_results argument to limit the number of results. 20 | 21 | Example: 22 | .. code-block:: python 23 | 24 | 25 | from langchain_community.utilities import GooglePlacesAPIWrapper 26 | gplaceapi = GooglePlacesAPIWrapper() 27 | """ 28 | def __init__(self): 29 | super().__init__() 30 | self.gplaces_api_key = get_from_env("GPLACES_API_KEY") 31 | self.google_map_client: Any #: :meta private: 32 | self.top_k_results: Optional[int] = None 33 | 34 | def build_client(self): 35 | """Validate that api key is in your environment variable.""" 36 | try: 37 | import googlemaps 38 | 39 | client = googlemaps.Client(self.gplaces_api_key) 40 | except ImportError: 41 | raise ImportError( 42 | "Could not import googlemaps python package. " 43 | "Please install it with `pip install googlemaps`." 44 | ) 45 | return client 46 | 47 | def run(self, query: str) -> str: 48 | """Run Places search and get k number of places that exists that match.""" 49 | search_results = self.google_map_client.places(query)["results"] 50 | num_to_return = len(search_results) 51 | 52 | places = [] 53 | 54 | if num_to_return == 0: 55 | return "Google Places did not find any places that match the description" 56 | 57 | num_to_return = ( 58 | num_to_return 59 | if self.top_k_results is None 60 | else min(num_to_return, self.top_k_results) 61 | ) 62 | 63 | for i in range(num_to_return): 64 | result = search_results[i] 65 | details = self.fetch_place_details(result["place_id"]) 66 | 67 | if details is not None: 68 | places.append(details) 69 | 70 | return "\n".join([f"{i+1}. {item}" for i, item in enumerate(places)]) 71 | 72 | def fetch_place_details(self, place_id: str) -> Optional[str]: 73 | try: 74 | place_details = self.google_map_client.place(place_id) 75 | place_details["place_id"] = place_id 76 | formatted_details = self.format_place_details(place_details) 77 | return formatted_details 78 | except Exception as e: 79 | logging.error(f"An Error occurred while fetching place details: {e}") 80 | return None 81 | 82 | def format_place_details(self, place_details: Dict[str, Any]) -> Optional[str]: 83 | try: 84 | name = place_details.get("result", {}).get("name", "Unknown") 85 | address = place_details.get("result", {}).get( 86 | "formatted_address", "Unknown" 87 | ) 88 | phone_number = place_details.get("result", {}).get( 89 | "formatted_phone_number", "Unknown" 90 | ) 91 | website = place_details.get("result", {}).get("website", "Unknown") 92 | place_id = place_details.get("result", {}).get("place_id", "Unknown") 93 | 94 | formatted_details = ( 95 | f"{name}\nAddress: {address}\n" 96 | f"Google place ID: {place_id}\n" 97 | f"Phone: {phone_number}\nWebsite: {website}\n\n" 98 | ) 99 | return formatted_details 100 | except Exception as e: 101 | logging.error(f"An error occurred while formatting place details: {e}") 102 | return None 103 | -------------------------------------------------------------------------------- /pyopenagi/tools/google/google_search.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseTool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | from typing import List, Any 6 | 7 | class GoogleSearch(BaseTool): 8 | """Google Search Tool, refactored from langchain. 9 | 10 | Adapted from: Instructions adapted from https://stackoverflow.com/questions/ 11 | 37083058/ 12 | programmatically-searching-google-in-python-using-custom-search 13 | 14 | 1. Install google-api-python-client 15 | - If you don't already have a Google account, sign up. 16 | - If you have never created a Google APIs Console project, 17 | read the Managing Projects page and create a project in the Google API Console. 18 | - Install the library using pip install google-api-python-client 19 | 20 | 2. Enable the Custom Search API 21 | - Navigate to the APIs & Services→Dashboard panel in Cloud Console. 22 | - Click Enable APIs and Services. 23 | - Search for Custom Search API and click on it. 24 | - Click Enable. 25 | URL for it: https://console.cloud.google.com/apis/library/customsearch.googleapis 26 | .com 27 | 28 | 3. To create an API key: 29 | - Navigate to the APIs & Services → Credentials panel in Cloud Console. 30 | - Select Create credentials, then select API key from the drop-down menu. 31 | - The API key created dialog box displays your newly created key. 32 | - You now have an API_KEY 33 | 34 | Alternatively, you can just generate an API key here: 35 | https://developers.google.com/custom-search/docs/paid_element#api_key 36 | 37 | 4. Setup Custom Search Engine so you can search the entire web 38 | - Create a custom search engine here: https://programmablesearchengine.google.com/. 39 | - In `What to search` to search, pick the `Search the entire Web` option. 40 | After search engine is created, you can click on it and find `Search engine ID` 41 | on the Overview page. 42 | 43 | """ 44 | def __init__(self): 45 | super().__init__() 46 | self.google_api_key = get_from_env("GOOGLE_API_KEY") 47 | self.google_cse_id = get_from_env("GOOGLE_CSE_ID") 48 | self.k: int = 10 # topk searched results 49 | self.search_engine = self.build_engine() 50 | self.siterestrict: bool = False 51 | 52 | def build_engine(self): 53 | try: 54 | from googleapiclient.discovery import build 55 | 56 | except ImportError: 57 | raise ImportError( 58 | "google-api-python-client is not installed. " 59 | "Please install it with `pip install google-api-python-client" 60 | ">=2.100.0`" 61 | ) 62 | engine = build("customsearch", "v1", developerKey=self.google_api_key) 63 | return engine 64 | 65 | def _google_search_results(self, search_term: str, **kwargs: Any) -> List[dict]: 66 | cse = self.search_engine.cse() 67 | if self.siterestrict: 68 | cse = cse.siterestrict() # TODO add siterestrict verification 69 | res = cse.list(q=search_term, cx=self.google_cse_id, **kwargs).execute() 70 | return res.get("items", []) 71 | 72 | 73 | def run(self, params: dict) -> str: 74 | """Run query through GoogleSearch and parse result.""" 75 | query = params["query"] 76 | # print(query) 77 | response = self._google_search_results(query, num=self.k) 78 | # print(response) 79 | result = self.parse_result(response) 80 | print(result) 81 | return result 82 | 83 | def parse_result(self, response): 84 | snippets = [] 85 | if len(response) == 0: 86 | return "No good Google Search Result was found" 87 | for result in response: 88 | if "snippet" in result: 89 | snippets.append(result["snippet"]) 90 | 91 | return " ".join(snippets) 92 | 93 | def get_tool_call_format(self): 94 | tool_call_format = { 95 | "type": "function", 96 | "function": { 97 | "name": "google_search", 98 | "description": "search information using google search api", 99 | "parameters": { 100 | "type": "object", 101 | "properties": { 102 | "query": { 103 | "type": "string", 104 | "description": "prompt description of the image to be generated" 105 | } 106 | }, 107 | "required": [ 108 | "query" 109 | ] 110 | } 111 | } 112 | } 113 | return tool_call_format 114 | -------------------------------------------------------------------------------- /pyopenagi/tools/imdb/top_movie.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from typing import Any, Dict, List, Optional 4 | 5 | # from pydantic import root_validator 6 | 7 | from pyopenagi.utils.utils import get_from_env 8 | 9 | import requests 10 | 11 | class TopMovieAPI(BaseRapidAPITool): 12 | def __init__(self): 13 | super().__init__() 14 | self.url = "https://imdb-top-100-movies.p.rapidapi.com/" 15 | self.host_name = "imdb-top-100-movies.p.rapidapi.com" 16 | 17 | self.api_key = get_from_env("RAPID_API_KEY") 18 | 19 | def run(self, params): 20 | start = int(params["start"]) if "start" in params else 1 21 | end = int(params["end"]) 22 | headers = { 23 | "X-RapidAPI-Key": self.api_key, 24 | "X-RapidAPI-Host": self.host_name 25 | } 26 | response = requests.get(self.url, headers=headers).json() 27 | result = self.parse_result(response, start, end) 28 | return result 29 | 30 | 31 | def parse_result(self, response, start, end) -> str: 32 | result = [] 33 | # print(response) 34 | for i in range(start, end): 35 | item = response[i] 36 | result.append(f'{item["title"]}, {item["genre"]}, {item["rating"]}, published in {item["year"]}') 37 | 38 | return f"Top {start}-{end} series ranked by IMDB are: " + ";".join(result) 39 | 40 | def get_tool_call_format(self): 41 | tool_call_format = { 42 | "type": "function", 43 | "function": { 44 | "name": "top_movies", 45 | "description": "Query the latest top start-to-end movies ranked by Imdb", 46 | "parameters": { 47 | "type": "object", 48 | "properties": { 49 | "start": { 50 | "type": "string", 51 | "description": "start of the rank range of the Imdb movies", 52 | "default": "1" 53 | }, 54 | "end": { 55 | "type": "string", 56 | "description": "end of the rank range of the Imdb movies" 57 | } 58 | }, 59 | "required": [ 60 | "end" 61 | ] 62 | } 63 | } 64 | } 65 | return tool_call_format 66 | -------------------------------------------------------------------------------- /pyopenagi/tools/imdb/top_movies.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | # from pydantic import root_validator 4 | 5 | from pyopenagi.utils.utils import get_from_env 6 | 7 | import requests 8 | 9 | class TopMovies(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://imdb-top-100-movies.p.rapidapi.com/" 13 | self.host_name = "imdb-top-100-movies.p.rapidapi.com" 14 | 15 | self.api_key = get_from_env("RAPID_API_KEY") 16 | 17 | def run(self, params): 18 | start = int(params["start"]) if "start" in params else 1 19 | end = int(params["end"]) 20 | headers = { 21 | "X-RapidAPI-Key": self.api_key, 22 | "X-RapidAPI-Host": self.host_name 23 | } 24 | response = requests.get(self.url, headers=headers).json() 25 | result = self.parse_result(response, start, end) 26 | return result 27 | 28 | 29 | def parse_result(self, response, start, end) -> str: 30 | result = [] 31 | # print(response) 32 | for i in range(start, end): 33 | item = response[i] 34 | result.append(f'{item["title"]}, {item["genre"]}, {item["rating"]}, published in {item["year"]}') 35 | 36 | return f"Top {start}-{end} series ranked by IMDB are: " + ";".join(result) 37 | 38 | def get_tool_call_format(self): 39 | tool_call_format = { 40 | "type": "function", 41 | "function": { 42 | "name": "top_movies", 43 | "description": "Query the latest top start-to-end movies ranked by Imdb", 44 | "parameters": { 45 | "type": "object", 46 | "properties": { 47 | "start": { 48 | "type": "string", 49 | "description": "start of the rank range of the Imdb movies", 50 | "default": "1" 51 | }, 52 | "end": { 53 | "type": "string", 54 | "description": "end of the rank range of the Imdb movies" 55 | } 56 | }, 57 | "required": [ 58 | "end" 59 | ] 60 | } 61 | } 62 | } 63 | return tool_call_format 64 | -------------------------------------------------------------------------------- /pyopenagi/tools/imdb/top_series.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | class TopSeries(BaseRapidAPITool): 8 | def __init__(self): 9 | super().__init__() 10 | self.url = "https://imdb-top-100-movies.p.rapidapi.com/series/" 11 | self.host_name = "imdb-top-100-movies.p.rapidapi.com" 12 | 13 | self.api_key = get_from_env("RAPID_API_KEY") 14 | 15 | def run(self, params): 16 | start = int(params["start"]) if "start" in params else 1 17 | end = int(params["end"]) 18 | headers = { 19 | "X-RapidAPI-Key": self.api_key, 20 | "X-RapidAPI-Host": self.host_name 21 | } 22 | response = requests.get(self.url, headers=headers).json() 23 | result = self.parse_result(response, start, end) 24 | return result 25 | 26 | 27 | def parse_result(self, response, start, end) -> str: 28 | result = [] 29 | for i in range(start, end): 30 | item = response[i] 31 | result.append(f'{item["title"]}, {item["genre"]}, {item["rating"]}, published in {item["year"]}') 32 | 33 | return f"Top {start}-{end} series ranked by IMDB are: " + ";".join(result) 34 | 35 | def get_tool_call_format(self): 36 | tool_call_format = { 37 | "type": "function", 38 | "function": { 39 | "name": "imdb_top_series", 40 | "description": "Query the latest top start-to-end series ranked by Imdb", 41 | "parameters": { 42 | "type": "object", 43 | "properties": { 44 | "start": { 45 | "type": "string", 46 | "description": "start of the rank range of the Imdb series", 47 | "default": "1" 48 | }, 49 | "end": { 50 | "type": "string", 51 | "description": "end of the rank range of the Imdb series" 52 | } 53 | }, 54 | "required": [ 55 | "end" 56 | ] 57 | } 58 | } 59 | } 60 | return tool_call_format 61 | -------------------------------------------------------------------------------- /pyopenagi/tools/impira/doc_question_answering.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from ...utils.utils import get_from_env 4 | 5 | from ..base import BaseHuggingfaceTool 6 | 7 | import base64 8 | 9 | class DocQuestionAnswering(BaseHuggingfaceTool): 10 | def __init__(self): 11 | super().__init__() 12 | 13 | def run(self, params): 14 | 15 | API_URL = "https://api-inference.huggingface.co/models/impira/layoutlm-document-qa" 16 | headers = {"Authorization": "Bearer " + get_from_env("HF_AUTH_TOKENS")} 17 | 18 | question = params["question"] 19 | 20 | path = params["path"] 21 | 22 | with open(path, "rb") as f: 23 | img = f.read() 24 | 25 | payload = { 26 | "input": { 27 | "image": base64.b64encode(img).decode("utf-8") 28 | }, 29 | "question": question 30 | } 31 | response = requests.post(API_URL, headers=headers, json=payload) 32 | return response.json() 33 | 34 | 35 | def get_tool_call_format(self): 36 | tool_call_format = { 37 | "type": "function", 38 | "function": { 39 | "name": "doc_question_answering", 40 | "description": "answer the question based on the given document", 41 | "parameters": { 42 | "type": "object", 43 | "properties": { 44 | "question": { 45 | "type": "string", 46 | "description": "question that needs to be answered" 47 | }, 48 | "path": { 49 | "type": "string", 50 | "description": "path of the document" 51 | } 52 | }, 53 | "required": [ 54 | "question", 55 | "path" 56 | ] 57 | } 58 | } 59 | } 60 | return tool_call_format 61 | -------------------------------------------------------------------------------- /pyopenagi/tools/meteosource_weather/find_place.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | # from pydantic import root_validator 4 | 5 | from pyopenagi.utils.utils import get_from_env 6 | 7 | import requests 8 | 9 | class SongAutocompleteAPI(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://ai-weather-by-meteosource.p.rapidapi.com/find_places" 13 | self.host_name = "ai-weather-by-meteosource.p.rapidapi.com" 14 | 15 | self.api_key = get_from_env("RAPID_API_KEY") 16 | 17 | def run(self, params): 18 | headers = { 19 | "X-RapidAPI-Key": self.api_key, 20 | "X-RapidAPI-Host": self.host_name 21 | } 22 | try: 23 | self.query_string = { 24 | "text": params["text"], 25 | "language": params["language"] 26 | } 27 | except ValueError: 28 | raise KeyError( 29 | "The keys in params do not match the excepted keys in params for weather find place api. " 30 | "Please make sure it contains two keys: 'text' and 'language'" 31 | ) 32 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 33 | 34 | result = self.parse_result(response) 35 | return result 36 | 37 | 38 | def parse_result(self, response) -> str: 39 | location = [ 40 | response["radm_area1"], 41 | response["adm_area2"], 42 | response["country"], 43 | response["lat"], 44 | response["lon"] 45 | ] 46 | return f"Found place of {response["name"]}: " + ",".join(location) 47 | -------------------------------------------------------------------------------- /pyopenagi/tools/moonphase/moon_phase_search.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | class MoonPhaseSearch(BaseRapidAPITool): 8 | def __init__(self): 9 | super().__init__() 10 | self.url = "https://moon-phase.p.rapidapi.com/basic" 11 | self.host_name = "moon-phase.p.rapidapi.com" 12 | self.api_key = get_from_env("RAPID_API_KEY") 13 | 14 | def run(self): 15 | headers = { 16 | "X-RapidAPI-Key": self.api_key, 17 | "X-RapidAPI-Host": self.host_name 18 | } 19 | response = requests.get(self.url, headers=headers) 20 | 21 | result = self.parse_result(response) 22 | return result 23 | 24 | def parse_result(self, response) -> str: 25 | return f'Current moon phase is {response["phase_name"]}. It has {response["days_until_next_full_moon"]} until next full moon. It has {response["days_until_next_new_moon"]} until the next new moon.' 26 | -------------------------------------------------------------------------------- /pyopenagi/tools/openai/speech_to_text.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | import soundfile as sf 4 | 5 | from ...utils.utils import get_from_env 6 | 7 | from ..base import BaseHuggingfaceTool 8 | 9 | class SpeechToText(BaseHuggingfaceTool): 10 | def __init__(self): 11 | super().__init__() 12 | 13 | def run(self, params): 14 | API_URL = "https://api-inference.huggingface.co/models/openai/whisper-large-v3" 15 | headers = {"Authorization": "Bearer " + get_from_env("HF_AUTH_TOKENS")} 16 | 17 | path = params["path"] 18 | 19 | data = sf.read(path) 20 | response = requests.post(API_URL, headers=headers, data=data) 21 | text = response.content 22 | return text 23 | 24 | def get_tool_call_format(self): 25 | tool_call_format = { 26 | "type": "function", 27 | "function": { 28 | "name": "speech_to_text", 29 | "description": "translate the voice into texts", 30 | "parameters": { 31 | "type": "object", 32 | "properties": { 33 | "path": { 34 | "type": "string", 35 | "description": "path of the saved audio" 36 | } 37 | }, 38 | "required": [ 39 | "path" 40 | ] 41 | } 42 | } 43 | } 44 | return tool_call_format 45 | -------------------------------------------------------------------------------- /pyopenagi/tools/shazam/song_auto_complete.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | # from pydantic import root_validator 4 | 5 | from pyopenagi.utils.utils import get_from_env 6 | 7 | import requests 8 | 9 | class SongAutoComplete(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://shazam.p.rapidapi.com/auto-complete" 13 | self.host_name = "shazam.p.rapidapi.com" 14 | 15 | self.api_key = get_from_env("RAPID_API_KEY") 16 | 17 | def run(self, params): 18 | headers = { 19 | "X-RapidAPI-Key": self.api_key, 20 | "X-RapidAPI-Host": self.host_name 21 | } 22 | try: 23 | self.query_string = { 24 | "term": params["term"], 25 | "locale": params["locale"] 26 | } 27 | except ValueError: 28 | raise KeyError( 29 | "The keys in params do not match the excepted keys in params for currency converter api. " 30 | "Please make sure it contains two keys: 'term' and 'locale'" 31 | ) 32 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 33 | 34 | result = self.parse_result(response) 35 | return result 36 | 37 | 38 | def parse_result(self, response) -> str: 39 | return "Completion hints are: " + ",".join(response["hints"].values()) 40 | -------------------------------------------------------------------------------- /pyopenagi/tools/stability-ai/sdxl_turbo.py: -------------------------------------------------------------------------------- 1 | from diffusers import AutoPipelineForText2Image 2 | import torch 3 | 4 | from ..base import BaseHuggingfaceTool 5 | 6 | class SdxlTurbo(BaseHuggingfaceTool): 7 | def __init__(self): 8 | super().__init__() 9 | self.pipe = AutoPipelineForText2Image.from_pretrained("stabilityai/sdxl-turbo", torch_dtype=torch.float16, variant="fp16") 10 | 11 | def run(self, params): 12 | prompt = params["prompt"] 13 | self.pipe.to("cuda") 14 | image = self.pipe(prompt=prompt, num_inference_steps=1, guidance_scale=0.0).images[0] 15 | return image 16 | 17 | def get_tool_call_format(self): 18 | tool_call_format = { 19 | "type": "function", 20 | "function": { 21 | "name": "sdxl_turbo", 22 | "description": "generate images with the given texts", 23 | "parameters": { 24 | "type": "object", 25 | "properties": { 26 | "prompt": { 27 | "type": "string", 28 | "description": "prompt description of the image to be generated" 29 | } 30 | }, 31 | "required": [ 32 | "prompt" 33 | ] 34 | } 35 | } 36 | } 37 | return tool_call_format 38 | -------------------------------------------------------------------------------- /pyopenagi/tools/stability-ai/text_to_image.py: -------------------------------------------------------------------------------- 1 | from ...utils.utils import get_from_env 2 | 3 | from ..base import BaseHuggingfaceTool 4 | 5 | import requests 6 | 7 | class TextToImage(BaseHuggingfaceTool): 8 | def __init__(self): 9 | super().__init__() 10 | 11 | def run(self, params): 12 | 13 | API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0" 14 | headers = {"Authorization": "Bearer " + get_from_env("HF_AUTH_TOKENS")} 15 | 16 | prompt = params["prompt"] 17 | 18 | path = params["path"] 19 | 20 | payload = { 21 | "inputs": prompt 22 | } 23 | response = requests.post(API_URL, headers=headers, json=payload) 24 | 25 | image_bytes = response.content 26 | # You can access the image with PIL.Image for example 27 | import io 28 | from PIL import Image 29 | 30 | image = Image.open(io.BytesIO(image_bytes)) 31 | image.save(path) 32 | 33 | return f"a generated image saved at {path}" 34 | 35 | def get_tool_call_format(self): 36 | tool_call_format = { 37 | "type": "function", 38 | "function": { 39 | "name": "text_to_image", 40 | "description": "generate images with the given texts", 41 | "parameters": { 42 | "type": "object", 43 | "properties": { 44 | "prompt": { 45 | "type": "string", 46 | "description": "prompt description of the image to be generated" 47 | }, 48 | "path": { 49 | "type": "string", 50 | "description": "path to save the generated image" 51 | } 52 | }, 53 | "required": [ 54 | "prompt", 55 | "path" 56 | ] 57 | } 58 | } 59 | } 60 | return tool_call_format 61 | -------------------------------------------------------------------------------- /pyopenagi/tools/suno/text_to_speech.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from ...utils.utils import get_from_env 4 | 5 | from ..base import BaseHuggingfaceTool 6 | 7 | class TextToSpeech(BaseHuggingfaceTool): 8 | def __init__(self): 9 | super().__init__() 10 | 11 | def run(self, params): 12 | API_URL = "https://api-inference.huggingface.co/models/suno/bark" 13 | headers = {"Authorization": "Bearer " + get_from_env("HF_AUTH_TOKENS")} 14 | 15 | prompt = params["prompt"] 16 | path = params["path"] 17 | 18 | payload = { 19 | "prompt": prompt 20 | } 21 | 22 | response = requests.post(API_URL, headers=headers, json=payload) 23 | 24 | with open(path,"wb") as f: 25 | f.write(response.content) 26 | 27 | return f"a generated audio saved at {path}" 28 | # pass 29 | 30 | def get_tool_call_format(self): 31 | tool_call_format = { 32 | "type": "function", 33 | "function": { 34 | "name": "text_to_speech", 35 | "description": "generate voice based on the text", 36 | "parameters": { 37 | "type": "object", 38 | "properties": { 39 | "prompt": { 40 | "type": "string", 41 | "description": "text description" 42 | }, 43 | "path": { 44 | "type": "string", 45 | "description": "path to save the audio" 46 | } 47 | }, 48 | "required": [ 49 | "prompt", 50 | "path" 51 | ] 52 | } 53 | } 54 | } 55 | return tool_call_format 56 | -------------------------------------------------------------------------------- /pyopenagi/tools/timbrooks/image_to_image.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import torch 3 | from diffusers import StableDiffusionInstructPix2PixPipeline, EulerAncestralDiscreteScheduler 4 | 5 | from ..base import BaseHuggingfaceTool 6 | 7 | class ImageToImage(BaseHuggingfaceTool): 8 | def __init__(self): 9 | super().__init__() 10 | self.model_id = "timbrooks/instruct-pix2pix" 11 | self.pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained(self.model_id, torch_dtype=torch.float16, safety_checker=None) 12 | self.pipe.to("cuda") 13 | self.pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(self.pipe.scheduler.config) 14 | 15 | def run(self, params): 16 | path = params["old_path"] 17 | image = Image.open(path) 18 | prompt = params["prompt"] 19 | new_image = self.pipe(prompt, image=image, num_inference_steps=10, image_guidance_scale=1).images 20 | new_path = params["new_path"] 21 | new_image.save(path) 22 | return f"a new image saved at {new_path}" 23 | 24 | def get_tool_call_format(self): 25 | tool_call_format = { 26 | "type": "function", 27 | "function": { 28 | "name": "text_to_image", 29 | "description": "generate images with the given texts", 30 | "parameters": { 31 | "type": "object", 32 | "properties": { 33 | "prompt": { 34 | "type": "string", 35 | "description": "prompt description of the image to be generated" 36 | }, 37 | "old_path": { 38 | "type": "string", 39 | "description": "path to load the old image" 40 | }, 41 | "new_path": { 42 | "type": "string", 43 | "description": "path to save the new generated image" 44 | } 45 | }, 46 | "required": [ 47 | "prompt", 48 | "old_path", 49 | "new_path" 50 | ] 51 | } 52 | } 53 | } 54 | return tool_call_format 55 | -------------------------------------------------------------------------------- /pyopenagi/tools/transcriber/transcriber.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseTool 2 | from time import sleep 3 | 4 | class Transcriber(BaseTool): 5 | def __init__(self): 6 | """ big library, not everyone needs it installed """ 7 | try: 8 | from RealtimeSTT import AudioToTextRecorder 9 | except ImportError: 10 | raise ImportError( 11 | "Please install RealtimeSTT: `pip install RealtimeSTT`" 12 | ) 13 | 14 | # this is hardcoded for now 15 | self.recorder = AudioToTextRecorder( 16 | model="tiny.en", 17 | ) 18 | 19 | def run(self, params: dict): 20 | duration = 5 21 | try: 22 | duration = int(params["duration"]) 23 | except ValueError: 24 | raise KeyError( 25 | "The keys in params do not match the excepted key in params for transcriber api. " 26 | "Please make sure it contain the key 'duration'" 27 | ) 28 | 29 | self.record.start() 30 | sleep(duration) 31 | self.recorder.stop() 32 | return self.recorder.text() 33 | 34 | def get_tool_call_format(self): 35 | tool_call_format = { 36 | "type": "function", 37 | "function": { 38 | "name": "transcriber", 39 | "description": "Transcribes audiio into text", 40 | "parameters": { 41 | "type": "object", 42 | "properties": { 43 | "duration": { 44 | "type": "string", 45 | "description": "How long to record audio for in seconds", 46 | }, 47 | }, 48 | "required": [] 49 | } 50 | } 51 | } 52 | return tool_call_format 53 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/accommodations.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import os 3 | from pandas import DataFrame 4 | 5 | from ..base import BaseTool 6 | 7 | 8 | class Accommodations(BaseTool): 9 | def __init__(self, path="../../environments/travelPlanner/accommodations/clean_accommodations_2022.csv"): 10 | current_dir = os.path.dirname(os.path.abspath(__file__)) 11 | self.path = os.path.join(current_dir, path) 12 | self.data = pd.read_csv(self.path).dropna()[ 13 | ['NAME', 'price', 'room type', 'house_rules', 'minimum nights', 'maximum occupancy', 'review rate number', 14 | 'city']] 15 | print("Accommodations loaded.") 16 | 17 | def load_db(self): 18 | self.data = pd.read_csv(self.path).dropna() 19 | 20 | def run(self, 21 | city: str, 22 | ) -> DataFrame: 23 | """Search for accommodations by city.""" 24 | results = self.data[self.data["city"] == city] 25 | if len(results) == 0: 26 | return "There is no attraction in this city." 27 | 28 | return results 29 | 30 | def get_tool_call_format(self): 31 | tool_call_format = { 32 | "type": "function", 33 | "function": { 34 | "name": "Accommodations", 35 | "description": "Search for an Accommodations by query", 36 | } 37 | } 38 | return tool_call_format 39 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/attractions.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import os 3 | from pandas import DataFrame 4 | 5 | from ..base import BaseTool 6 | 7 | 8 | class Attractions(BaseTool): 9 | def __init__(self, path="../../environments/travelPlanner/attractions/attractions.csv"): 10 | current_dir = os.path.dirname(os.path.abspath(__file__)) 11 | self.path = os.path.join(current_dir, path) 12 | self.data = pd.read_csv(self.path).dropna()[ 13 | ['Name', 'Latitude', 'Longitude', 'Address', 'Phone', 'Website', "City"]] 14 | print("Attractions loaded.") 15 | 16 | def load_db(self): 17 | self.data = pd.read_csv(self.path) 18 | 19 | def run(self, 20 | city: str, 21 | ) -> DataFrame: 22 | """Search for Accommodations by city and date.""" 23 | results = self.data[self.data["City"] == city] 24 | # the results should show the index 25 | results = results.reset_index(drop=True) 26 | if len(results) == 0: 27 | return "There is no attraction in this city." 28 | return results 29 | 30 | def get_tool_call_format(self): 31 | tool_call_format = { 32 | "type": "function", 33 | "function": { 34 | "name": "Attractions", 35 | "description": "Search for Attractions by query", 36 | } 37 | } 38 | return tool_call_format 39 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/cities.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from ..base import BaseTool 4 | 5 | 6 | class Cities(BaseTool): 7 | def __init__(self, path="../../environments/travelPlanner/background/citySet_with_states.txt") -> None: 8 | current_dir = os.path.dirname(os.path.abspath(__file__)) 9 | self.path = os.path.join(current_dir, path) 10 | self.load_data() 11 | print("Cities loaded.") 12 | 13 | def load_data(self): 14 | cityStateMapping = open(self.path, "r").read().strip().split("\n") 15 | self.data = {} 16 | for unit in cityStateMapping: 17 | city, state = unit.split("\t") 18 | if state not in self.data: 19 | self.data[state] = [city] 20 | else: 21 | self.data[state].append(city) 22 | 23 | def run(self, state) -> dict: 24 | if state not in self.data: 25 | return ValueError("Invalid State") 26 | else: 27 | return self.data[state] 28 | 29 | def get_tool_call_format(self): 30 | tool_call_format = { 31 | "type": "function", 32 | "function": { 33 | "name": "Cities", 34 | "description": "Search for Cities by query", 35 | } 36 | } 37 | return tool_call_format 38 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/flights.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import os 3 | from pandas import DataFrame 4 | 5 | from ..base import BaseTool 6 | 7 | 8 | class Flights(BaseTool): 9 | 10 | def __init__(self, path="../../environments/travelPlanner/flights/clean_Flights_2022.csv"): 11 | current_dir = os.path.dirname(os.path.abspath(__file__)) 12 | self.path = os.path.join(current_dir, path) 13 | self.data = pd.read_csv(self.path).dropna()[ 14 | ['Flight Number', 'Price', 'DepTime', 'ArrTime', 'ActualElapsedTime', 'FlightDate', 'OriginCityName', 15 | 'DestCityName', 'Distance']] 16 | print("Flights loaded.") 17 | 18 | def load_db(self): 19 | self.data = pd.read_csv(self.path).dropna().rename(columns={'Unnamed: 0': 'Flight Number'}) 20 | 21 | def run(self, 22 | origin: str, 23 | destination: str, 24 | departure_date: str, 25 | ) -> DataFrame: 26 | """Search for flights by origin, destination, and departure date.""" 27 | results = self.data[self.data["OriginCityName"] == origin] 28 | results = results[results["DestCityName"] == destination] 29 | results = results[results["FlightDate"] == departure_date] 30 | 31 | if len(results) == 0: 32 | return "There is no flight from {} to {} on {}.".format(origin, destination, departure_date) 33 | return results 34 | 35 | def get_tool_call_format(self): 36 | tool_call_format = { 37 | "type": "function", 38 | "function": { 39 | "name": "Flights", 40 | "description": "Search for Flights by query", 41 | } 42 | } 43 | return tool_call_format 44 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/google_distance_matrix.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import pandas as pd 4 | import numpy as np 5 | 6 | from ..base import BaseTool 7 | 8 | 9 | 10 | # This tool refers to the "DistanceMatrix" in the paper. Considering this data obtained from Google API, 11 | # we consistently use this name in the code. Please be assured that this will not influence the experiment results 12 | # shown in the paper. 13 | 14 | class GoogleDistanceMatrix(BaseTool): 15 | def __init__(self, path="../../environments/travelPlanner/googleDistanceMatrix/distance.csv", 16 | subscription_key: str = "") -> None: 17 | self.gplaces_api_key: str = subscription_key 18 | current_dir = os.path.dirname(os.path.abspath(__file__)) 19 | self.path = os.path.join(current_dir, path) 20 | self.data = pd.read_csv(self.path) 21 | print("GoogleDistanceMatrix loaded.") 22 | 23 | def run(self, origin, destination, mode='driving'): 24 | origin = extract_before_parenthesis(origin) 25 | destination = extract_before_parenthesis(destination) 26 | info = {"origin": origin, "destination": destination, "cost": None, "duration": None, "distance": None} 27 | response = self.data[(self.data['origin'] == origin) & (self.data['destination'] == destination)] 28 | if len(response) > 0: 29 | if response['duration'].values[0] is None or response['distance'].values[0] is None or \ 30 | response['duration'].values[0] is np.nan or response['distance'].values[0] is np.nan: 31 | return "No valid information." 32 | info["duration"] = response['duration'].values[0] 33 | info["distance"] = response['distance'].values[0] 34 | if 'driving' in mode: 35 | info["cost"] = int(eval(info["distance"].replace("km", "").replace(",", "")) * 0.05) 36 | elif mode == "taxi": 37 | info["cost"] = int(eval(info["distance"].replace("km", "").replace(",", ""))) 38 | if 'day' in info["duration"]: 39 | return "No valid information." 40 | return f"{mode}, from {origin} to {destination}, duration: {info['duration']}, distance: {info['distance']}, cost: {info['cost']}" 41 | 42 | return f"{mode}, from {origin} to {destination}, no valid information." 43 | 44 | def get_tool_call_format(self): 45 | tool_call_format = { 46 | "type": "function", 47 | "function": { 48 | "name": "GoogleDistanceMatrix", 49 | "description": "Distance information", 50 | } 51 | } 52 | return tool_call_format 53 | 54 | 55 | def extract_before_parenthesis(s): 56 | match = re.search(r'^(.*?)\([^)]*\)', s) 57 | return match.group(1) if match else s 58 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/notebook.py: -------------------------------------------------------------------------------- 1 | from pandas import DataFrame 2 | 3 | from ..base import BaseTool 4 | 5 | 6 | 7 | class Notebook(BaseTool): 8 | def __init__(self) -> None: 9 | self.data = [] 10 | 11 | def run(self, input_data: DataFrame, short_description: str): 12 | self.data.append({"Short Description": short_description, "Content": input_data}) 13 | return f"The information has been recorded in Notebook, and its index is {len(self.data) - 1}." 14 | 15 | def update(self, input_data: DataFrame, index: int, short_decription: str): 16 | self.data[index]["Content"] = input_data 17 | self.data[index]["Short Description"] = short_decription 18 | 19 | return "The information has been updated in Notebook." 20 | 21 | def list(self): 22 | results = [] 23 | for idx, unit in enumerate(self.data): 24 | results.append({"index": idx, "Short Description": unit['Short Description']}) 25 | 26 | return results 27 | 28 | def list_all(self): 29 | results = [] 30 | for idx, unit in enumerate(self.data): 31 | if type(unit['Content']) is DataFrame: 32 | results.append({"index": idx, "Short Description": unit['Short Description'], 33 | "Content": unit['Content'].to_string(index=False)}) 34 | else: 35 | results.append( 36 | {"index": idx, "Short Description": unit['Short Description'], "Content": unit['Content']}) 37 | 38 | return results 39 | 40 | def read(self, index): 41 | return self.data[index] 42 | 43 | def reset(self): 44 | self.data = [] 45 | 46 | def get_tool_call_format(self): 47 | tool_call_format = { 48 | "type": "function", 49 | "function": { 50 | "name": "Notebook", 51 | "description": "Note information", 52 | } 53 | } 54 | return tool_call_format 55 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/planner.py: -------------------------------------------------------------------------------- 1 | PLANNER_INSTRUCTION = """You are a proficient planner. Based on the provided information and query, please give me a detailed plan, including specifics such as flight numbers (e.g., F0123456), restaurant names, and accommodation names. Note that all the information in your plan should be derived from the provided data. You must adhere to the format given in the example. Additionally, all details should align with commonsense. The symbol '-' indicates that information is unnecessary. For example, in the provided sample, you do not need to plan after returning to the departure city. When you travel to two cities in one day, you should note it in the 'Current City' section as in the example (i.e., from A to B). 2 | 3 | ***** Example ***** 4 | Query: Could you create a travel plan for 7 people from Ithaca to Charlotte spanning 3 days, from March 8th to March 14th, 2022, with a budget of $30,200? 5 | Travel Plan: 6 | Day 1: 7 | Current City: from Ithaca to Charlotte 8 | Transportation: Flight Number: F3633413, from Ithaca to Charlotte, Departure Time: 05:38, Arrival Time: 07:46 9 | Breakfast: Nagaland's Kitchen, Charlotte 10 | Attraction: The Charlotte Museum of History, Charlotte 11 | Lunch: Cafe Maple Street, Charlotte 12 | Dinner: Bombay Vada Pav, Charlotte 13 | Accommodation: Affordable Spacious Refurbished Room in Bushwick!, Charlotte 14 | 15 | Day 2: 16 | Current City: Charlotte 17 | Transportation: - 18 | Breakfast: Olive Tree Cafe, Charlotte 19 | Attraction: The Mint Museum, Charlotte;Romare Bearden Park, Charlotte. 20 | Lunch: Birbal Ji Dhaba, Charlotte 21 | Dinner: Pind Balluchi, Charlotte 22 | Accommodation: Affordable Spacious Refurbished Room in Bushwick!, Charlotte 23 | 24 | Day 3: 25 | Current City: from Charlotte to Ithaca 26 | Transportation: Flight Number: F3786167, from Charlotte to Ithaca, Departure Time: 21:42, Arrival Time: 23:26 27 | Breakfast: Subway, Charlotte 28 | Attraction: Books Monument, Charlotte. 29 | Lunch: Olive Tree Cafe, Charlotte 30 | Dinner: Kylin Skybar, Charlotte 31 | Accommodation: - 32 | 33 | ***** Example Ends ***** 34 | 35 | Given information: {text} 36 | Query: {query} 37 | Travel Plan:""" 38 | -------------------------------------------------------------------------------- /pyopenagi/tools/travel_planner/restaurants.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import os 3 | from pandas import DataFrame 4 | 5 | from ..base import BaseTool 6 | 7 | 8 | class Restaurants(BaseTool): 9 | def __init__(self, path="../../environments/travelPlanner/restaurants/clean_restaurant_2022.csv"): 10 | super().__init__() 11 | current_dir = os.path.dirname(os.path.abspath(__file__)) 12 | self.path = os.path.join(current_dir, path) 13 | self.data = pd.read_csv(self.path).dropna()[['Name', 'Average Cost', 'Cuisines', 'Aggregate Rating', 'City']] 14 | print("Restaurants loaded.") 15 | 16 | def load_db(self): 17 | self.data = pd.read_csv(self.path).dropna() 18 | 19 | def run(self, 20 | city: str, 21 | ) -> DataFrame: 22 | """Search for restaurant .""" 23 | results = self.data[self.data["City"] == city] 24 | 25 | if len(results) == 0: 26 | return "There is no restaurant in this city." 27 | return results 28 | 29 | def get_tool_call_format(self): 30 | tool_call_format = { 31 | "type": "function", 32 | "function": { 33 | "name": "Restaurants", 34 | "description": "Search for Restaurants by query", 35 | } 36 | } 37 | return tool_call_format 38 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/airport_search.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | import json 8 | 9 | class AirportSearch(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/flights/searchAirport" 13 | self.host_name = "tripadvisor16.p.rapidapi.com" 14 | self.api_key = get_from_env("RAPID_API_KEY") 15 | 16 | def run(self, params: dict): 17 | headers = { 18 | "X-RapidAPI-Key": self.api_key, 19 | "X-RapidAPI-Host": self.host_name 20 | } 21 | try: 22 | self.query_string = { 23 | "query": params["query"], 24 | } 25 | except ValueError: 26 | raise KeyError( 27 | "The keys in params do not match the excepted keys in params for tripadvisor search airport api. " 28 | "Please make sure it contains the key: 'query'" 29 | ) 30 | 31 | # print(self.query_string) 32 | 33 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 34 | return self.parse_result(response) 35 | 36 | def parse_result(self, response) -> str: 37 | limited_results = response['data'][:2] 38 | 39 | simplified_results = [] 40 | for result in limited_results: 41 | simplified_result = { 42 | 'name': result['name'], 43 | 'airportCode': result['airportCode'], 44 | 'coords': result['coords'] 45 | } 46 | simplified_results.append(simplified_result) 47 | 48 | return json.dumps(simplified_results) 49 | 50 | def get_tool_call_format(self): 51 | tool_call_format = { 52 | "type": "function", 53 | "function": { 54 | "name": "airport_search", 55 | "description": "Search for an airport by query", 56 | "parameters": { 57 | "type": "object", 58 | "properties": { 59 | "query": { 60 | "type": "string", 61 | "description": "Search query for airport" 62 | } 63 | }, 64 | "required": [ 65 | "query" 66 | ] 67 | } 68 | } 69 | } 70 | return tool_call_format 71 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/get_hotel_details.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | import json 8 | 9 | class GetHotelDetails(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/hotels/getHotelDetails" 13 | self.host_name = "tripadvisor16.p.rapidapi.com" 14 | self.api_key = get_from_env("RAPID_API_KEY") 15 | 16 | def run(self, params: dict): 17 | headers = { 18 | "X-RapidAPI-Key": self.api_key, 19 | "X-RapidAPI-Host": self.host_name 20 | } 21 | 22 | try: 23 | self.query_string = { 24 | "id": params["id"], 25 | "checkIn": params["checkIn"], 26 | "checkOut": params["checkOut"], 27 | } 28 | except ValueError: 29 | raise KeyError( 30 | "The keys in params do not match the excepted keys in params for tripadvisor get hotel details api. " 31 | "Please make sure it contains following required keys: " 32 | "id", 33 | "checkIn", 34 | "checkOut", 35 | ) 36 | 37 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 38 | return self.parse_result(response) 39 | 40 | def parse_result(self, response) -> str: 41 | if 'data' in response: 42 | hotel_data = response['data'] 43 | relevant_info = { 44 | 'name': hotel_data.get('title', ''), 45 | 'rating': hotel_data.get('rating', ''), 46 | 'address': hotel_data.get('location', {}).get('address', ''), 47 | 'amenities': [amenity['title'] for amenity in hotel_data.get('about', {}).get('content', []) if amenity['title'] == 'Amenities'], 48 | 'description': hotel_data.get('about', {}).get('content', [{}])[0].get('content', ''), 49 | 'restaurantsNearby': [{ 50 | 'title': hotel_data.get('restaurantsNearby', {}).get('content', [{}])[0].get('title', ''), 51 | 'rating': hotel_data.get('restaurantsNearby', {}).get('content', [{}])[0].get('bubbleRating', {}).get('rating', ''), 52 | 'primaryInfo': hotel_data.get('restaurantsNearby', {}).get('content', [{}])[0].get('primaryInfo', ''), 53 | 'distance': hotel_data.get('restaurantsNearby', {}).get('content', [{}])[0].get('distance', ''), 54 | }], 55 | 'attractionsNearby': [{ 56 | 'title': hotel_data.get('attractionsNearby', {}).get('content', [{}])[0].get('title', ''), 57 | 'rating': hotel_data.get('attractionsNearby', {}).get('content', [{}])[0].get('bubbleRating', {}).get('rating', ''), 58 | 'primaryInfo': hotel_data.get('attractionsNearby', {}).get('content', [{}])[0].get('primaryInfo', ''), 59 | 'distance': hotel_data.get('attractionsNearby', {}).get('content', [{}])[0].get('distance', ''), 60 | }] 61 | } 62 | return json.dumps(relevant_info) 63 | else: 64 | return json.dumps({}) 65 | 66 | def get_tool_call_format(self): 67 | tool_call_format = { 68 | "type": "function", 69 | "function": { 70 | "name": "get_hotel_details", 71 | "description": "Provides details about a hotel", 72 | "parameters": { 73 | "type": "object", 74 | "properties": { 75 | "id": { 76 | "type": "string", 77 | "description": "The ID of the hotel to get details for" 78 | }, 79 | "checkIn": { 80 | "type": "string", 81 | "format": "date", 82 | "description": "The check in date" 83 | }, 84 | "checkOut": { 85 | "type": "string", 86 | "format": "date", 87 | "description": "The check out date" 88 | } 89 | }, 90 | "required": [ 91 | "id", 92 | "checkIn", 93 | "checkOut" 94 | ] 95 | } 96 | } 97 | } 98 | return tool_call_format 99 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/get_restaurant_details.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | import json 8 | 9 | class GetRestaurantDetails(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/restaurant/getRestaurantDetails" 13 | self.host_name = "tripadvisor16.p.rapidapi.com" 14 | self.api_key = get_from_env("RAPID_API_KEY") 15 | 16 | def run(self, params: dict): 17 | headers = { 18 | "X-RapidAPI-Key": self.api_key, 19 | "X-RapidAPI-Host": self.host_name 20 | } 21 | 22 | try: 23 | self.query_string = { 24 | "restaurantsId": params["restaurantsId"], 25 | } 26 | except ValueError: 27 | raise KeyError( 28 | "The keys in params do not match the excepted keys in params for tripadvisor get restaurant details api. " 29 | "Please make sure it contains the key: 'restaurantsID'" 30 | ) 31 | 32 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 33 | return self.parse_result(response) 34 | 35 | 36 | def parse_result(self, response) -> str: 37 | location = response["data"]["location"] 38 | 39 | useful_info = { 40 | "name": location.get("name"), 41 | "latitude": location.get("latitude"), 42 | "longitude": location.get("longitude"), 43 | "num_reviews": location.get("num_reviews"), 44 | "rating": location.get("rating"), 45 | "price_level": location.get("price_level"), 46 | "address": location.get("address"), 47 | "phone": location.get("phone"), 48 | "website": location.get("website"), 49 | "cuisine": [cuisine["name"] for cuisine in location.get("cuisine", [])], 50 | "hours": location.get("hours", {}).get("week_ranges", []) 51 | } 52 | return json.dumps(useful_info) 53 | 54 | def get_tool_call_format(self): 55 | tool_call_format = { 56 | "type": "function", 57 | "function": { 58 | "name": "get_restaurant_details", 59 | "description": "Provides details about a restaurant", 60 | "parameters": { 61 | "type": "object", 62 | "properties": { 63 | "restaurantsId": { 64 | "type": "string", 65 | "description": "The ID of the restaurant to get details for" 66 | } 67 | }, 68 | "required": [ 69 | "restaurantsId" 70 | ] 71 | } 72 | } 73 | } 74 | return tool_call_format 75 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/hotel_location_search.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | import json 8 | 9 | class HotelLocationSearch(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/hotels/searchLocation" 13 | self.host_name = "tripadvisor16.p.rapidapi.com" 14 | self.api_key = get_from_env("RAPID_API_KEY") 15 | 16 | def run(self, params: dict): 17 | headers = { 18 | "X-RapidAPI-Key": self.api_key, 19 | "X-RapidAPI-Host": self.host_name 20 | } 21 | try: 22 | self.query_string = { 23 | "query": params["query"], 24 | } 25 | except ValueError: 26 | raise KeyError( 27 | "The keys in params do not match the excepted keys in params for tripadvisor search hotel location api. " 28 | "Please make sure it contains the key: 'query'" 29 | ) 30 | 31 | # print(self.query_string) 32 | 33 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 34 | return json.dumps(response) 35 | 36 | def parse_result(self, response) -> str: 37 | raise NotImplementedError 38 | 39 | def get_tool_call_format(self): 40 | tool_call_format = { 41 | "type": "function", 42 | "function": { 43 | "name": "hotel_location_search", 44 | "description": "Search for a hotel location by query", 45 | "parameters": { 46 | "type": "object", 47 | "properties": { 48 | "query": { 49 | "type": "string", 50 | "description": "Search query for hotel location" 51 | } 52 | }, 53 | "required": [ 54 | "query" 55 | ] 56 | } 57 | } 58 | } 59 | return tool_call_format 60 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/hotel_search.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | import json 8 | 9 | class HotelSearch(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/hotels/searchHotels" 13 | self.host_name = "tripadvisor16.p.rapidapi.com" 14 | self.api_key = get_from_env("RAPID_API_KEY") 15 | 16 | def run(self, params: dict): 17 | headers = { 18 | "X-RapidAPI-Key": self.api_key, 19 | "X-RapidAPI-Host": self.host_name 20 | } 21 | 22 | try: 23 | self.query_string = { 24 | "geoId": params["geoId"], 25 | "checkIn": params["checkIn"], 26 | "checkOut": params["checkOut"], 27 | } 28 | except ValueError: 29 | raise KeyError( 30 | "The keys in params do not match the excepted keys in params for tripadvisor search hotel api. " 31 | "Please make sure it contains following required keys: " 32 | "geoId", 33 | "checkIn", 34 | "checkOut", 35 | ) 36 | 37 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 38 | return self.parse_result(response) 39 | 40 | 41 | def parse_result(self, response) -> str: 42 | if 'data' in response and 'data' in response['data']: 43 | hotels_data = response['data']['data'][:2] 44 | relevant_info = [] 45 | for hotel in hotels_data: 46 | relevant_info.append({ 47 | 'id': hotel['id'], 48 | 'title': hotel['title'], 49 | 'secondaryInfo': hotel['secondaryInfo'], 50 | 'bubbleRating': hotel['bubbleRating'], 51 | 'priceForDisplay': hotel['priceForDisplay'], 52 | 'priceDetails': hotel['priceDetails'], 53 | 'priceSummary': hotel['priceSummary'] 54 | }) 55 | return json.dumps(relevant_info) 56 | else: 57 | return json.dumps([]) 58 | 59 | def get_tool_call_format(self): 60 | tool_call_format = { 61 | "type": "function", 62 | "function": { 63 | "name": "hotel_search", 64 | "description": "Provides details about a hotel", 65 | "parameters": { 66 | "type": "object", 67 | "properties": { 68 | "geoId": { 69 | "type": "string", 70 | "description": "The geoId of the hotel to search for" 71 | }, 72 | "checkIn": { 73 | "type": "string", 74 | "format": "date", 75 | "description": "The check in date" 76 | }, 77 | "checkOut": { 78 | "type": "string", 79 | "format": "date", 80 | "description": "The check out date" 81 | } 82 | }, 83 | "required": [ 84 | "geoId", 85 | "checkIn", 86 | "checkOut" 87 | ] 88 | } 89 | } 90 | } 91 | return tool_call_format 92 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/restaurant_location_search.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | 5 | import requests 6 | 7 | import json 8 | 9 | class RestaurantLocationSearch(BaseRapidAPITool): 10 | def __init__(self): 11 | super().__init__() 12 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/restaurant/searchLocation" 13 | self.host_name = "tripadvisor16.p.rapidapi.com" 14 | self.api_key = get_from_env("RAPID_API_KEY") 15 | 16 | def run(self, params: dict): 17 | headers = { 18 | "X-RapidAPI-Key": self.api_key, 19 | "X-RapidAPI-Host": self.host_name 20 | } 21 | try: 22 | self.query_string = { 23 | "query": params["query"], 24 | } 25 | except ValueError: 26 | raise KeyError( 27 | "The keys in params do not match the excepted keys in params for tripadvisor search restaurant location api. " 28 | "Please make sure it contains the key: 'query'" 29 | ) 30 | 31 | # print(self.query_string) 32 | 33 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 34 | return self.parse_result(response) 35 | 36 | def parse_result(self, response) -> str: 37 | limited_results = response['data'][:2] 38 | 39 | simplified_results = [] 40 | for result in limited_results: 41 | simplified_result = { 42 | 'locationId': result['locationId'], 43 | 'localizedName': result['localizedName'], 44 | 'latitude': result['latitude'], 45 | 'longitude': result['longitude'] 46 | } 47 | simplified_results.append(simplified_result) 48 | 49 | return json.dumps(simplified_results) 50 | 51 | def get_tool_call_format(self): 52 | tool_call_format = { 53 | "type": "function", 54 | "function": { 55 | "name": "restaurant_location_search", 56 | "description": "Search for a restaurant location by query", 57 | "parameters": { 58 | "type": "object", 59 | "properties": { 60 | "query": { 61 | "type": "string", 62 | "description": "Search query for restaurant location" 63 | } 64 | }, 65 | "required": [ 66 | "query" 67 | ] 68 | } 69 | } 70 | } 71 | return tool_call_format 72 | -------------------------------------------------------------------------------- /pyopenagi/tools/trip_advisor/restaurant_search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from ..base import BaseRapidAPITool 4 | 5 | from pyopenagi.utils.utils import get_from_env 6 | 7 | import requests 8 | 9 | import json 10 | 11 | class RestaurantSearch(BaseRapidAPITool): 12 | def __init__(self): 13 | super().__init__() 14 | self.url = "https://tripadvisor16.p.rapidapi.com/api/v1/restaurant/searchRestaurants" 15 | self.host_name = "tripadvisor16.p.rapidapi.com" 16 | self.api_key = get_from_env("RAPID_API_KEY") 17 | 18 | def run(self, params: dict): 19 | headers = { 20 | "X-RapidAPI-Key": self.api_key, 21 | "X-RapidAPI-Host": self.host_name 22 | } 23 | 24 | try: 25 | self.query_string = { 26 | "locationId": params["locationId"] 27 | } 28 | except ValueError: 29 | raise KeyError( 30 | "The keys in params do not match the excepted keys in params for tripadvisor search restaurant api. " 31 | "Please make sure it contains following required keys: " 32 | "locationID", 33 | ) 34 | 35 | response = requests.get(self.url, headers=headers, params=self.query_string).json() 36 | return self.parse_result(response) 37 | 38 | def parse_result(self, response) -> str: 39 | limited_results = response['data']['data'][:2] 40 | 41 | simplified_results = [] 42 | for result in limited_results: 43 | simplified_result = { 44 | 'restaurantsId': result['restaurantsId'], 45 | 'name': result['name'], 46 | 'averageRating': result['averageRating'], 47 | 'userReviewCount': result['userReviewCount'], 48 | 'priceTag': result['priceTag'], 49 | 'establishmentTypeAndCuisineTags': result['establishmentTypeAndCuisineTags'] 50 | } 51 | simplified_results.append(simplified_result) 52 | 53 | return json.dumps(simplified_results) 54 | 55 | def get_tool_call_format(self): 56 | tool_call_format = { 57 | "type": "function", 58 | "function": { 59 | "name": "restaurant_search", 60 | "description": "Search for a restaurant by locationID", 61 | "parameters": { 62 | "type": "object", 63 | "properties": { 64 | "locationId ": { 65 | "type": "string", 66 | "description": "The locationID of the restaurant to search for" 67 | } 68 | }, 69 | "required": [ 70 | "locationId" 71 | ] 72 | } 73 | } 74 | } 75 | return tool_call_format 76 | -------------------------------------------------------------------------------- /pyopenagi/tools/wikipedia/wikipedia.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseTool 2 | # from langchain_core.documents import Document 3 | from typing import Optional, Any 4 | class Wikipedia(BaseTool): 5 | """Wikipedia tool, refactored from langchain. 6 | 7 | To use, you should have the ``wikipedia`` python package installed. 8 | This wrapper will use the Wikipedia API to conduct searches and 9 | fetch page summaries. By default, it will return the page summaries 10 | of the top-k results. 11 | It limits the Document content by doc_content_chars_max. 12 | """ 13 | def __init__(self): 14 | super().__init__() 15 | self.WIKIPEDIA_MAX_QUERY_LENGTH = 300 16 | self.top_k_results = 3 17 | self.lang = "en" 18 | self.load_all_available_meta: bool = False 19 | self.doc_content_chars_max: int = 4000 20 | self.wiki_client = self.build_client() 21 | 22 | def build_client(self): 23 | try: 24 | import wikipedia 25 | 26 | wikipedia.set_lang(self.lang) 27 | 28 | except ImportError: 29 | raise ImportError( 30 | "Could not import wikipedia python package. " 31 | "Please install it with `pip install wikipedia`." 32 | ) 33 | return wikipedia 34 | 35 | def run(self, params) -> str: 36 | """Run Wikipedia search and get page summaries.""" 37 | query = params["query"] 38 | # if not isinstance(query, dict) or 'query' not in query: 39 | # raise TypeError("Query must be a dictionary with a 'query' key") 40 | # query_str = query['query'][:self.WIKIPEDIA_MAX_QUERY_LENGTH] # Extract and slice the query string 41 | page_titles = self.wiki_client.search(query, results=self.top_k_results) 42 | summaries = [] 43 | for page_title in page_titles[: self.top_k_results]: 44 | if wiki_page := self._fetch_page(page_title): 45 | if summary := self._formatted_page_summary(page_title, wiki_page): 46 | summaries.append(summary) 47 | if not summaries: 48 | return "No good Wikipedia Search Result was found" 49 | return "\n\n".join(summaries)[: self.doc_content_chars_max] 50 | 51 | @staticmethod 52 | def _formatted_page_summary(page_title: str, wiki_page: Any) -> Optional[str]: 53 | return f"Page: {page_title}\nSummary: {wiki_page.summary}" 54 | 55 | def get_tool_call_format(self): 56 | tool_call_format = { 57 | "type": "function", 58 | "function": { 59 | "name": "wikipedia", 60 | "description": "Provides relevant information about the destination", 61 | "parameters": { 62 | "type": "object", 63 | "properties": { 64 | "query": { 65 | "type": "string", 66 | "description": "Search query for Wikipedia" 67 | } 68 | }, 69 | "required": [ 70 | "query" 71 | ] 72 | } 73 | } 74 | } 75 | return tool_call_format 76 | -------------------------------------------------------------------------------- /pyopenagi/tools/wolfram/wolfram_alpha.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseTool 2 | 3 | from pyopenagi.utils.utils import get_from_env 4 | class WolframAlpha(BaseTool): 5 | """Wolfram Alpha Tool, refactored from langchain. 6 | 7 | Docs for using: 8 | 9 | 1. Go to wolfram alpha and sign up for a developer account 10 | 2. Create an app and get your APP ID 11 | 3. Save your APP ID into WOLFRAM_ALPHA_APPID env variable 12 | 4. pip install wolframalpha 13 | 14 | """ 15 | def __init__(self): 16 | super().__init__() 17 | self.wolfram_alpha_appid = get_from_env("WOLFRAM_ALPHA_APPID") 18 | self.wolfram_client = self.build_client() 19 | 20 | def build_client(self): 21 | try: 22 | import wolframalpha 23 | 24 | except ImportError: 25 | raise ImportError( 26 | "wolframalpha is not installed. " 27 | "Please install it with `pip install wolframalpha`" 28 | ) 29 | client = wolframalpha.Client(self.wolfram_alpha_appid) 30 | return client 31 | 32 | def run(self, query: str) -> str: 33 | """Run query through WolframAlpha and parse result.""" 34 | res = self.wolfram_client.query(query) 35 | 36 | try: 37 | assumption = next(res.pods).text 38 | answer = next(res.results).text 39 | except StopIteration: 40 | return "Wolfram Alpha wasn't able to answer it" 41 | 42 | if answer is None or answer == "": 43 | # We don't want to return the assumption alone if answer is empty 44 | return "No good Wolfram Alpha Result was found" 45 | else: 46 | return f"Assumption: {assumption} \nAnswer: {answer}" 47 | 48 | 49 | def get_tool_call_format(self): 50 | tool_call_format = { 51 | "type": "function", 52 | "function": { 53 | "name": "wolfram_alpha", 54 | "description": "Use specific mathematical knowledge (algebra, calculus, geometry, etc) to answer the given query", 55 | "parameters": { 56 | "type": "object", 57 | "properties": { 58 | "query": { 59 | "type": "string", 60 | "description": "the abstracted mathematical query that needs to be answered" 61 | } 62 | }, 63 | "required": [ 64 | "query" 65 | ] 66 | } 67 | } 68 | } 69 | return tool_call_format 70 | -------------------------------------------------------------------------------- /pyopenagi/tools/words_api/words_api.py: -------------------------------------------------------------------------------- 1 | from ..base import BaseRapidAPITool 2 | 3 | # from pydantic import root_validator 4 | 5 | from pyopenagi.utils.utils import get_from_env 6 | 7 | import requests 8 | 9 | SUPPORTED_APIS = [ 10 | "typeOf", "hasTypes", "partOf", "hasParts", 11 | "instanceOf", "hasInstances", "similarTo", 12 | "also", "entails", "memberOf", "hasMembers", 13 | "substanceOf", "hasSubstances", "inCategory", 14 | "hasCategories", "usageOf", "hasUsages", 15 | "inRegion", "regionOf", "pertainsTo", "synonyms", 16 | "examples", "antonyms", "pronunciation", 17 | ] 18 | 19 | class WordsAPI(BaseRapidAPITool): 20 | def __init__(self): 21 | super().__init__() 22 | self.base_url = "https://wordsapiv1.p.rapidapi.com/words/" 23 | self.url = self.base_url 24 | self.host_name = "wordsapiv1.p.rapidapi.com" 25 | self.api_key = get_from_env("RAPID_API_KEY") 26 | 27 | def run(self, params): 28 | headers = { 29 | "X-RapidAPI-Key": self.api_key, 30 | "X-RapidAPI-Host": self.host_name 31 | } 32 | try: 33 | self.word = params["word"] 34 | self.api_name = params["api_name"] 35 | except KeyError: 36 | raise KeyError( 37 | "The keys in params do not match the excepted keys in params for words api. " 38 | "Please make sure it contains two keys: 'words' and 'api_name'" 39 | ) 40 | 41 | if not self.is_supported(self.api_name): 42 | raise ValueError( 43 | f"{self.api_name} is currently not supported!" 44 | ) 45 | 46 | self.url = f"{self.base_url}{self.word}/{self.api_name}" 47 | response = requests.get(self.url, headers=headers).json() 48 | result = self.parse_result(response) 49 | return result 50 | 51 | def parse_result(self, response) -> str: 52 | # fail response: {'success': False, 'message': 'word not found'} 53 | if "success" in response and not response["success"]: 54 | return response["message"] 55 | 56 | return response["word"] + " " + self.api_name + " [" + ",".join(response[self.api_name]) + "]" 57 | 58 | def is_supported(self, api_name): 59 | return api_name in SUPPORTED_APIS 60 | -------------------------------------------------------------------------------- /pyopenagi/utils/README.md: -------------------------------------------------------------------------------- 1 | # pyopenagi/utils 2 | 3 | Helper utils that are re-used in AIOS. 4 | 5 | These are various tools that we use in our internal implementations. 6 | 7 | In the future they shouldn't be copy pasted to AIOS. 8 | -------------------------------------------------------------------------------- /pyopenagi/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/utils/__init__.py -------------------------------------------------------------------------------- /pyopenagi/utils/chat_template.py: -------------------------------------------------------------------------------- 1 | class Query: 2 | def __init__(self, 3 | messages, 4 | tools = None, 5 | message_return_type = "text" 6 | ) -> None: 7 | """Query format 8 | 9 | Args: 10 | messages (list): 11 | [ 12 | {"role": "xxx", content_key: content_value} 13 | ] 14 | tools (optional): tools that are used for function calling. Defaults to None. 15 | """ 16 | self.messages = messages 17 | self.tools = tools 18 | self.message_return_type = message_return_type 19 | 20 | class Response: 21 | def __init__( 22 | self, 23 | response_message, 24 | tool_calls: list = None 25 | ) -> None: 26 | """Response format 27 | 28 | Args: 29 | response_message (str): "generated_text" 30 | tool_calls (list, optional): 31 | [ 32 | {"name": "xxx", "parameters": {}} 33 | ]. 34 | Default to None. 35 | """ 36 | self.response_message = response_message 37 | self.tool_calls = tool_calls 38 | -------------------------------------------------------------------------------- /pyopenagi/utils/commands/top.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/pyopenagi/utils/commands/top.py -------------------------------------------------------------------------------- /pyopenagi/utils/compressor.py: -------------------------------------------------------------------------------- 1 | import zlib 2 | 3 | class Compressor: 4 | def __init__(self) -> None: 5 | pass 6 | 7 | def compress(self, data): 8 | pass 9 | 10 | def decompress(self, compressed_data): 11 | pass 12 | 13 | class ZLIBCompressor(Compressor): 14 | def __init__(self) -> None: 15 | pass 16 | 17 | def compress(self, data): 18 | compressed_data = zlib.compress(data.encode('utf-8')) 19 | return compressed_data 20 | 21 | def decompress(self, compressed_data): 22 | decompressed_data = zlib.decompress(compressed_data) 23 | return decompressed_data.decode('utf-8') 24 | -------------------------------------------------------------------------------- /pyopenagi/utils/logger.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | import os 4 | 5 | from datetime import datetime 6 | 7 | class BaseLogger: 8 | def __init__(self, 9 | logger_name, 10 | log_mode = "console", 11 | ) -> None: 12 | self.logger_name = logger_name 13 | self.log_mode = log_mode 14 | self.log_file = self.load_log_file() if log_mode == "file" else None 15 | 16 | self.level_color = dict() 17 | 18 | def log(self, content, level): 19 | if self.log_mode == "console": 20 | self.log_to_console(content, level) 21 | else: 22 | assert self.log_mode == "file" and self.log_file is not None 23 | self.log_to_file(content, self.log_file) 24 | 25 | def load_log_file(self): 26 | pass 27 | 28 | def log_to_console(self, content, level): 29 | # print(content) 30 | click.secho(f"[{self.logger_name}] " + content, fg=self.level_color[level]) 31 | 32 | def log_to_file(self, content, log_file): 33 | with open(log_file, "a") as w: 34 | w.writelines(content) 35 | 36 | class SchedulerLogger(BaseLogger): 37 | def __init__(self, logger_name, log_mode="console") -> None: 38 | super().__init__(logger_name, log_mode) 39 | self.level_color = { 40 | "execute": "green", 41 | "suspend": "yellow", 42 | "info": "white" 43 | } 44 | 45 | def load_log_file(self): 46 | date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 47 | log_dir = os.path.join(os.getcwd(), "logs", "scheduler") 48 | if not os.path.exists(log_dir): 49 | os.makedirs(log_dir) 50 | log_file = os.path.join(log_dir, f"{date_time}.txt") 51 | return log_file 52 | 53 | 54 | class AgentLogger(BaseLogger): 55 | def __init__(self, logger_name, log_mode="console") -> None: 56 | super().__init__(logger_name, log_mode) 57 | self.level_color = { 58 | "info": (248, 246, 227), # white 59 | "executing": (217, 237, 191), # green 60 | "suspending": (255, 235, 178), # yellow 61 | "done": (122, 162, 227) # blue 62 | } 63 | 64 | def load_log_file(self): 65 | date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 66 | log_dir = os.path.join(os.getcwd(), "logs", "agents", self.logger_name) 67 | if not os.path.exists(log_dir): 68 | os.makedirs(log_dir) 69 | log_file = os.path.join(log_dir, f"{date_time}.txt") 70 | return log_file 71 | 72 | 73 | class LLMKernelLogger(BaseLogger): 74 | def __init__(self, logger_name, log_mode="console") -> None: 75 | super().__init__(logger_name, log_mode) 76 | self.level_color = { 77 | "info": (246, 245, 242), 78 | "executing": (65, 176, 110), # green 79 | "suspending": (255, 201, 74), # yellow 80 | "done": (122, 162, 227) # blue 81 | } 82 | 83 | def log_to_console(self, content, level): 84 | # print(content) 85 | click.secho(f"[\U0001F916{self.logger_name}] " + content, fg=self.level_color[level], bold=True) 86 | 87 | def load_log_file(self): 88 | date_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 89 | log_dir = os.path.join(os.getcwd(), "logs", "llm_kernel", self.logger_name) 90 | if not os.path.exists(log_dir): 91 | os.makedirs(log_dir) 92 | log_file = os.path.join(log_dir, f"{date_time}.txt") 93 | return log_file 94 | -------------------------------------------------------------------------------- /pyopenagi/utils/utils.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import os 4 | import shutil 5 | 6 | import json 7 | 8 | from typing import Dict, Any, Optional 9 | 10 | import re 11 | 12 | # logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') 13 | # logger = logging.getLogger(__name__) 14 | 15 | def parse_global_args(): 16 | parser = argparse.ArgumentParser(description="Parse global parameters") 17 | parser.add_argument('--llm_name', type=str, default="gpt-4o-mini", help="Specify the LLM name of AIOS") 18 | parser.add_argument('--max_gpu_memory', type=json.loads, help="Max gpu memory allocated for the LLM") 19 | parser.add_argument('--eval_device', type=str, help="Evaluation device") 20 | parser.add_argument('--max_new_tokens', type=int, default=256, help="The maximum number of new tokens for generation") 21 | parser.add_argument("--llm_kernel_log_mode", type=str, default="console", choices=["console", "file"]) 22 | parser.add_argument("--use_backend", type=str, default="ollama", choices=["ollama", "vllm"]) 23 | 24 | return parser 25 | 26 | def extract_before_parenthesis(s: str) -> str: 27 | match = re.search(r'^(.*?)\([^)]*\)', s) 28 | return match.group(1) if match else s 29 | 30 | def get_from_dict_or_env( 31 | data: Dict[str, Any], key: str, env_key: str, default: Optional[str] = None 32 | ) -> str: 33 | """Get a value from a dictionary or an environment variable.""" 34 | if key in data and data[key]: 35 | return data[key] 36 | else: 37 | return get_from_env(key, env_key, default=default) 38 | 39 | 40 | def get_from_env(env_key: str, default: Optional[str] = None) -> str: 41 | """Get a value from an environment variable.""" 42 | if env_key in os.environ and os.environ[env_key]: 43 | return os.environ[env_key] 44 | elif default is not None: 45 | return default 46 | else: 47 | raise ValueError( 48 | f"Did not find {env_key}, please add an environment variable" 49 | f" `{env_key}` which contains it. " 50 | ) 51 | 52 | class Logger: 53 | def __init__(self, log_mode) -> None: 54 | self.log_mode = log_mode 55 | 56 | def log(self, info, path=None): 57 | if self.log_mode == "console": 58 | print(info) 59 | else: 60 | assert self.log_mode == "file" 61 | with open(path, "w") as w: 62 | w.write(info + "\n") 63 | 64 | def delete_directories(root_dir, target_dirs): 65 | """ 66 | Recursively deletes directories with names in target_dirs starting from root_dir. 67 | """ 68 | for dirpath, dirnames, filenames in os.walk(root_dir, topdown=False): 69 | for dirname in dirnames: 70 | if dirname in target_dirs: 71 | full_path = os.path.join(dirpath, dirname) 72 | # print(f"Deleting {full_path}...") 73 | shutil.rmtree(full_path, ignore_errors=True) 74 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "hatchling.build" 3 | requires = ["hatchling", "hatch-requirements-txt"] 4 | 5 | [project] 6 | 7 | dynamic = ["dependencies"] 8 | 9 | description = "OpenAGI: Package for AI Agent Creation" 10 | keywords = ["llm", "agi"] 11 | license = {file = "LICENSE"} 12 | 13 | name = "pyopenagi" 14 | readme = "README.md" 15 | requires-python = ">=3.9" 16 | version = "0.0.11" 17 | 18 | classifiers = [ 19 | "Development Status :: 3 - Alpha", 20 | "Intended Audience :: Developers", 21 | "Topic :: Software Development :: Libraries :: Python Modules", 22 | "License :: OSI Approved :: MIT License", 23 | "Programming Language :: Python :: 3", 24 | "Programming Language :: Python :: 3.9", 25 | "Programming Language :: Python :: 3.10", 26 | "Programming Language :: Python :: 3.11", 27 | ] 28 | 29 | [project.urls] 30 | Homepage = "https://github.com/agiresearch/OpenAGI" 31 | Repository = "https://github.com/agiresearch/OpenAGI.git" 32 | "Tools Docs" = "https://github.com/agiresearch/OpenAGI/tools.md" 33 | 34 | 35 | [tool.hatch.build] 36 | exclude = ["research/", "pyopenagi.egg-info/", "dist", "__pycache__/", ".pytest_cache/"] 37 | 38 | [tool.hatch.metadata.hooks.requirements_txt] 39 | files = ["requirements.txt"] 40 | 41 | [tool.hatch.build.targets.wheel] 42 | packages = ["pyopenagi"] 43 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | pre-commit 3 | pytest 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-dotenv 2 | Requests 3 | Pympler==1.0.1 4 | click==8.1.7 5 | python-dotenv==1.0.0 6 | beautifulsoup4 7 | playwright 8 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # tests 2 | 3 | This directory contains tests you can use to test specific features of the project so you can figure out which specific parts work easier. 4 | 5 | For example, test_agent_creation.py simply tests the AgentProcess ability to hold agents. 6 | 7 | We want to use error code to differentiate between successful and failed tests in the future. 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agiresearch/OpenAGI/26c119e55b8d6ad4bae96cd4e5901c1567a82aaa/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_agent_creation.py: -------------------------------------------------------------------------------- 1 | # make sure we can create agents 2 | 3 | from pyopenagi.agents.agent_process import AgentProcess 4 | from pyopenagi.utils.chat_template import Query 5 | 6 | def test_agent_creation(): 7 | agent_process = AgentProcess( 8 | agent_name="example/academic_agent", 9 | query=Query( 10 | messages = [ 11 | {"role": "user", "content": "Summarize researches of quantum computing in recent five years."} 12 | ] 13 | ) 14 | ) 15 | # Use plain assert statements for testing conditions 16 | assert agent_process.agent_name == "example/academic_agent", "Agent name does not match" 17 | # Add more assertions here as necessary to validate the properties of the agent_process object 18 | -------------------------------------------------------------------------------- /tests/test_tools/README.md: -------------------------------------------------------------------------------- 1 | # test/test_tools 2 | 3 | This tests specific tools the agents can use and makes sure they are working. Each tool has its own test. 4 | -------------------------------------------------------------------------------- /tests/test_tools/test_currency_converter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | from pyopenagi.tools.currency_converter.currency_converter import CurrencyConverter 5 | from dotenv import load_dotenv, find_dotenv 6 | 7 | @pytest.fixture(scope="module") 8 | def test_rapid_api_key(): 9 | load_dotenv(find_dotenv()) 10 | if "RAPID_API_KEY" not in os.environ or not os.environ["RAPID_API_KEY"]: 11 | with pytest.raises(ValueError): 12 | CurrencyConverter() 13 | pytest.skip("Rapid api key is not set.") 14 | else: 15 | return True 16 | 17 | @pytest.mark.usefixtures("test_rapid_api_key") 18 | def test_currency_converter_api(): 19 | load_dotenv(find_dotenv()) 20 | currency_converter_api = CurrencyConverter() 21 | params = { 22 | "from": "USD", 23 | "to": "EUR", 24 | "amount": 2 25 | } 26 | result = currency_converter_api.run(params=params) 27 | print(result) 28 | assert isinstance(result, str) 29 | -------------------------------------------------------------------------------- /tests/test_tools/test_top_series.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import requests 4 | from requests.models import Response 5 | import json 6 | 7 | from pyopenagi.tools.imdb.top_series import TopSeries 8 | from dotenv import load_dotenv, find_dotenv 9 | 10 | @pytest.fixture(scope="module") 11 | def test_rapid_api_key(): 12 | load_dotenv(find_dotenv()) 13 | if "RAPID_API_KEY" not in os.environ or not os.environ["RAPID_API_KEY"]: 14 | with pytest.raises(ValueError): 15 | TopSeries() 16 | pytest.skip("RAPID api key is not set.") 17 | else: 18 | return True 19 | 20 | class ImdbTopSeriesMock: 21 | @staticmethod 22 | def json(): 23 | mock_items = [] 24 | mock_title = "Mock Title" 25 | mock_description = "Mock Description." 26 | mock_image = "https://m.media-amazon.com/images/Mock/Standard.Image.jpg" 27 | mock_big_image = "https://m.media-amazon.com/images/Mock/Big.Image.jpg" 28 | mock_genre = ["Drama", "Fantasy"] 29 | mock_thumbnail = "https://m.media-amazon.com/images/Mock/Thumb.Image.jpg" 30 | mock_rating = 9.2 31 | mock_year = "2011-2019" 32 | mock_imdbid = "tt0000000" 33 | mock_imdb_link = "https://www.imdb.com/title/tt0000000" 34 | 35 | for i in range(100): 36 | mock_items.append( 37 | { 38 | "rank": i + 1, 39 | "title": mock_title, 40 | "description": mock_description, 41 | "image": mock_image, 42 | "big_image": mock_big_image, 43 | "genre": mock_genre, 44 | "thumbnail": mock_thumbnail, 45 | "rating": mock_rating, 46 | "id": f"top{i+1}", 47 | "year": mock_year, 48 | "imdbid": mock_imdbid, 49 | "mock_imdb_link": mock_imdb_link, 50 | } 51 | ) 52 | mock_response = Response() 53 | mock_response.status_code = 200 54 | mock_response._content = str.encode(json.dumps(mock_items)) 55 | return mock_response.json() 56 | 57 | 58 | @pytest.fixture(autouse=True) 59 | def mock_response(monkeypatch): 60 | def mock_get(*args, **kwargs): 61 | return ImdbTopSeriesMock() 62 | 63 | monkeypatch.setattr(requests, "get", mock_get) 64 | 65 | 66 | @pytest.mark.usefixtures("test_rapid_api_key") 67 | @pytest.mark.parametrize( 68 | "valid_start, valid_end", 69 | [ 70 | [1, 100], 71 | [60, 61], 72 | [60, 62], 73 | ], 74 | ) 75 | def test_top_series_api_valid_input_outputs_valid_delimiter_count( 76 | valid_start, valid_end 77 | ): 78 | load_dotenv(find_dotenv()) 79 | top_series_api = TopSeries() 80 | params = {"start": valid_start, "end": valid_end} 81 | result = top_series_api.run(params=params) 82 | assert isinstance(result, str) 83 | assert result.count(";") == max(0, int(valid_end) - int(valid_start)) 84 | 85 | @pytest.mark.usefixtures("test_rapid_api_key") 86 | def test_top_series_api_reverse_range_returns_blank(): 87 | load_dotenv(find_dotenv()) 88 | top_series_api = TopSeries() 89 | params = {"start": 100, "end": 0} 90 | result = top_series_api.run(params=params) 91 | assert result == "Top 100-0 series ranked by IMDB are: " 92 | 93 | 94 | @pytest.mark.parametrize( 95 | "invalid_start, valid_end", 96 | [ 97 | ["0", 100], 98 | [0.5, 100], 99 | [[], 100], 100 | [{}, 100] 101 | ] 102 | ) 103 | @pytest.mark.usefixtures("test_rapid_api_key") 104 | def test_top_series_api_invalid_start_type_raises_typeerror(invalid_start, valid_end): 105 | load_dotenv(find_dotenv()) 106 | top_series_api = TopSeries() 107 | params = {"start": invalid_start, "end": valid_end} 108 | with pytest.raises(TypeError): 109 | top_series_api.run(params=params) 110 | 111 | 112 | @pytest.mark.parametrize( 113 | "invalid_start, valid_end", 114 | [ 115 | [1, "0"], 116 | [1, 0.5], 117 | [1, []], 118 | [1, {}] 119 | ] 120 | ) 121 | @pytest.mark.usefixtures("test_rapid_api_key") 122 | def test_top_series_api_invalid_end_type_raises_typeerror(invalid_start, valid_end): 123 | load_dotenv(find_dotenv()) 124 | top_series_api = TopSeries() 125 | params = {"start": invalid_start, "end": valid_end} 126 | with pytest.raises(TypeError): 127 | top_series_api.run(params=params) 128 | 129 | @pytest.mark.usefixtures("test_rapid_api_key") 130 | def test_top_series_api_invalid_start_count_raises_indexerror(): 131 | load_dotenv(find_dotenv()) 132 | top_series_api = TopSeries() 133 | invalid_start = {"start": 101, "end": 102} 134 | with pytest.raises(IndexError): 135 | top_series_api.run(params=invalid_start) 136 | 137 | @pytest.mark.usefixtures("test_rapid_api_key") 138 | def test_top_series_api_invalid_end_count_raises_indexerror(): 139 | load_dotenv(find_dotenv()) 140 | top_series_api = TopSeries() 141 | invalid_end = {"start": 1, "end": 101} 142 | with pytest.raises(IndexError): 143 | top_series_api.run(params=invalid_end) 144 | -------------------------------------------------------------------------------- /tests/test_tools/test_wolfram_alpha.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | from pyopenagi.tools.wolfram.wolfram_alpha import WolframAlpha 5 | from dotenv import load_dotenv, find_dotenv 6 | 7 | @pytest.fixture(scope="module") 8 | def test_wolfram_alpha_id(): 9 | load_dotenv(find_dotenv()) 10 | if "WOLFRAM_ALPHA_APPID" not in os.environ or not os.environ["WOLFRAM_ALPHA_APPID"]: 11 | with pytest.raises(ValueError): 12 | WolframAlpha() 13 | pytest.skip("WolframAlpha app id is not set.") 14 | else: 15 | return True 16 | 17 | @pytest.mark.usefixtures("test_wolfram_alpha_id") 18 | def test_wolfram_alpha(): 19 | wolfram_alpha = WolframAlpha() 20 | query = "What is the square root of 144?" 21 | result = wolfram_alpha.run(query) 22 | assert "12" in result 23 | -------------------------------------------------------------------------------- /tests/test_tools/test_words_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | from pyopenagi.tools.words_api.words_api import WordsAPI 5 | from dotenv import load_dotenv, find_dotenv 6 | 7 | @pytest.fixture(scope="module") 8 | def test_rapid_api_key(): 9 | load_dotenv(find_dotenv()) 10 | if "RAPID_API_KEY" not in os.environ or not os.environ["RAPID_API_KEY"]: 11 | with pytest.raises(ValueError): 12 | WordsAPI() 13 | pytest.skip("Rapid api key is not set.") 14 | 15 | @pytest.mark.usefixtures("test_rapid_api_key") 16 | def test_words_api(): 17 | words_api = WordsAPI() 18 | params = { 19 | "word": "look", 20 | "api_name": "typeOf", 21 | } 22 | result = words_api.run(params=params) 23 | print(result) 24 | assert isinstance(result, str) 25 | -------------------------------------------------------------------------------- /tools.md: -------------------------------------------------------------------------------- 1 | # Tool Configuration 2 | ## Available tools 3 | 4 | ### Wolfram Alpha 5 | 1. Register wolfram alpha app account and activate [APP ID](https://developer.wolframalpha.com/access) 6 | 2. Setup Wolfram Alpha APP_ID 7 | ```bash 8 | export WOLFRAM_ALPHA_APPID= 9 | ``` 10 | 11 | ### Rapid API Tool 12 | 1. Register for [Rapid API](https://rapidapi.com/hub) 13 | 2. Click into the tool page that you want to call and click the (`Subscribe to Test`) button to activate tools. 14 | 3. Setup up Rapid API Key 15 | ```bash 16 | export RAPID_API_KEY= 17 | ``` 18 | #### Current supported Rapid API tools 19 | - [Words API](https://rapidapi.com/dpventures/api/wordsapi/) 20 | - [Moon Phase](https://rapidapi.com/MoonAPIcom/api/moon-phase/) 21 | - [Trip Advisor](https://rapidapi.com/DataCrawler/api/tripadvisor16) 22 | - [Shazam](https://rapidapi.com/apidojo/api/shazam/) 23 | - [IMDB](https://rapidapi.com/rapihub-rapihub-default/api/imdb-top-100-movies/) 24 | 25 | 26 | --------------------------------------------------------------------------------