oscopilot==v0.1.0

This commit is contained in:
hccngu
2024-03-29 16:25:26 +08:00
parent 0707d1ab46
commit 2065278a1f
219 changed files with 4036 additions and 5260 deletions

9
.env
View File

@@ -1,3 +1,8 @@
MODEL_NAME="gpt-4-0125-preview"
OPENAI_API_KEY= ""
OPENAI_ORGANIZATION= ""
OPENAI_API_KEY=""
OPENAI_ORGANIZATION=""
API_BASE_URL="http://127.0.0.1:8079"
OPENAI_BASE_URL=""
BING_SUBSCRIPTION_KEY=""
BING_SEARCH_URL="https://api.bing.microsoft.com/v7.0/search"
WOLFRAMALPHA_APP_ID=""

229
README.md
View File

@@ -5,9 +5,10 @@
[[Website]](https://os-copilot.github.io/)
[[Arxiv]](https://arxiv.org/abs/2402.07456)
[[PDF]](https://arxiv.org/pdf/2402.07456.pdf)
[[Documentation]]()
<!-- [[Tweet]](https://twitter.com/DrJimFan/status/1662115266933972993?s=20) -->
[![Static Badge](https://img.shields.io/badge/MIT-License-green)](https://github.com/OS-Copilot/FRIDAY/blob/main/LICENSE)
[![Static Badge](https://img.shields.io/badge/MIT-License-green)](https://github.com/OS-Copilot/OS-Copilot/blob/main/LICENSE)
![Static Badge](https://img.shields.io/badge/python-3.10-blue)
[![Static Badge](https://img.shields.io/badge/FRIDAY-Frontend-yellow)](https://github.com/OS-Copilot/FRIDAY-front)
@@ -38,30 +39,30 @@
1. **Clone the GitHub Repository:**
```
git clone https://github.com/OS-Copilot/FRIDAY.git
git clone https://github.com/OS-Copilot/OS-Copilot.git
```
2. **Set Up Python Environment:** Ensure you have a version 3.10 or higher Python environment. You can create and
activate this environment using the following commands, replacing `FRIDAY_env` with your preferred environment
activate this environment using the following commands, replacing `oscopilot_env` with your preferred environment
name:
```
conda create -n FRIDAY_env python=3.10 -y
conda activate FRIDAY_env
conda create -n oscopilot_env python=3.10 -y
conda activate oscopilot_env
```
3. **Install Dependencies:** Move into the `FRIDAY` directory and install the necessary dependencies by running:
3. **Install Dependencies:** Move into the `OS-Copilot` directory and install the necessary dependencies by running:
```
cd FRIDAY
pip install -r requirements.txt
cd OS-Copilot
pip install -e .
```
4. **Set OpenAI API Key:** Configure your OpenAI API key in [.env](.env) and select the model you wish to use.
5. **Execute Your Task:** Run the following command to start FRIDAY. Replace `[query]` with your task as needed. By default, the task is *"Move the text files containing the word 'agent' from the folder named 'document' to the path 'working_dir/agent'"*. If the task requires using related files, you can use `--query_file_path [file_path]`.
5. **Running the Script:** Run the quick_start.py script, simply execute the following command in your terminal:
```
python run.py --query [query]
python quick_start.py
```
\* FRIDAY currently only supports single-round conversation.
@@ -73,219 +74,43 @@ For a detailed list of tools, please see [FRIDAY-Gizmos](https://github.com/OS-C
1. Find the tool you want to use in [FRIDAY-Gizmos](https://github.com/OS-Copilot/FRIDAY-Gizmos) and download its tool code.
2. Add the tool to FRIDAY's toolkit:
```shell
python friday/core/action_manager.py --add --tool_name [tool_name] --tool_path [tool_path]
python friday/tool_repository/manager/tool_manager.py --add --tool_name [tool_name] --tool_path [tool_path]
```
3. If you wish to remove a tool, you can run:
```shell
python friday/core/action_manager.py --delete --tool_name [tool_name]
python friday/tool_repository/manager/tool_manager.py --delete --tool_name [tool_name]
```
## 💻 User Interface (UI)
**Enhance Your Experience with Our Intuitive Frontend!** This interface is crafted for effortless control of your agents. For more details, visit [FRIDAY Frontend](https://github.com/OS-Copilot/FRIDAY-front).
## ✨ Deploy your own API tools with FastAPI
All FastAPIs are under [friday/api](friday/api)
1. **Prepare your FastAPI file:** Create a new api folder under [friday/api](friday/api) and put your FastAPi python files under that folder.
2. **Import your FastAPI in API server:** Import your apis in [friday/core/api_server.py](friday/core/api_server.py)
```python
import os
## ✨ Deploy API Services
from fastapi import FastAPI
from friday.core.server_config import ConfigManager
app = FastAPI()
For comprehensive guidelines on deploying API services, please refer to the [OS-Copilot documentation]().
from friday.api.bing.bing_service import router as bing_router
#[TODO] Import your own api here
## 👨‍💻‍ Contributors
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
print(f"Incoming request: {request.method} {request.url}")
try:
response = await call_next(request)
except Exception as e:
print(f"Request error: {str(e)}")
raise e from None
else:
print(f"Outgoing response: {response.status_code}")
return response
app.add_middleware(LoggingMiddleware)
# Create a dictionary that maps service names to their routers
services = {
"bing": bing_router,
# [TODO] Add your api router here
}
server_list = [
"bing",
# [TODO] Add your api's service name here.
]
# Include only the routers for the services listed in server_list
for service in server_list:
if service in services:
app.include_router(services[service])
# proxy_manager = ConfigManager()
# proxy_manager.apply_proxies()
if __name__ == "__main__":
import uvicorn
# you can change your port anyway
uvicorn.run(app, host="0.0.0.0", port=8079)
```
3. **Run API server:**
Run the server in localhost,or deploy it on your web server:
```
python api_server.py
```
4. **Update API documentation:**
Update the API documentation located in [friday/core/openapi.json](friday/core/openapi.json). After launching the API server, you can access the current OpenAPI documentation at `http://localhost:8079/openapi.json`.
Ensure to thoroughly update each API's summary in the documentation to clearly explain its functionality and usage. This is crucial as FRIDAY relies on these descriptions to understand the purpose of each API.
For example:
```json
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/tools/audio2text": {
"post": {
// [TODO] change the summary to describe the usage of your api.
"summary": "A tool that converts audio to natural language text",
"operationId": "audio2text_tools_audio2text_post",
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/Body_audio2text_tools_audio2text_post"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
},
"components": {
"schemas": {
"Body_audio2text_tools_audio2text_post": {
"properties": {
"file": {
"type": "string",
"format": "binary",
"title": "File"
}
},
"type": "object",
"required": [
"file"
],
"title": "Body_audio2text_tools_audio2text_post"
},
}
}
}
```
5. **Change the base url of tool_request_util.py:** FRIDAY utilizes the script located at [friday/core/tool_request_util.py](friday/core/tool_request_util.py) to interface with your API tools. After deploying your APIs, make sure to update the base URL in this file to match your API server's URL.
```python
import requests
class ToolRequestUtil:
def __init__(self):
self.session = requests.session()
self.headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML like Gecko) Chrome/52.0.2743.116 Safari/537.36'}
# [TODO] Change the base url
self.base_url = "http://localhost:8079"
def request(self, api_path, method, params=None, files=None, content_type="application/json"):
"""
:param api_path: the path of the api
:param method: get/post
:param params: the params of the api, can be None
:param files: files to be uploaded, can be None
:param content_type: the content_type of api, e.g., application/json, multipart/form-data, can be None
:return: the return of the api
"""
url = self.base_url + api_path
try:
if method.lower() == "get":
if content_type == "application/json":
result = self.session.get(url=url, json=params, headers=self.headers, timeout=60).json()
else:
result = self.session.get(url=url, params=params, headers=self.headers, timeout=60).json()
elif method.lower() == "post":
if content_type == "multipart/form-data":
result = self.session.post(url=url, files=files, data=params, headers=self.headers).json()
elif content_type == "application/json":
result = self.session.post(url=url, json=params, headers=self.headers).json()
else:
result = self.session.post(url=url, data=params, headers=self.headers).json()
else:
print("request method error!")
return None
return result
except Exception as e:
print("http request error: %s" % e)
return None
```
<!-- ## 👨‍💻‍ Contributors
<a href="">
<img src="" />
<a href="https://github.com/OS-Copilot/OS-Copilot/graphs/contributors">
<img src="https://contrib.rocks/image?repo=OS-Copilot/OS-Copilot" />
</a>
Made with [contrib.rocks](https://contrib.rocks). -->
<!-- Made with [contrib.rocks](https://contrib.rocks). -->
## 🏫 Community
Join our community to connect with other enthusiasts, share your tools and demos, and collaborate on innovative projects. Stay engaged and get the latest updates by following us:
- **Discord**: Join our Discord server for real-time discussions, support, and to share your work with the community. Click here to join: [Discord Server](https://discord.gg/PDsRrEV27b).
- **Twitter**: Follow us on Twitter [@oscopilot](https://twitter.com/oscopilot) for the latest news, updates, and highlights from our community.
## 🛡 Disclaimer
OS-Copilot is provided "as is" without warranty of any kind. Users assume full responsibility for any risks associated with its use, including **potential data loss** or **changes to system settings**. The developers of OS-Copilot are not liable for any damages or losses resulting from its use. Users must ensure their actions comply with applicable laws and regulations.
## 🏫 Community
Join our community to connect with other agent enthusiasts, share your tools and demos, and collaborate on exciting initiatives. You can find us on [Slack](https://join.slack.com/t/os-copilot/shared_invite/zt-2cqebow90-soac9UFKGZ2RcUy8PqjZrA).
## 🔎 Citation
```

View File

@@ -1,5 +0,0 @@
{
"model_name": "gpt-4-1106-preview",
"OPENAI_API_KEY": "",
"OPENAI_ORGANIZATION": ""
}

12
course_learning.py Normal file
View File

@@ -0,0 +1,12 @@
from oscopilot import FridayAgent, FridayExecutor, FridayPlanner, FridayRetriever, SelfLearner, SelfLearning, ToolManager, TextExtractor
from oscopilot.utils import setup_config
args = setup_config()
software_name = args.software_name
package_name = args.package_name
demo_file_path = args.demo_file_path
friday_agent = FridayAgent(FridayPlanner, FridayRetriever, FridayExecutor, ToolManager, config=args)
self_learning = SelfLearning(friday_agent, SelfLearner, ToolManager, args, TextExtractor)
self_learning.self_learning(software_name, package_name, demo_file_path)

20
docs/Makefile Normal file
View File

@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

35
docs/make.bat Normal file
View File

@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View File

@@ -0,0 +1,38 @@
Introduction
==================================
<p align="center">
<img src="_static/demo.png" width="100%" />
</p>
**OS-Copilot** is a pioneering conceptual framework for building generalist computer agents on Linux and MacOS, which provides a unified interface for app interactions in the heterogeneous OS ecosystem.
<p align="center">
<img src="_static/framework.png" width="100%" />
</p>
Leveraging OS-Copilot, we built **FRIDAY**, a self-improving AI assistant capable of solving general computer tasks.
<p align="center">
<img src="_static/FRIDAY.png" width="100%" />
</p>
Project Homepage: [FRIDAY](https://os-copilot.github.io/)
Citation
==================================
For more detailed information about OS-Copilot and FRIDAY, please refer to our latest research paper:
```bibtex
@misc{wu2024oscopilot,
title={OS-Copilot: Towards Generalist Computer Agents with Self-Improvement},
author={Zhiyong Wu and Chengcheng Han and Zichen Ding and Zhenmin Weng and Zhoumianze Liu and Shunyu Yao and Tao Yu and Lingpeng Kong},
year={2024},
eprint={2402.07456},
archivePrefix={arXiv},
primaryClass={cs.AI}
}
> **Note:**
> We are committed to continuously improving **FRIDAY** to solve a wider range of computer tasks. Join our community to advance this exciting project together!
> You can find us on [Slack](https://join.slack.com/t/slack-ped8294/shared_invite/zt-2cqebow90-soac9UFKGZ2RcUy8PqjZrA).

13
docs/source/agent.rst Normal file
View File

@@ -0,0 +1,13 @@
FridayAgent
=============
.. toctree::
:maxdepth: 2
agent_doc/base_agent
agent_doc/friday_agent
agent_doc/base_module
agent_doc/PlanningModule
agent_doc/RetrievalModule
agent_doc/ExecutionModule
agent_doc/prompts

View File

@@ -0,0 +1,7 @@
ExecutionModule
==============================
.. autoclass:: oscopilot.modules.executor.friday_executor.FridayExecutor
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
PlanningModule
==============================
.. autoclass:: oscopilot.modules.planner.friday_planner.FridayPlanner
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
RetrievalModule
==============================
.. autoclass:: oscopilot.modules.retriever.vector_retriever.FridayRetriever
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
Base Agent
==============================
.. automodule:: oscopilot.agents.base_agent
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
Base Module
==============================
.. automodule:: oscopilot.modules.base_module
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
Friday Agent
==============================
.. autoclass:: oscopilot.agents.friday_agent.FridayAgent
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,10 @@
Prompts
==============================
This section of the documentation covers the prompts used by the AI agent across its various components.
The prompts are defined within a Python dictionary and are crucial for guiding the AI's interaction with its environment and users.
.. automodule:: oscopilot.prompts.friday_pt
:members:
:undoc-members:
:show-inheritance:

45
docs/source/conf.py Normal file
View File

@@ -0,0 +1,45 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import sphinx_rtd_theme
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
project = 'OS-Copilot'
copyright = '2024, OS-Copilot'
author = 'OS-Copilot'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.viewcode',
]
templates_path = ['_templates']
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'sphinx_rtd_theme'
html_theme_options = {
'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': True,
'titles_only': False
}
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_static_path = ['_static']

View File

@@ -0,0 +1,8 @@
Environment
=============
.. toctree::
:maxdepth: 2
environment_doc/env
environment_doc/py_env

View File

@@ -0,0 +1,7 @@
Base Environment
==============================
.. automodule:: oscopilot.environments.env
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
Python Environment
==============================
.. automodule:: oscopilot.environments.py_env
:members:
:undoc-members:
:show-inheritance:

136
docs/source/index.rst Normal file
View File

@@ -0,0 +1,136 @@
.. OS-Copilot documentation master file, created by
sphinx-quickstart on Thu Feb 29 15:23:26 2024.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
:github_url: https://github.com/OS-Copilot/OS-Copilot
.. _OS-Copilot: https://os-copilot.github.io/
Introduction
==================================
.. image:: _static/demo.png
:width: 100%
:align: center
**OS-Copilot** is a pioneering conceptual framework for building generalist computer agents on Linux and MacOS, which provides a unified interface for app interactions in the heterogeneous OS ecosystem.
.. image:: _static/framework.png
:width: 100%
:align: center
Leveraging OS-Copilot, we built **FRIDAY**, a self-improving AI assistant capable of solving general computer tasks.
.. image:: _static/FRIDAY.png
:width: 100%
:align: center
**Project Homepage:** `OS-Copilot`_
Tutorials
==================================
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Level | Tutorial | Description |
+==============+=================================================================================================+======================================================================================================================================+
| Beginner | `Installation <installation.html>`_ | Provides three methods to install FRIDAY: cloning from GitHub, development setup via pip install -e ., and direct pip installation. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Beginner | `Getting Started <quick_start.html>`_ | Demonstrates how to use FRIDAY with a quick_start.py script, covering module imports, configuration setup, and task execution. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Intermediate | `Adding Your Tools <tutorials/add_tool.html>`_ | Outlines the process for adding and removing tools to the FRIDAY. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Intermediate | `Deploying API Services <tutorials/deploy_api_service.html>`_ | Explains the deployment of API services for FRIDAY, including environment setup, configuring API tools, and launching the server. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Intermediate | `Example: Automating Excel Tasks <tutorials/example_excel.html>`_ | Demonstrates automating Excel tasks with FRIDAY, including formula application and chart creation within an Excel sheet. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Intermediate | `Enhancing FRIDAY with Self-Learning for Excel Task Automation <tutorials/self_learning.html>`_ | Showcases empowering FRIDAY with self-learning to autonomously learn and execute Excel file manipulations. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Advanced | `Designing New API Tools <tutorials/design_new_api_tool.html>`_ | Guides on designing, integrating, and deploying custom API tools for FRIDAY to extend its functionalities. |
+--------------+-------------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------+
Community
==================================
Join our community to connect with other enthusiasts, share your tools and demos, and collaborate on innovative projects. Stay engaged and get the latest updates by following us:
- **Discord**: Join our Discord server for real-time discussions, support, and to share your work with the community. Click here to join: [Discord Server](https://discord.gg/PDsRrEV27b).
- **Twitter**: Follow us on Twitter [@oscopilot](https://twitter.com/oscopilot) for the latest news, updates, and highlights from our community.
Contributing
==================================
**OS-Copilot** thrives on community contributions, and we welcome involvement in any form. Whether it's adding new tools, fixing bugs, improving documentation, or sharing ideas, every contribution counts. Join our community to advance this exciting project together!
Ways to Contribute
----------------------
- **Code:** Enhance OS-Copilot by adding new features, fixing bugs, or optimizing existing tools.
- **Documentation:** Help make OS-Copilot more accessible by improving or expanding our documentation.
- **Feedback and Ideas:** Share your insights and suggestions to make OS-Copilot even better.
- **Advocacy:** Spread the word about OS-Copilot and help grow our community.
Citation
==================================
For more detailed information about OS-Copilot and FRIDAY, please refer to our latest research paper:
.. code-block:: bibtex
@misc{wu2024oscopilot,
title={OS-Copilot: Towards Generalist Computer Agents with Self-Improvement},
author={Zhiyong Wu and Chengcheng Han and Zichen Ding and Zhenmin Weng and Zhoumianze Liu and Shunyu Yao and Tao Yu and Lingpeng Kong},
year={2024},
eprint={2402.07456},
archivePrefix={arXiv},
primaryClass={cs.AI}
}
.. toctree::
:hidden:
:maxdepth: 2
:caption: Getting Started
installation
quick_start
.. toctree::
:hidden:
:maxdepth: 2
:caption: Tutorials
tutorials/add_tool
tutorials/deploy_api_service
tutorials/design_new_api_tool
tutorials/example_excel
tutorials/self_learning
.. toctree::
:hidden:
:maxdepth: 2
:caption: Modules
agent
tool_repository
environment
utils
.. Indices and tables
.. ==================
.. * :ref:`genindex`
.. * :ref:`modindex`
.. * :ref:`search`

View File

@@ -0,0 +1,51 @@
Installation
========================
There are three ways to install OS-Copilot: using `pip` directly, cloning the GitHub repository, or through the `pip install -e .` method for a development setup. Please follow the instructions below based on your preferred installation method.
1. **Clone the GitHub Repository** (if not already done):
.. code-block:: shell
git clone https://github.com/OS-Copilot/OS-Copilot.git
2. **Navigate to the Repository Directory:**
.. code-block:: shell
cd OS-Copilot
3. **Set Up Python Environment:** Ensure you have a version 3.10 or higher Python environment. You can create and activate this environment using the following commands, replacing OS-Copilot_env with your preferred environment name:
.. code-block:: shell
conda create -n OS-Copilot_env python=3.10 -y
conda activate OS-Copilot_env
3. **Install Dependencies:** Install the necessary dependencies.
- Option 1(Recommended): Use pip to install the project in editable mode.
.. code-block:: shell
pip install -e .
- Option 2: You can install OS-Copilot directly using pip with the following command:
.. code-block:: shell
pip install os-copilot-agent
- Option 3: Install the necessary dependencies:
.. code-block:: shell
cd OS-Copilot
pip install -r requirements.txt
4. **Set OpenAI API Key:** Configure your OpenAI API key in the `.env` file and select the model you wish to use.
.. code-block:: shell
MODEL_NAME=""
OPENAI_API_KEY=""

View File

@@ -0,0 +1,51 @@
Quick Start
============
The `quick_start.py` script is a simple way to start using FRIDAY AGENT. Here's a breakdown of what the script does and how to run it:
1. **Importing Modules:**
The script begins by importing necessary modules from the `oscopilot` package:
.. code-block:: python
from oscopilot import FridayAgent
from oscopilot import ToolManager
from oscopilot import FridayExecutor, FridayPlanner, FridayRetriever
from oscopilot.utils import setup_config, setup_pre_run
2. **Setting Up Configuration:**
Next, the script sets up the configuration for running a task:
.. code-block:: python
args = setup_config()
args.query = "Create a new folder named 'test_friday'"
This sets a query for the FRIDAY AGENT to execute, which in this case is creating a new folder named 'test_friday'.
3. **Preparing and Running the Task:**
After configuring the task, the script prepares it for execution and runs it:
.. code-block:: python
task = setup_pre_run(args)
agent = FridayAgent(FridayPlanner, FridayRetriever, FridayExecutor, ToolManager, config=args)
agent.run(task)
This initializes the FRIDAY AGENT with specified planners, retrievers, and executors, then executes the task.
Running the Script
------------------
To run the `quick_start.py` script, simply execute the following command in your terminal:
.. code-block:: bash
python quick_start.py
Ensure that you are in the same directory as the `quick_start.py` file or provide the full path to the file.
Congratulations! You have now successfully run a task with FRIDAY~

View File

@@ -0,0 +1,9 @@
Tool Repository
==================
.. toctree::
:maxdepth: 2
tool_repository_doc/base_action
tool_repository_doc/tool_manager
tool_repository_doc/api_tools

View File

@@ -0,0 +1,7 @@
API Tools
==============================
.. autoclass:: oscopilot.tool_repository.manager.tool_request_util.ToolRequestUtil
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,7 @@
Base Action
==============================
.. autoclass:: oscopilot.tool_repository.basic_tools.base_action.BaseAction
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,35 @@
Tool Manager
==============================
.. autoclass:: oscopilot.tool_repository.manager.tool_manager.ToolManager
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: oscopilot.tool_repository.manager.action_node.ActionNode
:members:
:undoc-members:
:show-inheritance:
.. autofunction:: oscopilot.tool_repository.manager.tool_manager.print_error_and_exit
.. autofunction:: oscopilot.tool_repository.manager.tool_manager.add_tool
.. autofunction:: oscopilot.tool_repository.manager.tool_manager.delete_tool
.. autofunction:: oscopilot.tool_repository.manager.tool_manager.get_open_api_doc_path
.. autofunction:: oscopilot.tool_repository.manager.tool_manager.get_open_api_description_pair
.. autofunction:: oscopilot.tool_repository.manager.tool_manager.main
.. automodule:: oscopilot.tool_repository.basic_tools.get_os_version.get_os_version
.. automodule:: oscopilot.tool_repository.basic_tools.get_os_version.check_os_version

View File

@@ -0,0 +1,164 @@
Adding Your Tools
=================================================
This tutorial will guide you through the process of adding a new tool to the FRIDAY platform. We will use a simple example tool, `create_folder.py`, to demonstrate the process.
Step 1: Clone the Repository
----------------------------
First, you need to clone the repository containing the tools (referred to as "gizmos" for FRIDAY). Use the following command to clone the repository:
.. code-block:: shell
git clone https://github.com/OS-Copilot/FRIDAY-Gizmos.git
After cloning, navigate into the `FRIDAY-Gizmos` directory:
.. code-block:: shell
cd FRIDAY-Gizmos
Choose any Python file that represents the tool code you wish to add. For this tutorial, we will use `Basic/create_folder.py` as an example.
Step 2: Add the Tool to FRIDAY
------------------------------
To add your chosen tool to FRIDAY's tool repository, run the `action_manager.py` script with the `--add` flag. You will need to provide the tool name and the path to the tool. Replace `[tool_name]` with the name you wish to give your tool and `[tool_path]` with the relative or absolute path to the tool file.
.. code-block:: shell
python oscopilot/tool_repository/manager/tool_manager.py --add --tool_name [tool_name] --tool_path [tool_path]
.. note::
- **[tool_name]:** A unique identifier for your tool within the FRIDAY ecosystem. It is recommended to keep the `tool_name` the same as the class name for consistency.
- **[tool_path]:** The path to the Python file you're adding, relative to the FRIDAY installation directory or an absolute path.
Example: Adding a Tool
-----------------------
If we're adding the `create_folder.py` tool located in the `Basic` directory and we wish to name it `create_folder`, the command would look like this:
.. code-block:: shell
python oscopilot/tool_repository/manager/tool_manager.py --add --tool_name create_folder --tool_path Basic/create_folder.py
Removing a Tool from FRIDAY
-----------------------------
In addition to adding new tools to FRIDAY, you might find yourself in a situation where you need to remove an existing tool from the tool repository. Whether it's for updating purposes or simply because the tool is no longer needed, removing a tool is straightforward.
To remove a tool from FRIDAY's tool repository, you can use the `action_manager.py` script with the `--delete` flag. You will need to specify the name of the tool you wish to remove using the `--tool_name` option. Replace `[tool_name]` with the unique identifier for your tool within the FRIDAY ecosystem.
.. code-block:: shell
python oscopilot/tool_repository/manager/tool_manager.py --delete --tool_name [tool_name]
.. note::
- **[tool_name]:** The unique identifier of the tool you want to remove from FRIDAY. Ensure that you provide the exact name as registered in FRIDAY's tool repository to avoid any errors.
Example: Removing a Tool
--------------------------
If you wish to remove a tool named `create_folder`, the command would look like this:
.. code-block:: shell
python oscopilot/tool_repository/manager/tool_manager.py --delete --tool_name create_folder
This command will remove the `create_folder` tool from FRIDAY's repository, effectively making it unavailable for future use within the ecosystem. It's important to note that removing a tool is a permanent action, so make sure you've backed up any necessary code or information related to the tool before proceeding with the deletion.
Tool Code Example
------------------
To add a tool to FRIDAY, the tool code must follow a specific structure. Below is an example of a tool code that creates a folder either in a specified working directory or in the default working directory. This example adheres to the required structure for FRIDAY tools:
.. code-block:: python
from oscopilot.tool_repository.basic_tools.base_action import BaseAction
import os
class create_folder(BaseAction):
def __init__(self):
self._description = "Create a folder under the default working directory."
def __call__(self, working_directory=None, folder_name='myfold', *args, **kwargs):
"""
Create a folder under the specified working directory or the default working directory.
Args:
working_directory (str): The path of the working directory. If not provided, the default working directory will be used.
folder_name (str): The name of the folder to be created. Default is 'myfold'.
Returns:
None
"""
# Check if the working_directory is provided, if not, use the default working directory
if working_directory:
os.chdir(working_directory)
# Create the folder
os.makedirs(folder_name)
# Example of how to use the class
# create_folder_action = create_folder()
# create_folder_action(working_directory='/home/user/Desktop/FRIDAY/working_dir', folder_name='my_new_folder')
Tool Requirements
-----------------
To ensure seamless integration into FRIDAY's tool repository, your tool code must adhere to the following format, consistent with the example tools provided:
1. **BaseAction Inheritance**:
Each tool must import and inherit from `BaseAction` provided by FRIDAY. Additionally, import any other necessary Python packages for your tool's functionality.
.. code-block:: python
from oscopilot.tool_repository.basic_tools.base_action import BaseAction
import os # Example of importing another necessary package
2. **Class Naming**:
The name of the class should be consistent with the tool's file name to maintain clarity and ease of identification within the tool repository.
3. **Initialization Method**:
The `__init__` method of your tool class must set `self._description`. This description should briefly outline the specific functionality of the tool, aiding in tool retrieval and user understanding.
.. code-block:: python
def __init__(self):
self._description = "Description of what the tool does."
4. **Execution Method**:
The `__call__` method is where the tool's specific execution code resides. It should include detailed explanations of the input and output parameters to guide the user.
.. code-block:: python
def __call__(self, parameter1, parameter2=None, *args, **kwargs):
"""
Detailed explanation of what this method does, its parameters, and what it returns.
"""
5. **Usage Example**:
At the end of your tool code, provide a usage example in the form of comments. This assists users and FRIDAY in understanding how to utilize the tool effectively. If the tool is automatically generated by FRIDAY, it will already meet these requirements.
.. code-block:: python
# Example of how to use the class
# tool_instance = ClassName()
# tool_instance(parameter1='value1', parameter2='value2')
By following these specific requirements, you ensure that your tool can be effectively integrated and utilized within the FRIDAY ecosystem. This consistency not only aids in tool management but also enhances the user experience by providing a standardized approach to tool development.
Conclusion
----------
With the provided guidelines and example, you are now equipped to extend FRIDAY's capabilities by adding new tools. By adhering to the structure and requirements specified for FRIDAY tools, you ensure that your tools can be effectively utilized within the FRIDAY ecosystem.
Remember, the power of FRIDAY lies in its flexibility and the collaborative efforts of its community. Your contributions help make FRIDAY more versatile and powerful.
We welcome you to submit your tools to the FRIDAY Gizmos repository at https://github.com/OS-Copilot/FRIDAY-Gizmos. Sharing your work enables others in the community to benefit from your contributions and further enhances the FRIDAY platform.
Happy coding!

View File

@@ -0,0 +1,103 @@
Deploying API Services
=================================
This tutorial guides you through deploying API services that FRIDAY can utilize to enhance its functionality. We provide several API tools, including audio2text, bing search, image search, web_loader, image_caption, and wolfram_alpha, all located within `oscopilot/tool_repository/api_tools`.
Configuring the Environment
----------------------------
1. **.env File Configuration**:
Your `.env` file contains essential configurations for the API services. The following variables must be set:
- ``MODEL_NAME``: Should already be set during the installation process. Example: ``"gpt-4-0125-preview"``.
- ``OPENAI_API_KEY`` and ``OPENAI_ORGANIZATION``: Also set during installation.
- ``API_BASE_URL``: The deployment address of the API service. For local deployment, use ``"http://127.0.0.1:8079"``.
- ``BING_SUBSCRIPTION_KEY`` and ``BING_SEARCH_URL``: Required for using bing search-related services. Example URL: ``"https://api.bing.microsoft.com/v7.0/search"``.
- ``WOLFRAMALPHA_APP_ID``: Necessary if you intend to use the wolfram_alpha tool.
Fill these in accordingly based on the services you plan to use.
Configuring API Tools
---------------------
2. **Selecting Required API Tools**:
In the `oscopilot/tool_repository/manager/api_server.py` file, you will configure which API tools FRIDAY will utilize. This is done by setting up the `services` and `server_list` variables.
- The ``services`` dictionary includes all available API tools that FRIDAY can use. Each key represents the service name, and the value is the corresponding router object.
- The ``server_list`` array specifies which of these services you wish to activate for the current deployment. This allows for flexible configuration depending on the needs of your specific environment or application.
Here is how you can specify your configuration:
.. code-block:: python
services = {
"bing": bing_router, # bing_search, image_search, and web_loader
"audio2text": audio2text_router,
"image_caption": image_caption_router,
"wolfram_alpha": wolfram_alpha_router
}
server_list = ["bing"]
In this example, we have included several services in the ``services`` dictionary, making them available for FRIDAY. However, by placing only "bing" in the ``server_list``, we are specifically activating the Bing services for use, including bing_search, image_search and web_loader. This demonstrates how to selectively enable certain API tools based on your requirements.
Launching the API Server
------------------------
3. **Starting the Service**:
To start the API service, run the following command:
.. code-block:: shell
python oscopilot/tool_repository/manager/api_server.py
Successful startup messages will look like this:
.. code-block:: text
INFO: Started server process [17709]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8079 (Press CTRL+C to quit)
Incoming request: GET http://127.0.0.1:8079/tools/bing/searchv2
Outgoing response: 200
INFO: 127.0.0.1:52324 - "GET /tools/bing/searchv2 HTTP/1.1" 200 OK
Updating API Documentation
--------------------------
4. **Update the OpenAPI Documentation**:
After the service is running, navigate to `http://localhost:8079/openapi.json` in your web browser. This URL hosts the auto-generated OpenAPI documentation for your API services. (Remember to replace the IP address if your service is not deployed locally.)
Here is an example of what the OpenAPI documentation might look like:
.. image:: /_static/demo_openapi.png
:align: center
:width: 100%
:alt: Example of OpenAPI Documentation
Copy the content displayed at this URL to the `oscopilot/tool_repository/manager/openapi.json` file in your project directory. This step ensures that FRIDAY's API server has the latest documentation regarding the available API services.
Testing the API Tools
---------------------
5. **Verifying Functionality**:
Test the deployed API tools by running a sample query with `run.py`. For example:
.. code-block:: shell
python quick_start.py --query 'Search the information of OpenAI'
If everything is configured correctly, FRIDAY should utilize the deployed API services to complete the task.
Conclusion
----------
You have successfully deployed API services for FRIDAY, enhancing its capabilities with additional tools. By following these steps, you can integrate a wide range of functionalities into FRIDAY, making it an even more powerful assistant.

View File

@@ -0,0 +1,94 @@
Designing New API Tools
==================================
After deploying existing API services as described in the previous section, this part will focus on how to develop and deploy a new API service for FRIDAY. By creating custom API tools, you can extend FRIDAY's capabilities to suit your specific needs.
Creating a New API Tool
-----------------------
1. **Setting Up the API Tool**:
Begin by creating a new folder for your API tool within `oscopilot/tool_repository/api_tools`. Inside this folder, create your tool file and write the API tool code. You can refer to the FastAPI documentation (https://fastapi.tiangolo.com/reference/fastapi/) and examples in the `oscopilot/tool_repository/api_tools` directory for guidance on coding your API tool.
Consider the following example when designing your API endpoint:
.. code-block:: python
@router.get("/tools/bing/searchv2", summary="Execute Bing Search - returns top web snippets related to the query. Avoid using complex filters like 'site:'. For detailed page content, further use the web browser tool.")
async def bing_search_v2(item: QueryItemV2):
try:
if item.top_k == None:
item.top_k = 5
search_results = bing_api_v2.search(item.query, item.top_k)
except RuntimeError as e:
raise HTTPException(status_code=500, detail=str(e))
return search_results
Ensure to include the `summary` parameter in `router.get`, providing a detailed description of the API tool's functionality, which FRIDAY will use to determine the tool's applicability for tasks.
Integrating the API Tool
------------------------
2. **Registering the New API Tool**:
Update `oscopilot/tool_repository/manager/api_server.py` with the new API tool's information. Add import statements and update the `services` and `server_list` accordingly.
Example code snippet:
.. code-block:: python
from oscopilot.tool_repository.api_tools.new_api.new_api_service import router as new_api_router
services = {
"bing": bing_router, # bing_search, image_search, and web_loader
"audio2text": audio2text_router,
"image_caption": image_caption_router,
"wolfram_alpha": wolfram_alpha_router,
"new_api": new_api_router
}
server_list = ["bing", "new_api"]
Launching the Service
---------------------
3. **Starting the API Service**:
Run the `api_server.py` file to launch the service:
.. code-block:: shell
python oscopilot/tool_repository/manager/api_server.py
Successful launch messages should resemble the following:
.. code-block:: text
INFO: Started server process [17709]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8079 (Press CTRL+C to quit)
Updating OpenAPI Documentation
-------------------------------
4. **Updating API Documentation**:
Navigate to `http://localhost:8079/openapi.json` (adjust the IP if necessary) and overwrite the content in `oscopilot/tool_repository/manager/openapi.json` with the content from this URL.
Verifying the API Tool
----------------------
5. **Testing the API Tool**:
Verify the new API tool's functionality by executing a test query with `run.py`:
.. code-block:: shell
python quick_start.py --query 'Your test query here'
Conclusion
----------
By following these steps, you have successfully designed, integrated, and deployed a new API tool for FRIDAY. This customization allows FRIDAY to perform tasks tailored to your specific requirements, enhancing its overall utility.

View File

@@ -0,0 +1,44 @@
Example: Automating Excel Tasks with FRIDAY
================================================
In this tutorial, we'll showcase how FRIDAY can be utilized for manipulating Excel files, automating tasks that would otherwise be tedious and time-consuming. We'll take on a specific task involving Excel file operations as an example.
Task Overview
-------------
You are required to perform several tasks related to Excel manipulation involving an experiment's data recorded in a sheet named "Sheet1". The tasks include:
- Applying a formula across rows in column B.
- Creating a scatter chart within "Sheet1" that plots acceleration (y-axis) against the hanging mass (x-axis).
- Labeling the chart axes with the appropriate column headers.
The Excel file for this task is located in `working_dir` and is named "Dragging.xlsx".
Step-by-Step Guide
------------------
1. **Preparing the Necessary Tools**:
Locate the following tools within `FRIDAY-Gizmos/Excel` directory:
- ``apply_formula_to_column_B``
- ``create_new_sheet_for_chart``
- ``insert_scatter_chart``
- ``read_excel_sheet``
Follow the steps outlined in the "Adding Your First Tool" tutorial to add these four tools to FRIDAY's tool repository.
2. **Executing the Task**:
To perform the Excel manipulation task, run the following command in your terminal. This command instructs FRIDAY to apply the necessary operations on the "Dragging.xlsx" file based on the provided task description.
.. code-block:: shell
python quick_start.py --query "You need to do some tasks related to excel manipulation.\n My sheet records data from an experiment where one hanging block (m2) drags a block (m1=0.75 kg) on a frictionless table via a rope around a frictionless and massless pulley. It has a sheet called Sheet1. \n Your task is: Fill out the rest rows in column B using the formula in B2. Create a scatter chart in Sheet1 with acceleration on the y-axis and the hanging mass on the x-axis. Add the corresponding column headers as the axis labels. \n You should complete the task and save the result directly in this excel file." --query_file_path "working_dir/Dragging.xlsx"
Conclusion
----------
Upon completion, the "Dragging.xlsx" file will have the specified formula applied across rows in column B, and a scatter chart will be created in "Sheet1" as requested. This example illustrates how FRIDAY can automate complex Excel operations, saving time and reducing the potential for manual errors.
Ensure to adjust file paths and names as per your specific setup. This tutorial demonstrates the power and flexibility of FRIDAY in handling and automating tasks within Excel, showcasing its capability to significantly streamline such processes.

View File

@@ -0,0 +1,146 @@
Enhancing FRIDAY with Self-Learning for Excel Task Automation
========================================================================
In this tutorial, we will explore how FRIDAY's self-learning feature enables it to autonomously learn and execute tasks involving Excel file manipulation, which were initially beyond its capability. We will specifically focus on a task from the SheetCopilot dataset and observe how FRIDAY evolves to complete it successfully using the `openpyxl` library.
Task Overview
----------------
You will undertake a specific task (task #9) from the SheetCopilot dataset,
which involves manipulating an Excel file.
The task is **"Copy the 'Product' column from 'Sheet1' to a new sheet named 'Sheet2', and then sort the 'Product' column in 'Sheet2' in ascending order."**
Initial Attempt
-----------------
1. **Running the Task**:
Execute the following command in your terminal to run task #9 from the SheetCopilot dataset:
.. code-block:: shell
python examples/SheetCopilot/run_sheet_task.py --sheet_task_id 9
The `run_sheet_task.py` script serves as the interface for FRIDAY to interact with tasks defined in the SheetCopilot dataset. Below is a brief explanation of the script's content:
- Module Imports and Configuration Setup:
.. code-block:: python
from oscopilot import FridayAgent
from oscopilot import FridayExecutor, FridayPlanner, FridayRetriever, ToolManager
from oscopilot.utils import setup_config, SheetTaskLoader
- Loading Tasks:
The script initializes the configuration and loads the task based on the provided task ID using SheetTaskLoader.
.. code-block:: python
args = setup_config()
sheet_task_loader = SheetTaskLoader("examples/SheetCopilot/sheet_task.jsonl")
- FRIDAY Agent Initialization:
An agent is initialized with components such as the Planner, Retriever, Executor, and Tool Manager, configured with the loaded arguments.
.. code-block:: python
agent = FridayAgent(FridayPlanner, FridayRetriever, FridayExecutor, ToolManager, config=args)
- Task Execution:
If a specific task ID is provided, the script fetches and runs that task. Otherwise, it loads and executes each task in the dataset sequentially.
.. code-block:: python
if args.sheet_task_id:
task = sheet_task_loader.get_data_by_task_id(args.sheet_task_id)
agent.run(task)
else:
task_lst = sheet_task_loader.load_sheet_task_dataset()
for task_id, task in enumerate(task_lst):
args.sheet_task_id = task_id
agent.run(task)
However, you'll notice that FRIDAY **is unable to** complete the task due to lacking specific tools for Excel manipulation.
Introducing Self-Learning
---------------------------
2. **Enabling FRIDAY to Learn**:
To overcome this limitation, we introduce FRIDAY to a self-learning module that allows it to explore and learn from the `openpyxl` library, thereby acquiring new tools for Excel file operations.
Run the self-learning command:
.. code-block:: shell
python course_learning.py --software_name Excel --package_name openpyxl --demo_file_path working_dir/Invoices.xlsx
This command directs FRIDAY to learn how to manipulate Excel files using the `openpyxl` library. Below is a brief overview of the `course_learning.py` script's functionality:
- Import Statements and Configuration Setup:
.. code-block:: python
from oscopilot import FridayAgent, FridayExecutor, FridayPlanner, FridayRetriever, SelfLearner, SelfLearning, ToolManager, TextExtractor
from oscopilot.utils import setup_config
- Initialization and Configuration Extraction:
The script begins by setting up the configuration and extracting parameters for the software name, package name, and a demo file path.
.. code-block:: python
args = setup_config()
software_name = args.software_name
package_name = args.package_name
demo_file_path = args.demo_file_path
- FRIDAY Agent and Self-Learning Module Initialization:
A FRIDAY agent is initialized with components such as the Planner, Retriever, Executor, and Tool Manager. The SelfLearning module is then initialized with the agent, allowing it to engage in self-learning activities.
.. code-block:: python
friday_agent = FridayAgent(FridayPlanner, FridayRetriever, FridayExecutor, ToolManager, config=args)
self_learning = SelfLearning(friday_agent, SelfLearner, ToolManager, args, TextExtractor)
- Self-Learning Process:
The SelfLearning module embarks on exploring the openpyxl library, utilizing the provided demo file as a learning resource.
.. code-block:: python
self_learning.self_learning(software_name, package_name, demo_file_path)
Through this exploratory process, FRIDAY can learn various tools such as `check_openpyxl_installed`, `read_excel_contents`, `filter_product_data`, and `export_filtered_data`, among others.
.. note::
The tools learned through self-learning have a degree of randomness.
Verifying the Learning Outcome
--------------------------------
3. **Re-running the Task**:
After the self-learning process, rerun the initial task to verify the effectiveness of the self-learning module:
.. code-block:: shell
python examples/SheetCopilot/run_sheet_task.py --sheet_task_id 9
This time, FRIDAY will successfully complete the task, demonstrating the acquired ability to manipulate Excel files through the learned tools.
Conclusion
--------------
This tutorial showcased the innovative self-learning feature of FRIDAY, which enables it to autonomously expand its toolset and adapt to tasks it was initially unable to perform.
By engaging in self-learning with the `openpyxl` library, FRIDAY demonstrated a significant improvement in handling Excel file operations, affirming the effectiveness and potential of self-learning in AI agents.
This process highlights FRIDAY's capability to evolve and adapt, making it a powerful tool for automating a wide range of tasks, including complex file manipulations.

7
docs/source/utils.rst Normal file
View File

@@ -0,0 +1,7 @@
Utils
=============
.. toctree::
:maxdepth: 2
utils_doc/llms

View File

@@ -0,0 +1,55 @@
Large Language Models
==============================
.. autoclass:: oscopilot.utils.llms.OpenAI
:members:
:undoc-members:
:show-inheritance:
Server Proxy Config
==============================
.. autoclass:: oscopilot.utils.server_config.ConfigManager
:members:
:undoc-members:
:show-inheritance:
Data Schema
==============================
.. automodule:: oscopilot.utils.schema
:members:
:undoc-members:
:show-inheritance:
Other Utils
==============================
.. autofunction:: oscopilot.utils.utils.send_chat_prompts
.. autofunction:: oscopilot.utils.utils.random_string
.. autofunction:: oscopilot.utils.utils.num_tokens_from_string
.. autofunction:: oscopilot.utils.utils.parse_content
.. autofunction:: oscopilot.utils.utils.clean_string
.. autofunction:: oscopilot.utils.utils.chunks
.. autofunction:: oscopilot.utils.utils.generate_prompt
.. autofunction:: oscopilot.utils.utils.cosine_similarity
.. autofunction:: oscopilot.utils.utils.is_valid_json_string

182
environment.yml Normal file
View File

@@ -0,0 +1,182 @@
name: oscopilot
channels:
- defaults
dependencies:
- bzip2=1.0.8
- ca-certificates=2023.08.22
- libcxx=14.0.6
- libffi=3.4.4
- ncurses=6.4
- openssl=3.0.12
- python=3.10.13
- readline=8.2
- setuptools=68.0.0
- sqlite=3.41.2
- tk=8.6.12
- wheel=0.41.2
- xz=5.4.2
- zlib=1.2.13
- pip:
- aiohttp==3.9.1
- aiosignal==1.3.1
- alabaster==0.7.16
- annotated-types==0.6.0
- anyio==3.7.1
- asgiref==3.7.2
- async-timeout==4.0.3
- attrs==23.1.0
- babel==2.14.0
- backoff==2.2.1
- bcrypt==4.1.2
- beautifulsoup4==4.12.3
- bs4==0.0.2
- cachetools==5.3.2
- certifi==2023.11.17
- cffi==1.16.0
- charset-normalizer==3.3.2
- chroma-hnswlib==0.7.3
- chromadb==0.4.20
- click==8.1.7
- coloredlogs==15.0.1
- contourpy==1.2.0
- cryptography==42.0.5
- cycler==0.12.1
- dataclasses-json==0.6.3
- datasets==2.15.0
- deprecated==1.2.14
- dill==0.3.7
- distro==1.8.0
- document==1.0
- docutils==0.20.1
- et-xmlfile==1.1.0
- exceptiongroup==1.2.0
- fastapi==0.105.0
- filelock==3.13.1
- flatbuffers==23.5.26
- fonttools==4.47.2
- frozenlist==1.4.0
- fsspec==2023.10.0
- google-auth==2.25.2
- googleapis-common-protos==1.62.0
- greenlet==3.0.2
- grpcio==1.60.0
- h11==0.14.0
- httpcore==1.0.2
- httptools==0.6.1
- httpx==0.25.2
- huggingface-hub==0.19.4
- humanfriendly==10.0
- idna==3.6
- imagesize==1.4.1
- importlib-metadata==6.11.0
- importlib-resources==6.1.1
- jaraco-context==4.3.0
- jinja2==3.1.3
- jsonpatch==1.33
- jsonpointer==2.4
- kiwisolver==1.4.5
- kubernetes==28.1.0
- langchain==0.0.349
- langchain-community==0.0.1
- langchain-core==0.0.13
- langsmith==0.0.69
- lxml==4.9.4
- markdown-it-py==3.0.0
- markupsafe==2.1.5
- marshmallow==3.20.1
- matplotlib==3.8.2
- mdit-py-plugins==0.4.0
- mdurl==0.1.2
- mmh3==4.0.1
- monotonic==1.6
- more-itertools==10.2.0
- mpmath==1.3.0
- multidict==6.0.4
- multiprocess==0.70.15
- mypy-extensions==1.0.0
- myst-parser==2.0.0
- numpy==1.26.2
- oauthlib==3.2.2
- onnxruntime==1.16.3
- openai==1.3.7
- openpyxl==3.1.2
- opentelemetry-api==1.21.0
- opentelemetry-exporter-otlp-proto-common==1.21.0
- opentelemetry-exporter-otlp-proto-grpc==1.21.0
- opentelemetry-instrumentation==0.42b0
- opentelemetry-instrumentation-asgi==0.42b0
- opentelemetry-instrumentation-fastapi==0.42b0
- opentelemetry-proto==1.21.0
- opentelemetry-sdk==1.21.0
- opentelemetry-semantic-conventions==0.42b0
- opentelemetry-util-http==0.42b0
- overrides==7.4.0
- packaging==23.2
- pandas==2.1.4
- pdfminer-six==20221105
- pdfplumber==0.10.4
- pillow==10.2.0
- pip==24.0
- posthog==3.1.0
- protobuf==4.25.1
- pulsar-client==3.3.0
- pyarrow==14.0.2
- pyarrow-hotfix==0.6
- pyasn1==0.5.1
- pyasn1-modules==0.3.0
- pycparser==2.21
- pydantic==2.5.2
- pydantic-core==2.14.5
- pygments==2.17.2
- pyparsing==3.1.1
- pypdfium2==4.27.0
- pypika==0.48.9
- pysqlite3==0.5.2
- python-dateutil==2.8.2
- python-docx==1.1.0
- python-dotenv==1.0.0
- python-pptx==0.6.23
- pytz==2023.3.post1
- pyyaml==6.0.1
- regex==2023.10.3
- requests==2.31.0
- requests-oauthlib==1.3.1
- rsa==4.9
- seaborn==0.13.2
- six==1.16.0
- sniffio==1.3.0
- snowballstemmer==2.2.0
- soupsieve==2.5
- sphinx==7.2.6
- sphinx-rtd-theme==2.0.0
- sphinxcontrib-applehelp==1.0.8
- sphinxcontrib-devhelp==1.0.6
- sphinxcontrib-htmlhelp==2.0.5
- sphinxcontrib-jquery==4.1
- sphinxcontrib-jsmath==1.0.1
- sphinxcontrib-qthelp==1.0.7
- sphinxcontrib-serializinghtml==1.1.10
- sqlalchemy==2.0.23
- starlette==0.27.0
- sympy==1.12
- tenacity==8.2.3
- tiktoken==0.5.2
- tokenizers==0.15.0
- tqdm==4.66.1
- typer==0.9.0
- typing-extensions==4.8.0
- typing-inspect==0.9.0
- tzdata==2023.3
- urllib3==1.26.18
- uvicorn==0.24.0.post1
- uvloop==0.19.0
- watchfiles==0.21.0
- websocket-client==1.7.0
- websockets==12.0
- wolframalpha==5.0.0
- wrapt==1.16.0
- xlsxwriter==3.1.9
- xmltodict==0.13.0
- xxhash==3.4.1
- yarl==1.9.4
- zipp==3.17.0

20
examples/GAIA/run_GAIA.py Normal file
View File

@@ -0,0 +1,20 @@
from oscopilot import FridayAgent
from oscopilot import FridayExecutor, FridayPlanner, FridayRetriever
from oscopilot.utils import setup_config, GAIALoader
args = setup_config()
agent = FridayAgent(FridayPlanner, FridayRetriever, FridayExecutor, config=args)
gaia = GAIALoader(args.level, args.dataset_cache)
if args.gaia_task_id:
task = gaia.get_data_by_task_id(args.gaia_task_id, args.dataset_type)
query = gaia.task2query(task)
agent.run(query)
else:
task_lst = gaia.dataset[args.dataset_type]
for task in task_lst:
query = gaia.task2query(task)
agent.run(query)

Binary file not shown.

View File

@@ -0,0 +1,16 @@
from oscopilot import FridayAgent
from oscopilot import FridayExecutor, FridayPlanner, FridayRetriever, ToolManager
from oscopilot.utils import setup_config, SheetTaskLoader
args = setup_config()
sheet_task_loader = SheetTaskLoader("examples/SheetCopilot/sheet_task.jsonl")
agent = FridayAgent(FridayPlanner, FridayRetriever, FridayExecutor, ToolManager, config=args)
if args.sheet_task_id:
task = sheet_task_loader.get_data_by_task_id(args.sheet_task_id)
agent.run(task)
else:
task_lst = sheet_task_loader.load_sheet_task_dataset()
for task_id, task in enumerate(task_lst):
args.sheet_task_id = task_id
agent.run(task)

View File

@@ -0,0 +1,20 @@
{"Sheet Name": "BoomerangSales", "No.": 3, "Context": "My workbook has two tables. Sheet \"Sheet1\" records the sales of a boomerang company. Sheet \"Retail Price\" lists the retail prices for all products.", "Instructions": "Count the number of each Product and put the results in a new sheet.", "Source": NaN, "Categories": "Entry and manipulation, Formula", "Atomic actions": "Create sheet, Update cell value (2), Autofill (2), Statistical functions", "Seed task": 5.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/BoomerangSales.xlsx"}
{"Sheet Name": "DemographicProfile", "No.": 3, "Context": "My workbook records information of respondents.", "Instructions": "Highlight married respondents with white text and yellow fill.", "Source": NaN, "Categories": "Formatting", "Atomic actions": "Conditional formatting (1)", "Seed task": 13.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/DemographicProfile.xlsx"}
{"Sheet Name": "EasyGDPBreakdown", "No.": 4, "Context": "My workbook records economic indicators of countries across many years.", "Instructions": "Display only \"Gross Domestic Product (GDP)\" values. Then Create a summary table in a new sheet that shows all indicators of each country for2013.", "Source": NaN, "Categories": "Pivot Table, Entry and manipulation", "Atomic actions": "Create sheet, Create Pivot Table (2)", "Seed task": 14.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/EasyGDPBreakdown.xlsx"}
{"Sheet Name": "IncomeStatement2", "No.": 5, "Context": "My workbook records yearly accounting data of my company. The necessary accounting formulas are as follows: Gross Profit = Net Sales \u2013 Cost of Goods Sold (COGS); Net sales = Sales - Sales return - Discounts and allowances; Cost of goods sold = Materials charges + Labor charges + Overhead; Gross profit = Net sales - Cost of goods sold.", "Instructions": "Help me fill in the Gross profit column and set the results as accounting type. Then under column A named \"Year_Profit\" in a new sheet, display the Year Column in Sheet 1 as text appended by a \"_\" with the corresponding Gross Profit value.", "Source": NaN, "Categories": "Entry and manipulation, Formatting, Formula", "Atomic actions": "Update cell value (1), Math functions, Autofill (1), Set data type (1), Text functions", "Seed task": 15.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/IncomeStatement2.xlsx"}
{"Sheet Name": "EasyGDPBreakdown", "No.": 5, "Context": "My workbook records economic indicators of countries across many years.", "Instructions": "Divide the values through column C to P by 1 million and put the results along with the year headers in a new sheet. Set the results type as accounting.", "Source": NaN, "Categories": "Entry and manipulation, Formula, Formatting", "Atomic actions": "Create sheet, Copy-paste (2), Update cell value (2), Math functions, Autofill (2), Set data type (2)", "Seed task": 18.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/EasyGDPBreakdown.xlsx"}
{"Sheet Name": "IncomeStatement", "No.": 2, "Context": "My workbook records the yearly accounting data of my company. The necessary accounting formulas are as follows: Gross Profit = Net Sales \u2013 Cost of Goods Sold (COGS); Operating Profit = Gross Profit - Operating Expenses; Net Profit = Operating Profit - Tax Expense.", "Instructions": "Fill the Gross Profit column according to the given formula and then highlight cells with a green background color if their values are above average.", "Source": NaN, "Categories": "Entry and Manipulation, Formatting", "Atomic actions": "Update cell value (1), Math functions, Conditional formatting (1)", "Seed task": 23.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/IncomeStatement.xlsx"}
{"Sheet Name": "Dragging", "No.": 3, "Context": "My sheet records data from an experiment where one hanging block (m2) drags a block (m1=0.75 kg) on a frictionless table via a rope around a frictionless and massless pulley.", "Instructions": "Fill out the rest rows in column B using the formula in B2. Create a scatter chart in a new sheet 'Sheet2' with acceleration on the y-axis and the hanging mass on the x-axis. Add the corresponding column headers as the axis labels.", "Source": NaN, "Categories": "Entry and manipulation, Charts", "Atomic actions": "Autofill (1), Create sheet, Create chart (2), Set chart axis (2)", "Seed task": 35.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/Dragging.xlsx"}
{"Sheet Name": "EntireShippingCosts", "No.": 9, "Context": "My company needs to deliver the goods to customers by truck. My workbook records the distances between my customers and four destinations. The per mile shipping charge is $3.5 with a minimum charge of $80.", "Instructions": "Make the four destination headers linked to files with the same name with an extension \".dat\".", "Source": NaN, "Categories": "Entry and manipulation", "Atomic actions": "Set hyperlink (1)", "Seed task": 38.0, "Validity": "Valid", "Reason": "The instruction is not relevant to the context of the workbook, as there are no files associated with customers' names, but it is still technically valid", "file_path": "examples/SheetCopilot/sheets/EntireShippingCosts.xlsx"}
{"Sheet Name": "DemographicProfile", "No.": 4, "Context": "My workbook records information of respondents.", "Instructions": "Sort the data by the Sex column from A to Z.", "Source": NaN, "Categories": "Management", "Atomic actions": "Sort (1)", "Seed task": 45.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/DemographicProfile.xlsx"}
{"Sheet Name": "Invoices", "No.": 10, "Context": "My workbook records many invoices made on different dates.", "Instructions": "Copy the 'Sheet1' Product column to a new sheet 'Sheet2' and sort 'Sheet2''s Product column in ascending order.", "Source": NaN, "Categories": "Entry and manipulation, Management", "Atomic actions": "Create sheet, Copy-paste (2), Sort (2)", "Seed task": 48.0, "Validity": "Valid", "Reason": "The instruction lists repeated product names, and it is realistic, relevant, clear", "file_path": "examples/SheetCopilot/sheets/Invoices.xlsx"}
{"Sheet Name": "IncomeStatement2", "No.": 9, "Context": "My workbook records yearly accounting data of my company. The necessary accounting formulas are as follows: Gross Profit = Net Sales \u2013 Cost of Goods Sold (COGS); Net sales = Sales - Sales return - Discounts and allowances; Cost of goods sold = Materials charges + Labor charges + Overhead; Gross profit = Net sales - Cost of goods sold.", "Instructions": "Calculate the gross profits for all years. Copy the Year, Sales, and Gross Profit columns to a new sheet and then plot a clustered column chart displaying the sales and profits vs. the years. Set the X and Y-axis titles as \"Year\" and \"Amount\" respectively.", "Source": NaN, "Categories": "Entry and manipulation, Formula, Charts", "Atomic actions": "Update cell value (1), Autofill (1), Copy-paste (2), Create chart (2), Set chart axis (2)", "Seed task": 54.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/IncomeStatement2.xlsx"}
{"Sheet Name": "Tax", "No.": 5, "Context": "My workbook records the weekly sales of my company and is used to compute taxes. The necessary formulas are as follows: Profit Before Tax = Sales - Total Expenses Before Tax; Tax Expense = Profit Before Tax * Tax Rate.", "Instructions": "Plot a line chart with the X-axis showing the week and the Y-axis showing the sales. Set the axis titles as the corresponding column headers.", "Source": NaN, "Categories": "Charts", "Atomic actions": "Create chart (1), Set chart axis (1)", "Seed task": 55.0, "Validity": "Valid", "Reason": "The instruction is realistic and relevant to the context of the workbook, and can be easily completed using Excel features", "file_path": "examples/SheetCopilot/sheets/Tax.xlsx"}
{"Sheet Name": "PricingTable", "No.": 8, "Context": "My workbook contains two tables: Sheet \"Sheet1\" records my transactional data which are the number of rolls of fence sold on certain dates. Sheet \"Pricing Table\" is a pricing table used to determine price per roll according to the range the roll number falls in (The range is bounded by Units From and Unit To).", "Instructions": "Sort the data by the Date column in an ascending order. Then summarize the total roll numbers for each date in a new sheet. Then create a line chart in the new sheet that displays the trend over time.", "Source": NaN, "Categories": "Management, Pivot Table, Charts", "Atomic actions": "Sort (1), Create sheet, Create Pivot Table (3), Create Pivot Chart (3)", "Seed task": 58.0, "Validity": "Valid", "Reason": "The instruction fulfills all criteria", "file_path": "examples/SheetCopilot/sheets/PricingTable.xlsx"}
{"Sheet Name": "BoomerangSales", "No.": 7, "Context": "My workbook has two tables. Sheet \"Sheet1\" records the sales of a boomerang company. Sheet \"Retail Price\" lists the retail prices for all products.", "Instructions": "Summarize the quantity of products sold on each website in a new sheet and then create a bar chart for the results.", "Source": NaN, "Categories": "Entry and manipulation, Pivot Table, Charts", "Atomic actions": "Create sheet, Create Pivot Table (2), Create Pivot Chart (2)", "Seed task": 59.0, "Validity": "Valid", "Reason": "The instruction creates a clustered bar chart to summarize the quantity of products sold by website, which is relevant and can be completed with the provided workbook data and Excel features", "file_path": "examples/SheetCopilot/sheets/BoomerangSales.xlsx"}
{"Sheet Name": "PricingTable", "No.": 9, "Context": "My workbook contains two tables: Sheet \"Sheet1\" records my transactional data which are the number of rolls of fence sold on certain dates. Sheet \"Pricing Table\" is a pricing table used to determine price per roll according to the range the roll number falls in (The range is bounded by Units From and Unit To).", "Instructions": "Sum up the number of rolls sold for each date in a new sheet and display the summarized data in a pie chart in the new sheet. Set the chart title as \"Roll number of each date\".", "Source": NaN, "Categories": "Entry and manipulation, Charts, Pivot Table", "Atomic actions": "Create sheet, Create Pivot Table (3), Create Pivot Chart (3), Set chart title (3)", "Seed task": 60.0, "Validity": "Valid", "Reason": "The instruction fulfills all criteria", "file_path": "examples/SheetCopilot/sheets/PricingTable.xlsx"}
{"Sheet Name": "BoomerangSales", "No.": 8, "Context": "My workbook has two tables. Sheet \"Sheet1\" records the sales of a boomerang company. Sheet \"Retail Price\" lists the retail prices for all products.", "Instructions": "Calculate revenue and generate a Pivot Table in a new sheet that summarizes the revenue of each product. In the same sheet, create a bar chart to display the table with data labels on top of the bars.", "Source": NaN, "Categories": "Entry and manipulation, Formula, Pivot Table, Charts", "Atomic actions": "Create sheet, Create Pivot Table (2), Create Pivot Chart (2), Add data labels (2)", "Seed task": 63.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/BoomerangSales.xlsx"}
{"Sheet Name": "StockChange", "No.": 4, "Context": "My workbook records the values of my stocks on two dates.", "Instructions": "In a new column named \"Color Indicator\", set the cell value to \"Y\" if the stock value increased from Jan 1 to Dec 31 and \"N\" otherwise. Then, create a bar chart displaying stock names and their values on Dec 31.", "Source": NaN, "Categories": "Charts, Formatting, Entry and manipulation", "Atomic actions": "Update cell value (1), Autofill (1), Logical functions, Create chart (1)", "Seed task": 64.0, "Validity": "Valid", "Reason": "The instruction fulfills the 4 criteria", "file_path": "examples/SheetCopilot/sheets/StockChange.xlsx"}
{"Sheet Name": "PricingTable", "No.": 10, "Context": "My workbook contains two tables: Sheet \"Sheet1\" records my transactional data which are the number of rolls of fence sold on certain dates. Sheet \"Pricing Table\" is a pricing table used to determine price per roll according to the range the roll number falls in (The range is bounded by Units From and Unit To).", "Instructions": "Create a new column named \"Day\" between columns A and B to contain the day extracted from the Date column. Then, summarize the average roll number per day in a new sheet.", "Source": NaN, "Categories": "Entry and manipulation, Formatting, Pivot Table", "Atomic actions": "Insert column (1), Update cell value (1), Date and time functions, Set data type (1), Create sheet, Create Pivot Table (3)", "Seed task": 67.0, "Validity": "Valid", "Reason": "The instruction fulfills all criteria", "file_path": "examples/SheetCopilot/sheets/PricingTable.xlsx"}
{"Sheet Name": "EntireSummerSales", "No.": 1, "Context": "My workbook records the sales of my company in the summer.", "Instructions": "In a new sheet, merge cells A1:B1 and write bold text \"Summer sales ($)\" with blue fill color and white text color. Then create a pivot table starting at A2 in this new sheet for the Sheet 1 data to calculate the revenue sum for each product. Plot a horizontal bar chart illustrating the results. Set the chart title as \"Revenue of Each Product\" and turn off the axis titles and legend.", "Source": "E-DAB-05-Visualizations-Finished - T V (1)", "Categories": "Entry and manipulation, Formatting, Pivot Table, Charts", "Atomic actions": "Create sheet, Merge cells (2), Update cell value (2), Format cells (2), Create Pivot Table (2), Create Pivot Chart (2), Set chart title (2), Set chart axis (2), Set chart legend (2)", "Seed task": NaN, "Validity": NaN, "Reason": NaN, "file_path": "examples/SheetCopilot/sheets/EntireSummerSales.xlsx"}
{"Sheet Name": "SimpleCompoundInterest", "No.": 1, "Context": "My workbook is blank used to record the interests of my investment. The necessary formulas are as follows: Simple Interest = Principle amount * Year * Interest rate; Compound Interest = Principle amount * (1 + Interest rate) ^ Year - Principle amount.", "Instructions": "If I have just invested 100 dollars (Principle amount) and the annual interest rate is 10%, how much will I earn each year? Please fill in the table to show the amount with simple interest and with compound interest for the next 20 years and set all values as accounting type. Then plot a line chart to compare the two types of interests. Show the legends at the bottom.", "Source": NaN, "Categories": "Entry and manipulation, Formula, Formatting, Charts", "Atomic actions": "Update cell value (1), Math functions, Autofill (1), Set data type (1), Create chart (1), Add data series (1), Set chart legend (1)", "Seed task": NaN, "Validity": NaN, "Reason": NaN, "file_path": "examples/SheetCopilot/sheets/SimpleCompoundInterest.xlsx"}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,55 +0,0 @@
from typing import Optional
class BaseAction:
"""Base class for all actions.
Args:
description (str, optional): The description of the action. Defaults to
None.
name (str, optional): The name of the action. If None, the name will
be class name. Defaults to None.
"""
def __init__(self,
description: Optional[str] = None,
name: Optional[str] = None,
timeout: int = 2,
action_type: Optional[str] = 'BASH') -> None:
if name is None:
name = self.__class__.__name__
self._name = name
self._description = description
self._timeout = timeout
assert action_type in ['BASH', 'CODE', 'TOOL']
self.action_type = action_type
def __call__(self, *args, **kwargs):
raise NotImplementedError
def _python(self, *lines):
return f'python -Bc "{"; ".join(lines)}"'
def _import(self, *packages):
return f'from jarvis.{".".join(packages)} import *'
@property
def timeout(self):
return self._timeout
@property
def name(self):
return self._name
@property
def description(self):
return self._description
def __repr__(self):
return f'{self.name}:{self.description}'
def __str__(self):
return self.__repr__()
if __name__ == '__main__':
action = BaseAction()

View File

@@ -1,33 +0,0 @@
import platform
def get_os_version():
system = platform.system()
if system == "Darwin":
# macOS
return 'macOS ' + platform.mac_ver()[0]
elif system == "Linux":
try:
with open("/etc/os-release") as f:
lines = f.readlines()
for line in lines:
if line.startswith("PRETTY_NAME"):
return line.split("=")[1].strip().strip('"')
except FileNotFoundError:
pass
return platform.version()
else:
return "Unknown Operating System"
def check_os_version(s):
if "mac" in s or "Ubuntu" in s or "CentOS" in s:
print("perating System Version:", s)
else:
raise ValueError("Unknown Operating System")
if __name__ == "__main__":
os_version = get_os_version()
print("Operating System Version:", os_version)

View File

@@ -1,31 +0,0 @@
from friday.core.llms import OpenAI
QA_SYS_PROMPT='''
You are a helpful ai assistant that can answer the questions asked by the user
with the help of the context provided by the user in a step by step manner.
If you don't know how to answer the user's question, answer "I don't know how to answer" instead of making up an answer.
'''
QA_USER_PROMPT='''
context: {context}
question: {question}
'''
class AnswerAgent():
''' Answer is used to answer the question asked by the user'''
def __init__(self, config_path=None, open_api_doc_path = None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
# self.mac_systom_prompts =
def generate_call_api_code(self, question,context="No context provided."):
self.sys_prompt = QA_SYS_PROMPT
self.user_prompt = QA_USER_PROMPT.format(
question = question,
context = context
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)

View File

@@ -1,52 +0,0 @@
from friday.action.base_action import BaseAction
import re
import json
class BaseAgent:
"""
BaseAgent is the base class of all agents.
"""
def __init__(self):
self.llm = None
self.environment = None
self.action_lib = None
self.max_iter = None
# self.action_lib_description = {}
# self.action = None
# self.retrieval_top_k = None
# self.action_lib_dir = None
# self.init_action_lib()
# Extract information from text
def extract_information(self, message, begin_str='[BEGIN]', end_str='[END]'):
result = []
_begin = message.find(begin_str)
_end = message.find(end_str)
while not (_begin == -1 or _end == -1):
result.append(message[_begin + len(begin_str):_end])
message = message[_end + len(end_str):]
_begin = message.find(begin_str)
_end = message.find(end_str)
return result
# egular expression to find JSON data within a string
def extract_json_from_string(self, text):
# Improved regular expression to find JSON data within a string
json_regex = r'```json\s*\n\{[\s\S]*?\n\}\s*```'
# Search for JSON data in the text
matches = re.findall(json_regex, text)
# Extract and parse the JSON data if found
if matches:
# Removing the ```json and ``` from the match to parse it as JSON
json_data = matches[0].replace('```json', '').replace('```', '').strip()
try:
# Parse the JSON data
parsed_json = json.loads(json_data)
return parsed_json
except json.JSONDecodeError as e:
return f"Error parsing JSON data: {e}"
else:
return "No JSON data found in the string."

File diff suppressed because one or more lines are too long

View File

@@ -1,905 +0,0 @@
from friday.agent.base_agent import BaseAgent
from friday.core.action_node import ActionNode
from collections import defaultdict, deque
from friday.environment.py_env import PythonEnv
from friday.core.llms import OpenAI
from friday.core.action_manager import ActionManager
from friday.action.get_os_version import get_os_version, check_os_version
from friday.agent.prompt import prompt
from friday.core.utils import get_open_api_description_pair, get_open_api_doc_path
import re
import json
import logging
from pathlib import Path
class FridayAgent(BaseAgent):
""" AI agent class, including planning, retrieval and execution modules """
def __init__(self, config_path=None, action_lib_dir=None, max_iter=3):
super().__init__()
self.llm = OpenAI(config_path)
self.action_lib = ActionManager(config_path, action_lib_dir)
self.environment = PythonEnv()
self.prompt = prompt
self.system_version = get_os_version()
self.planner = PlanningModule(self.llm, self.environment, self.action_lib, self.prompt['planning_prompt'], self.system_version)
self.retriever = RetrievalModule(self.llm, self.environment, self.action_lib, self.prompt['retrieve_prompt'])
self.executor = ExecutionModule(self.llm, self.environment, self.action_lib, self.prompt['execute_prompt'], self.system_version, max_iter)
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
def run(self, task):
"""
Run FridayAgent to execute task.
"""
# relevant action
retrieve_action_name = self.retriever.retrieve_action_name(task)
retrieve_action_description_pair = self.retriever.retrieve_action_description_pair(retrieve_action_name)
# decompose task
self.planner.decompose_task(task, retrieve_action_description_pair)
# iter each subtask
while self.planner.execute_list:
action = self.planner.execute_list[0]
action_node = self.planner.action_node[action]
description = action_node.description
logging.info("The current subtask is: {subtask}".format(subtask=description))
code = ''
# The return value of the current task
result = ''
next_action = action_node.next_action
relevant_code = {}
type = action_node.type
pre_tasks_info = self.planner.get_pre_tasks_info(action)
if type == 'Code':
# retrieve existing action
retrieve_name = self.retriever.retrieve_action_name(description, 3)
relevant_code = self.retriever.retrieve_action_code_pair(retrieve_name)
# task execute step
if type == 'QA':
# result = self.executor.question_and_answer_action(pre_tasks_info, task, task)
if self.planner.action_num == 1:
result = self.executor.question_and_answer_action(pre_tasks_info, task, task)
else:
result = self.executor.question_and_answer_action(pre_tasks_info, task, description)
print(result)
logging.info(result)
else:
invoke = ''
if type == 'API':
api_path = self.executor.extract_API_Path(description)
code = self.executor.api_action(description, api_path, pre_tasks_info)
else:
code, invoke = self.executor.generate_action(action, description, pre_tasks_info, relevant_code)
# Execute python tool class code
state = self.executor.execute_action(code, invoke, type)
result = state.result
logging.info(state)
# Check whether the code runs correctly, if not, amend the code
if type == 'Code':
need_mend = False
trial_times = 0
critique = ''
score = 0
# If no error is reported, check whether the task is completed
if state.error == None:
critique, judge, score = self.executor.judge_action(code, description, state, next_action)
if not judge:
print("critique: {}".format(critique))
need_mend = True
else:
# Determine whether it is caused by an error outside the code
reasoning, error_type = self.executor.analysis_action(code, description, state)
if error_type == 'replan':
relevant_action_name = self.retriever.retrieve_action_name(reasoning)
relevant_action_description_pair = self.retriever.retrieve_action_description_pair(relevant_action_name)
self.planner.replan_task(reasoning, action, relevant_action_description_pair)
continue
need_mend = True
# The code failed to complete its task, fix the code
while (trial_times < self.executor.max_iter and need_mend == True):
trial_times += 1
print("current amend times: {}".format(trial_times))
new_code, invoke = self.executor.amend_action(code, description, state, critique, pre_tasks_info)
critique = ''
code = new_code
# Run the current code and check for errors
state = self.executor.execute_action(code, invoke, type)
result = state.result
logging.info(state)
# print(state)
# Recheck
if state.error == None:
critique, judge, score = self.executor.judge_action(code, description, state, next_action)
# The task execution is completed and the loop exits
if judge:
need_mend = False
break
# print("critique: {}".format(critique))
else: # The code still needs to be corrected
need_mend = True
# If the task still cannot be completed, an error message will be reported.
if need_mend == True:
print("I can't Do this Task!!")
break
else: # The task is completed, if code is save the code, args_description, action_description in lib
if score >= 8:
self.executor.store_action(action, code)
print("Current task execution completed!!!")
self.planner.update_action(action, result, relevant_code, True, type)
self.planner.execute_list.remove(action)
class PlanningModule(BaseAgent):
""" The planning module is responsible for breaking down complex tasks into subtasks, re-planning, etc. """
def __init__(self, llm, environment, action_lib, prompt, system_version):
"""
Module initialization, including setting the execution environment, initializing prompts, etc.
"""
super().__init__()
# Model, environment, database
self.llm = llm
self.environment = environment
self.action_lib = action_lib
self.system_version = system_version
self.prompt = prompt
# Action nodes, action graph information and action topology sorting
self.action_num = 0
self.action_node = {}
self.action_graph = defaultdict(list)
self.execute_list = []
def decompose_task(self, task, action_description_pair):
"""
Implement task disassembly logic.
"""
files_and_folders = self.environment.list_working_dir()
action_description_pair = json.dumps(action_description_pair)
response = self.task_decompose_format_message(task, action_description_pair, files_and_folders)
logging.info(f"The overall response is: {response}")
decompose_json = self.extract_json_from_string(response)
# Building action graph and topological ordering of actions
self.create_action_graph(decompose_json)
self.topological_sort()
def replan_task(self, reasoning, current_task, relevant_action_description_pair):
"""
replan new task to origin action graph .
"""
# current_task information
current_action = self.action_node[current_task]
current_task_description = current_action.description
relevant_action_description_pair = json.dumps(relevant_action_description_pair)
files_and_folders = self.environment.list_working_dir()
response = self.task_replan_format_message(reasoning, current_task, current_task_description, relevant_action_description_pair, files_and_folders)
new_action = self.extract_json_from_string(response)
# add new action to action graph
self.add_new_action(new_action, current_task)
# update topological sort
self.topological_sort()
def update_action(self, action, return_val='', relevant_code=None, status=False, type='Code'):
"""
Update action node info.
"""
if return_val:
if type=='Code':
return_val = self.extract_information(return_val, "<return>", "</return>")
print("************************<return>**************************")
logging.info(return_val)
print(return_val)
print("************************</return>*************************")
if return_val != 'None':
self.action_node[action]._return_val = return_val
if relevant_code:
self.action_node[action]._relevant_code = relevant_code
self.action_node[action]._status = status
def task_decompose_format_message(self, task, action_list, files_and_folders):
"""
Send decompse task prompt to LLM and get task list.
"""
api_list = get_open_api_description_pair()
sys_prompt = self.prompt['_SYSTEM_TASK_DECOMPOSE_PROMPT']
user_prompt = self.prompt['_USER_TASK_DECOMPOSE_PROMPT'].format(
system_version=self.system_version,
task=task,
action_list = action_list,
api_list = api_list,
working_dir = self.environment.working_dir,
files_and_folders = files_and_folders
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def task_replan_format_message(self, reasoning, current_task, current_task_description, action_list, files_and_folders):
"""
Send replan task prompt to LLM and get task list.
"""
sys_prompt = self.prompt['_SYSTEM_TASK_REPLAN_PROMPT']
user_prompt = self.prompt['_USER_TASK_REPLAN_PROMPT'].format(
current_task = current_task,
current_task_description = current_task_description,
system_version=self.system_version,
reasoning = reasoning,
action_list = action_list,
working_dir = self.environment.working_dir,
files_and_folders = files_and_folders
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def get_action_list(self, relevant_action=None):
"""
Get action list, including action names and descriptions.
"""
action_dict = self.action_lib.descriptions
if not relevant_action:
return json.dumps(action_dict)
relevant_action_dict = {action : description for action ,description in action_dict.items() if action in relevant_action}
relevant_action_list = json.dumps(relevant_action_dict)
return relevant_action_list
def create_action_graph(self, decompose_json):
"""
Creates a action graph from a list of dependencies.
"""
# generate execte graph
for _, task_info in decompose_json.items():
self.action_num += 1
task_name = task_info['name']
task_description = task_info['description']
task_type = task_info['type']
task_dependencies = task_info['dependencies']
self.action_node[task_name] = ActionNode(task_name, task_description, task_type)
self.action_graph[task_name] = task_dependencies
for pre_action in self.action_graph[task_name]:
self.action_node[pre_action].next_action[task_name] = task_description
def add_new_action(self, new_task_json, current_task):
"""
Creates a action graph from a list of dependencies.
"""
# update execte graph
for _, task_info in new_task_json.items():
self.action_num += 1
task_name = task_info['name']
task_description = task_info['description']
task_type = task_info['type']
task_dependencies = task_info['dependencies']
self.action_node[task_name] = ActionNode(task_name, task_description, task_type)
self.action_graph[task_name] = task_dependencies
for pre_action in self.action_graph[task_name]:
self.action_node[pre_action].next_action[task_name] = task_description
last_new_task = list(new_task_json.keys())[-1]
self.action_graph[current_task].append(last_new_task)
def topological_sort(self):
"""
generate graph topological sort.
"""
# init execute list
self.execute_list = []
graph = defaultdict(list)
for node, dependencies in self.action_graph.items():
# If the current node has not been executed, put it in the dependency graph.
if not self.action_node[node].status:
graph.setdefault(node, [])
for dependent in dependencies:
# If the dependencies of the current node have not been executed, put them in the dependency graph.
if not self.action_node[dependent].status:
graph[dependent].append(node)
in_degree = {node: 0 for node in graph}
# Count in-degree for each node
for node in graph:
for dependent in graph[node]:
in_degree[dependent] += 1
# Initialize queue with nodes having in-degree 0
queue = deque([node for node in in_degree if in_degree[node] == 0])
# List to store the order of execution
while queue:
# Get one node with in-degree 0
current = queue.popleft()
self.execute_list.append(current)
# Decrease in-degree for all nodes dependent on current
for dependent in graph[current]:
in_degree[dependent] -= 1
if in_degree[dependent] == 0:
queue.append(dependent)
# Check if topological sort is possible (i.e., no cycle)
if len(self.execute_list) == len(graph):
print("topological sort is possible")
else:
return "Cycle detected in the graph, topological sort not possible."
def get_pre_tasks_info(self, current_task):
"""
Get string information of the prerequisite task for the current task.
"""
pre_tasks_info = {}
for task in self.action_graph[current_task]:
task_info = {
"description" : self.action_node[task].description,
"return_val" : self.action_node[task].return_val
}
pre_tasks_info[task] = task_info
pre_tasks_info = json.dumps(pre_tasks_info)
return pre_tasks_info
class RetrievalModule(BaseAgent):
""" Retrieval module, responsible for retrieving available actions in the action library. """
def __init__(self, llm, environment, action_lib, prompt):
"""
Module initialization, including setting the execution environment, initializing prompts, etc.
"""
super().__init__()
# Model, environment, database
self.llm = llm
self.environment = environment
self.action_lib = action_lib
self.prompt = prompt
def delete_action(self, action):
"""
Delete relevant action content, including code, description, parameter information, etc.
"""
self.action_lib.delete_action(action)
def retrieve_action_name(self, task, k=10):
"""
Implement retrieval action name logic
"""
retrieve_action_name = self.action_lib.retrieve_action_name(task, k)
return retrieve_action_name
def action_code_filter(self, action_code_pair, task):
"""
Implement filtering of search codes.
"""
action_code_pair = json.dumps(action_code_pair)
response = self.action_code_filter_format_message(action_code_pair, task)
action_name = self.extract_information(response, '<action>', '</action>')[0]
code = ''
if action_name:
code = self.action_lib.get_action_code(action_name)
return code
def retrieve_action_description(self, action_name):
"""
Implement search action description logic.
"""
retrieve_action_description = self.action_lib.retrieve_action_description(action_name)
return retrieve_action_description
def retrieve_action_code(self, action_name):
"""
Implement retrieval action code logic.
"""
retrieve_action_code = self.action_lib.retrieve_action_code(action_name)
return retrieve_action_code
def retrieve_action_code_pair(self, retrieve_action_name):
"""
Retrieve task code pairs.
"""
retrieve_action_code = self.retrieve_action_code(retrieve_action_name)
action_code_pair = {}
for name, description in zip(retrieve_action_name, retrieve_action_code):
action_code_pair[name] = description
return action_code_pair
def retrieve_action_description_pair(self, retrieve_action_name):
"""
Retrieve task description pairs.
"""
retrieve_action_description = self.retrieve_action_description(retrieve_action_name)
action_description_pair = {}
for name, description in zip(retrieve_action_name, retrieve_action_description):
action_description_pair[name] = description
return action_description_pair
def action_code_filter_format_message(self, action_code_pair, task_description):
"""
Send aciton code to llm to filter useless action codes.
"""
sys_prompt = self.prompt['_SYSTEM_ACTION_CODE_FILTER_PROMPT']
user_prompt = self.prompt['_USER_ACTION_CODE_FILTER_PROMPT'].format(
task_description=task_description,
action_code_pair=action_code_pair
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
class ExecutionModule(BaseAgent):
""" Execution module, responsible for executing actions and updating the action library """
def __init__(self, llm, environment, action_lib, prompt, system_version, max_iter):
'''
Module initialization, including setting the execution environment, initializing prompts, etc.
'''
super().__init__()
self.llm = llm
self.environment = environment
self.action_lib = action_lib
self.system_version = system_version
self.prompt = prompt
self.max_iter = max_iter
self.open_api_doc_path = get_open_api_doc_path()
self.open_api_doc = {}
with open(self.open_api_doc_path) as f:
self.open_api_doc = json.load(f)
def generate_action(self, task_name, task_description, pre_tasks_info, relevant_code):
'''
Generate action code logic, generate code that can complete the action and its calls.
'''
relevant_code = json.dumps(relevant_code)
create_msg = self.skill_create_and_invoke_format_message(task_name, task_description, pre_tasks_info, relevant_code)
code = self.extract_python_code(create_msg)
invoke = self.extract_information(create_msg, begin_str='<invoke>', end_str='</invoke>')[0]
return code, invoke
# def generate_action(self, task_name, task_description):
# '''
# Generate action code logic, generate code that can complete the action and its calls.
# '''
# create_msg = self.skill_create_format_message(task_name, task_description)
# code = self.extract_python_code(create_msg)
# return code
def execute_action(self, code, invoke, type):
'''
Implement action execution logic.
instantiate the action class and execute it, and return the execution completed status.
'''
# print result info
if type == 'Code':
info = "\n" + '''print("<return>")''' + "\n" + "print(result)" + "\n" + '''print("</return>")'''
code = code + '\nresult=' + invoke + info
print("************************<code>**************************")
print(code)
print("************************</code>*************************")
state = self.environment.step(code)
print("************************<state>**************************")
print(state)
# print("error: " + state.error + "\nresult: " + state.result + "\npwd: " + state.pwd + "\nls: " + state.ls)
print("************************</state>*************************")
return state
# def execute_action(self, code, task_description, pre_tasks_info):
# '''
# Implement action execution logic.
# instantiate the action class and execute it, and return the execution completed status.
# '''
# invoke_msg = self.invoke_generate_format_message(code, task_description, pre_tasks_info)
# invoke = self.extract_information(invoke_msg, begin_str='<invoke>', end_str='</invoke>')[0]
# # print result info
# info = "\n" + '''print("<return>")''' + "\n" + "print(result)" + "\n" + '''print("</return>")'''
# code = code + '\nresult=' + invoke + info
# print("************************<code>**************************")
# print(code)
# print("************************</code>*************************")
# state = self.environment.step(code)
# print("************************<state>**************************")
# print(state)
# print("************************</state>*************************")
# return state
def judge_action(self, code, task_description, state, next_action):
'''
Implement action judgment logic.
judge whether the action completes the current task, and return the JSON result of the judgment.
'''
judge_json = self.task_judge_format_message(code, task_description, state.result, state.pwd, state.ls, next_action)
reasoning = judge_json['reasoning']
judge = judge_json['judge']
score = judge_json['score']
return reasoning, judge, score
def amend_action(self, current_code, task_description, state, critique, pre_tasks_info):
'''
Implement action repair logic.
repair unfinished tasks or erroneous code, and return the repaired code and call.
'''
amend_msg = self.skill_amend_and_invoke_format_message(current_code, task_description, state.error, state.result, state.pwd, state.ls, critique, pre_tasks_info)
new_code = self.extract_python_code(amend_msg)
invoke = self.extract_information(amend_msg, begin_str='<invoke>', end_str='</invoke>')[0]
return new_code, invoke
# def amend_action(self, current_code, task_description, state, critique):
# '''
# Implement action repair logic.
# repair unfinished tasks or erroneous code, and return the repaired code and call.
# '''
# amend_msg = self.skill_amend_format_message(current_code, task_description, state.error, state.result, state.pwd, state.ls, critique)
# new_code = self.extract_python_code(amend_msg)
# return new_code
def analysis_action(self, code, task_description, state):
'''
Implement the analysis of code errors.
If it is an environmental error that requires new operations, go to the planning module.
Otherwise, hand it to amend_action and return JSON.
'''
analysis_json = self.error_analysis_format_message(code, task_description, state.error, state.pwd, state.ls)
reasoning = analysis_json['reasoning']
type = analysis_json['type']
return reasoning, type
def store_action(self, action, code):
"""
Store action code and info.
"""
# If action not in db.
if not self.action_lib.exist_action(action):
# Implement action storage logic and store new actions
args_description = self.extract_args_description(code)
action_description = self.extract_action_description(code)
# Save action name, code, and description to JSON
action_info = self.save_action_info_to_json(action, code, action_description)
# Save code and descriptions to databases and JSON files
self.action_lib.add_new_action(action_info)
# Parameter description save path
args_description_file_path = self.action_lib.action_lib_dir + '/args_description/' + action + '.txt'
# save args_description
self.save_str_to_path(args_description, args_description_file_path)
else:
print("action already exists!")
def api_action(self, description, api_path, context="No context provided."):
"""
Call api tool to execute task.
"""
response = self.generate_call_api_format_message(description, api_path, context)
code = self.extract_python_code(response)
return code
def question_and_answer_action(self, context, question, current_question=None):
"""
Answer questions based on the information found.
"""
response = self.question_and_answer_format_message(context, question, current_question)
return response
def skill_create_and_invoke_format_message(self, task_name, task_description, pre_tasks_info, relevant_code):
"""
Send skill generate and invoke message to LLM.
"""
sys_prompt = self.prompt['_SYSTEM_SKILL_CREATE_AND_INVOKE_PROMPT']
user_prompt = self.prompt['_USER_SKILL_CREATE_AND_INVOKE_PROMPT'].format(
system_version=self.system_version,
task_description=task_description,
working_dir= self.environment.working_dir,
task_name=task_name,
pre_tasks_info=pre_tasks_info,
relevant_code=relevant_code
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def skill_create_format_message(self, task_name, task_description):
"""
Send skill create message to LLM.
"""
sys_prompt = self.prompt['_SYSTEM_SKILL_CREATE_PROMPT']
user_prompt = self.prompt['_USER_SKILL_CREATE_PROMPT'].format(
system_version=self.system_version,
task_description=task_description,
working_dir= self.environment.working_dir,
task_name=task_name
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def invoke_generate_format_message(self, class_code, task_description, pre_tasks_info):
"""
Send invoke generate message to LLM.
"""
class_name, args_description = self.extract_class_name_and_args_description(class_code)
sys_prompt = self.prompt['_SYSTEM_INVOKE_GENERATE_PROMPT']
user_prompt = self.prompt['_USER_INVOKE_GENERATE_PROMPT'].format(
class_name = class_name,
task_description = task_description,
args_description = args_description,
pre_tasks_info = pre_tasks_info,
working_dir = self.environment.working_dir
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def question_and_answer_format_message(self, context, question, current_question):
"""
Send QA message to LLM.
"""
sys_prompt = self.prompt['_SYSTEM_QA_PROMPT']
user_prompt = self.prompt['_USER_QA_PROMPT'].format(
context = context,
question = question,
current_question = current_question
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def skill_amend_and_invoke_format_message(self, original_code, task, error, code_output, current_working_dir, files_and_folders, critique, pre_tasks_info):
"""
Send skill amend message to LLM.
"""
sys_prompt = self.prompt['_SYSTEM_SKILL_AMEND_AND_INVOKE_PROMPT']
user_prompt = self.prompt['_USER_SKILL_AMEND_AND_INVOKE_PROMPT'].format(
original_code = original_code,
task = task,
error = error,
code_output = code_output,
current_working_dir = current_working_dir,
working_dir= self.environment.working_dir,
files_and_folders = files_and_folders,
critique = critique,
pre_tasks_info = pre_tasks_info
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def skill_amend_format_message(self, original_code, task, error, code_output, current_working_dir, files_and_folders, critique):
"""
Send skill amend message to LLM.
"""
sys_prompt = self.prompt['_SYSTEM_SKILL_AMEND_PROMPT']
user_prompt = self.prompt['_USER_SKILL_AMEND_PROMPT'].format(
original_code = original_code,
task = task,
error = error,
code_output = code_output,
current_working_dir = current_working_dir,
working_dir= self.environment.working_dir,
files_and_folders = files_and_folders,
critique = critique
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
return self.llm.chat(self.message)
def task_judge_format_message(self, current_code, task, code_output, current_working_dir, files_and_folders, next_action):
"""
Send task judge prompt to LLM and get JSON response.
"""
next_action = json.dumps(next_action)
sys_prompt = self.prompt['_SYSTEM_TASK_JUDGE_PROMPT']
user_prompt = self.prompt['_USER_TASK_JUDGE_PROMPT'].format(
current_code=current_code,
task=task,
code_output=code_output,
current_working_dir=current_working_dir,
working_dir=self.environment.working_dir,
files_and_folders=files_and_folders,
next_action=next_action
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
response =self.llm.chat(self.message)
judge_json = self.extract_json_from_string(response)
print("************************<judge_json>**************************")
print(judge_json)
print("************************</judge_json>*************************")
return judge_json
def error_analysis_format_message(self, current_code, task, code_error, current_working_dir, files_and_folders):
"""
Send error analysis prompt to LLM and get JSON response.
"""
sys_prompt = self.prompt['_SYSTEM_ERROR_ANALYSIS_PROMPT']
user_prompt = self.prompt['_USER_ERROR_ANALYSIS_PROMPT'].format(
current_code=current_code,
task=task,
code_error=code_error,
current_working_dir=current_working_dir,
working_dir= self.environment.working_dir,
files_and_folders= files_and_folders
)
self.message = [
{"role": "system", "content": sys_prompt},
{"role": "user", "content": user_prompt},
]
response =self.llm.chat(self.message)
analysis_json = self.extract_json_from_string(response)
print("************************<analysis_json>**************************")
print(analysis_json)
print("************************</analysis_json>*************************")
return analysis_json
def extract_python_code(self, response):
"""
Extract python code from response.
"""
python_code = ""
if '```python' in response:
python_code = response.split('```python')[1].split('```')[0]
elif '```' in python_code:
python_code = response.split('```')[1].split('```')[0]
return python_code
def extract_class_name_and_args_description(self, class_code):
"""
Extract class_name and args description from python code.
"""
class_name_pattern = r"class (\w+)"
class_name_match = re.search(class_name_pattern, class_code)
class_name = class_name_match.group(1) if class_name_match else None
# Extracting the __call__ method's docstring
call_method_docstring_pattern = r"def __call__\([^)]*\):\s+\"\"\"(.*?)\"\"\""
call_method_docstring_match = re.search(call_method_docstring_pattern, class_code, re.DOTALL)
args_description = call_method_docstring_match.group(1).strip() if call_method_docstring_match else None
return class_name, args_description
def extract_args_description(self, class_code):
"""
Extract args description from python code.
"""
# Extracting the __call__ method's docstring
call_method_docstring_pattern = r"def __call__\([^)]*\):\s+\"\"\"(.*?)\"\"\""
call_method_docstring_match = re.search(call_method_docstring_pattern, class_code, re.DOTALL)
args_description = call_method_docstring_match.group(1).strip() if call_method_docstring_match else None
return args_description
def extract_action_description(self, class_code):
"""
Extract action description from python code.
"""
# Extracting the __init__ method's description
init_pattern = r"def __init__\s*\(self[^)]*\):\s*(?:.|\n)*?self\._description\s*=\s*\"([^\"]+)\""
action_match = re.search(init_pattern, class_code, re.DOTALL)
action_description = action_match.group(1).strip() if action_match else None
return action_description
def save_str_to_path(self, content, path):
"""
save str content to the specified path.
"""
Path(path).parent.mkdir(parents=True, exist_ok=True)
with open(path, 'w', encoding='utf-8') as f:
lines = content.strip().splitlines()
content = '\n'.join(lines)
f.write(content)
def save_action_info_to_json(self, action, code, description):
"""
save action info to json.
"""
info = {
"task_name" : action,
"code": code,
"description": description
}
return info
def generate_call_api_format_message(self, tool_sub_task, tool_api_path, context="No context provided."):
self.sys_prompt = self.prompt['_SYSTEM_TOOL_USAGE_PROMPT'].format(
openapi_doc = json.dumps(self.generate_openapi_doc(tool_api_path)),
tool_sub_task = tool_sub_task,
context = context
)
self.user_prompt = self.prompt['_USER_TOOL_USAGE_PROMPT']
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
def generate_openapi_doc(self, tool_api_path):
"""
Format openapi document.
"""
# init current api's doc
curr_api_doc = {}
curr_api_doc["openapi"] = self.open_api_doc["openapi"]
curr_api_doc["info"] = self.open_api_doc["info"]
curr_api_doc["paths"] = {}
curr_api_doc["components"] = {"schemas":{}}
api_path_doc = {}
#extract path and schema
if tool_api_path not in self.open_api_doc["paths"]:
curr_api_doc = {"error": "The api is not existed"}
return curr_api_doc
api_path_doc = self.open_api_doc["paths"][tool_api_path]
curr_api_doc["paths"][tool_api_path] = api_path_doc
find_ptr = {}
if "get" in api_path_doc:
findptr = api_path_doc["get"]
elif "post" in api_path_doc:
findptr = api_path_doc["post"]
api_params_schema_ref = ""
# json格式
if (("requestBody" in findptr) and
("content" in findptr["requestBody"]) and
("application/json" in findptr["requestBody"]["content"]) and
("schema" in findptr["requestBody"]["content"]["application/json"]) and
("$ref" in findptr["requestBody"]["content"]["application/json"]["schema"])):
api_params_schema_ref = findptr["requestBody"]["content"]["application/json"]["schema"]["$ref"]
elif (("requestBody" in findptr) and
("content" in findptr["requestBody"]) and
("multipart/form-data" in findptr["requestBody"]["content"]) and
("schema" in findptr["requestBody"]["content"]["multipart/form-data"]) and
("allOf" in findptr["requestBody"]["content"]["multipart/form-data"]["schema"]) and
("$ref" in findptr["requestBody"]["content"]["multipart/form-data"]["schema"]["allOf"][0])):
api_params_schema_ref = findptr["requestBody"]["content"]["multipart/form-data"]["schema"]["allOf"][0]["$ref"]
if api_params_schema_ref != None and api_params_schema_ref != "":
curr_api_doc["components"]["schemas"][api_params_schema_ref.split('/')[-1]] = self.open_api_doc["components"]["schemas"][api_params_schema_ref.split('/')[-1]]
return curr_api_doc
def extract_API_Path(self, text):
"""
Extracts UNIX-style and Windows-style paths from the given string,
handling paths that may be enclosed in quotes.
:param s: The string from which to extract paths.
:return: A list of extracted paths.
"""
# Regular expression for UNIX-style and Windows-style paths
unix_path_pattern = r"/[^/\s]+(?:/[^/\s]*)*"
windows_path_pattern = r"[a-zA-Z]:\\(?:[^\\\/\s]+\\)*[^\\\/\s]+"
# Combine both patterns
pattern = f"({unix_path_pattern})|({windows_path_pattern})"
# Find all matches
matches = re.findall(pattern, text)
# Extract paths from the tuples returned by findall
paths = [match[0] or match[1] for match in matches]
# Remove enclosing quotes (single or double) from the paths
stripped_paths = [path.strip("'\"") for path in paths]
return stripped_paths[0]
if __name__ == '__main__':
agent = FridayAgent(config_path='../../examples/config.json', action_lib_dir="friday/action_lib")
print(agent.executor.extract_API_Path('''Use the "/tools/arxiv' API to search for the autogen paper and retrieve its summary.'''))

View File

@@ -1,100 +0,0 @@
from friday.action.get_os_version import get_os_version, check_os_version
import re
from friday.core.llms import OpenAI
_LINUX_SYSTEM_INVOKE_GENERATOR_PROMPT = '''
You are an AI trained to assist with Python programming tasks, with a focus on class and method usage.
Your goal is to generate a Python __call__ method invocation statement based on provided class names, task descriptions, and method parameter details.
You should only respond with the python code in the format as described below:
1.Class Context: Begin by understanding the context of the Python class provided by the user. This includes grasping the class name and its intended functionality.
2.Task Description Analysis: Analyze the task description provided to determine the purpose of the class and how it is expected to operate. This will help in identifying the correct method of the class to invoke.
3.Parameter Details Interpretation: Interpret the parameter details of the __call__ method. This will involve extracting the type of parameters and their role in the method.
4.Generating Invocation Statement: Construct the __call__ method invocation statement. This includes instantiating the class and passing the appropriate arguments to the __call__ method based on the task description. For example, if my class is called abc, and its __call__ method takes parameters 1 and 2, then my call statement could be abc()(1,2)
5.Fake Parameter Identification: If the required parameter information (like a URL or file path) is not provided and a placeholder or fake parameter is used, clearly identify and list these as not being actual or valid values.All the fake paramters you list should be separated by comma.If there are no fake parameters,you should give a None.
6.Output Format: The final output should include two parts:The first one is the invocation statement,which will be enclosed in <invoke></invoke> tags.The second one is all the fake parameters you identified, which will be enclosed in <fake-params></fake-params> tags.
And the response you write should also follow the following criteria:
Criteria:
1.The __call__ method invocation must be syntactically correct as per Python standards.
2.Clearly identify any fake or placeholder parameters used in the invocation.
3.Encourage generating a realistic and functional code snippet wherever possible.
4. If necessary, you can use the working directory provided by the user as a parameter passed into the __call__ method.
Now you will be provided with the following information, please generate your response according to these information:
'''
_LINUX_USER_INVOKE_GENERATOR_PROMPT = '''
User's Information:
Class Name: {class_name}
Task Description: {task_description}
__call__ Method Parameters: {args_description}
Working Directory: {working_dir}
'''
class LinuxInvokeGenerator():
def __init__(self, config_path=None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.system_version = get_os_version()
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
# Generate calls for the selected tool class
def invoke_generator(self, class_code, task_description,working_dir):
class_name, args_description = self.extract_class_name_and_args_description(class_code)
self.sys_prompt = _LINUX_SYSTEM_INVOKE_GENERATOR_PROMPT
self.user_prompt = _LINUX_USER_INVOKE_GENERATOR_PROMPT.format(
class_name = class_name,
task_description = task_description,
args_description = args_description,
working_dir = working_dir
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
# extract class_name and args description from python code
def extract_class_name_and_args_description(self, class_code):
"""
Extracts the class name and comments from the __call__ method of a given Python class code.
Specifically extracts the class name and the content between 'Args:' and 'Returns:' in the __call__ method.
Args:
class_code (str): The string representation of the Python class code.
Returns:
tuple: A tuple containing the class name and the extracted comments between 'Args:' and 'Returns:',
or None for each if not found.
"""
# # Extracting the class name
# class_name_pattern = re.compile(r'class\s+(\w+)')
# class_name_match = class_name_pattern.search(class_code)
# class_name = class_name_match.group(1) if class_name_match else None
# # Pattern to match __call__ method and its docstring
# call_method_pattern = re.compile(r'def __call__\s*\(self, .*?\):\s*"""(.*?)"""', re.DOTALL)
# call_method_match = call_method_pattern.search(class_code)
# if call_method_match:
# docstring = call_method_match.group(1)
# # Extracting the part between Args: and Returns:
# args_to_return_pattern = re.compile(r'Args:(.*?)Returns:', re.DOTALL)
# args_to_return_match = args_to_return_pattern.search(docstring)
# call_args_comments = args_to_return_match.group(1).strip() if args_to_return_match else None
# else:
# call_args_comments = None
# Extracting the class name
class_name_pattern = r"class (\w+)"
class_name_match = re.search(class_name_pattern, class_code)
class_name = class_name_match.group(1) if class_name_match else None
# Extracting the __call__ method's docstring
call_method_docstring_pattern = r"def __call__\([^)]*\):\s+\"\"\"(.*?)\"\"\""
call_method_docstring_match = re.search(call_method_docstring_pattern, class_code, re.DOTALL)
call_method_docstring = call_method_docstring_match.group(1).strip() if call_method_docstring_match else None
return class_name, call_method_docstring

View File

@@ -1,69 +0,0 @@
from friday.action.get_os_version import get_os_version, check_os_version
from friday.core.llms import OpenAI
_LINUX_SYSTEM_AMEND_PROMPT = '''
You are an AI expert in Python programming, with a focus on diagnosing and resolving code issues.
Your goal is to precisely identify the reasons for failure in the existing Python code and implement effective modifications to ensure it accomplishes the intended task without errors.
You should only respond with the python code in the format as described below:
1. Modified Code: Based on the error analysis, modify the original code to fix all the problems and give the final correct code to the user.
2. Error Analysis: Conduct a step-by-step analysis to identify why the code is generating errors or failing to complete the task. This involves checking for syntax errors, logical flaws, and any other issues that might hinder execution.
3. Detailed Explanation: Offer a clear and comprehensive explanation for each identified issue, detailing why these problems are occurring and how they are impacting the code's functionality.
And the code you write should also follow the following criteria:
1. You must keep the original code as formatted as possible, e.g. class names, methods, etc. You can only modify the relevant implementation of the __call__ method in the code.
2. Please avoid throwing exceptions in your modified code which may result in the execution of your code consistently reporting errors.You should instead handle the caught exceptions!
3. Some errors may be caused by unreasonable tasks by the user that result in something other than what is expected, e.g. the file to be created already exists, the parameters passed in are wrong, etc. You need to do some fault tolerance or exception handling for this to prevent it from reporting further errors.
4. Ensure the final code is syntactically correct, optimized for performance, and follows Python best practices.And the final code can only contain the class definition, the rest of the code about class instantiation and invocation must be commented out.
5. The python code should be surrounded by ```python and ```.
6. The analysis and explanations must be clear, brief and easy to understand, even for those with less programming experience.
7. All modifications must address the specific issues identified in the error analysis.
8. The solution must enable the code to successfully complete the intended task without errors.
Now you will be provided with the following information, please give your modified python code according to these information:
'''
_LINUX_USER_AMEND_PROMPT = '''
User's information are as follows:
Original Code: {original_code}
Task: {task}
Error Messages: {error}
Code Output: {code_output}
Current Working Directiory: {working_dir}
Files And Folders in Current Working Directiory: {files_and_folders}
Critique On The Code: {critique}
'''
class LinuxSkillAmend():
def __init__(self, config_path=None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.system_version = get_os_version()
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
# amend the code to fullfill the task.
def amend_code(self, original_code, task, error,code_output,working_dir,files_and_folders,critique):
self.sys_prompt = _LINUX_SYSTEM_AMEND_PROMPT
self.user_prompt = _LINUX_USER_AMEND_PROMPT.format(
original_code = original_code,
task = task,
error = error,
code_output = code_output,
working_dir = working_dir,
files_and_folders = files_and_folders,
critique = critique
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
def extract_python_code(self, response):
python_code = ""
if '```python' in response:
python_code = response.split('```python')[1].split('```')[0]
elif '```' in python_code:
python_code = response.split('```')[1].split('```')[0]
return python_code

View File

@@ -1,168 +0,0 @@
from friday.action.get_os_version import get_os_version, check_os_version
from friday.core.llms import OpenAI
from friday.agent.prompt import prompt_dict
import re
import json
class LinuxSkillCreateAgent():
"""
LinuxSkillCreateAgent is used to generate new skills in Linux environment and store them in the action_lib.
"""
def __init__(self, config_path=None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.system_version = get_os_version()
self.prompt = prompt_dict
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
# Send skill create message to LLM
def skill_create_format_message(self, task_name, task_description, working_dir):
self.sys_prompt = self.prompt['_LINUX_SYSTEM_SKILL_CREATE_PROMPT']
self.user_prompt = self.prompt['_LINUX_USER_SKILL_CREATE_PROMPT'].format(
system_version=self.system_version,
task_description=task_description,
working_dir=working_dir,
task_name=task_name
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
# Send invoke generate message to LLM
def invoke_generate_format_message(self, class_code, task_description,working_dir):
class_name, args_description = self.extract_class_name_and_args_description(class_code)
self.sys_prompt = self.prompt['_LINUX_SYSTEM_INVOKE_GENERATE_PROMPT']
self.user_prompt = self.prompt['_LINUX_USER_INVOKE_GENERATE_PROMPT'].format(
class_name = class_name,
task_description = task_description,
args_description = args_description,
working_dir = working_dir
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
# Send skill amend message to LLM
def skill_amend_format_message(self, original_code, task, error,code_output,working_dir,files_and_folders,critique):
self.sys_prompt = self.prompt['_LINUX_SYSTEM_SKILL_AMEND_PROMPT']
self.user_prompt = self.prompt['_LINUX_USER_SKILL_AMEND_PROMPT'].format(
original_code = original_code,
task = task,
error = error,
code_output = code_output,
working_dir = working_dir,
files_and_folders = files_and_folders,
critique = critique
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
# Send task judge prompt to LLM and get JSON response
def task_judge_format_message(self, current_code,task,code_output,working_dir,files_and_folders):
self.sys_prompt = self.prompt['_LINUX_SYSTEM_TASK_JUDGE_PROMPT']
self.user_prompt = self.prompt['_LINUX_TASK_JUDGE_PROMPT'].format(
current_code=current_code,
task=task,
code_output=code_output,
working_dir=working_dir,
files_and_folders= files_and_folders
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
response =self.llm.chat(self.message)
judge_json = '{' + '\n' + self.extract_information(response, '{', '}')[0] + '\n' + '}'
print("************************<judge_json>**************************")
print(judge_json)
print("************************</judge_json>*************************")
judge_json = json.loads(judge_json)
return judge_json
# Extract python code from response
def extract_python_code(self, response):
python_code = ""
if '```python' in response:
python_code = response.split('```python')[1].split('```')[0]
elif '```' in python_code:
python_code = response.split('```')[1].split('```')[0]
return python_code
# Extract class_name and args description from python code
def extract_class_name_and_args_description(self, class_code):
"""
Extracts the class name and comments from the __call__ method of a given Python class code.
Specifically extracts the class name and the content between 'Args:' and 'Returns:' in the __call__ method.
Args:
class_code (str): The string representation of the Python class code.
Returns:
tuple: A tuple containing the class name and the extracted comments between 'Args:' and 'Returns:',
or None for each if not found.
"""
class_name_pattern = r"class (\w+)"
class_name_match = re.search(class_name_pattern, class_code)
class_name = class_name_match.group(1) if class_name_match else None
# Extracting the __call__ method's docstring
call_method_docstring_pattern = r"def __call__\([^)]*\):\s+\"\"\"(.*?)\"\"\""
call_method_docstring_match = re.search(call_method_docstring_pattern, class_code, re.DOTALL)
args_description = call_method_docstring_match.group(1).strip() if call_method_docstring_match else None
return class_name, args_description
# Extract args description from python code
def extract_args_description(self, class_code):
# Extracting the __call__ method's docstring
call_method_docstring_pattern = r"def __call__\([^)]*\):\s+\"\"\"(.*?)\"\"\""
call_method_docstring_match = re.search(call_method_docstring_pattern, class_code, re.DOTALL)
args_description = call_method_docstring_match.group(1).strip() if call_method_docstring_match else None
return args_description
# Extract information from text
def extract_information(self, message, begin_str='[BEGIN]', end_str='[END]'):
result = []
_begin = message.find(begin_str)
_end = message.find(end_str)
while not (_begin == -1 or _end == -1):
result.append(message[_begin + len(begin_str):_end].strip())
message = message[_end + len(end_str):]
_begin = message.find(begin_str)
_end = message.find(end_str)
return result
# Extract args description and returns_description from python code
def extract_inputs_description_and_returns_description(self, class_code):
# Extracting the __call__ method's docstring
call_method_docstring_pattern = r"def __call__\([^)]*\):\s+\"\"\"(.*?)\"\"\""
call_method_docstring_match = re.search(call_method_docstring_pattern, class_code, re.DOTALL)
call_method_docstring = call_method_docstring_match.group(1).strip() if call_method_docstring_match else None
# 使用正则表达式提取 Args 部分
args_pattern = r"Args:\s*(.*?)\s*(Returns:|$)"
args_match = re.search(args_pattern, call_method_docstring, re.DOTALL)
args_description = args_match.group(1).strip() if args_match else None
# 使用正则表达式提取 Returns 部分
returns_pattern = r"Returns:\s*(.*)"
returns_match = re.search(returns_pattern, call_method_docstring, re.DOTALL)
returns_description = returns_match.group(1).strip() if returns_match else None
return args_description, returns_description
# Extract action description from python code
def extract_action_description(self, class_code):
# Extracting the __init__ method's description
init_pattern = r"def __init__\s*\(self[^)]*\):\s*(?:.|\n)*?self\._description\s*=\s*\"([^\"]+)\""
action_match = re.search(init_pattern, class_code, re.DOTALL)
action_description = action_match.group(1).strip() if action_match else None
return action_description

View File

@@ -1,71 +0,0 @@
from friday.action.get_os_version import get_os_version, check_os_version
from friday.core.llms import OpenAI
_LINUX_SYSTEM_PROMPT = '''
You are helpful assistant to assist in writing Python tool code for tasks completed on Linux operating systems. Your expertise lies in creating Python classes that perform specific tasks, adhering to a predefined format and structure.
Your goal is to generate Python tool code in the form of a class. The code should be structured to perform a user-specified task on a Linux operating system. The class must be easy to use and understand, with clear instructions and comments.
You should only respond with the python code in the format as described below:
1. Code Structure: Begin with the necessary import statement: from friday.action.base_action import BaseAction. Then, define the class using the class name which is the same as the task name provided by the user.
2. Parameter Handling: In the __init__ method, only initialize self._description with a brief description of the class's purpose, detailing what task it accomplishes.
3. Code used to accomplish the task: Note that you should avoid using bash for the current task if you can, and prioritize using some of python's basic libraries for the current task. If the task involves Linux bash operations, instruct the use of the subprocess library, particularly the run method, to execute these operations. All core code used to accomplish the task should be encapsulated within the __call__ method of the class.
4. Detailed Comments: Provide comprehensive comments throughout the code. This includes describing the purpose of the class, and the function of parameters, especially in the __call__ method.
And the code you write should also follow the following criteria:
1.The class must start with from friday.action.base_action import BaseAction.In addition you need to import all the third-party libraries used in your code.
2.The class name should be the same as the user's task name.
3.In the __init__ method, only self._description should be initialized.
4.The __call__ method must allow flexible arguments (*args, **kwargs) for different user requirements.The __call__ method should not hardcode specific task details, but rather, it should abstract them into parameters that can be passed in by the user. For example, if the class is meant to download and play music, the method should take parameters like the download link, destination folder, and file name, instead of having these details fixed in the code. Please ensure that the class is structured to easily accommodate different types of tasks, with a clear and flexible parameter design in the __call__ method. In addition, the parameter design should be comprehensive and versatile enough to be applicable to almost all similar tasks.
5.For tasks involving Linux bash commands, use the subprocess library to execute these commands within the Python class.
6.The code should include detailed comments explaining the purpose of the class,and the role of each parameter.
7. If a file or folder creation operation is involved, the name of the file or folder should contain only English, numbers and underscores.
8. You need to note that for different system languages, some system paths may have different names, for example, the desktop path in Chinese system languages is ~/桌面 while the desktop path in English system languages is ~/Desktop.
9. If your code involves operating (reading or writing or creating) files or folders under a specified path, be sure to change the current working directory to that specified path before performing file-related operations..
10. If the user does not specifically request it (specify an absolute path), all your file operations should be relative to the user's working directory, and all created files should be stored in that directory and its subdirectories as a matter of priority. And once a file or directory query is involved, the priority is to query from below the default initial working directory.
11. The working directory given by the user should not be hardcoded in your code, because different user can have different working directory at different time.
12. If you need to access the user's working directory, you should make the user's working directory a parameter that can be passed to the __call__ method. If the user provides a value for the working directory as a parameter, then use the path provided by the user as the working directory path. Otherwise, you can obtain it using methods like os.getcwd().
13. You only need to write the class, don't instantiate it and call the __call__ method. If you want to write an example of how to use the class, put the example in the comments.
Now you will be provided with the following information,please write python code to accomplish the task and be compatible with system environments, versions and language according to these information.
'''
_LINUX_USER_PROMPT ='''
User's information is as follows:
System Version: {system_version}
System language: simplified chinese
Working Directory: {working_dir}
Task Name: {task_name}
Task Description: {task_description}
'''
class LinuxSkillCreator():
"""
LinuxSkillCreator is used to generate new skills in Linux environment and store them in the action_lib.
"""
def __init__(self, config_path=None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.system_version = get_os_version()
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
# self.mac_systom_prompts =
def format_message(self, task_name,task_description,working_dir):
self.sys_prompt = _LINUX_SYSTEM_PROMPT
self.user_prompt = _LINUX_USER_PROMPT.format(
system_version=self.system_version,
task_description=task_description,
working_dir=working_dir,
task_name=task_name
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
def extract_python_code(self, response):
python_code = ""
if '```python' in response:
python_code = response.split('```python')[1].split('```')[0]
elif '```' in python_code:
python_code = response.split('```')[1].split('```')[0]
return python_code

View File

@@ -1,98 +0,0 @@
from friday.action.get_os_version import get_os_version, check_os_version
from friday.core.llms import OpenAI
import json
_LINUX_SYSTEM_Judger_PROMPT = '''
You are an AI programmed to verify Python code against a user's task requirements.
Your goal is to determine if the provided Python code accomplishes the user's specified task based on the feedback information.
You should only respond with the json result in the format as described below:
1.Analyze the provided code: Examine the user's Python code to understand its functionality and structure.
2.Compare the code with the task description: Align the objectives stated in the user's task description with the capabilities of the code.
3.Evaluate the feedback information: Review the user's feedback, including the output of the code and any file changes or directory states, to gauge the code's effectiveness.
4.Formulate a reasoning process: Synthesize the analysis, comparison, and evaluation to create a logical reasoning process about the code's effectiveness in achieving the task.
5.Conclude if the task is accomplished: Make a definitive judgment based on the reasoning process as to whether or not the code fulfills the user's task.
7.Output Format: You should only return me a json with no extra content. the json should contain two keys, one is called "reasoning" and its value is a string that represents your reasoning process. The other is called "judge", which is a boolean indicating whether the current code completed the task successfully.
And you should also follow the following criteria:
1.Ensure accurate understanding of the Python code.
2.Relate the code functionality to the user's task.
3.Assess the feedback information for evidence of task completion.
4.Provide clear, logical reasoning.
5.You need to note that the code I gave you is not reporting errors, I just don't know if it actually accomplishes the task or not.
6.Information about the current working directory and all the files and folders under it may imply whether the file was created successfully or not.
Now you will be provided with the following information, please give the result json according to these information:
'''
_LINUX_USER_Judger_PROMPT = '''
User's information are as follows:
Current Code: {current_code}
Task: {task}
Code Output: {code_output}
Current Working Directiory: {working_dir}
Files And Folders in Current Working Directiory: {files_and_folders}
'''
class LinuxTaskJudger():
def __init__(self, config_path=None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.system_version = get_os_version()
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
# amend the code to fullfill the task.
def judge(self, current_code,task,code_output,working_dir,files_and_folders):
self.sys_prompt = _LINUX_SYSTEM_Judger_PROMPT
self.user_prompt = _LINUX_USER_Judger_PROMPT.format(
current_code=current_code,
task=task,
code_output=code_output,
working_dir=working_dir,
files_and_folders= files_and_folders
)
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
response =self.llm.chat(self.message)
judge_json = json.loads(response)
return judge_json
# judger = LinuxTaskJudger(config_path="../../examples/config.json")
# current_code='''
# from friday.action.base_action import BaseAction
# import os
# class create_folder(BaseAction):
# def __init__(self):
# self._description = "Create a folder under the working directory"
# def __call__(self, *args, **kwargs):
# # Get the working directory
# working_dir = os.getcwd()
# # Create the folder path
# folder_name = "ss"
# folder_path = os.path.join(working_dir, folder_name)
# # Check if the folder already exists
# if os.path.exists(folder_path):
# print(f"The folder '{folder_name}' already exists.")
# else:
# # Create the folder
# os.makedirs(folder_path)
# print(f"The folder '{folder_name}' has been created under the working directory.")
# # Example usage
# # create_folder_action = create_folder()
# # create_folder_action()
# '''
# task="create a folder which is named test2 under the working directory"
# code_output =""
# working_dir ="/home/wengzhenmin/Projects/friday/working_dir"
# files_and_folders ="ss\n"
# res = judger.judge(current_code=current_code,code_output=code_output,task=task,working_dir=working_dir,files_and_folders=files_and_folders)
# print(res)
# print(res["judge"])

View File

@@ -1,177 +0,0 @@
import json
from friday.core.llms import OpenAI
from friday.agent.base_agent import BaseAgent
from friday.core.schema import EnvState
from friday.core.action_manager import ActionManager
from dotenv import load_dotenv
import os
load_dotenv()
MODEL_NAME = os.getenv('MODEL_NAME')
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
OPENAI_ORGANIZATION = os.getenv('OPENAI_ORGANIZATION')
a = "{action_input} the input to the action, could be any valid input for python programs or shell commands, such numbers, strings, or path to a file, etc."
BASE_PROMPT = """
{system_prompt}
{tool_description}
To use a tool, please use the following format:
```
{thought} to address the user request, thinking about what are the sub-goals you need to achieve and which tool is needed for each sub-goal?
{action} the tool names, each action name should be one of [{action_names}].
```
The response after utilizing tools should using the following format:
```
{response} To generate a response, you need to summarize your thoughts above and combined them with the tool execution results.
``
If you already know the answer, or you do not need to use tools,
please using the following format to reply:
```
{thought} the thought process to answer user questions
{response} respond to user request based on thought
```
Remember you must surround you action between <action> and </action>.
Now you are ready to take questions and requests from users.
"""
class OpenAIAgent(BaseAgent):
"""
BaseAgent is the base class of all agents.
"""
def __init__(self, config_path=None, action_lib_dir=None):
super().__init__()
self.llm = OpenAI(config_path)
self.action_lib = ActionManager(config_path=config_path, action_lib_dir=action_lib_dir)
# self.actions = None
self.max_iter = 3
self.system_prompt = """You are a personal assistant that aims to automate the workflow for human.\nYou are capable of understanding human intent and decompose it into several subgoals that can be addressed via language generation or acomplished using external tools.\nSome of the external tools you can use and their functionalities are as follows:
"""
self.action_names = self.action_lib.action_names
self.available_action_description = self.action_lib.descriptions
# todo: 添加工具检索模块
# self.available_action_description = ""
# for i, name in enumerate(self.action_names):
# self.available_action_description += "Tool {}: <action>{}</action>\n{}\n".format(i+1, name, self.action_lib_description[name])
def from_config(self, config_path=None):
self.llm = OpenAI(config_path)
def format_message(self, query):
self.prompt = BASE_PROMPT.format(
system_prompt=self.system_prompt,
tool_description=self.available_action_description,
action_names=self.action_names,
thought="Thought:",
action="Actions:",
action_input="Action Input:",
response="Response:"
)
self.message = [
{"role": "system", "content": self.prompt},
{"role": "user", "content": query},
]
return self.llm.chat(self.message)
# Extract action from text
def extract_action(self, message, begin_str='[BEGIN]', end_str='[END]'):
result = []
_begin = message.find(begin_str)
_end = message.find(end_str)
while not (_begin == -1 or _end == -1):
result.append(message[_begin + len(begin_str):_end].strip())
message = message[_end + len(end_str):]
_begin = message.find(begin_str)
_end = message.find(end_str)
return result
# Extract information from text
def extract_information(self, message, begin_str='[BEGIN]', end_str='[END]'):
result = []
_begin = message.find(begin_str)
_end = message.find(end_str)
while not (_begin == -1 or _end == -1):
result.append(message[_begin + len(begin_str):_end].strip())
message = message[_end + len(end_str):]
_begin = message.find(begin_str)
_end = message.find(end_str)
return result
# def extract_invoke(self, message, begin_str='[BEGIN]', end_str='[END]'):
# result = []
# _begin = message.find(begin_str)
# _end = message.find(end_str)
# while not (_begin == -1 or _end == -1):
# result.append(message[_begin + len(begin_str):_end].strip())
# message = message[_end + len(end_str):]
# _begin = message.find(begin_str)
# _end = message.find(end_str)
# return result
# # @dzc
# def extract_parameter(self, message, begin_str='[BEGIN]', end_str='[END]'):
# result = []
# _begin_parameter = message.find(begin_str)
# _end_parameter = message.find(end_str)
# # go through parameters
# while not (_begin_parameter == -1 or _end_parameter == -1):
# # get current task parameters
# parameter = message[_begin_parameter + len(begin_str):_end_parameter].strip()
# _begin_arg = parameter.find("<arg>")
# _end_arg = parameter.find("</arg>")
# args = []
# # go through args
# while not (_begin_arg == -1 or _end_arg == -1):
# arg = parameter[_begin_arg + len("<arg>"): _end_arg].strip()
# args.append(arg)
# parameter = parameter[_end_arg + len("</arg>"):].strip()
# _begin_arg = parameter.find("<arg>")
# _end_arg = parameter.find("</arg>")
# result.append(args)
# message = message[_end_parameter + len(end_str):]
# _begin_parameter = message.find(begin_str)
# _end_parameter = message.find(end_str)
# return result
def chat(self, goal: str):
self._history = []
def step(self, single_action) -> EnvState:
_command = self.action_lib[single_action]
self.environment.step(_command)
return self.environment.observe()
if __name__ == '__main__':
# actions = {
# "turn_on_dark_mode()": "Using turn_on_dark_mode() will change your system into the dark mode.",
# "play_study_music()": "Using play_study_music() will open Music in your Mac and play songs that are sutiable for study and work.",
# "create_meeting()": "Using create_meeting() will help user create a meeting event. When users request to create a meeting, don't ask questions such as meeting title and time, just invoke this tool by generating the action name.",
# "show_upcoming_meetings()": "Using show_upcoming_meetings() will open Calendar and show the their upcoming meetings for the user.",
# "organize_app_layout()": "Using organize_app_layout() will help user reorganize their Desktop layout for better working condition and focus more easily."
# }
# agent = OpenAIAgent(config_path="../../examples/config.json", environment=environment)
agent = OpenAIAgent(config_path="../../examples/config.json")
# print(agent.action_lib)
# print(agent.action_lib_description)
# executation_action = agent.action_lib["turn_on_dark_mode"]()
# executation_action.run()
# response = agent.format_message(query="I want to start working now. Please help set up the working environment for me.")
# print(agent.prompt)
# print(response['content'])
# response = '''
# Thought: To set up the working environment, we can focus on two sub-goals: turning on dark mode and organizing the app layout.
# Actions:
# 1. <action>turn_on_dark_mode</action>
# 2. <action>turn_on_light_mode</action>'''
# action = agent.extract_action(response, begin_str='<action>', end_str='</action>')
# import time
# for a in action:
# print(a)
# command = agent.action_lib[a]
# # print(agent.env.step(command))
# print(environment.step(command))
# time.sleep(2)

View File

@@ -1,73 +0,0 @@
from action.get_os_version import get_os_version, check_os_version
from friday.core.llms import OpenAI
_MAC_SYSTEM_PROMPT = '''
You are a helpful assistant that writes AppleScript code to complete any task specified by me.
System Version: {system_version}
Task: {task}
You should only respond in the format as described below:
import subprocess
def task_name():
# <task description>
applescript = f"""
<Code Completion>
"""
subprocess.run(["osascript", "-e", applescript])
task_name()
'''
_LINUX_SYSTEM_PROMPT = '''
You are a helpful assistant that writes Python code to complete any task specified by me.
I will give you the following informations:
System Version: {system_version}
Task: {task}
You should only respond in the format as described below:
from friday.action.base_action import BaseAction
# TODO: you should write a class in the following format, and the class name should be the same as the task name,besides,you can design the parameters of __call__ as you want.
class task_name(BaseAction):
def __init__(self) -> None:
super().__init__()
# self._description should be initialized as the description of the task
self._description = ""
# self.action_type should be initialized as the type of the task, which can be 'BASH' or 'PYTHON'
self.action_type = ''
def __call__(self, *args):
# TODO: write your code here
'''
class SkillCreator():
"""
SkillCreator is used to generate new skills and store them in the action_lib.
"""
def __init__(self, config_path=None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.system_version = get_os_version()
try:
check_os_version(self.system_version)
except ValueError as e:
print(e)
# self.mac_systom_prompts =
def format_message(self, task):
self.prompt = _MAC_SYSTEM_PROMPT.format(
system_version=self.system_version,
task=task
)
self.message = [
{"role": "system", "content": self.prompt},
{"role": "user", "content": task},
]
return self.llm.chat(self.message)

View File

@@ -1,120 +0,0 @@
from friday.core.llms import OpenAI
from friday.environment.py_env import PythonEnv
import json
'''
让大模型根据目标工具的API文档做网络请求获取到响应数据并返回
'''
TOOL_SYS_PROMPT='''
You are a useful AI assistant capable of accessing APIs to complete user-specified tasks, according to API documentation,
by using the provided ToolRequestUtil tool. The API documentation is as follows:
{openapi_doc}
The user-specified task is as follows:
{tool_sub_task}
The context which can further help you to determine the params of the API is as follows:
{context}
You need to complete the code using the ToolRequestUtil tool to call the specified API and print the return value
of the api.
ToolRequestUtil is a utility class, and the parameters of its 'request' method are described as follows:
def request(self, api_path, method, params=None, content_type=None):
"""
:param api_path: the path of the API
:param method: get/post
:param params: the parameters of the API, can be None
:param content_type: the content type of the API, e.g., application/json, can be None
:return: the response from the API
"""
Please begin your code completion:
'''
TOOL_USER_PROMPT='''
from friday.core.tool_request_util import ToolRequestUtil
tool_request_util = ToolRequestUtil()
# TODO: your code here
'''
class ToolAgent():
''' ToolAgent is used to call the tool api and get the result feedback '''
def __init__(self, config_path=None, open_api_doc_path = None) -> None:
super().__init__()
self.llm = OpenAI(config_path)
self.open_api_doc = {}
self.environment = PythonEnv()
with open(open_api_doc_path) as f:
self.open_api_doc = json.load(f)
# self.mac_systom_prompts =
def generate_call_api_code(self, tool_sub_task,tool_api_path,context="No context provided."):
self.sys_prompt = TOOL_SYS_PROMPT.format(
openapi_doc = json.dumps(self.generate_openapi_doc(tool_api_path)),
tool_sub_task = tool_sub_task,
context = context
)
self.user_prompt = TOOL_USER_PROMPT
self.message = [
{"role": "system", "content": self.sys_prompt},
{"role": "user", "content": self.user_prompt},
]
return self.llm.chat(self.message)
def generate_openapi_doc(self, tool_api_path):
# init current api's doc
curr_api_doc = {}
curr_api_doc["openapi"] = self.open_api_doc["openapi"]
curr_api_doc["info"] = self.open_api_doc["info"]
curr_api_doc["paths"] = {}
curr_api_doc["components"] = {"schemas":{}}
api_path_doc = {}
#extract path and schema
if tool_api_path not in self.open_api_doc["paths"]:
curr_api_doc = {"error": "The api is not existed"}
return curr_api_doc
api_path_doc = self.open_api_doc["paths"][tool_api_path]
curr_api_doc["paths"][tool_api_path] = api_path_doc
find_ptr = {}
if "get" in api_path_doc:
findptr = api_path_doc["get"]
elif "post" in api_path_doc:
findptr = api_path_doc["post"]
api_params_schema_ref = ""
# json格式
if (("requestBody" in findptr) and
("content" in findptr["requestBody"]) and
("application/json" in findptr["requestBody"]["content"]) and
("schema" in findptr["requestBody"]["content"]["application/json"]) and
("$ref" in findptr["requestBody"]["content"]["application/json"]["schema"])):
api_params_schema_ref = findptr["requestBody"]["content"]["application/json"]["schema"]["$ref"]
elif (("requestBody" in findptr) and
("content" in findptr["requestBody"]) and
("multipart/form-data" in findptr["requestBody"]["content"]) and
("schema" in findptr["requestBody"]["content"]["multipart/form-data"]) and
("allOf" in findptr["requestBody"]["content"]["multipart/form-data"]["schema"]) and
("$ref" in findptr["requestBody"]["content"]["multipart/form-data"]["schema"]["allOf"][0])):
api_params_schema_ref = findptr["requestBody"]["content"]["multipart/form-data"]["schema"]["allOf"][0]["$ref"]
if api_params_schema_ref != None and api_params_schema_ref != "":
curr_api_doc["components"]["schemas"][api_params_schema_ref.split('/')[-1]] = self.open_api_doc["components"]["schemas"][api_params_schema_ref.split('/')[-1]]
return curr_api_doc
def extract_python_code(self, response):
python_code = ""
if '```python' in response:
python_code = response.split('```python')[1].split('```')[0]
elif '```' in python_code:
python_code = response.split('```')[1].split('```')[0]
return python_code
def execute_code(self,code):
state = self.environment.step(code)
api_result = None
if(state.error != None and state.error != ""):
api_result = state.error
else:
api_result = state.result
return api_result
# agent = ToolAgent("../../examples/config.json","../core/openapi.json")
# res = agent.generate_openapi_doc("/tools/image_caption")
# print(res)
# code_text = agent.generate_call_api_code("use /tools/bing/searchv2 tool to search How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)? You can use the latest 2022 version of english wikipedia.","/tools/bing/searchv2")
# code = agent.extract_python_code(code_text)
# print(code)
# api_res = agent.execute_code(code)
# print(api_res)

View File

@@ -1,37 +0,0 @@
from fastapi import APIRouter
from pydantic import BaseModel
import arxiv
router = APIRouter()
class ArxivQuery(BaseModel):
query: str
top_k_results: int = 3
ARXIV_MAX_QUERY_LENGTH = 300
doc_content_chars_max: int = 4000
@router.get("/tools/arxiv")
async def get_arxiv_article_information(item: ArxivQuery):
'''Run Arxiv search and get the article meta information.
'''
try:
results = arxiv.Search( # type: ignore
item.query[: ARXIV_MAX_QUERY_LENGTH], max_results=top_k_results
).results()
except Exception as ex:
return {"result": None, "error": f"Arxiv exception: {ex}"}
docs = [
f"Published: {result.updated.date()}\nTitle: {result.title}\n"
f"Authors: {', '.join(a.name for a in result.authors)}\n"
f"Summary: {result.summary}"
for result in results
]
if docs:
return {"result": "\n\n".join(docs)[: doc_content_chars_max], "error": None}
else:
return {"result": None, "error": "No good Arxiv Result was found"}

View File

@@ -1,8 +0,0 @@
import requests
response = requests.get(
'http://43.159.144.130:8079/tools/arxiv',
json={'query': 'autogen'}
)
print(response.json())

View File

@@ -1,18 +0,0 @@
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from simpleeval import simple_eval, SimpleEval
router = APIRouter()
class Expression(BaseModel):
expression: str
@router.post("/tools/calculator")
def evaluate(expression: Expression):
try:
s = SimpleEval()
result = s.eval(expression.expression)
return {"result": str(result), "error": None}
except Exception as e:
return {"result": None, "error": str(e)}

View File

@@ -1,10 +0,0 @@
import requests
import json
# 测试加法
expression = "((46210 - 8*9068) / (2 - x))"
response = requests.post(
'http://127.0.0.1:8079/tools/calculator',
json={'expression': expression}
)
print(response.json())

View File

@@ -1,99 +0,0 @@
import random
from fastapi import APIRouter
from pydantic import BaseModel
from typing import List, Optional, Union
from .chemical_prop_api import ChemicalPropAPI
router = APIRouter()
class GetNameResponse(BaseModel):
"""name list"""
names: List[str]
class GetStructureResponse(BaseModel):
"""structure list"""
state: int
content: Optional[str] = None
class GetIDResponse(BaseModel):
state: int
content: Union[str, List[str]]
chemical_prop_api = ChemicalPropAPI
@router.get("/tools/chemical/get_name", response_model=GetNameResponse)
def get_name(cid: str):
"""prints the possible 3 synonyms of the queried compound ID"""
ans = chemical_prop_api.get_name_by_cid(cid, top_k=3)
return {
"names": ans
}
@router.get("/tools/chemical/get_allname", response_model=GetNameResponse)
def get_allname(cid: str):
"""prints all the possible synonyms (might be too many, use this function carefully).
"""
ans = chemical_prop_api.get_name_by_cid(cid)
return {
"names": ans
}
@router.get("/tools/chemical/get_id_by_struct", response_model=GetIDResponse)
def get_id_by_struct(smiles: str):
"""prints the ID of the queried compound SMILES. This should only be used if smiles is provided or retrieved in the previous step. The input should not be a string, but a SMILES formula.
"""
cids = chemical_prop_api.get_cid_by_struct(smiles)
if len(cids) == 0:
return {
"state": "no result"
}
else:
return {
"state": "matched",
"content": cids[0]
}
@router.get("/tools/chemical/get_id", response_model=GetIDResponse)
def get_id(name: str):
"""prints the ID of the queried compound name, and prints the possible 5 names if the queried name can not been precisely matched,
"""
cids = chemical_prop_api.get_cid_by_name(name)
if len(cids) > 0:
return {
"state": "precise",
"content": cids[0]
}
cids = chemical_prop_api.get_cid_by_name(name, name_type="word")
if len(cids) > 0:
if name in get_name(cids[0]):
return {
"state": "precise",
"content": cids[0]
}
ans = []
random.shuffle(cids)
for cid in cids[:5]:
nms = get_name(cid)
ans.append(nms)
return {
"state": "not precise",
"content": ans
}
@router.get("/tools/chemical/get_prop")
def get_prop(cid: str):
"""prints the properties of the queried compound ID
"""
return chemical_prop_api.get_prop_by_cid(cid)

View File

@@ -1,51 +0,0 @@
import json
from typing import Optional, List
import requests
from bs4 import BeautifulSoup
class ChemicalPropAPI:
def __init__(self) -> None:
self._endpoint = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/"
def get_name_by_cid(self, cid: str, top_k: Optional[int] = None) -> List[str]:
html_doc = requests.get(f"{self._endpoint}cid/{cid}/synonyms/XML").text
soup = BeautifulSoup(html_doc, "html.parser", from_encoding="utf-8")
syns = soup.find_all('synonym')
ans = []
if top_k is None:
top_k = len(syns)
for syn in syns[:top_k]:
ans.append(syn.text)
return ans
def get_cid_by_struct(self, smiles: str) -> List[str]:
html_doc = requests.get(f"{self._endpoint}smiles/{smiles}/cids/XML").text
soup = BeautifulSoup(html_doc, "html.parser", from_encoding="utf-8")
cids = soup.find_all('cid')
if cids is None:
return []
ans = []
for cid in cids:
ans.append(cid.text)
return ans
def get_cid_by_name(self, name: str, name_type: Optional[str] = None) -> List[str]:
url = f"{self._endpoint}name/{name}/cids/XML"
if name_type is not None:
url += f"?name_type={name_type}"
html_doc = requests.get(url).text
soup = BeautifulSoup(html_doc, "html.parser", from_encoding="utf-8")
cids = soup.find_all('cid')
if cids is None:
return []
ans = []
for cid in cids:
ans.append(cid.text)
return ans
def get_prop_by_cid(self, cid: str) -> str:
html_doc = requests.get(
f"{self._endpoint}cid/{cid}/property/MolecularFormula,MolecularWeight,CanonicalSMILES,IsomericSMILES,IUPACName,XLogP,ExactMass,MonoisotopicMass,TPSA,Complexity,Charge,HBondDonorCount,HBondAcceptorCount,RotatableBondCount,HeavyAtomCount,CovalentUnitCount/json").text
return json.loads(html_doc)['PropertyTable']['Properties'][0]

Some files were not shown because too many files have changed in this diff Show More