├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docgen.py ├── docs ├── CHANGELOG.md ├── CustomGraphicsView.html ├── Edge.html ├── ExecCommandDialog.html ├── MainWindow.html ├── MapView.html ├── Node.html ├── NodeData.html ├── NodeLayout.html ├── Tee.html ├── WorkFlow.html ├── backend.html ├── example │ ├── credentials.ini │ ├── example.json │ └── gpt4o-output │ │ ├── game_design_article.md │ │ └── seo_game_design_article.md ├── file_operations.html ├── frontend.html ├── index.html ├── project_README.md └── search.js ├── example ├── blog_writer.json ├── credentials.ini ├── customer_support.json ├── data_analysis.json ├── example.json └── gpt4o-output │ ├── game_design_article.md │ └── seo_game_design_article.md ├── frontend.webp └── src ├── AdditionalTools.py ├── CustomGraphicsView.py ├── Edge.py ├── ExecCommandDialog.py ├── KeyboardMouseTool.py ├── MainWindow.py ├── MapView.py ├── Node.py ├── NodeData.py ├── NodeLayout.py ├── Tee.py ├── WorkFlow.py ├── backend.py ├── file_operations.py ├── frontend.py ├── hook-pyside6.py └── setup-backend.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | /.vs 162 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.5.0] - 2024-09-05 9 | thx for hemangjoshi37a's work 10 | ### Features 11 | - doc gen 12 | - doc update 13 | - Additional Tools 14 | - more example graphs json 15 | ### Features 16 | - chatopenai 17 | 18 | ## [0.4.3] - 2024-06-01 19 | ### Feature 20 | - hierarchical with task after topological sort 21 | - red when cursor on item 22 | 23 | ### Fixed 24 | - cannot remove node 25 | - remove process, use default 26 | 27 | ## [0.3.2] - 2024-05-31 28 | ### Feature 29 | - Make space + left drag to right click drag 30 | 31 | ### Fixed 32 | - Fixed an issue with node read/write JSON causing duplicate node IDs. 33 | 34 | ## [0.2.0] - 2024-05-31 35 | ### Fixed 36 | - Fixed some syntax errors. 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 HomunMage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🤖 CrewAI-GUI 2 | 3 |
4 | 5 | *A Node-Based Frontend for CrewAI: Revolutionizing AI Workflow Creation* 6 | 7 | ![CrewAI-GUI Frontend](./frontend.webp) 8 | 9 | [![GitHub stars](https://img.shields.io/github/stars/LangGraph-GUI/CrewAI-GUI.svg?style=for-the-badge&logo=github&color=gold)](https://github.com/LangGraph-GUI/CrewAI-GUI) 10 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge)](https://opensource.org/licenses/MIT) 11 | [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/downloads/) 12 | 13 | [Features](#-features) • [Installation](#️-installation) • [Usage](#-usage) • [Build](#️-build) • [Documentation](#-documentation) • [Contributing](#-contributing) 14 | 15 |
16 | 17 | --- 18 | 19 | ## 🌟 Features 20 | 21 | CrewAI-GUI empowers you to create sophisticated AI workflows with ease: 22 | 23 | - 🖱️ **Intuitive Node-Based Interface**: Design complex AI agent interactions through a user-friendly drag-and-drop interface 24 | - 🔗 **JSON Export**: Seamlessly export your CrewAI designs to JSON, enhancing modularity and reusability 25 | - 🧠 **Flexible AI Backend**: Full support for both GPT-4 API and Ollama, catering to various AI needs 26 | - 💻 **Cross-Platform Compatibility**: Create AI workflows on Windows, Linux, or macOS with equal efficiency 27 | 28 | --- 29 | 30 | ## 🎥 Video Introduction 31 | 32 |
33 | 34 | [![CrewAI-GUI Introduction](https://img.youtube.com/vi/P5tkYJ-AgSc/0.jpg)](https://www.youtube.com/watch?v=P5tkYJ-AgSc) 35 | 36 | *Click to watch our comprehensive video guide* 37 | 38 |
39 | 40 | --- 41 | 42 | ## 🛠️ Installation 43 | 44 |
45 | Frontend GUI 46 | 47 | Install the required dependencies: 48 | ```bash 49 | pip install PySide6 50 | ``` 51 |
52 | 53 |
54 | Backend 55 | 56 | Install the necessary packages: 57 | 58 | For Linux: 59 | ```bash 60 | pip install 'crewai[tools]' langchain crewai networkx 61 | ``` 62 | 63 | For Windows: 64 | ```bash 65 | pip install crewai[tools] langchain crewai networkx 66 | ``` 67 |
68 | 69 | --- 70 | 71 | ## 🚀 Usage 72 | 73 |
74 | Frontend GUI 75 | 76 | Launch the CrewAI-GUI interface: 77 | ```bash 78 | python frontend.py 79 | ``` 80 | Create, manipulate, save, and load Directed Acyclic Graph (DAG) structures for CrewAI as JSON files. 81 |
82 | 83 |
84 | Backend 85 | 86 | Run the backend with different configurations: 87 | 88 | For GPT-4: 89 | ```bash 90 | python backend.py --graph example.json --keys credentials.ini --tee output.log 91 | ``` 92 | 93 | For Ollama (e.g., Mistral): 94 | ```bash 95 | python backend.py --graph example.json --llm mistral --tee output.log 96 | ``` 97 | The backend seamlessly converts JSON files into CrewAI tasks and agents. 98 |
99 | 100 | --- 101 | 102 | ## 🏗️ Build 103 | 104 |
105 | Frontend GUI 106 | 107 | Create a standalone executable with PyInstaller: 108 | ```bash 109 | pip install pyinstaller 110 | cd src 111 | pyinstaller --onefile --additional-hooks-dir=. frontend.py 112 | ``` 113 |
114 | 115 |
116 | Backend 117 | 118 | Package the backend with cx_Freeze: 119 | ```bash 120 | pip install cx_Freeze 121 | cd src 122 | python setup-backend.py build 123 | ``` 124 |
125 | 126 | --- 127 | 128 | ## 📚 Documentation 129 | 130 | Explore CrewAI-GUI in-depth with our comprehensive [GitHub Pages Documentation](https://LangGraph-GUI.github.io/CrewAI-GUI/). 131 | 132 | --- 133 | 134 | ## Learn CrewAI 135 | 136 | If you want see some example code for CrewAI, you can see [crewai examples](https://github.com/LangGraph-GUI/CrewAI-learn/tree/main/crewAI) 137 | 138 | --- 139 | 140 | ## 🧪 Examples 141 | 142 | Discover real-world applications of CrewAI-GUI in our [example graph source](https://github.com/LangGraph-GUI/CrewAI-learn/blob/main/crewAI/gpt/agents.py). 143 | 144 | --- 145 | 146 | ## ⚠️ Limitations 147 | 148 | - 🔒 The current version supports a limited set of node types and slots 149 | - 🚧 Some advanced CrewAI variables and features are planned for future releases 150 | 151 | --- 152 | 153 | ## 🤝 Contributing 154 | 155 | We welcome contributions to CrewAI-GUI! Please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on: 156 | - Submitting pull requests 157 | - Reporting issues 158 | - Requesting new features 159 | 160 | Join our community and help shape the future of AI workflow design! 161 | 162 | --- 163 | 164 | ## 📄 License 165 | 166 | CrewAI-GUI is open-source software, released under the MIT License. For full details, see the [LICENSE](LICENSE) file. 167 | 168 | --- 169 | 170 | ## 📬 Contact 171 | 172 | Have questions, suggestions, or want to collaborate? [Open an issue](https://github.com/LangGraph-GUI/CrewAI-GUI/issues) on our GitHub repository. 173 | 174 | --- 175 | 176 |
177 | 178 | Crafted with ❤️ by the `LangGraph-GUI` Team 179 | 180 | ## 👥 Contributors 181 | 182 |
183 | 184 | | [HomunMage](https://github.com/HomunMage) | [hemangjoshi37a](https://github.com/hemangjoshi37a) | 185 | |:-:|:-:| 186 | | [![HomunMage](https://github.com/HomunMage.png?size=100)](https://github.com/HomunMage) | [![hemangjoshi37a](https://github.com/hemangjoshi37a.png?size=100)](https://github.com/hemangjoshi37a) | 187 | 188 | 189 | 190 |
191 | 192 | [⬆ Back to Top](#-crewai-gui) 193 | 194 |
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /docgen.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import shutil 4 | import pdoc 5 | from pathlib import Path 6 | 7 | # Add the src directory to the Python path 8 | sys.path.insert(0, os.path.abspath('src')) 9 | 10 | def create_docs_folder(): 11 | if os.path.exists('docs'): 12 | shutil.rmtree('docs') 13 | os.makedirs('docs') 14 | 15 | def copy_non_python_files(src_dir, dest_dir): 16 | for root, dirs, files in os.walk(src_dir): 17 | for file in files: 18 | if not file.endswith('.py'): 19 | src_path = os.path.join(root, file) 20 | rel_path = os.path.relpath(src_path, src_dir) 21 | dest_path = os.path.join(dest_dir, rel_path) 22 | os.makedirs(os.path.dirname(dest_path), exist_ok=True) 23 | shutil.copy2(src_path, dest_path) 24 | 25 | def generate_documentation(): 26 | # Specify the modules to document 27 | modules = [ 28 | 'Tee', 29 | 'ExecCommandDialog', 30 | 'Edge', 31 | 'NodeLayout', 32 | 'CustomGraphicsView', 33 | 'MainWindow', 34 | 'WorkFlow', 35 | 'file_operations', 36 | 'MapView', 37 | 'NodeData', 38 | 'Node', 39 | 'frontend', 40 | 'backend' 41 | ] 42 | 43 | # Generate the documentation 44 | pdoc.pdoc(*modules, output_directory=Path('docs')) 45 | 46 | def generate_main_readme(): 47 | with open('docs/index.html', 'w') as f: 48 | f.write(""" 49 | 50 | 51 | 52 | 53 | 54 | CrewAI-GUI Documentation 55 | 63 | 64 | 65 |

CrewAI-GUI Documentation

66 |

This documentation provides an overview of the CrewAI-GUI project structure and components.

67 |

Project Structure

68 | 83 | 84 | 85 | """) 86 | 87 | # Main execution 88 | if __name__ == "__main__": 89 | create_docs_folder() 90 | generate_documentation() 91 | generate_main_readme() 92 | copy_non_python_files('src', 'docs/src') 93 | copy_non_python_files('example', 'docs/example') 94 | shutil.copy2('CHANGELOG.md', 'docs/CHANGELOG.md') 95 | shutil.copy2('README.md', 'docs/project_README.md') 96 | print("Enhanced HTML documentation generated successfully in the 'docs' folder.") -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.4.3] - 2024-06-01 9 | ### Feature 10 | - hierarchical with task after topological sort 11 | - red when cursor on item 12 | 13 | ### Fixed 14 | - cannot remove node 15 | - remove process, use default 16 | 17 | ## [0.3.2] - 2024-05-31 18 | ### Feature 19 | - Make space + left drag to right click drag 20 | 21 | ### Fixed 22 | - Fixed an issue with node read/write JSON causing duplicate node IDs. 23 | 24 | ## [0.2.0] - 2024-05-31 25 | ### Fixed 26 | - Fixed some syntax errors. 27 | -------------------------------------------------------------------------------- /docs/backend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | backend API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 49 |
50 |
51 |

52 | backend

53 | 54 | 55 | 56 | 57 | 58 | 59 |
 1# main.py
 60 |  2
 61 |  3import argparse
 62 |  4import os
 63 |  5import configparser
 64 |  6from WorkFlow import run_workflow_from_file
 65 |  7from langchain_community.llms import Ollama
 66 |  8from langchain.chat_models import ChatOpenAI
 67 |  9from Tee import Tee
 68 | 10
 69 | 11def main():
 70 | 12    parser = argparse.ArgumentParser(description="Run workflow from a JSON graph definition.")
 71 | 13    parser.add_argument('--graph', required=True, help="Path to the JSON file defining the graph.")
 72 | 14    parser.add_argument('--keys', help="Path to the credentials file.")
 73 | 15    parser.add_argument('--tee', help="File to write the output log to.")
 74 | 16    parser.add_argument('--llm', help="use what llm")
 75 | 17    
 76 | 18    args = parser.parse_args()
 77 | 19    
 78 | 20    if args.tee:
 79 | 21        tee = Tee(args.tee)
 80 | 22
 81 | 23    if args.keys:
 82 | 24        config = configparser.ConfigParser()
 83 | 25        config.read(args.keys)
 84 | 26        os.environ["OPENAI_API_KEY"] = config['OpenAI']['api_key']
 85 | 27        llm = ChatOpenAI(temperature=0.7, model_name="gpt-4o")
 86 | 28    elif args.llm:
 87 | 29        os.environ["OPENAI_API_KEY"] = "sk-proj-not-use-it"
 88 | 30        llm = Ollama(model=args.llm)
 89 | 31
 90 | 32    run_workflow_from_file(args.graph, llm)
 91 | 33
 92 | 34    if args.tee:
 93 | 35        tee.close()
 94 | 36
 95 | 37if __name__ == "__main__":
 96 | 38    main()
 97 | 
98 | 99 | 100 |
101 |
102 | 103 |
104 | 105 | def 106 | main(): 107 | 108 | 109 | 110 |
111 | 112 |
12def main():
113 | 13    parser = argparse.ArgumentParser(description="Run workflow from a JSON graph definition.")
114 | 14    parser.add_argument('--graph', required=True, help="Path to the JSON file defining the graph.")
115 | 15    parser.add_argument('--keys', help="Path to the credentials file.")
116 | 16    parser.add_argument('--tee', help="File to write the output log to.")
117 | 17    parser.add_argument('--llm', help="use what llm")
118 | 18    
119 | 19    args = parser.parse_args()
120 | 20    
121 | 21    if args.tee:
122 | 22        tee = Tee(args.tee)
123 | 23
124 | 24    if args.keys:
125 | 25        config = configparser.ConfigParser()
126 | 26        config.read(args.keys)
127 | 27        os.environ["OPENAI_API_KEY"] = config['OpenAI']['api_key']
128 | 28        llm = ChatOpenAI(temperature=0.7, model_name="gpt-4o")
129 | 29    elif args.llm:
130 | 30        os.environ["OPENAI_API_KEY"] = "sk-proj-not-use-it"
131 | 31        llm = Ollama(model=args.llm)
132 | 32
133 | 33    run_workflow_from_file(args.graph, llm)
134 | 34
135 | 35    if args.tee:
136 | 36        tee.close()
137 | 
138 | 139 | 140 | 141 | 142 |
143 |
144 | 326 | -------------------------------------------------------------------------------- /docs/example/credentials.ini: -------------------------------------------------------------------------------- 1 | [OpenAI] 2 | api_key = sk-proj- 3 | 4 | -------------------------------------------------------------------------------- /docs/example/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "type": "Start", 5 | "uniq_id": "uniq_id_1", 6 | "pos_x": -1250.0, 7 | "pos_y": -829.0, 8 | "width": 177.0, 9 | "height": 74.0, 10 | "name": "Node", 11 | "role": "", 12 | "goal": "", 13 | "backstory": "", 14 | "agent": "", 15 | "description": "", 16 | "expected_output": "", 17 | "tool": "", 18 | "arg": "", 19 | "output_var": "", 20 | "nexts": [ 21 | "uniq_id_4", 22 | "uniq_id_3" 23 | ], 24 | "prevs": [] 25 | }, 26 | { 27 | "type": "Start", 28 | "uniq_id": "uniq_id_2", 29 | "pos_x": -1179.0, 30 | "pos_y": 7.0, 31 | "width": 168.0, 32 | "height": 89.0, 33 | "name": "Node", 34 | "role": "", 35 | "goal": "", 36 | "backstory": "", 37 | "agent": "", 38 | "description": "", 39 | "expected_output": "", 40 | "tool": "", 41 | "arg": "", 42 | "output_var": "", 43 | "nexts": [], 44 | "prevs": [] 45 | }, 46 | { 47 | "type": "Task", 48 | "uniq_id": "uniq_id_3", 49 | "pos_x": -667.0, 50 | "pos_y": -988.0, 51 | "width": 269.0, 52 | "height": 346.0, 53 | "name": "Node", 54 | "role": "", 55 | "goal": "", 56 | "backstory": "", 57 | "agent": "researcher", 58 | "description": "Write and save an article about game design using the FileWriter tool.", 59 | "expected_output": "A file named 'game_design_article.md' with the article content more than 1000 words.", 60 | "tool": "", 61 | "arg": "", 62 | "output_var": "", 63 | "nexts": [ 64 | "uniq_id_6", 65 | "uniq_id_5" 66 | ], 67 | "prevs": [ 68 | "uniq_id_1" 69 | ] 70 | }, 71 | { 72 | "type": "Team", 73 | "uniq_id": "uniq_id_4", 74 | "pos_x": -1057.0, 75 | "pos_y": -570.0, 76 | "width": 194.0, 77 | "height": 136.0, 78 | "name": "Company", 79 | "role": "", 80 | "goal": "", 81 | "backstory": "", 82 | "agent": "", 83 | "description": "", 84 | "expected_output": "", 85 | "tool": "", 86 | "arg": "", 87 | "output_var": "", 88 | "nexts": [ 89 | "uniq_id_7", 90 | "uniq_id_8" 91 | ], 92 | "prevs": [ 93 | "uniq_id_1" 94 | ] 95 | }, 96 | { 97 | "type": "Step", 98 | "uniq_id": "uniq_id_5", 99 | "pos_x": -310.0, 100 | "pos_y": -595.0, 101 | "width": 219.0, 102 | "height": 340.0, 103 | "name": "Node", 104 | "role": "", 105 | "goal": "", 106 | "backstory": "", 107 | "agent": "", 108 | "description": "", 109 | "expected_output": "", 110 | "tool": "FileWriterTool()", 111 | "arg": "{'filename': 'game_design_article.md', 'content': 'Detailed content generated by LLM about game design.'}", 112 | "output_var": "", 113 | "nexts": [], 114 | "prevs": [ 115 | "uniq_id_3" 116 | ] 117 | }, 118 | { 119 | "type": "Task", 120 | "uniq_id": "uniq_id_6", 121 | "pos_x": -124.0, 122 | "pos_y": -984.0, 123 | "width": 224.0, 124 | "height": 350.0, 125 | "name": "Node", 126 | "role": "", 127 | "goal": "", 128 | "backstory": "", 129 | "agent": "seo_specialist", 130 | "description": "Read 'game_design_article.md' and convert it into an SEO-optimized article. Save it as 'seo_game_design_article.md'.", 131 | "expected_output": "define keywords. and out A file named 'seo_game_design_article.md' with the SEO-optimized content.", 132 | "tool": "", 133 | "arg": "", 134 | "output_var": "", 135 | "nexts": [ 136 | "uniq_id_9" 137 | ], 138 | "prevs": [ 139 | "uniq_id_3" 140 | ] 141 | }, 142 | { 143 | "type": "Agent", 144 | "uniq_id": "uniq_id_7", 145 | "pos_x": -817.0, 146 | "pos_y": -404.0, 147 | "width": 201.0, 148 | "height": 409.0, 149 | "name": "researcher", 150 | "role": "Knowledge Article Writer", 151 | "goal": "Create and save detailed content on professional domains to a file.", 152 | "backstory": "Passionate about crafting in-depth articles on Game Design.", 153 | "agent": "", 154 | "description": "", 155 | "expected_output": "", 156 | "tool": "", 157 | "arg": "", 158 | "output_var": "", 159 | "nexts": [], 160 | "prevs": [ 161 | "uniq_id_4" 162 | ] 163 | }, 164 | { 165 | "type": "Agent", 166 | "uniq_id": "uniq_id_8", 167 | "pos_x": -609.0, 168 | "pos_y": -463.0, 169 | "width": 201.0, 170 | "height": 397.0, 171 | "name": "seo_specialist", 172 | "role": "SEO Specialist", 173 | "goal": "Optimize content for search engines.", 174 | "backstory": "An expert in SEO, focused on enhancing content visibility on search engines.", 175 | "agent": "", 176 | "description": "", 177 | "expected_output": "", 178 | "tool": "", 179 | "arg": "", 180 | "output_var": "", 181 | "nexts": [], 182 | "prevs": [ 183 | "uniq_id_4" 184 | ] 185 | }, 186 | { 187 | "type": "Step", 188 | "uniq_id": "uniq_id_9", 189 | "pos_x": 158.0, 190 | "pos_y": -622.0, 191 | "width": 211.0, 192 | "height": 336.0, 193 | "name": "Node", 194 | "role": "", 195 | "goal": "", 196 | "backstory": "", 197 | "agent": "", 198 | "description": "", 199 | "expected_output": "", 200 | "tool": "FileReadTool()", 201 | "arg": "{'filename': 'game_design_article.md'},", 202 | "output_var": "'original_content'", 203 | "nexts": [ 204 | "uniq_id_10" 205 | ], 206 | "prevs": [ 207 | "uniq_id_6" 208 | ] 209 | }, 210 | { 211 | "type": "Step", 212 | "uniq_id": "uniq_id_10", 213 | "pos_x": 467.0, 214 | "pos_y": -616.0, 215 | "width": 206.0, 216 | "height": 348.0, 217 | "name": "Node", 218 | "role": "", 219 | "goal": "", 220 | "backstory": "", 221 | "agent": "", 222 | "description": "", 223 | "expected_output": "", 224 | "tool": "", 225 | "arg": "{'text': '{original_content}', 'task': 'Optimize this content for SEO'},", 226 | "output_var": "'seo_content'", 227 | "nexts": [ 228 | "uniq_id_11" 229 | ], 230 | "prevs": [ 231 | "uniq_id_9" 232 | ] 233 | }, 234 | { 235 | "type": "Step", 236 | "uniq_id": "uniq_id_11", 237 | "pos_x": 737.0, 238 | "pos_y": -615.0, 239 | "width": 205.0, 240 | "height": 352.0, 241 | "name": "Node", 242 | "role": "", 243 | "goal": "", 244 | "backstory": "", 245 | "agent": "", 246 | "description": "", 247 | "expected_output": "", 248 | "tool": "FileWriterTool()", 249 | "arg": "{'filename': 'seo_game_design_article.md', 'content': '{seo_content}'}", 250 | "output_var": "", 251 | "nexts": [], 252 | "prevs": [ 253 | "uniq_id_10" 254 | ] 255 | } 256 | ], 257 | "node_counter": 12 258 | } -------------------------------------------------------------------------------- /docs/example/gpt4o-output/game_design_article.md: -------------------------------------------------------------------------------- 1 | # The Art and Science of Game Design 2 | 3 | ## Introduction 4 | 5 | Game design is a multidisciplinary field that blends creativity with technical skills to create engaging and interactive experiences for players. It involves the design of game mechanics, storytelling, user interfaces, and more. Understanding the principles of game design is essential for anyone looking to break into the industry or improve their craft. 6 | 7 | ## Core Principles of Game Design 8 | 9 | ### 1. Gameplay and Mechanics 10 | 11 | The heart of any game is its gameplay and mechanics. These are the rules and systems that define how a game operates and how players interact with it. Good game mechanics are intuitive, balanced, and engaging. 12 | 13 | ### 2. Storytelling 14 | 15 | A compelling story can elevate a game from good to great. Storytelling in games involves narrative design, character development, and world-building. A well-told story can create emotional connections and keep players invested. 16 | 17 | ### 3. User Experience (UX) 18 | 19 | User experience is crucial in game design. It encompasses everything from the user interface (UI) to the overall feel of the game. Good UX design ensures that players can navigate and enjoy the game without frustration. 20 | 21 | ## Game Mechanics 22 | 23 | Game mechanics are the backbone of any game. They define the rules and interactions that take place within the game world. Some common types of game mechanics include: 24 | 25 | - **Progression Mechanics:** These involve the ways in which a player advances through a game, such as leveling up or unlocking new abilities. 26 | - **Combat Mechanics:** These define how battles and conflicts are resolved within the game. 27 | - **Puzzle Mechanics:** These involve problem-solving elements that challenge the player's thinking and creativity. 28 | 29 | ## The Role of Storytelling 30 | 31 | Storytelling in games is more than just writing a good plot. It involves creating a world that players want to explore and characters they care about. Effective storytelling can be achieved through various techniques, such as environmental storytelling, dialogue, and cutscenes. 32 | 33 | ### Environmental Storytelling 34 | 35 | This technique uses the game world itself to tell a story. Details in the environment, such as abandoned buildings or hidden notes, can provide context and background to the main narrative. 36 | 37 | ### Dialogue and Characters 38 | 39 | Well-written dialogue and compelling characters can drive a game's story forward. Characters should have depth and motivations that make them feel real and relatable. 40 | 41 | ## User Experience in Game Design 42 | 43 | User experience (UX) design is all about making the player's interaction with the game as smooth and enjoyable as possible. This includes designing intuitive controls, clear objectives, and a satisfying feedback system. 44 | 45 | ### Intuitive Controls 46 | 47 | Players should be able to pick up and play the game without needing extensive tutorials. Controls should be responsive and consistent. 48 | 49 | ### Clear Objectives 50 | 51 | Players need to understand what they are supposed to do in the game. Clear objectives and goals help keep players engaged and motivated. 52 | 53 | ### Feedback Systems 54 | 55 | Feedback systems, such as sound effects or visual cues, provide players with immediate responses to their actions. This helps to create a sense of immersion and satisfaction. 56 | 57 | ## Current Trends in Game Design 58 | 59 | The field of game design is constantly evolving. Some current trends include: 60 | 61 | - **Virtual Reality (VR) and Augmented Reality (AR):** These technologies are creating new opportunities for immersive gameplay experiences. 62 | - **Live Service Games:** Games that are continuously updated with new content to keep players engaged over time. 63 | - **Indie Games:** Independent game developers are creating innovative and unique experiences outside the traditional gaming industry. 64 | 65 | ## Conclusion 66 | 67 | Game design is a complex and rewarding field that combines art, science, and technology. By understanding the core principles and staying up-to-date with current trends, aspiring game designers can create engaging and memorable experiences for players. Whether you're interested in crafting compelling stories, designing intricate mechanics, or enhancing user experiences, there's a place for you in the world of game design. 68 | -------------------------------------------------------------------------------- /docs/example/gpt4o-output/seo_game_design_article.md: -------------------------------------------------------------------------------- 1 | # The Art and Science of Game Design 2 | 3 | ## Introduction 4 | 5 | Game design is a multidisciplinary field that blends creativity with technical skills to create engaging and interactive experiences for players. It involves the design of game mechanics, storytelling, user interfaces, and more. Understanding the principles of game design is essential for anyone looking to break into the game development industry or improve their craft in game design. 6 | 7 | ## Core Principles of Game Design 8 | 9 | ### 1. Gameplay and Mechanics 10 | 11 | The heart of any game is its gameplay and mechanics. These are the rules and systems that define how a game operates and how players interact with it. Good game mechanics are intuitive, balanced, and engaging, making them crucial for successful game design. 12 | 13 | ### 2. Storytelling in Games 14 | 15 | A compelling story can elevate a game from good to great. Storytelling in games involves narrative design, character development, and world-building. A well-told story can create emotional connections and keep players invested in the game. 16 | 17 | ### 3. User Experience (UX) in Games 18 | 19 | User experience is crucial in game design. It encompasses everything from the user interface (UI) to the overall feel of the game. Good UX design ensures that players can navigate and enjoy the game without frustration, enhancing their overall game experience. 20 | 21 | ## Game Mechanics 22 | 23 | Game mechanics are the backbone of any game. They define the rules and interactions that take place within the game world. Some common types of game mechanics include: 24 | 25 | - **Progression Mechanics:** These involve the ways in which a player advances through a game, such as leveling up or unlocking new abilities. 26 | - **Combat Mechanics:** These define how battles and conflicts are resolved within the game. 27 | - **Puzzle Mechanics:** These involve problem-solving elements that challenge the player's thinking and creativity. 28 | 29 | ## The Role of Storytelling in Games 30 | 31 | Storytelling in games is more than just writing a good plot. It involves creating a world that players want to explore and characters they care about. Effective storytelling can be achieved through various techniques, such as environmental storytelling, dialogue, and cutscenes. 32 | 33 | ### Environmental Storytelling 34 | 35 | This technique uses the game world itself to tell a story. Details in the environment, such as abandoned buildings or hidden notes, can provide context and background to the main narrative, enhancing the storytelling experience in games. 36 | 37 | ### Dialogue and Characters 38 | 39 | Well-written dialogue and compelling characters can drive a game's story forward. Characters should have depth and motivations that make them feel real and relatable, contributing to the game's storytelling. 40 | 41 | ## User Experience (UX) in Game Design 42 | 43 | User experience (UX) design is all about making the player's interaction with the game as smooth and enjoyable as possible. This includes designing intuitive controls, clear objectives, and a satisfying feedback system. 44 | 45 | ### Intuitive Controls 46 | 47 | Players should be able to pick up and play the game without needing extensive tutorials. Controls should be responsive and consistent to enhance the user experience in the game. 48 | 49 | ### Clear Objectives 50 | 51 | Players need to understand what they are supposed to do in the game. Clear objectives and goals help keep players engaged and motivated, contributing to a positive user experience. 52 | 53 | ### Feedback Systems 54 | 55 | Feedback systems, such as sound effects or visual cues, provide players with immediate responses to their actions. This helps to create a sense of immersion and satisfaction, enhancing the overall user experience in the game. 56 | 57 | ## Current Trends in Game Development 58 | 59 | The field of game design is constantly evolving. Some current trends in game development include: 60 | 61 | - **Virtual Reality (VR) and Augmented Reality (AR) in Games:** These technologies are creating new opportunities for immersive gameplay experiences. 62 | - **Live Service Games:** Games that are continuously updated with new content to keep players engaged over time. 63 | - **Indie Game Development:** Independent game developers are creating innovative and unique experiences outside the traditional gaming industry. 64 | 65 | ## Conclusion 66 | 67 | Game design is a complex and rewarding field that combines art, science, and technology. By understanding the core principles of game design and staying up-to-date with current trends in game development, aspiring game designers can create engaging and memorable experiences for players. Whether you're interested in crafting compelling stories, designing intricate mechanics, or enhancing user experiences, there's a place for you in the world of game design. 68 | -------------------------------------------------------------------------------- /docs/frontend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | frontend API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 49 |
50 |
51 |

52 | frontend

53 | 54 | 55 | 56 | 57 | 58 | 59 |
 1# main.py
 60 |  2
 61 |  3import sys
 62 |  4from PySide6.QtWidgets import QApplication
 63 |  5from PySide6.QtCore import QTimer  # Import QTimer
 64 |  6from MainWindow import MainWindow
 65 |  7
 66 |  8def initialize_main_window():
 67 |  9    window = MainWindow()
 68 | 10    window.setWindowTitle("Json Node Editor")
 69 | 11    window.setGeometry(100, 100, 800, 600)  # Set initial size to 800x600
 70 | 12    
 71 | 13    # Set up a timer to refresh the map view periodically
 72 | 14    window.timer = QTimer(window)
 73 | 15    window.timer.timeout.connect(window.view.update_map_view)
 74 | 16    window.timer.start(1000)  # Refresh every second
 75 | 17
 76 | 18    return window
 77 | 19
 78 | 20if __name__ == "__main__":
 79 | 21    app = QApplication(sys.argv)
 80 | 22    window = initialize_main_window()
 81 | 23    window.show()
 82 | 24    sys.exit(app.exec())
 83 | 
84 | 85 | 86 |
87 |
88 | 89 |
90 | 91 | def 92 | initialize_main_window(): 93 | 94 | 95 | 96 |
97 | 98 |
 9def initialize_main_window():
 99 | 10    window = MainWindow()
100 | 11    window.setWindowTitle("Json Node Editor")
101 | 12    window.setGeometry(100, 100, 800, 600)  # Set initial size to 800x600
102 | 13    
103 | 14    # Set up a timer to refresh the map view periodically
104 | 15    window.timer = QTimer(window)
105 | 16    window.timer.timeout.connect(window.view.update_map_view)
106 | 17    window.timer.start(1000)  # Refresh every second
107 | 18
108 | 19    return window
109 | 
110 | 111 | 112 | 113 | 114 |
115 |
116 | 298 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CrewAI-GUI Documentation 8 | 16 | 17 | 18 |

CrewAI-GUI Documentation

19 |

This documentation provides an overview of the CrewAI-GUI project structure and components.

20 |

Project Structure

21 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /docs/project_README.md: -------------------------------------------------------------------------------- 1 | # CrewAI-GUI 2 | crewai frontend gui. 3 | 4 | This is node based gui for crewai frontend. that will export to json for better decoupling. 5 | 6 | ![](./frontend.webp) 7 | 8 | ### video introduce 9 | [https://www.youtube.com/P5tkYJ-AgSc](https://youtu.be/P5tkYJ-AgSc) 10 | 11 | ### Limitation 12 | 13 | Current node types and slots are limited. 14 | 15 | Not all crewai var or features have imp the path. 16 | 17 | ## Environment 18 | 19 | ### front-end GUI 20 | ``` 21 | pip install PySide6 22 | ``` 23 | 24 | ### back-end 25 | 26 | * Linux 27 | ``` 28 | pip install 'crewai[tools]' langchain crewai networkx 29 | ``` 30 | 31 | * Windows 32 | without symbol 33 | ``` 34 | pip install crewai[tools] langchain crewai networkx 35 | ``` 36 | 37 | This is crewai's problem 38 | 39 | ## Usage 40 | 41 | ### front-end GUI 42 | 43 | run 44 | 45 | ``` 46 | python frontend.py 47 | ``` 48 | and you can read and write json file as DAG graph for crewai. 49 | 50 | ### back-end 51 | 52 | base on gpt4 api key or ollama 53 | 54 | if gpt4 run 55 | 56 | ``` 57 | python backend.py --graph example.json --keys credentials.ini --tee output.log 58 | ``` 59 | 60 | if local run such mistral 61 | 62 | ``` 63 | python backend.py --graph example.json --llm mistral --tee output.log 64 | ``` 65 | it will parse json file into crewai tasks and agents 66 | 67 | 68 | ## Build 69 | ### front-end GUI 70 | remember hook 71 | 72 | ``` 73 | pip install pyinstaller 74 | 75 | cd src 76 | pyinstaller --onefile --additional-hooks-dir=. frontend.py 77 | ``` 78 | ### back-end 79 | ``` 80 | pip install cx_Freeze 81 | cd src 82 | python .\setup-backend.py build 83 | ``` 84 | 85 | 86 | 87 | 88 | ## crewai example 89 | 90 | example graph source 91 | 92 | https://github.com/HomunMage/AI_Agents/blob/main/crewAI/gpt/agents.py 93 | -------------------------------------------------------------------------------- /example/blog_writer.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "type": "Step", 5 | "tools": [], 6 | "uniq_id": "editing_output", 7 | "pos_x": 546.0, 8 | "pos_y": 434.0, 9 | "width": 200, 10 | "height": 150, 11 | "name": "Save Final Post", 12 | "role": "", 13 | "goal": "", 14 | "backstory": "", 15 | "agent": "", 16 | "description": "", 17 | "expected_output": "", 18 | "tool": "FileWriterTool()", 19 | "arg": "{'filename': 'final_blog_post.md', 'content': 'Finalized blog post on The Future of AI in Healthcare'}", 20 | "output_var": "final_post", 21 | "nexts": [], 22 | "prevs": [ 23 | "editing_task" 24 | ] 25 | }, 26 | { 27 | "type": "Step", 28 | "tools": [], 29 | "uniq_id": "writing_output", 30 | "pos_x": 756.0, 31 | "pos_y": 21.0, 32 | "width": 200, 33 | "height": 150, 34 | "name": "Save Draft", 35 | "role": "", 36 | "goal": "", 37 | "backstory": "", 38 | "agent": "", 39 | "description": "", 40 | "expected_output": "", 41 | "tool": "FileWriterTool()", 42 | "arg": "{'filename': 'blog_post_draft.md', 'content': 'Draft of the blog post on The Future of AI in Healthcare'}", 43 | "output_var": "blog_draft", 44 | "nexts": [ 45 | "editing_task" 46 | ], 47 | "prevs": [ 48 | "writing_task" 49 | ] 50 | }, 51 | { 52 | "type": "Step", 53 | "tools": [], 54 | "uniq_id": "research_output", 55 | "pos_x": 550.0, 56 | "pos_y": -150.0, 57 | "width": 200, 58 | "height": 150, 59 | "name": "Save Research", 60 | "role": "", 61 | "goal": "", 62 | "backstory": "", 63 | "agent": "", 64 | "description": "", 65 | "expected_output": "", 66 | "tool": "FileWriterTool()", 67 | "arg": "{'filename': 'research_findings.md', 'content': 'Detailed research on The Future of AI in Healthcare'}", 68 | "output_var": "research_document", 69 | "nexts": [ 70 | "writing_task" 71 | ], 72 | "prevs": [ 73 | "research_task" 74 | ] 75 | }, 76 | { 77 | "type": "Task", 78 | "tools": [], 79 | "uniq_id": "editing_task", 80 | "pos_x": 243.0, 81 | "pos_y": 834.0, 82 | "width": 250, 83 | "height": 200, 84 | "name": "Edit and Finalize", 85 | "role": "", 86 | "goal": "", 87 | "backstory": "", 88 | "agent": "Editor", 89 | "description": "Review, edit, and finalize the blog post for publication", 90 | "expected_output": "A polished, publication-ready blog post", 91 | "tool": "", 92 | "arg": "", 93 | "output_var": "", 94 | "nexts": [ 95 | "editing_output" 96 | ], 97 | "prevs": [ 98 | "editor", 99 | "writing_output" 100 | ] 101 | }, 102 | { 103 | "type": "Task", 104 | "tools": [], 105 | "uniq_id": "writing_task", 106 | "pos_x": 244.0, 107 | "pos_y": 283.0, 108 | "width": 250, 109 | "height": 200, 110 | "name": "Write Blog Post", 111 | "role": "", 112 | "goal": "", 113 | "backstory": "", 114 | "agent": "Content Writer", 115 | "description": "Write a 1500-word blog post on 'The Future of AI in Healthcare' based on the research", 116 | "expected_output": "A well-structured 1500-word blog post draft", 117 | "tool": "", 118 | "arg": "", 119 | "output_var": "", 120 | "nexts": [ 121 | "writing_output" 122 | ], 123 | "prevs": [ 124 | "research_output", 125 | "writer" 126 | ] 127 | }, 128 | { 129 | "type": "Task", 130 | "tools": [], 131 | "uniq_id": "research_task", 132 | "pos_x": 227.0, 133 | "pos_y": -291.0, 134 | "width": 250, 135 | "height": 200, 136 | "name": "Conduct Research", 137 | "role": "", 138 | "goal": "", 139 | "backstory": "", 140 | "agent": "Research Specialist", 141 | "description": "Research the topic 'The Future of AI in Healthcare' and compile key findings", 142 | "expected_output": "A comprehensive research document with key points and sources", 143 | "tool": "", 144 | "arg": "", 145 | "output_var": "", 146 | "nexts": [ 147 | "research_output" 148 | ], 149 | "prevs": [ 150 | "researcher" 151 | ] 152 | }, 153 | { 154 | "type": "Agent", 155 | "tools": [], 156 | "uniq_id": "editor", 157 | "pos_x": -223.0, 158 | "pos_y": 406.0, 159 | "width": 200, 160 | "height": 150, 161 | "name": "Editor", 162 | "role": "Editor", 163 | "goal": "Refine and polish the content for publication", 164 | "backstory": "Detail-oriented editor with a keen eye for quality and consistency", 165 | "agent": "", 166 | "description": "", 167 | "expected_output": "", 168 | "tool": "", 169 | "arg": "", 170 | "output_var": "", 171 | "nexts": [ 172 | "editing_task" 173 | ], 174 | "prevs": [ 175 | "content_team" 176 | ] 177 | }, 178 | { 179 | "type": "Agent", 180 | "tools": [], 181 | "uniq_id": "writer", 182 | "pos_x": 11.0, 183 | "pos_y": 546.0, 184 | "width": 200, 185 | "height": 150, 186 | "name": "Content Writer", 187 | "role": "Content Writer", 188 | "goal": "Create engaging and informative content based on research", 189 | "backstory": "Skilled writer with experience in various niches and styles", 190 | "agent": "", 191 | "description": "", 192 | "expected_output": "", 193 | "tool": "", 194 | "arg": "", 195 | "output_var": "", 196 | "nexts": [ 197 | "writing_task" 198 | ], 199 | "prevs": [ 200 | "content_team" 201 | ] 202 | }, 203 | { 204 | "type": "Agent", 205 | "tools": [], 206 | "uniq_id": "researcher", 207 | "pos_x": -22.0, 208 | "pos_y": -158.0, 209 | "width": 200, 210 | "height": 150, 211 | "name": "Research Specialist", 212 | "role": "Research Specialist", 213 | "goal": "Gather comprehensive information on the given topic", 214 | "backstory": "Experienced researcher with a knack for finding reliable sources", 215 | "agent": "", 216 | "description": "", 217 | "expected_output": "", 218 | "tool": "", 219 | "arg": "", 220 | "output_var": "", 221 | "nexts": [ 222 | "research_task" 223 | ], 224 | "prevs": [ 225 | "content_team" 226 | ] 227 | }, 228 | { 229 | "type": "Team", 230 | "tools": [], 231 | "uniq_id": "content_team", 232 | "pos_x": -338.0, 233 | "pos_y": -3.0, 234 | "width": 150, 235 | "height": 100, 236 | "name": "Content Creation Team", 237 | "role": "", 238 | "goal": "", 239 | "backstory": "", 240 | "agent": "", 241 | "description": "", 242 | "expected_output": "", 243 | "tool": "", 244 | "arg": "", 245 | "output_var": "", 246 | "nexts": [ 247 | "researcher", 248 | "editor", 249 | "writer" 250 | ], 251 | "prevs": [ 252 | "start_node" 253 | ] 254 | }, 255 | { 256 | "type": "Start", 257 | "tools": [], 258 | "uniq_id": "start_node", 259 | "pos_x": -535.0, 260 | "pos_y": 29.0, 261 | "width": 100, 262 | "height": 50, 263 | "name": "Start", 264 | "role": "", 265 | "goal": "", 266 | "backstory": "", 267 | "agent": "", 268 | "description": "", 269 | "expected_output": "", 270 | "tool": "", 271 | "arg": "", 272 | "output_var": "", 273 | "nexts": [ 274 | "content_team" 275 | ], 276 | "prevs": [] 277 | } 278 | ], 279 | "node_counter": 12 280 | } -------------------------------------------------------------------------------- /example/credentials.ini: -------------------------------------------------------------------------------- 1 | [OpenAI] 2 | api_key = sk-proj- 3 | 4 | -------------------------------------------------------------------------------- /example/customer_support.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "type": "Start", 5 | "uniq_id": "start_node", 6 | "pos_x": -400, 7 | "pos_y": 0, 8 | "width": 100, 9 | "height": 50, 10 | "name": "Start", 11 | "nexts": ["support_team"], 12 | "prevs": [] 13 | }, 14 | { 15 | "type": "Team", 16 | "uniq_id": "support_team", 17 | "pos_x": -200, 18 | "pos_y": 0, 19 | "width": 150, 20 | "height": 100, 21 | "name": "Customer Support Team", 22 | "nexts": ["inquiry_classifier", "technical_support", "billing_support", "general_support"], 23 | "prevs": ["start_node"] 24 | }, 25 | { 26 | "type": "Agent", 27 | "uniq_id": "inquiry_classifier", 28 | "pos_x": 0, 29 | "pos_y": -225, 30 | "width": 200, 31 | "height": 150, 32 | "name": "Inquiry Classifier", 33 | "role": "Support Request Classifier", 34 | "goal": "Accurately classify customer inquiries by type", 35 | "backstory": "AI-powered classifier trained on thousands of customer support tickets", 36 | "nexts": ["classification_task"], 37 | "prevs": ["support_team"] 38 | }, 39 | { 40 | "type": "Agent", 41 | "uniq_id": "technical_support", 42 | "pos_x": 0, 43 | "pos_y": -75, 44 | "width": 200, 45 | "height": 150, 46 | "name": "Technical Support", 47 | "role": "Technical Support Specialist", 48 | "goal": "Resolve technical issues and provide guidance to customers", 49 | "backstory": "Experienced IT professional with deep knowledge of our products", 50 | "nexts": ["tech_support_task"], 51 | "prevs": ["support_team"] 52 | }, 53 | { 54 | "type": "Agent", 55 | "uniq_id": "billing_support", 56 | "pos_x": 0, 57 | "pos_y": 75, 58 | "width": 200, 59 | "height": 150, 60 | "name": "Billing Support", 61 | "role": "Billing and Account Specialist", 62 | "goal": "Address billing inquiries and resolve account-related issues", 63 | "backstory": "Finance expert with a customer-centric approach to problem-solving", 64 | "nexts": ["billing_support_task"], 65 | "prevs": ["support_team"] 66 | }, 67 | { 68 | "type": "Agent", 69 | "uniq_id": "general_support", 70 | "pos_x": 0, 71 | "pos_y": 225, 72 | "width": 200, 73 | "height": 150, 74 | "name": "General Support", 75 | "role": "General Customer Support Representative", 76 | "goal": "Handle a wide range of customer inquiries and provide satisfactory resolutions", 77 | "backstory": "Versatile support agent with excellent communication skills and broad product knowledge", 78 | "nexts": ["general_support_task"], 79 | "prevs": ["support_team"] 80 | }, 81 | { 82 | "type": "Task", 83 | "uniq_id": "classification_task", 84 | "pos_x": 250, 85 | "pos_y": -225, 86 | "width": 250, 87 | "height": 200, 88 | "name": "Classify Inquiry", 89 | "agent": "Inquiry Classifier", 90 | "description": "Analyze the customer inquiry and classify it as technical, billing, or general", 91 | "expected_output": "A classification label for the customer inquiry", 92 | "nexts": ["classification_output"], 93 | "prevs": ["inquiry_classifier"] 94 | }, 95 | { 96 | "type": "Task", 97 | "uniq_id": "tech_support_task", 98 | "pos_x": 250, 99 | "pos_y": -75, 100 | "width": 250, 101 | "height": 200, 102 | "name": "Resolve Technical Issue", 103 | "agent": "Technical Support", 104 | "description": "Address the technical issue described in the customer inquiry", 105 | "expected_output": "A detailed solution or troubleshooting steps for the technical issue", 106 | "nexts": ["tech_support_output"], 107 | "prevs": ["technical_support", "classification_output"] 108 | }, 109 | { 110 | "type": "Task", 111 | "uniq_id": "billing_support_task", 112 | "pos_x": 250, 113 | "pos_y": 75, 114 | "width": 250, 115 | "height": 200, 116 | "name": "Address Billing Inquiry", 117 | "agent": "Billing Support", 118 | "description": "Investigate and resolve the billing or account-related inquiry", 119 | "expected_output": "A clear explanation or resolution for the billing issue", 120 | "nexts": ["billing_support_output"], 121 | "prevs": ["billing_support", "classification_output"] 122 | }, 123 | { 124 | "type": "Task", 125 | "uniq_id": "general_support_task", 126 | "pos_x": 250, 127 | "pos_y": 225, 128 | "width": 250, 129 | "height": 200, 130 | "name": "Handle General Inquiry", 131 | "agent": "General Support", 132 | "description": "Respond to the general customer inquiry with appropriate information or assistance", 133 | "expected_output": "A helpful response addressing the customer's general inquiry", 134 | "nexts": ["general_support_output"], 135 | "prevs": ["general_support", "classification_output"] 136 | }, 137 | { 138 | "type": "Step", 139 | "uniq_id": "classification_output", 140 | "pos_x": 550, 141 | "pos_y": -225, 142 | "width": 200, 143 | "height": 150, 144 | "name": "Route Inquiry", 145 | "tool": "FileWriterTool()", 146 | "arg": "{'filename': 'inquiry_classification.json', 'content': 'Classification result for customer inquiry'}", 147 | "output_var": "inquiry_type", 148 | "nexts": ["tech_support_task", "billing_support_task", "general_support_task"], 149 | "prevs": ["classification_task"] 150 | }, 151 | { 152 | "type": "Step", 153 | "uniq_id": "tech_support_output", 154 | "pos_x": 550, 155 | "pos_y": -75, 156 | "width": 200, 157 | "height": 150, 158 | "name": "Save Technical Response", 159 | "tool": "FileWriterTool()", 160 | "arg": "{'filename': 'technical_response.md', 'content': 'Detailed technical solution or troubleshooting steps'}", 161 | "output_var": "tech_response", 162 | "nexts": ["generate_final_response"], 163 | "prevs": ["tech_support_task"] 164 | }, 165 | { 166 | "type": "Step", 167 | "uniq_id": "billing_support_output", 168 | "pos_x": 550, 169 | "pos_y": 75, 170 | "width": 200, 171 | "height": 150, 172 | "name": "Save Billing Response", 173 | "tool": "FileWriterTool()", 174 | "arg": "{'filename': 'billing_response.md', 'content': 'Explanation or resolution for the billing issue'}", 175 | "output_var": "billing_response", 176 | "nexts": ["generate_final_response"], 177 | "prevs": ["billing_support_task"] 178 | }, 179 | { 180 | "type": "Step", 181 | "uniq_id": "general_support_output", 182 | "pos_x": 550, 183 | "pos_y": 225, 184 | "width": 200, 185 | "height": 150, 186 | "name": "Save General Response", 187 | "tool": "FileWriterTool()", 188 | "arg": "{'filename': 'general_response.md', 'content': 'Response addressing the general customer inquiry'}", 189 | "output_var": "general_response", 190 | "nexts": ["generate_final_response"], 191 | "prevs": ["general_support_task"] 192 | }, 193 | { 194 | "type": "Task", 195 | "uniq_id": "generate_final_response", 196 | "pos_x": 800, 197 | "pos_y": 0, 198 | "width": 250, 199 | "height": 200, 200 | "name": "Generate Final Response", 201 | "agent": "General Support", 202 | "description": "Compile the specific response with a standard greeting and closing", 203 | "expected_output": "A complete, customer-ready response", 204 | "nexts": ["final_response_output"], 205 | "prevs": ["tech_support_output", "billing_support_output", "general_support_output"] 206 | }, 207 | { 208 | "type": "Step", 209 | "uniq_id": "final_response_output", 210 | "pos_x": 1100, 211 | "pos_y": 0, 212 | "width": 200, 213 | "height": 150, 214 | "name": "Save Final Response", 215 | "tool": "FileWriterTool()", 216 | "arg": "{'filename': 'final_customer_response.md', 'content': 'Complete response ready to be sent to the customer'}", 217 | "output_var": "final_response", 218 | "nexts": [], 219 | "prevs": ["generate_final_response"] 220 | } 221 | ], 222 | "node_counter": 18 223 | } -------------------------------------------------------------------------------- /example/data_analysis.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "type": "Start", 5 | "uniq_id": "start_node", 6 | "pos_x": -400, 7 | "pos_y": 0, 8 | "width": 100, 9 | "height": 50, 10 | "name": "Start", 11 | "nexts": ["analysis_team"], 12 | "prevs": [] 13 | }, 14 | { 15 | "type": "Team", 16 | "uniq_id": "analysis_team", 17 | "pos_x": -200, 18 | "pos_y": 0, 19 | "width": 150, 20 | "height": 100, 21 | "name": "Data Analysis Team", 22 | "nexts": ["data_collector", "data_preprocessor", "data_analyst", "data_visualizer"], 23 | "prevs": ["start_node"] 24 | }, 25 | { 26 | "type": "Agent", 27 | "uniq_id": "data_collector", 28 | "pos_x": 0, 29 | "pos_y": -225, 30 | "width": 200, 31 | "height": 150, 32 | "name": "Data Collector", 33 | "role": "Data Collection Specialist", 34 | "goal": "Gather relevant data from various sources", 35 | "backstory": "Expert in data acquisition with experience in multiple APIs and databases", 36 | "nexts": ["collection_task"], 37 | "prevs": ["analysis_team"] 38 | }, 39 | { 40 | "type": "Agent", 41 | "uniq_id": "data_preprocessor", 42 | "pos_x": 0, 43 | "pos_y": -75, 44 | "width": 200, 45 | "height": 150, 46 | "name": "Data Preprocessor", 47 | "role": "Data Cleaning Specialist", 48 | "goal": "Clean and prepare the data for analysis", 49 | "backstory": "Skilled in data cleaning techniques and familiar with various data formats", 50 | "nexts": ["preprocessing_task"], 51 | "prevs": ["analysis_team"] 52 | }, 53 | { 54 | "type": "Agent", 55 | "uniq_id": "data_analyst", 56 | "pos_x": 0, 57 | "pos_y": 75, 58 | "width": 200, 59 | "height": 150, 60 | "name": "Data Analyst", 61 | "role": "Statistical Analyst", 62 | "goal": "Perform in-depth analysis on the prepared data", 63 | "backstory": "Experienced data scientist with strong statistical and programming skills", 64 | "nexts": ["analysis_task"], 65 | "prevs": ["analysis_team"] 66 | }, 67 | { 68 | "type": "Agent", 69 | "uniq_id": "data_visualizer", 70 | "pos_x": 0, 71 | "pos_y": 225, 72 | "width": 200, 73 | "height": 150, 74 | "name": "Data Visualizer", 75 | "role": "Data Visualization Expert", 76 | "goal": "Create compelling visualizations from the analysis results", 77 | "backstory": "Creative designer with expertise in data visualization tools and techniques", 78 | "nexts": ["visualization_task"], 79 | "prevs": ["analysis_team"] 80 | }, 81 | { 82 | "type": "Task", 83 | "uniq_id": "collection_task", 84 | "pos_x": 250, 85 | "pos_y": -225, 86 | "width": 250, 87 | "height": 200, 88 | "name": "Collect Sales Data", 89 | "agent": "Data Collector", 90 | "description": "Collect monthly sales data for the past year from our e-commerce platform", 91 | "expected_output": "A CSV file containing monthly sales data", 92 | "nexts": ["collection_output"], 93 | "prevs": ["data_collector"] 94 | }, 95 | { 96 | "type": "Task", 97 | "uniq_id": "preprocessing_task", 98 | "pos_x": 250, 99 | "pos_y": -75, 100 | "width": 250, 101 | "height": 200, 102 | "name": "Preprocess Sales Data", 103 | "agent": "Data Preprocessor", 104 | "description": "Clean and normalize the collected sales data, handling missing values and outliers", 105 | "expected_output": "A cleaned and normalized dataset ready for analysis", 106 | "nexts": ["preprocessing_output"], 107 | "prevs": ["data_preprocessor", "collection_output"] 108 | }, 109 | { 110 | "type": "Task", 111 | "uniq_id": "analysis_task", 112 | "pos_x": 250, 113 | "pos_y": 75, 114 | "width": 250, 115 | "height": 200, 116 | "name": "Analyze Sales Trends", 117 | "agent": "Data Analyst", 118 | "description": "Perform time series analysis on the sales data to identify trends and patterns", 119 | "expected_output": "A report detailing sales trends, seasonality, and forecasts", 120 | "nexts": ["analysis_output"], 121 | "prevs": ["data_analyst", "preprocessing_output"] 122 | }, 123 | { 124 | "type": "Task", 125 | "uniq_id": "visualization_task", 126 | "pos_x": 250, 127 | "pos_y": 225, 128 | "width": 250, 129 | "height": 200, 130 | "name": "Create Visualizations", 131 | "agent": "Data Visualizer", 132 | "description": "Create interactive charts and graphs to visualize the sales trends and forecasts", 133 | "expected_output": "A set of interactive visualizations showcasing the analysis results", 134 | "nexts": ["visualization_output"], 135 | "prevs": ["data_visualizer", "analysis_output"] 136 | }, 137 | { 138 | "type": "Step", 139 | "uniq_id": "collection_output", 140 | "pos_x": 550, 141 | "pos_y": -225, 142 | "width": 200, 143 | "height": 150, 144 | "name": "Save Raw Data", 145 | "tool": "FileWriterTool()", 146 | "arg": "{'filename': 'raw_sales_data.csv', 'content': 'Monthly sales data for the past year'}", 147 | "output_var": "raw_data", 148 | "nexts": ["preprocessing_task"], 149 | "prevs": ["collection_task"] 150 | }, 151 | { 152 | "type": "Step", 153 | "uniq_id": "preprocessing_output", 154 | "pos_x": 550, 155 | "pos_y": -75, 156 | "width": 200, 157 | "height": 150, 158 | "name": "Save Cleaned Data", 159 | "tool": "FileWriterTool()", 160 | "arg": "{'filename': 'cleaned_sales_data.csv', 'content': 'Cleaned and normalized sales data'}", 161 | "output_var": "cleaned_data", 162 | "nexts": ["analysis_task"], 163 | "prevs": ["preprocessing_task"] 164 | }, 165 | { 166 | "type": "Step", 167 | "uniq_id": "analysis_output", 168 | "pos_x": 550, 169 | "pos_y": 75, 170 | "width": 200, 171 | "height": 150, 172 | "name": "Save Analysis Report", 173 | "tool": "FileWriterTool()", 174 | "arg": "{'filename': 'sales_analysis_report.md', 'content': 'Detailed report on sales trends and forecasts'}", 175 | "output_var": "analysis_report", 176 | "nexts": ["visualization_task"], 177 | "prevs": ["analysis_task"] 178 | }, 179 | { 180 | "type": "Step", 181 | "uniq_id": "visualization_output", 182 | "pos_x": 550, 183 | "pos_y": 225, 184 | "width": 200, 185 | "height": 150, 186 | "name": "Save Visualizations", 187 | "tool": "FileWriterTool()", 188 | "arg": "{'filename': 'sales_visualizations.html', 'content': 'Interactive charts and graphs of sales data'}", 189 | "output_var": "visualizations", 190 | "nexts": [], 191 | "prevs": ["visualization_task"] 192 | } 193 | ], 194 | "node_counter": 15 195 | } -------------------------------------------------------------------------------- /example/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "type": "Start", 5 | "uniq_id": "uniq_id_1", 6 | "pos_x": -1250.0, 7 | "pos_y": -829.0, 8 | "width": 177.0, 9 | "height": 74.0, 10 | "name": "Node", 11 | "role": "", 12 | "goal": "", 13 | "backstory": "", 14 | "agent": "", 15 | "description": "", 16 | "expected_output": "", 17 | "tool": "", 18 | "arg": "", 19 | "output_var": "", 20 | "nexts": [ 21 | "uniq_id_4", 22 | "uniq_id_3" 23 | ], 24 | "prevs": [] 25 | }, 26 | { 27 | "type": "Start", 28 | "uniq_id": "uniq_id_2", 29 | "pos_x": -1179.0, 30 | "pos_y": 7.0, 31 | "width": 168.0, 32 | "height": 89.0, 33 | "name": "Node", 34 | "role": "", 35 | "goal": "", 36 | "backstory": "", 37 | "agent": "", 38 | "description": "", 39 | "expected_output": "", 40 | "tool": "", 41 | "arg": "", 42 | "output_var": "", 43 | "nexts": [], 44 | "prevs": [] 45 | }, 46 | { 47 | "type": "Task", 48 | "uniq_id": "uniq_id_3", 49 | "pos_x": -667.0, 50 | "pos_y": -988.0, 51 | "width": 269.0, 52 | "height": 346.0, 53 | "name": "Node", 54 | "role": "", 55 | "goal": "", 56 | "backstory": "", 57 | "agent": "researcher", 58 | "description": "Write and save an article about game design using the FileWriter tool.", 59 | "expected_output": "A file named 'game_design_article.md' with the article content more than 1000 words.", 60 | "tool": "", 61 | "arg": "", 62 | "output_var": "", 63 | "nexts": [ 64 | "uniq_id_6", 65 | "uniq_id_5" 66 | ], 67 | "prevs": [ 68 | "uniq_id_1" 69 | ] 70 | }, 71 | { 72 | "type": "Team", 73 | "uniq_id": "uniq_id_4", 74 | "pos_x": -1057.0, 75 | "pos_y": -570.0, 76 | "width": 194.0, 77 | "height": 136.0, 78 | "name": "Company", 79 | "role": "", 80 | "goal": "", 81 | "backstory": "", 82 | "agent": "", 83 | "description": "", 84 | "expected_output": "", 85 | "tool": "", 86 | "arg": "", 87 | "output_var": "", 88 | "nexts": [ 89 | "uniq_id_7", 90 | "uniq_id_8" 91 | ], 92 | "prevs": [ 93 | "uniq_id_1" 94 | ] 95 | }, 96 | { 97 | "type": "Step", 98 | "uniq_id": "uniq_id_5", 99 | "pos_x": -310.0, 100 | "pos_y": -595.0, 101 | "width": 219.0, 102 | "height": 340.0, 103 | "name": "Node", 104 | "role": "", 105 | "goal": "", 106 | "backstory": "", 107 | "agent": "", 108 | "description": "", 109 | "expected_output": "", 110 | "tool": "FileWriterTool()", 111 | "arg": "{'filename': 'game_design_article.md', 'content': 'Detailed content generated by LLM about game design.'}", 112 | "output_var": "", 113 | "nexts": [], 114 | "prevs": [ 115 | "uniq_id_3" 116 | ] 117 | }, 118 | { 119 | "type": "Task", 120 | "uniq_id": "uniq_id_6", 121 | "pos_x": -124.0, 122 | "pos_y": -984.0, 123 | "width": 224.0, 124 | "height": 350.0, 125 | "name": "Node", 126 | "role": "", 127 | "goal": "", 128 | "backstory": "", 129 | "agent": "seo_specialist", 130 | "description": "Read 'game_design_article.md' and convert it into an SEO-optimized article. Save it as 'seo_game_design_article.md'.", 131 | "expected_output": "define keywords. and out A file named 'seo_game_design_article.md' with the SEO-optimized content.", 132 | "tool": "", 133 | "arg": "", 134 | "output_var": "", 135 | "nexts": [ 136 | "uniq_id_9" 137 | ], 138 | "prevs": [ 139 | "uniq_id_3" 140 | ] 141 | }, 142 | { 143 | "type": "Agent", 144 | "uniq_id": "uniq_id_7", 145 | "pos_x": -817.0, 146 | "pos_y": -404.0, 147 | "width": 201.0, 148 | "height": 409.0, 149 | "name": "researcher", 150 | "role": "Knowledge Article Writer", 151 | "goal": "Create and save detailed content on professional domains to a file.", 152 | "backstory": "Passionate about crafting in-depth articles on Game Design.", 153 | "agent": "", 154 | "description": "", 155 | "expected_output": "", 156 | "tool": "", 157 | "arg": "", 158 | "output_var": "", 159 | "nexts": [], 160 | "prevs": [ 161 | "uniq_id_4" 162 | ] 163 | }, 164 | { 165 | "type": "Agent", 166 | "uniq_id": "uniq_id_8", 167 | "pos_x": -609.0, 168 | "pos_y": -463.0, 169 | "width": 201.0, 170 | "height": 397.0, 171 | "name": "seo_specialist", 172 | "role": "SEO Specialist", 173 | "goal": "Optimize content for search engines.", 174 | "backstory": "An expert in SEO, focused on enhancing content visibility on search engines.", 175 | "agent": "", 176 | "description": "", 177 | "expected_output": "", 178 | "tool": "", 179 | "arg": "", 180 | "output_var": "", 181 | "nexts": [], 182 | "prevs": [ 183 | "uniq_id_4" 184 | ] 185 | }, 186 | { 187 | "type": "Step", 188 | "uniq_id": "uniq_id_9", 189 | "pos_x": 158.0, 190 | "pos_y": -622.0, 191 | "width": 211.0, 192 | "height": 336.0, 193 | "name": "Node", 194 | "role": "", 195 | "goal": "", 196 | "backstory": "", 197 | "agent": "", 198 | "description": "", 199 | "expected_output": "", 200 | "tool": "FileReadTool()", 201 | "arg": "{'filename': 'game_design_article.md'},", 202 | "output_var": "'original_content'", 203 | "nexts": [ 204 | "uniq_id_10" 205 | ], 206 | "prevs": [ 207 | "uniq_id_6" 208 | ] 209 | }, 210 | { 211 | "type": "Step", 212 | "uniq_id": "uniq_id_10", 213 | "pos_x": 467.0, 214 | "pos_y": -616.0, 215 | "width": 206.0, 216 | "height": 348.0, 217 | "name": "Node", 218 | "role": "", 219 | "goal": "", 220 | "backstory": "", 221 | "agent": "", 222 | "description": "", 223 | "expected_output": "", 224 | "tool": "", 225 | "arg": "{'text': '{original_content}', 'task': 'Optimize this content for SEO'},", 226 | "output_var": "'seo_content'", 227 | "nexts": [ 228 | "uniq_id_11" 229 | ], 230 | "prevs": [ 231 | "uniq_id_9" 232 | ] 233 | }, 234 | { 235 | "type": "Step", 236 | "uniq_id": "uniq_id_11", 237 | "pos_x": 737.0, 238 | "pos_y": -615.0, 239 | "width": 205.0, 240 | "height": 352.0, 241 | "name": "Node", 242 | "role": "", 243 | "goal": "", 244 | "backstory": "", 245 | "agent": "", 246 | "description": "", 247 | "expected_output": "", 248 | "tool": "FileWriterTool()", 249 | "arg": "{'filename': 'seo_game_design_article.md', 'content': '{seo_content}'}", 250 | "output_var": "", 251 | "nexts": [], 252 | "prevs": [ 253 | "uniq_id_10" 254 | ] 255 | } 256 | ], 257 | "node_counter": 12 258 | } -------------------------------------------------------------------------------- /example/gpt4o-output/game_design_article.md: -------------------------------------------------------------------------------- 1 | # The Art and Science of Game Design 2 | 3 | ## Introduction 4 | 5 | Game design is a multidisciplinary field that blends creativity with technical skills to create engaging and interactive experiences for players. It involves the design of game mechanics, storytelling, user interfaces, and more. Understanding the principles of game design is essential for anyone looking to break into the industry or improve their craft. 6 | 7 | ## Core Principles of Game Design 8 | 9 | ### 1. Gameplay and Mechanics 10 | 11 | The heart of any game is its gameplay and mechanics. These are the rules and systems that define how a game operates and how players interact with it. Good game mechanics are intuitive, balanced, and engaging. 12 | 13 | ### 2. Storytelling 14 | 15 | A compelling story can elevate a game from good to great. Storytelling in games involves narrative design, character development, and world-building. A well-told story can create emotional connections and keep players invested. 16 | 17 | ### 3. User Experience (UX) 18 | 19 | User experience is crucial in game design. It encompasses everything from the user interface (UI) to the overall feel of the game. Good UX design ensures that players can navigate and enjoy the game without frustration. 20 | 21 | ## Game Mechanics 22 | 23 | Game mechanics are the backbone of any game. They define the rules and interactions that take place within the game world. Some common types of game mechanics include: 24 | 25 | - **Progression Mechanics:** These involve the ways in which a player advances through a game, such as leveling up or unlocking new abilities. 26 | - **Combat Mechanics:** These define how battles and conflicts are resolved within the game. 27 | - **Puzzle Mechanics:** These involve problem-solving elements that challenge the player's thinking and creativity. 28 | 29 | ## The Role of Storytelling 30 | 31 | Storytelling in games is more than just writing a good plot. It involves creating a world that players want to explore and characters they care about. Effective storytelling can be achieved through various techniques, such as environmental storytelling, dialogue, and cutscenes. 32 | 33 | ### Environmental Storytelling 34 | 35 | This technique uses the game world itself to tell a story. Details in the environment, such as abandoned buildings or hidden notes, can provide context and background to the main narrative. 36 | 37 | ### Dialogue and Characters 38 | 39 | Well-written dialogue and compelling characters can drive a game's story forward. Characters should have depth and motivations that make them feel real and relatable. 40 | 41 | ## User Experience in Game Design 42 | 43 | User experience (UX) design is all about making the player's interaction with the game as smooth and enjoyable as possible. This includes designing intuitive controls, clear objectives, and a satisfying feedback system. 44 | 45 | ### Intuitive Controls 46 | 47 | Players should be able to pick up and play the game without needing extensive tutorials. Controls should be responsive and consistent. 48 | 49 | ### Clear Objectives 50 | 51 | Players need to understand what they are supposed to do in the game. Clear objectives and goals help keep players engaged and motivated. 52 | 53 | ### Feedback Systems 54 | 55 | Feedback systems, such as sound effects or visual cues, provide players with immediate responses to their actions. This helps to create a sense of immersion and satisfaction. 56 | 57 | ## Current Trends in Game Design 58 | 59 | The field of game design is constantly evolving. Some current trends include: 60 | 61 | - **Virtual Reality (VR) and Augmented Reality (AR):** These technologies are creating new opportunities for immersive gameplay experiences. 62 | - **Live Service Games:** Games that are continuously updated with new content to keep players engaged over time. 63 | - **Indie Games:** Independent game developers are creating innovative and unique experiences outside the traditional gaming industry. 64 | 65 | ## Conclusion 66 | 67 | Game design is a complex and rewarding field that combines art, science, and technology. By understanding the core principles and staying up-to-date with current trends, aspiring game designers can create engaging and memorable experiences for players. Whether you're interested in crafting compelling stories, designing intricate mechanics, or enhancing user experiences, there's a place for you in the world of game design. 68 | -------------------------------------------------------------------------------- /example/gpt4o-output/seo_game_design_article.md: -------------------------------------------------------------------------------- 1 | # The Art and Science of Game Design 2 | 3 | ## Introduction 4 | 5 | Game design is a multidisciplinary field that blends creativity with technical skills to create engaging and interactive experiences for players. It involves the design of game mechanics, storytelling, user interfaces, and more. Understanding the principles of game design is essential for anyone looking to break into the game development industry or improve their craft in game design. 6 | 7 | ## Core Principles of Game Design 8 | 9 | ### 1. Gameplay and Mechanics 10 | 11 | The heart of any game is its gameplay and mechanics. These are the rules and systems that define how a game operates and how players interact with it. Good game mechanics are intuitive, balanced, and engaging, making them crucial for successful game design. 12 | 13 | ### 2. Storytelling in Games 14 | 15 | A compelling story can elevate a game from good to great. Storytelling in games involves narrative design, character development, and world-building. A well-told story can create emotional connections and keep players invested in the game. 16 | 17 | ### 3. User Experience (UX) in Games 18 | 19 | User experience is crucial in game design. It encompasses everything from the user interface (UI) to the overall feel of the game. Good UX design ensures that players can navigate and enjoy the game without frustration, enhancing their overall game experience. 20 | 21 | ## Game Mechanics 22 | 23 | Game mechanics are the backbone of any game. They define the rules and interactions that take place within the game world. Some common types of game mechanics include: 24 | 25 | - **Progression Mechanics:** These involve the ways in which a player advances through a game, such as leveling up or unlocking new abilities. 26 | - **Combat Mechanics:** These define how battles and conflicts are resolved within the game. 27 | - **Puzzle Mechanics:** These involve problem-solving elements that challenge the player's thinking and creativity. 28 | 29 | ## The Role of Storytelling in Games 30 | 31 | Storytelling in games is more than just writing a good plot. It involves creating a world that players want to explore and characters they care about. Effective storytelling can be achieved through various techniques, such as environmental storytelling, dialogue, and cutscenes. 32 | 33 | ### Environmental Storytelling 34 | 35 | This technique uses the game world itself to tell a story. Details in the environment, such as abandoned buildings or hidden notes, can provide context and background to the main narrative, enhancing the storytelling experience in games. 36 | 37 | ### Dialogue and Characters 38 | 39 | Well-written dialogue and compelling characters can drive a game's story forward. Characters should have depth and motivations that make them feel real and relatable, contributing to the game's storytelling. 40 | 41 | ## User Experience (UX) in Game Design 42 | 43 | User experience (UX) design is all about making the player's interaction with the game as smooth and enjoyable as possible. This includes designing intuitive controls, clear objectives, and a satisfying feedback system. 44 | 45 | ### Intuitive Controls 46 | 47 | Players should be able to pick up and play the game without needing extensive tutorials. Controls should be responsive and consistent to enhance the user experience in the game. 48 | 49 | ### Clear Objectives 50 | 51 | Players need to understand what they are supposed to do in the game. Clear objectives and goals help keep players engaged and motivated, contributing to a positive user experience. 52 | 53 | ### Feedback Systems 54 | 55 | Feedback systems, such as sound effects or visual cues, provide players with immediate responses to their actions. This helps to create a sense of immersion and satisfaction, enhancing the overall user experience in the game. 56 | 57 | ## Current Trends in Game Development 58 | 59 | The field of game design is constantly evolving. Some current trends in game development include: 60 | 61 | - **Virtual Reality (VR) and Augmented Reality (AR) in Games:** These technologies are creating new opportunities for immersive gameplay experiences. 62 | - **Live Service Games:** Games that are continuously updated with new content to keep players engaged over time. 63 | - **Indie Game Development:** Independent game developers are creating innovative and unique experiences outside the traditional gaming industry. 64 | 65 | ## Conclusion 66 | 67 | Game design is a complex and rewarding field that combines art, science, and technology. By understanding the core principles of game design and staying up-to-date with current trends in game development, aspiring game designers can create engaging and memorable experiences for players. Whether you're interested in crafting compelling stories, designing intricate mechanics, or enhancing user experiences, there's a place for you in the world of game design. 68 | -------------------------------------------------------------------------------- /frontend.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LangGraph-GUI/CrewAI-GUI-Qt/463bb270ffea8d093472acab59910f147638d448/frontend.webp -------------------------------------------------------------------------------- /src/AdditionalTools.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | import subprocess 4 | from crewai_tools import BaseTool 5 | from typing import Optional 6 | 7 | class WebRequestTool(BaseTool): 8 | name: str = "WebRequestTool" 9 | description: str = "A tool for making web requests and fetching content from URLs." 10 | 11 | def _run(self, url: str, method: str = "GET", params: Optional[dict] = None) -> str: 12 | try: 13 | response = requests.request(method, url, params=params) 14 | response.raise_for_status() 15 | return response.text 16 | except requests.RequestException as e: 17 | return f"Error making request: {str(e)}" 18 | 19 | class FileOperationTool(BaseTool): 20 | name: str = "FileOperationTool" 21 | description: str = "A tool for reading from and writing to files." 22 | 23 | def _run(self, action: str, path: str, content: Optional[str] = None) -> str: 24 | if action == "read": 25 | try: 26 | with open(path, 'r') as file: 27 | return file.read() 28 | except IOError as e: 29 | return f"Error reading file: {str(e)}" 30 | elif action == "write": 31 | try: 32 | with open(path, 'w') as file: 33 | file.write(content) 34 | return f"Successfully wrote to {path}" 35 | except IOError as e: 36 | return f"Error writing to file: {str(e)}" 37 | else: 38 | return "Invalid action. Supported actions are 'read' and 'write'." 39 | 40 | class SystemCommandTool(BaseTool): 41 | name: str = "SystemCommandTool" 42 | description: str = "A tool for executing system commands." 43 | 44 | def _run(self, command: str) -> str: 45 | try: 46 | result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) 47 | return result.stdout 48 | except subprocess.CalledProcessError as e: 49 | return f"Error executing command: {str(e)}" -------------------------------------------------------------------------------- /src/CustomGraphicsView.py: -------------------------------------------------------------------------------- 1 | # CustomGraphicsView.py 2 | 3 | from PySide6.QtWidgets import QGraphicsView, QMenu, QGraphicsProxyWidget 4 | from PySide6.QtGui import QAction, QPixmap, QPainter 5 | from PySide6.QtCore import Qt, QPointF, QElapsedTimer 6 | from NodeData import NodeData 7 | from Node import Node 8 | from Edge import Edge 9 | from NodeLayout import NodeLayout # Make sure to import NodeLayout 10 | 11 | class CustomGraphicsView(QGraphicsView): 12 | def __init__(self, scene, main_window): 13 | super().__init__(scene) 14 | self.setDragMode(QGraphicsView.NoDrag) 15 | self._dragging = False 16 | self._last_mouse_pos = QPointF() 17 | self.main_window = main_window # Reference to the MainWindow 18 | self.timer = QElapsedTimer() # Initialize the timer 19 | self.right_clicked_item = None 20 | 21 | def mousePressEvent(self, event): 22 | if event.button() == Qt.RightButton: 23 | self._last_mouse_pos = event.pos() 24 | self.timer.start() # Start the timer 25 | 26 | # Check if the right-click is on an Edge or a Node 27 | item = self.itemAt(event.pos()) 28 | while item and not isinstance(item, (Node, Edge)) and isinstance(item, (QGraphicsProxyWidget, NodeLayout)): 29 | item = item.parentItem() 30 | 31 | if isinstance(item, Edge): 32 | self.right_clicked_item = item 33 | elif isinstance(item, Node): 34 | self.right_clicked_item = item 35 | else: 36 | self.right_clicked_item = None 37 | 38 | super().mousePressEvent(event) 39 | 40 | def mouseMoveEvent(self, event): 41 | if event.buttons() & Qt.RightButton: 42 | delta = event.pos() - self._last_mouse_pos 43 | self._last_mouse_pos = event.pos() 44 | self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - delta.x()) 45 | self.verticalScrollBar().setValue(self.verticalScrollBar().value() - delta.y()) 46 | self._dragging = True 47 | else: 48 | super().mouseMoveEvent(event) 49 | 50 | def mouseReleaseEvent(self, event): 51 | if event.button() == Qt.RightButton: 52 | elapsed_time = self.timer.elapsed() # Get the elapsed time 53 | if elapsed_time < 170: # If the right-click was shorter than 170 ms 54 | self.show_context_menu(event.pos()) 55 | self._dragging = False 56 | else: 57 | super().mouseReleaseEvent(event) 58 | 59 | def add_node(self, position): 60 | unique_id = f"uniq_id_{self.scene().node_counter}" 61 | node_data = NodeData(name="Node", uniq_id=unique_id) 62 | node = Node(node_data) 63 | self.scene().addItem(node) 64 | node.setPos(position) # Set the node position to the right-click position 65 | self.scene().node_counter += 1 # Increment the counter 66 | self.update_map_view() 67 | 68 | def remove_node(self): 69 | if self.right_clicked_item and isinstance(self.right_clicked_item, Node): 70 | self.right_clicked_item.remove_node() 71 | self.right_clicked_item = None 72 | 73 | def remove_edge(self): 74 | if self.right_clicked_item and isinstance(self.right_clicked_item, Edge): 75 | self.right_clicked_item.remove() 76 | self.right_clicked_item = None 77 | 78 | def update_map_view(self): 79 | rect = self.scene().sceneRect() 80 | pixmap = QPixmap(int(rect.width()), int(rect.height())) 81 | pixmap.fill(Qt.transparent) 82 | painter = QPainter(pixmap) 83 | self.scene().render(painter) 84 | painter.end() 85 | self.main_window.map_view.update_map(pixmap) # Update the map view in the MainWindow 86 | 87 | def show_context_menu(self, position): 88 | self.right_click_position = self.mapToScene(position) 89 | context_menu = QMenu(self) 90 | 91 | if isinstance(self.right_clicked_item, Edge): 92 | remove_edge_action = QAction("Remove Edge", self) 93 | remove_edge_action.triggered.connect(self.remove_edge) 94 | context_menu.addAction(remove_edge_action) 95 | elif isinstance(self.right_clicked_item, Node): 96 | remove_node_action = QAction("Remove Node", self) 97 | remove_node_action.triggered.connect(self.remove_node) 98 | context_menu.addAction(remove_node_action) 99 | else: 100 | add_node_action = QAction("Add Node", self) 101 | add_node_action.triggered.connect(lambda: self.add_node(self.right_click_position)) 102 | context_menu.addAction(add_node_action) 103 | 104 | context_menu.exec(self.mapToGlobal(position)) 105 | -------------------------------------------------------------------------------- /src/Edge.py: -------------------------------------------------------------------------------- 1 | # Edge.py 2 | 3 | from PySide6.QtWidgets import QGraphicsPathItem 4 | from PySide6.QtCore import Qt, QPointF 5 | from PySide6.QtGui import QPen, QPainterPath 6 | 7 | class Edge(QGraphicsPathItem): 8 | def __init__(self, source_port): 9 | super().__init__() 10 | self.source_port = source_port 11 | self.source_id = source_port.parentItem().data.uniq_id 12 | self.setPen(QPen(Qt.black, 2)) 13 | self.setZValue(-1) 14 | self.destination_port = None 15 | self.destination_id = None 16 | self.update_position() 17 | self.setAcceptHoverEvents(True) 18 | 19 | def update_position(self, end_point=None): 20 | path = QPainterPath() 21 | start_point = self.source_port.scenePos() 22 | if end_point: 23 | control_point_1 = QPointF(start_point.x() + 50, start_point.y()) 24 | control_point_2 = QPointF(end_point.x() - 50, end_point.y()) 25 | path.moveTo(start_point) 26 | path.cubicTo(control_point_1, control_point_2, end_point) 27 | elif self.destination_port: 28 | end_point = self.destination_port.scenePos() 29 | control_point_1 = QPointF(start_point.x() + 50, start_point.y()) 30 | control_point_2 = QPointF(end_point.x() - 50, end_point.y()) 31 | path.moveTo(start_point) 32 | path.cubicTo(control_point_1, control_point_2, end_point) 33 | else: 34 | path.moveTo(start_point) 35 | path.cubicTo(start_point, start_point, start_point) 36 | self.setPath(path) 37 | 38 | def set_destination(self, destination_port): 39 | self.destination_port = destination_port 40 | self.destination_id = destination_port.parentItem().data.uniq_id 41 | self.update_position() 42 | source_node = self.source_port.parentItem().data 43 | dest_node = self.destination_port.parentItem().data 44 | source_node.nexts.append(self.destination_id) 45 | dest_node.prevs.append(self.source_id) # Add to prevs 46 | 47 | def remove(self): 48 | if self in self.source_port.edges: 49 | self.source_port.edges.remove(self) 50 | if self in self.destination_port.edges: 51 | self.destination_port.edges.remove(self) 52 | if self.destination_id in self.source_port.parentItem().data.nexts: 53 | self.source_port.parentItem().data.nexts.remove(self.destination_id) 54 | if self.source_id in self.destination_port.parentItem().data.prevs: 55 | self.destination_port.parentItem().data.prevs.remove(self.source_id) # Remove from prevs 56 | self.scene().removeItem(self) 57 | 58 | def hoverEnterEvent(self, event): 59 | self.setPen(QPen(Qt.red, 2)) # Change color on hover 60 | self.setCursor(Qt.PointingHandCursor) 61 | super().hoverEnterEvent(event) 62 | 63 | def hoverLeaveEvent(self, event): 64 | self.setPen(QPen(Qt.black, 2)) # Revert color on hover exit 65 | self.unsetCursor() 66 | super().hoverLeaveEvent(event) 67 | -------------------------------------------------------------------------------- /src/ExecCommandDialog.py: -------------------------------------------------------------------------------- 1 | from PySide6.QtWidgets import QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QCheckBox, QMessageBox, QComboBox 2 | from PySide6.QtCore import Qt, QProcess 3 | import sys 4 | 5 | class ExecCommandDialog(QDialog): 6 | def __init__(self, parent=None): 7 | super().__init__(parent) 8 | self.setWindowTitle("Execute Command") 9 | self.setModal(True) 10 | self.setMinimumSize(400, 400) 11 | 12 | print("Initializing ExecCommandDialog") # Debug print 13 | 14 | self.layout = QVBoxLayout() 15 | 16 | self.backend_input = QLineEdit(self) 17 | self.backend_input.setPlaceholderText("Enter the backend executable") 18 | self.backend_input.setText("backend.exe") 19 | 20 | self.graph_file_input = QLineEdit(self) 21 | self.graph_file_input.setPlaceholderText("Enter the graph file") 22 | self.graph_file_input.setText("example.json") 23 | 24 | self.keys_choice = QComboBox(self) 25 | self.keys_choice.addItem("GPT4o") 26 | self.keys_choice.addItem("Ollama") 27 | self.keys_choice.currentIndexChanged.connect(self.update_ui) 28 | 29 | self.keys_input = QLineEdit(self) 30 | self.keys_input.setPlaceholderText("Enter the keys file") 31 | self.keys_input.setText("credentials.ini") 32 | 33 | self.llm_input = QLineEdit(self) 34 | self.llm_input.setPlaceholderText("Enter the LLM model") 35 | self.llm_input.setText("phi3") 36 | self.llm_input.hide() # Hide LLM input by default 37 | 38 | self.log_to_file_checkbox = QCheckBox("Log to file", self) 39 | self.log_to_file_checkbox.setChecked(True) 40 | 41 | self.run_button = QPushButton("Run", self) 42 | self.run_button.clicked.connect(self.run_command) 43 | 44 | self.output_text = QTextEdit(self) 45 | self.output_text.setReadOnly(True) 46 | 47 | self.layout.addWidget(QLabel("Backend:")) 48 | self.layout.addWidget(self.backend_input) 49 | self.layout.addWidget(QLabel("Graph File:")) 50 | self.layout.addWidget(self.graph_file_input) 51 | self.layout.addWidget(QLabel("Key Files:")) 52 | self.layout.addWidget(self.keys_choice) 53 | self.layout.addWidget(self.keys_input) 54 | self.layout.addWidget(self.llm_input) 55 | self.layout.addWidget(self.log_to_file_checkbox) 56 | self.layout.addWidget(self.run_button) 57 | self.layout.addWidget(QLabel("Output:")) 58 | self.layout.addWidget(self.output_text) 59 | 60 | self.setLayout(self.layout) 61 | 62 | self.process = QProcess(self) 63 | self.process.readyReadStandardOutput.connect(self.read_stdout) 64 | self.process.readyReadStandardError.connect(self.read_stderr) 65 | self.process.finished.connect(self.process_finished) 66 | 67 | print("ExecCommandDialog initialized") # Debug print 68 | 69 | def update_ui(self): 70 | print(f"Updating UI: Keys choice changed to {self.keys_choice.currentText()}") # Debug print 71 | if self.keys_choice.currentText() == "Ollama": 72 | self.keys_input.hide() 73 | self.llm_input.show() 74 | else: 75 | self.keys_input.show() 76 | self.llm_input.hide() 77 | 78 | def run_command(self): 79 | print("Run command triggered") # Debug print 80 | backend = self.backend_input.text().strip() 81 | graph_file = self.graph_file_input.text().strip() 82 | keys_choice = self.keys_choice.currentText() 83 | log_to_file = self.log_to_file_checkbox.isChecked() 84 | 85 | print(f"Backend: {backend}") # Debug print 86 | print(f"Graph file: {graph_file}") # Debug print 87 | print(f"Keys choice: {keys_choice}") # Debug print 88 | print(f"Log to file: {log_to_file}") # Debug print 89 | 90 | if backend and graph_file: 91 | self.output_text.clear() 92 | if keys_choice == "Ollama": 93 | llm = self.llm_input.text().strip() 94 | if not llm: 95 | print("Error: LLM model not specified") # Debug print 96 | QMessageBox.warning(self, "Warning", "Please enter the LLM model.") 97 | return 98 | command = [backend, "--graph", graph_file, "--llm", llm] 99 | else: 100 | keys_file = self.keys_input.text().strip() 101 | if not keys_file: 102 | print("Error: Keys file not specified") # Debug print 103 | QMessageBox.warning(self, "Warning", "Please enter the keys file.") 104 | return 105 | command = [backend, "--graph", graph_file, "--keys", keys_file] 106 | 107 | if log_to_file: 108 | command.append("--tee") 109 | command.append("output.log") 110 | 111 | print(f"Executing command: {' '.join(command)}") # Debug print 112 | self.process.start(command[0], command[1:]) 113 | if not self.process.waitForStarted(): 114 | print("Error: Failed to start the command") # Debug print 115 | QMessageBox.critical(self, "Error", "Failed to start the command.") 116 | self.output_text.append("Failed to start the command.") 117 | else: 118 | print("Error: Backend or graph file not specified") # Debug print 119 | QMessageBox.warning(self, "Warning", "Please enter all required fields.") 120 | 121 | def read_stdout(self): 122 | data = self.process.readAllStandardOutput() 123 | stdout = data.data().decode() 124 | print(f"Process stdout: {stdout}") # Debug print 125 | self.output_text.append(stdout) 126 | 127 | def read_stderr(self): 128 | data = self.process.readAllStandardError() 129 | stderr = data.data().decode() 130 | print(f"Process stderr: {stderr}", file=sys.stderr) # Debug print to stderr 131 | self.output_text.append(stderr) 132 | 133 | def process_finished(self): 134 | exit_code = self.process.exitCode() 135 | print(f"Process finished with exit code: {exit_code}") # Debug print 136 | self.output_text.append("Process finished.") 137 | self.output_text.append(f"Process exited with code: {exit_code}") -------------------------------------------------------------------------------- /src/KeyboardMouseTool.py: -------------------------------------------------------------------------------- 1 | import pyautogui 2 | import time 3 | from crewai_tools import BaseTool 4 | from typing import Optional 5 | 6 | class KeyboardMouseTool(BaseTool): 7 | name: str = "KeyboardMouseTool" 8 | description: str = "A tool for keyboard and mouse control, as well as taking screenshots." 9 | 10 | def _run(self, action: str, params: Optional[dict] = None) -> str: 11 | if action == "type": 12 | text = params.get("text", "") 13 | pyautogui.typewrite(text) 14 | return f"Typed: {text}" 15 | elif action == "click": 16 | x = params.get("x") 17 | y = params.get("y") 18 | if x is not None and y is not None: 19 | pyautogui.click(x, y) 20 | return f"Clicked at ({x}, {y})" 21 | else: 22 | pyautogui.click() 23 | return "Clicked at current position" 24 | elif action == "move": 25 | x = params.get("x") 26 | y = params.get("y") 27 | pyautogui.moveTo(x, y) 28 | return f"Moved to ({x}, {y})" 29 | elif action == "screenshot": 30 | filename = params.get("filename", "screenshot.png") 31 | folder = params.get("folder", ".") 32 | path = f"{folder}/{filename}" 33 | screenshot = pyautogui.screenshot() 34 | screenshot.save(path) 35 | return f"Screenshot saved as {path}" 36 | else: 37 | return "Invalid action. Supported actions are 'type', 'click', 'move', and 'screenshot'." 38 | 39 | def _arun(self, action: str, params: Optional[dict] = None) -> str: 40 | # For async operations, you might want to add a small delay 41 | time.sleep(0.1) 42 | return self._run(action, params) -------------------------------------------------------------------------------- /src/MainWindow.py: -------------------------------------------------------------------------------- 1 | # MainWindow.py 2 | 3 | from PySide6.QtWidgets import QMainWindow, QGraphicsScene, QGraphicsView, QMenu, QFileDialog, QDockWidget 4 | from PySide6.QtGui import QAction, QPixmap, QPainter 5 | from PySide6.QtCore import Qt, QTimer, QPointF 6 | from MapView import MapView 7 | import file_operations 8 | from CustomGraphicsView import CustomGraphicsView 9 | from ExecCommandDialog import ExecCommandDialog # Import the new dialog 10 | 11 | class MainWindow(QMainWindow): 12 | def __init__(self): 13 | super().__init__() 14 | self.scene = CustomGraphicsScene() 15 | self.view = CustomGraphicsView(self.scene, self) # Pass MainWindow reference to CustomGraphicsView 16 | self.setCentralWidget(self.view) 17 | self.create_dock_widgets() 18 | self.create_actions_and_menus() 19 | 20 | def create_actions_and_menus(self): 21 | actions = { 22 | "File": { 23 | "New": self.new, 24 | "Save": self.save, 25 | "Load": self.load, 26 | }, 27 | "Tools": { 28 | "Map View": self.toggle_map_view, 29 | "Exec Command": self.exec_command, 30 | } 31 | } 32 | 33 | self.menu_bar = self.menuBar() 34 | self.actions = {} 35 | 36 | for menu_name, actions_dict in actions.items(): 37 | menu = self.menu_bar.addMenu(menu_name) 38 | for action_name, method in actions_dict.items(): 39 | action = QAction(action_name, self) 40 | action.triggered.connect(method) 41 | self.actions[action_name.lower().replace(" ", "_") + "_action"] = action 42 | menu.addAction(action) 43 | 44 | def create_dock_widgets(self): 45 | self.map_view = MapView() 46 | self.dock_widget = QDockWidget("Map View", self) 47 | self.dock_widget.setWidget(self.map_view) 48 | self.dock_widget.setFloating(True) 49 | 50 | # Move the dock widget to the bottom-left corner of the screen 51 | screen_geometry = self.screen().geometry() 52 | screen_height = screen_geometry.height() 53 | self.dock_widget.move(0, screen_height - 200) 54 | self.addDockWidget(Qt.BottomDockWidgetArea, self.dock_widget) 55 | 56 | def new(self): 57 | self.scene.clear() # Clears all items from the scene 58 | self.scene.node_counter = 1 # Reset the node counter 59 | self.view.update_map_view() # Refresh the view 60 | 61 | def save(self): 62 | file_operations.save(self.scene) 63 | 64 | def load(self): 65 | self.new() 66 | file_operations.load(self.scene) 67 | self.view.update_map_view() # Refresh the view after loading 68 | 69 | def exec_command(self): 70 | self.exec_command_dialog = ExecCommandDialog(self) 71 | self.exec_command_dialog.show() 72 | 73 | def toggle_map_view(self): 74 | is_visible = self.dock_widget.isVisible() 75 | self.dock_widget.setVisible(not is_visible) 76 | 77 | 78 | class CustomGraphicsScene(QGraphicsScene): 79 | def __init__(self): 80 | super().__init__() 81 | self.node_counter = 1 # Initialize the counter 82 | -------------------------------------------------------------------------------- /src/MapView.py: -------------------------------------------------------------------------------- 1 | # MapView.py 2 | 3 | from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel 4 | from PySide6.QtCore import Qt 5 | from PySide6.QtGui import QPixmap, QTransform 6 | 7 | class MapView(QWidget): 8 | def __init__(self): 9 | super().__init__() 10 | self.setWindowTitle("Map View") 11 | self.layout = QVBoxLayout(self) 12 | 13 | self.map_label = QLabel(self) 14 | self.map_label.setFixedSize(200, 200) # Set fixed size for the map view 15 | self.layout.addWidget(self.map_label) 16 | self.setLayout(self.layout) 17 | 18 | def update_map(self, pixmap): 19 | scaled_pixmap = pixmap.scaled(self.map_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) 20 | self.map_label.setPixmap(scaled_pixmap) 21 | -------------------------------------------------------------------------------- /src/Node.py: -------------------------------------------------------------------------------- 1 | # Node.py 2 | 3 | from PySide6.QtWidgets import QGraphicsItem, QGraphicsEllipseItem 4 | from PySide6.QtCore import QRectF, Qt, QPointF 5 | from PySide6.QtGui import QPainter, QPen, QBrush 6 | from Edge import Edge 7 | from NodeData import NodeData 8 | from NodeLayout import NodeLayout 9 | 10 | class Node(QGraphicsItem): 11 | def __init__(self, node_data: NodeData): 12 | super().__init__() 13 | self.data = node_data 14 | self.setFlag(QGraphicsItem.ItemIsMovable) 15 | self.setFlag(QGraphicsItem.ItemIsSelectable) 16 | self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) 17 | self.rect = QRectF(0, 0, self.data.width, self.data.height) 18 | self.resizeHandleSize = 10 19 | self.resizing = False 20 | self.resizeDirection = None 21 | self.content = NodeLayout(self) 22 | self.input_port = Port(self, QPointF(0, 25), "input") 23 | self.output_port = Port(self, QPointF(self.rect.width(), 25), "output") 24 | self.setPos(self.data.pos_x, self.data.pos_y) # Set initial position from NodeData 25 | self.setAcceptHoverEvents(True) 26 | self.hovered = False # Track hover state 27 | 28 | def setWidth(self, width): 29 | self.rect.setWidth(width) 30 | self.output_port.setPos(width, 25) 31 | self.prepareGeometryChange() 32 | self.content.update_proxy_widget_geometry() # Ensure the content size is updated 33 | self.data.width = width 34 | 35 | def setHeight(self, height): 36 | self.rect.setHeight(height) 37 | self.prepareGeometryChange() 38 | self.content.update_proxy_widget_geometry() # Ensure the content size is updated 39 | self.data.height = height 40 | 41 | def boundingRect(self): 42 | return self.rect 43 | 44 | def paint(self, painter, option, widget): 45 | pen = QPen(Qt.black, 2) 46 | if self.hovered: 47 | pen.setColor(Qt.red) 48 | painter.setPen(pen) 49 | painter.drawRect(self.rect) 50 | self.content.paint(painter, option, widget) # Delegate the painting to NodeLayout 51 | 52 | def itemChange(self, change, value): 53 | if change == QGraphicsItem.ItemPositionChange: 54 | for port in [self.input_port, self.output_port]: 55 | for edge in port.edges: 56 | edge.update_position() 57 | # Sync position with NodeData 58 | self.data.pos_x = value.x() 59 | self.data.pos_y = value.y() 60 | return super().itemChange(change, value) 61 | 62 | def mousePressEvent(self, event): 63 | pos = event.pos() 64 | if pos.x() >= self.rect.right() - self.resizeHandleSize and \ 65 | pos.y() >= self.rect.bottom() - self.resizeHandleSize: 66 | self.resizing = True 67 | self.resizeDirection = "bottom_right" 68 | self.setCursor(Qt.SizeFDiagCursor) 69 | else: 70 | self.resizing = False 71 | super().mousePressEvent(event) 72 | 73 | def mouseMoveEvent(self, event): 74 | if self.resizing: 75 | delta = event.pos() - event.lastPos() 76 | if self.resizeDirection == "bottom_right": 77 | self.setWidth(self.rect.width() + delta.x()) 78 | self.setHeight(self.rect.height() + delta.y()) 79 | else: 80 | super().mouseMoveEvent(event) 81 | 82 | def mouseReleaseEvent(self, event): 83 | if self.resizing: 84 | self.resizing = False 85 | self.setCursor(Qt.ArrowCursor) 86 | else: 87 | super().mouseReleaseEvent(event) 88 | 89 | def remove_node(self): 90 | # Remove all edges connected to this node 91 | for edge in self.input_port.edges[:]: 92 | edge.remove() 93 | for edge in self.output_port.edges[:]: 94 | edge.remove() 95 | 96 | # Update prevs and nexts of connected nodes 97 | for prev_id in self.data.prevs: 98 | prev_node = self.scene().get_node_by_id(prev_id) 99 | if prev_node: 100 | prev_node.data.nexts.remove(self.data.uniq_id) 101 | 102 | for next_id in self.data.nexts: 103 | next_node = self.scene().get_node_by_id(next_id) 104 | if next_node: 105 | next_node.data.prevs.remove(self.data.uniq_id) 106 | 107 | # Finally, remove this node from the scene 108 | self.scene().removeItem(self) 109 | 110 | def hoverEnterEvent(self, event): 111 | self.hovered = True 112 | self.update() # Trigger a repaint 113 | super().hoverEnterEvent(event) 114 | 115 | def hoverLeaveEvent(self, event): 116 | self.hovered = False 117 | self.update() # Trigger a repaint 118 | super().hoverLeaveEvent(event) 119 | 120 | class Port(QGraphicsEllipseItem): 121 | def __init__(self, parent, position, port_type): 122 | super().__init__(-5, -5, 10, 10, parent) 123 | self.setBrush(Qt.black) 124 | self.setFlag(QGraphicsItem.ItemIsMovable, False) 125 | self.setPos(position) 126 | self.port_type = port_type 127 | self.edges = [] 128 | 129 | def mousePressEvent(self, event): 130 | if self.port_type == "output": 131 | edge = Edge(self) 132 | self.edges.append(edge) 133 | self.scene().addItem(edge) 134 | 135 | def mouseMoveEvent(self, event): 136 | if self.edges: 137 | self.edges[-1].update_position(event.scenePos()) 138 | 139 | def mouseReleaseEvent(self, event): 140 | if self.edges: 141 | items = self.scene().items(event.scenePos()) 142 | for item in items: 143 | if isinstance(item, Port) and item != self: 144 | if self.port_type == "output" and item.port_type == "input": 145 | self.edges[-1].set_destination(item) 146 | item.edges.append(self.edges[-1]) 147 | break 148 | else: 149 | self.scene().removeItem(self.edges[-1]) 150 | self.edges.pop() 151 | -------------------------------------------------------------------------------- /src/NodeData.py: -------------------------------------------------------------------------------- 1 | # NodeData.py 2 | 3 | from dataclasses import dataclass, asdict, field 4 | from typing import List 5 | 6 | @dataclass 7 | class Serializable: 8 | def to_dict(self): 9 | return asdict(self) 10 | 11 | @classmethod 12 | def from_dict(cls, data): 13 | return cls(**data) 14 | 15 | @dataclass 16 | class NodeData(Serializable): 17 | 18 | # "None", "Start", "Agent", "Task", "Step", "Team" 19 | type: str = "" 20 | 21 | # New field for tools 22 | tools: List[str] = field(default_factory=list) 23 | 24 | uniq_id: str = "" 25 | pos_x: float = 0.0 26 | pos_y: float = 0.0 27 | width: float = 200.0 # Default width 28 | height: float = 200.0 # Default height 29 | 30 | name: str = "" 31 | 32 | # Agent 33 | role: str = "" 34 | goal: str = "" 35 | backstory: str = "" 36 | 37 | # Task 38 | agent: str = "" 39 | description: str = "" 40 | expected_output: str = "" 41 | 42 | # Step 43 | tool: str = "" 44 | arg: str = "" 45 | output_var: str = "" 46 | 47 | 48 | nexts: List[int] = field(default_factory=list) 49 | prevs: List[int] = field(default_factory=list) 50 | 51 | def to_dict(self): 52 | return asdict(self) 53 | 54 | @classmethod 55 | def from_dict(cls, data): 56 | return cls(**data) 57 | -------------------------------------------------------------------------------- /src/NodeLayout.py: -------------------------------------------------------------------------------- 1 | # NodeLayout.py 2 | 3 | 4 | from PySide6.QtWidgets import QVBoxLayout, QWidget, QLabel, QComboBox, QListWidget, QPushButton 5 | from KeyboardMouseTool import KeyboardMouseTool 6 | from AdditionalTools import WebRequestTool, FileOperationTool, SystemCommandTool 7 | 8 | from PySide6.QtWidgets import QGraphicsItem, QLineEdit, QGraphicsProxyWidget, QVBoxLayout, QWidget, QLabel, QComboBox, QTextEdit, QHBoxLayout 9 | from PySide6.QtCore import QRectF, Qt 10 | from PySide6.QtGui import QPainter, QBrush, QPen 11 | 12 | class Slot: 13 | def __init__(self, data_attr, parent, layout, widget_class=QTextEdit): 14 | self.data_attr = data_attr 15 | self.parent = parent 16 | self.layout = layout 17 | self.widget_class = widget_class 18 | 19 | self.create_widgets() 20 | self.add_to_layout() 21 | self.connect_signals() 22 | 23 | def create_widgets(self): 24 | self.label = QLabel(self.data_attr.capitalize()) 25 | self.edit = self.widget_class(getattr(self.parent.data, self.data_attr)) 26 | 27 | def add_to_layout(self): 28 | self.layout.addWidget(self.label) 29 | self.layout.addWidget(self.edit) 30 | 31 | def connect_signals(self): 32 | if isinstance(self.edit, QTextEdit): 33 | self.edit.textChanged.connect(self.update_data) 34 | elif isinstance(self.edit, QLineEdit): 35 | self.edit.textChanged.connect(self.update_data) 36 | 37 | def update_data(self): 38 | if isinstance(self.edit, QTextEdit): 39 | setattr(self.parent.data, self.data_attr, self.edit.toPlainText()) 40 | elif isinstance(self.edit, QLineEdit): 41 | setattr(self.parent.data, self.data_attr, self.edit.text()) 42 | 43 | def show(self): 44 | self.label.show() 45 | self.edit.show() 46 | 47 | def hide(self): 48 | self.label.hide() 49 | self.edit.hide() 50 | 51 | class NodeLayout(QGraphicsItem): 52 | def __init__(self, parent): 53 | super().__init__(parent) 54 | self.parent = parent # Save the reference to the parent node 55 | 56 | # Create a widget to hold the QLineEdit widgets 57 | self.container_widget = QWidget() 58 | self.layout = QVBoxLayout() 59 | self.container_widget.setLayout(self.layout) 60 | 61 | # Type Label and Combo Box 62 | self.type_layout = QHBoxLayout() 63 | self.type_label = QLabel("Type:") 64 | self.type_combo = QComboBox() 65 | self.type_combo.addItems(["None", "Start", "Agent", "Task", "Step", "Team"]) 66 | self.type_combo.setCurrentText(self.parent.data.type) 67 | self.type_layout.addWidget(self.type_label) 68 | self.type_layout.addWidget(self.type_combo) 69 | self.layout.addLayout(self.type_layout) 70 | self.type_combo.currentTextChanged.connect(self.update_data_type) 71 | 72 | # Initialize slots 73 | self.slots = {} 74 | self.add_slot("name", QLineEdit) 75 | 76 | # Agent 77 | self.add_slot("role", QTextEdit) 78 | self.add_slot("goal", QTextEdit) 79 | self.add_slot("backstory", QTextEdit) 80 | 81 | # Task 82 | self.add_slot("agent", QTextEdit) 83 | self.add_slot("description", QTextEdit) 84 | self.add_slot("expected_output", QTextEdit) 85 | 86 | # Step 87 | self.add_slot("tool", QTextEdit) 88 | self.add_slot("arg", QTextEdit) 89 | self.add_slot("output_var", QTextEdit) 90 | 91 | self.proxy_widget = QGraphicsProxyWidget(self) 92 | self.proxy_widget.setWidget(self.container_widget) 93 | 94 | # Initialize visibility based on the current type 95 | self.update_field_visibility() 96 | 97 | # Call update_proxy_widget_geometry once to ensure layout is correct 98 | self.update_proxy_widget_geometry() 99 | 100 | # Add tool selection widgets 101 | self.tool_label = QLabel("Tools:") 102 | self.tool_combo = QComboBox() 103 | self.tool_combo.addItems(["KeyboardMouseTool", "WebRequestTool", "FileOperationTool", "SystemCommandTool"]) 104 | self.tool_list = QListWidget() 105 | self.add_tool_button = QPushButton("Add Tool") 106 | self.remove_tool_button = QPushButton("Remove Tool") 107 | 108 | self.layout.addWidget(self.tool_label) 109 | self.layout.addWidget(self.tool_combo) 110 | self.layout.addWidget(self.tool_list) 111 | self.layout.addWidget(self.add_tool_button) 112 | self.layout.addWidget(self.remove_tool_button) 113 | 114 | self.add_tool_button.clicked.connect(self.add_tool) 115 | self.remove_tool_button.clicked.connect(self.remove_tool) 116 | 117 | # Initialize tool list 118 | self.update_tool_list() 119 | 120 | def add_tool(self): 121 | tool = self.tool_combo.currentText() 122 | if tool not in self.parent.data.tools: 123 | self.parent.data.tools.append(tool) 124 | self.update_tool_list() 125 | 126 | def remove_tool(self): 127 | current_item = self.tool_list.currentItem() 128 | if current_item: 129 | tool = current_item.text() 130 | self.parent.data.tools.remove(tool) 131 | self.update_tool_list() 132 | 133 | def update_tool_list(self): 134 | self.tool_list.clear() 135 | self.tool_list.addItems(self.parent.data.tools) 136 | 137 | 138 | def add_slot(self, data_attr, widget_class): 139 | slot = Slot(data_attr, self.parent, self.layout, widget_class) 140 | self.slots[data_attr] = slot 141 | 142 | def boundingRect(self): 143 | return self.parent.rect 144 | 145 | def paint(self, painter, option, widget): 146 | rect = self.boundingRect() 147 | painter.setBrush(QBrush(Qt.white)) 148 | painter.setPen(QPen(Qt.black)) 149 | painter.drawRect(rect) 150 | 151 | # Draw right-bottom square 152 | painter.setBrush(QBrush(Qt.black)) 153 | painter.drawRect(rect.right() - 10, rect.bottom() - 10, 10, 10) 154 | 155 | def update_proxy_widget_geometry(self): 156 | rect = self.boundingRect() 157 | self.proxy_widget.setGeometry(rect.adjusted(5, 5, -5, -5)) 158 | 159 | def update_data_type(self): 160 | self.parent.data.type = self.type_combo.currentText() 161 | self.update_field_visibility() # Update field visibility when type changes 162 | 163 | def update_field_visibility(self): 164 | node_type = self.parent.data.type 165 | for slot in self.slots.values(): 166 | slot.hide() 167 | 168 | if node_type in ["Start", "None"]: 169 | pass 170 | elif node_type == "Team": 171 | self.slots["name"].show() 172 | elif node_type == "Agent": 173 | self.slots["name"].show() 174 | self.slots["role"].show() 175 | self.slots["goal"].show() 176 | self.slots["backstory"].show() 177 | elif node_type == "Task": 178 | self.slots["agent"].show() 179 | self.slots["description"].show() 180 | self.slots["expected_output"].show() 181 | elif node_type == "Step": 182 | self.slots["tool"].show() 183 | self.slots["arg"].show() 184 | self.slots["output_var"].show() 185 | 186 | 187 | 188 | # Update geometry after setting visibility 189 | self.update_proxy_widget_geometry() 190 | -------------------------------------------------------------------------------- /src/Tee.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | 4 | class Tee: 5 | def __init__(self, filename, mode='a'): # Use 'a' for append mode 6 | self.file = open(filename, mode) 7 | self.stdout = sys.stdout 8 | sys.stdout = self 9 | 10 | def write(self, message): 11 | timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') 12 | message_with_timestamp = f"{timestamp} - {message}" 13 | self.stdout.write("\n") 14 | self.stdout.write(message_with_timestamp) 15 | self.file.write(message_with_timestamp) 16 | self.file.flush() 17 | 18 | def flush(self): 19 | self.stdout.flush() 20 | self.file.flush() 21 | 22 | def close(self): 23 | sys.stdout = self.stdout 24 | self.file.close() 25 | -------------------------------------------------------------------------------- /src/WorkFlow.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import configparser 4 | from typing import Dict, List 5 | from NodeData import NodeData 6 | from crewai import Agent, Task, Crew, Process 7 | from langchain_community.llms import Ollama 8 | from langchain_community.chat_models import ChatOpenAI 9 | from crewai_tools import FileReadTool, BaseTool 10 | import networkx as nx 11 | from KeyboardMouseTool import KeyboardMouseTool 12 | from AdditionalTools import WebRequestTool, FileOperationTool, SystemCommandTool 13 | 14 | def load_nodes_from_json(filename: str) -> Dict[str, NodeData]: 15 | with open(filename, 'r') as file: 16 | data = json.load(file) 17 | node_map = {} 18 | for node_data in data["nodes"]: 19 | node = NodeData.from_dict(node_data) 20 | node_map[node.uniq_id] = node 21 | return node_map 22 | 23 | def find_nodes_by_type(node_map: Dict[str, NodeData], node_type: str) -> List[NodeData]: 24 | return [node for node in node_map.values() if node.type == node_type] 25 | 26 | def find_node_by_type(node_map: Dict[str, NodeData], node_type: str) -> NodeData: 27 | for node in node_map.values(): 28 | if node.type == node_type: 29 | return node 30 | return None 31 | 32 | class FileWriterTool(BaseTool): 33 | name: str = "FileWriter" 34 | description: str = "Writes given content to a specified file." 35 | 36 | def _run(self, filename: str, content: str) -> str: 37 | with open(filename, 'w') as file: 38 | file.write(content) 39 | return f"Content successfully written to {filename}" 40 | 41 | 42 | def create_agent(node: NodeData, llm) -> Agent: 43 | tools = [] 44 | for tool_name in node.tools: 45 | if tool_name == "KeyboardMouseTool": 46 | tools.append(KeyboardMouseTool()) 47 | elif tool_name == "WebRequestTool": 48 | tools.append(WebRequestTool()) 49 | elif tool_name == "FileOperationTool": 50 | tools.append(FileOperationTool()) 51 | elif tool_name == "SystemCommandTool": 52 | tools.append(SystemCommandTool()) 53 | 54 | return Agent( 55 | role=node.role, 56 | goal=node.goal, 57 | backstory=node.backstory, 58 | verbose=True, 59 | allow_delegation=False, 60 | llm=llm, 61 | tools=tools 62 | ) 63 | 64 | def create_task(node: NodeData, agent: Agent, node_map: Dict[str, NodeData], task_map: Dict[str, Task]) -> Task: 65 | steps = [] 66 | for step_id in node.nexts: 67 | step_node = node_map[step_id] 68 | tool_instance = None 69 | if step_node.tool == "FileWriterTool()": 70 | tool_instance = FileWriterTool() 71 | elif step_node.tool == "FileReadTool()": 72 | tool_instance = FileReadTool() 73 | step = { 74 | 'tool': tool_instance, 75 | 'args': step_node.arg, 76 | 'output_var': step_node.output_var 77 | } 78 | steps.append(step) 79 | 80 | # Resolve dependencies with actual Task instances 81 | dependencies = [task_map[dep_id] for dep_id in node.prevs if dep_id in task_map] 82 | 83 | return Task( 84 | description=node.description, 85 | expected_output=node.expected_output, 86 | agent=agent, 87 | steps=steps, 88 | dependencies=dependencies 89 | ) 90 | 91 | def topological_sort_tasks(task_nodes: List[NodeData]) -> List[NodeData]: 92 | graph = nx.DiGraph() 93 | 94 | # Add nodes to the graph 95 | for node in task_nodes: 96 | graph.add_node(node.uniq_id) 97 | 98 | # Add edges to the graph 99 | for node in task_nodes: 100 | for prev_id in node.prevs: 101 | if prev_id in graph: 102 | graph.add_edge(prev_id, node.uniq_id) 103 | 104 | # Perform topological sort 105 | sorted_ids = list(nx.topological_sort(graph)) 106 | 107 | # Return nodes in sorted order 108 | id_to_node = {node.uniq_id: node for node in task_nodes} 109 | sorted_tasks = [id_to_node[node_id] for node_id in sorted_ids] 110 | 111 | return sorted_tasks 112 | 113 | def RunWorkFlow(node: NodeData, node_map: Dict[str, NodeData], llm): 114 | print(f"Start root ID: {node.uniq_id}") 115 | 116 | # from root find team 117 | sub_node_map = {next_id: node_map[next_id] for next_id in node.nexts} 118 | team_node = find_node_by_type(sub_node_map, "Team") 119 | if not team_node: 120 | print("No Team node found") 121 | return 122 | 123 | print(f"Processing Team {team_node.name} ID: {team_node.uniq_id}") 124 | 125 | # from team find agents 126 | agent_map = {next_id: node_map[next_id] for next_id in team_node.nexts} 127 | agent_nodes = find_nodes_by_type(node_map, "Agent") 128 | agents = {agent_node.name: create_agent(agent_node, llm) for agent_node in agent_nodes} 129 | for agent_node in agent_nodes: 130 | print(f"Agent {agent_node.name} ID: {agent_node.uniq_id}") 131 | 132 | # Use BFS to collect all task nodes 133 | task_nodes = [] 134 | queue = find_nodes_by_type(sub_node_map, "Task") 135 | 136 | while queue: 137 | current_node = queue.pop(0) 138 | if current_node not in task_nodes: 139 | print(f"Processing task_node ID: {current_node.uniq_id}") 140 | task_nodes.append(current_node) 141 | next_sub_node_map = {next_id: node_map[next_id] for next_id in current_node.nexts} 142 | queue.extend(find_nodes_by_type(next_sub_node_map, "Task")) 143 | 144 | # Sort tasks topologically to respect dependencies 145 | sorted_task_nodes = topological_sort_tasks(task_nodes) 146 | 147 | tasks = [] 148 | task_map = {} 149 | 150 | # Create tasks with dependencies resolved 151 | for task_node in sorted_task_nodes: 152 | if task_node: 153 | print(f"Processing task_node ID: {task_node.description}") 154 | agent = agents[task_node.agent] 155 | task = create_task(task_node, agent, node_map, task_map) 156 | tasks.append(task) 157 | task_map[task_node.uniq_id] = task 158 | else: 159 | print("No task_node found") 160 | return 161 | 162 | crew = Crew( 163 | agents=list(agents.values()), 164 | tasks=tasks, 165 | verbose=2 166 | ) 167 | 168 | result = crew.kickoff() 169 | print("######################") 170 | print(result) 171 | 172 | def run_workflow_from_file(filename: str, llm): 173 | node_map = load_nodes_from_json(filename) 174 | start_nodes = find_nodes_by_type(node_map, "Start") 175 | for start_node in start_nodes: 176 | RunWorkFlow(start_node, node_map, llm) 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | # ... (rest of the file remains the same) -------------------------------------------------------------------------------- /src/backend.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import configparser 4 | from WorkFlow import run_workflow_from_file 5 | from langchain_community.llms import Ollama 6 | from langchain_community.chat_models import ChatOpenAI 7 | from Tee import Tee 8 | 9 | def get_openai_api_key(config_path=None): 10 | # First, try to get the API key from environment variable 11 | api_key = os.environ.get("OPENAI_API_KEY") 12 | 13 | # If not found in environment, try to get from config file 14 | if not api_key and config_path: 15 | config = configparser.ConfigParser() 16 | config.read(config_path) 17 | if 'OpenAI' in config and 'api_key' in config['OpenAI']: 18 | api_key = config['OpenAI']['api_key'] 19 | 20 | return api_key 21 | 22 | def main(): 23 | parser = argparse.ArgumentParser(description="Run workflow from a JSON graph definition.") 24 | parser.add_argument('--graph', required=True, help="Path to the JSON file defining the graph.") 25 | parser.add_argument('--keys', help="Path to the credentials file.") 26 | parser.add_argument('--tee', help="File to write the output log to.") 27 | parser.add_argument('--llm', help="Specify which LLM to use") 28 | 29 | args = parser.parse_args() 30 | 31 | if args.tee: 32 | tee = Tee(args.tee) 33 | 34 | api_key = get_openai_api_key(args.keys) 35 | 36 | if api_key: 37 | os.environ["OPENAI_API_KEY"] = api_key 38 | llm = ChatOpenAI(temperature=0.7, model_name="gpt-4") 39 | elif args.llm: 40 | llm = Ollama(model=args.llm) 41 | else: 42 | raise ValueError("No OpenAI API key found and no alternative LLM specified.") 43 | 44 | run_workflow_from_file(args.graph, llm) 45 | 46 | if args.tee: 47 | tee.close() 48 | 49 | if __name__ == "__main__": 50 | main() -------------------------------------------------------------------------------- /src/file_operations.py: -------------------------------------------------------------------------------- 1 | # file_operations.py 2 | 3 | import json 4 | from PySide6.QtWidgets import QFileDialog 5 | from Node import Node 6 | from Edge import Edge 7 | from NodeData import NodeData 8 | 9 | def save(scene): 10 | filename, _ = QFileDialog.getSaveFileName(None, "Save File", "", "JSON Files (*.json)") 11 | if filename: 12 | data = { 13 | "nodes": [], 14 | "node_counter": scene.node_counter 15 | } 16 | 17 | for item in scene.items(): 18 | if isinstance(item, Node): 19 | node_dict = item.data.to_dict() 20 | data["nodes"].append(node_dict) 21 | 22 | with open(filename, 'w') as file: 23 | json.dump(data, file, indent=4) 24 | 25 | def load(scene): 26 | filename, _ = QFileDialog.getOpenFileName(None, "Load File", "", "JSON Files (*.json)") 27 | if filename: 28 | with open(filename, 'r') as file: 29 | data = json.load(file) 30 | 31 | node_map = {} 32 | 33 | # Load the node counter 34 | scene.node_counter = data.get("node_counter", 1) 35 | 36 | # First create all nodes 37 | for node_data in data["nodes"]: 38 | node_data_obj = NodeData.from_dict(node_data) 39 | node = Node(node_data_obj) 40 | scene.addItem(node) 41 | node_map[node_data_obj.uniq_id] = node 42 | 43 | # Collect edges 44 | edge_set = set() # To track created edges and avoid duplicates 45 | for node_data in data["nodes"]: 46 | source_id = node_data["uniq_id"] 47 | for next_id in node_data["nexts"]: 48 | edge_tuple = (source_id, next_id) 49 | edge_set.add(edge_tuple) 50 | 51 | # Clear nexts and prevs to avoid duplicates 52 | for node in node_map.values(): 53 | node.data.nexts.clear() 54 | node.data.prevs.clear() 55 | 56 | # Then create edges based on the collected edge_set 57 | for source_id, next_id in edge_set: 58 | if source_id in node_map and next_id in node_map: 59 | source_node = node_map[source_id] 60 | destination_node = node_map[next_id] 61 | edge = Edge(source_node.output_port) 62 | edge.set_destination(destination_node.input_port) 63 | scene.addItem(edge) 64 | source_node.output_port.edges.append(edge) 65 | destination_node.input_port.edges.append(edge) 66 | 67 | # Ensure edges are properly connected and update their positions 68 | for node in node_map.values(): 69 | for edge in node.input_port.edges + node.output_port.edges: 70 | edge.update_position() 71 | -------------------------------------------------------------------------------- /src/frontend.py: -------------------------------------------------------------------------------- 1 | # main.py 2 | 3 | import sys 4 | from PySide6.QtWidgets import QApplication 5 | from PySide6.QtCore import QTimer # Import QTimer 6 | from MainWindow import MainWindow 7 | 8 | def initialize_main_window(): 9 | window = MainWindow() 10 | window.setWindowTitle("Json Node Editor") 11 | window.setGeometry(100, 100, 800, 600) # Set initial size to 800x600 12 | 13 | # Set up a timer to refresh the map view periodically 14 | window.timer = QTimer(window) 15 | window.timer.timeout.connect(window.view.update_map_view) 16 | window.timer.start(1000) # Refresh every second 17 | 18 | return window 19 | 20 | if __name__ == "__main__": 21 | app = QApplication(sys.argv) 22 | window = initialize_main_window() 23 | window.show() 24 | sys.exit(app.exec()) 25 | -------------------------------------------------------------------------------- /src/hook-pyside6.py: -------------------------------------------------------------------------------- 1 | # hook-pyside6.py 2 | 3 | from PyInstaller.utils.hooks import collect_submodules, collect_data_files 4 | 5 | hiddenimports = collect_submodules('PySide6') 6 | datas = collect_data_files('PySide6', include_py_files=True) 7 | -------------------------------------------------------------------------------- /src/setup-backend.py: -------------------------------------------------------------------------------- 1 | from cx_Freeze import setup, Executable 2 | 3 | # Define the executable and other build options 4 | executables = [Executable("backend.py")] 5 | 6 | build_options = { 7 | 'packages': [], 8 | 'excludes': [], 9 | } 10 | 11 | setup( 12 | name="MyApplication", 13 | version="0.1", 14 | description="My Python Application", 15 | options={'build_exe': build_options}, 16 | executables=executables 17 | ) 18 | --------------------------------------------------------------------------------