├── .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 | 
8 |
9 | [](https://github.com/LangGraph-GUI/CrewAI-GUI)
10 | [](https://opensource.org/licenses/MIT)
11 | [](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 | [](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 | | [](https://github.com/HomunMage) | [](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 |
17 |
18 |
19 |
48 |
49 |
50 |
101 |
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 |
17 |
18 |
19 |
48 |
49 |
50 |
87 |
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 | 
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 |
--------------------------------------------------------------------------------