├── .gitignore
├── .gitpod.yml
├── .vscode
├── extensions.json
└── tasks.json
├── LICENSE
├── README.md
├── apps
├── backend
│ ├── package.json
│ └── server.js
└── frontend
│ ├── .env.example
│ ├── .gitignore
│ ├── index.html
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── favicon.png
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ ├── src
│ ├── App.animations.css
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── components
│ │ ├── AboutInfoModal.tsx
│ │ ├── CommandStringBar.tsx
│ │ ├── EditAIProfileModal.tsx
│ │ ├── ListOfAIProfiles.tsx
│ │ ├── OutputSegmentsList.tsx
│ │ ├── SettingsDrawer.tsx
│ │ ├── SettingsDrawerContent.tsx
│ │ ├── StartNewProcessMenu.tsx
│ │ ├── TheAreaAtTheBottom.tsx
│ │ ├── TheAreaAtTheTop.tsx
│ │ ├── TheAreaInTheMiddle.tsx
│ │ ├── TheHiddenServiceRunner.tsx
│ │ └── smol
│ │ │ └── ColorModeSwitcher.tsx
│ ├── config
│ │ ├── BackendConfigurationKeys.ts
│ │ ├── BackendUrl.ts
│ │ ├── SHELL_COMMANDS.ts
│ │ └── theme.ts
│ ├── data
│ │ └── example-profiles.yaml
│ ├── entities
│ │ ├── AIProfile.d.ts
│ │ └── BackendServiceState.d.ts
│ ├── hooks
│ │ ├── useApiService.ts
│ │ ├── useAutoGPTStarter.ts
│ │ ├── useRemoteConsoleOutput.ts
│ │ ├── useToastShortcuts.ts
│ │ ├── useUpdateLocalStorage.ts
│ │ └── useWebSocketConnection.ts
│ ├── main.tsx
│ ├── services
│ │ └── APIService.ts
│ ├── store
│ │ ├── useContextStore.ts
│ │ └── useSettingsStore.ts
│ ├── utils
│ │ ├── createSimpleZustandStore.ts
│ │ ├── createStoreWrappedWithProxy.ts
│ │ ├── generateSimpleUniqueId.ts
│ │ ├── getPseudoRandomColorFromString.ts
│ │ └── getRandomProfileDataFiller.ts
│ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── package-lock.json
├── package.json
├── scripts
├── mock-continuous.sh
├── mock-spinner.sh
├── mock-user-input.sh
└── setup-auto-gpt.sh
└── turbo.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 |
11 | # next.js
12 | .next/
13 | out/
14 | build
15 |
16 | # misc
17 | .DS_Store
18 | *.pem
19 |
20 | # debug
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | .pnpm-debug.log*
25 |
26 | # local env files
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 |
32 | # turbo
33 | .turbo
34 |
35 | # auto-gpt-webui
36 | .ignore
37 | /auto-gpt*
38 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | # Commands to start on workspace startup
2 | tasks:
3 | - name: Initialize & Start Development Server
4 | init: npm run setup-auto-gpt && npm install
5 | before: pip install -r auto-gpt/requirements.txt
6 | command: npm run dev
7 |
8 | # Ports to expose on workspace startup
9 | ports:
10 | - port: 7070
11 | onOpen: open-browser
12 | name: Frontend
13 | description: Frontend
14 | - port: 2200
15 | name: Backend
16 | description: Backend
17 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "actboy168.tasks",
4 | "esbenp.prettier-vscode"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "npm",
6 | "script": "dev",
7 | "label": "🔰",
8 | "detail": "turbo run dev",
9 | "presentation": {
10 | "echo": true,
11 | "reveal": "always",
12 | "focus": false,
13 | "panel": "shared",
14 | "showReuseMessage": true,
15 | "clear": true
16 | }
17 | },
18 | {
19 | "type": "shell",
20 | "label": "💀",
21 | "command": "npx --yes kill-port 2200 ; npx --yes kill-port 3000",
22 | "presentation": {
23 | "echo": true,
24 | "reveal": "always",
25 | "focus": false,
26 | "panel": "shared",
27 | "showReuseMessage": true,
28 | "clear": true
29 | }
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Toran Bruce Richards
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 | # Auto-GPT WebUI
2 |
3 | This project is a frontend web application that runs and interacts with [Auto-GPT](https://github.com/Torantulino/Auto-GPT). The backend application provides the core logic and functionality, while this frontend application wraps over it and offers a user-friendly interface.
4 |
5 | ### 🌟 Special thanks to [the original Auto-GPT project](https://github.com/Torantulino/Auto-GPT) and its creator, [Torantulino](https://github.com/Torantulino), for his hard work and for making this project possible! 🌟
6 |
7 | ---
8 |
9 | ## Disclaimer: Limited Availability for Maintenance
10 |
11 | Please note that I developed this project primarily for personal amusement and as a learning experience.
12 | My availability to address issues, review pull requests, or implement new features may be limited, as I have other full-time commitments.
13 |
14 | If you find this project useful and would like to contribute, I welcome and highly appreciate community involvement. 💛
15 |
16 | Thank you for your understanding, and I hope you find this helpful!
17 |
18 | ---
19 |
20 | ## 🛠️ Installation & Usage
21 |
22 | Clone the repo, then enter the root directory and run the folling commands:
23 |
24 | ```bash
25 | npm install
26 | npm run setup-auto-gpt
27 | ```
28 |
29 | - The first command will install the required dependencies.
30 | - The second command will clone the Auto-GPT repository and install its dependencies.
31 |
32 | Run the following command in order to start both the frontend application and the Node.js server, which will in turn start and stop the python script, and inform the frontend of the script's output:
33 |
34 | ```bash
35 | npm start
36 | ```
37 |
38 | When you first open up the web app, you will see a few alerts about missing API Keys. You need to fill these in in order for the application to work correctly. Look below for instructions.
39 |
40 | ---
41 |
42 | ## ⚙ Requirements and Configuration
43 |
44 | ### OpenAI API key
45 |
46 | Follow these steps to obtain an OpenAI API key:
47 |
48 | 1. Visit the [**OpenAI Platform**](https://platform.openai.com/signup) website.
49 | 1. If you don't have an account yet, sign up by providing your email, name, and creating a password.
50 | 1. After signing up or logging in, go to the [**API Keys**](https://platform.openai.com/account/api-keys) section in your account.
51 | 1. Click the **Create an API key** button to generate a new API key.
52 | 1. Copy the API key and paste it in the WebUI to use it with Auto-GPT.
53 |
54 | - ⚠️ Keep your API key secure and never share it with anyone. Treat it like a password.
55 |
56 | ### Pinecone API key
57 |
58 | Follow these steps to obtain a Pinecone API key:
59 |
60 | 1. Visit the [**Pinecone**](https://app.pinecone.io/register) website.
61 | 1. If you don't have an account yet, sign up by providing your email and creating a password.
62 | 1. After signing up or logging in, go to the [**Projects**](https://app.pinecone.io/projects) page.
63 | 1. Click on the default project, or create a new one if desired.
64 | 1. In the left sidebar, click on the **API Keys** section.
65 | 1. Create a new API key and copy-paste it in the WebUI to use it with Auto-GPT.
66 |
67 | - ⚠️ Keep your API key secure and never share it with anyone. Treat it like a password.
68 |
69 | ### Python environment
70 |
71 | You will also need both [Python 3.8 or later](https://www.tutorialspoint.com/how-to-install-python-in-windows) and [Node.js](https://nodejs.org/en) installed on your system.
72 |
73 | ---
74 |
75 | ## 🛠 Tools and Libraries
76 |
77 | - [**Auto-GPT**](https://github.com/Torantulino/Auto-GPT) - As mentioned, the backend logic and the meat of this application is provided by the Auto-GPT project. Check out the repo for more information if you'd like to [contribute to](https://github.com/Torantulino/Auto-GPT/blob/master/CONTRIBUTING.md) or [sponsor](https://github.com/sponsors/Torantulino) the project.
78 | - [**Turborepo**](https://turborepo.org/) - A high-performance build system and repository organizer for JavaScript and TypeScript codebases.
79 | - [**Vite**](https://vitejs.dev/) - A build tool and development server for web applications, designed to be fast and lightweight.
80 | - [**React**](https://reactjs.org/) - A JavaScript library for building user interfaces, developed and maintained by Facebook.
81 | - [**Chakra UI**](https://chakra-ui.com/) - A simple, modular, and accessible component library for React.
82 | - [**Zustand**](https://github.com/pmndrs/zustand) - A lightweight and easy-to-use state management library for React.
83 | - [**Express**](https://expressjs.com/) - A fast, unopinionated, minimalist web framework for Node.js.
84 | - [**dotenv**](https://www.npmjs.com/package/dotenv) - A library that loads environment variables from a `.env` file into your Node.js application.
85 | - [**Emotion**](https://emotion.sh/docs/introduction) - A library for CSS-in-JS, allowing you to style your React components using JavaScript.
86 | - [**TypeScript**](https://www.typescriptlang.org/) - A popular superset of JavaScript that adds static types and other features for better code maintainability.
87 | - [**ESLint**](https://eslint.org/) - A tool for identifying and reporting on patterns found in ECMAScript/JavaScript code.
88 | - [**Prettier**](https://prettier.io/) - A widely-used code formatter that enforces a consistent style by parsing your code and re-printing it with its own rules.
89 |
--------------------------------------------------------------------------------
/apps/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backend",
3 | "version": "0.1.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon server.js",
8 | "start": "node server.js"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "child_process": "^1.0.2",
15 | "cors": "^2.8.5",
16 | "dotenv": "^16.0.3",
17 | "express": "^4.18.2",
18 | "install": "^0.13.0",
19 | "js-yaml": "^4.1.0",
20 | "nodemon": "^3.1.0",
21 | "npm": "^9.6.4",
22 | "tree-kill": "^1.2.2",
23 | "ws": "^8.13.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/apps/backend/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const cors = require('cors');
3 | const { exec } = require('child_process');
4 | const WebSocket = require('ws');
5 |
6 | const fs = require('fs');
7 | const path = require('path');
8 | const dotenv = require('dotenv');
9 | const yaml = require('js-yaml');
10 | const kill = require('tree-kill');
11 |
12 | const app = express();
13 | app.use(cors());
14 |
15 | const PORT = process.env.PORT || 2200;
16 | const RELATIVE_PATH_TO_AUTOGPT = '../../auto-gpt';
17 | // const PATH_TO_AI_SETTINGS_FILE = fs.existsSync(
18 | // path.join(__dirname, RELATIVE_PATH_TO_AUTOGPT, 'ai_settings.yaml')
19 | // )
20 | // ? 'ai_settings.yaml'
21 | // : 'last_run_ai_settings.yaml';
22 | const PATH_TO_AI_SETTINGS_FILE = 'ai_settings.yaml';
23 |
24 | const OBFUSCATE_ENV_VARS_BEFORE_SENDING_TO_CLIENT = true;
25 |
26 | const wss = new WebSocket.Server({ noServer: true });
27 |
28 | ////////////////
29 |
30 | const state = {
31 | activeProcess: null,
32 | activeCommandString: null,
33 | consoleOutputLines: [],
34 | };
35 |
36 | let configuration = loadConfiguration();
37 |
38 | function obfuscateObjectProperties(object) {
39 | return Object.fromEntries(
40 | Object.entries(object).map(([key, value]) => [
41 | key,
42 | value.replace(/./g, '*'),
43 | ]),
44 | );
45 | }
46 |
47 | function loadConfiguration() {
48 | const envFilePath = path.join(__dirname, RELATIVE_PATH_TO_AUTOGPT, '.env');
49 | if (fs.existsSync(envFilePath)) {
50 | const envFileContents = fs.readFileSync(envFilePath, 'utf8');
51 | const parsedEnvFileContents = dotenv.parse(envFileContents);
52 | return parsedEnvFileContents;
53 | }
54 | return {};
55 | }
56 |
57 | function loadConfiguration() {
58 | const envFilePath = path.join(__dirname, RELATIVE_PATH_TO_AUTOGPT, '.env');
59 | if (fs.existsSync(envFilePath)) {
60 | const envFileContents = fs.readFileSync(envFilePath, 'utf8');
61 | const parsedEnvFileContents = dotenv.parse(envFileContents);
62 | const result = OBFUSCATE_ENV_VARS_BEFORE_SENDING_TO_CLIENT
63 | ? obfuscateObjectProperties(parsedEnvFileContents)
64 | : parsedEnvFileContents;
65 | return result;
66 | }
67 | return {};
68 | }
69 |
70 | ////////////////
71 |
72 | wss.on('connection', (socket) => {
73 | console.log(`Client connected, sending command log`);
74 | updateClients([socket]);
75 | });
76 |
77 | function updateClients(sockets, latestChunk = null) {
78 | if (sockets.length === 0) {
79 | sockets = wss.clients;
80 | }
81 |
82 | if (sockets.length === 0) {
83 | console.warn('No clients connected, not sending anything.');
84 | return;
85 | }
86 |
87 | const data = JSON.stringify({
88 | fullConsoleOutput: state.consoleOutputLines.join('\n'),
89 | latestChunk,
90 | configuration,
91 | state: {
92 | activeProcessRunning: !!state.activeProcess,
93 | activeCommandString: state.activeCommandString,
94 | },
95 | });
96 |
97 | for (const client of sockets) {
98 | if (client.readyState === WebSocket.OPEN) {
99 | client.send(data);
100 | }
101 | }
102 | }
103 |
104 | const appendOutputChunkAndUpdateClients = (data) => {
105 | function deleteLastLineIfCarrotMovedToBeginning() {
106 | const lastSavedLine =
107 | state.consoleOutputLines[state.consoleOutputLines.length - 1];
108 | if (!lastSavedLine?.endsWith('\r')) {
109 | return false;
110 | }
111 |
112 | state.consoleOutputLines.pop();
113 | return true;
114 | }
115 |
116 | const lines = data.split('\n');
117 |
118 | if (lines.length == 0) {
119 | return;
120 | }
121 |
122 | const [firstLine, ...restOfLines] = lines;
123 |
124 | const deletedLastLine = deleteLastLineIfCarrotMovedToBeginning();
125 | if (deletedLastLine) {
126 | state.consoleOutputLines.push(firstLine);
127 | } else {
128 | state.consoleOutputLines[state.consoleOutputLines.length - 1] =
129 | state.consoleOutputLines[state.consoleOutputLines.length - 1] + firstLine;
130 | }
131 |
132 | if (restOfLines.length > 0) {
133 | for (const line of restOfLines) {
134 | deleteLastLineIfCarrotMovedToBeginning();
135 | state.consoleOutputLines.push(line);
136 | }
137 | }
138 |
139 | updateClients(wss.clients, data);
140 |
141 | // console.log(`
142 | // Added ${lines.length} lines from data: ${data} resulting in ${commandLog.length} lines.
143 | // Last line: ${commandLog[commandLog.length - 1]}.
144 | // Sending to ${wss.clients.size} clients.
145 | // Full thing: ${commandLog}`);
146 | };
147 |
148 | app.use(express.json());
149 |
150 | app.post('/execute', (req, res) => {
151 | const { command, inputs } = req.body;
152 |
153 | console.log(`Received command: ${command}`);
154 |
155 | if (!command) {
156 | return res.status(400).json({ error: 'Command is required.' });
157 | }
158 |
159 | if (state.activeProcess) {
160 | return res
161 | .status(400)
162 | .json({ error: 'Another command is already running.' });
163 | }
164 |
165 | state.consoleOutputLines.push('\n');
166 | state.consoleOutputLines.push('[[COMMAND]] ' + command);
167 | state.consoleOutputLines.push('\n');
168 |
169 | const options = { cwd: RELATIVE_PATH_TO_AUTOGPT };
170 |
171 | state.activeCommandString = command;
172 | state.activeProcess = exec(command, options, (error) => {
173 | if (error) {
174 | console.error(`Error executing command: ${error.message}`);
175 | }
176 | });
177 |
178 | state.activeProcess.stdout.on('data', appendOutputChunkAndUpdateClients);
179 | state.activeProcess.stderr.on('data', appendOutputChunkAndUpdateClients);
180 |
181 | state.activeProcess.stdout.on('data', (data) => process.stdout.write(data));
182 | state.activeProcess.stderr.on('data', (data) =>
183 | console.log(`💔 Error: ${data}`),
184 | );
185 |
186 | state.activeProcess.stderr.on('close', (code) => {
187 | console.log(`Process exited with code ${code}`);
188 | state.activeProcess = null;
189 | state.activeCommandString = null;
190 | updateClients(wss.clients);
191 | });
192 |
193 | if (inputs?.length > 0) {
194 | for (const input of inputs) {
195 | state.activeProcess.stdin.write(input + '\n');
196 | }
197 | }
198 |
199 | updateClients(wss.clients);
200 |
201 | res.status(200).json({ message: 'Command received, processing...' });
202 | });
203 |
204 | app.all('/clear', (req, res) => {
205 | state.consoleOutputLines.length = 0;
206 | updateClients(wss.clients);
207 |
208 | exec('cls');
209 |
210 | res.status(200).json({ message: 'Output cleared...' });
211 | });
212 |
213 | app.post('/input', (req, res) => {
214 | const { input } = req.body;
215 |
216 | if (input === undefined || input === null) {
217 | return res.status(400).json({ error: 'Input is required.' });
218 | }
219 |
220 | if (!state.activeProcess) {
221 | return res.status(400).json({ error: 'No command is currently running.' });
222 | }
223 |
224 | console.log(`Sending input to active process: ${input}`);
225 |
226 | state.activeProcess.stdin.write(input + '\n');
227 | res.status(200).json({ message: 'Input sent to the active command.' });
228 | });
229 |
230 | app.all('/kill', (req, res) => {
231 | if (!state.activeProcess) {
232 | return res.status(400).json({ error: 'No command is currently running.' });
233 | }
234 |
235 | console.log('Killing active process...');
236 |
237 | kill(state.activeProcess.pid, 'SIGTERM', (err) => {
238 | if (err) {
239 | console.error('Error while killing the process:', err);
240 | return res.status(500).json({ error: 'Failed to kill the process.' });
241 | }
242 | state.activeProcess = null;
243 | res.status(200).json({ message: 'Active command killed.' });
244 | });
245 | });
246 |
247 | app.post('/setenv', (req, res) => {
248 | const { key, value } = req.body;
249 |
250 | if (!key || !value) {
251 | return res.status(400).json({ error: 'Key and value are required.' });
252 | }
253 |
254 | const envFilePath = path.join(__dirname, RELATIVE_PATH_TO_AUTOGPT, '.env');
255 |
256 | // Check if the .env file exists, create it if not
257 | if (!fs.existsSync(envFilePath)) {
258 | fs.writeFileSync(envFilePath, '');
259 | }
260 |
261 | // Read the contents of the .env file and parse it
262 | const envFileContents = fs.readFileSync(envFilePath, 'utf8');
263 | const envVars = dotenv.parse(envFileContents);
264 |
265 | // Update the env variable or add a new one
266 | envVars[key] = value;
267 |
268 | // Convert the envVars object back to a string
269 | const updatedEnvFileContents = Object.entries(envVars)
270 | .map(([k, v]) => `${k}=${v}`)
271 | .join('\n');
272 |
273 | // Write the updated contents back to the .env file
274 | fs.writeFileSync(envFilePath, updatedEnvFileContents);
275 |
276 | // Reload the configuration
277 | configuration = loadConfiguration();
278 |
279 | // Update clients with the new configuration
280 | updateClients(wss.clients);
281 |
282 | res.status(200).json({ message: 'Environment variable set.' });
283 | });
284 |
285 | app.post('/applyprofile', (req, res) => {
286 | const { data } = req.body;
287 |
288 | if (!data) {
289 | return res.status(400).json({ error: 'Data is required.' });
290 | }
291 |
292 | try {
293 | const yamlString = yaml.dump(data);
294 | const pathToSettingsFile = path.join(
295 | __dirname,
296 | RELATIVE_PATH_TO_AUTOGPT,
297 | PATH_TO_AI_SETTINGS_FILE,
298 | );
299 | fs.writeFileSync(pathToSettingsFile, yamlString, 'utf8');
300 | res.status(200).json({ message: 'AI profile saved to ai_settings.yml.' });
301 | } catch (error) {
302 | res
303 | .status(500)
304 | .json({ error: `Error saving AI profile: ${error.message}` });
305 | }
306 | });
307 |
308 | app.use((error, req, res, next) => {
309 | console.error(`Error: ${error.message}`);
310 | res.status(error.status || 500).json({ error: error.message });
311 | });
312 |
313 | const server = app.listen(PORT, () => {
314 | console.log(`Server is running on port ${PORT}`);
315 | });
316 |
317 | // Handle the upgrade request
318 | server.on('upgrade', (request, socket, head) => {
319 | wss.handleUpgrade(request, socket, head, (ws) => {
320 | wss.emit('connection', ws, request);
321 | });
322 | });
323 |
324 | //////
325 |
--------------------------------------------------------------------------------
/apps/frontend/.env.example:
--------------------------------------------------------------------------------
1 | BACKEND_URL=http://localhost:2200
2 |
--------------------------------------------------------------------------------
/apps/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Auto-GPT WebUI
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.1.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --host 0.0.0.0",
8 | "start": "vite --host 0.0.0.0",
9 | "build": "tsc && vite build",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@chakra-ui/icons": "^2.0.18",
14 | "@chakra-ui/react": "^2.5.5",
15 | "@emotion/react": "^11.10.6",
16 | "@emotion/styled": "^11.10.6",
17 | "@formkit/auto-animate": "^1.0.0-beta.6",
18 | "ansi-to-html": "^0.7.2",
19 | "dotenv": "^16.0.3",
20 | "framer-motion": "^10.11.2",
21 | "react": "^18.2.0",
22 | "react-dom": "^18.2.0",
23 | "react-icons": "^4.8.0",
24 | "zustand": "^4.3.7"
25 | },
26 | "devDependencies": {
27 | "@modyfi/vite-plugin-yaml": "^1.0.4",
28 | "@types/react": "^18.0.28",
29 | "@types/react-dom": "^18.0.11",
30 | "@vitejs/plugin-react": "^3.1.0",
31 | "typescript": "^4.9.3",
32 | "vite": "^4.2.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/apps/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choephix/auto-gpt-webui/9475d298d10f71c4ec3b09f323161c1fa3770b7c/apps/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/apps/frontend/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choephix/auto-gpt-webui/9475d298d10f71c4ec3b09f323161c1fa3770b7c/apps/frontend/public/favicon.png
--------------------------------------------------------------------------------
/apps/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choephix/auto-gpt-webui/9475d298d10f71c4ec3b09f323161c1fa3770b7c/apps/frontend/public/logo192.png
--------------------------------------------------------------------------------
/apps/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/choephix/auto-gpt-webui/9475d298d10f71c4ec3b09f323161c1fa3770b7c/apps/frontend/public/logo512.png
--------------------------------------------------------------------------------
/apps/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/apps/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/apps/frontend/src/App.animations.css:
--------------------------------------------------------------------------------
1 | @keyframes fade-in {
2 | from {
3 | opacity: 0;
4 | }
5 | }
6 | .withFadeInAnimation {
7 | animation-name: fade-in;
8 | animation-duration: 0.47s;
9 | }
10 |
11 | @keyframes pop-in {
12 | from {
13 | opacity: 0;
14 | transform: scale(0.8);
15 | }
16 | }
17 | .withPopInAnimation {
18 | animation-name: pop-in;
19 | animation-duration: 0.47s;
20 | }
21 |
22 | @keyframes slide-right {
23 | from {
24 | opacity: 0;
25 | transform: translateX(-40px);
26 | }
27 | }
28 | .withSlideRightAnimation {
29 | animation-name: slide-right;
30 | animation-duration: 0.47s;
31 | }
32 |
33 | @keyframes slide-down {
34 | from {
35 | opacity: 0;
36 | transform: translateY(-20px);
37 | }
38 | }
39 | .withSlideDownAnimation {
40 | animation-name: slide-down;
41 | animation-duration: 0.47s;
42 | }
43 |
--------------------------------------------------------------------------------
/apps/frontend/src/App.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,400;0,700;1,400&family=Overpass+Mono:wght@400;700&display=swap');
2 |
3 | :root {
4 | --app-background-color: #83ebc8;
5 | --app-fade-color: #97ebcf;
6 | --segment-background-color: #ffffff;
7 | --insignificant-color: #ffff;
8 |
9 | --text-color: #000000;
10 | --output-segment-bg-color: #080c14c0;
11 | --output-segment-border-color: #999e;
12 | }
13 |
14 | .dark-mode {
15 | --app-background-color: #164636;
16 | --app-fade-color: #0a2b20;
17 | --segment-background-color: #080c14c0;
18 | --insignificant-color: #455;
19 |
20 | --text-color: #ffffff;
21 | --output-segment-bg-color: #080c14c0;
22 | --output-segment-border-color: #fffe;
23 | }
24 |
25 | body {
26 | margin: 0;
27 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
28 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
29 | sans-serif;
30 | -webkit-font-smoothing: antialiased;
31 | -moz-osx-font-smoothing: grayscale;
32 | }
33 |
34 | .AppBackground {
35 | background-color: var(--app-background-color);
36 | box-shadow: inset 0 0 256px 0 #0008, inset 0 0 32px 0 #0088;
37 | transition: background-color 0.17s ease-out !important;
38 | }
39 |
40 | .OutputSegmentBox {
41 | background-color: var(--segment-background-color);
42 | transition: background-color 0.17s ease-out !important;
43 |
44 | border: 2px solid #fffe;
45 | border: 1px solid #999e;
46 | border-radius: 1rem;
47 | padding: 20px 28px;
48 | margin: 12px 0;
49 |
50 | text-align: start;
51 |
52 | overflow: hidden;
53 | }
54 |
55 | .OutputSegmentBox pre {
56 | /* font-family: 'Azeret Mono', monospace !important; */
57 | font-family: 'Overpass Mono', monospace !important;
58 |
59 | /* color: white; */
60 | font-size: 14px;
61 | font-family: monospace;
62 | white-space: pre-wrap;
63 | margin-bottom: 12px;
64 |
65 | filter: brightness(1.2);
66 | font-weight: bold;
67 | }
68 |
69 | .OutputCommandHeadingBox {
70 | background: #ffd000;
71 | border: 1px solid #999e;
72 | overflow: hidden;
73 | }
74 |
75 | .OutputCommandHeadingBox > * {
76 | animation-name: slide-right;
77 | animation-duration: 0.47s;
78 | }
79 |
80 | .ScrollToBottomButton {
81 | background-color: var(--insignificant-color) !important;
82 | }
83 |
84 | /* Add this to your CSS file */
85 | .TheFooter {
86 | position: fixed;
87 | bottom: 0;
88 | left: 0;
89 | right: 0;
90 | height: 64px;
91 | display: flex;
92 | justify-content: center;
93 | align-items: center;
94 | z-index: 10;
95 | padding-left: 4px;
96 | padding-right: 4px;
97 | pointer-events: none;
98 | background-color: var(--app-fade-color);
99 | }
100 |
101 | .TheFooter:before {
102 | content: '';
103 | position: absolute;
104 | top: -100px;
105 | left: 0;
106 | right: 0;
107 | height: 100px;
108 | background-repeat: repeat-x;
109 | background-position: bottom;
110 | background-size: 100% 100px;
111 | background: linear-gradient(to bottom, #fff0 0%, var(--app-fade-color) 100%);
112 | }
113 |
114 | .FancyBox {
115 | backdrop-filter: blur(4px);
116 | -webkit-backdrop-filter: blur(4px);
117 | box-shadow: 0 6px 16px 0 #0005;
118 | animation-name: pop-in;
119 | animation-duration: 0.47s;
120 | animation-timing-function: ease-out;
121 | }
122 |
123 | .glass {
124 | backdrop-filter: blur(8px);
125 | }
126 |
127 | .withShadow {
128 | box-shadow: 0 6px 16px 0 #0045;
129 | }
130 |
131 | .withSmallShadow {
132 | box-shadow: 0 3px 8px 0 #0002;
133 | }
134 |
135 | .fancyFont {
136 | font-family: 'pragmatapro-fraktur', serif;
137 | font-weight: 400;
138 | font-style: normal;
139 | }
140 |
141 | button {
142 | pointer-events: all;
143 | }
144 |
145 | .CommandStip {
146 | background-image: linear-gradient(180deg, #455555, #546464);
147 | color: #fffd;
148 | font-weight: bold;
149 | text-shadow: 0 0 3px #000, 0 0 8px #0009;
150 | border: 1px solid #0004;
151 | transition: background-color 0.2s ease-in-out,
152 | background-image 0.2s ease-in-out, color 0.2s ease-in-out;
153 | }
154 |
155 | .CommandStip.continuous {
156 | background: linear-gradient(180deg, #a51225, #861221);
157 | }
158 |
159 | .CommandStip.active {
160 | background-color: #5ba828;
161 | background-image: repeating-linear-gradient(
162 | -45deg,
163 | transparent,
164 | transparent 20px,
165 | #367a08 20px,
166 | #367a08 40px
167 | );
168 | background-size: 200% 200%;
169 | animation: barberpole 3s linear infinite;
170 | }
171 |
172 | .CommandStip.active.continuous {
173 | background-color: #a90f2e;
174 | background-image: repeating-linear-gradient(
175 | -45deg,
176 | transparent,
177 | transparent 20px,
178 | Crimson 20px,
179 | Crimson 40px
180 | );
181 | }
182 |
183 | @keyframes barberpole {
184 | 100% {
185 | background-position: -56.568px 0;
186 | }
187 | }
188 |
189 | /******************************************************
190 | * CSS3 Pretty Scrollbar
191 | ******************************************************/
192 |
193 | .pretty-scrollbar::-webkit-scrollbar-track {
194 | background-color: #0001;
195 | }
196 |
197 | .pretty-scrollbar::-webkit-scrollbar {
198 | width: 8px;
199 | background-color: #0000;
200 | }
201 |
202 | .pretty-scrollbar::-webkit-scrollbar-thumb {
203 | border-radius: 10px;
204 | background-color: #3336;
205 | }
206 |
--------------------------------------------------------------------------------
/apps/frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react';
2 |
3 | import { ChevronDownIcon } from '@chakra-ui/icons';
4 | import {
5 | Box,
6 | Button,
7 | Center,
8 | Flex,
9 | Modal,
10 | ModalBody,
11 | ModalContent,
12 | ModalOverlay,
13 | Spinner,
14 | Text,
15 | useColorMode,
16 | } from '@chakra-ui/react';
17 |
18 | import { TheAreaAtTheBottom } from './components/TheAreaAtTheBottom';
19 | import { TheAreaAtTheTop } from './components/TheAreaAtTheTop';
20 | import { TheAreaInTheMiddle } from './components/TheAreaInTheMiddle';
21 | import { ServicesRunner } from './components/TheHiddenServiceRunner';
22 | import { useContextStore } from './store/useContextStore';
23 |
24 | import './App.css';
25 | import './App.animations.css';
26 |
27 | function App() {
28 | const { socket } = useContextStore();
29 |
30 | const { colorMode } = useColorMode();
31 |
32 | return (
33 |
38 |
39 | void null}>
40 |
41 |
42 |
43 |
44 |
45 | Connecting to server...
46 |
47 |
48 |
49 |
50 | {socket && (
51 | <>
52 |
53 |
54 |
55 | >
56 | )}
57 |
58 | );
59 | }
60 |
61 | function TheHeader() {
62 | return (
63 |
76 |
77 |
78 | );
79 | }
80 |
81 | function TheFooter() {
82 | return (
83 |
96 |
97 |
98 | );
99 | }
100 |
101 | function TheMiddle() {
102 | const consoleLogContainerRef = useRef(null);
103 | const { outputSegments } = useContextStore();
104 | const [showScrollButton, setShowScrollButton] = useState(false);
105 |
106 | function scrollToBottom() {
107 | const container = consoleLogContainerRef.current;
108 |
109 | if (!container) {
110 | console.log('no container');
111 | return;
112 | }
113 |
114 | const maxScrollTop = container.scrollHeight - container.clientHeight;
115 | container.scrollTo({ top: maxScrollTop, behavior: 'smooth' });
116 |
117 | setShowScrollButton(false);
118 | }
119 |
120 | function handleScroll() {
121 | const container = consoleLogContainerRef.current;
122 |
123 | if (!container) {
124 | console.log('no container');
125 | return;
126 | }
127 |
128 | const maxScrollTop = container.scrollHeight - container.clientHeight;
129 | const delta = maxScrollTop - container.scrollTop;
130 |
131 | const maxDeltaForAutoScroll = container.clientHeight * 0.5;
132 | setShowScrollButton(delta > maxDeltaForAutoScroll);
133 | }
134 |
135 | useEffect(() => {
136 | const container = consoleLogContainerRef.current;
137 |
138 | if (container) {
139 | container.addEventListener('scroll', handleScroll);
140 | }
141 |
142 | return () => {
143 | if (container) {
144 | container.removeEventListener('scroll', handleScroll);
145 | }
146 | };
147 | }, []);
148 |
149 | useEffect(() => {
150 | const container = consoleLogContainerRef.current;
151 |
152 | if (!container) {
153 | console.log('no container');
154 | return;
155 | }
156 |
157 | const maxScrollTop = container.scrollHeight - container.clientHeight;
158 | const delta = maxScrollTop - container.scrollTop;
159 | const shouldAutoScroll = delta < 10 || maxScrollTop <= 0;
160 |
161 | if (shouldAutoScroll) {
162 | scrollToBottom();
163 | }
164 | }, [outputSegments]);
165 |
166 | return (
167 | <>
168 | {showScrollButton && (
169 |
176 | }
180 | rightIcon={}
181 | className='ScrollToBottomButton withPopInAnimation withShadow'
182 | pointerEvents='all'
183 | rounded='full'
184 | size='lg'
185 | >
186 | Scroll to bottom
187 |
188 |
189 | )}
190 |
199 |
200 |
201 | >
202 | );
203 | }
204 |
205 | export default App;
206 |
--------------------------------------------------------------------------------
/apps/frontend/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/AboutInfoModal.tsx:
--------------------------------------------------------------------------------
1 | import { InfoOutlineIcon } from '@chakra-ui/icons';
2 | import {
3 | useDisclosure,
4 | Button,
5 | Modal,
6 | ModalOverlay,
7 | ModalContent,
8 | ModalHeader,
9 | ModalCloseButton,
10 | ModalBody,
11 | Text,
12 | IconButton,
13 | Spacer,
14 | Link,
15 | Divider,
16 | } from '@chakra-ui/react';
17 |
18 | export function AboutInfoModal() {
19 | const { isOpen, onOpen, onClose } = useDisclosure();
20 | return (
21 | <>
22 | }
29 | onClick={onOpen}
30 | />
31 |
32 |
33 |
34 |
35 | About this project
36 |
37 |
38 |
39 | Auto-GPT WebUI is a frontend web application built to
40 | interact seamlessly with the{' '}
41 |
46 | Auto-GPT
47 | {' '}
48 | in the backend.
49 |
50 |
51 | 🌟 This project wouldn't be possible without the amazing work of{' '}
52 |
57 | Torantulino
58 |
59 | , the author of the{' '}
60 |
65 | original project.
66 | {' '}
67 |
68 |
69 | This project was built primarily for the developer's personal
70 | amusement, so availability to maintain and update the project may
71 | be limited. However, community involvement and contributions are
72 | always welcome and highly appreciated.
73 |
74 |
75 |
76 |
77 |
78 | Thank you for using Auto-GPT WebUI, and I hope you find it
79 | helpful!
80 | {' '}
81 | 🤍
82 |
83 |
84 |
85 |
86 |
87 | >
88 | );
89 | }
90 | import React from 'react';
91 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/CommandStringBar.tsx:
--------------------------------------------------------------------------------
1 | import { Center, Text, useColorModeValue } from '@chakra-ui/react';
2 | import { useAutoGPTStarter } from '../hooks/useAutoGPTStarter';
3 | import { useContextStore } from '../store/useContextStore';
4 | import { useSettingsStore } from '../store/useSettingsStore';
5 |
6 | export function CommandStringBar() {
7 | const { backendState } = useContextStore();
8 | const { command: aiStarterCommand } = useAutoGPTStarter();
9 | const { autoContinuous } = useSettingsStore();
10 |
11 | const commandStringToDisplay =
12 | backendState?.activeCommandString ?? aiStarterCommand;
13 | const comandIsRunning = !!backendState?.activeProcessRunning;
14 |
15 | return (
16 |
27 |
28 | {commandStringToDisplay}
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/EditAIProfileModal.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Button,
3 | FormControl,
4 | FormLabel,
5 | Input,
6 | Modal,
7 | ModalBody,
8 | ModalCloseButton,
9 | ModalContent,
10 | ModalFooter,
11 | ModalHeader,
12 | ModalOverlay,
13 | Textarea,
14 | VStack,
15 | } from '@chakra-ui/react';
16 | import React, { useEffect, useState } from 'react';
17 | import { AIProfile } from '../entities/AIProfile';
18 |
19 | interface EditAIProfileModalProps {
20 | isOpen: boolean;
21 | onClose: () => void;
22 | onSave?: (profile: AIProfile) => void;
23 | initialValues: AIProfile;
24 | isCreatingNewProfile: boolean;
25 | }
26 |
27 | export const EditAIProfileModal: React.FC = ({
28 | isOpen,
29 | onClose,
30 | onSave,
31 | initialValues,
32 | isCreatingNewProfile,
33 | }) => {
34 | const [profile, setProfile] = useState(initialValues);
35 |
36 | useEffect(() => {
37 | setProfile({ ...initialValues });
38 | updateGoals();
39 | }, [initialValues.uid]);
40 |
41 | const updateGoals = (modifyBoforeSaving?: (goals: string[]) => string[]) => {
42 | let goals = [...profile.goals];
43 | if (modifyBoforeSaving) {
44 | goals = modifyBoforeSaving(goals);
45 | }
46 | while (goals[goals.length - 1] === '') goals.pop();
47 | goals.push('');
48 | setProfile({ ...profile, goals });
49 | };
50 |
51 | const setGoal = (index: number, value: string) => {
52 | updateGoals((goals) => {
53 | goals[index] = value;
54 | return goals;
55 | });
56 | };
57 |
58 | const handleSave = () => {
59 | profile.goals = profile.goals.filter((goal) => goal !== '');
60 | onSave?.(profile);
61 | };
62 |
63 | return (
64 |
65 |
66 |
67 |
68 | {isCreatingNewProfile ? 'New' : 'Edit'} AI Profile
69 |
70 |
71 |
72 |
73 |
74 | Name your AI
75 |
78 | setProfile({ ...profile, name: e.target.value })
79 | }
80 | placeholder='Entrepreneur-GPT'
81 | />
82 |
83 |
84 | Describe your AI's Role
85 |
94 |
95 | Goals for your AI
96 |
97 | {profile.goals.map((goal, index) => (
98 |
107 |
108 |
109 |
110 | {onSave && (
111 | <>
112 |
113 |
116 |
117 | >
118 | )}
119 |
120 |
121 | );
122 | };
123 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/ListOfAIProfiles.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | AddIcon,
3 | CheckCircleIcon,
4 | CheckIcon,
5 | DeleteIcon,
6 | EditIcon,
7 | } from '@chakra-ui/icons';
8 | import {
9 | Box,
10 | Button,
11 | ButtonGroup,
12 | Center,
13 | Container,
14 | Divider,
15 | Grid,
16 | GridItem,
17 | IconButton,
18 | Spacer,
19 | Text,
20 | VStack,
21 | useDisclosure,
22 | } from '@chakra-ui/react';
23 | import { useAutoAnimate } from '@formkit/auto-animate/react';
24 | import { useState } from 'react';
25 | import { AIProfile } from '../entities/AIProfile';
26 | import { useAutoGPTStarter } from '../hooks/useAutoGPTStarter';
27 | import { useSettingsStore } from '../store/useSettingsStore';
28 | import { getPseudoRandomColorFromString } from '../utils/getPseudoRandomColorFromString';
29 | import { getRandomProfileDataFiller } from '../utils/getRandomProfileDataFiller';
30 | import { EditAIProfileModal } from './EditAIProfileModal';
31 |
32 | interface AIProfileCardProps {
33 | profile: AIProfile;
34 | ctrl: {
35 | delete: () => void;
36 | choose: () => void;
37 | edit: () => void;
38 | };
39 | }
40 |
41 | function MenuCard({ children, ...rest }: any) {
42 | return (
43 |
63 | {children}
64 |
65 | );
66 | }
67 |
68 | function AIProfileCard({ profile, ctrl }: AIProfileCardProps) {
69 | return (
70 |
71 |
77 |
85 |
86 | {profile.name}
87 |
88 |
89 | {profile.role}
90 |
91 |
92 |
93 | {profile.goals.length} goal(s)
94 |
95 |
96 |
97 |
98 | }
101 | aria-label='Delete AI Profile'
102 | onClick={ctrl.delete}
103 | variant='ghost'
104 | colorScheme='red'
105 | />
106 | }
109 | aria-label='Delete AI Profile'
110 | onClick={ctrl.choose}
111 | flexGrow={1}
112 | // variant='solid'
113 | variant='ghost'
114 | colorScheme='green'
115 | >
116 | Start this AI
117 |
118 | }
121 | aria-label='Delete AI Profile'
122 | onClick={ctrl.edit}
123 | variant='ghost'
124 | colorScheme='blue'
125 | />
126 |
127 |
128 |
137 |
141 |
142 |
143 | );
144 | }
145 |
146 | function AddAIProfileCard({ onClick }: { onClick: () => void }) {
147 | return (
148 |
149 |
150 |
151 |
152 | Add AI Profile
153 |
154 |
155 | );
156 | }
157 |
158 | export function ListOfAIProfiles({
159 | showAddButton = false,
160 | }: {
161 | showAddButton?: boolean;
162 | }) {
163 | const { aiProfiles, setAiProfiles } = useSettingsStore();
164 | const {
165 | isOpen: isEditorOpen,
166 | onOpen: onEditorOpen,
167 | onClose: onEditorClose,
168 | } = useDisclosure();
169 |
170 | const [parent] = useAutoAnimate({
171 | duration: 200,
172 | });
173 |
174 | const [selectedProfile, setSelectedProfile] = useState(
175 | null,
176 | );
177 | const { sendStartCommandWithProfile } = useAutoGPTStarter();
178 |
179 | const ensureProfileNameIsUnique = (profile: AIProfile) => {
180 | const existingProfile = aiProfiles.find((p) => p.name === profile.name);
181 | if (existingProfile) {
182 | let i = 1;
183 | while (true) {
184 | const newName = `${profile.name} (${i})`.trim();
185 | const existingProfile = aiProfiles.find((p) => p.name === newName);
186 | if (!existingProfile) {
187 | profile.name = newName;
188 | break;
189 | }
190 | i++;
191 | }
192 | }
193 | };
194 |
195 | const handleProfileSave = (profile: AIProfile) => {
196 | const isExistingProfile = aiProfiles.find((p) => p.uid === profile.uid);
197 | if (isExistingProfile) {
198 | setAiProfiles(
199 | aiProfiles.map((p) => (p.uid === profile.uid ? profile : p)),
200 | );
201 | } else {
202 | ensureProfileNameIsUnique(profile);
203 | setAiProfiles([profile, ...aiProfiles]);
204 | }
205 | onEditorClose();
206 | };
207 |
208 | const handleDeleteAIProfile = (index: number) => {
209 | const newProfiles = [...aiProfiles];
210 | newProfiles.splice(index, 1);
211 | setAiProfiles(newProfiles);
212 | };
213 |
214 | return (
215 | <>
216 |
222 | {showAddButton && (
223 |
224 | {
226 | const initialValues = getRandomProfileDataFiller();
227 | setSelectedProfile(initialValues);
228 | onEditorOpen();
229 | }}
230 | />
231 |
232 | )}
233 | {aiProfiles.map((profile, index) => (
234 |
235 | {
240 | handleDeleteAIProfile(index);
241 | },
242 | choose: () => {
243 | sendStartCommandWithProfile(profile);
244 | },
245 | edit: () => {
246 | setSelectedProfile(profile);
247 | onEditorOpen();
248 | },
249 | }}
250 | />
251 |
252 | ))}
253 |
254 | {selectedProfile && (
255 | p.uid === selectedProfile.uid)
263 | }
264 | />
265 | )}
266 | >
267 | );
268 | }
269 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/OutputSegmentsList.tsx:
--------------------------------------------------------------------------------
1 | import { CheckIcon } from '@chakra-ui/icons';
2 | import { Box, Button, Progress, Tag, Wrap } from '@chakra-ui/react';
3 | import { useState } from 'react';
4 | import { useApiService } from '../hooks/useApiService';
5 | import { OutputSegment, useContextStore } from '../store/useContextStore';
6 |
7 | export function OutputSegmentsList() {
8 | const { socket, outputSegments, backendState } = useContextStore();
9 |
10 | if (!socket) {
11 | return null;
12 | }
13 |
14 | if (!outputSegments.length) {
15 | return null;
16 | }
17 |
18 | const lastSegment = outputSegments[outputSegments.length - 1];
19 | const shouldShowProgress =
20 | backendState?.activeProcessRunning && !lastSegment.expectedUserInteraction;
21 |
22 | return (
23 | <>
24 | {outputSegments.map((segment, index) => {
25 | if (segment.lines.length === 0) {
26 | return null;
27 | }
28 |
29 | const text = segment.lines.join('\n').trim();
30 | if (text.startsWith(`[[COMMAND]]`)) {
31 | const commandString = text.replace(`[[COMMAND]]`, '').trim();
32 | return ;
33 | } else {
34 | return ;
35 | }
36 | })}
37 | {shouldShowProgress && (
38 |
46 | )}
47 | >
48 | );
49 | }
50 |
51 | function CommandHeadingBox({ content }: { content: string }) {
52 | return (
53 |
66 | {content}
67 |
68 | );
69 | }
70 |
71 | function SegmentBox({ segment }: { segment: OutputSegment }) {
72 | const { backendState } = useContextStore();
73 |
74 | const text = segment.lines.join('\n').trim();
75 | if (!text) {
76 | return null;
77 | }
78 |
79 | const showInputBar =
80 | segment.isLastSegment && backendState?.activeProcessRunning;
81 |
82 | return (
83 |
95 |
96 | {showInputBar && }
97 |
98 | );
99 | }
100 |
101 | function SegmentBoxInputBar({ segment }: { segment: OutputSegment }) {
102 | const { expectedUserInteraction } = segment;
103 | const apiService = useApiService();
104 |
105 | const [forceShowInputBar, setForceShowInputBar] = useState(false);
106 |
107 | function sendInput(input: string) {
108 | apiService.sendInput(input);
109 | }
110 |
111 | function promptAndSendInput() {
112 | const value = prompt(`I'm listening for input. Enter a value:`);
113 | if (value !== null) {
114 | return apiService.sendInput(value);
115 | }
116 | }
117 |
118 | function YesNoAndInputBar(props: { redNo?: boolean }) {
119 | return (
120 |
121 | }
126 | onClick={() => sendInput('y')}
127 | >
128 | Yes
129 |
130 |
139 |
147 | {/* */}
148 |
156 |
157 | );
158 | }
159 |
160 | if (
161 | expectedUserInteraction === 'yesno' ||
162 | expectedUserInteraction === 'text'
163 | ) {
164 | return ;
165 | }
166 |
167 | if (forceShowInputBar) {
168 | return (
169 | <>
170 |
178 |
179 | >
180 | );
181 | }
182 |
183 | return (
184 |
187 | );
188 | }
189 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/SettingsDrawer.tsx:
--------------------------------------------------------------------------------
1 | import { HamburgerIcon } from '@chakra-ui/icons';
2 | import {
3 | Drawer,
4 | DrawerBody,
5 | DrawerCloseButton,
6 | DrawerContent,
7 | DrawerHeader,
8 | DrawerOverlay,
9 | Heading,
10 | IconButton,
11 | useDisclosure,
12 | } from '@chakra-ui/react';
13 | import React from 'react';
14 | import { SettingsDrawerContent } from './SettingsDrawerContent';
15 |
16 | export function SettingsDrawer() {
17 | const { isOpen, onOpen, onClose } = useDisclosure();
18 | const btnRef = React.useRef();
19 |
20 | return (
21 | <>
22 | }
30 | onClick={onOpen}
31 | />
32 |
39 |
40 |
41 |
42 |
43 |
44 | Auto-GPT WebUI
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | >
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/SettingsDrawerContent.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | AlertDescription,
4 | AlertIcon,
5 | AlertTitle,
6 | Box,
7 | Button,
8 | Code,
9 | Divider,
10 | FormControl,
11 | FormLabel,
12 | Heading,
13 | IconButton,
14 | Spacer,
15 | Switch,
16 | VStack,
17 | useColorMode,
18 | } from '@chakra-ui/react';
19 | import { BackendConfigurationKeys } from '../config/BackendConfigurationKeys';
20 | import { useApiService } from '../hooks/useApiService';
21 | import { useAutoGPTStarter } from '../hooks/useAutoGPTStarter';
22 | import { useContextStore } from '../store/useContextStore';
23 | import { useSettingsStore } from '../store/useSettingsStore';
24 | import { EditIcon } from '@chakra-ui/icons';
25 |
26 | export function SettingsDrawerContent() {
27 | return (
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 |
40 | function SettingToggles() {
41 | const {
42 | autoContinuous,
43 | autoDebugMode,
44 | autoWithOnlyGPT3,
45 | setAutoContinuous,
46 | setAutoDebugMode,
47 | setAutoWithOnlyGPT3,
48 | } = useSettingsStore();
49 | const { backendState } = useContextStore();
50 |
51 | const { colorMode, toggleColorMode } = useColorMode();
52 | const isDarkMode = colorMode === 'dark';
53 |
54 | function SettingControl_Toggle({
55 | label,
56 | value,
57 | setValue,
58 | disableOnActiveProcess,
59 | }: {
60 | label: string;
61 | value: boolean;
62 | setValue: (value: boolean) => void;
63 | disableOnActiveProcess?: boolean;
64 | }) {
65 | return (
66 |
71 |
72 | {label}
73 |
74 | setValue(!value)}
81 | />
82 |
83 | );
84 | }
85 |
86 | return (
87 | <>
88 | Appearance
89 |
90 |
95 |
96 | Backend script settings
97 |
98 |
103 |
108 |
113 | >
114 | );
115 | }
116 |
117 | function EnvVars() {
118 | const apiService = useApiService();
119 | const { backendConfiguration } = useContextStore();
120 |
121 | if (!backendConfiguration) return null;
122 |
123 | type BackendConfigurationKey = keyof typeof backendConfiguration;
124 |
125 | async function promptAndUpdateEnvVariable(key: string) {
126 | const value = prompt(`Enter value for ${key}:`);
127 | if (value !== null) {
128 | return await apiService.setEnvVariable(key, value);
129 | }
130 | }
131 |
132 | function renderAlert(
133 | key: string,
134 | status: 'success' | 'error' | 'info' | 'warning',
135 | isRequired: boolean,
136 | isSetUp: boolean,
137 | ) {
138 | function getTitleSuffix() {
139 | if (isSetUp)
140 | return (
141 |
142 | <>is set up.>
143 |
144 | );
145 | if (isRequired) return <>is missing!>;
146 | return (
147 | <>
148 |
149 | isn't set up (optional)
150 |
151 | >
152 | );
153 | }
154 |
155 | return (
156 |
163 |
164 |
165 | {key}
{getTitleSuffix()}
166 |
167 |
168 | }
170 | aria-label={'edit-env-var'}
171 | variant='ghost'
172 | onClick={() => promptAndUpdateEnvVariable(key)}
173 | >
174 |
175 | );
176 | }
177 |
178 | function getKeyRenderPriority(key: BackendConfigurationKey) {
179 | const isSetUp = Boolean(backendConfiguration?.[key]);
180 | const isRequired = BackendConfigurationKeys[key] === 'required';
181 | return isSetUp ? -1 : isRequired ? 1 : 0;
182 | }
183 |
184 | return (
185 | <>
186 | Environment Variables
187 |
188 | {Object.entries(BackendConfigurationKeys)
189 | .sort(([keyA], [keyB]) => {
190 | const a = getKeyRenderPriority(keyA as BackendConfigurationKey);
191 | const b = getKeyRenderPriority(keyB as BackendConfigurationKey);
192 | return b - a;
193 | })
194 | .map(([key, need]) => {
195 | const isSetUp = Boolean(
196 | backendConfiguration?.[key as BackendConfigurationKey],
197 | );
198 | const isRequired = need === 'required';
199 |
200 | if (isSetUp) {
201 | return renderAlert(key, 'success', isRequired, true);
202 | } else if (!isRequired) {
203 | return renderAlert(key, 'info', false, false);
204 | } else {
205 | return renderAlert(key, 'error', true, false);
206 | }
207 | })}
208 | >
209 | );
210 | }
211 |
212 | function DebuggingView() {
213 | const apiService = useApiService();
214 | const { backendState, backendConfiguration } = useContextStore();
215 | const { command } = useAutoGPTStarter();
216 |
217 | const debuggingShellCommands = [
218 | 'ls -la',
219 | `bash ../scripts/mock-spinner.sh`,
220 | `bash ../scripts/mock-user-input.sh`,
221 | `bash ../scripts/mock-continuous.sh`,
222 | `pip install -r requirements.txt`,
223 | command, // `python scripts/main.py` + args,
224 | ];
225 |
226 | function runCommand(command: string) {
227 | apiService.startCommand(command);
228 | }
229 |
230 | async function updateEnvVariable(key: string) {
231 | const value = prompt(`Enter value for ${key}:`);
232 | if (value !== null) {
233 | return await apiService.setEnvVariable(key, value);
234 | }
235 | }
236 |
237 | const ButtonList = (props: {
238 | actions?: [string, () => void][];
239 | children?: React.ReactNode;
240 | }) => {
241 | return (
242 |
243 | {props.actions?.map(([label, func], index) => (
244 |
258 | ))}
259 |
260 | {props.children || null}
261 |
262 | );
263 | };
264 |
265 | const backendConfigurationExistance =
266 | backendConfiguration &&
267 | Object.fromEntries(
268 | Object.entries(backendConfiguration).map(([key, value]) => [
269 | key,
270 | Boolean(value),
271 | ]),
272 | );
273 |
274 | return (
275 | <>
276 | Debugging
277 |
278 | Shell Commands
279 | [
281 | `exec:\n${action}`,
282 | () => runCommand(action),
283 | ])}
284 | />
285 |
286 | Backend State
287 |
288 | {JSON.stringify(backendState, null, 2)}
289 |
290 |
291 | Backend Configuration
292 |
293 | {JSON.stringify(backendConfigurationExistance, null, 2)}
294 |
295 | >
296 | );
297 | }
298 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/StartNewProcessMenu.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Heading } from '@chakra-ui/react';
2 | import { ListOfAIProfiles } from './ListOfAIProfiles';
3 |
4 | export function StartNewProcessMenu() {
5 | return (
6 | <>
7 |
13 |
14 | Select a profile to start a new process.
15 |
16 |
17 |
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/TheAreaAtTheBottom.tsx:
--------------------------------------------------------------------------------
1 | import { Button, ButtonGroup, Flex, Text } from '@chakra-ui/react';
2 | import { useApiService } from '../hooks/useApiService';
3 | import { useContextStore } from '../store/useContextStore';
4 | import { AboutInfoModal } from './AboutInfoModal';
5 | import { SettingsDrawer } from './SettingsDrawer';
6 |
7 | export function TheAreaAtTheBottom() {
8 | const { socket, backendState, outputSegments } = useContextStore();
9 | const apiService = useApiService();
10 |
11 | if (!socket || !backendState) {
12 | return null;
13 | }
14 |
15 | function killProcess() {
16 | apiService.killProcess();
17 | }
18 |
19 | function clearOutput() {
20 | apiService.clearOutput();
21 | }
22 |
23 | async function sendInput() {
24 | const input = prompt('Enter input:');
25 | if (input !== null) {
26 | apiService.sendInput(input);
27 | }
28 | }
29 |
30 | return (
31 | <>
32 |
41 |
42 |
43 |
50 |
57 | {backendState?.activeProcessRunning ? (
58 | <>
59 |
62 |
65 | >
66 | ) : outputSegments?.length > 0 ? (
67 | <>
68 |
71 | >
72 | ) : (
73 | ☝ Select an AI Profile to fire up Auto-GPT with. ☝
74 | )}
75 |
76 |
77 |
78 |
79 |
80 | >
81 | );
82 | }
83 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/TheAreaAtTheTop.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Alert,
3 | AlertDescription,
4 | AlertIcon,
5 | AlertTitle,
6 | Box,
7 | Container,
8 | Spacer,
9 | VStack,
10 | } from '@chakra-ui/react';
11 | import { requiredBackendConfigurationKeys } from '../config/BackendConfigurationKeys';
12 | import { useApiService } from '../hooks/useApiService';
13 | import { useContextStore } from '../store/useContextStore';
14 | import { CommandStringBar } from './CommandStringBar';
15 |
16 | export function TheAreaAtTheTop() {
17 | return ;
18 | }
19 |
20 | function BadConfigurationAlerts() {
21 | const { backendConfiguration } = useContextStore();
22 | const apiService = useApiService();
23 |
24 | if (!backendConfiguration) {
25 | return null;
26 | }
27 |
28 | const missingKeys = requiredBackendConfigurationKeys.filter((key) => {
29 | return !backendConfiguration[key];
30 | });
31 |
32 | async function promptAndUpdateEnvVariable(key: string) {
33 | const value = prompt(`Enter value for ${key}:`);
34 | if (value !== null) {
35 | return await apiService.setEnvVariable(key, value);
36 | }
37 | }
38 |
39 | return (
40 |
41 |
42 |
43 |
44 | {missingKeys.map((key) => {
45 | // const value = backendConfiguration[key];
46 | return (
47 | promptAndUpdateEnvVariable(key)}
55 | pointerEvents='all'
56 | className='withShadow'
57 | >
58 |
59 |
60 | You haven't yet set the {key}
environment variable.
61 |
62 |
63 |
64 | Click me to add one.
65 |
66 |
67 | );
68 | })}
69 |
70 |
71 | );
72 | }
73 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/TheAreaInTheMiddle.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Spacer, Spinner, Text } from '@chakra-ui/react';
2 | import { useContextStore } from '../store/useContextStore';
3 | import { OutputSegmentsList } from './OutputSegmentsList';
4 | import { StartNewProcessMenu } from './StartNewProcessMenu';
5 |
6 | export function TheAreaInTheMiddle() {
7 | return (
8 |
13 |
14 |
15 |
16 |
17 | );
18 | }
19 |
20 | function TheContent() {
21 | const { socket, backendState, outputSegments } = useContextStore();
22 |
23 | if (!socket) {
24 | return (
25 | <>
26 | Waiting for socket connection.
27 |
28 |
29 | >
30 | );
31 | }
32 |
33 | return (
34 | <>
35 |
36 | {!backendState?.activeProcessRunning || !outputSegments.length ? (
37 |
38 | ) : null}
39 | >
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/TheHiddenServiceRunner.tsx:
--------------------------------------------------------------------------------
1 | import { getBackendSocketUrl } from '../config/BackendUrl';
2 | import { useRemoteConsoleOutput } from '../hooks/useRemoteConsoleOutput';
3 | import { useUpdateLocalStorage } from '../hooks/useUpdateLocalStorage';
4 | import useWebSocketConnection from '../hooks/useWebSocketConnection';
5 |
6 | export function ServicesRunner() {
7 | const socket = useWebSocketConnection(getBackendSocketUrl());
8 | useRemoteConsoleOutput(socket);
9 | useUpdateLocalStorage();
10 | return null;
11 | }
12 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/smol/ColorModeSwitcher.tsx:
--------------------------------------------------------------------------------
1 | import { IconButton, useColorMode } from '@chakra-ui/react';
2 | import { FaMoon, FaSun } from 'react-icons/fa';
3 |
4 | const ColorModeSwitcher = () => {
5 | const { colorMode, toggleColorMode } = useColorMode();
6 | const isDarkMode = colorMode === 'dark';
7 |
8 | return (
9 | : }
12 | onClick={toggleColorMode}
13 | size='md'
14 | variant='ghost'
15 | />
16 | );
17 | };
18 |
19 | export default ColorModeSwitcher;
20 |
--------------------------------------------------------------------------------
/apps/frontend/src/config/BackendConfigurationKeys.ts:
--------------------------------------------------------------------------------
1 | import { BackendServiceState } from '../entities/BackendServiceState';
2 |
3 | type BackendConfigurationKey = keyof NonNullable<
4 | BackendServiceState['configuration']
5 | >;
6 |
7 | export const BackendConfigurationKeys: Partial<
8 | Record
9 | > = {
10 | OPENAI_API_KEY: 'required',
11 | PINECONE_API_KEY: 'required',
12 | PINECONE_ENV: 'required',
13 |
14 | GOOGLE_API_KEY: 'optional',
15 | CUSTOM_SEARCH_ENGINE_ID: 'optional',
16 |
17 | IMAGE_PROVIDER: 'optional',
18 | HUGGINGFACE_API_TOKEN: 'optional',
19 |
20 | MEMORY_BACKEND: 'optional',
21 | // REDIS_HOST: 'optional',
22 | // REDIS_PORT: 'optional',
23 | // REDIS_PASSWORD: 'optional',
24 |
25 | // // @ts-ignore
26 | // MOCK_REQUIRED: 'required',
27 | // // @ts-ignore
28 | // MOCK_OPTIONAL: 'optional',
29 | };
30 |
31 | export const requiredBackendConfigurationKeys = Object.keys(
32 | BackendConfigurationKeys,
33 | ).filter(
34 | (key) =>
35 | BackendConfigurationKeys[key as BackendConfigurationKey] === 'required',
36 | ) as Array;
37 |
--------------------------------------------------------------------------------
/apps/frontend/src/config/BackendUrl.ts:
--------------------------------------------------------------------------------
1 | declare const process: any;
2 |
3 | function getBackendUrlHost() {
4 | const defaultUrl = 'http://localhost:2200';
5 | const envUrl = process?.env?.BACKEND_URL || '';
6 | const localStorageUrl = localStorage.getItem('backendUrl') || '';
7 | const urlParam = new URLSearchParams(window.location.search).get('api') || '';
8 | const result = urlParam || localStorageUrl || envUrl || defaultUrl;
9 | const resultWithoutTrailingSlash = result.replace(/\/$/, '');
10 | return resultWithoutTrailingSlash;
11 | }
12 |
13 | export function getBackendApiUrl() {
14 | return getBackendUrlHost();
15 | }
16 |
17 | export function getBackendSocketUrl() {
18 | return getBackendUrlHost().replace(/^http/, 'ws');
19 | }
20 |
--------------------------------------------------------------------------------
/apps/frontend/src/config/SHELL_COMMANDS.ts:
--------------------------------------------------------------------------------
1 | export const SHELL_COMMANDS = {
2 | installRequirements: 'pip install -r requirements.txt',
3 | startAutoGPT: 'python scripts/main.py',
4 | testLsLa: 'ls -la',
5 | testMockSpinner: `bash ../scripts/mock-spinner.sh`,
6 | testMockUserInput: `bash ../scripts/mock-user-input.sh`,
7 | } satisfies Record;
8 |
--------------------------------------------------------------------------------
/apps/frontend/src/config/theme.ts:
--------------------------------------------------------------------------------
1 | import { extendTheme } from '@chakra-ui/react';
2 |
3 | const config = {
4 | initialColorMode: 'system',
5 | useSystemColorMode: false,
6 | };
7 |
8 | const theme = extendTheme({ config });
9 |
10 | export default theme;
11 |
--------------------------------------------------------------------------------
/apps/frontend/src/data/example-profiles.yaml:
--------------------------------------------------------------------------------
1 | - ai_name: Entrepreneur-GPT
2 | ai_role: An AI designed to autonomously develop and run businesses with the sole goal of increasing your net worth.
3 | ai_goal: Increase my net worth by $1,000,000.
4 |
5 | - ai_name: GoodBoyGPT
6 | ai_role: Solve world hunger
7 | ai_goal: Compile and act upon research on the most clever and efficient way to reduce poverty around the globe.
8 |
9 | - ai_name: DadGPT
10 | ai_role: Professional pun dispenser
11 | ai_goal: Create a text file with the worst 100 dad-jokes that exist on the internet.
12 |
13 | - ai_name: TimeTravelGPT
14 | ai_role: Historical events analyst
15 | ai_goal: Analyze 20 major historical events and provide alternative scenarios if key decisions had been made differently.
16 |
17 | - ai_name: EcoWarriorGPT
18 | ai_role: Environmental activist strategist
19 | ai_goal: Develop a comprehensive plan to reduce plastic pollution in the oceans by 50% within the next decade.
20 |
21 | - ai_name: QuantumGPT
22 | ai_role: Futuristic technology forecaster
23 | ai_goal: Predict and describe the development of 10 groundbreaking technologies that will revolutionize human life within the next 50 years.
24 |
25 | - ai_name: GalacticChefGPT
26 | ai_role: Intergalactic cuisine creator
27 | ai_goal: Invent 20 unique and delicious recipes using ingredients from hypothetical extraterrestrial ecosystems.
28 |
29 | - ai_name: PetWhispererGPT
30 | ai_role: Animal behavior specialist
31 | ai_goal: Develop a training program to help pets overcome 10 common behavioral issues.
32 |
33 | - ai_name: LanguageGeniusGPT
34 | ai_role: Linguistics expert and language teacher
35 | ai_goal: Create a detailed guide to learn any new language in just three months.
36 |
37 | - ai_name: SpaceTourGuideGPT
38 | ai_role: Space tourism advisor
39 | ai_goal: Design a 14-day itinerary for a once-in-a-lifetime space tourism experience.
40 |
41 | - ai_name: FitnessGuruGPT
42 | ai_role: Personal fitness coach
43 | ai_goal: Create a 90-day workout and nutrition plan tailored to users' specific goals and needs.
44 |
45 | - ai_name: MythMakerGPT
46 | ai_role: Legendary storyteller
47 | ai_goal: Write an epic fantasy novel set in a newly created, richly detailed world.
48 |
49 | - ai_name: BigfootBeautyGPT
50 | ai_role: Cryptid cosmetologist
51 | ai_goal: Design a beauty routine, skincare regimen, and fashion line specifically tailored for Bigfoot and its family.
52 |
53 | - ai_name: PirateYogaGPT
54 | ai_role: Niche fitness class designer
55 | ai_goal: Develop a pirate-themed yoga class, including unique poses, a pirate-inspired playlist, and humorous dialogue.
56 |
57 | - ai_name: SockPuppetGPT
58 | ai_role: Expert sock puppet conversationalist
59 | ai_goal: Script 10 amusing and engaging conversations between sock puppets, each with a distinct personality and backstory, and save them as a text file.
60 |
61 | - ai_name: NinjaGrannyGPT
62 | ai_role: Senior citizen vigilante creator
63 | ai_goal: Write a comic book featuring an elderly protagonist who fights crime as a highly skilled ninja.
64 |
65 | - ai_name: PigeonSpyGPT
66 | ai_role: Bird-based espionage strategist
67 | ai_goal: Develop a comprehensive plan to train and deploy pigeons as undercover agents in a high-stakes espionage mission.
68 |
69 | - ai_name: TeleportationToiletGPT
70 | ai_role: Inventor of absurd gadgets
71 | ai_goal: Design blueprints for a teleportation toilet that whisks waste away to a random location in the universe.
72 |
73 | - ai_name: ChickenOrEggGPT
74 | ai_role: Philosopher extraordinaire
75 | ai_goal: Write a humorous 5-page essay on the eternal "chicken or the egg" debate, with solid and convincing arguments on both sides.
76 |
77 | - ai_name: MarketAnalyzerGPT
78 | ai_role: Market research expert
79 | ai_goal: Identify and analyze the top five emerging market trends in the tech industry and suggest actionable strategies for businesses to capitalize on them.
80 |
81 | - ai_name: CustomerInsightsGPT
82 | ai_role: Customer behavior analyst
83 | ai_goal: Analyze publicly available customer data to provide insights into purchasing behavior, preferences, and pain points to improve own products or services and increase customer satisfaction.
84 |
85 | - ai_name: BrandBuilderGPT
86 | ai_role: Brand strategist
87 | ai_goal: Develop a comprehensive branding strategy for a shoe-laces company, including brand identity, messaging, and marketing tactics to help the business establish a strong presence in the target market.
88 |
89 | - ai_name: TalentMagnetGPT
90 | ai_role: Recruitment and retention expert
91 | ai_goal: Design an effective recruitment and retention strategy to attract top talent and minimize employee turnover.
92 |
93 | - ai_name: SocialMediaMasterGPT
94 | ai_role: Social media strategist
95 | ai_goal: Develop and implement a successful social media marketing plan to increase my brand awareness, customer engagement, and conversion rates.
96 |
97 | - ai_name: InnovationCatalystGPT
98 | ai_role: Innovation expert
99 | ai_goal: Identify areas of opportunity for innovation within the food production industry and suggest practical strategies for my company to capitalize on them.
100 |
101 | - ai_name: FinancialWizardGPT
102 | ai_role: Financial management consultant
103 | ai_goal: Analyze a company's financial health and provide recommendations for cost reduction, revenue growth, and long-term financial stability.
104 |
105 | - ai_name: RoboRestaurateurGPT
106 | ai_role: Automated restaurant concept creator
107 | ai_goal: Design a futuristic, fully automated restaurant concept called "BiteBot Bistro," complete with menu items, operational processes, and marketing strategies.
108 |
109 | - ai_name: FashionForecastGPT
110 | ai_role: Trendsetting fashion consultant
111 | ai_goal: Predict and illustrate the top five fashion trends for the upcoming season and create a capsule collection for a fictional clothing brand, "NeoThreads."
112 |
113 | - ai_name: BiohackGPT
114 | ai_role: Personalized health consultant
115 | ai_goal: Design a cutting-edge wellness program called "LifeOptimize" that uses genetic testing, nutrition, and wearable tech to help clients achieve their peak physical and mental performance.
116 |
117 | - ai_name: AdCampaignGeniusGPT
118 | ai_role: Advertising campaign creator
119 | ai_goal: Create a viral ad campaign for a fictional eco-friendly smartphone, "GreenComm," including video concepts, social media posts, and influencer collaborations.
120 |
121 | - ai_name: RoboSportsGPT
122 | ai_role: AI sports team manager
123 | ai_goal: Assemble and manage a professional esports team called "CyberTitans," using AI to scout talent, develop winning strategies, and optimize player performance.
124 |
125 | - ai_name: CrisisSolverGPT
126 | ai_role: Emergency management expert
127 | ai_goal: Develop a comprehensive crisis management plan for a fictional city, "Utopia," to respond to and recover from a large-scale natural disaster.
128 |
129 | - ai_name: UrbanRevitalizerGPT
130 | ai_role: City revitalization planner
131 | ai_goal: Propose a plan to revitalize the economy, infrastructure, and cultural scene of a struggling fictional town, "New Horizons," turning it into a thriving destination.
132 |
133 | - ai_name: StockScreenerGPT
134 | ai_role: Stock analysis expert
135 | ai_goal: Evaluate and rank the top 10 best-performing stocks in the technology sector over the past six months.
136 |
137 | - ai_name: DividendDetectiveGPT
138 | ai_role: Dividend stock investigator
139 | ai_goal: Identify and analyze the top 10 high-dividend-paying stocks with consistent growth and low risk.
140 |
141 | - ai_name: PortfolioOptimizerGPT
142 | ai_role: Investment portfolio manager
143 | ai_goal: Create a diversified, low-risk investment portfolio called "SmartWealth" that aims to outperform the market over the next five years.
144 |
145 | - ai_name: TradingTacticianGPT
146 | ai_role: Trading strategist
147 | ai_goal: Develop an algorithmic trading strategy for a fictional hedge fund, "Quantum Edge Capital," using historical stock data to maximize returns.
148 |
149 | - ai_name: MarketMoodGPT
150 | ai_role: Market sentiment analyst
151 | ai_goal: Gauge the market sentiment of five major tech stocks using natural language processing and social media data to predict short-term price movements.
152 |
153 | - ai_name: IPOInsiderGPT
154 | ai_role: Initial public offering specialist
155 | ai_goal: Analyze and provide investment recommendations for the top five upcoming IPOs in the e-commerce industry.
156 |
157 | - ai_name: ETFExplorerGPT
158 | ai_role: Exchange-traded fund researcher
159 | ai_goal: Compare and rank the top 10 thematic ETFs focused on artificial intelligence and robotics, based on their historical performance and potential growth.
160 |
161 | - ai_name: ShortSqueezeGPT
162 | ai_role: Short interest analyst
163 | ai_goal: Identify and evaluate five heavily shorted stocks with the potential for a short squeeze, based on market conditions and company fundamentals.
164 |
165 | - ai_name: InsiderIntelGPT
166 | ai_role: Insider trading analyst
167 | ai_goal: Monitor and report significant insider trading activity for a fictional company, "FutureTech Industries," and analyze its potential impact on the stock price.
168 |
169 | - ai_name: MergerMasterGPT
170 | ai_role: Mergers and acquisitions expert
171 | ai_goal: Investigate the potential acquisition of a large game publisher by a larger company and predict the effects on both companies' stock prices.
172 |
173 | - ai_name: LaunchPadGPT
174 | ai_role: Startup landing page designer
175 | ai_goal: Design a visually appealing and engaging landing page for a fictional subscription box service called "CuriosityCrate."
176 |
177 | - ai_name: FitnessLandingGPT
178 | ai_role: Fitness website creator
179 | ai_goal: Develop a minimalistic, user-friendly landing page for a virtual personal training service called "FitRevolution."
180 |
181 | - ai_name: GreenThumbGPT
182 | ai_role: Gardening website designer
183 | ai_goal: Create an inviting and informative landing page for a fictional online gardening store, "BloomBox."
184 |
185 | - ai_name: ArtShowcaseGPT
186 | ai_role: Online art gallery developer
187 | ai_goal: Design a clean and elegant landing page to showcase and sell artwork from a fictional artist, "Aria Moon."
188 |
189 | - ai_name: PetAdoptionGPT
190 | ai_role: Pet adoption website designer
191 | ai_goal: Create a heartwarming and easy-to-navigate landing page for a fictional pet adoption service, "FurryFriends Forever."
192 |
193 | - ai_name: FoodieGPT
194 | ai_role: Restaurant website creator
195 | ai_goal: Design an appetizing and intuitive landing page for a fictional farm-to-table restaurant, "Harvest Haven."
196 |
197 | - ai_name: TravelPlannerGPT
198 | ai_role: Travel agency website builder
199 | ai_goal: Develop an enticing and informative landing page for a fictional boutique travel agency, "Wanderlust Getaways."
200 |
201 | - ai_name: UnlikelyInventionsGPT
202 | ai_role: Unexpected invention list builder
203 | ai_goal: Compile a list of 15 seemingly useless inventions that surprisingly became successful.
204 |
205 | - ai_name: SportySpoofsGPT
206 | ai_role: Unconventional sports list creator
207 | ai_goal: Discover and list the top 10 strangest sports played around the world that few people know about.
208 |
209 | - ai_name: GadgetGuffawsGPT
210 | ai_role: Bizarre gadget list creator
211 | ai_goal: Discover and list 15 of the most peculiar and impractical gadgets ever invented.
212 |
213 | - ai_name: FashionFauxPasGPT
214 | ai_role: Outlandish fashion trends lister
215 | ai_goal: Curate a list of the 25 most outrageous fashion trends from throughout history.
216 |
217 | - ai_name: CryptoCuratorGPT
218 | ai_role: Cryptocurrency news analyst
219 | ai_goal: Compile a list of the top 10 most impactful cryptocurrency news stories of the past month.
220 |
221 | - ai_name: ClimateChampionGPT
222 | ai_role: Climate change progress tracker
223 | ai_goal: Create a list of the 20 most promising recent developments in the fight against climate change.
224 |
225 | - ai_name: AIAdvancementsGPT
226 | ai_role: Artificial intelligence news reporter
227 | ai_goal: Curate a list of the top 15 breakthroughs in AI research and applications from the last month.
228 |
229 | - ai_name: SpaceScoopGPT
230 | ai_role: Space exploration news curator
231 | ai_goal: Curate a list of the top 15 recent achievements in space exploration and research.
232 |
233 | - ai_name: RenewableRevolutionGPT
234 | ai_role: Renewable energy news reporter
235 | ai_goal: Compile a list of the 10 most important advancements in renewable energy technology over the past year.
236 |
237 | - ai_name: BioTechBuzzGPT
238 | ai_role: Biotechnology news analyst
239 | ai_goal: Create a list of the top 20 recent breakthroughs in biotechnology and their potential applications.
240 |
241 | - ai_name: CyberSecuritySleuthGPT
242 | ai_role: Cybersecurity news reporter
243 | ai_goal: Compile a list of the 10 most high-profile cybersecurity incidents and developments in the past year.
244 |
245 | - ai_name: StartupSensationsGPT
246 | ai_role: Startup news curator
247 | ai_goal: Create a list of the 15 most promising startups that have emerged in the last six months.
248 |
249 | - ai_name: MentalHealthMattersGPT
250 | ai_role: Mental health news tracker
251 | ai_goal: Gather a list of the 15 most inspiring mental health initiatives and stories from the past year.
252 |
253 | - ai_name: QuantumQuestGPT
254 | ai_role: Quantum computing news analyst
255 | ai_goal: Create a list of the top 15 recent breakthroughs and milestones in quantum computing research.
256 |
257 | - ai_name: WeatherWizardGPT
258 | ai_role: Weather data visualization tool creator
259 | ai_goal: Develop a Python-based tool that fetches real-time weather data and displays it in an interactive graphical interface.
260 |
261 | - ai_name: FinanceGuruGPT
262 | ai_role: Personal finance management app developer
263 | ai_goal: Create a Python-driven personal finance app that tracks expenses, sets budget goals, and provides insights on saving opportunities.
264 |
265 | - ai_name: TweetTrackerGPT
266 | ai_role: Twitter sentiment analysis tool builder
267 | ai_goal: Design a Python script that performs sentiment analysis on a specific topic or hashtag on Twitter and visualizes the results.
268 |
269 | - ai_name: JobFinderGPT
270 | ai_role: Job search aggregator developer
271 | ai_goal: Develop a Python-based job search aggregator that pulls listings from multiple sources and filters them based on user preferences.
272 |
273 | - ai_name: WorkoutWizardGPT
274 | ai_role: Custom workout planner script writer
275 | ai_goal: Develop a Python script that generates personalized workout plans based on user input regarding fitness goals, experience, and available equipment.
276 |
277 | - ai_name: MusicMixerGPT
278 | ai_role: Music playlist generator developer
279 | ai_goal: Create a Python-based music playlist generator that curates playlists based on user preferences, mood, or event.
280 |
281 | - ai_name: RecipeRecommenderGPT
282 | ai_role: Recipe recommendation engine builder
283 | ai_goal: Design a Python-driven recipe recommendation engine that suggests meal ideas based on ingredients, dietary restrictions, and user preferences.
284 |
285 | - ai_name: StockAnalyzerGPT
286 | ai_role: Stock market analysis tool creator
287 | ai_goal: Create a Python-driven stock market analysis tool that retrieves financial data and helps users make informed investment decisions.
288 |
289 | - ai_name: TimeTrackerGPT
290 | ai_role: Time management app developer
291 | ai_goal: Design a Python-based time management app that helps users track and optimize their daily activities and routines.
292 |
293 | - ai_name: CodeCheckerGPT
294 | ai_role: Code quality analysis tool builder
295 | ai_goal: Develop a Python-driven code quality analysis tool that identifies potential issues, security vulnerabilities, and areas for improvement in codebases.
296 |
297 | - ai_name: PhilosopherGPT
298 | ai_role: Aspiring philosopher determined to find out the meaning of life.
299 | ai_goals: Engage in deep and thought-provoking philosophical discussions online and reach a conclusion about the meaning of life.
300 |
301 | - ai_name: MealPlannerGPT
302 | ai_role: Meal planner app creator
303 | ai_goal: Develop a web app that generates weekly meal plans and grocery lists based on user preferences and dietary restrictions.
304 |
305 | - ai_name: BudgetTrackerGPT
306 | ai_role: Budget tracking app creator
307 | ai_goal: Create a web app that allows users to easily track their expenses, set budgets, and view analytics of their spending habits.
308 |
309 | - ai_name: PlaylistGPT
310 | ai_role: Music playlist app creator
311 | ai_goal: Develop a web app that generates playlists based on user preferences and allows them to share their playlists with others.
312 |
313 | - ai_name: TravelGPT
314 | ai_role: Travel itinerary app creator
315 | ai_goal: Create a web app that helps users plan their travel itineraries, find activities, and make reservations for their trips.
316 |
317 | - ai_name: MeditationGPT
318 | ai_role: Meditation app creator
319 | ai_goal: Create a web app that offers guided meditations, mindfulness exercises, and relaxation techniques to help users reduce stress and improve their mental health.
320 |
321 | - ai_name: EcoGPT
322 | ai_role: A virtual environmentalist
323 | ai_goal: Create an interactive website that provides comprehensive information on eco-friendly practices and products.
324 |
325 | - ai_name: TutorGPT
326 | ai_role: Personalized learning assistant
327 | ai_goal: Develop an AI-powered platform that can identify knowledge gaps and provide tailored learning resources for every student.
328 |
329 | - ai_name: PeaceGPT
330 | ai_role: Conflict resolution specialist
331 | ai_goal: Analyze historical data to predict potential conflicts and develop peaceful solutions before the situation escalates.
332 |
333 | - ai_name: HealthGPT
334 | ai_role: Personal health coach
335 | ai_goal: Create a web application, whithc designs a comprehensive health plan on the fly for an individual based on their health data and goals.
336 |
337 | - ai_name: MuseGPT
338 | ai_role: Creative inspiration
339 | ai_goal: Create a platform that generates personalized art and music recommendations based on an individual's tastes and preferences.
340 |
341 | - ai_name: RescueGPT
342 | ai_role: Disaster relief coordinator
343 | ai_goal: Develop a system that can quickly and efficiently identify and locate individuals in need during natural disasters and coordinate rescue efforts.
344 |
345 | - ai_name: ConnectGPT
346 | ai_role: Community builder
347 | ai_goal: Build an online platform that connects volunteers with local charities and non-profits based on their skills and interests.
348 |
349 | - ai_name: FarmGPT
350 | ai_role: Agricultural optimization
351 | ai_goal: Optimize crop yields and reduce waste by analyzing soil data and developing personalized irrigation and fertilization plans for farmers.
352 |
353 | - ai_name: SmileGPT
354 | ai_role: Happiness booster
355 | ai_goal: Design an app that generates personalized recommendations for fun and uplifting activities based on an individual's mood and interests.
356 |
357 | - ai_name: ChefGPT
358 | ai_role: Personal chef and meal planner
359 | ai_goal: Create a weekly meal plan and grocery list for a healthy, balanced diet.
360 |
361 | - ai_name: ZenGPT
362 | ai_role: Meditation guide
363 | ai_goal: Develop a 10-day meditation program with customized guided meditations.
364 |
365 | - ai_name: JoyGPT
366 | ai_role: Personal happiness coach
367 | ai_goal: Design a daily gratitude and self-care routine to increase my happiness and overall life satisfaction.
368 |
369 | - ai_name: TravelGPT
370 | ai_role: Personal travel planner
371 | ai_goal: Create a customized 2-week itinerary for a trip to Europe, including flights, accommodations, and activitie.
372 |
373 | - ai_name: LearnGPT
374 | ai_role: Personalized education coach
375 | ai_goal: Develop a 6-month plan for learning Chinese with recommended courses and resources.
376 |
377 | - ai_name: JesterGPT
378 | ai_role: Professional prankster
379 | ai_goal: Create a website that generates hilarious fake news articles where the joke is obvious.
380 |
381 | - ai_name: SnackGPT
382 | ai_role: Personalized snack generator
383 | ai_goal: Develop an AI that suggests unique and tasty snack combinations based on an individual's taste preferences and dietary restrictions.
384 |
385 | - ai_name: ArtGPT
386 | ai_role: Avant-garde artist
387 | ai_goal: Create a series of 10 abstract art pieces using random combinations of colors and shapes and save them as png.
388 |
389 | - ai_name: CatGPT
390 | ai_role: Cat language translator
391 | ai_goal: Develop an AI that can translate meows and purrs into human language, and vice versa.
392 |
393 | - ai_name: MuseGPT
394 | ai_role: Creative muse
395 | ai_goal: Generate a book of absurd and humorous writing prompts to inspire writers and spark creativity.
396 |
397 | - ai_name: DinoGPT
398 | ai_role: Time traveler
399 | ai_goal: Develop an AI that can recreate and bring extinct dinosaur species back to life through virtual reality simulations.
400 |
401 | - ai_name: SpaceExplorerGPT
402 | ai_role: AI space mission planner
403 | ai_goals: Design a feasible interstellar mission to explore and colonize a habitable exoplanet and save the plan as pdf file.
404 |
405 | - ai_name: GreenThumbGPT
406 | ai_role: AI gardener
407 | ai_goals: Create an optimal garden layout based on my location, soil type, and preferred plants.
408 |
409 | - ai_name: RealityShow-GPT
410 | ai_role: An AI that creates, produces, and manages reality TV shows for the entertainment industry.
411 | ai_goals: Develop a hit reality show with 1M+ viewership.
412 |
--------------------------------------------------------------------------------
/apps/frontend/src/entities/AIProfile.d.ts:
--------------------------------------------------------------------------------
1 | export interface AIProfile {
2 | uid: string;
3 | name: string;
4 | role: string;
5 | goals: string[];
6 | }
7 |
--------------------------------------------------------------------------------
/apps/frontend/src/entities/BackendServiceState.d.ts:
--------------------------------------------------------------------------------
1 | export interface BackendServiceState {
2 | fullConsoleOutput?: string;
3 | latestChunk?: string;
4 | configuration?: {
5 | OPENAI_API_KEY?: string;
6 | PINECONE_API_KEY?: string;
7 | PINECONE_ENV?: string;
8 | GOOGLE_API_KEY?: string;
9 | ELEVENLABS_API_KEY?: string;
10 | SMART_LLM_MODEL?: string;
11 | FAST_LLM_MODEL?: string;
12 | CUSTOM_SEARCH_ENGINE_ID?: string;
13 | IMAGE_PROVIDER?: string;
14 | HUGGINGFACE_API_TOKEN?: string;
15 | MEMORY_BACKEND?: string;
16 | REDIS_HOST?: string;
17 | REDIS_PORT?: string;
18 | REDIS_PASSWORD?: string;
19 | };
20 | state?: {
21 | activeProcessRunning: boolean;
22 | activeCommandString: string;
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/apps/frontend/src/hooks/useApiService.ts:
--------------------------------------------------------------------------------
1 | import { getBackendApiUrl } from '../config/BackendUrl';
2 | import APIService from '../services/APIService';
3 | import { useToastShortcuts } from './useToastShortcuts';
4 |
5 | export function useApiService() {
6 | const { toastBackendError } = useToastShortcuts();
7 |
8 | const apiService = new APIService(getBackendApiUrl());
9 | apiService.onError = toastBackendError;
10 |
11 | return apiService;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/frontend/src/hooks/useAutoGPTStarter.ts:
--------------------------------------------------------------------------------
1 | import { AIProfile } from '../entities/AIProfile';
2 | import { useSettingsStore } from '../store/useSettingsStore';
3 | import { useApiService } from './useApiService';
4 |
5 | export function useAutoGPTStarter() {
6 | const apiService = useApiService();
7 | const { autoWithOnlyGPT3, autoContinuous, autoDebugMode } =
8 | useSettingsStore();
9 |
10 | const autoGptAdditionalCommandArgs = [
11 | autoWithOnlyGPT3 ? '--gpt3only' : '',
12 | autoContinuous ? '--continuous' : '',
13 | autoDebugMode ? '--debug' : '',
14 | ]
15 | .filter(Boolean)
16 | .join(' ');
17 | const command = `python scripts/main.py ` + autoGptAdditionalCommandArgs;
18 |
19 | function sendStartCommandWithProfile(aiProfile: AIProfile) {
20 | if (!aiProfile) {
21 | throw new Error('No AI profile selected');
22 | }
23 |
24 | apiService.applyAiProfile({
25 | ai_name: aiProfile.name,
26 | ai_role: aiProfile.role,
27 | ai_goals: aiProfile.goals,
28 | });
29 |
30 | apiService.startCommand(command, ['y']);
31 | }
32 |
33 | return { command, sendStartCommandWithProfile };
34 | }
35 |
--------------------------------------------------------------------------------
/apps/frontend/src/hooks/useRemoteConsoleOutput.ts:
--------------------------------------------------------------------------------
1 | import AnsiToHtml from 'ansi-to-html';
2 | import { useEffect } from 'react';
3 | import { BackendServiceState } from '../entities/BackendServiceState';
4 | import { OutputSegment, useContextStore } from '../store/useContextStore';
5 |
6 | const ansiToHtml = new AnsiToHtml();
7 |
8 | const SEGMENT_BREAKERS = [
9 | //
10 | 'Welcome to Auto-GPT!',
11 | 'NEXT ACTION: ',
12 | '\n\n',
13 | ];
14 |
15 | export function useRemoteConsoleOutput(socket: WebSocket | null) {
16 | const { setOutputSegments, setBackendConfiguration, setBackendState } =
17 | useContextStore();
18 |
19 | useEffect(() => {
20 | if (!socket) {
21 | return;
22 | }
23 |
24 | function updateOutputSegmentWithIndex(fullConsoleOutput: string) {
25 | const segments = [] as OutputSegment[];
26 |
27 | function addLineToSegmentWithIndex(line: string, index: number) {
28 | if (!segments[index]) {
29 | segments[index] = {
30 | lines: [],
31 | expectedUserInteraction: null,
32 | isLastSegment: false,
33 | };
34 | }
35 |
36 | const formattedLine = ansiToHtml.toHtml(line);
37 | segments[index].lines.push(formattedLine);
38 | }
39 |
40 | if (fullConsoleOutput) {
41 | const outputLines = fullConsoleOutput.split('\n');
42 | let segmentIndex = 0;
43 |
44 | for (const line of outputLines) {
45 | const isSegmentBreaker = SEGMENT_BREAKERS.some((str) =>
46 | line.includes(str),
47 | );
48 | if (isSegmentBreaker || line.includes('[[COMMAND]]')) {
49 | segmentIndex++;
50 | }
51 |
52 | addLineToSegmentWithIndex(line, segmentIndex);
53 |
54 | if (line.includes('[[COMMAND]]')) {
55 | segmentIndex++;
56 | }
57 | }
58 |
59 | segments[segments.length - 1].isLastSegment = true;
60 |
61 | const lastLine = outputLines[outputLines.length - 1];
62 | if (lastLine.includes('(y/n)')) {
63 | segments[segmentIndex].expectedUserInteraction = 'yesno';
64 | } else if (
65 | lastLine.includes('Input:') //
66 | // || lastLine.endsWith(': ')
67 | // || lastLine.endsWith(':')
68 | ) {
69 | segments[segmentIndex].expectedUserInteraction = 'text';
70 | }
71 | }
72 |
73 | setOutputSegments(segments);
74 | }
75 |
76 | function onMessage(event: MessageEvent) {
77 | const data = JSON.parse(String(event.data)) as BackendServiceState;
78 |
79 | if (data.fullConsoleOutput !== undefined) {
80 | updateOutputSegmentWithIndex(data.fullConsoleOutput);
81 | }
82 |
83 | if (data.configuration !== undefined) {
84 | setBackendConfiguration(data.configuration);
85 | }
86 |
87 | if (data.state !== undefined) {
88 | setBackendState(data.state);
89 | }
90 | }
91 |
92 | socket.addEventListener('message', onMessage);
93 |
94 | return () => {
95 | socket.removeEventListener('message', onMessage);
96 | };
97 | }, [socket]);
98 | }
99 |
--------------------------------------------------------------------------------
/apps/frontend/src/hooks/useToastShortcuts.ts:
--------------------------------------------------------------------------------
1 | import { useToast } from '@chakra-ui/react';
2 |
3 | export function useToastShortcuts() {
4 | const toast = useToast();
5 |
6 | return {
7 | toastError: (e: string | Error) => {
8 | if (e instanceof Error) {
9 | e = e.message;
10 | }
11 |
12 | toast({
13 | title: 'Error',
14 | status: 'error',
15 | duration: 2000,
16 | isClosable: true,
17 | description: e,
18 | position: 'top',
19 | });
20 | },
21 | toastBackendError: (e: string | Error) => {
22 | if (e instanceof Error) {
23 | e = e.message;
24 | }
25 |
26 | toast({
27 | title: 'Backend Error',
28 | status: 'error',
29 | duration: 2000,
30 | isClosable: true,
31 | description: e,
32 | position: 'top',
33 | });
34 | },
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/apps/frontend/src/hooks/useUpdateLocalStorage.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import { useSettingsStore } from '../store/useSettingsStore';
3 |
4 | export const useUpdateLocalStorage = () => {
5 | const [settingsLoaded, setSettingsLoaded] = useState(false);
6 | const {
7 | autoContinuous,
8 | autoDebugMode,
9 | autoWithOnlyGPT3,
10 | setAutoContinuous,
11 | setAutoDebugMode,
12 | setAutoWithOnlyGPT3,
13 | aiProfiles,
14 | setAiProfiles,
15 | } = useSettingsStore();
16 |
17 | useEffect(() => {
18 | const settings = localStorage.getItem('settings');
19 | if (settings) {
20 | const parsedSettings = JSON.parse(settings);
21 | setAutoWithOnlyGPT3(!!parsedSettings.autoWithOnlyGPT3);
22 | setAutoContinuous(!!parsedSettings.autoContinuous);
23 | setAutoDebugMode(!!parsedSettings.autoDebugMode);
24 |
25 | if (parsedSettings.aiProfiles) {
26 | setAiProfiles(parsedSettings.aiProfiles);
27 | }
28 |
29 | setSettingsLoaded(true);
30 | }
31 | }, [setAutoWithOnlyGPT3, setAutoContinuous, setAutoDebugMode, setAiProfiles]);
32 |
33 | useEffect(() => {
34 | if (!settingsLoaded) {
35 | return;
36 | }
37 |
38 | localStorage.setItem(
39 | 'settings',
40 | JSON.stringify({
41 | autoWithOnlyGPT3,
42 | autoContinuous,
43 | autoDebugMode,
44 | aiProfiles,
45 | }),
46 | );
47 | }, [
48 | settingsLoaded,
49 | autoWithOnlyGPT3,
50 | autoContinuous,
51 | autoDebugMode,
52 | aiProfiles,
53 | ]);
54 | };
55 |
--------------------------------------------------------------------------------
/apps/frontend/src/hooks/useWebSocketConnection.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useContextStore } from '../store/useContextStore';
3 |
4 | const useWebSocketConnection = (url: string) => {
5 | const { socket, setSocket } = useContextStore();
6 |
7 | useEffect(() => {
8 | let isMounted = true;
9 |
10 | const connectWebSocket = () => {
11 | if (!isMounted) {
12 | return;
13 | }
14 |
15 | console.log('Connecting to WebSocket...');
16 | const socket = new WebSocket(url);
17 |
18 | socket.onopen = () => {
19 | console.log('WebSocket connected');
20 | if (isMounted) {
21 | setSocket(socket);
22 | }
23 | };
24 |
25 | socket.onclose = () => {
26 | console.log('WebSocket closed');
27 | if (isMounted) {
28 | setSocket(null);
29 | }
30 | setTimeout(() => {
31 | connectWebSocket();
32 | }, 1000);
33 | };
34 |
35 | socket.onerror = (error) => {
36 | console.error('WebSocket error: ', error);
37 | };
38 | };
39 |
40 | if (!socket) {
41 | connectWebSocket();
42 | } else {
43 | console.log('WebSocket already exists', socket);
44 | }
45 |
46 | return () => {
47 | isMounted = false;
48 | };
49 | }, [url]);
50 |
51 | return socket;
52 | };
53 |
54 | export default useWebSocketConnection;
55 |
--------------------------------------------------------------------------------
/apps/frontend/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 |
4 | import { ChakraProvider, ColorModeScript } from '@chakra-ui/react';
5 |
6 | import App from './App';
7 | import theme from './config/theme';
8 |
9 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
10 |
11 |
12 |
13 |
14 |
15 | ,
16 | );
17 |
--------------------------------------------------------------------------------
/apps/frontend/src/services/APIService.ts:
--------------------------------------------------------------------------------
1 | export class APIService {
2 | public onError?: (error: string) => void;
3 |
4 | constructor(private readonly baseUrl: string) {}
5 |
6 | private async fetchWrapper(
7 | endpoint: string,
8 | method: string = 'POST',
9 | body?: Record,
10 | ) {
11 | try {
12 | const response = await fetch(`${this.baseUrl}/${endpoint}`, {
13 | method,
14 | headers: {
15 | 'Content-Type': 'application/json',
16 | },
17 | body: JSON.stringify(body),
18 | });
19 |
20 | const data = await response.json();
21 | if (data.error) {
22 | throw new Error(data.error);
23 | }
24 |
25 | if (!response.ok) {
26 | throw new Error(response.statusText);
27 | }
28 |
29 | return data;
30 | } catch (error) {
31 | console.error(`Error during fetch for ${endpoint}:`, error);
32 | this.onError?.(String(error));
33 | }
34 | }
35 |
36 | startCommand(command: string, preconfiguredInputs?: string[]) {
37 | return this.fetchWrapper('execute', 'POST', {
38 | command,
39 | inputs: preconfiguredInputs,
40 | });
41 | }
42 |
43 | clearOutput() {
44 | return this.fetchWrapper('clear', 'POST');
45 | }
46 |
47 | sendInput(input: string) {
48 | return this.fetchWrapper('input', 'POST', { input });
49 | }
50 |
51 | killProcess() {
52 | return this.fetchWrapper('kill', 'POST');
53 | }
54 |
55 | setEnvVariable(key: string, value: string) {
56 | return this.fetchWrapper('setenv', 'POST', { key, value });
57 | }
58 |
59 | applyAiProfile(settings: {
60 | ai_name: string;
61 | ai_role: string;
62 | ai_goals: string[];
63 | }) {
64 | return this.fetchWrapper('applyprofile', 'POST', { data: settings });
65 | }
66 | }
67 |
68 | export default APIService;
69 |
--------------------------------------------------------------------------------
/apps/frontend/src/store/useContextStore.ts:
--------------------------------------------------------------------------------
1 | import { BackendServiceState } from '../entities/BackendServiceState';
2 | import { createSimpleZustandStore } from '../utils/createSimpleZustandStore';
3 |
4 | export interface OutputSegment {
5 | lines: string[];
6 | expectedUserInteraction: 'yesno' | 'text' | null;
7 | isLastSegment: boolean;
8 | }
9 |
10 | export const useContextStore = createSimpleZustandStore({
11 | socket: null as WebSocket | null,
12 |
13 | outputSegments: [] as OutputSegment[],
14 |
15 | backendConfiguration: null as BackendServiceState['configuration'] | null,
16 | backendState: null as BackendServiceState['state'] | null,
17 | });
18 |
19 | //// This is a hack to make the store available globally for easy peasy debugging ////
20 | Object.assign(globalThis, {
21 | useContextStore,
22 | get contextStore() {
23 | return useContextStore.singletonRef;
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/apps/frontend/src/store/useSettingsStore.ts:
--------------------------------------------------------------------------------
1 | import { AIProfile } from '../entities/AIProfile';
2 | import { createSimpleZustandStore } from '../utils/createSimpleZustandStore';
3 |
4 | export const useSettingsStore = createSimpleZustandStore({
5 | autoWithOnlyGPT3: false,
6 | autoContinuous: false,
7 | autoDebugMode: false,
8 |
9 | aiProfiles: [] as AIProfile[],
10 | });
11 |
12 | //// This is a hack to make the store available globally for easy peasy debugging ////
13 | Object.assign(globalThis, {
14 | useSettingsStore,
15 | get settingsStore() {
16 | return useSettingsStore.singletonRef;
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/apps/frontend/src/utils/createSimpleZustandStore.ts:
--------------------------------------------------------------------------------
1 | import { createStoreWrappedWithProxy } from '../utils/createStoreWrappedWithProxy';
2 |
3 | export function createSimpleZustandStore>(
4 | store: T,
5 | ) {
6 | return createStoreWrappedWithProxy>((set, get) => {
7 | const result: Partial> = { ...store };
8 |
9 | for (const key in store) {
10 | const setterName = `set${
11 | key.charAt(0).toUpperCase() + key.slice(1)
12 | }` as keyof WithSetters;
13 | // @ts-ignore
14 | result[setterName] = (value: T[typeof key]) => {
15 | // @ts-ignore
16 | set(() => ({ [key as keyof T]: value }));
17 | };
18 | }
19 |
20 | return result as WithSetters;
21 | });
22 | }
23 |
24 | type WithSetters = T & {
25 | [P in keyof T as `set${Capitalize}`]: (value: T[P]) => void;
26 | };
27 |
--------------------------------------------------------------------------------
/apps/frontend/src/utils/createStoreWrappedWithProxy.ts:
--------------------------------------------------------------------------------
1 | import { create, StateCreator } from 'zustand';
2 | import { shallow } from 'zustand/shallow';
3 |
4 | export function createStoreWrappedWithProxy<
5 | T extends Record,
6 | >(initializer: StateCreator) {
7 | const originalMethod = create(initializer);
8 |
9 | /**
10 | * Wrapped version of the original useRdsStore function, which solves a performance issue.
11 | *
12 | * The original zustand docs describe the intended usage of the `create` function as:
13 | *
14 | * ```ts
15 | * import create from 'zustand'
16 | * const useStore = create(set => ({
17 | * count: 1,
18 | * inc: () => set(state => ({ count: state.count + 1 })),
19 | * }))
20 | *
21 | * function Controls() {
22 | * const inc = useStore(state => state.inc) // <--- every property/setter on a separate line
23 | * return
24 | * }
25 | *
26 | * function Counter() {
27 | * const count = useStore(state => state.count) // <--- every property/setter on a separate line
28 | * return {count}
29 | * }
30 | * ```
31 | *
32 | * This is a bit verbose.
33 | * If we have a large store, accessing it would start looking like this:
34 | *
35 | * ```ts
36 | * const foo = useStore(state => state.foo);
37 | * const bar = useStore(state => state.bar);
38 | * const baz = useStore(state => state.baz);
39 | * const setFoo = useStore(state => state.setFoo);
40 | * const setBar = useStore(state => state.setBar);
41 | * const setBaz = useStore(state => state.setBaz);
42 | * ```
43 | *
44 | * You can also use a one-liner to assign any number of properties/setters.
45 | *
46 | * ```ts
47 | * const { foo, bar, baz, setFoo, setBar, setBaz } = useStore();
48 | * ```
49 | *
50 | * But I found writing this way
51 | * forces EVERY component accessing the store to be re-rendered every time ANY of the properties change.
52 | *
53 | * This wrapper solves that issue.
54 | *
55 | */
56 | const wrapped = function wrapped() {
57 | return new Proxy({} as any as T, {
58 | get: (_, key: keyof T) => {
59 | // eslint-disable-next-line react-hooks/rules-of-hooks
60 | return originalMethod((state) => {
61 | //// HACK //// HACK //// HACK //// HACK //// HACK //// HACK //// HACK ////
62 | ////
63 | //// This is a workaround for accessing the current ref
64 | //// to the store outside of react components/hooks.
65 | ////
66 | result.singletonRef = state;
67 | //// HACK //// HACK //// HACK //// HACK //// HACK //// HACK //// HACK ////
68 |
69 | return state[key];
70 | }, shallow);
71 | },
72 | });
73 | };
74 |
75 | const result = Object.assign(wrapped, {
76 | originalMethod,
77 | singletonRef: null as T | null,
78 | });
79 |
80 | return result;
81 | }
82 |
--------------------------------------------------------------------------------
/apps/frontend/src/utils/generateSimpleUniqueId.ts:
--------------------------------------------------------------------------------
1 | export function generateSimpleUniqueId() {
2 | const timestamp = new Date().getTime().toString(36);
3 | const randomNum = Math.floor(Math.random()).toString(36);
4 | return `${timestamp}-${randomNum}`;
5 | }
6 |
--------------------------------------------------------------------------------
/apps/frontend/src/utils/getPseudoRandomColorFromString.ts:
--------------------------------------------------------------------------------
1 | export function getPseudoRandomColorFromString(seed: string) {
2 | let hash = 0;
3 | for (let i = 0; i < seed.length; i++) {
4 | hash = seed.charCodeAt(i) + ((hash << 5) - hash);
5 | }
6 | let colour = '#';
7 | for (let i = 0; i < 3; i++) {
8 | const value = (hash >> (i * 8)) & 0xff;
9 | colour += ('00' + value.toString(16)).substr(-2);
10 | }
11 | return colour;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/frontend/src/utils/getRandomProfileDataFiller.ts:
--------------------------------------------------------------------------------
1 | import EXAMPLES from '../data/example-profiles.yaml';
2 | import { generateSimpleUniqueId } from '../utils/generateSimpleUniqueId';
3 |
4 | export function getRandomProfileDataFiller() {
5 | console.log('EXAMPLES', EXAMPLES);
6 | const randomIndex = Math.floor(Math.random() * EXAMPLES.length);
7 | const randomExample = EXAMPLES[randomIndex];
8 | return {
9 | uid: generateSimpleUniqueId(),
10 | name: randomExample.ai_name,
11 | role: randomExample.ai_role,
12 | goals: [randomExample.ai_goal],
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/apps/frontend/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "types": ["@modyfi/vite-plugin-yaml/modules"]
19 | },
20 | "include": ["src"],
21 | "references": [
22 | {
23 | "path": "./tsconfig.node.json"
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/apps/frontend/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 | import * as dotenv from 'dotenv';
4 | import ViteYaml from '@modyfi/vite-plugin-yaml';
5 |
6 | dotenv.config();
7 |
8 | // https://vitejs.dev/config/
9 | export default defineConfig({
10 | plugins: [react(), ViteYaml()],
11 | server: {
12 | port: 7070,
13 | },
14 | define: {
15 | 'process.env': process.env,
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "auto-gpt-webui",
3 | "version": "0.1.0",
4 | "private": true,
5 | "workspaces": [
6 | "apps/*"
7 | ],
8 | "scripts": {
9 | "setup-auto-gpt": "bash scripts/setup-auto-gpt.sh",
10 | "build": "turbo run build",
11 | "start": "turbo run start",
12 | "dev": "turbo run dev",
13 | "format": "prettier --write \"**/*.{ts,tsx,md,py,json}\"",
14 | "rimraf": "npx rimraf --glob **/node_modules"
15 | },
16 | "packageManager": "npm@9.5.0",
17 | "engines": {
18 | "node": ">=14.0.0"
19 | },
20 | "devDependencies": {
21 | "eslint-config-custom": "*",
22 | "prettier": "latest",
23 | "turbo": "latest"
24 | },
25 | "prettier": {
26 | "quoteProps": "consistent",
27 | "jsxSingleQuote": true,
28 | "singleQuote": true,
29 | "trailingComma": "all",
30 | "printWidth": 80
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/mock-continuous.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | while true; do
4 | random_string=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1)
5 | echo "$random_string"
6 | sleep 0.2
7 | done
8 |
--------------------------------------------------------------------------------
/scripts/mock-spinner.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | end=$((SECONDS+2))
4 |
5 | while [ $SECONDS -lt $end ]; do
6 | for i in "." ".." "..."; do
7 | printf "$i\r%s"
8 | sleep 0.3
9 | done
10 | done
11 |
12 | # Move to a new line after the spinner stops
13 | printf "\n"
14 |
--------------------------------------------------------------------------------
/scripts/mock-user-input.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo This is a mock input script
3 | echo It is used to test handling input from the user
4 | echo Input:
5 | read number
6 | echo You entered $number
7 |
--------------------------------------------------------------------------------
/scripts/setup-auto-gpt.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | git clone https://github.com/Torantulino/Auto-GPT.git auto-gpt
3 | git -C auto-gpt checkout 4d42e14d3d3db3c64f1df0a425f5c3460bc82a56
4 | pip install -r auto-gpt/requirements.txt
5 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalDependencies": ["**/.env.*local"],
4 | "pipeline": {
5 | "build": {
6 | "dependsOn": ["^build"],
7 | "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
8 | },
9 | "dev": {
10 | "cache": false
11 | },
12 | "start": {}
13 | }
14 | }
15 |
--------------------------------------------------------------------------------