Compare commits

..

452 Commits

Author SHA1 Message Date
SwiftyOS
6c436d6137 add default keyords 2024-09-20 13:55:52 +02:00
SwiftyOS
0146a01595 fix dockerfile 2024-09-20 13:39:47 +02:00
SwiftyOS
7e3b40cef3 Update docker file formatting 2024-09-20 13:37:42 +02:00
SwiftyOS
6afd670e9d Update docker file formatting 2024-09-20 13:36:44 +02:00
Reinier van der Leer
6ae6c711b7 fix Dockerfile.autogpt 2024-09-20 13:32:49 +02:00
Reinier van der Leer
c8f55bc518 fix .pre-commit-config.yml 2024-09-20 13:27:50 +02:00
Reinier van der Leer
df2126c1a8 fix Dockerfile.autogpt 2024-09-20 13:23:15 +02:00
Reinier van der Leer
dfcfd003df fix original_autogpt path in classic/cli.py 2024-09-20 13:21:36 +02:00
Reinier van der Leer
4e33399d31 fix code workspace file (vol. 2) 2024-09-20 13:11:05 +02:00
Reinier van der Leer
369b1d9023 fix code workspace file 2024-09-20 13:10:00 +02:00
Reinier van der Leer
241f21ab5f fix classic-autogpts-ci.yml 2024-09-20 13:04:36 +02:00
Reinier van der Leer
7551782cd1 fix classic-autogpts-ci.yml 2024-09-20 13:00:57 +02:00
Reinier van der Leer
430835e539 fix classic-forge-ci.yml 2024-09-20 12:59:12 +02:00
Reinier van der Leer
f5040fa3ab fix classic-benchmark-ci.yml 2024-09-20 12:58:59 +02:00
Reinier van der Leer
6ced85d203 fix classic docker CI workflows 2024-09-20 12:53:12 +02:00
Reinier van der Leer
5e1a3d5717 fix classic-autogpts-ci.yml 2024-09-20 12:50:05 +02:00
Reinier van der Leer
d35b91cde4 delete Classic AutoGPTs Nightly Benchmark 2024-09-20 12:44:27 +02:00
Reinier van der Leer
aeab5aac67 unbreak Classic AutoGPT Docker Release workflow 2024-09-20 12:42:20 +02:00
Reinier van der Leer
31cd6dc652 move back .pre-commit-config.yaml 2024-09-20 12:39:01 +02:00
Reinier van der Leer
13b82c86f5 unbreak Classic AutoGPT CI workflows 2024-09-20 12:37:56 +02:00
SwiftyOS
ff11d00f74 isort fixes 2024-09-20 12:35:53 +02:00
SwiftyOS
9d7dfb0a6d fix type errors 2024-09-20 12:33:39 +02:00
SwiftyOS
f1bf7f269b Merge branch 'repo-restructure' of github.com:Significant-Gravitas/AutoGPT into repo-restructure 2024-09-20 12:25:02 +02:00
SwiftyOS
46cc8ae3ea more linting fixes 2024-09-20 12:24:55 +02:00
Reinier van der Leer
43bf6f2349 unbreak classic/.dockerignore 2024-09-20 12:23:49 +02:00
SwiftyOS
2582eb1ee8 Merge branch 'repo-restructure' of github.com:Significant-Gravitas/AutoGPT into repo-restructure 2024-09-20 12:18:04 +02:00
SwiftyOS
10cefc149f linitng fixes 2024-09-20 12:17:56 +02:00
Reinier van der Leer
d62fe001b8 move back .pre-commit-config.yaml 2024-09-20 12:15:51 +02:00
SwiftyOS
f583a15fd0 formatting changes 2024-09-20 12:13:33 +02:00
SwiftyOS
2cad2093eb updaing python checks ci 2024-09-20 12:04:11 +02:00
SwiftyOS
4e569f4562 add flake8 path to python checks ci 2024-09-20 11:58:52 +02:00
SwiftyOS
7f514c10cf Merge branch 'repo-restructure' of github.com:Significant-Gravitas/AutoGPT into repo-restructure 2024-09-20 11:54:56 +02:00
SwiftyOS
d7aba4f6c0 updaate python checks ci 2024-09-20 11:54:17 +02:00
Swifty
ba30aa2fce Merge branch 'master' into repo-restructure 2024-09-20 11:43:19 +02:00
SwiftyOS
efeba4400e update symbolic links 2024-09-20 11:40:38 +02:00
Aarushi
2dfc927f03 tweak(docs) add to docs supabase submodule steps (#8115)
add to docs
2024-09-20 10:39:52 +01:00
SwiftyOS
ba206e3bec docs update 2024-09-20 11:32:38 +02:00
SwiftyOS
be16fd90d4 fixing repo workflow checker CI 2024-09-20 11:20:03 +02:00
SwiftyOS
d10167ceab renamed all CI's to make it clear which subproject they are for 2024-09-20 11:14:54 +02:00
SwiftyOS
d593f76437 updating CI's 2024-09-20 11:07:14 +02:00
SwiftyOS
bda938422e fix frontend paths 2024-09-20 11:02:23 +02:00
SwiftyOS
8397b78ec2 update frontend ci 2024-09-20 10:56:10 +02:00
SwiftyOS
0d7342826b Restructureing Repo 2024-09-20 10:48:08 +02:00
Aarushi
e3f35d79c7 tweak(.github): Update pr template wording (#8103)
* update pr template wording

* add what and how

* Update .github/PULL_REQUEST_TEMPLATE.md

---------

Co-authored-by: Toran Bruce Richards <toran.richards@gmail.com>
2024-09-19 12:44:50 +00:00
Aarushi
0040495143 tweak(.github): Update PR template (#8100)
* update PR template

* Update .github/PULL_REQUEST_TEMPLATE.md

Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com>

* add note

* typo

---------

Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com>
2024-09-19 13:00:16 +01:00
Aarushi
d3eac86f9a fix(frontend): Update REST API port (#8096)
update server port to 8006

Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-09-19 01:06:04 +02:00
Zamil Majdy
c3cb90ac20 feat(rnd): Add initial block execution credit accounting UI on AutoGPT Builder (#8078) 2024-09-19 04:21:40 +07:00
matanm
9b5bf81d7c Fix typo in Groq setup docs (#8018)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-09-18 20:22:57 +00:00
Nicholas Tindle
86db4deef9 feat(server): backend analytics endpoints (#8030) 2024-09-18 18:23:20 +00:00
Aarushi
d8f989daf8 docs(rnd): Update submodules info in readme (#8095)
update submodules info in readme
2024-09-18 18:59:23 +01:00
Aarushi
00f2b134cb tweak(rnd): add env var to docker compose so no messing with .env (#8091)
add env var to docker compose so no messing with .env
2024-09-18 16:39:15 +01:00
Aarushi
a3959712dc tweak(builder): Update .env.example server url with right port (#8090)
update server url with right port
2024-09-18 15:51:44 +01:00
Aarushi
8477b25c5a tweak(builder) Add local supabse credentials (#8089)
add local supabse credentials
2024-09-18 15:45:09 +01:00
Swifty
f133c9c1ef fix(rnd): incorrect docker image for migrate (#8086)
fix incorrect docker image for migrate
2024-09-18 15:21:38 +02:00
Aarushi
dc72ec97bc feat(rnd): Add support for supabase locally (#8077)
* add just auth for now

* add supabase script

* add to docker compose

* update docker compose

* tweak(rnd) Add prefix in logs (#8001)

* add prefix

* fix typos

* fix conflicts

* feat(rnd): Reduce container size remove dep with forge and autogpt (#8040)

* Remove forge and autogpt

* update lock files

* Update build process to reduce image size

* Reduced built image size

* fixed docker compose watch

* Updated logging

* updated env.example

* formatting

* linting issue

* linting not working in github actions..

* trying to get around github action linting issue

* updated version

* sleep for prisma issues

* add exp backoff on connection issues

* updated config based on review comments

* Sorting alphabetical

* updated default config

* updated depends checks

* fixed missing prisma binaries

* remove dead layer

* remove try

* remove dead layer

* updated lock file

* add to docker compose

* update for init

* add local supabase variables to docker compose

* wip supbase connectioon

* subabase submodule

* combined docker file wth new supbase url pointing to kong

* updated combined

* ngix

* updated docker compose without frontend

* updated docker compose

* update to remove frontend

* update docs

* update newline

* remove unescessary change

---------

Co-authored-by: Swifty <craigswift13@gmail.com>
2024-09-18 09:50:39 +01:00
Nicholas Tindle
0c915cb558 feat(server): anthropic updates, csv, sampling, and code blocks (#7803)
Co-authored-by: Bentlybro <tomnoon9@gmail.com>
2024-09-17 21:29:35 -05:00
Nicholas Tindle
f6ab15db47 feat(market): add filters to the market queries (#8064) 2024-09-17 14:59:25 +00:00
Krzysztof Czerwinski
80161decb9 feat(server): Add credentials API endpoints (#8024)
- Add two endpoints to OAuth `integrations.py`:
  - `GET /integrations/{provider}/credentials` - list all credentials for a provider, without secrets (metadata only)
   - `GET /integrations/{provider}/credentials/{cred_id}` - retrieve a set of credentials (including secrets)

- Add `username` property to `Credentials` types
   - Add logic to populate `username` in OAuth handlers

- Expand `CredentialsMetaResponse` and remove `credentials_` prefix from properties

- Fix `autogpt_libs` dependency caching issue

- Remove accidentally duplicated OAuth handler files in `autogpt_server/integrations`
2024-09-17 11:16:16 +00:00
Swifty
0bf8edcd96 fix(autogpt_server): Fix vulnerability in Dockerfile (#8071) 2024-09-17 11:37:22 +01:00
Zamil Majdy
b1347a92de fix(rnd): Fix execution error on non-saved agent (#8054) 2024-09-16 19:35:31 +00:00
Nicholas Tindle
22ce8e0047 feat(builder): sentry integration (#8053) 2024-09-16 23:19:52 +07:00
Bently
5a7193cfb7 Feat(Builder): Add Runner input and ouput screens (#8038)
* Feat(Builder): Add Runner input and ouput screens

* Fix run button not working

* prettier

* prettier again -- forgot flow

* fix input scaling + auto close on run

* removed "Runner Input" button to make it auto open runner input if input block is  + Fixed issue with output not showing in output UI

* replaced runner output icon and added a new icon for it

* replaced IconOutput icon with LogOut from lucide-react

* prettier

* fix type safety issue + add error handling for formatOutput

* Updates based on comments

* prettier for utils
2024-09-16 13:05:07 +02:00
Zamil Majdy
c1f301ab8b feat(rnd): Add initial credit accounting system for block execution (#8047)
### Background

We need a way to set an execution quota per user for each block execution.

### Changes 🏗️

* Introduced a `UserBlockCredit`, a transaction table tracking the block usage along with it cost/quota.
* The tracking is toggled by `ENABLE_CREDIT` config, default = false.
* Introduced  `BLOCK_COSTS` | `GET /blocks/costs` as a source of information for the cost on each block depending on the input configuration.

Improvements:
* Refactor logging in manager.py to always print a prefix and pass the metadata.
* Make executionStatus on AgentNodeExecution prisma enum. And add executionStatus on AgentGraphExecution.
* Use executionStatus from AgentGraphExecution to improve waiting logic on test_manager.py.
2024-09-14 23:47:28 +07:00
Zamil Majdy
f32244a112 fix(rnd): Fix broken save feature on Agent Builder (#8052) 2024-09-13 18:04:51 -05:00
Aarushi
9395706841 fix(rnd,market): Fix docker issues with market, and DB connection (#8050)
fix docker issues with market, and DB connection
2024-09-13 16:15:06 +01:00
SwiftyOS
a98677b79d Revert "updated lock file"
This reverts commit 056eb46c0f.
2024-09-12 17:59:39 +02:00
SwiftyOS
056eb46c0f updated lock file 2024-09-12 16:18:13 +02:00
Swifty
6fde030c37 feat(rnd): Reduce container size remove dep with forge and autogpt (#8040)
* Remove forge and autogpt

* update lock files

* Update build process to reduce image size

* Reduced built image size

* fixed docker compose watch

* Updated logging

* updated env.example

* formatting

* linting issue

* linting not working in github actions..

* trying to get around github action linting issue

* updated version

* sleep for prisma issues

* add exp backoff on connection issues

* updated config based on review comments

* Sorting alphabetical

* updated default config

* updated depends checks

* fixed missing prisma binaries

* remove dead layer

* remove try

* remove dead layer
2024-09-12 13:03:37 +02:00
Aarushi
bf1e01d423 tweak(rnd) Add prefix in logs (#8001)
* add prefix

* fix typos

* fix conflicts
2024-09-12 11:48:47 +01:00
Zamil Majdy
52c731abd6 fix(rnd): Fix decorator function type hint (#8043) 2024-09-12 05:35:33 +07:00
Aarushi
c8fbce643e fix(rnd): Add connection timeout (#8041)
add connection timeout
2024-09-11 18:32:07 +01:00
Swifty
6c001bd595 Create Input Node Custom UI Node (#8016) 2024-09-11 14:14:03 +02:00
Aarushi
f5b89672f8 feat(rnd): Add k8s default health check (#8037)
add k8s default health check
2024-09-11 12:30:34 +01:00
Aarushi
76480ffa03 fix(rnd): Update port in market (#8036)
update port
2024-09-11 12:19:11 +01:00
Aarushi
ab60a57379 tweak(rnd): Ignore .env in market (#8035)
ignore .env
2024-09-11 11:01:34 +01:00
Aarushi
1d9b01fc77 tweak(rnd): Use docker compose not docker-compose (#8034)
* use docker compose not docker-compose

* linting
2024-09-11 10:20:31 +01:00
Swifty
e81d9f9f0b docker nits (#8033) 2024-09-11 10:31:12 +02:00
Bentlybro
0d5d0270ea Merge branch 'master' of https://github.com/Significant-Gravitas/AutoGPT 2024-09-10 18:24:53 +01:00
SwiftyOS
bd25f9223c expose schedular port and fix marketplace port 2024-09-10 17:09:39 +02:00
SwiftyOS
07305b55ff fix(rnd) use migrate deploy 2024-09-10 16:18:42 +02:00
SwiftyOS
cdfe3e5fbc Updated rnd/README.md 2024-09-10 16:16:16 +02:00
Swifty
e992cdf8c2 Fixing docker setup for local testing (#8026)
* Fixing docker setup

* Updated docker compose setup

* update helm charts

* Corrected agent server host name
2024-09-10 15:46:22 +02:00
Aarushi
ebd2ecd84c docs(server): Update docs (#8031)
update docs
2024-09-10 10:24:54 +01:00
Aarushi
0b919522ae feat(rnd): Split Execution Manager (#8008)
* split execution manager and removed ns and use direct uri with k8s and docker specific dns

* formating

* split execution manager

* refactor(builder): Fix linting warning and errors (#8021)

* Fix lint errors

* Fix dependency loop

* address feedback

* docker compose

* remove ns entirely

* remove yarn lock changes

* update readme

* remove ref

* dockerfile and log

* update log

* debug

* rename to executor

* remove execution from rest

* exec.py

* linting

* udpate tests to use config

* fix test

---------

Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com>
2024-09-10 10:05:31 +01:00
Nicholas Tindle
ef691359b7 feat: document the use of isolation better (#8028) 2024-09-09 20:55:05 +00:00
Aarushi
f8815c3053 fix(builder): Use escaped apostrophe (#8027)
* use escaped apostrophe

* linter
2024-09-09 18:08:32 +00:00
Reinier van der Leer
a60ed21404 feat(server): Add OAuth flow endpoints for integrations (#7872)
- feat(server): Initial draft of OAuth init and exchange endpoints
  - Add `supabase` dependency
  - Add Supabase credentials to `Secrets`
  - Add `get_supabase` utility to `.server.utils`
  - Add `.server.integrations` API segment with initial implementations for OAuth init and exchange endpoints
- Move integration OAuth handlers to `autogpt_server.integrations.oauth`
- Change constructor of `SupabaseIntegrationCredentialsStore` to take a Supabase client
- Fix type issues in `GoogleOAuthHandler`
2024-09-09 17:21:56 +02:00
Krzysztof Czerwinski
2618d1d87c refactor(builder): Fix linting warning and errors (#8021)
* Fix lint errors

* Fix dependency loop
2024-09-09 09:54:36 +02:00
Andy Hooker
e17ea22a0a feat(builder): Aligning error and loading for NextJS best practices (#7894)
* feat(builder): Add skeleton loading components for Monitor views

Introduce skeleton components for Agents, Flow Runs List, and Flow Runs Status sections to enhance loading state indication. These components help improve user experience by visually outlining content placeholders while data is being fetched.

* feat(builder): Leveraging NextJS's error boundary with error.tsx

Replace the basic error page with a more detailed and interactive error component. The new component includes a retry option, a link to the homepage, and logs the error details to the console. It also aligns with NextJS standards

---------
2024-09-08 12:47:36 +02:00
Nicholas Tindle
60669903a0 fix: couple block categories improved (#8017) 2024-09-06 22:47:40 -05:00
Zamil Majdy
b1b31390a4 feat(rnd): Add creation & update time for AgentGraphExecutionSchedule, AgentGraphExecution, and AgentGraph (#8015) 2024-09-06 21:29:53 +00:00
Nicholas Tindle
3c12a398ae feat: marketplace analytics (#7998) 2024-09-06 19:22:24 +00:00
Aarushi
126d070396 tweak(rnd,redis) Make redis logging more clear (#8014)
make redis logging more clear
2024-09-06 15:23:33 +00:00
Reinier van der Leer
090f22b05c fix(server): Improve logging consistency (#8012)
- Make process/service startup/shutdown messages consistent
- Configure `uvicorn` to use our logging config instead of its own
- Replace `print(..)` statements in ws_api.py with log statements
- Improve log statements in ws_api.py
2024-09-06 17:05:30 +02:00
Reinier van der Leer
1b9adf5434 fix(server): Always JSON-encode AgentNodeExecutionInputOutput data (#8010)
- Handle JSON-encoding inside `.data.execution.upsert_execution_output(..)` to ensure it is always encoded the same
- Amend `.executor.manager.execute_node(..)` to pass unencoded data into `upsert_execution_output(..)`
2024-09-06 16:58:04 +02:00
Reinier van der Leer
3bd8040d6a feat(server): Clean up resources when spinning down services/processes (#7938)
- Add SIGTERM handler and `cleanup()` hook to `AppProcess`
- Implement `cleanup()` on `AppService` to close DB and Redis connections
- Implement `cleanup()` on `ExecutionManager` to shut down worker pool
- Add `atexit` and SIGTERM handlers to node executor to close DB connection and shut down node workers
- Improve logging in `.executor.manager`
- Fix shutdown order of `.util.test:SpinTestServer`
2024-09-06 16:50:59 +02:00
Aarushi
b12dba13f4 docs(readme) Update readme to include non docker steps (#8013)
update readme
2024-09-06 15:33:17 +01:00
Swifty
2cae9ba8da feat(server): Updated Output block (#7997)
* Add Block UI Types and StickyNote Block

* Renamed StickyNote to Note

* Add comment

* Updated Input Block

* rename default_values to placeholder_values

* Update sample graph

* Update input block output to match previous change

* fixing test

* Updated the output block

* re-remove old output block
2024-09-06 15:56:41 +02:00
Swifty
3753906482 feat(builder): Block UI Types and StickyNote (#7994)
* Add Block UI Types and StickyNote Block

* Renamed StickyNote to Note

* Add comment
2024-09-06 15:52:03 +02:00
Swifty
fd54ad8666 update(server): update input nodes (#7996)
* Add Block UI Types and StickyNote Block

* Updated Input Block
2024-09-06 13:27:37 +01:00
Zamil Majdy
e645cc4b33 tweak(rnd): Remove duplicated call 2024-09-05 17:20:55 -05:00
Zamil Majdy
010a8ffaaf tweak(rnd): Stop publishing custom Sentry metrics (#8000) 2024-09-05 21:51:00 +00:00
Zamil Majdy
2df325d033 fix(rnd): Fix prisma connection acquisition intermittent error on linux (#7999) 2024-09-06 03:22:25 +07:00
Nicholas Tindle
79ebc4c13b feat(builder): promotion/demotion of featured agents (#7932) 2024-09-05 20:04:11 +00:00
Zamil Majdy
e5eb42d84a tweak(rnd): Post infra change cleanup - fix process creation lifecycle (#7981) 2024-09-06 01:41:24 +07:00
Bently
d62b940baf Feat(Monitor/Builder): Fix loading of "Import from file" modal (#7976)
* Feat(Monitor/Builder): Fix loading of "Import from file" modal

* prettier

* prettier - forgot page.tsx
2024-09-05 15:02:48 +00:00
Reinier van der Leer
8fd22bcfd7 feat(server, builder): Implement "STOP" button for graph runs (#7892)
- feat(builder): Add "Stop Run" buttons to monitor and builder
  - Implement additional state management in `useAgentGraph` hook
    - Add "stop" request mechanism
    - Implement execution status tracking using WebSockets
    - Add `isSaving`, `isRunning`, `isStopping` outputs
    - Add `requestStopRun` method
      - Rename `requestSaveRun` to `requestSaveAndRun` for clarity
  - Add needed functionality for the above to `AutoGPTServerAPI` client
    - Add `stopGraphExecution` method
    - Add support for multiple handlers per WebSocket method
    - Fix parsing of timestamps in `execution_event` WebSocket messages
  - Add `IconSquare` from Lucide to `@/components/ui/icons`

- feat(server): Add `POST /graphs/{graph_id}/executions/{graph_exec_id}/stop` route
  - Add `stop_graph_run` method to `AgentServer`

- feat(server): Add `cancel_execution` method to `ExecutionManager`
  - Replace node executor `ProcessPoolExecutor` by `multiprocessing.Pool` (which has a `terminate()` method)
    - Remove now unnecessary `Executor.wait_future(..)` method
  - Add `get_graph_execution(..)` in `.data.execution`

- fix(server): Reduce number of node executors to 5 per graph executor
  This is necessary because `multiprocessing.Pool` spawns its workers on init, instead of based on demand like `ProcessPoolExecutor` does

- dx(server): Improve debug logging in `ExecutionManager`
- ci(server): Add debug logging mode to CI Pytest step

### Other improvements
Server:
- Improve output type of `ExecutionManager.add_execution(..)`
- Renamed a few things in `.server.rest_api` for consistency

Front end:
- Improved typing in `AutoGPTServerAPI` client
2024-09-05 14:42:28 +02:00
Bently
11827835a0 Feat(Builder): Add tooltips to Blocks and Save buttons (#7975) 2024-09-05 12:04:29 +00:00
Zamil Majdy
70fab8711a fix(rnd): avoid duplicating name on input/output pin for blocks (#7979) 2024-09-05 10:54:02 +00:00
Swifty
8ec015ba72 fix(builder): update tutorial routing to work on safari (#7992) 2024-09-05 10:27:15 +00:00
SwiftyOS
bc7d2f0f37 added test password as default for redis queue 2024-09-05 11:41:53 +02:00
Zamil Majdy
54694709bb tweak(rnd): Hide non required field on Blocks (#7977)
Add prompt_values for LLM block, and make it non-advanced.
Make all-field, advanced by default.
cleanup .env.example and .env.template
2024-09-05 05:00:14 +07:00
Zamil Majdy
b4b5a09b6b fix(rnd): Dockerfile Avoid full rebuild on each file change (#7971)
Co-authored-by: Aarushi <50577581+aarushik93@users.noreply.github.com>
2024-09-04 17:30:13 +00:00
Reinier van der Leer
82239dd129 fix(server): Fix node input concurrency deadlock (#7936)
In `autogpt_server.util.lock:KeyedMutex`:
- track number of pending requests for each lock
- only remove a lock from `self.locks` when the number of pending lock requests hits 0
2024-09-04 18:13:35 +02:00
Aarushi
078ad29356 update docker compose (#7972) 2024-09-04 16:19:41 +01:00
Aarushi
5000aa7ee0 tweak(rnd,docker) Remove SQLite (#7966)
* move migrations, update networking and dockignore

* update docs

* remove sqlite from ci

* remove schema linting checks

* fix formatting

* remove schema linting

* add test script

* formatting and linting

* stop pg not down

* seperate test db

* diff port

* remove duplicate
2024-09-04 10:18:57 +01:00
Bently
dc1077f893 Feat(Builder): Replace `Math Block with Calculator Block` (#7969) 2024-09-04 08:12:01 +00:00
Swifty
80df44a978 feat(server): Add endpoint to calculate required graph inputs (#7965) 2024-09-04 09:45:45 +02:00
Zamil Majdy
c2a79d2f10 feat(rnd): Add Node & Graph level execution stats instrumentation (#7957)
### **User description**
### Background

The scope of this change is collecting the required information that will be needed for the execution analytics.

### Changes 🏗️

* Add sentry integration.
* Refactor logging_metadata on manager.py.
* Collect graph-level & node-level instrumentation.
* Introduced `stats` column for `AgentNodeExecution` & `AgentGraphExecution`.
2024-09-04 02:45:19 +07:00
Bently
7db85a8990 Feat(Builder): Update tutorial to work with latest block UI (#7962)
* Feat(Builder): Update tutorial to work with latest block UI

* prettier
2024-09-03 14:56:55 +00:00
Krzysztof Czerwinski
0454a9a7be fix(builder): Prevent zooming on input field modal
- Add `nowheel` class to Textarea parent div
2024-09-03 14:30:35 +02:00
Krzysztof Czerwinski
09951fed4b feat(builder, server): Add advanced block inputs (#7934)
- Add `advanced` to `SchemaField` and pass it to `json_extra`
- Add `advanced` to `BlockIOSubSchemaMeta` type
- Update `CustomNode`, so that:
  - non-required advanced inputs are hidden
  - non-advanced and required inputs are always shown
2024-09-03 12:01:42 +01:00
Aarushi
6204d82d84 feat(server): Integrate forge.logging (#7915)
* feat(server): Integrate `forge.logging`

- Add `configure_logging()` in `.util.logging` - a wrapper for `forge.logging.configure_logging()` with project-specific extras
- Call `configure_logging()` in `.app.main()`, and in child process initializers (e.g. `AppProcess.execute_run_command(..)`, `ExecutorManager.on_graph_executor_start()`)
- Change some `logger.warning` statements to `logger.info` where appropriate

* fix warnings to info

* fix(rnd): Fix broken test and Input/Output block field renaming

* Rename

* fix(rnd): Fix flaky CI

* feat(server): Add OAuth handlers for GitHub, Notion, Google & amend store data structure (#7868)

- Add `BaseOAuthHandler` + 3 initial implementations
  - Add `GitHubOAuthHandler`
  - Add `NotionOAuthHandler`
  - Add `GoogleOAuthHandler`
- Amend `OAuth2Credentials` type
  - Add `metadata` attribute
  - Make `access_token_expires_at`, `refresh_token`, `refresh_token_expires_at` optional

* extend GCP Logger

* update manager & add flag

* linting

* use default logger behaviour

* update messages

* update another message

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-09-03 11:56:21 +01:00
Krzysztof Czerwinski
8c9fe5c167 feat(builder): Resizable input modal (#7955)
- Add minimize/maximize button in the corner of modal to make it significantly larger and centered
- Add copy button to copy all text
- Add optional `title` to display as a modal header
2024-09-03 11:42:25 +01:00
Reinier van der Leer
71de1a6a5e fix(server): Fix type checking and propagation issues (#7941)
- fix type propagation by `AppService.run_and_wait(..)`
- fix type propagation by `@expose` and add note
- fix type propagation by `wait(..)` in `.executor.manager.execute_node(..)`
- fix type propagation by `wait(..)` in `.executor.manager._enqueue_next_nodes(..)`
- remove unnecessary null checks for `.data.graph.get_node(..)`
- fix type issue in `ExecutionScheduler`
- reduce use of `# type: ignore` in `.data.execution`
- reduce usage of `# type: ignore` in `.executor.manager`
- reduce usage of `# type: ignore` in `.server`
- reduce usage of `# type: ignore` in cli.py
- update `pyright` to v1.1.378
2024-09-02 14:13:56 +00:00
Bently
956165adf3 Feat(Builder): Fix block menu width (#7946) 2024-09-02 09:54:52 +00:00
Aarushi
e4dc16a867 docs(server): Update docs with new docker compose steps (#7944)
* update setup

* add docker links
2024-09-02 10:55:54 +02:00
Aarushi
cfa0b6610c feat(rnd,infra): Pull out websockets away from server api (#7899)
* standalone websocket server

* add websocket url

* wip: talk to ws directly

* rename to api server

* dockerfile and queue

* fix paths

* update poetry lock

* helm charts for websockets

* create seperate deployments for websockets and rest server with redis queue for async comms

* delete duplicate queue

* add depends in ws_api

* singleton for conn manager

* update from review

* fix CI

* address feedback

* update readme

* update docker file and add migration step in readm

* ad watch

* add step to copy example env file

* put connect back in
2024-09-02 10:32:51 +02:00
Krzysztof Czerwinski
933baa0e8d feat(builder): Store and display output data history in nodes (#7930)
- Update styling and use tailwind more
- Add `react-toast` dependency
- Fix output button not changing checked state on execution
- Make status a badge in node's corner
- Rename `output_data` to `executionResults` and store multiple results with execution UUIDs
- Add `DataTable` component that displays execution outputs
- Outputs can be copied and there's a toast displayed as a feedback
2024-09-01 11:18:57 +01:00
Bently
370b2dabe8 Feat(Builder): Add block categories to block menu (#7918) 2024-08-31 19:22:37 +00:00
Nicholas Tindle
baa00a5b03 feat(builder,server): add review of agent submissions to the admin portal (#7914)
Co-authored-by: Aarushi <aarushik93@gmail.com>
2024-08-30 18:20:59 +00:00
Zamil Majdy
60a8e00578 fix(rnd): Fix overflowing card due to long block name (#7931) 2024-08-30 18:02:55 +00:00
Bently
85e7d678ce Feat(Builder): Fix Multiple Connections Between Pins (#7924) 2024-08-30 18:15:18 +01:00
Krzysztof Czerwinski
476b307d69 feat(builder): Draw selected edges above nodes (#7893) 2024-08-30 16:39:22 +01:00
Bently
5dbfb4e3f1 feat(builder): Turn block border red on error (#7910)
* Turn block border red on error

* prettier
2024-08-30 15:09:19 +00:00
Zamil Majdy
f6d09c74f5 feat(rnd): Rename Blocks (#7925)
### Background

Standardize block names with a focus of making them intuitive to a non-technical person.

### Changes 🏗️

Replace these names:
* TextLlmCallBlock -> AITextGeneratorBlock
* ObjectLlmCallBlock -> AIStructuredResponseBlock
* AdvancedLlmCallBlock -> AIConversationBlock
* CreateTalkingAvatarClipBlock -> CreateTalkingAvatarVideoBlock
* DiscordReaderBlock -> ReadDiscordMessagesBlock
* DiscordMessageSenderBlock -> SendDiscordMessageBlock
* YouTubeTranscriberBlock -> TranscribeYouTubeVideoBlock
* CreateMediumPostBlock -> PublishToMediumBlock
* ForEachBlock -> ListIteratorBlock
* MathsBlock -> CalculatorBlock
* CounterBlock -> CountItemsBlock
* ValueBlock -> StoreValueBlock
* PrintingBlock -> PrintToConsoleBlock
* DictionaryAddEntryBlock -> AddToDictionaryBlock
* ListAddEntryBlock -> AddToListBlock
* ObjectLookupBlock -> FindInDictionaryBlock
* RedditGetPostsBlock -> GetRedditPostsBlock
* RedditPostCommentBlock -> PostRedditCommentBlock
* WikipediaSummaryBlock -> GetWikipediaSummaryBlock
* WebSearchBlock -> SearchTheWebBlock
* WebScraperBlock -> ExtractWebsiteContentBlock
* GetOpenWeatherMapBlock -> GetWeatherInformationBlock
* HttpRequestBlock -> SendWebRequestBlock
* CurrentTimeBlock -> GetCurrentTimeBlock
* CurrentDateBlock -> GetCurrentDateBlock
* CurrentDateAndTimeBlock -> GetCurrentDateAndTimeBlock
* TimerBlock -> CountdownTimerBlock
* RSSReaderBlock -> ReadRSSFeedBlock
* TextMatcherBlock -> MatchTextPatternBlock
* TextParserBlock -> ExtractTextInformationBlock
* TextFormatterBlock -> FillTextTemplateBlock
* TextCombinerBlock -> CombineTextsBlock
2024-08-30 21:36:42 +07:00
Zamil Majdy
6d17e627e8 fix(rnd): Add null checking & remove console logs on Input UI component (#7927)
fix(rnd): And null checking & remove logs
2024-08-30 12:21:09 +00:00
Bently
5cfa807f00 Feat(Builder): Make the copy block button work (#7920)
* Feat(Builder): Make the copy block button work

* prettier

* Fixes

* fix block id

* prettier

* fix type
2024-08-30 11:48:33 +00:00
Bently
6fff06f0f6 Feat(Builder): Fix delete block is non-undoable (#7922)
* Feat(Builder): Fix delete block is non-undoable

* prettier
2024-08-30 08:05:37 +00:00
Aarushi
cbe553a547 feat(infra): Add deployment for Market (#7907)
* deployment for marketplace

* set up deployment for marketplace
update nodes

update helm

* update health check & allow builder origin
2024-08-29 18:23:40 +00:00
Reinier van der Leer
96ef35536c ci(server): Run tests even if linting fails 2024-08-29 15:42:49 +02:00
Reinier van der Leer
087d3a3760 test(server): Add type to server fixture usages 2024-08-29 15:42:15 +02:00
Zamil Majdy
5da58aa284 fix(rnd): Fix jumping caret problem on builder input text field (#7917)
Issue 1:
Input text field cursor keeps moving to the end of the text.
Try to type "Hello World!" into the input text. Then try to type "some string" in the middle of the "Hello" and "World".

Issue 2:
History should only tracks on the input box onBlur/onLeave
Try to type a "longcharacters" and try to undo it, the undo is removing 1 character at a time, polluting the history, and make the undo pretty much unusable.

Issue 3:
KeyValue & ArrayInput is non-undoable.
Try to add key-value or add an entry to the list, it doesn't undo the value, but you need to click as many number of entries being added to make the undo work again
2024-08-29 13:12:10 +00:00
Bently
7de12a2200 Feat(Builder): Add first guide tutorial (#7862)
* Feat(Builder): Add first guide tutorial

* added more steps + some fixes

* added local storage to fix starting every time going to build

* update copy & paste to support mac

* small fix

* Prettier fixes

* Added "Skip Tutorial" button to first step

* some fixes based on requests

* revert camelCase change

* add ability to use url to reset tutorial

* prettier

* Added Tutorial button next to tally

* prettier

* change pinBlocksPopover to setPinBlocksPopover

* fixes + update + prettier

* made the resetTutorial url dynamic

* force to /build on reset tutorial

* fix renaming

* prettier
2024-08-29 07:53:45 -05:00
Reinier van der Leer
8f1c63a7ea feat(server): Add OAuth handlers for GitHub, Notion, Google & amend store data structure (#7868)
- Add `BaseOAuthHandler` + 3 initial implementations
  - Add `GitHubOAuthHandler`
  - Add `NotionOAuthHandler`
  - Add `GoogleOAuthHandler`
- Amend `OAuth2Credentials` type
  - Add `metadata` attribute
  - Make `access_token_expires_at`, `refresh_token`, `refresh_token_expires_at` optional
2024-08-29 09:11:37 +00:00
Zamil Majdy
6ec200f912 fix(rnd): Fix flaky CI 2024-08-28 15:59:51 -05:00
Zamil Majdy
b5db7f575e Rename 2024-08-28 15:56:02 -05:00
Zamil Majdy
98c909f99f fix(rnd): Fix broken test and Input/Output block field renaming 2024-08-28 15:54:48 -05:00
Krzysztof Czerwinski
c5615aa862 fix(builder): Prevent overflow in node output (#7912) 2024-08-28 16:27:42 +01:00
Bently
e725305e15 fix(builder): Set default value in NodeStringInput to silence uncontrolled input warning (#7909)
add default value to fix bug
2024-08-28 12:44:06 +01:00
Aarushi
9551f54c35 feat(infra): Add websockets IP (#7905)
tf changes from original branch to unblock other work
2024-08-27 20:35:31 +01:00
Bently
777f7d25bf Fix copy-paste of text creating unintended blocks (#7903) 2024-08-27 11:49:35 +01:00
Zamil Majdy
ea6f37bf98 feat(rnd): Integrate Jinja2 into TextFormatter (#7891)
Add Jinja2 to TextFormatter while maintaining backward compatibility.
2024-08-27 07:17:57 +00:00
Krzysztof Czerwinski
299530cf95 refactor(builder): Update ReactFlow to version 12 & split up Flow.tsx (#7808)
Update ReactFlow to version 12 and split `Flow.tsx` into `useAgentGraph` hook that takes care of agent state and API calls to the server.

- Update ReactFlow to v12 ([migration guide](https://reactflow.dev/learn/troubleshooting/migrate-to-v12))
- Move `setIsAnyModalOpen` to `FlowContext`
- Make `setHardcodedValues` and `setErrors` functions of `CustomNode` and utilize new `updateNodeData` ReactFlow API
- Fix type errors
- `useAgentGraph` hook
  - Take care of all API calls, websocket, agent state and logic
  - Make saving and execution async and thus more consistent and reliable
	- Save&run requests are state
	- Wait for node ids to sync with backend reactively
	- Queue execution updates
  - Memoize functions using `useCallback`
2024-08-26 11:45:05 +01:00
Nicholas Tindle
1df7d527dd Remove debug (#7890) 2024-08-25 17:59:41 +00:00
Aarushi
407cf858e7 fix(server): Ensure nameserver is started before other processes (#7788)
ensure nameserver is started before other processes
2024-08-24 20:54:11 +00:00
Aarushi
a670b384f6 tweak(infra): Add 1 more node to GKE (#7889)
add one more node
2024-08-24 18:53:24 +01:00
Zamil Majdy
f9b8b0a41a feat(rnd): Add dynamic input pin for input object construction (#7871)
### Background

Currently, there is no way to construct the output of nodes into a composite data structure (list/dict/object) using the builder UI.

The backend already supports this feature by connecting the output pin to the input pin using these format:
* <pin_name>_$_<list_index> for constructing list
* <pin_name>_#_<dict_key> for constructing dict
* <pin_name>_@_<field_name> for constructing object

The scope of this PR is implementing the UX for this in the builder UI.

### Changes 🏗️

<img width="765" alt="image" src="https://github.com/user-attachments/assets/8fc319a4-1350-410f-98cf-24f2aa2bc34b">

This allows you to add more pins in a key value & list input: `_$_` list constructor & `_#_` dict constructor.
2024-08-23 18:21:38 +00:00
Zamil Majdy
e59e138352 fix(rnd): Prevent boolean with no default value on AGPT-builder (#7884)
### Background

Boolean without default value is a UX problem. It's currently displayed as a toggle and it has no way to describe the `null` value.
So we need to prevent blocks from introducing a nullable boolean.

### Changes 🏗️

Add explicit check to prevent nullable boolean. Fix existing block field that has nullable boolean.
2024-08-23 18:03:21 +00:00
Aarushi
a95ee693dd fix(rnd): Add missing libs folder (#7887)
add missing libs folder
2024-08-23 16:42:24 +00:00
Aarushi
26f56114d1 feat(infra): Add ws server infra changes (#7886)
add ws server infra changes
2024-08-23 11:25:57 -05:00
Aarushi
45ace8ccab Add number of workers to environment variables in in charts (#7869)
add workers to env vars
2024-08-23 20:21:51 +07:00
Aarushi
95af63b5ad feat(libs): Add integration credentials store (#7826)
- Add `SupabaseIntegrationCredentialsStore` in `.supabase_integration_credentials_store`
- Add `supabase` dependency
- Add `pydantic` dependency

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-08-23 12:39:26 +02:00
Aarushi
012bad72e8 feat(rnd,blocks): Add D-ID Block (#7798)
* talking head

* linting

* remove clip id, not needed

* add more descriptive name

* add min requirement to polling attempts and intervals

* add docs and link to docs

* remove extra space

* force new tab

* fix linting

* add did key to .env.template
2024-08-23 09:39:55 +01:00
Nicholas Tindle
efcd0f93ed ci(all): take two on the global status checker (#7849)
* ci(all): fundamentally change how we do this

* Update workflow-checker.yml

* ci: dupe and move file in attempt to figure out why its not accessible

* Update workflow-checker.yml

* fix: sleep before checking, move files, improve code

* Update workflow-checker.yml

* Update check_actions_status.py

* Update check_actions_status.py

* Update workflow-checker.yml

* Update workflow-checker.yml

* ci: remove debug, add more

* Update check_actions_status.py

* Update check_actions_status.py

* Update check_actions_status.py

* Revert "Update check_actions_status.py"

This reverts commit 36134527af.

* Revert "Update check_actions_status.py"

This reverts commit 828aabf532.

* Update check_actions_status.py

* Update check_actions_status.py

* Update check_actions_status.py

* Update check_actions_status.py
2024-08-22 07:06:15 -05:00
Swifty
4c32b46d40 feat(builder): Add google analytics to the project (#7860) 2024-08-22 10:24:12 +01:00
Konstantinos Voulgaropoulos
41fbfe35fb tweak(rnd): Trim Whitespace from BlockSecret to Prevent Authentication Issues (#7789)
* ensure secret value of BlockSecret is always trimmed

* avoid logging secret values when trimming
2024-08-21 14:44:26 -05:00
Zamil Majdy
c719e4f177 fix(rnd): Fix JS event-loop freeze caused by websocket connection retry (#7861)
### Background

Websocket connection retry has no backoff period which causes event-loop freeze.

### Changes 🏗️

Add a back-off period on retry.
2024-08-21 21:41:15 +07:00
Zamil Majdy
3d62cec553 fix(rnd): Guarantee execution ordering per node by waiting the node completion (#7855)
### Background

We don't have an ordering guarantee on the node execution.
Let's say we have a node that has to execute different data A, B, and C.
The current implementation limits the execution to 1 execution at a time, but there is no guarantee that A, B, and C will be executed in order.

The initial implementation did not have any restrictions, so it used to be A, B, and C executed in parallel
In the current implementation with the per-node constraint, it's A, B, C are executed serially but with no guarantee of ordering.

The scope of this PR is to guarantee that order.

### Changes 🏗️

Guaranteeing the execution per node ordering by avoiding any re-enqueue mechanism. If there are two executions run in the same node, the first one will be executed and the other will block. The blocking mechanism is indeed sub-optimal, the performance improvement can be done later (a follow-up issue will be added).
2024-08-21 19:08:18 +07:00
Aarushi
fa12564954 bug(infra): Add frontend config to ingres (#7854)
add frontend config to ingres
2024-08-21 11:31:16 +01:00
Nicholas Tindle
f6d8e597e1 clean(builder): learned we should be using this to keep our tailwind classes sorted (#7836) 2024-08-21 05:14:21 -05:00
Krzysztof Czerwinski
a1cbc101a5 fix(builder): Correctly display static links on new edges (#7851)
Fix static links on edge creation
2024-08-21 10:24:01 +01:00
Aarushi
afc8338145 feat(infra) Create builder account with necessary permissions (#7840)
* create builder account with necessary permissions

* remove unrelated changes
2024-08-21 10:18:51 +01:00
Aarushi
7fe4e455fd feat(infra): Ensure http is always redirected to HTTPS (#7853)
redirect to https always
2024-08-21 10:01:06 +01:00
Bently
52d40d0f8b Feat(Builder): Make blocks spawn in the center of the screen not at 0,0 (#7805)
Feat(Builder): Make nodes spawn in the center of the screen not at 0,0
2024-08-20 15:05:12 +01:00
Aarushi
9e35f8c5cb tweak(infra): Rename builder for consistency (#7837)
rename builder for consistency
2024-08-20 13:37:40 +01:00
Nicholas Tindle
c0afb133a7 feat(builder): checkbox for tos on login page and submit agent (#7745)
* feat(builder): checkbox for tos on login page

* feat(builder): submit agent page

DOES NOT WORK

* feat(builder): basic upload (not working)

* feat(builder): submit page more working but still not

* fix(builder): working categories, not dynamic

* feat(builder, server): enable submissions (auth error)

* fix(lint): linting

* feat(builder): submit page terms of service

* fix(builder): update lockfile

* lint(builder): lint marketplace files
2024-08-20 07:04:22 -05:00
Zamil Majdy
526364297c feat(rnd): Add staticOutput field on block API (#7802) 2024-08-16 22:13:10 +07:00
Krzysztof Czerwinski
aed067e61c feat(builder): Support static connections (#7799)
- Add static link/connection support on the frontend and display them as dashed lines
- Remove queueing for static connections - there'll always be only one bead waiting at the end
- Make beads slightly larger and further from the end arrow
2024-08-16 13:39:48 +01:00
Zamil Majdy
653eb4964f fix(rnd): Fix graph validation error message (#7797) 2024-08-16 17:10:11 +07:00
SwiftyOS
406206f5d0 Add categories to all the blocks 2024-08-16 10:11:30 +02:00
SwiftyOS
1e05d6a8e9 added categories 2024-08-16 10:10:18 +02:00
Krzysztof Czerwinski
848637bfeb feat(builder): Visualise data beads on connections (#7791)
* Visualise data beads on edges

* Add `useBezierPath` hook

* Fix edge color on load

* Updates

* Merge branch 'master' into kpczerwinski/open-1580-visualise-data-coming-down-connections

* Add `visualizeBeads` state in `FlowEditor`

* Add `FlowContext`

Allow disabling beads animation

* fix(builder): linting

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-14 13:29:19 -05:00
Swifty
cea81bfe4e feat(builder): Updated Block Categories and colouring (#7794)
* Updated Block Categories

* formatting

* Added category coloring
2024-08-14 11:06:01 +02:00
Zamil Majdy
1e92c284d9 fix(rnd): Fix text blocks, make outbound links on ValueBlock static (#7796) 2024-08-14 10:03:43 +01:00
Swifty
98c1cb8ff9 feat(builder): Added tooltips for blocks (#7793) 2024-08-14 10:41:01 +02:00
Zamil Majdy
58dc8296db fix(rnd): Fix unparsed string on HTTP block (#7795) 2024-08-14 06:19:46 +02:00
Zamil Majdy
4782f4383c fix(rnd): string to list auto-conversion 2024-08-13 18:08:24 +02:00
Zamil Majdy
2b60a392fb fix(rnd): Fix broken test 2024-08-13 17:51:59 +02:00
Konstantinos Voulgaropoulos
f30b2cdf25 inclusion of poetry run prisma generate in the documentation (#7790) 2024-08-13 10:46:28 -05:00
Zamil Majdy
9084c31662 fix(rnd): Make discord bot continuously running (#7792) 2024-08-13 10:39:39 -05:00
SwiftyOS
183c72b2d0 fix typo 2024-08-13 15:07:57 +02:00
Swifty
55e100ee1e tweak(builder) Changes to transparency, connector color and title boldness (#7766)
Changes to transparency, connector color and title boldness
2024-08-13 15:07:07 +02:00
Toran Bruce Richards
82c5cd2d79 Add Advanced Chatbot with History using Discord (#7786)
* Add Advanced Chatbot with History using Discord

* Update Discord Chatbot with History_v145.json

update is_active to false and is_template to true

---------

Co-authored-by: Bently <tomnoon9@gmail.com>
2024-08-13 09:03:14 +01:00
Aarushi
f0ab795248 fix(builder,server) Add missing create user calls (#7784)
* fix missing create user calls

* formating and linting
2024-08-09 16:49:21 +01:00
Zamil Majdy
5b9caa4345 fix(rnd): Added run-time implicit type conversion on agent node execution (#7775)
The execution graph is supposed to be typed, but there are cases where generic types like Any were used, and there are cases, where incompatible data passed into the wrong type. 

If such a thing happens on runtime, we should do the best-effort conversion instead of breaking the run. E.g.: try to json-stringify the object to str input, or try to parse number in the string to int input, etc.
2024-08-09 20:38:33 +07:00
Bently
1e054064f6 Feat(Builder): Allow for zooming out more on builder UI (#7760)
add min and max zoom
2024-08-09 07:43:06 -05:00
Aarushi
646d98470f fix(builder): Temporarily disbale TS checks (#7777)
* disable ts checks temporarily

* prettier
2024-08-09 09:59:39 +01:00
Reinier van der Leer
5a68be5419 refactor(server, builder): Remove unused/duplicate WebSocket endpoints (#7740)
* Remove WS API method `runGraph` in favor of REST method `executeGraph`

* Remove unused WebSocket methods from `autogpt_server.server`
2024-08-08 17:01:37 +02:00
Reinier van der Leer
2ff8a0743a fix(server): Make userId required on DB entities and apply default ID to existing entries (#7755)
* Make `userId` required on DB entities `AgentGraph`, `AgentGraphExecution`, and `AgentGraphExecutionSchedule`

* Add SQLite and Postgres migrations to make `userId` required and set `userId` to `3e53486c-cf57-477e-ba2a-cb02dc828e1a` on existing entries without `userId`

* Amend `create_graph` endpoint and `.data.graph`, `.data.execution` methods to handle required `user_id`

* Add `.data.user.DEFAULT_USER_ID` constant to replace hardcoded literals
2024-08-08 16:57:59 +02:00
Reinier van der Leer
582571631e fix+refactor(builder): Sort out FlowEditor+ReactFlow state management (#7737)
* refactor(builder): Migrate `FlowEditor` to use ReactFlow's state management system

  We have been keeping two copies of node and edge data: one inside ReactFlow and one outside.
  It works, but it's accidental and implicit and there is no reason to be using shadow copies rather than a single data source.

  - Replace `useNodesState` and `useEdgesState` with `useReactFlow` hook
  - Use `addNodes`, `addEdges`, and `deleteElements` where appropriate instead of `setNodes`/`setEdges` to allow use of event hooks
  - Consolidate all edge -> node state sync logic into `onEdgesChange` event handler
    This replaces `updateNodesOnEdgeChange`, part of `onConnect`, and `onEdgesDelete`.
  - Move node deletion logic from `CustomNode` to `FlowEditor:onNodesChange`

* fix(builder): Refactor and fix copy-paste mechanism
  - Rename variables for readability
  - Use an ID map to correctly set the source and target IDs for the pasted edges
2024-08-08 16:18:15 +02:00
Reinier van der Leer
bf10df612e feat(builder): Move /monitor to / (#7769)
- Move `monitor/page.tsx` to `page.tsx`
- Remove redirect from `/` to `/build`
- Set temporary redirect from `/monitor` to `/` to prevent breaking UX (may be removed after a grace period, e.g. 2024-09-01)
2024-08-08 16:07:12 +02:00
SwiftyOS
c577d04692 Changed background to white and add an effect on it 2024-08-08 15:50:59 +02:00
Swifty
85d895ef77 tweak(builder): Node styling (#7763)
* wix width

* teak(builder): blocks control styling

* tweak node styling

* formatting
2024-08-08 13:16:38 +01:00
Swifty
deacc2bd8f tweak(builder) block control styling (#7762)
* wix width

* teak(builder): blocks control styling
2024-08-08 13:15:04 +01:00
Swifty
3eb0d73461 fix(builder): Fix the control panel being too narrow for its elements (#7758)
wix width
2024-08-08 13:38:40 +02:00
Swifty
be0f6498ed fix(builder): Added header background and moved some styling to tailwind (#7756)
Added header background and moved some styling to tailwind
2024-08-08 12:12:20 +01:00
Swifty
0bab2714e9 feat(market): Agent Submission Process (#7718)
* adding auth to store

* Add ability to submit agents and review them before being added to the market

* Added auth decorator

* Added auth to market api client

* fix(builder): Fix drag-select behavior on `NodeKeyValueInput`

* fix(github): Added in fallback variables for postgres testing (#7715)

Co-authored-by: Leslie Cruz <lelcruz@users.noreply.github.com>
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>

* feat(builder): basic tally feedback form (#7725)

* removed database changes

* moved auth to libs project

* fixed formatting

* cleaned up auth

* Added tests and database migration

* delete decorator
2024-08-08 11:12:35 +02:00
Aarushi
9c74d76a3a fix(tests): Ensure user ID is not None on schedule in tests (#7749)
* ensure user id is never None on schedule

* remove comment
2024-08-08 11:11:57 +02:00
Bently
78e96f8a1a Feat(Builder): Add TimerBlock (#7728) 2024-08-08 09:59:06 +01:00
Bently
904b444b13 Feat(Builder): Add email sending block (#7736) 2024-08-08 10:57:39 +02:00
Andy Hooker
3cad0f89ee feat(builder): Standardize icons and integrate with ControlPanel and NavBar (#7743)
* feat(builder): Add new icons and integrate with ControlPanel

Introduce SVG icons and replace ControlPanel dependencies with new icons. The ControlPanel component now uses the new icon components for improved consistency.

* feat(builder): add additional icon and update icons in NavBar

Introduced the new icon with relevant documentation and examples. Replaced existing icon imports with the newly defined icons in the NavBar component for consistency.

* feat(builder): Add icon for megaphone and replace usage

Introduced the IconMegaphone for reuse across the application. Updated TallyPopup to utilize IconMegaphone instead of the previous Megaphone icon from lucide-react.

* fix(builder): Running prettier to format changed files.

Adjusted various files for consistent code formatting. Ensured proper spacing and alignment of imports, JSX tags, and object properties to enhance readability and maintain coding standards. No functional changes made.
2024-08-08 10:55:57 +02:00
lelcruz
8131fc385b fix(builder): Added option to pan view to center agents on build screen (#7742)
Co-authored-by: Leslie Cruz <lelcruz@users.noreply.github.com>
Co-authored-by: Bently <tomnoon9@gmail.com>
2024-08-08 10:54:37 +02:00
Reinier van der Leer
335fea8605 fix(builder): Always show connected optional pins on node (#7724)
- Always show connected optional pins on node
- Refactor `hasOptionalFields()` to a constant `hasOptionalFields`
2024-08-08 10:48:14 +02:00
Reinier van der Leer
55d32f0324 fix(builder): Automatically (re)connect websocket when sending message (#7739)
- Amend `AutoGPTServerAPI.sendWebSocketMessage(..)` to automatically (re)connect the websocket if disconnected
- Amend `AutoGPTServerAPI.connectWebSocket()` to prevent race conditions
2024-08-08 09:09:50 +01:00
Swifty
56ce7ac628 feat(market): Added Auth to Market and Protect routes (#7717)
* adding auth to store

* Added auth decorator

* Added auth to market api client

* removed database changes

* moved auth to libs project

* fixed formatting

* Switched to using fastapi dependencies

* Return a user object

* removed logging of the token

* Added tests
2024-08-08 09:28:44 +02:00
Zamil Majdy
81adf84032 fix(rnd): Fix flaky CurrentDateAndTimeBlock test (#7741)
### Background

CurrentDateAndTimeBlock would fail if the test is not complete within 1-second wall-time.
In the case a test started at the second 01:59:59, it becomes flaky.

We can change the test to only assert the type. But this is also a good chance to add more assertion options for Block: a custom function.

### Changes 🏗️

Change assertion for the time block using an additional margin of error.
2024-08-07 23:44:47 -05:00
Aarushi
f8d07a27af feat(builder): Add jwt support on websockets (#7734)
add jwt support on websockets
2024-08-07 14:47:35 +01:00
Aarushi
1bad26657c feat(rnd): Add user management (#7663)
* add user management support

* fixing tests

* fix formatting and linting

* default user, formatting & linting

* remove unused code

* remove default creation when auth enabled

* add client side calls

* addressing feedback

* prettier

* add defaults for websockets

* linting

---------

Co-authored-by: Swifty <craigswift13@gmail.com>
2024-08-07 14:15:29 +01:00
Nicholas Tindle
31dbb543a2 feat(builder): basic tally feedback form (#7725) 2024-08-07 09:14:18 +02:00
lelcruz
60d25135e6 fix(github): Added in fallback variables for postgres testing (#7715)
Co-authored-by: Leslie Cruz <lelcruz@users.noreply.github.com>
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-06 15:09:02 -05:00
Reinier van der Leer
4678ed2e57 fix(builder): Fix drag-select behavior on NodeKeyValueInput 2024-08-06 20:53:22 +02:00
Nicholas Tindle
98a07f1265 Add new agent ideas to docs (#7716) 2024-08-06 13:45:28 -05:00
Bently
5e8ff5e3ed Feat(Builder): Add discord bot templates (#7711)
Add discord bot templates
2024-08-06 19:44:43 +01:00
Bently
89adcefd63 Feat(Builder): added time blocks (#7710)
* added time blocks

* lint & format
2024-08-06 13:43:43 -05:00
Reinier van der Leer
d82e577196 feat(builder): Rewrite & split up node inputs (#7722)
- feat(builder): Rewrite & split up `NodeInputField`
  - Create `NodeObjectInputTree`
  - Create `NodeGenericInputField`
  - Create `NodeKeyValueInput`
  - Create `NodeArrayInput`
  - Create `NodeStringInput`
  - Create `NodeNumberInput`
  - Create `NodeBooleanInput`
  - Create `NodeFallbackInput`
  - Create `ClickableInput` from `renderClickableInput(..)`
  - Amend usage in `CustomNode`
  - Remove deprecated/unused styling from `flow.css` and `customnode.css`
  - Fix alignment between `NodeHandle` and `NodeInputField`
  - Split up `BlockIOSchema` & rename to `BlockIOSubSchema`
    - Create `BlockIOObjectSubSchema`
    - Create `BlockIOKVSubSchema`
    - Create `BlockIOArraySubSchema`
    - Create `BlockIOStringSubSchema`
    - Create `BlockIONumberSubSchema`
    - Create `BlockIOBooleanSubSchema`
    - Create `BlockIONullSubSchema`
  - Install `Select` component from shad/cn

- refactor(builder): Replace boilerplate `button` styling with `Button` components

- refactor(builder): Move `NodeInputField.tsx` to `node-input-components.tsx`

---------

Co-authored-by: Krzysztof Czerwinski <kpczerwinski@gmail.com>
2024-08-06 20:32:49 +02:00
Andy Hooker
e6cc8687a5 feat(builder): Refactor components and types for monitoring page streamlining (#7714) 2024-08-05 21:13:08 -05:00
Aarushi
fbad0d01ee feat(rnd, infra): Add deployment for frontend (#7705)
* add helm charts

* dont check in secrets
2024-08-05 21:12:57 +01:00
Zamil Majdy
fe5c1968bc fix(rnd): Cleanup block names and add explicit block name check (#7709) 2024-08-05 17:58:03 +00:00
Swifty
951abf6d5b feat(rnd) Agent Marketplace MVP (#7657)
* Added listing, sorting, filtering and ordering of agents

* feat(market): general upkeep for vscode and small docs

* feat(market): most of search

* fix(market): hinting on the sort was weird + linting

* feat(market): migrations and schema updates

* lint(market): autolint

* feat(market): better search

* feat(market): file download

* feat(market): analytics of downloads

* Added tracking of views

* changed all imports to be fully qualified

* Upgrade sentry sdk

* Added an admin endpoint to submit new agents

* fixes

* Added endpoint that just tracks download

* Starting adding the marketplace page

* Marketplace client

* Create template of the marketplace page

* Updated client

* fix(market): debug port

* feat(market): agents by downloads

* fix(market, builder): hook up frontend and backend

* feat(builder, market): build a "better" market page that loads data

* feat(builder): updated search (working) and page (kinda working)

* feat(builder): add a feature agents ui (not backed yet)

* feat(builder): improve detail page content

* Added run script

* Added pre populate database command

* Add AnalyticsTracker on create agent

* Add download counts for top agents

* Add hb page prometheus metrics

* Added featured agents funcitonality

* renamed endpoint to health

* Adding download flow

* normalised api routes

* update readme

* feat(market) : default featured

* formatting

* revert changes to autogpt and forge

* Updated Readme

* Eerror when creating an agent from a template installed from (#7697)

fix creating graph from template

* Add dockerfile

* z level fix

* Updated env vars

* updated populate url

* formatting

* fixed linting error

* Set defaults

* Allow only next.js dev server

* fixed url

* removed graph reassignment as due to change in master

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-05 16:51:17 +02:00
SwiftyOS
9ae6389c6c fix(autogpt_server): Incorrect graph reassignment 2024-08-05 16:16:56 +02:00
Zamil Majdy
4cf1dd30f1 feat(rnd): Introduce Sub-Graph on Agent Server (#7693)
### Background

This change brings the capability to decompose a graph into sub-graphs. The objective of this feature is to allow a user to build a visually modular, and easier-to-understand graph. Also, allowing you to import a graph into your existing graph, without decluttering your existing graph.

This feature will require more implementation on the UI side, to allow the grouping of subgraph to be represented as a node in the builder.

### Changes 🏗️

Introduced a subgraph functionality with the following property:

* Sub-graph is simply a set of nodes that are grouped together, making it representable as a node.
* Sub-graph input & output pins/schema are the `InputBlock` / `OutputBlock` nodes present in the subgraph.
* The previous point implies that connecting two nodes from different sub-graphs, other than input/output nodes, is not allowed.
* Graph can be nested, but defined flatly, e.g.: graph is now only represented by three components: nodes, links, and subgraphs (a set of list of nodes). A nested subgraph is simply connecting a node inside a subgraph into another `InputBlock` node of another subgraph.
2024-08-05 16:48:14 +07:00
Swifty
c7fdfa0f77 fix(builder): Apply Prettier Formatting (#7695)
formatting
2024-08-05 09:18:08 +02:00
Andy Hooker
6fa7d22c91 feat(builder): Addition of prettier for aligned of development styles (#7629)
* fix(builder): Adding prettier configuration files and to package.

* fix(builder): Running script "format" added to the package.json

* feat(builder): Adding a job to the yaml file. This job will run "format" which leverages prettier.

* feat(builder): Running script "format" and merging master

* feat(builder): Setting configuration to prettier defaults in .prettierrc.json, and adding a requested newline in the .prettierignore

* feat(builder): Updating the CI to not add a job for prettier but instead add a check to verify prettier was run before commiting.

* feat(builder): Confirming CI update fails when user does not run prettier first. Updating with file changes after prettier

* feat(builder): Running prettier write to fix warnings

* fix(builder): Removing .prett
per PR change request

* fix(builder): Running prettier formatter

* fix(builder): Running prettier formatter check found additional files

* fix(builder): Running prettier format

* fix(builder): Removing running "format" command from PR due to a change request.

* fix(builder): Removing running "format" command from PR due to a change request.

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-05 09:14:02 +02:00
Amelia Bruno
52bd033a02 tweak(setup): Improvements to setup script (#7469)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
Co-authored-by: Toran Bruce Richards <toran.richards@gmail.com>
2024-08-05 00:13:25 -05:00
Andy Hooker
bb5baadeb2 feat(builder): Implementation of UI / UX improvement to editor (#7690)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-05 00:09:31 -05:00
Andy Hooker
db97b24518 feat(builder): Addition of blocks control component (#7688)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-04 23:58:12 -05:00
Andy Hooker
533d7b7da8 feat(builder): Addition of SaveControl component for ControlPanel (#7687)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-04 23:39:42 -05:00
Andy Hooker
183c2a4845 feat(builder): Addition ControlPanel and Separator components (#7680) 2024-08-04 23:38:14 -05:00
alex
6440a8e217 fix punctuation in setup.md (#7689) 2024-08-04 06:53:32 -07:00
Toran Bruce Richards
e0930ba39d feat(Block): Add AdvancedLlmCallBlock (#7677)
* feat(Block): Add AdvancedLlmCallBlock

Adds a block for handling advanced LLM calls, enabling messages to be handled within the AutoGPT builder.

* fix linting
2024-08-02 18:17:03 -05:00
Andy Hooker
a21fd30fce fix(github): Update CODEOWNERS for rnd directories (#7684)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-02 17:23:28 -05:00
Nicholas Tindle
e2df2cd90d Revert "feat(builder): Rewrite node input UI" (#7685) 2024-08-02 17:07:03 -05:00
Toran Bruce Richards
6bdb849150 feat(Block): Add blocks for Building Lists, Dictionaries & Strings (#7678)
* feat(Block): Add block for building lists

* feat(Block): Add block for building dictionaries

* feat(Block): Add TextCombinerBlock for combining multiple input texts

* Fix linting issues where default values are None

---------

Co-authored-by: Bently <tomnoon9@gmail.com>
2024-08-02 16:49:51 -05:00
Bently
8469fafc6f Feat(Builder): Discord bots blocks (#7670)
* Super early version of the discord bots blocks

* updated to add secrets for token + other fixes

* lint & format

* rename DiscordBot to DiscordReader

* add discord-py

* fix poetry lock

* rm duplicated file

* updated name to add Block to end

* update .env.template to add DISCORD_BOT_TOKEN

* updates to add description Field

* swap channel name and message content + add field description

---------

Co-authored-by: Bently <bently@bentlybro.com>
2024-08-02 22:21:58 +01:00
Reinier van der Leer
3c2c3e57a0 feat(builder): Rewrite node input UI (#7626)
- feat(builder): Rewrite & split up `NodeInputField`
  - Create `NodeObjectInputTree`
  - Create `NodeGenericInputField`
  - Create `NodeKeyValueInput`
  - Create `NodeArrayInput`
  - Create `NodeStringInput`
  - Create `NodeNumberInput`
  - Create `NodeBooleanInput`
  - Create `NodeFallbackInput`
  - Create `ClickableInput` from `renderClickableInput(..)`
  - Amend usage in `CustomNode`
  - Remove deprecated/unused styling from `flow.css` and `customnode.css`
  - Fix alignment between `NodeHandle` and `NodeInputField`
  - Split up `BlockIOSchema` & rename to `BlockIOSubSchema`
    - Create `BlockIOObjectSubSchema`
    - Create `BlockIOKVSubSchema`
    - Create `BlockIOArraySubSchema`
    - Create `BlockIOStringSubSchema`
    - Create `BlockIONumberSubSchema`
    - Create `BlockIOBooleanSubSchema`
    - Create `BlockIONullSubSchema`
  - Install `Select` component from shad/cn

- refactor(builder): Move `NodeInputField.tsx` to `node-input.tsx`
2024-08-02 19:28:46 +01:00
lelcruz
ec6bae0467 fix(documentation): Corrected Markdown formatting for server start command (#7649)
Co-authored-by: Leslie Cruz <lelcruz@users.noreply.github.com>
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-08-02 12:33:12 -05:00
Bently
f5fe96260e Fix(Builder): beautifyString duplicated import (#7679) 2024-08-02 15:39:42 +01:00
SwiftyOS
49a18437ac fix(builder) fix merge conflict errors 2024-08-02 16:29:55 +02:00
Reinier van der Leer
3cee893314 fix(builder): Rewrite additionalProperties input field (#7608)
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-08-02 16:11:38 +02:00
Reinier van der Leer
5d1035aeb0 fix(builder): Fix BlockSchema handling & type propagation (#7604)
- Merge `Flow.tsx:CustomNodeData` type definition into `CustomNode.tsx:CustomNodeData`
- Move `lib/types:BlockSchema` type definition into `lib/autogpt-server-api/types` in place of `ObjectSchema`
- Expand and rename `BlockSchema` -> `BlockIOSchema` + `BlockIORootSchema`
- Fix all `BlockIOSchema` related type narrowing checks
- Add warning messages to fallback cases in `NodeInputField` logic

Co-authored-by: Swifty <craigswift13@gmail.com>
2024-08-02 16:09:46 +02:00
Bently
2e2c6fed52 feat(Builder): Implement undo/redo functionality (#7559)
* feat(Builder): Implement undo/redo functionality

* updates to work with latest UI

* add CTRL + Z & CTRL + Y support

* fixed undo/redo for inputs in nodes

* fix for deleting node

* fixes to make the undo/redo work better

* small fix

* Updates based on feedback

* add margin right to space out buttons

* added CTRL + SHIFT + Z for redo
2024-08-02 16:06:40 +02:00
Krzysztof Czerwinski
ca9c52f76a feat(builder): Add Supabase Auth, session and sign in UI (#7655)
This PR adds Supabase Auth (cloud) integration, login and profile UI, configures password login and three OAuth providers: Google, GitHub and Discord.

For `Account` button to show up and ability to login two env vars need to be set in `.env.local`: `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY`. OAuth providers are by the Supabase and don't require env vars.

Email confirmation (for email/password signup) is disabled because there's limit of 3 emails per hour without custom SMTP server configuration. [Link](https://supabase.com/dashboard/project/adfjtextkuilwuhzdjpf/auth/templates) to configure custom SMTP server and email template.

### Added dependencies:
- "@supabase/ssr": "^0.4.0"
- "@supabase/supabase-js": "^2.45.0"
- "react-icons": "^5.2.1"

### Added pages/routes:
- `app/auth/auth-code-error/page.tsx`: displayed when login using OAuth provider fails
- `app/auth/callback/route.ts`: route accessed when logging in using OAuth provider; it passes session code to Supabase
- `app/auth/confirm/route.ts`: accessed when confirming email, users will be directed here from email they get after signing in.
- `app/error/page.tsx`: Generic error page without explanation (any errors should be visible in the console)
- `app/login/page.tsx` and `app/login/actions.ts`: Login page and related login/signup server actions
- `app/profile/page.tsx`: Profile page, displays email address of the user and button to logout

### Changes
- Update `layout.tsx`: add `Log In` button and make icons consistent. The log in button shows up if user is logged out, avatar is shown when logged in, and if supabase is unavailable nothing shows up.
- Login form is verified using `zod` on the frontend (recommended by shadcn) and in case login fails feedback is displayed. On successful login users are redirected to `/profile`
- Add `PasswordInput` component, [source](https://gist.github.com/mjbalcueva/b21f39a8787e558d4c536bf68e267398)
- Add `SupabaseProvider` with context for Supabase accessed via hook `useSupabase(): { supabase: SupabaseClient | null, isLoading: boolean }`
- Add `useUser` hook to get `{ user, session, isLoading, error }` on the client 
- Add `getServerUser`: async function to get `{ user: User | null, error: string | null }` on the server side
- Add `src/middleware.ts` and `client.ts`, `server.ts`, `middleware.ts` in `src/lib/supabase` which are utility functions and middleware to refresh auth token
2024-08-02 12:58:28 +01:00
Zamil Majdy
973822d973 feat(rnd): Add InputBlock & OutputBlock (#7654)
### Background

We need an explicit block for providing input & output for the graph.
This will later allow us to build a subgraph with pre-declared input & output schema.
This will also allow us to set input for the node in the middle of the graph, and enable a graph to have output values.

### Changes 🏗️

* Add InputBlock & OutputBlock
* Add graph structure validation  on the graph execution step that asserts the following property:
    - All mandatory input pin, has to be connected or have a default value, except the `InputBlock` node.
    - All links have to connect valid nodes, and the sink & source name using the valid block field.
2024-08-02 13:33:34 +07:00
Bently
e773329391 Feat(Builder): Clear Status and Output upon graph edit (#7656)
* Feat(Builder): Clear Status and Output upon graph edit

* close output dropdown on graph edit

* make deleting edge clear block outputs

* make it so deleting nodes clears block output
2024-08-01 17:21:01 +01:00
Zamil Majdy
c9d41e69bd feat(rnd): Add postgres schema check on linting step + auto gen migration and schema sync command (poetry run schema) (#7667)
### Background

Migration not being synced has caused issues and CI is not catching this yet.

### Changes 🏗️

* Added schema check in both sqlite & postgres prisma.schema on linting step.
* Introduced `poetry run schema` for syncing prisma.schema + generating migration in both files.
2024-08-01 20:11:07 +07:00
Nicholas Tindle
08905d71f9 docs(server): add docs link to server readme (#7638)
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-08-01 14:32:08 +02:00
Aarushi
8becde370c docs(ollama) Add ollama to mkdocs (#7666)
add ollama to mkdocs
2024-08-01 13:14:40 +01:00
Aarushi
dccc33152b feat(docs) Add how to run ollama with AutoGPT (#7665)
add ollama howto
2024-08-01 12:12:01 +01:00
Aarushi
b23bd9c479 feat(server): Add JWT validation (#7642)
* add auth middleware

* update readnme

* Update rnd/autogpt_server/pyproject.toml

Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com>

* address newline feedback

* update poetry lock

---------

Co-authored-by: Krzysztof Czerwinski <34861343+kcze@users.noreply.github.com>
2024-08-01 11:11:40 +01:00
Aarushi
ac45b7cae9 feat(rnd,infra): Add Helm linting in CI (#7633)
* Set up helm and tf for backend

* update helm charts and settings

* remove example files

* use latest tag

* delay and timeouts for probes

* env based pyro host

* default backend

* linting

* add helm linting in CI

* read from settings

* fix formatting

* update to use config
2024-08-01 09:22:04 +01:00
Aarushi
3d54a9103c feat(rnd, infra): Add deployment for backend (#7612)
* Set up helm and tf for backend

* update helm charts and settings

* remove example files

* use latest tag

* delay and timeouts for probes

* env based pyro host

* default backend

* linting

* read from settings

* fix formatting

* update to use config
2024-08-01 09:21:12 +01:00
Nicholas Tindle
ca7182403b fix(server): enable other methods of setting configs outside of the config.json file (#7645) 2024-07-31 12:33:36 -05:00
Zamil Majdy
53826ab360 fix(rnd): Fix bug caused by different AJV version (#7653) 2024-07-31 16:47:31 +07:00
Andy Hooker
eac5548023 feat(builder): UI / UX upgrade and simplification of tailwind styles (#7647)
* fix(builder): Implementing a basic shadCn theme until color palette is decided upon

* feat(builder): Separating NavBar into its own component and providing a standard UI/UX Approach

* feat(builder): Removing old implementation of logo, removing excessive css implementation, updating styles to better support standard desktop views.

* feature(builder): Addition of UI component Sheet from ShadCn for update
2024-07-31 10:43:13 +02:00
Zamil Majdy
122f544966 fix(rnd): Disable unused prisma connection on pyro API Server process (#7641)
### Background

Pyro for API Server is not using Prisma, but still holding a Prisma connection.
The fast-API thread is also holding a Prisma connection, making Prisma connected in two different loop within a single process.

### Changes 🏗️

Disable a Prisma connection on Pyro thread for Server API process.
Fix test flakiness issue due to concurrency issue.
2024-07-30 22:33:28 +07:00
Zamil Majdy
29ba4c2c73 feat(rnd): Add freezed/static input link feature for retaining input data to be re-used in multiple node executions (#7631)
### Background

Input from the input pin is consumed only once. While this is required in most of the use cases, there are some cases where the input can only be produced once, and that input needs to be re-used just like an input default value, that is passively providing input data, without triggering any execution. The scope of this change is providing that functionality in the link level, this property will be called **`static link`** in this system.

### Changes 🏗️

Provides a static link feature with the following behaviours:
* A link can be marked `static` to become a static link.
* Once a node produces an output it will persist the output data and propagate the output to the other nodes through the link, for a static link, instead of making the data queued in the input pin, it will override the default value.
* Any input executions still waiting for the input will be backfilled using this output produced by the static link.
* And any upcoming executions that will use the input will always reuse the output produced by the static link.

See the added test to see the expected usage.
2024-07-30 10:37:38 +07:00
Nicholas Tindle
76feead3b1 docs(server): written setup guide for server (#7595)
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-29 22:24:03 +01:00
Bently
081df805df Fix(Builder): Copy and Paste when editing textbox causes issue (#7636)
Fix(Builder): Copy and Paste when editing textbox causes graph elements to be erroneously pasted
2024-07-29 21:55:19 +01:00
Nicholas Tindle
acc1d79146 feat(builder): add delete handles to the edges and nodes (#7614)
Co-authored-by: Andy Hooker <58448663+andrewhooker2@users.noreply.github.com>
Co-authored-by: Toran Bruce Richards <toran.richards@gmail.com>
2024-07-29 21:30:56 +01:00
Nicholas Tindle
07811b2133 fix(builder): show colors for all statuses (#7625) 2024-07-29 20:04:48 +01:00
Amr Aly
01b6c2d4bf Update README.advanced.md (#7607)
* Update README.advanced.md

The copy .env command is wrong. it should be .env.example instead of env.example

* fix(server): extra space removal

---------

Co-authored-by: Nicholas Tindle <nicktindle@outlook.com>
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
Co-authored-by: Aarushi <50577581+aarushik93@users.noreply.github.com>
2024-07-29 12:00:58 +01:00
Krzysztof Czerwinski
905b1df218 feat(builder): Add input validation on the frontend (#7606)
- Add `ajv` dependency to check values against json schema
- Add `errors` and `setErrors` to `CustomNodeData`
- Add `validateNodes` run before executing agent
- Add `*` on labels for required fields
- Add `setNestedProperty` and `removeEmptyStringsAndNulls` utility function
2024-07-29 10:17:27 +01:00
Andy Hooker
edf84fb9f8 feat(builder): Apply standard theming for shadCn and centralize providers (#7622) 2024-07-26 17:59:44 -05:00
Swifty
b62c24dc77 Add Marketplace template (#7605)
* init

* Added sentry.io integration

* updated readme
2024-07-26 17:11:26 +02:00
Zamil Majdy
dfa855f533 fix(rnd): Fix broken server execution update due to split prisma connection to different loops (#7620) 2024-07-26 15:47:41 +01:00
Aarushi
da2111bafb fix(server): Add missing PG migrations (#7619)
missing migrations
2024-07-26 14:38:05 +01:00
Reinier van der Leer
b2dba39810 fix(builder/api-client): Fix websocket client types (#7603)
- Fix type signatures of `sendWebSocketMessage(..)`, `onWebSocketMessage(..)`, `runGraph(..)` in `autogpt-server-api/client`
  - Add `WebsocketMessageTypeMap`

- Fix type signature of `updateNodesWithExecutionData` in `FlowEditor`
2024-07-26 13:26:11 +02:00
Zamil Majdy
d2a5bb286f feat(rnd): Fix concurrency issue on Agent Server & Apply max-1-execution-per-node constraint in graph execution (#7551)
### Background

When multiple executors are executing the same node within the same graph execution, two node executions can read the same queue of input and read the same value—making the data that is supposed to be consumed once, consumed by two executions. The lack of lock & concurrency support for parallel execution within a single graph causes this issue.

Node concurrency also introduces poor UX in the current frontend implementation, when two nodes are executed in parallel, the current UI will not display its parallel execution update, but instead, it shows the updates that override each other. Until the execution observability is improved on the builder UI, this capability will be limited.

### Changes 🏗️

The scope of this change is to solve this issue by:
* Decouple Graph execution & Node execution, each has its own configured process pool.
* Make sure there is only 1 execution per node (we still allow parallel executions on different nodes) in a graph.
* Fixed concurrency issue by adding distributed lock API on agent_server.
* Few cleanups:
    - Add more logging with geid & neid prefix on graph/node executions
    - Moved execution status update to agent-server for a single source of status update (required by conn-manager/web-socket)
    - Configured node parallelism to 10 & graph parallelism to 10 by default, so in the very rare worst-case, there can be 100 node executions.
    - Re-use server resource for each integration test run
2024-07-26 17:08:03 +07:00
Toran Bruce Richards
36b9a0a930 feat(Blocks): Add conditional logic block (#7536)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-07-25 14:25:07 -05:00
Aarushi
f40db85b43 feat(rnd, infra) Add static ips module (#7588)
* add terraform

* gitignore update

* linting

* formatting and linting in ci

* store state in backend bucket

* Update .github/workflows/autogpt-infra-ci.yml

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>

* add static ips module

* formatting

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-25 17:50:19 +01:00
Reinier van der Leer
0767b17779 dx: Set Python type checking mode to "basic" for all subprojects (#7599) 2024-07-25 12:25:56 +01:00
Nicholas Tindle
0f0c13bae8 fix(server): gitignore was missing new file name (#7596)
Co-authored-by: Aarushi <50577581+aarushik93@users.noreply.github.com>
2024-07-25 10:07:16 +01:00
A.Daee
3b0cd9518d feat(froge): Improvement in web components (#7068)
- Add `duckduckgo_backend` field to `WebSearchComponent` configuration
- Add `selenium_proxy` to `WebSeleniumComponent` configuration
- Update docs
2024-07-25 10:02:34 +01:00
Aarushi
22b6dbbf6a feat(rnd,infra) Set up terraform (#7565)
* add terraform

* gitignore update

* linting

* formatting and linting in ci

* store state in backend bucket
2024-07-25 09:45:36 +01:00
Reinier van der Leer
d9a1a1edc8 fix(builder): Correctly load node connections (#7592)
- Set `node.data.connections` based on `graph.links` in `loadGraph(..)`
- Use reactflow's built-in `useNodesState`, `useEdgesState` to replace some boilerplate functions that do exactly the same

Resolves #7392
2024-07-25 09:30:35 +01:00
vasnt
3c0d37d5d1 docs(forge):fix #7198 dead image links (#7593)
Co-authored-by: Nicholas Tindle <nicktindle@outlook.com>
2024-07-24 22:55:07 -05:00
Bently
c98061bc3b fix(Builder): fixes to copy and pasted blocks (#7591)
* fix(Builder): fixes to copy and pasted blocks

* clear the blocks state on paste
2024-07-24 17:00:59 +01:00
Toran Bruce Richards
a8c0cbef54 refactor(Blocks): Simplify MathsBlock and CounterBlock to Return Numeric Results Only (#7582)
* refactor(MathsBlock): Simplify output to return numeric result directly

- Remove MathsResult class and explanation field
- Update Output schema to use float type
- Simplify run method to yield numeric result only
- Adjust error handling to return inf or nan for errors
- Update test cases to reflect new output structure

* run format

* refactor(CounterBlock): Simplify output to return count as integer

- Remove CounterResult class
- Update Output schema to use int type directly
- Simplify run method to yield count without explanation
- Modify error handling to return -1 for any errors
- Update test case to reflect new output structure
2024-07-24 08:55:18 -05:00
Zamil Majdy
8ccd14c4bf fix(rnd): Disable BlockInstallationBlock and add disable field for block (#7583) 2024-07-24 08:52:14 -05:00
Nicholas Tindle
3e384c9771 ci(server): add sqlite processing (#7586)
* ci(server): add sqlite processing

* ci(server): try setting DATABASE_URL based on db platform

* fix(server): swap default back to sqlite

* ci(server): go back to database url

---------

Co-authored-by: Aarushi <50577581+aarushik93@users.noreply.github.com>
2024-07-24 14:46:39 +01:00
Aarushi
22f2a05f08 feat(server) Fix schema file (#7589)
* fix schema file

* remove invalide migrations

* make sqlite url hardcode
2024-07-24 14:31:48 +01:00
Krzysztof Czerwinski
e94a7b08c9 refactor(builder): Move renderInputField to NodeInputField component (#7581) 2024-07-24 11:14:20 +01:00
Aarushi
7b8928f49b feat(server) Update readme (#7580)
update readme
2024-07-24 16:53:09 +07:00
Aarushi
699087e289 feat(rnd) Add dockerfiles (#7523)
* replace SQLite with Postgres

* dockerfiles and optional docker compose set up

* Update rnd/autogpt_builder/Dockerfile

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>

* address feedback

* Update .dockerignore

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>

* Remove example files folder

* remove backend and frontend from docker compose

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-07-24 10:01:22 +01:00
Bently
aa8ca37f86 feat(Builder): Work on copy and paste of blocks (#7564)
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-24 09:30:48 +01:00
Toran Bruce Richards
8bdb48cba4 feat(blocks): Add basic YouTube transcriber block (#7495)
* feat: Add YouTubeTranscriber block for extracting transcripts from YouTube videos

* feat: Add youtube-transcript-api dependency to pyproject.toml

* feat: Add SchemaField and test_mock to YoutTube Transcriber.

* feat: update lock

* fix(server): the agbenchmark was out of date

* fix(server): linting

* fix(server): drop mock

* fix(server): poetry locked in

* fix(server): test had ... at the end?

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-07-24 09:00:09 +01:00
Bently
03ea51b266 feat(Builder): Limit Output preview size (#7554)
* feat(Builder): Limit Output preview size

* fix indent
2024-07-24 09:23:43 +02:00
Aarushi
77034f2df0 feat(blocks): Add 405b on ollama (#7573)
405b on ollama
2024-07-24 09:22:18 +02:00
Nicholas Tindle
ccf4397883 feat(server): author and contributor information on blocks (#7575) 2024-07-24 09:19:33 +02:00
Reinier van der Leer
e7885f943b fix(agent): Unbreak LLM status check on start-up
Fixes #7508

- Amend `app/configurator.py:check_model(..)` to check multiple models at once and save duplicate API calls
- Amend `MultiProvider.get_available_providers()` to verify availability by fetching models and handle failure
2024-07-23 22:36:04 +02:00
Aarushi
aca7165694 feat(rnd) Fix linting and formatting (#7568)
fix linting and formatting
2024-07-23 18:25:04 +01:00
Toran Bruce Richards
22b223037e feat(Blocks): Add support for new Llama 3.1 models with Groq! (#7567)
feat: Add support for new Groq models

The commit adds support for new Groq models, including LLAMA3_1_405B, LLAMA3_1_70B, and LLAMA3_1_8B. These models are part of the preview release and offer enhanced reasoning and versatility capabilities.
2024-07-23 16:48:39 +01:00
Toran Bruce Richards
6747ae1559 feat(Blocks): Add Blocks Providing Basic Arithmetic Operations (#7529)
* feat(blocks): Add MathsBlock for performing mathematical operations

The commit adds a new block called MathsBlock to perform various mathematical operations such as addition, subtraction, multiplication, division, and exponentiation. The block takes input parameters for the operation type, two numbers, and an option to round the result. It returns the result of the calculation along with an explanation of the performed operation.

---------

Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-23 16:28:59 +02:00
Aarushi
39afba6da8 feat(ci) Fix migrations in CI (#7563)
* update CI

* add poetry run

* schema prisma
2024-07-23 14:01:45 +01:00
Toran Bruce Richards
a00df25092 feat(Blocks): Add RSS Reader Block for reading RSS feeds (#7533)
* feat: Add RSSReaderBlock for reading RSS feeds

The commit adds a new `RSSReaderBlock` class in the `rss-reader-block.py` file. This block allows users to read RSS feeds by providing the URL of the feed, start datetime, polling rate, and a flag to run the block continuously. The block fetches the feed using the `feedparser` library and returns the title, link, description, publication date, author, and categories of each RSS item.

This commit also includes the addition of the `feedparser` dependency in the `pyproject.toml` file.

* fix(server): update lock file

* updated poetry lock

* fixed rss reader testing

* Updated error message in test to include check info

* Set starttime as 1 day ago

* Changed start time to time period

---------

Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-23 13:15:14 +02:00
Toran Bruce Richards
ea698ab0fe feat(Blocks): Add For-Each Block for iterating over a List. (#7531)
* feat: Add ForEachBlock for iterating over a List.

---------

Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-23 11:40:41 +02:00
Krzysztof Czerwinski
902d2a8924 feat(builder): UX and style updates (#7550)
- Handles:
  - Add `NodeHandle` to draw input and output handles
  - Position handles relatively
  - Make entire handle label clickable/connectable
  - Add input/output types below labels
  - Change color on hover and when connected
  - "Connected" no longer shows up when connected
- Edges:
  - Draw edge above node when connecting to the same node
  - Add custom `ConnectionLine`; drawn when making a connection
  - Add `CustomEdge`; drawn for existing connections
  - Add arrow to the edge end
  - Colorize depending on type
- Input field modal:
  - Select all text when opened
  - Disable node dragging
- CSS:
  - Remove not needed styling
  - Use tailwind classes instead of css for some components
  - Minor style changes
- Add shadcn switch
- Change bottom node buttons (for properties and advanced) to switches
- Format code
2024-07-23 09:36:42 +01:00
Reinier van der Leer
ab0df04bfe fix(builder/monitor): Fix Graph export (#7556)
- fix(builder/monitor): Export `Graph` rather than `GraphMeta`
  - Fixes #7557

- refactor(builder): Split up `lib/autogpt_server_api` into multi-file module
  - Resolves #7555
  - Rename `lib/autogpt_server_api` to `lib/autogpt-server-api`
  - Split up `lib/autogpt-server-api` into `/client`, `/types`
  - Move `ObjectSchema` from `lib/types` to `lib/autogpt-server-api/types`
  - Make definition of `Node['metadata']['position']` independent of `reactflow.XYPosition`

- fix(builder/monitor): Strip secrets from graph on export
  - Resolves #7492
  - Add `safeCopyGraph` function in `lib/autogpt-server-api/utils`
  - Use `safeCopyGraph` to strip secrets from graph on export in `/monitor` > `FlowInfo`
2024-07-23 09:28:06 +02:00
Zamil Majdy
d407fd101e fix(rnd): Make Agent Server's pin connections become the mandatory source of input (#7539)
### Background

Input from the input pin is consumed only once, and the default input can always be used. So when you have an input pin overriding the default input, the value will be used only once and the following run will always fall back to the default input. This can mislead the user.

Expected behaviour: the node should NOT RUN, making connected pins only use their connection(s) for sources of data.

### Changes 🏗️

* Make pin connection the mandatory source of input and not falling back to default value.
* Fix the type flakiness on block input & output. Unify the typing for BlockInput & BlockOutput using the right alias to avoid wrong typing.
* Add comment on alias
* automated test on the new behaviour.
2024-07-23 09:06:26 +07:00
Aarushi
a911f9a5eb feat(rnd) Add Postgres support (#7513)
* replace SQLite with Postgres

* make sqlite default

* add migrations for sqlite and postgres

* update readme

* fix formatting
2024-07-22 23:43:49 +01:00
Reinier van der Leer
470c738732 fix(builder/monitor): Fix # of runs count on first load (#7558)
Return mutated copy rather than in-place mutated `flowRuns` in `refreshFlowRuns(..)`

Fixes #7507
2024-07-22 23:15:54 +01:00
Nicholas Tindle
7de49dfbe5 Update workflow-checker.yml (#7553) 2024-07-22 13:49:27 -05:00
Reinier van der Leer
a02b017cea feat(builder): Built-in templates (#7511)
- Add "Medium Blogger" template to `graph_templates` folder
- Add `import_packaged_templates()` function in `data.graph`
- Call `import_packaged_templates()` in `AgentServer` lifecycle setup
- Fix `util.json:loads` typing and parameter forwarding
2024-07-22 17:54:48 +02:00
Nicholas Tindle
56b82369b6 ci(all): verify the workflows as part of each run (#7500) 2024-07-22 07:23:48 -05:00
Tyrion Lannister
a7926584ca Fix(rnd): Add Prerequisites Documentation in README.md (Server) (#7546) 2024-07-22 07:19:20 -05:00
Nicholas Tindle
6ffa644fb6 fix(ops/issues): update template for bugs (#7211) 2024-07-22 07:16:51 -05:00
Bently
a82317e2ac feat(builder): fix sidebar scroll issue (#7548)
fix sidebar scroll issue
2024-07-22 13:05:50 +01:00
Bently
fd000a4173 feat(Builder): Fix sidebar covering navbar (#7544)
Fix sidebar covering flow
2024-07-22 10:11:46 +01:00
Nicholas Tindle
235715e054 feat(server): read_csv block (#7499) 2024-07-22 09:58:02 +02:00
Nicholas Tindle
d0ec31b698 docs: fix dead links (#7470) 2024-07-22 09:56:32 +02:00
Zamil Majdy
fa1b486c64 feat(rnd): Add Agent block UUID validation for Agent Server (#7501)
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-07-20 09:14:18 +07:00
Reinier van der Leer
21084c5817 feat(builder, server): Speed up fetching graphs (#7477)
- Let `GET /graphs` return `GraphMeta[]` instead of `string[]` (list of IDs)
- Rename `AutoGPTServerAPI` method `listGraphIDs` -> `listGraphs` and adjust return type
   - Replace all usages of `Graph` with `GraphMeta` in `/monitor`
- Delete `data.graph:get_graph_ids()`
2024-07-19 18:28:59 -05:00
Zamil Majdy
82fd3166ef feat(rnd): Add block description and categories (#7463)
### Background

Add block description and categories metadata.

### Changes 🏗️

* Add block description and categories metadata.
* Initialize description and categories on the existing blocks.
2024-07-19 11:10:16 -05:00
Toran Bruce Richards
f833fa3624 feat: Update CreateMediumPostBlock to use secret value for author_id
This commit updates the `CreateMediumPostBlock` class in `create_medium_post.py` to use the secret value for the `author_id` parameter. Previously, it was using the plain value, which caused the value to be sent incorrectly to the API.
2024-07-19 14:52:02 +01:00
Aarushi
e6f9870f2e feat(blocks) Add ollama integration (#7505)
* add ollama integration

* address review comments & formatting
2024-07-19 11:27:48 +01:00
Bently
6e319a6881 Feat(Builder): Decrease the delay before a tooltip opens (#7478)
Make tooltip show in 10 ms
2024-07-19 09:21:34 +01:00
Nicholas Tindle
64edf12c31 fix(server): code.py as a filename overshadows an internal python file (#7496) 2024-07-19 10:30:54 +07:00
Nicholas Tindle
e1795b8216 fix(server): add create medium post test missing params (#7497) 2024-07-19 10:28:43 +07:00
Nicholas Tindle
057d0848ef fix(server): various linting errors (#7498) 2024-07-19 10:28:21 +07:00
Bently
2dc673614f feat(doc): Update node creation docs (#7462)
* feat(doc): Update node creation docs

* small fixes

* fix formatting

* remove mention of registering a block

* Update docs/content/server/new_blocks.md

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>

---------

Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
2024-07-18 20:01:40 -05:00
Toran Bruce Richards
24e08d57ef Update README.md 2024-07-19 00:46:17 +01:00
Toran Bruce Richards
bd540b5cc4 tweak(builder): Replace erroneous mentions of "Properties" to "Output" (#7489) 2024-07-19 00:42:22 +01:00
Toran Bruce Richards
6d192429a6 feat(blocks): Add Medium Post block (#7494)
* feat: Add CreateMediumPostBlock to create Medium posts

* feat: Add medium_api_key to Secrets class in settings.py

* feat: Update medium post block to work with latest system.

* feat: Add medium_author_id field to Secrets class in settings.py

* run isort

* run black
2024-07-19 00:26:32 +01:00
Toran Bruce Richards
314a24ab8f fix(blocks): resolve duplicate ID in ObjectLookupBlock - basic.py 2024-07-18 23:58:19 +01:00
Toran Bruce Richards
ff962d8d88 Merge branch 'master' of https://github.com/Significant-Gravitas/AutoGPT 2024-07-18 23:56:09 +01:00
Toran Bruce Richards
5e5182e236 feat: Add CreateMediumPostBlock to create Medium posts 2024-07-18 22:54:13 +01:00
Nicholas Tindle
04dcd230cd feat(builder): allow deletion of nodes and edges via del key (#7483) 2024-07-18 22:45:28 +01:00
Nicholas Tindle
c8b46109fe feat(builder, server): hide secrets from the UI and mark them as secrets in the api (#7490)
* feat(server): add json_extra marking some fields as secret

* devx(builder): add a launch.json for debugging

* feat(builder): hide secret strings

---------

Co-authored-by: Toran Bruce Richards <toran.richards@gmail.com>
2024-07-18 16:30:11 -05:00
Toran Bruce Richards
c00caa4bcf tweak(Builder): Remove unnecessary node output heading in CustomNode component (#7487)
feat: Remove unnecessary node output heading in CustomNode component
2024-07-18 21:43:47 +01:00
Bently
e382dcf823 feat(blocks): Adds node to get weather from openweathermap (#7382)
* Add node for openweathermap

* fix naming

* Rename GetOpenweatherMapWeather.py to getopenweathermapweather.py

* fix naming

* fix conflicts

* fix conflicts

* Updates to node + moved to search.py

* small cleanup
2024-07-18 21:36:09 +01:00
Toran Bruce Richards
27e6c3a95d feat(block): Add support for GPT-4O Mini model in LLM block (#7480)
feat: Add support for GPT-4O Mini model in LLM block
2024-07-18 19:02:36 +01:00
Bently
aaf650ee23 feat(builder): Fix message covering navbar on < lg screen size (#7474)
* Remove excess styling from message box
* Replace "node" with "block" in message (-> "Get started by adding a block")
2024-07-18 16:46:28 +02:00
Reinier van der Leer
4c003d6e20 fix(builder): Make detail view on /monitor obey grid
Unwrap the detail view element and style it directly instead
2024-07-18 04:12:51 +02:00
Reinier van der Leer
8264d7bf5a feat(builder): Importing/exporting Agents and Templates (#7466)
Builder:
* Add download button to agent info view
   - Add download button to `FlowInfo`
   - Add `exportAsJSONFile(..)` function to lib/utils.ts

* Add Create button + menu to /monitor page
   - Add "Create | v" split button to Agent list
   - Add list of templates to Create menu
   - Add "Import from file" option + dialog
      - Create `AgentImportForm` component
         - Install `Form`, `Label`, `Switch` components from shad/cn UI
      - Install `Dialog` component from shad/cn

* Support saving/editing Templates
   - Add `templateID` query parameter to `/build`
   - Use `templateID` query parameter in `AgentImportForm` redirect if imported as template
   - Make `FlowEditor` suitable for saving/editing templates
      - Add `template` (boolean) parameter to `FlowEditor` component
      - Add conditions to all `createGraph` or `updateGraph` calls, to use `createTemplate`/`updateTemplate` if applicable
      - Add "Save as Template" button, visible if the graph is new (unsaved)
      - Hide "Save & Run Agent" button when editing a template

* Add template endpoints to `AutoGPTServerAPI` client
   - Add `listTemplates()`
   - Add `getTemplate(id, version?)`
   - Add `getTemplateAllVersions(id)`
   - Add `createTemplate(templateCreateBody)`
   - Add `updateTemplate(id, template)`

* fix inner alignment of `<Input type="file">` elements

Server:
* fix(server): Fix return of `create_graph` for templates
2024-07-18 13:19:56 +01:00
Reinier van der Leer
37b7053e14 feat(server): Add API prefix /api to prevent route collisions with frontend (#7472)
- Add prefix `/api` to `APIRouter` in server.py
- Update API client in Builder
   - Update default `AGPT_SERVER_URL` in .env.template
   - Update default `baseUrl` in `AutoGPTServerAPI` constructor
2024-07-18 07:16:58 -05:00
Reinier van der Leer
354e626965 feat(dx): Add Builder and Server to auto-labeler 2024-07-18 00:19:38 +02:00
Reinier van der Leer
90371e1781 fix(builder): Remove legacy global styling from globals.css
This global styling was clashing with the theme of shad/cn and making text unreadable.
2024-07-18 00:06:52 +02:00
Reinier van der Leer
e128bfaf5f refactor(builder): Clean up AutoGPTServerAPI implementation (#7468)
- Abstract request implementation out of individual endpoints
- Rename Flow -> Graph (in type, method, and variable names)
2024-07-17 21:51:03 +02:00
Kate Silverstein
62c420e26f feat(forge/llm): Add LlamafileProvider (#7091)
* Add minimal implementation of `LlamafileProvider`, a new `ChatModelProvider` for llamafiles. It extends `BaseOpenAIProvider` and only overrides methods that are necessary to get the system to work at a basic level.

* Add support for `mistral-7b-instruct-v0.2`. This is the only model currently supported by `LlamafileProvider` because this is the only model I tested anything with.

* Add instructions to use AutoGPT with llamafile in the docs at `autogpt/setup/index.md`
* Add helper script to get it running quickly at `scripts/llamafile/serve.py`

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-07-17 15:56:55 +02:00
Krzysztof Czerwinski
97a5582c34 feat(agent_builder): Human-readable titles for blocks and fields (#7439)
Display human readable names for blocks and fields. `title` from schema is used if available, otherwise `beautifyString(name)`.
2024-07-17 14:44:41 +01:00
Zamil Majdy
78b84289cb feat(rnd): Add code-formatter & linting on AutoGPT server (#7458)
### Background

Add formatter & linter command.
Tools: ruff --> isort --> black --> pyright.

### Changes 🏗️

Introduced:
* `poetry run format`
* `poetry run lint`

`poetry run lint` will be executed on CI.
2024-07-17 11:54:29 +01:00
Krzysztof Czerwinski
9e22409d66 feat(agent_builder): Add block input fields description tooltip (#7437)
- Renamed `Schema` to `BlockSchema` and moved to `lib/types.ts`
- Add `SchemaTooltip` component that renders markdown tooltip for node fields
- Add `SecretField` function (which uses `BlockSecret` as value) that replaces `BlockFieldSecret` functionality for models
- Rename `get` to `get_secret_value` to make name clearer and inline with pydantic `Secret` types
- Add shadcn tooltip
- Add `react-markdown` dependency
2024-07-17 10:54:43 +01:00
Zamil Majdy
e7c075a521 feat(doc): Remove excessive docs for AutoGPT server (#7457) 2024-07-17 14:22:13 +07:00
Krzysztof Czerwinski
555e113706 feat(agent_builder, agent_server): Add customizable placeholders for input fields (#7451)
- Add `SchemaField` that works like Pydantic `Field` but allows to add extra json schema values. This PR adds `placeholder` entry but it could be extended with other data.
- Render `placeholder` inside input fields if available.
- Restyle placeholders so they are visually distinct from user-entered values
2024-07-16 20:08:43 +01:00
Bently
420e6cae2f feat(autogpt_builder): Fix Read API base URL from .env (#7455) 2024-07-16 16:28:24 +01:00
Toran Bruce Richards
920f931a21 feat(blocks): Add summariser block for recursive text summarization functionality (#7431)
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-07-16 17:51:37 +07:00
Toran Bruce Richards
e874318832 feat(blocks): Add WebSearch & WebScrapper block for searching the web with Jina Reader (#7445)
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-07-16 15:32:23 +07:00
Toran Bruce Richards
cb4b96a70c feat(server): Add DictionaryLookup block for looking up values in a dictionary (#7427)
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-07-16 15:23:12 +07:00
Toran Bruce Richards
03ea4c2690 fix(server): Fix bug in time comparison logic in RedditGetPostsBlock (#7426)
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-07-16 14:35:51 +07:00
Zamil Majdy
e70e613f73 feat(rnd): Refactor Block testing infra and Add Block auto-generation graph usecase (#7405)
### Background

The main scope of this change is enhancing the system capability (by fixing bug, correcting execution behaviour) to allow for creating a graph with a loop, to allow the use case of block auto-generation agent.

### Changes 🏗️

* Main changes: Add block_autogen.py (block auto-generation agent graph example).
* Refactor on test boilerplate: introduced `util/test` for initiating a server, and waiting graph execution.
* Removing unnecessary db lookup and duplicated codes used for sending execution updates on agent executor.
* Removed redundant code on test and cli code.
* Moved block test execution helper into the main code (so blockinstallerblock can use it).
* Eliminate the need of explicitly add a module into the `AVAILABLE_BLOCKS` list, any block class under the `block` folder will be auto-discovered.
* Few patches on the existing blocks.
2024-07-15 21:41:18 -05:00
Toran Bruce Richards
854f6dcaec Update README.md 2024-07-15 22:31:30 +01:00
Toran Bruce Richards
ea5ba9d193 Update README.md 2024-07-15 22:31:19 +01:00
Toran Bruce Richards
da14957fce Update README.md 2024-07-15 22:29:03 +01:00
Toran Bruce Richards
629f575dde Delete rnd/AutoGPT Server Setup Tutorial.mp4 (#7452) 2024-07-15 22:18:49 +01:00
Toran Bruce Richards
8883d7db53 Create README.md in rnd folder 2024-07-15 22:17:37 +01:00
Toran Bruce Richards
a6063e1550 Add files via upload 2024-07-15 22:06:57 +01:00
Toran Bruce Richards
e311847fa8 Update autogpt_server README.md 2024-07-15 20:26:44 +01:00
Bently
d673bf741a feat(autogpt_builder): Add websocket support to replace polling (#7449)
feat(autogpt_builder): Add websocket support
2024-07-15 20:18:23 +01:00
Swifty
110e093e7b feat(autogpt_server, autogpt_builder): Add Agent Versioning & Templates functionality (#7376)
1. Add graph versioning functionality:
   - Add `version`, `isActive` fields in the `AgentGraph` model
      - Add `agentGraphVersion` field in related models
   - Amend & add API endpoints for graph versioning (see below)
      - Amend & add data layer functions (`autogpt_server.data`) to support new operations & data schema

2. Add graph template functionality:
   - Add `isTemplate` fields in the `AgentGraph` model
   - Add `GraphMeta` model for template/graph metadata
   - Add API endpoints for template management (see below)
      - Amend & add data layer functions (`autogpt_server.data`) to support new operations & data schema

3. Enhance graph creation:
   - Amended `create_graph` route to handle template-based graph creation

4. Code refactoring:
   - Improved import statements
   - Enhanced error handling in graph creation

5. Minor improvements:
   - Add validator to auto-assign `Graph.id` if not set

## API Changes

New endpoints:
- `GET /templates`: Retrieve all templates (metadata only)
- `POST /templates`: Create a new template
- `PUT /graphs/{graph_id}`: Create a new version of a graph
- `GET /templates/{graph_id}`: Get a specific template
- `PUT /templates/{graph_id}`: Create a new version of a graph template
- `GET /graphs/{graph_id}/versions`: Get all versions of a graph
- `GET /templates/{graph_id}/versions`: Get all versions of a graph template
- `GET /graphs/{graph_id}/versions/{version}`: Get a specific graph version
- `PUT /graphs/{graph_id}/versions/active`: Set active graph version

Modified endpoints:
- `POST /graphs`: Now supports creating graphs directly from templates
- `GET /graphs/{graph_id}`: Add `version` query parameter
- `GET /graphs/{graph_id}/executions`: Add `graph_version` query parameters

## UI changes
- Improve `/build` / `FlowEditor` save mechanism
   - Implement updating current agent instead of creating a new agent on every save
   - Add check to only save a new version if local graph has been edited
      - Add `deepEquals` function to lib/utils.ts
- Add version indicators and selector on `/monitor`
  ![image](https://github.com/user-attachments/assets/742a66ee-4d12-4129-981d-fa6399e717fa)
   - Add shad/cn `DropdownMenu` component
- Update `AutoGPTServerAPI` client
   - Update input/output types with added attributes (see above)
   - Add parameter `version` to `getFlow`
   - Add parameter `flowVersion?` to `listFlowRunIDs`
   - Add endpoint `updateFlow(flowID, FlowUpdateable)`
   - Add endpoint `createFlow(fromTemplateID, templateVersion)` (overload)
   - Add endpoint `getFlowAllVersions(id)`
   - Add endpoint `setFlowActiveVersion(flowID, version)`
2024-07-15 20:26:00 +02:00
Toran Bruce Richards
93b6e0ee51 feat(blocks): Add support for additional LLM providers to LLM Block (#7434)
This commit adds support for the following models:

```python
# OpenAI Models
GPT4O = "gpt-4o"
GPT4_TURBO = "gpt-4-turbo"
GPT3_5_TURBO = "gpt-3.5-turbo"

# Anthropic models
CLAUDE_3_5_SONNET = "claude-3-5-sonnet-20240620"
CLAUDE_3_HAIKU = "claude-3-haiku-20240307"

# Groq models
LLAMA3_8B = "llama3-8b-8192"
LLAMA3_70B = "llama3-70b-8192"
MIXTRAL_8X7B = "mixtral-8x7b-32768"
GEMMA_7B = "gemma-7b-it"
GEMMA2_9B = "gemma2-9b-it"
```

Every model has been tested with a single LLM block and is confirmed to be working in that setup.
2024-07-15 20:30:56 +07:00
Bently
450f120510 feat(autogpt_builder): Fixes to wikipedia block (#7435) 2024-07-15 11:10:16 +07:00
Bently
2355d56801 feat(Builder): Updates to Nodes UI (#7414)
Updates to Nodes UI
2024-07-14 13:52:57 +01:00
Toran Bruce Richards
d3dae2264d tweak(docs): Replace all instances of "Node" with "Block". (#7416)
Replace all instances of "Node" with "Block" in the doccumentation.
2024-07-13 22:04:24 +01:00
Toran Bruce Richards
58313d9ae7 tweak(docs): Make link to The AutoGPT Server more prominant in docs navbar. (#7402) 2024-07-13 21:48:51 +01:00
Toran Bruce Richards
9e7dd4be74 tweak(rnd): Update AutoGPT logo file to be larger (#7408) 2024-07-13 21:08:09 +01:00
Krzysztof Czerwinski
6550bdc10c feat(rnd): AutoGPT Agent wrapper for Agent Server (#7365)
- Add `autogpt` and `forge` dependency to the `autogpt_server`
- Add `AutoGPTAgentBlock` that initializes and runs a single agent loop on execution
- Add `BlockAgent` that inherits from `autogpt` `Agent` and is a thin extension on the agent that allows to disable components
- Add `OutputComponent` that adds `output` command for the agent
2024-07-13 19:47:29 +01:00
Nicholas Tindle
0b9f3be6b8 feat(docs): add server new node docs (#7395)
Co-authored-by: Bently <27962737+Bentlybro@users.noreply.github.com>
2024-07-12 08:59:35 -05:00
Reinier van der Leer
a2d8d9bac9 feat(autogpt_builder/monitor): Monitor page v0.2 (#7385)
- Improve responsive grid layout
  - Remove `container` class from `<main>` to utilize full screen width

- Improve detail views & add view for run details
  - Make flow run list entries selectable
  - Create `FlowInfo` and `FlowRunInfo` components
  - Improve layout of `FlowRunsStats`

- Improve ScrollableLegend spacing & styling
  - Hide scroll bar of scrollable legend
  - Center legend items if there is space left
  - Round icons
  - Vertically align icons with labels
  - FIX: Add condition to not display legend items for series with `legendType="none"`

- Add periodic 5s refresh of non-terminal flow run statuses
  - Split off `refreshFlowRuns(flowID)` from `fetchFlowsAndRuns()`
  - Add effect to run `refreshFlowRuns` every 5 seconds

- Improve and expand FlowRun info
  - Add `FlowRun.totalRunTime`: sum of the individual execution durations of all nodes
  - Add `FlowRun.endTime`
  - Use `NodeExecutionResult.add_time` instead of `start_time` as `FlowRun.startTime`

- Sort Flows by last executed

- Add icons to navbar items & hide unused items Backtrack and Explore

- Change UI mentions of "(Agent) Flow" to "Agent"
2024-07-12 14:56:48 +02:00
Bently
b7096e01fb feat(Builder): Adds node to send messages to discord webhook (#7381)
* Adds core files for discord node

* fix naming

* Rename DiscordSendMessage.py to discordsendmessage.py

* update naming again

* fix conflicts

* fix imports
2024-07-12 11:58:11 +01:00
Krzysztof Czerwinski
bffb92bfbc fix(agent, forge): Fix Pydantic v2 protected namespace model_ warnings (#7340)
Rename `model_*` fields to `llm_*`
2024-07-12 12:05:03 +02:00
Bently
2ecce27653 feat(Builder): Adds node to get summary from wikipedia (#7384)
* adds node to get summary from wikipedia

* update name

* Fix naming again
2024-07-12 09:51:56 +01:00
Zamil Majdy
1089551869 feat(builder): Add service-level creds access for agent blocks (#7373)
### Background

Credentials for blocks could only be defined through the block input. The scope of this change is providing system-wide that becomes the default value for these input blocks.

### Changes 🏗️

* Add system-wide credential support for agent blocks `BlockFieldSecret`.
* Update llmcall & reddit block to adopt `BlockFieldSecret`.
2024-07-12 15:41:55 +07:00
Aarushi
58af7f9466 feat(autogpt_builder) Remove saving of execution data into sink node (#7383)
remove saving of execution data into sink node
2024-07-12 01:44:11 +02:00
Aarushi
b89609fd16 feat(autogpt_builder) Add logic for node disconnection (#7386)
add logic for node disconnection
2024-07-11 23:33:21 +01:00
Reinier van der Leer
dcfc3a4dad fix(autogpt_builder): Restore node input presets when loading a flow (#7379)
- Assign `node.input_default` to `hardcodedValues` in `FlowEditor:loadFlow(..)`
2024-07-11 18:37:04 +01:00
Nicholas Tindle
7cf6d3ff79 feat(dx): add a code-workspace for vscode (#7285)
Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-07-11 19:24:35 +02:00
Toran Bruce Richards
f4f164ac15 chore(rnd): Update animation direction for customnode.css to "ping-pong" (#7377) 2024-07-11 16:09:02 +01:00
Bently
cd9d041fe5 feat(builder): add button for showing/hiding optional inputs (#7375)
add button for showing/hiding optional inputs in nodes
2024-07-11 15:14:07 +01:00
Bently
0fb8a84382 fix to make monitor use env (#7371)
fix to make it use env
2024-07-10 17:18:59 +01:00
Aarushi
a408da8317 aarushikansal/flow stats scroll legend (#7370)
* feat(agent_builder): Add shad/cn + Radix Icons + Tailwind

* move favicon.ico to static folder

* delete empty custominput.css

* feat(agent_builder): Add basic app layout with nav

* add timeline chart to Monitor page and improve overall mock-up functionality

* add some (mock) stats to the overview

* monitor/page.tsx: Switch from mock data to API & reorder file

* undo out-of-scope changes to Flow.tsx and .css files

* fix linting issue in `FlowRunsTimeline`

* wip scrollable

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-10 18:07:07 +02:00
Reinier van der Leer
976ff04cce feat(autogpt_builder): Initial Monitor page implementation (#7335)
* feat(agent_builder): Add shad/cn + Radix Icons + Tailwind

* move favicon.ico to static folder

* delete empty custominput.css

* feat(agent_builder): Add basic app layout with nav

* add timeline chart to Monitor page and improve overall mock-up functionality

* add some (mock) stats to the overview

* monitor/page.tsx: Switch from mock data to API & reorder file

* undo out-of-scope changes to Flow.tsx and .css files

* fix linting issue in `FlowRunsTimeline`

---------

Co-authored-by: Aarushi <aarushik93@gmail.com>
Co-authored-by: Swifty <craigswift13@gmail.com>
2024-07-10 18:04:51 +02:00
Swifty
3c91038089 tweak(ui): Switch from modal.css to tailwindcss classes (#7369)
* reverts dark theme for now

* change "Show/Hide nodes" button to be "Icon"

* swap over to light mode + fix sizing

* fix color for agent name + description text

* Change navbar to white

* Added darkmode tag for the navbar

* Added dark mode text color

* Changed to tailwind classes

---------

Co-authored-by: Bentlybro <tomnoon9@gmail.com>
2024-07-10 18:04:02 +02:00
Swifty
d4b441932d tweak(ui): Tweaked light mode color (#7366)
* reverts dark theme for now

* change "Show/Hide nodes" button to be "Icon"

* swap over to light mode + fix sizing

* fix color for agent name + description text

* Change navbar to white

* Added darkmode tag for the navbar

* Added dark mode text color

---------

Co-authored-by: Bentlybro <tomnoon9@gmail.com>
2024-07-10 18:02:06 +02:00
Bently
14ddb915bf feat(autogpt_builder): swapping to white theme for the layout (#7358)
* reverts dark theme for now

* change "Show/Hide nodes" button to be "Icon"

* swap over to light mode + fix sizing

* fix color for agent name + description text

* update icon
2024-07-10 18:01:07 +02:00
Aarushi
d6cbb48609 feat(agpt_builder) Fix data persistance on agent execution (#7363)
* fixing issue with data vanishing when executing agent

* fix rebasing

* add unique key

* reset to neutral & failed colours
2024-07-10 12:47:55 +01:00
Swifty
3789b00479 feat(autogpt_server): Expose rest api via websocket (#7350)
* Add in websocket event types

* adding in api endpoints

* Updated ws messages
2024-07-10 10:54:18 +01:00
Swifty
f94e81f48b feat(builder) Add save Agent functionality (#7361)
Add save functionality
2024-07-10 10:01:12 +01:00
Zamil Majdy
e10c4ee4cd fix(doc): Fix Auto GPT server Running The Server doc (#7360) 2024-07-10 10:31:52 +02:00
Reinier van der Leer
81dee568cb feat(autogpt_builder): Add basic layout with nav (#7317)
* feat(agent_builder): Add shad/cn + Radix Icons + Tailwind

* move favicon.ico to static folder

* delete empty custominput.css

* feat(agent_builder): Add basic app layout with nav

* Revert unwanted changes

* Fix /build + Flow layout issues

- Add `className` passthrough to `Flow` component
- Fix /build page layout

* unfix build/page.tsx indentation for git tracking

---------

Co-authored-by: Toran Bruce Richards <toran.richards@gmail.com>
2024-07-09 17:33:12 +01:00
Bently
7929f1a4ac Reads data from .env (#7357) 2024-07-09 17:20:31 +01:00
Bently
0a28c72bad Fix issue with node inputs (#7356) 2024-07-09 16:21:13 +01:00
Toran Bruce Richards
b9861a5308 fix(autogpt_builder): Update AutoGPT logo in page component (#7354)
feat(autogpt_builder): Update AutoGPT logo in page component
2024-07-09 16:10:13 +01:00
Zamil Majdy
af3a2bb5f5 fix(doc): Fix Auto GPT server Running The Server doc (#7355) 2024-07-09 16:09:43 +01:00
Bently
2f174837bd Add input for agent name & description (#7351) 2024-07-09 15:44:36 +01:00
Aarushi
b30eaf653a feat(autogpt_builder) Remove submit button (#7353)
remove submit button
2024-07-09 15:23:36 +01:00
Zamil Majdy
d9c9b22886 feat(rnd): Add Agent Block sample/test input output for auto-generated unit testing for block (#7310)
Sample test input and output on the block can serve as documentation and auto-generated unit-testing code for the agent block.

What's within the scope of this change:
Adding the fields for block test (input, output, mocks), and its execution.

What's still outside the scope:
Handling of mock and stub for a block using sensitive credentials or network calls or 3rd-party connections.
2024-07-09 13:08:42 +02:00
Zamil Majdy
ff71b0beb7 fix(rnd): Avoid expensive call of get_service_client on block execution (#7349) 2024-07-09 13:07:57 +02:00
Swifty
57cc8b69e9 fix(agent_server): Fix async issue with executor integration test (#7348)
Fix execution completed check
2024-07-09 17:09:44 +07:00
Aarushi
7ce0c655d0 feat(autogpt_builder) Hook up execution progress animation to live execution data (#7347)
* hooked up animation to actual execution data

* remove console log
2024-07-09 09:32:38 +01:00
Bently
1e755f9e8d feat(agent_builder): Updates to Builder GUI + Nodes (#7345)
* Updates to Builder GUI + Nodes

* fix apiUrl back to local host
2024-07-08 21:33:07 +01:00
Swifty
f9bedb0fd9 fix(autogpt_server): Changed inconstant timestamps to all use UTC (#7343)
* Changed timestamps to utc

* made other datetime.now timezone aware
2024-07-08 17:09:38 +02:00
Swifty
a32bc72314 feast(agent server) Add populate db command (#7342)
* Add populate db command

* Added in a reddit graph creation command
2024-07-08 17:09:20 +02:00
Reinier van der Leer
227092b669 feat(autogpt_builder): Enable loading an existing flow (#7338)
- Add `flowID` query parameter
- Add logic to load a flow from the server into the builder
2024-07-07 01:39:05 +02:00
Reinier van der Leer
39556a71cc feat(autogpt_builder): Improve server error handling in AutoGPTServerAPI 2024-07-06 01:03:23 -06:00
Reinier van der Leer
1fb8c1adac fix(autogpt_builder): Fix date conversion in AutoGPTServerAPI client
Explicitly parse dates in JSON response object in `getFlowExecutionInfo(..)`
2024-07-06 01:01:41 -06:00
Swifty
37e1780d76 feat(agent server): Added websocket communication (#7291)
* Refactor on the link structure and API

* Refactor on the link structure and API

* Cleanup IDS

* Remove run_id

* Update block interface

* Added websockets dependency

* Adding routes

* Adding in websocket code

* Added cli to test the websocket

* Added an outline of the message formats I plan on using

* Added webscoket message types

* Updated poetry lock

* Adding subscription logic

* Updating subscription mechanisms

* update cli

* Send updates to server

* Get single execution data

* Fix type hints and renamed function

* add callback function and type hints

* fix type hints

* Updated manager to use property

* Added in websocket updates

* Added connection manager tests

* Added tests for ws_api

* trying to work around process issues

* test formatting

* Added a create and execute command for the cli

* Updated send format

* websockets command working

* cli update

* Added model.py

* feat: Update server.py and manager.py

- Initialize blocks in AgentServer lifespan context
- Remove unnecessary await in AgentServer get_graph_blocks
- Fix type hinting in manager.py
- Validate input data in validate_exec function

* fix tests

* feat: Add autogpt_server.blocks.sample and autogpt_server.blocks.text modules

This commit adds the `autogpt_server.blocks.sample` and `autogpt_server.blocks.text` modules to the project. These modules contain blocks that are used in the execution of the Autogpt server. The `ParrotBlock` and `PrintingBlock` classes are imported from `autogpt_server.blocks.sample`, while the `TextFormatterBlock` class is imported from `autogpt_server.blocks.text`. This addition enhances the functionality of the server by providing additional blocks for text processing and sample operations.

* fixed circular import issue

* Update readme

---------

Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-07-05 17:02:26 +02:00
Reinier van der Leer
0df2199c42 feat(autogpt_server): Add GET /graphs/{graph_id}/executions endpoint (#7330)
* feat(autogpt_builder): Add `AutoGPTServerAPI` client

* migrate API calls in Flow.tsx to new API client

* feat(autogpt_server): Add `/graphs/{graph_id}/executions` endpoint

In `data/execution.py`:
- Add `list_executions` function
- Rename `get_executions` to `get_execution_results`

In `server/server.py`:
- Add route
- Add `AgentServer.list_graph_runs`
- Rename `AgentServer.get_executions` to `get_run_execution_results`

* feat(autogpt_builder): Add `listFlowRunIDs` endpoint to `AutoGPTServerAPI` client

* Move `Schema` to `types.ts` and rename to `ObjectSchema`
2024-07-05 10:51:27 +01:00
Reinier van der Leer
200800312a feat(autogpt_builder): Add AutoGPTServerAPI client (#7328)
- Add fully typed `AutoGPTServerAPI` client in lib/autogpt_server_api.ts
- Migrate API calls in `Flow.tsx` to new API client
2024-07-05 11:03:59 +02:00
Reinier van der Leer
b7a90ce768 fix(autogpt_builder): Unignore lib/ and add lib/utils.ts 2024-07-04 17:40:59 -06:00
Reinier van der Leer
f359ed0983 feat(agent_builder): Add shad/cn UI library (#7316)
- Add shad/cn + Radix Icons + Tailwind
- move favicon.ico to static folder
- delete empty custominput.css

---------

Co-authored-by: Bentlybro <tomnoon9@gmail.com>
2024-07-05 08:14:45 +02:00
Aarushi
6456285753 feat(autogpt_builder) Update custom node to handle deeply nested structures (#7319)
* feat(rnd): Add type hint and strong pydantic type validation for block input/output + add reddit agent-blocks.

* feat(rnd): Add type hint and strong pydantic type validation for block input/output + add reddit agent-blocks.

* Fix reddit block

* Fix serialization

* Eliminate deprecated class property

* Remove RedditCredentialsBlock

* Cache jsonschema computation, add dictionary construction

* Add dict_split and list_split to output, add more blocks

* Add objc_split for completeness, int both input and output

* Update reddit block

* Add reddit test (untested)

* Resolved json issue on pydantic

* Add creds check on client

* Add dict <--> pydantic object flexibility

* Fix error retry

* Skip reddit test

* Code cleanup

* Chang prompt

* Make this work

* Fix linting

* Hide input_links and output_links from Node

* Add docs

* updating UI to handle deeply nested data structures for reddit usecase

* changing expected key in reddit post to comment

---------

Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
2024-07-04 17:54:41 +01:00
Zamil Majdy
833944e228 feat(rnd): Add strong pydantic type & composite data extraction for Block input/output schema + add reddit agent-blocks (#7288)
* feat(rnd): Add type hint and strong pydantic type validation for block input/output + add reddit agent-blocks.

* feat(rnd): Add type hint and strong pydantic type validation for block input/output + add reddit agent-blocks.

* Fix reddit block

* Fix serialization

* Eliminate deprecated class property

* Remove RedditCredentialsBlock

* Cache jsonschema computation, add dictionary construction

* Add dict_split and list_split to output, add more blocks

* Add objc_split for completeness, int both input and output

* Update reddit block

* Add reddit test (untested)

* Resolved json issue on pydantic

* Add creds check on client

* Add dict <--> pydantic object flexibility

* Fix error retry

* Skip reddit test

* Code cleanup

* Chang prompt

* Make this work

* Fix linting

* Hide input_links and output_links from Node

* Add docs

---------

Co-authored-by: Aarushi <50577581+aarushik93@users.noreply.github.com>
2024-07-04 11:37:28 +01:00
Reinier van der Leer
db0e726954 fix(agent, benchmark): Specify path_type=Path for CLI path options/arguments
Without `path_type=Path`, an option/argument with `type=click.Path()` will return a `str`.
2024-07-03 15:21:04 -06:00
Krzysztof Czerwinski
08612cc3bf refactor(agent, forge): Move tests from autogpt to forge (#7247)
- Move `autogpt/tests/vcr_cassettes` submodule to `forge/tests/vcr_cassettes`
- Remove not needed markers from `pyproject.toml`: `"requires_openai_api_key", "requires_huggingface_api_key"`
- Update relevant GitHub workflows

Moved relevant tests from `autogpt/tests` to appropiate directories:
- Component tests to their respective component dirs
- `autogpt/tests/unit/test_web_search.py` → `forge/components/web/test_search.py`
- `autogpt/tests/unit/test_git_commands.py` → `forge/components/git_operations/test_git_operations.py`
- `autogpt/tests/unit/test_file_operations.py` → `forge/components/file_manager/test_file_manager.py`
- `autogpt/tests/integration/test_image_gen.py` → `forge/components/image_gen/test_image_gen.py`
- `autogpt/tests/integration/test_web_selenium.py` → `forge/components/web/test_selenium.py`
- `autogpt/tests/integration/test_execute_code.py` → `forge/components/code_executor/test_code_executor.py`
- `autogpt/tests/unit/test_s3_file_storage.py` → `forge/file_storage/test_s3_file_storage.py`
- `autogpt/tests/unit/test_gcs_file_storage.py` → `forge/file_storage/test_gcs_file_storage.py`
- `autogpt/tests/unit/test_local_file_storage.py` → `forge/file_storage/test_local_file_storage.py`
- `autogpt/tests/unit/test_json.py` → `forge/json/test_parsing.py`
- `autogpt/tests/unit/test_logs.py` → `forge/logging/test_utils.py`
- `autogpt/tests/unit/test_url_validation.py` → `forge/utils/test_url_validator.py`
- `autogpt/tests/unit/test_text_file_parsers.py` → `forge/utils/test_file_operations.py`

- (Re)moved dependencies from `autogpt/pyproject.toml` that were only used in these test files.

Also:
- Added `load_env_vars` fixture to `forge/conftest.py`
- Fixed a type error in `forge/components/web/test_search.py`
- Merged `autogpt/.gitattributes` into root `.gitattributes`

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-07-04 02:09:01 +02:00
Reinier van der Leer
7415e24fc3 fix(forge): Add google-api-python-client-stubs and fix type error (#7303)
- Add `google-api-python-client-stubs` dev dependency
- Add version specification to `google-api-python-client` dependency
- Fix type error (by ignoring it) in forge/components/web/search.py
2024-07-03 23:26:37 +02:00
Dimitri Brooks
ecb054af56 feat: Add support for Claude 3.5 Sonnet (#7301)
add support for Claude 3.5 Sonnet
2024-07-03 16:02:14 +02:00
Krzysztof Czerwinski
39f70b0c83 docs(agent, forge): Update component configuration docs (#7232) 2024-07-03 09:50:38 +01:00
Krzysztof Czerwinski
7cb4d4a903 feat(forge, agent, benchmark): Upgrade to Pydantic v2 (#7280)
Update Pydantic dependency of `autogpt`, `forge` and `benchmark` to `^2.7`
[Pydantic Migration Guide](https://docs.pydantic.dev/2.7/migration/)

- Migrate usages of now-deprecated functions to their replacements
- Update `Field` definitions
  - Ellipsis `...` for required fields is deprecated
  - `Field` no longer supports extra `kwargs`, replace use of this feature with field metadata
- Replace `Config` class for specifying model configuration with `model_config = ConfigDict(..)`
- Removed `ModelContainer` in `BaseAgent`, component configuration dict is now directly serialized using Pydantic v2 helper functions
- Forked `agent-protocol` and updated `packages/client/python` for Pydantic v2 support: https://github.com/Significant-Gravitas/agent-protocol

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-07-02 20:45:32 +02:00
Aarushi
8feaced92e (rnd) Add support for multiple, dynamic inputs (#7296)
multi dynamic inputs
2024-07-02 09:56:33 +01:00
Reinier van der Leer
97e4cceb94 feat(agent, forge): Markdown-formatted history -> message history (#7228)
- Implement message based history in `ActionHistoryComponent`
- Make non-summarized message count configurable (`ActionHistoryComponent.full_message_count`)
- Run `ActionHistoryComponent` after `SystemComponent` so that history messages are last in the prompt
- Omit final instruction message if prompt already contains assistant messages
- Filter `raw_message` from `ActionProposal.schema()`

---------

Co-authored-by: Krzysztof Czerwinski <kpczerwinski@gmail.com>
2024-07-02 03:47:55 +02:00
Reinier van der Leer
2fa4fd23af fix(agent): Make build dependencies optional to unbreak install (#7298)
* Create optional `build` dependency group
* Move `cx-freeze` dependency to `build` dependency group

To include the `build` group when installing dependencies, run `poetry install --with=build`.

Fixes #7297 (`cx-freeze` dependency install fails after #7271)
2024-07-02 02:08:02 +02:00
angeousta
976ea7cd3c fix readme (broken link) (#7293)
Update README.md
2024-07-01 09:44:46 +02:00
Bently
d5ab83aa34 Updating api calls in AutoGPT builder (#7275)
* update api endpoints

* get multi-node working + fix node output

* updated multi-node running + re-add "wire" on rebuild

* Fix node data mapping

* removed getStatusValue
2024-06-28 10:39:26 +01:00
Reinier van der Leer
cbae8b5c14 chore(agent, forge, benchmark): Clean up dependencies (#7286)
* Remove unused dependencies
* Move dependencies for moved code from `autogpt` to `forge`
* Loosen dependency for `uvicorn` to improve compatibility
2024-06-28 02:21:36 +02:00
Aarushi
854080f7af ci(builder): Add CI for AutoGPT Builder (#7267)
Add a simple Node/NPM linting workflow for `rnd/autogpt_builder/`

---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-06-28 01:30:34 +02:00
Reinier van der Leer
fbb3891e79 chore(forge, agent, benchmark): Update pytest-asyncio to v0.23.x
Resolves #7283
2024-06-27 14:09:36 -06:00
Reinier van der Leer
4d8ee65ca7 fix(pre-commit): Add benchmark/poetry.lock to "Typecheck - Benchmark" trigger file pattern 2024-06-27 14:05:12 -06:00
Aarushi
6093acc813 Add more functionality to Nodes (#7278)
updating node behaviour
2024-06-27 17:03:10 +01:00
Nicholas Tindle
785a40ff9d feat(server, autogpt): Add Example files and update build option (#7271) 2024-06-27 09:56:21 -05:00
Zamil Majdy
2bc22c5450 feat(rnd): Add support for dynamic input as list for AgentServer Block (#7268)
On AgentServer, To create a Block like StringFormatterBlock or LllmCallBlock, we need some way to dynamically link input pins and aggregate them into a single list input. This will give a better experience for the user to construct an input and link it from the output of the other nodes. The scope of this change is adding support for that in the least intrusive way.

Proposal
To differentiate the input list name and its singular entry we are using the $_<index> prefix. For example:
For the input items: list[int], you can set a pin items with values like [1,2,3,4]. But you can also add input pins like items_$_0 or items_$_4 with values 1 or 2, which will be appended to the items input in alphabetical order.
The execution engine will guarantee to wait for the execution until all the input pin value is produced, so input pin with list input will produce fix-sized list.
2024-06-27 21:51:34 +07:00
Aarushi
cdc658695f Add reactflow component in AutoGPT builder (#7270)
* Getting started with nextjs

* fix linting

* remove gitignore for package.json

* pulling in reactflow components

* updating css

* use environment variables

* clean up css / ui a lil

* Fixed nodes/run button animation

so they are always visible

---------

Co-authored-by: Bentlybro <tomnoon9@gmail.com>
2024-06-27 10:14:25 +01:00
Aarushi
dd960f9306 Add support for nextjs based app (#7266)
* Getting started with nextjs

* fix linting

* remove gitignore for package.json
2024-06-27 10:02:54 +01:00
Krzysztof Czerwinski
6e1c9d44a4 Update Agent Server README.md 2024-06-26 19:41:12 +02:00
Zamil Majdy
26bcb26bb7 feat(rnd): Refactor AgentServer Node Input/Output Relation & Block output interface (#7231)
### Background

The current implementation of AgentServer doesn't allow for a single pin to be connected to multiple nodes, this will be problematic when you have a single output node that needs to be propagated into many nodes. Or multiple nodes that possibly feed the data into a single pin (first come first serve).

This infra change is also part of the preparation for changing the `block` interface to return a stream of output instead of a single output.  Treating blocks as streams requires this capability.

### Changes 🏗️

* Update block run interface from returning `(output_name, output_data)` to `Generator[(output_name, output_data)]`
* Removed `agent` term in the API, replace it with `graph` for consistency.
* Reintroduced `AgentNodeExecutionInputOutput`. `AgentNodeExecution` input & output will be a list of `AgentNodeExecutionInputOutput` which describes the input & output data of its execution. Making an execution has 1-many relation to its input output data.
* Propagating the relation and block interface change into the execution engine.
2024-06-26 12:41:55 +02:00
Nicholas Tindle
f04ddceacf docs(forge): Update and rename QUICKSTART.md to FORGE-QUICKSTART.md (#7215) 2024-06-25 19:03:05 -05:00
Bently
3e01b19d6f chore(forge): Update duckduckgo-search to v6.1.7 (#7254)
This should (for now) mitigate the RateLimitErrors that people have been experiencing.
---------

Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2024-06-25 03:24:10 +02:00
Zamil Majdy
9f1e521857 feat(rnd): Add AutoGPT server scheduling service (#7226)
### Background

Agent execution should be able to be triggered in a recurring manner.
This PR introduced an ExecutionScheduling service, a process responsible for managing the execution schedule and triggering its execution based on a predefined cron expression.

### Changes 🏗️

* Added `scheduler.py` / `ExecutionScheduler` implementation.
* Added scheduler test.
* Added `AgentExecutionSchedule` table and its logical model & prisma queries.
* Moved `add_execution` from API server to `execution_manager`
2024-06-24 09:41:02 +07:00
Zamil Majdy
d9226888b2 feat(rnd): Add node metadata on Agent Server Node object (#7237) 2024-06-21 17:50:50 +01:00
Zamil Majdy
210d7738b9 feat(rnd): Add IPC support on autogpt_server (#7212)
### Background

This PR adds support on IPC on autogpt_server.
To make this happen, there are a couple of refactoring efforts being made (will be described in the `Changes` section).
Currently, there are three independent processes:

```
AgentServer ----> ExecutionManager
    | 
     --> ExecutionScheduler
```


### Changes 🏗️

* Added Pyro5 for IPC support.
* Introduced `AppService`: a class to construct an independent process that can expose a method to other running processes (this is analogous to a microservice).
* Introduced `AppProcess`: used by `AppService` a class for creating a child process that can be executed in the background.
* Adapting existing codebase to user `AppService`.
2024-06-19 22:49:47 +07:00
Krzysztof Czerwinski
c19ab2b24f feat(forge): Component-specific configuration (#7170)
Remove many env vars and use component-level configuration that could be loaded from file instead.

### Changed

- `BaseAgent` provides `serialize_configs` and `deserialize_configs` that can save and load all component configuration as json `str`. Deserialized components/values overwrite existing values, so not all values need to be present in the serialized config.
- Decoupled `forge/content_processing/text.py` from `Config`
- Kept `execute_local_commands` in `Config` because it's needed to know if OS info should be included in the prompt
- Updated docs to reflect changes
- Renamed `Config` to `AppConfig`

### Added

- Added `ConfigurableComponent` class for components and following configs:
  - `ActionHistoryConfiguration`
  - `CodeExecutorConfiguration`
  - `FileManagerConfiguration` - now file manager allows to have multiple agents using the same workspace
  - `GitOperationsConfiguration`
  - `ImageGeneratorConfiguration`
  - `WebSearchConfiguration`
  - `WebSeleniumConfiguration`
- `BaseConfig` in `forge` and moved `Config` (now inherits from `BaseConfig`) back to `autogpt`
- Required `config_class` attribute for the `ConfigurableComponent` class that should be set to configuration class for a component
`--component-config-file` CLI option and `COMPONENT_CONFIG_FILE` env var and field in `Config`. This option allows to load configuration from a specific file, CLI option takes precedence over env var.
- Added comments to config models

### Removed

- Unused `change_agent_id` method from `FileManagerComponent`
- Unused `allow_downloads` from `Config` and CLI options (it should be in web component config if needed)
- CLI option `--browser-name` (the option is inside `WebSeleniumConfiguration`)
- Unused `workspace_directory` from CLI options
- No longer needed variables from `Config` and docs
- Unused fields from `Config`: `image_size`, `audio_to_text_provider`, `huggingface_audio_to_text_model`
- Removed `files` and `workspace` class attributes from `FileManagerComponent`
2024-06-19 09:14:01 +01:00
Reinier van der Leer
02dc198a9f fix(agent): Fix resumption from mid-cycle state in CLI mode (#7224)
When an agent is resumed from a mid-cycle state (having made a proposal but not executed it yet), we need to use the previously determined `current_episode.action` proposal instead of calling `agent.propose_action()` again.
2024-06-18 19:30:24 +02:00
2848 changed files with 129036 additions and 18715 deletions

7
.gitattributes vendored
View File

@@ -1,5 +1,10 @@
frontend/build/** linguist-generated
classic/frontend/build/** linguist-generated
**/poetry.lock linguist-generated
docs/_javascript/** linguist-vendored
# Exclude VCR cassettes from stats
classic/forge/tests/vcr_cassettes/**/**.y*ml linguist-generated
* text=auto

12
.github/CODEOWNERS vendored
View File

@@ -1,5 +1,7 @@
.github/workflows/ @Significant-Gravitas/devops
autogpt/ @Significant-Gravitas/maintainers
forge/ @Significant-Gravitas/forge-maintainers
benchmark/ @Significant-Gravitas/benchmark-maintainers
frontend/ @Significant-Gravitas/frontend-maintainers
* @Significant-Gravitas/maintainers
.github/workflows/ @Significant-Gravitas/devops
classic/forge/ @Significant-Gravitas/forge-maintainers
classic/benchmark/ @Significant-Gravitas/benchmark-maintainers
classic/frontend/ @Significant-Gravitas/frontend-maintainers
autogpt_platform/infra @Significant-Gravitas/devops
.github/CODEOWNERS @Significant-Gravitas/admins

View File

@@ -88,14 +88,16 @@ body:
- type: dropdown
attributes:
label: Do you use OpenAI GPT-3 or GPT-4?
label: What LLM Provider do you use?
description: >
If you are using AutoGPT with `SMART_LLM=gpt-3.5-turbo`, your problems may be caused by
the [limitations](https://github.com/Significant-Gravitas/AutoGPT/issues?q=is%3Aissue+label%3A%22AI+model+limitation%22) of GPT-3.5.
options:
- GPT-3.5
- GPT-4
- GPT-4(32k)
- Azure
- Groq
- Anthropic
- Llamafile
- Other (detail in issue)
validations:
required: true
@@ -126,6 +128,13 @@ body:
label: Specify the area
description: Please specify the area you think is best related to the issue.
- type: input
attributes:
label: What commit or version are you using?
description: It is helpful for us to reproduce to know what version of the software you were using when this happened. Please run `git log -n 1 --pretty=format:"%H"` to output the full commit hash.
validations:
required: true
- type: textarea
attributes:
label: Describe your issue.

View File

@@ -6,26 +6,18 @@
<!-- Concisely describe all of the changes made in this pull request: -->
### PR Quality Scorecard ✨
### Testing 🔍
> [!NOTE]
Only for the new autogpt platform, currently in autogpt_platform/
<!--
Check out our contribution guide:
https://github.com/Significant-Gravitas/AutoGPT/wiki/Contributing
1. Avoid duplicate work, issues, PRs etc.
2. Also consider contributing something other than code; see the [contribution guide]
for options.
3. Clearly explain your changes.
4. Avoid making unnecessary changes, especially if they're purely based on personal
preferences. Doing so is the maintainers' job. ;-)
Please make sure your changes have been tested and are in good working condition.
Here is a list of our critical paths, if you need some inspiration on what and how to test:
-->
- [x] Have you used the PR description template? &ensp; `+2 pts`
- [ ] Is your pull request atomic, focusing on a single change? &ensp; `+5 pts`
- [ ] Have you linked the GitHub issue(s) that this PR addresses? &ensp; `+5 pts`
- [ ] Have you documented your changes clearly and comprehensively? &ensp; `+5 pts`
- [ ] Have you changed or added a feature? &ensp; `-4 pts`
- [ ] Have you added/updated corresponding documentation? &ensp; `+4 pts`
- [ ] Have you added/updated corresponding integration tests? &ensp; `+5 pts`
- [ ] Have you changed the behavior of AutoGPT? &ensp; `-5 pts`
- [ ] Have you also run `agbenchmark` to verify that these changes do not regress performance? &ensp; `+10 pts`
- Create from scratch and execute an agent with at least 3 blocks
- Import an agent from file upload, and confirm it executes correctly
- Upload agent to marketplace
- Import an agent from marketplace and confirm it executes correctly
- Edit an agent from monitor, and confirm it executes correctly

16
.github/labeler.yml vendored
View File

@@ -1,19 +1,27 @@
AutoGPT Agent:
- changed-files:
- any-glob-to-any-file: autogpt/**
- any-glob-to-any-file: classic/original_autogpt/**
Forge:
- changed-files:
- any-glob-to-any-file: forge/**
- any-glob-to-any-file: classic/forge/**
Benchmark:
- changed-files:
- any-glob-to-any-file: benchmark/**
- any-glob-to-any-file: classic/benchmark/**
Frontend:
- changed-files:
- any-glob-to-any-file: frontend/**
- any-glob-to-any-file: classic/frontend/**
documentation:
- changed-files:
- any-glob-to-any-file: docs/**
Builder:
- changed-files:
- any-glob-to-any-file: autogpt_platform/autogpt_builder/**
Server:
- changed-files:
- any-glob-to-any-file: autogpt_platform/autogpt_server/**

View File

@@ -1,268 +0,0 @@
name: AutoGPT Server CI
on:
push:
branches: [master, development, ci-test*]
paths:
- ".github/workflows/autogpt-server-ci.yml"
- "rnd/autogpt_server/**"
- "!autogpt/tests/vcr_cassettes"
pull_request:
branches: [master, development, release-*]
paths:
- ".github/workflows/autogpt-server-ci.yml"
- "rnd/autogpt_server/**"
- "!autogpt/tests/vcr_cassettes"
concurrency:
group: ${{ format('autogpt-server-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }}
defaults:
run:
shell: bash
working-directory: rnd/autogpt_server
jobs:
test:
permissions:
contents: read
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
platform-os: [ubuntu, macos, macos-arm64, windows]
runs-on: ${{ matrix.platform-os != 'macos-arm64' && format('{0}-latest', matrix.platform-os) || 'macos-14' }}
steps:
# Quite slow on macOS (2~4 minutes to set up Docker)
# - name: Set up Docker (macOS)
# if: runner.os == 'macOS'
# uses: crazy-max/ghaction-setup-docker@v3
- name: Start MinIO service (Linux)
if: runner.os == 'Linux'
working-directory: "."
run: |
docker pull minio/minio:edge-cicd
docker run -d -p 9000:9000 minio/minio:edge-cicd
- name: Start MinIO service (macOS)
if: runner.os == 'macOS'
working-directory: ${{ runner.temp }}
run: |
brew install minio/stable/minio
mkdir data
minio server ./data &
# No MinIO on Windows:
# - Windows doesn't support running Linux Docker containers
# - It doesn't seem possible to start background processes on Windows. They are
# killed after the step returns.
# See: https://github.com/actions/runner/issues/598#issuecomment-2011890429
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- id: get_date
name: Get date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Set up Python dependency cache
# On Windows, unpacking cached dependencies takes longer than just installing them
if: runner.os != 'Windows'
uses: actions/cache@v4
with:
path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }}
key: poetry-${{ runner.os }}-${{ hashFiles('rnd/autogpt_server/poetry.lock') }}
- name: Install Poetry (Unix)
if: runner.os != 'Windows'
run: |
curl -sSL https://install.python-poetry.org | python3 -
if [ "${{ runner.os }}" = "macOS" ]; then
PATH="$HOME/.local/bin:$PATH"
echo "$HOME/.local/bin" >> $GITHUB_PATH
fi
- name: Install Poetry (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
$env:PATH += ";$env:APPDATA\Python\Scripts"
echo "$env:APPDATA\Python\Scripts" >> $env:GITHUB_PATH
- name: Install Python dependencies
run: poetry install
- name: Generate Prisma Client
run: poetry run prisma generate
- name: Run Database Migrations
run: poetry run prisma migrate dev --name updates
- name: Run pytest with coverage
run: |
poetry run pytest -vv \
test
env:
CI: true
PLAIN_OUTPUT: True
# - name: Upload coverage reports to Codecov
# uses: codecov/codecov-action@v4
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# flags: autogpt-server,${{ runner.os }}
build:
permissions:
contents: read
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
platform-os: [ubuntu, macos, macos-arm64, windows]
runs-on: ${{ matrix.platform-os != 'macos-arm64' && format('{0}-latest', matrix.platform-os) || 'macos-14' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- id: get_date
name: Get date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Set up Python dependency cache
# On Windows, unpacking cached dependencies takes longer than just installing them
if: runner.os != 'Windows'
uses: actions/cache@v4
with:
path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }}
key: poetry-${{ runner.os }}-${{ hashFiles('rnd/autogpt_server/poetry.lock') }}
- name: Install Poetry (Unix)
if: runner.os != 'Windows'
run: |
curl -sSL https://install.python-poetry.org | python3 -
if [ "${{ runner.os }}" = "macOS" ]; then
PATH="$HOME/.local/bin:$PATH"
echo "$HOME/.local/bin" >> $GITHUB_PATH
fi
- name: Install Poetry (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
$env:PATH += ";$env:APPDATA\Python\Scripts"
echo "$env:APPDATA\Python\Scripts" >> $env:GITHUB_PATH
- name: Install Python dependencies
run: poetry install
- name: Generate Prisma Client
run: poetry run prisma generate
- name: Run Database Migrations
run: poetry run prisma migrate dev --name updates
- name: install rpm
if: matrix.platform-os == 'ubuntu'
run: sudo apt-get install -y alien fakeroot rpm
- name: Build distribution
run: |
case "${{ matrix.platform-os }}" in
"macos" | "macos-arm64")
${MAC_COMMAND}
;;
"windows")
${WINDOWS_COMMAND}
;;
*)
${LINUX_COMMAND}
;;
esac
env:
MAC_COMMAND: "poetry run poe dist_dmg"
WINDOWS_COMMAND: "poetry run poe dist_msi"
LINUX_COMMAND: "poetry run poe dist_appimage"
# break this into seperate steps each with their own name that matches the file
- name: Upload App artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-app-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.app
- name: Upload dmg artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-dmg-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/AutoGPTServer.dmg
- name: Upload msi artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-msi-${{ matrix.platform-os }}
path: D:\a\AutoGPT\AutoGPT\rnd\autogpt_server\dist\*.msi
- name: Upload deb artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-deb-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.deb
- name: Upload rpm artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-rpm-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.rpm
- name: Upload tar.gz artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-tar.gz-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.tar.gz
- name: Upload zip artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-zip-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.zip
- name: Upload pkg artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-pkg-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.pkg
- name: Upload AppImage artifact
uses: actions/upload-artifact@v4
with:
name: autogptserver-AppImage-${{ matrix.platform-os }}
path: /Users/runner/work/AutoGPT/AutoGPT/rnd/autogpt_server/build/*.AppImage

View File

@@ -1,97 +0,0 @@
name: AutoGPTs Nightly Benchmark
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *'
jobs:
benchmark:
permissions:
contents: write
runs-on: ubuntu-latest
strategy:
matrix:
agent-name: [ autogpt ]
fail-fast: false
timeout-minutes: 120
env:
min-python-version: '3.10'
REPORTS_BRANCH: data/benchmark-reports
REPORTS_FOLDER: ${{ format('benchmark/reports/{0}', matrix.agent-name) }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Set up Python ${{ env.min-python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.min-python-version }}
- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python -
- name: Prepare reports folder
run: mkdir -p ${{ env.REPORTS_FOLDER }}
- run: poetry -C benchmark install
- name: Benchmark ${{ matrix.agent-name }}
run: |
./run agent start ${{ matrix.agent-name }}
cd ${{ matrix.agent-name }}
set +e # Do not quit on non-zero exit codes
poetry run agbenchmark run -N 3 \
--test=ReadFile \
--test=BasicRetrieval --test=RevenueRetrieval2 \
--test=CombineCsv --test=LabelCsv --test=AnswerQuestionCombineCsv \
--test=UrlShortener --test=TicTacToe --test=Battleship \
--test=WebArenaTask_0 --test=WebArenaTask_21 --test=WebArenaTask_124 \
--test=WebArenaTask_134 --test=WebArenaTask_163
# Convert exit code 1 (some challenges failed) to exit code 0
if [ $? -eq 0 ] || [ $? -eq 1 ]; then
exit 0
else
exit $?
fi
env:
AGENT_NAME: ${{ matrix.agent-name }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
REQUESTS_CA_BUNDLE: /etc/ssl/certs/ca-certificates.crt
REPORTS_FOLDER: ${{ format('../../{0}', env.REPORTS_FOLDER) }} # account for changed workdir
TELEMETRY_ENVIRONMENT: autogpt-benchmark-ci
TELEMETRY_OPT_IN: ${{ github.ref_name == 'master' }}
- name: Push reports to data branch
run: |
# BODGE: Remove success_rate.json and regression_tests.json to avoid conflicts on checkout
rm ${{ env.REPORTS_FOLDER }}/*.json
# Find folder with newest (untracked) report in it
report_subfolder=$(find ${{ env.REPORTS_FOLDER }} -type f -name 'report.json' \
| xargs -I {} dirname {} \
| xargs -I {} git ls-files --others --exclude-standard {} \
| xargs -I {} dirname {} \
| sort -u)
json_report_file="$report_subfolder/report.json"
# Convert JSON report to Markdown
markdown_report_file="$report_subfolder/report.md"
poetry -C benchmark run benchmark/reports/format.py "$json_report_file" > "$markdown_report_file"
cat "$markdown_report_file" >> $GITHUB_STEP_SUMMARY
git config --global user.name 'GitHub Actions'
git config --global user.email 'github-actions@agpt.co'
git fetch origin ${{ env.REPORTS_BRANCH }}:${{ env.REPORTS_BRANCH }} \
&& git checkout ${{ env.REPORTS_BRANCH }} \
|| git checkout --orphan ${{ env.REPORTS_BRANCH }}
git reset --hard
git add ${{ env.REPORTS_FOLDER }}
git commit -m "Benchmark report for ${{ matrix.agent-name }} @ $(date +'%Y-%m-%d')" \
&& git push origin ${{ env.REPORTS_BRANCH }}

View File

@@ -1,25 +1,25 @@
name: Forge CI
name: Classic - AutoGPT CI
on:
push:
branches: [ master, development, ci-test* ]
paths:
- '.github/workflows/forge-ci.yml'
- 'forge/**'
- '.github/workflows/classic-autogpt-ci.yml'
- 'classic/original_autogpt/**'
pull_request:
branches: [ master, development, release-* ]
paths:
- '.github/workflows/forge-ci.yml'
- 'forge/**'
- '.github/workflows/classic-autogpt-ci.yml'
- 'classic/original_autogpt/**'
concurrency:
group: ${{ format('forge-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
group: ${{ format('classic-autogpt-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }}
defaults:
run:
shell: bash
working-directory: forge
working-directory: classic/original_autogpt
jobs:
test:
@@ -66,18 +66,27 @@ jobs:
fetch-depth: 0
submodules: true
- name: Configure git user Auto-GPT-Bot
run: |
git config --global user.name "Auto-GPT-Bot"
git config --global user.email "github-bot@agpt.co"
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- id: get_date
name: Get date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Set up Python dependency cache
# On Windows, unpacking cached dependencies takes longer than just installing them
if: runner.os != 'Windows'
uses: actions/cache@v4
with:
path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }}
key: poetry-${{ runner.os }}-${{ hashFiles('forge/poetry.lock') }}
key: poetry-${{ runner.os }}-${{ hashFiles('classic/original_autogpt/poetry.lock') }}
- name: Install Poetry (Unix)
if: runner.os != 'Windows'
@@ -104,9 +113,9 @@ jobs:
- name: Run pytest with coverage
run: |
poetry run pytest -vv \
--cov=forge --cov-branch --cov-report term-missing --cov-report xml \
--durations=10 \
forge
--cov=autogpt --cov-branch --cov-report term-missing --cov-report xml \
--numprocesses=logical --durations=10 \
tests/unit tests/integration
env:
CI: true
PLAIN_OUTPUT: True
@@ -119,11 +128,11 @@ jobs:
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: forge,${{ runner.os }}
flags: autogpt-agent,${{ runner.os }}
- name: Upload logs to artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: test-logs
path: forge/logs/
path: classic/original_autogpt/logs/

View File

@@ -1,4 +1,4 @@
name: Purge Auto-GPT Docker CI cache
name: Classic - Purge Auto-GPT Docker CI cache
on:
schedule:
@@ -25,7 +25,8 @@ jobs:
name: Build image
uses: docker/build-push-action@v5
with:
file: Dockerfile.autogpt
context: classic/
file: classic/Dockerfile.autogpt
build-args: BUILD_TYPE=${{ matrix.build-type }}
load: true # save to docker images
# use GHA cache as read-only

View File

@@ -1,26 +1,26 @@
name: AutoGPT Docker CI
name: Classic - AutoGPT Docker CI
on:
push:
branches: [ master, development ]
paths:
- '.github/workflows/autogpt-docker-ci.yml'
- 'autogpt/**'
- '!autogpt/tests/vcr_cassettes'
- '.github/workflows/classic-autogpt-docker-ci.yml'
- 'classic/original_autogpt/**'
- 'classic/forge/**'
pull_request:
branches: [ master, development, release-* ]
paths:
- '.github/workflows/autogpt-docker-ci.yml'
- 'autogpt/**'
- '!autogpt/tests/vcr_cassettes'
- '.github/workflows/classic-autogpt-docker-ci.yml'
- 'classic/original_autogpt/**'
- 'classic/forge/**'
concurrency:
group: ${{ format('autogpt-docker-ci-{0}', github.head_ref && format('pr-{0}', github.event.pull_request.number) || github.sha) }}
group: ${{ format('classic-autogpt-docker-ci-{0}', github.head_ref && format('pr-{0}', github.event.pull_request.number) || github.sha) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
defaults:
run:
working-directory: autogpt
working-directory: classic/original_autogpt
env:
IMAGE_NAME: auto-gpt
@@ -49,7 +49,8 @@ jobs:
name: Build image
uses: docker/build-push-action@v5
with:
file: Dockerfile.autogpt
context: classic/
file: classic/Dockerfile.autogpt
build-args: BUILD_TYPE=${{ matrix.build-type }}
tags: ${{ env.IMAGE_NAME }}
labels: GIT_REVISION=${{ github.sha }}
@@ -118,7 +119,8 @@ jobs:
name: Build image
uses: docker/build-push-action@v5
with:
file: Dockerfile.autogpt
context: classic/
file: classic/Dockerfile.autogpt
build-args: BUILD_TYPE=dev # include pytest
tags: >
${{ env.IMAGE_NAME }},

View File

@@ -1,4 +1,4 @@
name: AutoGPT Docker Release
name: Classic - AutoGPT Docker Release
on:
release:
@@ -44,6 +44,7 @@ jobs:
name: Build image
uses: docker/build-push-action@v5
with:
context: classic/
file: Dockerfile.autogpt
build-args: BUILD_TYPE=release
load: true # save to docker images

View File

@@ -1,4 +1,4 @@
name: Agent smoke tests
name: Classic - Agent smoke tests
on:
workflow_dispatch:
@@ -7,32 +7,37 @@ on:
push:
branches: [ master, development, ci-test* ]
paths:
- '.github/workflows/autogpts-ci.yml'
- 'autogpt/**'
- 'forge/**'
- 'benchmark/**'
- 'run'
- 'cli.py'
- 'setup.py'
- '.github/workflows/classic-autogpts-ci.yml'
- 'classic/original_autogpt/**'
- 'classic/forge/**'
- 'classic/benchmark/**'
- 'classic/run'
- 'classic/cli.py'
- 'classic/setup.py'
- '!**/*.md'
pull_request:
branches: [ master, development, release-* ]
paths:
- '.github/workflows/autogpts-ci.yml'
- 'autogpt/**'
- 'forge/**'
- 'benchmark/**'
- 'run'
- 'cli.py'
- 'setup.py'
- '.github/workflows/classic-autogpts-ci.yml'
- 'classic/original_autogpt/**'
- 'classic/forge/**'
- 'classic/benchmark/**'
- 'classic/run'
- 'classic/cli.py'
- 'classic/setup.py'
- '!**/*.md'
defaults:
run:
shell: bash
working-directory: classic
jobs:
serve-agent-protocol:
runs-on: ubuntu-latest
strategy:
matrix:
agent-name: [ autogpt ]
agent-name: [ original_autogpt ]
fail-fast: false
timeout-minutes: 20
env:
@@ -50,7 +55,7 @@ jobs:
python-version: ${{ env.min-python-version }}
- name: Install Poetry
working-directory: ./${{ matrix.agent-name }}/
working-directory: ./classic/${{ matrix.agent-name }}/
run: |
curl -sSL https://install.python-poetry.org | python -

View File

@@ -1,18 +1,18 @@
name: AGBenchmark CI
name: Classic - AGBenchmark CI
on:
push:
branches: [ master, development, ci-test* ]
paths:
- 'benchmark/**'
- .github/workflows/benchmark-ci.yml
- '!benchmark/reports/**'
- 'classic/benchmark/**'
- '!classic/benchmark/reports/**'
- .github/workflows/classic-benchmark-ci.yml
pull_request:
branches: [ master, development, release-* ]
paths:
- 'benchmark/**'
- '!benchmark/reports/**'
- .github/workflows/benchmark-ci.yml
- 'classic/benchmark/**'
- '!classic/benchmark/reports/**'
- .github/workflows/classic-benchmark-ci.yml
concurrency:
group: ${{ format('benchmark-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
@@ -39,7 +39,7 @@ jobs:
defaults:
run:
shell: bash
working-directory: benchmark
working-directory: classic/benchmark
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -58,7 +58,7 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }}
key: poetry-${{ runner.os }}-${{ hashFiles('benchmark/poetry.lock') }}
key: poetry-${{ runner.os }}-${{ hashFiles('classic/benchmark/poetry.lock') }}
- name: Install Poetry (Unix)
if: runner.os != 'Windows'
@@ -122,7 +122,7 @@ jobs:
curl -sSL https://install.python-poetry.org | python -
- name: Run regression tests
working-directory: .
working-directory: classic
run: |
./run agent start ${{ matrix.agent-name }}
cd ${{ matrix.agent-name }}
@@ -155,7 +155,7 @@ jobs:
poetry run agbenchmark --mock
CHANGED=$(git diff --name-only | grep -E '(agbenchmark/challenges)|(../frontend/assets)') || echo "No diffs"
CHANGED=$(git diff --name-only | grep -E '(agclassic/benchmark/challenges)|(../classic/frontend/assets)') || echo "No diffs"
if [ ! -z "$CHANGED" ]; then
echo "There are unstaged changes please run agbenchmark and commit those changes since they are needed."
echo "$CHANGED"

View File

@@ -1,4 +1,4 @@
name: Publish to PyPI
name: Classic - Publish to PyPI
on:
workflow_dispatch:
@@ -21,21 +21,21 @@ jobs:
python-version: 3.8
- name: Install Poetry
working-directory: ./benchmark/
working-directory: ./classic/benchmark/
run: |
curl -sSL https://install.python-poetry.org | python3 -
echo "$HOME/.poetry/bin" >> $GITHUB_PATH
- name: Build project for distribution
working-directory: ./benchmark/
working-directory: ./classic/benchmark/
run: poetry build
- name: Install dependencies
working-directory: ./benchmark/
working-directory: ./classic/benchmark/
run: poetry install
- name: Check Version
working-directory: ./benchmark/
working-directory: ./classic/benchmark/
id: check-version
run: |
echo version=$(poetry version --short) >> $GITHUB_OUTPUT
@@ -43,7 +43,7 @@ jobs:
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "benchmark/dist/*"
artifacts: "classic/benchmark/dist/*"
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
generateReleaseNotes: false
@@ -51,5 +51,5 @@ jobs:
commit: master
- name: Build and publish
working-directory: ./benchmark/
working-directory: ./classic/benchmark/
run: poetry publish -u __token__ -p ${{ secrets.PYPI_API_TOKEN }}

View File

@@ -1,27 +1,27 @@
name: AutoGPT CI
name: Classic - Forge CI
on:
push:
branches: [ master, development, ci-test* ]
paths:
- '.github/workflows/autogpt-ci.yml'
- 'autogpt/**'
- '!autogpt/tests/vcr_cassettes'
- '.github/workflows/classic-forge-ci.yml'
- 'classic/forge/**'
- '!classic/forge/tests/vcr_cassettes'
pull_request:
branches: [ master, development, release-* ]
paths:
- '.github/workflows/autogpt-ci.yml'
- 'autogpt/**'
- '!autogpt/tests/vcr_cassettes'
- '.github/workflows/classic-forge-ci.yml'
- 'classic/forge/**'
- '!classic/forge/tests/vcr_cassettes'
concurrency:
group: ${{ format('autogpt-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
group: ${{ format('forge-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }}
defaults:
run:
shell: bash
working-directory: autogpt
working-directory: classic/forge
jobs:
test:
@@ -68,11 +68,6 @@ jobs:
fetch-depth: 0
submodules: true
- name: Configure git user Auto-GPT-Bot
run: |
git config --global user.name "Auto-GPT-Bot"
git config --global user.email "github-bot@agpt.co"
- name: Checkout cassettes
if: ${{ startsWith(github.event_name, 'pull_request') }}
env:
@@ -109,17 +104,13 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- id: get_date
name: Get date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Set up Python dependency cache
# On Windows, unpacking cached dependencies takes longer than just installing them
if: runner.os != 'Windows'
uses: actions/cache@v4
with:
path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }}
key: poetry-${{ runner.os }}-${{ hashFiles('autogpt/poetry.lock') }}
key: poetry-${{ runner.os }}-${{ hashFiles('classic/forge/poetry.lock') }}
- name: Install Poetry (Unix)
if: runner.os != 'Windows'
@@ -146,9 +137,9 @@ jobs:
- name: Run pytest with coverage
run: |
poetry run pytest -vv \
--cov=autogpt --cov-branch --cov-report term-missing --cov-report xml \
--numprocesses=logical --durations=10 \
tests/unit tests/integration
--cov=forge --cov-branch --cov-report term-missing --cov-report xml \
--durations=10 \
forge
env:
CI: true
PLAIN_OUTPUT: True
@@ -161,7 +152,7 @@ jobs:
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: autogpt-agent,${{ runner.os }}
flags: forge,${{ runner.os }}
- id: setup_git_auth
name: Set up git token authentication
@@ -242,4 +233,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: test-logs
path: autogpt/logs/
path: classic/forge/logs/

View File

@@ -1,4 +1,4 @@
name: Frontend CI/CD
name: Classic - Frontend CI/CD
on:
push:
@@ -7,11 +7,11 @@ on:
- development
- 'ci-test*' # This will match any branch that starts with "ci-test"
paths:
- 'frontend/**'
- 'classic/frontend/**'
- '.github/workflows/frontend-ci.yml'
pull_request:
paths:
- 'frontend/**'
- 'classic/frontend/**'
- '.github/workflows/frontend-ci.yml'
jobs:
@@ -34,7 +34,7 @@ jobs:
- name: Build Flutter to Web
run: |
cd frontend
cd classic/frontend
flutter build web --base-href /app/
# - name: Commit and Push to ${{ env.BUILD_BRANCH }}
@@ -42,7 +42,7 @@ jobs:
# run: |
# git config --local user.email "action@github.com"
# git config --local user.name "GitHub Action"
# git add frontend/build/web
# git add classic/frontend/build/web
# git checkout -B ${{ env.BUILD_BRANCH }}
# git commit -m "Update frontend build to ${GITHUB_SHA:0:7}" -a
# git push -f origin ${{ env.BUILD_BRANCH }}
@@ -51,7 +51,7 @@ jobs:
if: github.event_name == 'push'
uses: peter-evans/create-pull-request@v6
with:
add-paths: frontend/build/web
add-paths: classic/frontend/build/web
base: ${{ github.ref_name }}
branch: ${{ env.BUILD_BRANCH }}
delete-branch: true

View File

@@ -1,24 +1,24 @@
name: Python checks
name: Classic - Python checks
on:
push:
branches: [ master, development, ci-test* ]
paths:
- '.github/workflows/lint-ci.yml'
- 'autogpt/**'
- 'forge/**'
- 'benchmark/**'
- 'classic/original_autogpt/**'
- 'classic/forge/**'
- 'classic/benchmark/**'
- '**.py'
- '!autogpt/tests/vcr_cassettes'
- '!classic/forge/tests/vcr_cassettes'
pull_request:
branches: [ master, development, release-* ]
paths:
- '.github/workflows/lint-ci.yml'
- 'autogpt/**'
- 'forge/**'
- 'benchmark/**'
- 'classic/original_autogpt/**'
- 'classic/forge/**'
- 'classic/benchmark/**'
- '**.py'
- '!autogpt/tests/vcr_cassettes'
- '!classic/forge/tests/vcr_cassettes'
concurrency:
group: ${{ format('lint-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
@@ -40,18 +40,18 @@ jobs:
uses: dorny/paths-filter@v3
with:
filters: |
autogpt:
- autogpt/autogpt/**
- autogpt/tests/**
- autogpt/poetry.lock
original_autogpt:
- classic/original_autogpt/autogpt/**
- classic/original_autogpt/tests/**
- classic/original_autogpt/poetry.lock
forge:
- forge/forge/**
- forge/tests/**
- forge/poetry.lock
- classic/forge/forge/**
- classic/forge/tests/**
- classic/forge/poetry.lock
benchmark:
- benchmark/agbenchmark/**
- benchmark/tests/**
- benchmark/poetry.lock
- classic/benchmark/agbenchmark/**
- classic/benchmark/tests/**
- classic/benchmark/poetry.lock
outputs:
changed-parts: ${{ steps.changes-in.outputs.changes }}
@@ -89,23 +89,23 @@ jobs:
# Install dependencies
- name: Install Python dependencies
run: poetry -C ${{ matrix.sub-package }} install
run: poetry -C classic/${{ matrix.sub-package }} install
# Lint
- name: Lint (isort)
run: poetry run isort --check .
working-directory: ${{ matrix.sub-package }}
working-directory: classic/${{ matrix.sub-package }}
- name: Lint (Black)
if: success() || failure()
run: poetry run black --check .
working-directory: ${{ matrix.sub-package }}
working-directory: classic/${{ matrix.sub-package }}
- name: Lint (Flake8)
if: success() || failure()
run: poetry run flake8 .
working-directory: ${{ matrix.sub-package }}
working-directory: classic/${{ matrix.sub-package }}
types:
needs: get-changed-parts
@@ -141,11 +141,11 @@ jobs:
# Install dependencies
- name: Install Python dependencies
run: poetry -C ${{ matrix.sub-package }} install
run: poetry -C classic/${{ matrix.sub-package }} install
# Typecheck
- name: Typecheck
if: success() || failure()
run: poetry run pyright
working-directory: ${{ matrix.sub-package }}
working-directory: classic/${{ matrix.sub-package }}

View File

@@ -1,133 +0,0 @@
name: Hackathon
on:
workflow_dispatch:
inputs:
agents:
description: "Agents to run (comma-separated)"
required: false
default: "autogpt" # Default agents if none are specified
jobs:
matrix-setup:
runs-on: ubuntu-latest
# Service containers to run with `matrix-setup`
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres
# Provide the password for postgres
env:
POSTGRES_PASSWORD: postgres
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
env-name: ${{ steps.set-matrix.outputs.env-name }}
steps:
- id: set-matrix
run: |
if [ "${{ github.event_name }}" == "schedule" ]; then
echo "::set-output name=env-name::production"
echo "::set-output name=matrix::[ 'irrelevant']"
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
IFS=',' read -ra matrix_array <<< "${{ github.event.inputs.agents }}"
matrix_string="[ \"$(echo "${matrix_array[@]}" | sed 's/ /", "/g')\" ]"
echo "::set-output name=env-name::production"
echo "::set-output name=matrix::$matrix_string"
else
echo "::set-output name=env-name::testing"
echo "::set-output name=matrix::[ 'irrelevant' ]"
fi
tests:
environment:
name: "${{ needs.matrix-setup.outputs.env-name }}"
needs: matrix-setup
env:
min-python-version: "3.10"
name: "${{ matrix.agent-name }}"
runs-on: ubuntu-latest
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres
# Provide the password for postgres
env:
POSTGRES_PASSWORD: postgres
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
timeout-minutes: 50
strategy:
fail-fast: false
matrix:
agent-name: ${{fromJson(needs.matrix-setup.outputs.matrix)}}
steps:
- name: Print Environment Name
run: |
echo "Matrix Setup Environment Name: ${{ needs.matrix-setup.outputs.env-name }}"
- name: Check Docker Container
id: check
run: docker ps
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Set up Python ${{ env.min-python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.min-python-version }}
- id: get_date
name: Get date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Install Poetry
run: |
curl -sSL https://install.python-poetry.org | python -
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: v18.15
- name: Run benchmark
run: |
link=$(jq -r '.["github_repo_url"]' arena/$AGENT_NAME.json)
branch=$(jq -r '.["branch_to_benchmark"]' arena/$AGENT_NAME.json)
git clone "$link" -b "$branch" "$AGENT_NAME"
cd $AGENT_NAME
cp ./$AGENT_NAME/.env.example ./$AGENT_NAME/.env || echo "file not found"
./run agent start $AGENT_NAME
cd ../benchmark
poetry install
poetry run agbenchmark --no-dep
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
SERP_API_KEY: ${{ secrets.SERP_API_KEY }}
SERPAPI_API_KEY: ${{ secrets.SERP_API_KEY }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GOOGLE_CUSTOM_SEARCH_ENGINE_ID: ${{ secrets.GOOGLE_CUSTOM_SEARCH_ENGINE_ID }}
AGENT_NAME: ${{ matrix.agent-name }}

View File

@@ -0,0 +1,41 @@
name: Platform - AutoGPT Builder CI
on:
push:
branches: [ master ]
paths:
- '.github/workflows/autogpt-builder-ci.yml'
- 'autogpt_platform/autogpt_builder/**'
pull_request:
paths:
- '.github/workflows/autogpt-builder-ci.yml'
- 'autogpt_platform/autogpt_builder/**'
defaults:
run:
shell: bash
working-directory: autogpt_platform/autogpt_builder
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '21'
- name: Install dependencies
run: |
npm install
- name: Check formatting with Prettier
run: |
npx prettier --check .
- name: Run lint
run: |
npm run lint

View File

@@ -0,0 +1,56 @@
name: Platform - AutoGPT Builder Infra
on:
push:
branches: [ master ]
paths:
- '.github/workflows/autogpt-infra-ci.yml'
- 'autogpt_platform/infra/**'
pull_request:
paths:
- '.github/workflows/autogpt-infra-ci.yml'
- 'autogpt_platform/infra/**'
defaults:
run:
shell: bash
working-directory: autogpt_platform/infra
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: TFLint
uses: pauloconnor/tflint-action@v0.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tflint_path: terraform/
tflint_recurse: true
tflint_changed_only: false
- name: Set up Helm
uses: azure/setup-helm@v4.2.0
with:
version: v3.14.4
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.6.0
- name: Run chart-testing (list-changed)
id: list-changed
run: |
changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }})
if [[ -n "$changed" ]]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
- name: Run chart-testing (lint)
if: steps.list-changed.outputs.changed == 'true'
run: ct lint --target-branch ${{ github.event.repository.default_branch }}

View File

@@ -0,0 +1,155 @@
name: Platform - AutoGPT Server CI
on:
push:
branches: [master, development, ci-test*]
paths:
- ".github/workflows/autogpt-server-ci.yml"
- "autogpt_platform/autogpt_server/**"
pull_request:
branches: [master, development, release-*]
paths:
- ".github/workflows/autogpt-server-ci.yml"
- "autogpt_platform/autogpt_server/**"
concurrency:
group: ${{ format('autogpt-server-ci-{0}', github.head_ref && format('{0}-{1}', github.event_name, github.event.pull_request.number) || github.sha) }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull_request') }}
defaults:
run:
shell: bash
working-directory: autogpt_platform/autogpt_server
jobs:
test:
permissions:
contents: read
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
python-version: ["3.10"]
platform-os: [ubuntu, macos, macos-arm64, windows]
runs-on: ${{ matrix.platform-os != 'macos-arm64' && format('{0}-latest', matrix.platform-os) || 'macos-14' }}
steps:
- name: Setup PostgreSQL
uses: ikalnytskyi/action-setup-postgres@v6
with:
username: ${{ secrets.DB_USER || 'postgres' }}
password: ${{ secrets.DB_PASS || 'postgres' }}
database: postgres
port: 5432
id: postgres
# Quite slow on macOS (2~4 minutes to set up Docker)
# - name: Set up Docker (macOS)
# if: runner.os == 'macOS'
# uses: crazy-max/ghaction-setup-docker@v3
- name: Start MinIO service (Linux)
if: runner.os == 'Linux'
working-directory: "."
run: |
docker pull minio/minio:edge-cicd
docker run -d -p 9000:9000 minio/minio:edge-cicd
- name: Start MinIO service (macOS)
if: runner.os == 'macOS'
working-directory: ${{ runner.temp }}
run: |
brew install minio/stable/minio
mkdir data
minio server ./data &
# No MinIO on Windows:
# - Windows doesn't support running Linux Docker containers
# - It doesn't seem possible to start background processes on Windows. They are
# killed after the step returns.
# See: https://github.com/actions/runner/issues/598#issuecomment-2011890429
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- id: get_date
name: Get date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Set up Python dependency cache
# On Windows, unpacking cached dependencies takes longer than just installing them
if: runner.os != 'Windows'
uses: actions/cache@v4
with:
path: ${{ runner.os == 'macOS' && '~/Library/Caches/pypoetry' || '~/.cache/pypoetry' }}
key: poetry-${{ runner.os }}-${{ hashFiles('autogpt_platform/autogpt_server/poetry.lock') }}
- name: Install Poetry (Unix)
if: runner.os != 'Windows'
run: |
curl -sSL https://install.python-poetry.org | python3 -
if [ "${{ runner.os }}" = "macOS" ]; then
PATH="$HOME/.local/bin:$PATH"
echo "$HOME/.local/bin" >> $GITHUB_PATH
fi
- name: Install Poetry (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
$env:PATH += ";$env:APPDATA\Python\Scripts"
echo "$env:APPDATA\Python\Scripts" >> $env:GITHUB_PATH
- name: Install Python dependencies
run: poetry install
- name: Generate Prisma Client
run: poetry run prisma generate
- name: Run Database Migrations
run: poetry run prisma migrate dev --name updates
env:
CONNECTION_STR: ${{ steps.postgres.outputs.connection-uri }}
- id: lint
name: Run Linter
run: poetry run lint
- name: Run pytest with coverage
run: |
if [[ "${{ runner.debug }}" == "1" ]]; then
poetry run pytest -vv -o log_cli=true -o log_cli_level=DEBUG test
else
poetry run pytest -vv test
fi
if: success() || (failure() && steps.lint.outcome == 'failure')
env:
LOG_LEVEL: ${{ runner.debug && 'DEBUG' || 'INFO' }}
env:
CI: true
PLAIN_OUTPUT: True
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
DB_USER: ${{ secrets.DB_USER || 'postgres' }}
DB_PASS: ${{ secrets.DB_PASS || 'postgres' }}
DB_NAME: postgres
DB_PORT: 5432
RUN_ENV: local
PORT: 8080
DATABASE_URL: postgresql://${{ secrets.DB_USER || 'postgres' }}:${{ secrets.DB_PASS || 'postgres' }}@localhost:5432/${{ secrets.DB_NAME || 'postgres'}}
# - name: Upload coverage reports to Codecov
# uses: codecov/codecov-action@v4
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# flags: autogpt-server,${{ runner.os }}

View File

@@ -1,4 +1,4 @@
name: 'Close stale issues'
name: Repo - Close stale issues
on:
schedule:
- cron: '30 1 * * *'

View File

@@ -1,12 +1,12 @@
name: "Pull Request auto-label"
name: Repo - Pull Request auto-label
on:
# So that PRs touching the same files as the push are updated
push:
branches: [ master, development, release-* ]
paths-ignore:
- 'autogpt/tests/vcr_cassettes'
- 'benchmark/reports/**'
- 'classic/forge/tests/vcr_cassettes'
- 'classic/benchmark/reports/**'
# So that the `dirtyLabel` is removed if conflicts are resolve
# We recommend `pull_request_target` so that github secrets are available.
# In `pull_request` we wouldn't be able to change labels of fork PRs

View File

@@ -1,4 +1,4 @@
name: github-repo-stats
name: Repo - Github Stats
on:
schedule:

View File

@@ -0,0 +1,31 @@
name: Repo - PR Status Checker
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
status-check:
name: Check PR Status
runs-on: ubuntu-latest
steps:
# - name: Wait some time for all actions to start
# run: sleep 30
- uses: actions/checkout@v4
# with:
# fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Check PR Status
run: |
echo "Current directory before running Python script:"
pwd
echo "Attempting to run Python script:"
python .github/workflows/scripts/check_actions_status.py
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,109 @@
import json
import os
import requests
import sys
import time
from typing import Dict, List, Tuple
def get_environment_variables() -> Tuple[str, str, str, str, str]:
"""Retrieve and return necessary environment variables."""
try:
with open(os.environ["GITHUB_EVENT_PATH"]) as f:
event = json.load(f)
sha = event["pull_request"]["head"]["sha"]
return (
os.environ["GITHUB_API_URL"],
os.environ["GITHUB_REPOSITORY"],
sha,
os.environ["GITHUB_TOKEN"],
os.environ["GITHUB_RUN_ID"],
)
except KeyError as e:
print(f"Error: Missing required environment variable or event data: {e}")
sys.exit(1)
def make_api_request(url: str, headers: Dict[str, str]) -> Dict:
"""Make an API request and return the JSON response."""
try:
print("Making API request to:", url)
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
print(f"Error: API request failed. {e}")
sys.exit(1)
def process_check_runs(check_runs: List[Dict]) -> Tuple[bool, bool]:
"""Process check runs and return their status."""
runs_in_progress = False
all_others_passed = True
for run in check_runs:
if str(run["name"]) != "Check PR Status":
status = run["status"]
conclusion = run["conclusion"]
if status == "completed":
if conclusion not in ["success", "skipped", "neutral"]:
all_others_passed = False
print(
f"Check run {run['name']} (ID: {run['id']}) has conclusion: {conclusion}"
)
else:
runs_in_progress = True
print(f"Check run {run['name']} (ID: {run['id']}) is still {status}.")
all_others_passed = False
else:
print(
f"Skipping check run {run['name']} (ID: {run['id']}) as it is the current run."
)
return runs_in_progress, all_others_passed
def main():
api_url, repo, sha, github_token, current_run_id = get_environment_variables()
endpoint = f"{api_url}/repos/{repo}/commits/{sha}/check-runs"
headers = {
"Accept": "application/vnd.github.v3+json",
}
if github_token:
headers["Authorization"] = f"token {github_token}"
print(f"Current run ID: {current_run_id}")
while True:
data = make_api_request(endpoint, headers)
check_runs = data["check_runs"]
print("Processing check runs...")
print(check_runs)
runs_in_progress, all_others_passed = process_check_runs(check_runs)
if not runs_in_progress:
break
print(
"Some check runs are still in progress. Waiting 3 minutes before checking again..."
)
time.sleep(180)
if all_others_passed:
print("All other completed check runs have passed. This check passes.")
sys.exit(0)
else:
print("Some check runs have failed or have not completed. This check fails.")
sys.exit(1)
if __name__ == "__main__":
main()

11
.gitignore vendored
View File

@@ -1,7 +1,7 @@
## Original ignores
.github_access_token
autogpt/keys.py
autogpt/*.json
classic/original_autogpt/keys.py
classic/original_autogpt/*.json
auto_gpt_workspace/*
*.mpeg
.env
@@ -32,7 +32,6 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
@@ -158,11 +157,11 @@ openai/
CURRENT_BULLETIN.md
# AgBenchmark
agbenchmark/reports/
agclassic/benchmark/reports/
# Nodejs
package-lock.json
package.json
# Allow for locally private items
# private
@@ -170,3 +169,5 @@ pri*
# ignore
ig*
.github_access_token
LICENSE.rtf
autogpt_platform/autogpt_server/settings.py

7
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "autogpt/tests/vcr_cassettes"]
path = autogpt/tests/vcr_cassettes
[submodule "classic/forge/tests/vcr_cassettes"]
path = classic/forge/tests/vcr_cassettes
url = https://github.com/Significant-Gravitas/Auto-GPT-test-cassettes
[submodule "autogpt_platform/supabase"]
path = autogpt_platform/supabase
url = https://github.com/supabase/supabase.git

View File

@@ -16,22 +16,22 @@ repos:
hooks:
- id: isort-autogpt
name: Lint (isort) - AutoGPT
entry: poetry -C autogpt run isort
files: ^autogpt/
entry: poetry -C classic/original_autogpt run isort
files: ^classic/original_autogpt/
types: [file, python]
language: system
- id: isort-forge
name: Lint (isort) - Forge
entry: poetry -C forge run isort
files: ^forge/
entry: poetry -C classic/forge run isort
files: ^classic/forge/
types: [file, python]
language: system
- id: isort-benchmark
name: Lint (isort) - Benchmark
entry: poetry -C benchmark run isort
files: ^benchmark/
entry: poetry -C classic/benchmark run isort
files: ^classic/benchmark/
types: [file, python]
language: system
@@ -52,20 +52,20 @@ repos:
- id: flake8
name: Lint (Flake8) - AutoGPT
alias: flake8-autogpt
files: ^autogpt/(autogpt|scripts|tests)/
args: [--config=autogpt/.flake8]
files: ^classic/original_autogpt/(autogpt|scripts|tests)/
args: [--config=classic/original_autogpt/.flake8]
- id: flake8
name: Lint (Flake8) - Forge
alias: flake8-forge
files: ^forge/(forge|tests)/
args: [--config=forge/.flake8]
files: ^classic/forge/(forge|tests)/
args: [--config=classic/forge/.flake8]
- id: flake8
name: Lint (Flake8) - Benchmark
alias: flake8-benchmark
files: ^benchmark/(agbenchmark|tests)/((?!reports).)*[/.]
args: [--config=benchmark/.flake8]
files: ^classic/benchmark/(agbenchmark|tests)/((?!reports).)*[/.]
args: [--config=classic/benchmark/.flake8]
- repo: local
# To have watertight type checking, we check *all* the files in an affected
@@ -74,10 +74,10 @@ repos:
- id: pyright
name: Typecheck - AutoGPT
alias: pyright-autogpt
entry: poetry -C autogpt run pyright
entry: poetry -C classic/original_autogpt run pyright
args: [-p, autogpt, autogpt]
# include forge source (since it's a path dependency) but exclude *_test.py files:
files: ^(autogpt/((autogpt|scripts|tests)/|poetry\.lock$)|forge/(forge/.*(?<!_test)\.py|poetry\.lock)$)
files: ^(classic/original_autogpt/((autogpt|scripts|tests)/|poetry\.lock$)|classic/forge/(classic/forge/.*(?<!_test)\.py|poetry\.lock)$)
types: [file]
language: system
pass_filenames: false
@@ -85,9 +85,9 @@ repos:
- id: pyright
name: Typecheck - Forge
alias: pyright-forge
entry: poetry -C forge run pyright
entry: poetry -C classic/forge run pyright
args: [-p, forge, forge]
files: ^forge/(forge/|poetry\.lock$)
files: ^classic/forge/(classic/forge/|poetry\.lock$)
types: [file]
language: system
pass_filenames: false
@@ -95,9 +95,9 @@ repos:
- id: pyright
name: Typecheck - Benchmark
alias: pyright-benchmark
entry: poetry -C benchmark run pyright
entry: poetry -C classic/benchmark run pyright
args: [-p, benchmark, benchmark]
files: ^benchmark/(agbenchmark|tests)/
files: ^classic/benchmark/(agclassic/benchmark/|tests/|poetry\.lock$)
types: [file]
language: system
pass_filenames: false
@@ -106,22 +106,22 @@ repos:
hooks:
- id: pytest-autogpt
name: Run tests - AutoGPT (excl. slow tests)
entry: bash -c 'cd autogpt && poetry run pytest --cov=autogpt -m "not slow" tests/unit tests/integration'
entry: bash -c 'cd classic/original_autogpt && poetry run pytest --cov=autogpt -m "not slow" tests/unit tests/integration'
# include forge source (since it's a path dependency) but exclude *_test.py files:
files: ^(autogpt/((autogpt|tests)/|poetry\.lock$)|forge/(forge/.*(?<!_test)\.py|poetry\.lock)$)
files: ^(classic/original_autogpt/((autogpt|tests)/|poetry\.lock$)|classic/forge/(classic/forge/.*(?<!_test)\.py|poetry\.lock)$)
language: system
pass_filenames: false
- id: pytest-forge
name: Run tests - Forge (excl. slow tests)
entry: bash -c 'cd forge && poetry run pytest --cov=forge -m "not slow"'
files: ^forge/(forge/|tests/|poetry\.lock$)
entry: bash -c 'cd classic/forge && poetry run pytest --cov=forge -m "not slow"'
files: ^classic/forge/(classic/forge/|tests/|poetry\.lock$)
language: system
pass_filenames: false
- id: pytest-benchmark
name: Run tests - Benchmark
entry: bash -c 'cd benchmark && poetry run pytest --cov=benchmark'
files: ^benchmark/(agbenchmark/|tests/|poetry\.lock$)
entry: bash -c 'cd classic/benchmark && poetry run pytest --cov=benchmark'
files: ^classic/benchmark/(agclassic/benchmark/|tests/|poetry\.lock$)
language: system
pass_filenames: false

61
.vscode/all-projects.code-workspace vendored Normal file
View File

@@ -0,0 +1,61 @@
{
"folders": [
{
"name": "autogpt_server",
"path": "../autogpt_platform/autogpt_server"
},
{
"name": "autogpt_builder",
"path": "../autogpt_platform/autogpt_builder"
},
{
"name": "market",
"path": "../autogpt_platform/market"
},
{
"name": "lib",
"path": "../autogpt_platform/autogpt_libs"
},
{
"name": "infra",
"path": "../autogpt_platform/infra"
},
{
"name": "docs",
"path": "../docs"
},
{
"name": "[root]",
"path": ".."
},
{
"name": "classic - autogpt",
"path": "../classic/original_autogpt"
},
{
"name": "classic - benchmark",
"path": "../classic/benchmark"
},
{
"name": "classic - forge",
"path": "../classic/forge"
},
{
"name": "classic - frontend",
"path": "../classic/frontend"
},
],
"settings": {
"python.analysis.typeCheckingMode": "basic"
},
"extensions": {
"recommendations": [
"charliermarsh.ruff",
"dart-code.flutter",
"ms-python.black-formatter",
"ms-python.vscode-pylance",
"prisma.prisma",
"qwtel.sqlite-viewer"
]
}
}

View File

@@ -1,17 +1,43 @@
# AutoGPT: build & use AI agents
# AutoGPT: Build & Use AI Agents
[![Discord Follow](https://dcbadge.vercel.app/api/server/autogpt?style=flat)](https://discord.gg/autogpt) &ensp;
[![Twitter Follow](https://img.shields.io/twitter/follow/Auto_GPT?style=social)](https://twitter.com/Auto_GPT) &ensp;
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
**AutoGPT** is a generalist LLM based AI agent that can autonomously accomplish minor tasks.
**AutoGPT** is a powerful tool that lets you create and run intelligent agents. These agents can perform various tasks automatically, making your life easier.
**Examples**:
## How to Get Started
- Look up and summarize this research paper
- Write a marketing for food supplements
- Write a blog post detailing the news in AI
https://github.com/user-attachments/assets/8508f4dc-b362-4cab-900f-644964a96cdf
### 🧱 AutoGPT Builder
The AutoGPT Builder is the frontend. It allows you to design agents using an easy flowchart style. You build your agent by connecting blocks, where each block performs a single action. It's simple and intuitive!
[Read this guide](https://docs.agpt.co/server/new_blocks/) to learn how to build your own custom blocks.
### 💽 AutoGPT Server
The AutoGPT Server is the backend. This is where your agents run. Once deployed, agents can be triggered by external sources and can operate continuously.
### 🐙 Example Agents
Here are two examples of what you can do with AutoGPT:
1. **Reddit Marketing Agent**
- This agent reads comments on Reddit.
- It looks for people asking about your product.
- It then automatically responds to them.
2. **YouTube Content Repurposing Agent**
- This agent subscribes to your YouTube channel.
- When you post a new video, it transcribes it.
- It uses AI to write a search engine optimized blog post.
- Then, it publishes this blog post to your Medium account.
These examples show just a glimpse of what you can achieve with AutoGPT!
---
Our mission is to provide the tools, so that you can focus on what matters:
- 🏗️ **Building** - Lay the foundation for something amazing.
@@ -23,16 +49,18 @@ Be part of the revolution! **AutoGPT** is here to stay, at the forefront of AI i
**📖 [Documentation](https://docs.agpt.co)**
&ensp;|&ensp;
**🚀 [Contributing](CONTRIBUTING.md)**
&ensp;|&ensp;
**🛠️ [Build your own Agent - Quickstart](QUICKSTART.md)**
## 🧱 Building blocks
---
## 🤖 AutoGPT Classic
> Below is information about the classic version of AutoGPT.
**🛠️ [Build your own Agent - Quickstart](FORGE-QUICKSTART.md)**
### 🏗️ Forge
**Forge your own agent!** &ndash; Forge is a ready-to-go template for your agent application. All the boilerplate code is already handled, letting you channel all your creativity into the things that set *your* agent apart. All tutorials are located [here](https://medium.com/@aiedge/autogpt-forge-e3de53cc58ec). Components from the [`forge.sdk`](/forge/forge/sdk) can also be used individually to speed up development and reduce boilerplate in your agent project.
**Forge your own agent!** &ndash; Forge is a ready-to-go template for your agent application. All the boilerplate code is already handled, letting you channel all your creativity into the things that set *your* agent apart. All tutorials are located [here](https://medium.com/@aiedge/autogpt-forge-e3de53cc58ec). Components from the [`forge.sdk`](/forge/sdk) can also be used individually to speed up development and reduce boilerplate in your agent project.
🚀 [**Getting Started with Forge**](https://github.com/Significant-Gravitas/AutoGPT/blob/master/forge/tutorials/001_getting_started.md) &ndash;
🚀 [**Getting Started with Forge**](https://github.com/Significant-Gravitas/AutoGPT/blob/master/classic/forge/tutorials/001_getting_started.md) &ndash;
This guide will walk you through the process of creating your own agent and using the benchmark and user interface.
📘 [Learn More](https://github.com/Significant-Gravitas/AutoGPT/tree/master/forge) about Forge
@@ -43,7 +71,7 @@ This guide will walk you through the process of creating your own agent and usin
<!-- TODO: insert visual demonstrating the benchmark -->
📦 [`agbenchmark`](https://pypi.org/project/agbenchmark/) on Pypi
📦 [`agbenchmark`](https://pypi.org/project/agclassic/benchmark/) on Pypi
&ensp;|&ensp;
📘 [Learn More](https://github.com/Significant-Gravitas/AutoGPT/blob/master/benchmark) about the Benchmark

BIN
assets/gpt_dark_RGB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -1,5 +0,0 @@
# Exclude VCR cassettes from stats
tests/vcr_cassettes/**/**.y*ml linguist-generated
# Mark documentation as such
docs/**.md linguist-documentation

View File

@@ -1,60 +0,0 @@
from pkgutil import iter_modules
from shutil import which
from cx_Freeze import Executable, setup
packages = [
m.name
for m in iter_modules()
if m.ispkg
and m.module_finder
and ("poetry" in m.module_finder.path) # type: ignore
]
icon = (
"../../assets/gpt_dark_RGB.icns"
if which("sips")
else "../../assets/gpt_dark_RGB.ico"
)
setup(
executables=[
Executable(
"autogpt/__main__.py", target_name="autogpt", base="console", icon=icon
),
],
options={
"build_exe": {
"packages": packages,
"includes": [
"autogpt",
"spacy",
"spacy.lang",
"spacy.vocab",
"spacy.lang.lex_attrs",
"uvicorn.loops.auto",
"srsly.msgpack.util",
"blis",
"uvicorn.protocols.http.auto",
"uvicorn.protocols.websockets.auto",
"uvicorn.lifespan.on",
],
"excludes": ["readability.compat.two"],
},
"bdist_mac": {
"bundle_name": "AutoGPT",
"iconfile": "../assets/gpt_dark_RGB.icns",
"include_resources": [""],
},
"bdist_dmg": {
"applications_shortcut": True,
"volume_label": "AutoGPT",
},
"bdist_msi": {
"target_name": "AutoGPT",
"add_to_path": True,
"install_icon": "../assets/gpt_dark_RGB.ico",
},
},
)

133
autogpt_platform/README.md Normal file
View File

@@ -0,0 +1,133 @@
# AutoGPT Platform
Welcome to the AutoGPT Platform - a powerful system for creating and running AI agents to solve business problems. This platform enables you to harness the power of artificial intelligence to automate tasks, analyze data, and generate insights for your organization.
## Getting Started
### Prerequisites
- Docker
- Docker Compose V2 (comes with Docker Desktop, or can be installed separately)
### Running the System
To run the AutoGPT Platform, follow these steps:
1. Clone this repository to your local machine.
2. Navigate to autogpt_platform/supabase
3. Run the following command:
```
git submodule update --init --recursive
```
4. Navigate back to rnd (cd ..)
5. Run the following command:
```
cp supabase/docker/.env.example .env
```
6. Run the following command:
```
docker compose -f docker-compose.combined.yml up -d
```
This command will start all the necessary backend services defined in the `docker-compose.combined.yml` file in detached mode.
7. Navigate to autogpt_platform/autogpt_builder.
8. Run the following command:
```
cp .env.example .env.local
```
9. Run the following command:
```
yarn dev
```
### Docker Compose Commands
Here are some useful Docker Compose commands for managing your AutoGPT Platform:
- `docker compose -f docker-compose.combined.yml up -d`: Start the services in detached mode.
- `docker compose -f docker-compose.combined.yml stop`: Stop the running services without removing them.
- `docker compose rm`: Remove stopped service containers.
- `docker compose build`: Build or rebuild services.
- `docker compose down`: Stop and remove containers, networks, and volumes.
- `docker compose watch`: Watch for changes in your services and automatically update them.
### Sample Scenarios
Here are some common scenarios where you might use multiple Docker Compose commands:
1. Updating and restarting a specific service:
```
docker compose build api_srv
docker compose up -d --no-deps api_srv
```
This rebuilds the `api_srv` service and restarts it without affecting other services.
2. Viewing logs for troubleshooting:
```
docker compose logs -f api_srv ws_srv
```
This shows and follows the logs for both `api_srv` and `ws_srv` services.
3. Scaling a service for increased load:
```
docker compose up -d --scale executor=3
```
This scales the `executor` service to 3 instances to handle increased load.
4. Stopping the entire system for maintenance:
```
docker compose stop
docker compose rm -f
docker compose pull
docker compose up -d
```
This stops all services, removes containers, pulls the latest images, and restarts the system.
5. Developing with live updates:
```
docker compose watch
```
This watches for changes in your code and automatically updates the relevant services.
6. Checking the status of services:
```
docker compose ps
```
This shows the current status of all services defined in your docker-compose.yml file.
These scenarios demonstrate how to use Docker Compose commands in combination to manage your AutoGPT Platform effectively.
### Persisting Data
To persist data for PostgreSQL and Redis, you can modify the `docker-compose.yml` file to add volumes. Here's how:
1. Open the `docker-compose.yml` file in a text editor.
2. Add volume configurations for PostgreSQL and Redis services:
```yaml
services:
postgres:
# ... other configurations ...
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
# ... other configurations ...
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
```
3. Save the file and run `docker compose up -d` to apply the changes.
This configuration will create named volumes for PostgreSQL and Redis, ensuring that your data persists across container restarts.

View File

@@ -0,0 +1,15 @@
NEXT_PUBLIC_AUTH_CALLBACK_URL=http://localhost:8006/auth/callback
NEXT_PUBLIC_AGPT_SERVER_URL=http://localhost:8006/api
NEXT_PUBLIC_AGPT_WS_SERVER_URL=ws://localhost:8001/ws
NEXT_PUBLIC_AGPT_MARKETPLACE_URL=http://localhost:8015/api/v1/market
## Supabase credentials
NEXT_PUBLIC_SUPABASE_URL=http://localhost:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE
## OAuth Callback URL
## This should be {domain}/auth/callback
## Only used if you're using Supabase and OAuth
AUTH_CALLBACK_URL=http://localhost:3000/auth/callback
GA_MEASUREMENT_ID=G-FH2XK2W4GN

View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -0,0 +1,39 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# Sentry Config File
.env.sentry-build-plugin

View File

@@ -0,0 +1,4 @@
node_modules
.next
build
public

View File

@@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}

View File

@@ -0,0 +1,32 @@
# Base stage for both dev and prod
FROM node:21-alpine AS base
WORKDIR /app
COPY autogpt_platform/autogpt_builder/package.json autogpt_platform/autogpt_builder/yarn.lock ./
RUN yarn install --frozen-lockfile
# Dev stage
FROM base AS dev
ENV NODE_ENV=development
COPY autogpt_platform/autogpt_builder/ .
EXPOSE 3000
CMD ["yarn", "run", "dev"]
# Build stage for prod
FROM base AS build
COPY autogpt_platform/autogpt_builder/ .
RUN npm run build
# Prod stage
FROM node:21-alpine AS prod
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/package.json /app/yarn.lock ./
RUN yarn install --frozen-lockfile
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public
COPY --from=build /app/next.config.mjs ./next.config.mjs
EXPOSE 3000
CMD ["npm", "start"]

View File

@@ -0,0 +1,41 @@
This is the frontend for AutoGPT's next generation
## Getting Started
Run the following installation once.
```bash
npm install
# or
yarn install
# or
pnpm install
# or
bun install
```
Next, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
For subsequent runs, you do not have to `npm install` again. Simply do `npm run dev`.
If the project is updated via git, you will need to `npm install` after each update.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Deploy
TODO

View File

@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}

View File

@@ -0,0 +1,84 @@
import { withSentryConfig } from "@sentry/nextjs";
import dotenv from "dotenv";
// Load environment variables
dotenv.config();
/** @type {import('next').NextConfig} */
const nextConfig = {
env: {
NEXT_PUBLIC_AGPT_SERVER_URL: process.env.NEXT_PUBLIC_AGPT_SERVER_URL,
NEXT_PUBLIC_AGPT_MARKETPLACE_URL:
process.env.NEXT_PUBLIC_AGPT_MARKETPLACE_URL,
},
images: {
domains: ["images.unsplash.com"],
},
async redirects() {
return [
{
source: "/monitor", // FIXME: Remove after 2024-09-01
destination: "/",
permanent: false,
},
];
},
// TODO: Re-enable TypeScript checks once current issues are resolved
typescript: {
ignoreBuildErrors: true,
},
};
export default withSentryConfig(nextConfig, {
// For all available options, see:
// https://github.com/getsentry/sentry-webpack-plugin#options
org: "significant-gravitas",
project: "builder",
// Only print logs for uploading source maps in CI
silent: !process.env.CI,
// For all available options, see:
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
// Upload a larger set of source maps for prettier stack traces (increases build time)
widenClientFileUpload: true,
// Automatically annotate React components to show their full name in breadcrumbs and session replay
reactComponentAnnotation: {
enabled: true,
},
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
// This can increase your server load as well as your hosting bill.
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
// side errors will fail.
tunnelRoute: "/monitoring",
// Hides source maps from generated client bundles
hideSourceMaps: true,
// Automatically tree-shake Sentry logger statements to reduce bundle size
disableLogger: true,
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
// See the following for more information:
// https://docs.sentry.io/product/crons/
// https://vercel.com/docs/cron-jobs
automaticVercelMonitors: true,
async headers() {
return [
{
source: "/:path*",
headers: [
{
key: "Document-Policy",
value: "js-profiling",
},
],
},
];
},
});

View File

@@ -0,0 +1,72 @@
{
"name": "autogpt_builder",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"format": "prettier --write ."
},
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"@next/third-parties": "^14.2.5",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.1",
"@radix-ui/react-collapsible": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.2",
"@sentry/nextjs": "^8",
"@supabase/ssr": "^0.4.0",
"@supabase/supabase-js": "^2.45.0",
"@tanstack/react-table": "^8.20.5",
"@xyflow/react": "^12.1.0",
"ajv": "^8.17.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5",
"lucide-react": "^0.407.0",
"moment": "^2.30.1",
"next": "14.2.4",
"next-themes": "^0.3.0",
"react": "^18",
"react-day-picker": "^8.10.1",
"react-dom": "^18",
"react-hook-form": "^7.52.1",
"react-icons": "^5.2.1",
"react-markdown": "^9.0.1",
"react-modal": "^3.16.1",
"react-shepherd": "^6.1.1",
"recharts": "^2.12.7",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-modal": "^3.16.3",
"eslint": "^8",
"eslint-config-next": "14.2.4",
"postcss": "^8",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,57 @@
// This file configures the initialization of Sentry on the client.
// The config you add here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://fe4e4aa4a283391808a5da396da20159@o4505260022104064.ingest.us.sentry.io/4507946746380288",
// Add optional integrations for additional features
integrations: [
Sentry.replayIntegration(),
Sentry.httpClientIntegration(),
Sentry.replayCanvasIntegration(),
Sentry.reportingObserverIntegration(),
Sentry.browserProfilingIntegration(),
// Sentry.feedbackIntegration({
// // Additional SDK configuration goes in here, for example:
// colorScheme: "system",
// }),
],
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
tracesSampleRate: 1,
// Set `tracePropagationTargets` to control for which URLs trace propagation should be enabled
tracePropagationTargets: [
"localhost",
/^https:\/\/dev\-builder\.agpt\.co\/api/,
],
beforeSend(event, hint) {
// Check if it is an exception, and if so, show the report dialog
if (event.exception && event.event_id) {
Sentry.showReportDialog({ eventId: event.event_id });
}
return event;
},
// Define how likely Replay events are sampled.
// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0.1,
// Define how likely Replay events are sampled when an error occurs.
replaysOnErrorSampleRate: 1.0,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
// Set profilesSampleRate to 1.0 to profile every transaction.
// Since profilesSampleRate is relative to tracesSampleRate,
// the final profiling rate can be computed as tracesSampleRate * profilesSampleRate
// For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would
// result in 25% of transactions being profiled (0.5*0.5=0.25)
profilesSampleRate: 1.0,
});

View File

@@ -0,0 +1,16 @@
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
// The config you add here will be used whenever one of the edge features is loaded.
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: "https://fe4e4aa4a283391808a5da396da20159@o4505260022104064.ingest.us.sentry.io/4507946746380288",
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
tracesSampleRate: 1,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
});

View File

@@ -0,0 +1,23 @@
// This file configures the initialization of Sentry on the server.
// The config you add here will be used whenever the server handles a request.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
// import { NodeProfilingIntegration } from "@sentry/profiling-node";
Sentry.init({
dsn: "https://fe4e4aa4a283391808a5da396da20159@o4505260022104064.ingest.us.sentry.io/4507946746380288",
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
tracesSampleRate: 1,
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
// Integrations
integrations: [
Sentry.anrIntegration(),
// NodeProfilingIntegration,
// Sentry.fsIntegration(),
],
});

View File

@@ -0,0 +1,18 @@
import { withRoleAccess } from "@/lib/withRoleAccess";
import React from "react";
function AdminDashboard() {
return (
<div>
<h1>Admin Dashboard</h1>
{/* Add your admin-only content here */}
</div>
);
}
export default async function AdminDashboardPage() {
"use server";
const withAdminAccess = await withRoleAccess(["admin"]);
const ProtectedAdminDashboard = await withAdminAccess(AdminDashboard);
return <ProtectedAdminDashboard />;
}

View File

@@ -0,0 +1,100 @@
"use client";
import { useState } from "react";
import Link from "next/link";
import { BinaryIcon, XIcon } from "lucide-react";
import { usePathname } from "next/navigation"; // Add this import
const tabs = [
{ name: "Dashboard", href: "/admin/dashboard" },
{ name: "Marketplace", href: "/admin/marketplace" },
{ name: "Users", href: "/admin/users" },
{ name: "Settings", href: "/admin/settings" },
];
export default function AdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const pathname = usePathname(); // Get the current pathname
const [activeTab, setActiveTab] = useState(() => {
// Set active tab based on the current route
return tabs.find((tab) => tab.href === pathname)?.name || tabs[0].name;
});
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<div className="min-h-screen bg-gray-100">
<nav className="bg-white shadow-sm">
<div className="max-w-10xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex h-16 items-center justify-between">
<div className="flex items-center">
<div className="flex-shrink-0">
<h1 className="text-xl font-bold">Admin Panel</h1>
</div>
<div className="hidden sm:ml-6 sm:flex sm:space-x-8">
{tabs.map((tab) => (
<Link
key={tab.name}
href={tab.href}
className={`${
activeTab === tab.name
? "border-indigo-500 text-indigo-600"
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700"
} inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium`}
onClick={() => setActiveTab(tab.name)}
>
{tab.name}
</Link>
))}
</div>
</div>
<div className="sm:hidden">
<button
type="button"
className="inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
<span className="sr-only">Open main menu</span>
{mobileMenuOpen ? (
<XIcon className="block h-6 w-6" aria-hidden="true" />
) : (
<BinaryIcon className="block h-6 w-6" aria-hidden="true" />
)}
</button>
</div>
</div>
</div>
{mobileMenuOpen && (
<div className="sm:hidden">
<div className="space-y-1 pb-3 pt-2">
{tabs.map((tab) => (
<Link
key={tab.name}
href={tab.href}
className={`${
activeTab === tab.name
? "border-indigo-500 bg-indigo-50 text-indigo-700"
: "border-transparent text-gray-600 hover:border-gray-300 hover:bg-gray-50 hover:text-gray-800"
} block border-l-4 py-2 pl-3 pr-4 text-base font-medium`}
onClick={() => {
setActiveTab(tab.name);
setMobileMenuOpen(false);
}}
>
{tab.name}
</Link>
))}
</div>
</div>
)}
</nav>
<main className="py-10">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">{children}</div>
</main>
</div>
);
}

View File

@@ -0,0 +1,25 @@
import { withRoleAccess } from "@/lib/withRoleAccess";
import React from "react";
import { getReviewableAgents } from "@/components/admin/marketplace/actions";
import AdminMarketplaceAgentList from "@/components/admin/marketplace/AdminMarketplaceAgentList";
import AdminFeaturedAgentsControl from "@/components/admin/marketplace/AdminFeaturedAgentsControl";
import { Separator } from "@/components/ui/separator";
async function AdminMarketplace() {
const reviewableAgents = await getReviewableAgents();
return (
<>
<AdminMarketplaceAgentList agents={reviewableAgents.agents} />
<Separator className="my-4" />
<AdminFeaturedAgentsControl className="mt-4" />
</>
);
}
export default async function AdminDashboardPage() {
"use server";
const withAdminAccess = await withRoleAccess(["admin"]);
const ProtectedAdminMarketplace = await withAdminAccess(AdminMarketplace);
return <ProtectedAdminMarketplace />;
}

View File

@@ -0,0 +1,18 @@
import { withRoleAccess } from "@/lib/withRoleAccess";
import React from "react";
function AdminSettings() {
return (
<div>
<h1>Admin Settings</h1>
{/* Add your admin-only settings content here */}
</div>
);
}
export default async function AdminSettingsPage() {
"use server";
const withAdminAccess = await withRoleAccess(["admin"]);
const ProtectedAdminSettings = await withAdminAccess(AdminSettings);
return <ProtectedAdminSettings />;
}

View File

@@ -0,0 +1,18 @@
import { withRoleAccess } from "@/lib/withRoleAccess";
import React from "react";
function AdminUsers() {
return (
<div>
<h1>Users Dashboard</h1>
{/* Add your admin-only content here */}
</div>
);
}
export default async function AdminUsersPage() {
"use server";
const withAdminAccess = await withRoleAccess(["admin"]);
const ProtectedAdminUsers = await withAdminAccess(AdminUsers);
return <ProtectedAdminUsers />;
}

View File

@@ -0,0 +1,36 @@
"use client";
import { useEffect, useState } from "react";
export default function AuthErrorPage() {
const [errorType, setErrorType] = useState<string | null>(null);
const [errorCode, setErrorCode] = useState<string | null>(null);
const [errorDescription, setErrorDescription] = useState<string | null>(null);
useEffect(() => {
// This code only runs on the client side
if (typeof window !== "undefined") {
const hash = window.location.hash.substring(1); // Remove the leading '#'
const params = new URLSearchParams(hash);
setErrorType(params.get("error"));
setErrorCode(params.get("error_code"));
setErrorDescription(
params.get("error_description")?.replace(/\+/g, " ") ?? null,
); // Replace '+' with space
}
}, []);
if (!errorType && !errorCode && !errorDescription) {
return <div>Loading...</div>;
}
return (
<div>
<h1>Authentication Error</h1>
{errorType && <p>Error Type: {errorType}</p>}
{errorCode && <p>Error Code: {errorCode}</p>}
{errorDescription && <p>Error Description: {errorDescription}</p>}
</div>
);
}

View File

@@ -0,0 +1,36 @@
import { NextResponse } from "next/server";
import { createServerClient } from "@/lib/supabase/server";
// Handle the callback to complete the user session login
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
// if "next" is in param, use it as the redirect URL
const next = searchParams.get("next") ?? "/profile";
if (code) {
const supabase = createServerClient();
if (!supabase) {
return NextResponse.redirect(`${origin}/error`);
}
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
// data.session?.refresh_token is available if you need to store it for later use
if (!error) {
const forwardedHost = request.headers.get("x-forwarded-host"); // original origin before load balancer
const isLocalEnv = process.env.NODE_ENV === "development";
if (isLocalEnv) {
// we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host
return NextResponse.redirect(`${origin}${next}`);
} else if (forwardedHost) {
return NextResponse.redirect(`https://${forwardedHost}${next}`);
} else {
return NextResponse.redirect(`${origin}${next}`);
}
}
}
// return the user to an error page with instructions
return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}

View File

@@ -0,0 +1,33 @@
import { type EmailOtpType } from "@supabase/supabase-js";
import { type NextRequest } from "next/server";
import { redirect } from "next/navigation";
import { createServerClient } from "@/lib/supabase/server";
// Email confirmation route
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const token_hash = searchParams.get("token_hash");
const type = searchParams.get("type") as EmailOtpType | null;
const next = searchParams.get("next") ?? "/";
if (token_hash && type) {
const supabase = createServerClient();
if (!supabase) {
redirect("/error");
}
const { error } = await supabase.auth.verifyOtp({
type,
token_hash,
});
if (!error) {
// redirect user to specified redirect URL or root of app
redirect(next);
}
}
// redirect the user to an error page with some instructions
redirect("/error");
}

View File

@@ -0,0 +1,16 @@
"use client";
import { useSearchParams } from "next/navigation";
import FlowEditor from '@/components/Flow';
export default function Home() {
const query = useSearchParams();
return (
<FlowEditor
className="flow-container w-full min-h-[86vh] border border-gray-300 dark:border-gray-700 rounded-lg"
flowID={query.get("flowID") ?? query.get("templateID") ?? undefined}
template={!!query.get("templateID")}
/>
);
}

View File

@@ -0,0 +1,43 @@
"use client";
import { useEffect } from "react";
import { IconCircleAlert } from "@/components/ui/icons";
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div className="fixed inset-0 flex items-center justify-center bg-background">
<div className="w-full max-w-md px-4 text-center sm:px-6">
<div className="mx-auto flex size-12 items-center justify-center rounded-full bg-muted">
<IconCircleAlert className="size-10" />
</div>
<h1 className="mt-8 text-2xl font-bold tracking-tight text-foreground">
Oops, something went wrong!
</h1>
<p className="mt-4 text-muted-foreground">
We&apos;re sorry, but an unexpected error has occurred. Please try
again later or contact support if the issue persists.
</p>
<div className="mt-6 flex flex-row justify-center gap-4">
<Button onClick={reset} variant="outline">
Retry
</Button>
<Button>
<Link href="/">Go to Homepage</Link>
</Button>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,27 @@
"use client";
import * as Sentry from "@sentry/nextjs";
import NextError from "next/error";
import { useEffect } from "react";
export default function GlobalError({
error,
}: {
error: Error & { digest?: string };
}) {
useEffect(() => {
Sentry.captureException(error);
}, [error]);
return (
<html>
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
<NextError statusCode={0} />
</body>
</html>
);
}

View File

@@ -0,0 +1,75 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -0,0 +1,49 @@
import React from "react";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { Providers } from "@/app/providers";
import { NavBar } from "@/components/NavBar";
import { cn } from "@/lib/utils";
import "./globals.css";
import TallyPopupSimple from "@/components/TallyPopup";
import { GoogleAnalytics } from "@next/third-parties/google";
import { Toaster } from "@/components/ui/toaster";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "NextGen AutoGPT",
description: "Your one stop shop to creating AI Agents",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={cn("antialiased transition-colors", inter.className)}>
<Providers
attribute="class"
defaultTheme="light"
// Feel free to remove this line if you want to use the system theme by default
// enableSystem
disableTransitionOnChange
>
<div className="flex min-h-screen flex-col">
<NavBar />
<main className="flex-1 overflow-hidden p-4">{children}</main>
<TallyPopupSimple />
</div>
<Toaster />
</Providers>
</body>
<GoogleAnalytics
gaId={process.env.GA_MEASUREMENT_ID || "G-FH2XK2W4GN"} // This is the measurement Id for the Google Analytics dev project
/>
</html>
);
}

View File

@@ -0,0 +1,21 @@
import AgentFlowListSkeleton from "@/components/monitor/skeletons/AgentFlowListSkeleton";
import React from "react";
import FlowRunsListSkeleton from "@/components/monitor/skeletons/FlowRunsListSkeleton";
import FlowRunsStatusSkeleton from "@/components/monitor/skeletons/FlowRunsStatusSkeleton";
export default function MonitorLoadingSkeleton() {
return (
<div className="space-y-4 p-4">
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{/* Agents Section */}
<AgentFlowListSkeleton />
{/* Runs Section */}
<FlowRunsListSkeleton />
{/* Stats Section */}
<FlowRunsStatusSkeleton />
</div>
</div>
);
}

View File

@@ -0,0 +1,64 @@
"use server";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { createServerClient } from "@/lib/supabase/server";
import { z } from "zod";
import * as Sentry from "@sentry/nextjs";
const loginFormSchema = z.object({
email: z.string().email().min(2).max(64),
password: z.string().min(6).max(64),
});
export async function login(values: z.infer<typeof loginFormSchema>) {
return await Sentry.withServerActionInstrumentation("login", {}, async () => {
const supabase = createServerClient();
if (!supabase) {
redirect("/error");
}
// We are sure that the values are of the correct type because zod validates the form
const { data, error } = await supabase.auth.signInWithPassword(values);
if (error) {
return error.message;
}
if (data.session) {
await supabase.auth.setSession(data.session);
}
revalidatePath("/", "layout");
redirect("/profile");
});
}
export async function signup(values: z.infer<typeof loginFormSchema>) {
"use server";
return await Sentry.withServerActionInstrumentation(
"signup",
{},
async () => {
const supabase = createServerClient();
if (!supabase) {
redirect("/error");
}
// We are sure that the values are of the correct type because zod validates the form
const { data, error } = await supabase.auth.signUp(values);
if (error) {
return error.message;
}
if (data.session) {
await supabase.auth.setSession(data.session);
}
revalidatePath("/", "layout");
redirect("/profile");
},
);
}

View File

@@ -0,0 +1,234 @@
"use client";
import useUser from "@/hooks/useUser";
import { login, signup } from "./actions";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { useForm } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { PasswordInput } from "@/components/PasswordInput";
import { FaGoogle, FaGithub, FaDiscord, FaSpinner } from "react-icons/fa";
import { useState } from "react";
import { useSupabase } from "@/components/SupabaseProvider";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { Checkbox } from "@/components/ui/checkbox";
const loginFormSchema = z.object({
email: z.string().email().min(2).max(64),
password: z.string().min(6).max(64),
agreeToTerms: z.boolean().refine((value) => value === true, {
message: "You must agree to the Terms of Service and Privacy Policy",
}),
});
export default function LoginPage() {
const { supabase, isLoading: isSupabaseLoading } = useSupabase();
const { user, isLoading: isUserLoading } = useUser();
const [feedback, setFeedback] = useState<string | null>(null);
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const form = useForm<z.infer<typeof loginFormSchema>>({
resolver: zodResolver(loginFormSchema),
defaultValues: {
email: "",
password: "",
agreeToTerms: false,
},
});
if (user) {
console.log("User exists, redirecting to profile");
router.push("/profile");
}
if (isUserLoading || isSupabaseLoading || user) {
return (
<div className="flex h-[80vh] items-center justify-center">
<FaSpinner className="mr-2 h-16 w-16 animate-spin" />
</div>
);
}
if (!supabase) {
return (
<div>
User accounts are disabled because Supabase client is unavailable
</div>
);
}
async function handleSignInWithProvider(
provider: "google" | "github" | "discord",
) {
const { data, error } = await supabase!.auth.signInWithOAuth({
provider: provider,
options: {
redirectTo:
process.env.AUTH_CALLBACK_URL ??
`http://localhost:3000/auth/callback`,
},
});
if (!error) {
setFeedback(null);
return;
}
setFeedback(error.message);
}
const onLogin = async (data: z.infer<typeof loginFormSchema>) => {
setIsLoading(true);
const error = await login(data);
setIsLoading(false);
if (error) {
setFeedback(error);
return;
}
setFeedback(null);
};
const onSignup = async (data: z.infer<typeof loginFormSchema>) => {
if (await form.trigger()) {
setIsLoading(true);
const error = await signup(data);
setIsLoading(false);
if (error) {
setFeedback(error);
return;
}
setFeedback(null);
}
};
return (
<div className="flex h-[80vh] items-center justify-center">
<div className="w-full max-w-md space-y-6 rounded-lg p-8 shadow-md">
<div className="mb-6 space-y-2">
<Button
className="w-full"
onClick={() => handleSignInWithProvider("google")}
variant="outline"
type="button"
disabled={isLoading}
>
<FaGoogle className="mr-2 h-4 w-4" />
Sign in with Google
</Button>
<Button
className="w-full"
onClick={() => handleSignInWithProvider("github")}
variant="outline"
type="button"
disabled={isLoading}
>
<FaGithub className="mr-2 h-4 w-4" />
Sign in with GitHub
</Button>
<Button
className="w-full"
onClick={() => handleSignInWithProvider("discord")}
variant="outline"
type="button"
disabled={isLoading}
>
<FaDiscord className="mr-2 h-4 w-4" />
Sign in with Discord
</Button>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onLogin)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem className="mb-4">
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="user@email.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<PasswordInput placeholder="password" {...field} />
</FormControl>
<FormDescription>
Password needs to be at least 6 characters long
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="agreeToTerms"
render={({ field }) => (
<FormItem className="mt-4 flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
I agree to the{" "}
<Link href="/terms-of-service" className="underline">
Terms of Service
</Link>{" "}
and{" "}
<Link
href="https://www.notion.so/auto-gpt/Privacy-Policy-ab11c9c20dbd4de1a15dcffe84d77984"
className="underline"
>
Privacy Policy
</Link>
</FormLabel>
<FormMessage />
</div>
</FormItem>
)}
/>
<div className="mb-6 mt-6 flex w-full space-x-4">
<Button
className="flex w-1/2 justify-center"
type="submit"
disabled={isLoading}
>
Log in
</Button>
<Button
className="flex w-1/2 justify-center"
variant="outline"
type="button"
onClick={form.handleSubmit(onSignup)}
disabled={isLoading}
>
Sign up
</Button>
</div>
</form>
<p className="text-sm text-red-500">{feedback}</p>
</Form>
</div>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import { Suspense } from "react";
import { notFound } from "next/navigation";
import MarketplaceAPI from "@/lib/marketplace-api";
import { AgentDetailResponse } from "@/lib/marketplace-api";
import AgentDetailContent from "@/components/marketplace/AgentDetailContent";
async function getAgentDetails(id: string): Promise<AgentDetailResponse> {
const apiUrl =
process.env.NEXT_PUBLIC_AGPT_MARKETPLACE_URL ||
"http://localhost:8015/api/v1/market";
const api = new MarketplaceAPI(apiUrl);
try {
console.log(`Fetching agent details for id: ${id}`);
const agent = await api.getAgentDetails(id);
console.log(`Agent details fetched:`, agent);
return agent;
} catch (error) {
console.error(`Error fetching agent details:`, error);
throw error;
}
}
export default async function AgentDetailPage({
params,
}: {
params: { id: string };
}) {
let agent: AgentDetailResponse;
try {
agent = await getAgentDetails(params.id);
} catch (error) {
return notFound();
}
return (
<Suspense fallback={<div>Loading...</div>}>
<AgentDetailContent agent={agent} />
</Suspense>
);
}

View File

@@ -0,0 +1,317 @@
"use client";
import React, { useEffect, useMemo, useState, useCallback } from "react";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import MarketplaceAPI, {
AgentResponse,
AgentListResponse,
AgentWithRank,
} from "@/lib/marketplace-api";
import {
ChevronLeft,
ChevronRight,
PlusCircle,
Search,
Star,
} from "lucide-react";
// Utility Functions
function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number,
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout | null = null;
return (...args: Parameters<T>) => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
// Types
type Agent = AgentResponse | AgentWithRank;
// Components
const HeroSection: React.FC = () => {
const router = useRouter();
return (
<div className="relative bg-indigo-600 py-6">
<div className="absolute inset-0 z-0">
<Image
src="https://images.unsplash.com/photo-1562408590-e32931084e23?auto=format&fit=crop&w=2070&q=80"
alt="Marketplace background"
layout="fill"
objectFit="cover"
quality={75}
priority
className="opacity-20"
/>
<div
className="absolute inset-0 bg-indigo-600 mix-blend-multiply"
aria-hidden="true"
></div>
</div>
<div className="relative mx-auto flex max-w-7xl items-center justify-between px-4 py-4 sm:px-6 lg:px-8">
<div>
<h1 className="text-2xl font-extrabold tracking-tight text-white sm:text-3xl lg:text-4xl">
AutoGPT Marketplace
</h1>
<p className="mt-2 max-w-3xl text-sm text-indigo-100 sm:text-base">
Discover and share proven AI Agents to supercharge your business.
</p>
</div>
<Button
onClick={() => router.push("/marketplace/submit")}
className="flex items-center bg-white text-indigo-600 hover:bg-indigo-50"
>
<PlusCircle className="mr-2 h-4 w-4" />
Submit Agent
</Button>
</div>
</div>
);
};
const SearchInput: React.FC<{
value: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}> = ({ value, onChange }) => (
<div className="relative mb-8">
<Input
placeholder="Search agents..."
type="text"
className="w-full rounded-full border-gray-300 py-2 pl-10 pr-4 focus:border-indigo-500 focus:ring-indigo-500"
value={value}
onChange={onChange}
/>
<Search
className="absolute left-3 top-1/2 -translate-y-1/2 transform text-gray-400"
size={20}
/>
</div>
);
const AgentCard: React.FC<{ agent: Agent; featured?: boolean }> = ({
agent,
featured = false,
}) => {
const router = useRouter();
const handleClick = () => {
router.push(`/marketplace/${agent.id}`);
};
return (
<div
className={`flex cursor-pointer flex-col justify-between rounded-lg border p-6 transition-colors duration-200 hover:bg-gray-50 ${featured ? "border-indigo-500 shadow-md" : "border-gray-200"}`}
onClick={handleClick}
>
<div>
<div className="mb-2 flex items-center justify-between">
<h3 className="truncate text-lg font-semibold text-gray-900">
{agent.name}
</h3>
{featured && <Star className="text-indigo-500" size={20} />}
</div>
<p className="mb-4 line-clamp-2 text-sm text-gray-500">
{agent.description}
</p>
<div className="mb-2 text-xs text-gray-400">
Categories: {agent.categories.join(", ")}
</div>
</div>
<div className="flex items-end justify-between">
<div className="text-xs text-gray-400">
Updated {new Date(agent.updatedAt).toLocaleDateString()}
</div>
<div className="text-xs text-gray-400">Downloads {agent.downloads}</div>
{"rank" in agent && (
<div className="text-xs text-indigo-600">
Rank: {agent.rank.toFixed(2)}
</div>
)}
</div>
</div>
);
};
const AgentGrid: React.FC<{
agents: Agent[];
title: string;
featured?: boolean;
}> = ({ agents, title, featured = false }) => (
<div className="mb-12">
<h2 className="mb-4 text-2xl font-bold text-gray-900">{title}</h2>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{agents.map((agent) => (
<AgentCard agent={agent} key={agent.id} featured={featured} />
))}
</div>
</div>
);
const Pagination: React.FC<{
page: number;
totalPages: number;
onPrevPage: () => void;
onNextPage: () => void;
}> = ({ page, totalPages, onPrevPage, onNextPage }) => (
<div className="mt-8 flex items-center justify-between">
<Button
onClick={onPrevPage}
disabled={page === 1}
className="flex items-center space-x-2 rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50"
>
<ChevronLeft size={16} />
<span>Previous</span>
</Button>
<span className="text-sm text-gray-700">
Page {page} of {totalPages}
</span>
<Button
onClick={onNextPage}
disabled={page === totalPages}
className="flex items-center space-x-2 rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50"
>
<span>Next</span>
<ChevronRight size={16} />
</Button>
</div>
);
// Main Component
const Marketplace: React.FC = () => {
const apiUrl =
process.env.NEXT_PUBLIC_AGPT_MARKETPLACE_URL ||
"http://localhost:8015/api/v1/market";
const api = useMemo(() => new MarketplaceAPI(apiUrl), [apiUrl]);
const [searchValue, setSearchValue] = useState("");
const [searchResults, setSearchResults] = useState<Agent[]>([]);
const [featuredAgents, setFeaturedAgents] = useState<Agent[]>([]);
const [topAgents, setTopAgents] = useState<Agent[]>([]);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const fetchTopAgents = useCallback(
async (currentPage: number) => {
setIsLoading(true);
try {
const response = await api.getTopDownloadedAgents(currentPage, 9);
setTopAgents(response.agents);
setTotalPages(response.total_pages);
} catch (error) {
console.error("Error fetching top agents:", error);
} finally {
setIsLoading(false);
}
},
[api],
);
const fetchFeaturedAgents = useCallback(async () => {
try {
const featured = await api.getFeaturedAgents();
setFeaturedAgents(featured.agents);
} catch (error) {
console.error("Error fetching featured agents:", error);
}
}, [api]);
const searchAgents = useCallback(
async (searchTerm: string) => {
setIsLoading(true);
try {
const response = await api.searchAgents(searchTerm, 1, 30);
const filteredAgents = response.filter((agent) => agent.rank > 0);
setSearchResults(filteredAgents);
} catch (error) {
console.error("Error searching agents:", error);
} finally {
setIsLoading(false);
}
},
[api],
);
const debouncedSearch = useMemo(
() => debounce(searchAgents, 300),
[searchAgents],
);
useEffect(() => {
if (searchValue) {
debouncedSearch(searchValue);
} else {
fetchTopAgents(page);
}
}, [searchValue, page, debouncedSearch, fetchTopAgents]);
useEffect(() => {
fetchFeaturedAgents();
}, [fetchFeaturedAgents]);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchValue(e.target.value);
setPage(1);
};
const handleNextPage = () => {
if (page < totalPages) {
setPage(page + 1);
}
};
const handlePrevPage = () => {
if (page > 1) {
setPage(page - 1);
}
};
return (
<div className="min-h-screen bg-gray-50">
<HeroSection />
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
<SearchInput value={searchValue} onChange={handleInputChange} />
{isLoading ? (
<div className="py-12 text-center">
<div className="inline-block h-8 w-8 animate-spin rounded-full border-b-2 border-gray-900"></div>
<p className="mt-2 text-gray-600">Loading agents...</p>
</div>
) : searchValue ? (
searchResults.length > 0 ? (
<AgentGrid agents={searchResults} title="Search Results" />
) : (
<div className="py-12 text-center">
<p className="text-gray-600">
No agents found matching your search criteria.
</p>
</div>
)
) : (
<>
{featuredAgents.length > 0 && (
<AgentGrid
agents={featuredAgents}
title="Featured Agents"
featured={true}
/>
)}
<AgentGrid agents={topAgents} title="Top Downloaded Agents" />
<Pagination
page={page}
totalPages={totalPages}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
/>
</>
)}
</div>
</div>
);
};
export default Marketplace;

View File

@@ -0,0 +1,449 @@
"use client";
import React, { useState, useEffect, useMemo } from "react";
import { useRouter } from "next/navigation";
import { useForm, Controller } from "react-hook-form";
import MarketplaceAPI from "@/lib/marketplace-api";
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
import { Checkbox } from "@/components/ui/checkbox";
import {
MultiSelector,
MultiSelectorContent,
MultiSelectorInput,
MultiSelectorItem,
MultiSelectorList,
MultiSelectorTrigger,
} from "@/components/ui/multiselect";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
type FormData = {
name: string;
description: string;
author: string;
keywords: string[];
categories: string[];
agreeToTerms: boolean;
selectedAgentId: string;
};
const keywords = [
"Automation",
"AI Workflows",
"Integration",
"Task Automation",
"Data Processing",
"Workflow Management",
"Real-time Analytics",
"Custom Triggers",
"Event-driven",
"API Integration",
"Data Transformation",
"Multi-step Workflows",
"Collaboration Tools",
"Business Process Automation",
"No-code Solutions",
"AI-Powered",
"Smart Notifications",
"Data Syncing",
"User Engagement",
"Reporting Automation",
"Lead Generation",
"Customer Support Automation",
"E-commerce Automation",
"Social Media Management",
"Email Marketing Automation",
"Document Management",
"Data Enrichment",
"Performance Tracking",
"Predictive Analytics",
"Resource Allocation",
"Chatbot",
"Virtual Assistant",
"Workflow Automation",
"Social Media Manager",
"Email Optimizer",
"Content Generator",
"Data Analyzer",
"Task Scheduler",
"Customer Service Bot",
"Personalization Engine",
];
const SubmitPage: React.FC = () => {
const router = useRouter();
const {
control,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<FormData>({
defaultValues: {
selectedAgentId: "", // Initialize with an empty string
name: "",
description: "",
author: "",
keywords: [],
categories: [],
agreeToTerms: false,
},
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null);
const [userAgents, setUserAgents] = useState<
Array<{ id: string; name: string; version: number }>
>([]);
const [selectedAgentGraph, setSelectedAgentGraph] = useState<any>(null);
const selectedAgentId = watch("selectedAgentId");
useEffect(() => {
const fetchUserAgents = async () => {
const api = new AutoGPTServerAPI();
const agents = await api.listGraphs();
console.log(agents);
setUserAgents(
agents.map((agent) => ({
id: agent.id,
name: agent.name || `Agent (${agent.id})`,
version: agent.version,
})),
);
};
fetchUserAgents();
}, []);
useEffect(() => {
const fetchAgentGraph = async () => {
if (selectedAgentId) {
const api = new AutoGPTServerAPI();
const graph = await api.getGraph(selectedAgentId);
setSelectedAgentGraph(graph);
setValue("name", graph.name);
setValue("description", graph.description);
}
};
fetchAgentGraph();
}, [selectedAgentId, setValue]);
const onSubmit = async (data: FormData) => {
setIsSubmitting(true);
setSubmitError(null);
if (!data.agreeToTerms) {
throw new Error("You must agree to the terms of service");
}
try {
if (!selectedAgentGraph) {
throw new Error("Please select an agent");
}
const api = new MarketplaceAPI();
await api.submitAgent(
{
...selectedAgentGraph,
name: data.name,
description: data.description,
},
data.author,
data.keywords,
data.categories,
);
router.push("/marketplace?submission=success");
} catch (error) {
console.error("Submission error:", error);
setSubmitError(
error instanceof Error ? error.message : "An unknown error occurred",
);
} finally {
setIsSubmitting(false);
}
};
return (
<div className="container mx-auto px-4 py-8">
<h1 className="mb-6 text-3xl font-bold">Submit Your Agent</h1>
<Card className="p-6">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-4">
<Controller
name="selectedAgentId"
control={control}
rules={{ required: "Please select an agent" }}
render={({ field }) => (
<div>
<label
htmlFor={field.name}
className="block text-sm font-medium text-gray-700"
>
Select Agent
</label>
<Select
onValueChange={field.onChange}
value={field.value || ""}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select an agent" />
</SelectTrigger>
<SelectContent>
{userAgents.map((agent) => (
<SelectItem key={agent.id} value={agent.id}>
{agent.name} (v{agent.version})
</SelectItem>
))}
</SelectContent>
</Select>
{errors.selectedAgentId && (
<p className="mt-1 text-sm text-red-600">
{errors.selectedAgentId.message}
</p>
)}
</div>
)}
/>
{/* {selectedAgentGraph && (
<div className="mt-4" style={{ height: "600px" }}>
<ReactFlow
nodes={nodes}
edges={edges}
fitView
attributionPosition="bottom-left"
nodesConnectable={false}
nodesDraggable={false}
zoomOnScroll={false}
panOnScroll={false}
elementsSelectable={false}
>
<Controls showInteractive={false} />
<Background />
</ReactFlow>
</div>
)} */}
<Controller
name="name"
control={control}
rules={{ required: "Name is required" }}
render={({ field }) => (
<div>
<label
htmlFor={field.name}
className="block text-sm font-medium text-gray-700"
>
Agent Name
</label>
<Input
id={field.name}
placeholder="Enter your agent's name"
{...field}
/>
{errors.name && (
<p className="mt-1 text-sm text-red-600">
{errors.name.message}
</p>
)}
</div>
)}
/>
<Controller
name="description"
control={control}
rules={{ required: "Description is required" }}
render={({ field }) => (
<div>
<label
htmlFor={field.name}
className="block text-sm font-medium text-gray-700"
>
Description
</label>
<Textarea
id={field.name}
placeholder="Describe your agent"
{...field}
/>
{errors.description && (
<p className="mt-1 text-sm text-red-600">
{errors.description.message}
</p>
)}
</div>
)}
/>
<Controller
name="author"
control={control}
rules={{ required: "Author is required" }}
render={({ field }) => (
<div>
<label
htmlFor={field.name}
className="block text-sm font-medium text-gray-700"
>
Author
</label>
<Input
id={field.name}
placeholder="Your name or username"
{...field}
/>
{errors.author && (
<p className="mt-1 text-sm text-red-600">
{errors.author.message}
</p>
)}
</div>
)}
/>
<Controller
name="keywords"
control={control}
rules={{ required: "At least one keyword is required" }}
render={({ field }) => (
<div>
<label
htmlFor={field.name}
className="block text-sm font-medium text-gray-700"
>
Keywords
</label>
<MultiSelector
values={field.value || []}
onValuesChange={field.onChange}
>
<MultiSelectorTrigger>
<MultiSelectorInput placeholder="Add keywords" />
</MultiSelectorTrigger>
<MultiSelectorContent>
<MultiSelectorList>
{keywords.map((keyword) => (
<MultiSelectorItem key={keyword} value={keyword}>
{keyword}
</MultiSelectorItem>
))}
</MultiSelectorList>
</MultiSelectorContent>
</MultiSelector>
{errors.keywords && (
<p className="mt-1 text-sm text-red-600">
{errors.keywords.message}
</p>
)}
</div>
)}
/>
<Controller
name="categories"
control={control}
rules={{ required: "At least one category is required" }}
render={({ field }) => (
<div>
<label
htmlFor={field.name}
className="block text-sm font-medium text-gray-700"
>
Categories
</label>
<MultiSelector
values={field.value || []}
onValuesChange={field.onChange}
>
<MultiSelectorTrigger>
<MultiSelectorInput placeholder="Select categories" />
</MultiSelectorTrigger>
<MultiSelectorContent>
<MultiSelectorList>
<MultiSelectorItem value="productivity">
Productivity
</MultiSelectorItem>
<MultiSelectorItem value="entertainment">
Entertainment
</MultiSelectorItem>
<MultiSelectorItem value="education">
Education
</MultiSelectorItem>
<MultiSelectorItem value="business">
Business
</MultiSelectorItem>
<MultiSelectorItem value="other">
Other
</MultiSelectorItem>
</MultiSelectorList>
</MultiSelectorContent>
</MultiSelector>
{errors.categories && (
<p className="mt-1 text-sm text-red-600">
{errors.categories.message}
</p>
)}
</div>
)}
/>
<Controller
name="agreeToTerms"
control={control}
rules={{ required: "You must agree to the terms of service" }}
render={({ field }) => (
<div className="flex items-center space-x-2">
<Checkbox
id="agreeToTerms"
checked={field.value}
onCheckedChange={field.onChange}
/>
<label
htmlFor="agreeToTerms"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
I agree to the{" "}
<a href="/terms" className="text-blue-500 hover:underline">
terms of service
</a>
</label>
</div>
)}
/>
{errors.agreeToTerms && (
<p className="mt-1 text-sm text-red-600">
{errors.agreeToTerms.message}
</p>
)}
{submitError && (
<Alert variant="destructive">
<AlertTitle>Submission Failed</AlertTitle>
<AlertDescription>{submitError}</AlertDescription>
</Alert>
)}
<Button type="submit" className="w-full" disabled={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit Agent"}
</Button>
</div>
</form>
</Card>
</div>
);
};
export default SubmitPage;

View File

@@ -0,0 +1,178 @@
"use client";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import AutoGPTServerAPI, {
GraphMeta,
NodeExecutionResult,
} from "@/lib/autogpt-server-api";
import { Card } from "@/components/ui/card";
import { FlowRun } from "@/lib/types";
import {
AgentFlowList,
FlowInfo,
FlowRunInfo,
FlowRunsList,
FlowRunsStats,
} from "@/components/monitor";
const Monitor = () => {
const [flows, setFlows] = useState<GraphMeta[]>([]);
const [flowRuns, setFlowRuns] = useState<FlowRun[]>([]);
const [selectedFlow, setSelectedFlow] = useState<GraphMeta | null>(null);
const [selectedRun, setSelectedRun] = useState<FlowRun | null>(null);
const api = useMemo(() => new AutoGPTServerAPI(), []);
const refreshFlowRuns = useCallback(
(flowID: string) => {
// Fetch flow run IDs
api.listGraphRunIDs(flowID).then((runIDs) =>
runIDs.map((runID) => {
let run;
if (
(run = flowRuns.find((fr) => fr.id == runID)) &&
!["waiting", "running"].includes(run.status)
) {
return;
}
// Fetch flow run
api.getGraphExecutionInfo(flowID, runID).then((execInfo) =>
setFlowRuns((flowRuns) => {
if (execInfo.length == 0) return flowRuns;
const flowRunIndex = flowRuns.findIndex((fr) => fr.id == runID);
const flowRun = flowRunFromNodeExecutionResults(execInfo);
if (flowRunIndex > -1) {
flowRuns.splice(flowRunIndex, 1, flowRun);
} else {
flowRuns.push(flowRun);
}
return [...flowRuns];
}),
);
}),
);
},
[api, flowRuns],
);
const fetchFlowsAndRuns = useCallback(() => {
api.listGraphs().then((flows) => {
setFlows(flows);
flows.map((flow) => refreshFlowRuns(flow.id));
});
}, [api, refreshFlowRuns]);
useEffect(() => fetchFlowsAndRuns(), [fetchFlowsAndRuns]);
useEffect(() => {
const intervalId = setInterval(
() => flows.map((f) => refreshFlowRuns(f.id)),
5000,
);
return () => clearInterval(intervalId);
}, [flows, refreshFlowRuns]);
const column1 = "md:col-span-2 xl:col-span-3 xxl:col-span-2";
const column2 = "md:col-span-3 lg:col-span-2 xl:col-span-3 space-y-4";
const column3 = "col-span-full xl:col-span-4 xxl:col-span-5";
return (
<div className="grid grid-cols-1 gap-4 md:grid-cols-5 lg:grid-cols-4 xl:grid-cols-10">
<AgentFlowList
className={column1}
flows={flows}
flowRuns={flowRuns}
selectedFlow={selectedFlow}
onSelectFlow={(f) => {
setSelectedRun(null);
setSelectedFlow(f.id == selectedFlow?.id ? null : f);
}}
/>
<FlowRunsList
className={column2}
flows={flows}
runs={[
...(selectedFlow
? flowRuns.filter((v) => v.graphID == selectedFlow.id)
: flowRuns),
].sort((a, b) => Number(a.startTime) - Number(b.startTime))}
selectedRun={selectedRun}
onSelectRun={(r) => setSelectedRun(r.id == selectedRun?.id ? null : r)}
/>
{(selectedRun && (
<FlowRunInfo
flow={selectedFlow || flows.find((f) => f.id == selectedRun.graphID)!}
flowRun={selectedRun}
className={column3}
/>
)) ||
(selectedFlow && (
<FlowInfo
flow={selectedFlow}
flowRuns={flowRuns.filter((r) => r.graphID == selectedFlow.id)}
className={column3}
/>
)) || (
<Card className={`p-6 ${column3}`}>
<FlowRunsStats flows={flows} flowRuns={flowRuns} />
</Card>
)}
</div>
);
};
function flowRunFromNodeExecutionResults(
nodeExecutionResults: NodeExecutionResult[],
): FlowRun {
// Determine overall status
let status: "running" | "waiting" | "success" | "failed" = "success";
for (const execution of nodeExecutionResults) {
if (execution.status === "FAILED") {
status = "failed";
break;
} else if (["QUEUED", "RUNNING"].includes(execution.status)) {
status = "running";
break;
} else if (execution.status === "INCOMPLETE") {
status = "waiting";
}
}
// Determine aggregate startTime, endTime, and totalRunTime
const now = Date.now();
const startTime = Math.min(
...nodeExecutionResults.map((ner) => ner.add_time.getTime()),
now,
);
const endTime = ["success", "failed"].includes(status)
? Math.max(
...nodeExecutionResults.map((ner) => ner.end_time?.getTime() || 0),
startTime,
)
: now;
const duration = (endTime - startTime) / 1000; // Convert to seconds
const totalRunTime =
nodeExecutionResults.reduce(
(cum, node) =>
cum +
((node.end_time?.getTime() ?? now) -
(node.start_time?.getTime() ?? now)),
0,
) / 1000;
return {
id: nodeExecutionResults[0].graph_exec_id,
graphID: nodeExecutionResults[0].graph_id,
graphVersion: nodeExecutionResults[0].graph_version,
status,
startTime,
endTime,
duration,
totalRunTime,
nodeExecutionResults: nodeExecutionResults,
};
}
export default Monitor;

View File

@@ -0,0 +1,33 @@
"use client";
import { useSupabase } from "@/components/SupabaseProvider";
import { Button } from "@/components/ui/button";
import useUser from "@/hooks/useUser";
import { useRouter } from "next/navigation";
import { FaSpinner } from "react-icons/fa";
export default function PrivatePage() {
const { user, isLoading, error } = useUser();
const { supabase } = useSupabase();
const router = useRouter();
if (isLoading) {
return (
<div className="flex h-[80vh] items-center justify-center">
<FaSpinner className="mr-2 h-16 w-16 animate-spin" />
</div>
);
}
if (error || !user || !supabase) {
router.push("/login");
return null;
}
return (
<div>
<p>Hello {user.email}</p>
<Button onClick={() => supabase.auth.signOut()}>Log out</Button>
</div>
);
}

View File

@@ -0,0 +1,17 @@
"use client";
import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ThemeProviderProps } from "next-themes/dist/types";
import { TooltipProvider } from "@/components/ui/tooltip";
import SupabaseProvider from "@/components/SupabaseProvider";
export function Providers({ children, ...props }: ThemeProviderProps) {
return (
<NextThemesProvider {...props}>
<SupabaseProvider>
<TooltipProvider>{children}</TooltipProvider>
</SupabaseProvider>
</NextThemesProvider>
);
}

View File

@@ -0,0 +1,9 @@
// app/unauthorized/page.tsx
export default function Unauthorized() {
return (
<div>
<h1>Unauthorized Access</h1>
<p>You do not have permission to view this page.</p>
</div>
);
}

View File

@@ -0,0 +1,34 @@
import {
BaseEdge,
ConnectionLineComponentProps,
getBezierPath,
Position,
} from "@xyflow/react";
const ConnectionLine: React.FC<ConnectionLineComponentProps> = ({
fromPosition,
fromHandle,
fromX,
fromY,
toPosition,
toX,
toY,
}) => {
const sourceX =
fromPosition === Position.Right
? fromX + (fromHandle?.width! / 2 - 5)
: fromX - (fromHandle?.width! / 2 - 5);
const [path] = getBezierPath({
sourceX: sourceX,
sourceY: fromY,
sourcePosition: fromPosition,
targetX: toX,
targetY: toY,
targetPosition: toPosition,
});
return <BaseEdge path={path} style={{ strokeWidth: 2, stroke: "#555" }} />;
};
export default ConnectionLine;

View File

@@ -0,0 +1,32 @@
"use client";
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { IconRefresh } from "@/components/ui/icons";
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
export default function CreditButton() {
const [credit, setCredit] = useState<number | null>(null);
const api = new AutoGPTServerAPI();
const fetchCredit = async () => {
const response = await api.getUserCredit();
setCredit(response.credits);
};
useEffect(() => {
fetchCredit();
}, [api]);
return (
credit !== null && (
<Button
onClick={fetchCredit}
variant="outline"
className="flex items-center space-x-2 text-muted-foreground"
>
<span>Credits: {credit}</span>
<IconRefresh />
</Button>
)
);
}

View File

@@ -0,0 +1,235 @@
import React, { useCallback, useContext, useEffect, useState } from "react";
import {
BaseEdge,
EdgeLabelRenderer,
EdgeProps,
useReactFlow,
XYPosition,
Edge,
Node,
} from "@xyflow/react";
import "./customedge.css";
import { X } from "lucide-react";
import { useBezierPath } from "@/hooks/useBezierPath";
import { FlowContext } from "./Flow";
export type CustomEdgeData = {
edgeColor: string;
sourcePos?: XYPosition;
isStatic?: boolean;
beadUp?: number;
beadDown?: number;
beadData?: any[];
};
type Bead = {
t: number;
targetT: number;
startTime: number;
};
export type CustomEdge = Edge<CustomEdgeData, "custom">;
export function CustomEdge({
id,
data,
selected,
sourceX,
sourceY,
targetX,
targetY,
markerEnd,
}: EdgeProps<CustomEdge>) {
const [isHovered, setIsHovered] = useState(false);
const [beads, setBeads] = useState<{
beads: Bead[];
created: number;
destroyed: number;
}>({ beads: [], created: 0, destroyed: 0 });
const { svgPath, length, getPointForT, getTForDistance } = useBezierPath(
sourceX - 5,
sourceY,
targetX + 3,
targetY,
);
const { deleteElements } = useReactFlow<Node, CustomEdge>();
const { visualizeBeads } = useContext(FlowContext) ?? {
visualizeBeads: "no",
};
const onEdgeRemoveClick = () => {
deleteElements({ edges: [{ id }] });
};
const animationDuration = 500; // Duration in milliseconds for bead to travel the curve
const beadDiameter = 12;
const deltaTime = 16;
const setTargetPositions = useCallback(
(beads: Bead[]) => {
const distanceBetween = Math.min(
(length - beadDiameter) / (beads.length + 1),
beadDiameter,
);
return beads.map((bead, index) => {
const distanceFromEnd = beadDiameter * 1.35;
const targetPosition = distanceBetween * index + distanceFromEnd;
const t = getTForDistance(-targetPosition);
return {
...bead,
t: visualizeBeads === "animate" ? bead.t : t,
targetT: t,
} as Bead;
});
},
[getTForDistance, length, visualizeBeads],
);
useEffect(() => {
if (data?.beadUp === 0 && data?.beadDown === 0) {
setBeads({ beads: [], created: 0, destroyed: 0 });
return;
}
const beadUp = data?.beadUp!;
// Add beads
setBeads(({ beads, created, destroyed }) => {
const newBeads = [];
for (let i = 0; i < beadUp - created; i++) {
newBeads.push({ t: 0, targetT: 0, startTime: Date.now() });
}
const b = setTargetPositions([...beads, ...newBeads]);
return { beads: b, created: beadUp, destroyed };
});
// Remove beads if not animating
if (visualizeBeads !== "animate") {
setBeads(({ beads, created, destroyed }) => {
let destroyedCount = 0;
const newBeads = beads
.map((bead) => ({ ...bead }))
.filter((bead, index) => {
const beadDown = data?.beadDown!;
// Remove always one less bead in case of static edge, so it stays at the connection point
const removeCount = beadDown - destroyed - (data?.isStatic ? 1 : 0);
if (bead.t >= bead.targetT && index < removeCount) {
destroyedCount++;
return false;
}
return true;
});
return {
beads: setTargetPositions(newBeads),
created,
destroyed: destroyed + destroyedCount,
};
});
return;
}
// Animate and remove beads
const interval = setInterval(() => {
setBeads(({ beads, created, destroyed }) => {
let destroyedCount = 0;
const newBeads = beads
.map((bead) => {
const progressIncrement = deltaTime / animationDuration;
const t = Math.min(
bead.t + bead.targetT * progressIncrement,
bead.targetT,
);
return {
...bead,
t,
};
})
.filter((bead, index) => {
const beadDown = data?.beadDown!;
// Remove always one less bead in case of static edge, so it stays at the connection point
const removeCount = beadDown - destroyed - (data?.isStatic ? 1 : 0);
if (bead.t >= bead.targetT && index < removeCount) {
destroyedCount++;
return false;
}
return true;
});
return {
beads: setTargetPositions(newBeads),
created,
destroyed: destroyed + destroyedCount,
};
});
}, deltaTime);
return () => clearInterval(interval);
}, [data, setTargetPositions, visualizeBeads]);
const middle = getPointForT(0.5);
return (
<>
<BaseEdge
path={svgPath}
markerEnd={markerEnd}
style={{
strokeWidth: (isHovered ? 3 : 2) + (data?.isStatic ? 0.5 : 0),
stroke:
(data?.edgeColor ?? "#555555") +
(selected || isHovered ? "" : "80"),
strokeDasharray: data?.isStatic ? "5 3" : "0",
}}
/>
<path
d={svgPath}
fill="none"
strokeOpacity={0}
strokeWidth={20}
className="react-flow__edge-interaction"
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
/>
<EdgeLabelRenderer>
<div
style={{
position: "absolute",
transform: `translate(-50%, -50%) translate(${middle.x}px,${middle.y}px)`,
pointerEvents: "all",
}}
className="edge-label-renderer"
>
<button
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className={`edge-label-button ${isHovered ? "visible" : ""}`}
onClick={onEdgeRemoveClick}
>
<X className="size-4" />
</button>
</div>
</EdgeLabelRenderer>
{beads.beads.map((bead, index) => {
const pos = getPointForT(bead.t);
return (
<circle
key={index}
cx={pos.x}
cy={pos.y}
r={beadDiameter / 2} // Bead radius
fill={data?.edgeColor ?? "#555555"}
/>
);
})}
</>
);
}

View File

@@ -0,0 +1,660 @@
import React, {
useState,
useEffect,
useCallback,
useRef,
useContext,
} from "react";
import { NodeProps, useReactFlow, Node, Edge } from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import "./customnode.css";
import InputModalComponent from "./InputModalComponent";
import OutputModalComponent from "./OutputModalComponent";
import {
BlockIORootSchema,
BlockIOStringSubSchema,
Category,
NodeExecutionResult,
BlockUIType,
BlockCost,
} from "@/lib/autogpt-server-api/types";
import { beautifyString, cn, setNestedProperty } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Copy, Trash2 } from "lucide-react";
import { history } from "./history";
import NodeHandle from "./NodeHandle";
import {
NodeGenericInputField,
NodeTextBoxInput,
} from "./node-input-components";
import SchemaTooltip from "./SchemaTooltip";
import { getPrimaryCategoryColor } from "@/lib/utils";
import { FlowContext } from "./Flow";
import { Badge } from "./ui/badge";
import DataTable from "./DataTable";
type ParsedKey = { key: string; index?: number };
export type ConnectionData = Array<{
edge_id: string;
source: string;
sourceHandle: string;
target: string;
targetHandle: string;
}>;
export type CustomNodeData = {
blockType: string;
blockCosts: BlockCost[];
title: string;
description: string;
categories: Category[];
inputSchema: BlockIORootSchema;
outputSchema: BlockIORootSchema;
hardcodedValues: { [key: string]: any };
connections: ConnectionData;
isOutputOpen: boolean;
status?: NodeExecutionResult["status"];
/** executionResults contains outputs across multiple executions
* with the last element being the most recent output */
executionResults?: {
execId: string;
data: NodeExecutionResult["output_data"];
}[];
block_id: string;
backend_id?: string;
errors?: { [key: string]: string };
isOutputStatic?: boolean;
uiType: BlockUIType;
};
export type CustomNode = Node<CustomNodeData, "custom">;
export function CustomNode({ data, id, width, height }: NodeProps<CustomNode>) {
const [isOutputOpen, setIsOutputOpen] = useState(data.isOutputOpen || false);
const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [activeKey, setActiveKey] = useState<string | null>(null);
const [inputModalValue, setInputModalValue] = useState<string>("");
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const { updateNodeData, deleteElements, addNodes, getNode } = useReactFlow<
CustomNode,
Edge
>();
const isInitialSetup = useRef(true);
const flowContext = useContext(FlowContext);
if (!flowContext) {
throw new Error("FlowContext consumer must be inside FlowEditor component");
}
const { setIsAnyModalOpen, getNextNodeId } = flowContext;
useEffect(() => {
if (data.executionResults || data.status) {
setIsOutputOpen(true);
}
}, [data.executionResults, data.status]);
useEffect(() => {
setIsOutputOpen(data.isOutputOpen);
}, [data.isOutputOpen]);
useEffect(() => {
setIsAnyModalOpen?.(isModalOpen || isOutputModalOpen);
}, [isModalOpen, isOutputModalOpen, data, setIsAnyModalOpen]);
useEffect(() => {
isInitialSetup.current = false;
}, []);
const setHardcodedValues = (values: any) => {
updateNodeData(id, { hardcodedValues: values });
};
const setErrors = (errors: { [key: string]: string }) => {
updateNodeData(id, { errors });
};
const toggleOutput = (checked: boolean) => {
setIsOutputOpen(checked);
};
const toggleAdvancedSettings = (checked: boolean) => {
setIsAdvancedOpen(checked);
};
const generateOutputHandles = (
schema: BlockIORootSchema,
nodeType: BlockUIType,
) => {
if (
!schema?.properties ||
nodeType === BlockUIType.OUTPUT ||
nodeType === BlockUIType.NOTE
)
return null;
const keys = Object.keys(schema.properties);
return keys.map((key) => (
<div key={key}>
<NodeHandle
keyName={key}
isConnected={isHandleConnected(key)}
schema={schema.properties[key]}
side="right"
/>
</div>
));
};
const generateInputHandles = (
schema: BlockIORootSchema,
nodeType: BlockUIType,
) => {
if (!schema?.properties) return null;
let keys = Object.entries(schema.properties);
switch (nodeType) {
case BlockUIType.INPUT:
// For INPUT blocks, dont include connection handles
return keys.map(([propKey, propSchema]) => {
const isRequired = data.inputSchema.required?.includes(propKey);
const isConnected = isHandleConnected(propKey);
const isAdvanced = propSchema.advanced;
return (
(isRequired || isAdvancedOpen || !isAdvanced) && (
<div key={propKey}>
<span className="text-m green -mb-1 text-gray-900">
{propSchema.title || beautifyString(propKey)}
</span>
<div key={propKey} onMouseOver={() => {}}>
{!isConnected && (
<NodeGenericInputField
className="mb-2 mt-1"
propKey={propKey}
propSchema={propSchema}
currentValue={getValue(propKey)}
connections={data.connections}
handleInputChange={handleInputChange}
handleInputClick={handleInputClick}
errors={data.errors ?? {}}
displayName={propSchema.title || beautifyString(propKey)}
/>
)}
</div>
</div>
)
);
});
case BlockUIType.NOTE:
// For NOTE blocks, don't render any input handles
const [noteKey, noteSchema] = keys[0];
return (
<div key={noteKey}>
<NodeTextBoxInput
className=""
selfKey={noteKey}
schema={noteSchema as BlockIOStringSubSchema}
value={getValue(noteKey)}
handleInputChange={handleInputChange}
handleInputClick={handleInputClick}
error={data.errors?.[noteKey] ?? ""}
displayName={noteSchema.title || beautifyString(noteKey)}
/>
</div>
);
case BlockUIType.OUTPUT:
// For OUTPUT blocks, only show the 'value' property
return keys.map(([propKey, propSchema]) => {
const isRequired = data.inputSchema.required?.includes(propKey);
const isConnected = isHandleConnected(propKey);
const isAdvanced = propSchema.advanced;
return (
(isRequired || isAdvancedOpen || !isAdvanced) && (
<div key={propKey} onMouseOver={() => {}}>
{propKey !== "value" ? (
<span className="text-m green -mb-1 text-gray-900">
{propSchema.title || beautifyString(propKey)}
</span>
) : (
<NodeHandle
keyName={propKey}
isConnected={isConnected}
isRequired={isRequired}
schema={propSchema}
side="left"
/>
)}
{!isConnected && (
<NodeGenericInputField
className="mb-2 mt-1"
propKey={propKey}
propSchema={propSchema}
currentValue={getValue(propKey)}
connections={data.connections}
handleInputChange={handleInputChange}
handleInputClick={handleInputClick}
errors={data.errors ?? {}}
displayName={propSchema.title || beautifyString(propKey)}
/>
)}
</div>
)
);
});
default:
return keys.map(([propKey, propSchema]) => {
const isRequired = data.inputSchema.required?.includes(propKey);
const isConnected = isHandleConnected(propKey);
const isAdvanced = propSchema.advanced;
return (
(isRequired || isAdvancedOpen || isConnected || !isAdvanced) && (
<div key={propKey} onMouseOver={() => {}}>
<NodeHandle
keyName={propKey}
isConnected={isConnected}
isRequired={isRequired}
schema={propSchema}
side="left"
/>
{!isConnected && (
<NodeGenericInputField
className="mb-2 mt-1"
propKey={propKey}
propSchema={propSchema}
currentValue={getValue(propKey)}
connections={data.connections}
handleInputChange={handleInputChange}
handleInputClick={handleInputClick}
errors={data.errors ?? {}}
displayName={propSchema.title || beautifyString(propKey)}
/>
)}
</div>
)
);
});
}
};
const handleInputChange = (path: string, value: any) => {
const keys = parseKeys(path);
const newValues = JSON.parse(JSON.stringify(data.hardcodedValues));
let current = newValues;
for (let i = 0; i < keys.length - 1; i++) {
const { key: currentKey, index } = keys[i];
if (index !== undefined) {
if (!current[currentKey]) current[currentKey] = [];
if (!current[currentKey][index]) current[currentKey][index] = {};
current = current[currentKey][index];
} else {
if (!current[currentKey]) current[currentKey] = {};
current = current[currentKey];
}
}
const lastKey = keys[keys.length - 1];
if (lastKey.index !== undefined) {
if (!current[lastKey.key]) current[lastKey.key] = [];
current[lastKey.key][lastKey.index] = value;
} else {
current[lastKey.key] = value;
}
// console.log(`Updating hardcoded values for node ${id}:`, newValues);
if (!isInitialSetup.current) {
history.push({
type: "UPDATE_INPUT",
payload: { nodeId: id, oldValues: data.hardcodedValues, newValues },
undo: () => setHardcodedValues(data.hardcodedValues),
redo: () => setHardcodedValues(newValues),
});
}
setHardcodedValues(newValues);
const errors = data.errors || {};
// Remove error with the same key
setNestedProperty(errors, path, null);
setErrors({ ...errors });
};
// Helper function to parse keys with array indices
//TODO move to utils
const parseKeys = (key: string): ParsedKey[] => {
const splits = key.split(/_@_|_#_|_\$_|\./);
const keys: ParsedKey[] = [];
let currentKey: string | null = null;
splits.forEach((split) => {
const isInteger = /^\d+$/.test(split);
if (!isInteger) {
if (currentKey !== null) {
keys.push({ key: currentKey });
}
currentKey = split;
} else {
if (currentKey !== null) {
keys.push({ key: currentKey, index: parseInt(split, 10) });
currentKey = null;
} else {
throw new Error("Invalid key format: array index without a key");
}
}
});
if (currentKey !== null) {
keys.push({ key: currentKey });
}
return keys;
};
const getValue = (key: string) => {
const keys = parseKeys(key);
return keys.reduce((acc, k) => {
if (acc === undefined) return undefined;
if (k.index !== undefined) {
return Array.isArray(acc[k.key]) ? acc[k.key][k.index] : undefined;
}
return acc[k.key];
}, data.hardcodedValues as any);
};
const isHandleConnected = (key: string) => {
return (
data.connections &&
data.connections.some((conn: any) => {
if (typeof conn === "string") {
const [source, target] = conn.split(" -> ");
return (
(target.includes(key) && target.includes(data.title)) ||
(source.includes(key) && source.includes(data.title))
);
}
return (
(conn.target === id && conn.targetHandle === key) ||
(conn.source === id && conn.sourceHandle === key)
);
})
);
};
const handleInputClick = (key: string) => {
console.log(`Opening modal for key: ${key}`);
setActiveKey(key);
const value = getValue(key);
setInputModalValue(
typeof value === "object" ? JSON.stringify(value, null, 2) : value,
);
setIsModalOpen(true);
};
const handleModalSave = (value: string) => {
if (activeKey) {
try {
const parsedValue = JSON.parse(value);
handleInputChange(activeKey, parsedValue);
} catch (error) {
handleInputChange(activeKey, value);
}
}
setIsModalOpen(false);
setActiveKey(null);
};
const handleOutputClick = () => {
setIsOutputModalOpen(true);
};
const handleHovered = () => {
setIsHovered(true);
};
const handleMouseLeave = () => {
setIsHovered(false);
};
const deleteNode = useCallback(() => {
console.log("Deleting node:", id);
// Remove the node
deleteElements({ nodes: [{ id }] });
}, [id, deleteElements]);
const copyNode = useCallback(() => {
const newId = getNextNodeId();
const currentNode = getNode(id);
if (!currentNode) {
console.error("Cannot copy node: current node not found");
return;
}
const verticalOffset = height ?? 100;
const newNode: CustomNode = {
id: newId,
type: currentNode.type,
position: {
x: currentNode.position.x,
y: currentNode.position.y - verticalOffset - 20,
},
data: {
...data,
title: `${data.title} (Copy)`,
block_id: data.block_id,
connections: [],
isOutputOpen: false,
},
};
addNodes(newNode);
history.push({
type: "ADD_NODE",
payload: { node: newNode },
undo: () => deleteElements({ nodes: [{ id: newId }] }),
redo: () => addNodes(newNode),
});
}, [id, data, height, addNodes, deleteElements, getNode, getNextNodeId]);
const hasConfigErrors =
data.errors &&
Object.entries(data.errors).some(([_, value]) => value !== null);
const outputData = data.executionResults?.at(-1)?.data;
const hasOutputError =
typeof outputData === "object" &&
outputData !== null &&
"error" in outputData;
useEffect(() => {
if (hasConfigErrors) {
const filteredErrors = Object.fromEntries(
Object.entries(data.errors || {}).filter(
([_, value]) => value !== null,
),
);
console.error(
"Block configuration errors for",
data.title,
":",
filteredErrors,
);
}
if (hasOutputError) {
console.error(
"Block output contains error for",
data.title,
":",
outputData.error,
);
}
}, [hasConfigErrors, hasOutputError, data.errors, outputData, data.title]);
const blockClasses = [
"custom-node",
"dark-theme",
"rounded-xl",
"border",
"bg-white/[.9]",
"shadow-md",
]
.filter(Boolean)
.join(" ");
const errorClass =
hasConfigErrors || hasOutputError ? "border-red-500 border-2" : "";
const statusClass =
hasConfigErrors || hasOutputError
? "failed"
: (data.status?.toLowerCase() ?? "");
const hasAdvancedFields =
data.inputSchema &&
Object.entries(data.inputSchema.properties).some(([key, value]) => {
return (
value.advanced === true && !data.inputSchema.required?.includes(key)
);
});
const inputValues = data.hardcodedValues;
const blockCost =
data.blockCosts &&
data.blockCosts.find((cost) =>
Object.entries(cost.cost_filter).every(
// Undefined, null, or empty values are considered equal
([key, value]) =>
value === inputValues[key] || (!value && !inputValues[key]),
),
);
console.debug(`Block cost ${inputValues}|${data.blockCosts}=${blockCost}`);
return (
<div
className={`${data.uiType === BlockUIType.NOTE ? "w-[300px]" : "w-[500px]"} ${blockClasses} ${errorClass} ${statusClass} ${data.uiType === BlockUIType.NOTE ? "bg-yellow-100" : "bg-white"}`}
onMouseEnter={handleHovered}
onMouseLeave={handleMouseLeave}
data-id={`custom-node-${id}`}
>
<div
className={`mb-2 p-3 ${data.uiType === BlockUIType.NOTE ? "bg-yellow-100" : getPrimaryCategoryColor(data.categories)} rounded-t-xl`}
>
<div className="flex items-center justify-between">
<div className="font-roboto p-3 text-lg font-semibold">
{beautifyString(
data.blockType?.replace(/Block$/, "") || data.title,
)}
</div>
<SchemaTooltip description={data.description} />
</div>
<div className="flex gap-[5px]">
{isHovered && (
<>
<Button
variant="outline"
size="icon"
onClick={copyNode}
title="Copy node"
>
<Copy size={18} />
</Button>
<Button
variant="outline"
size="icon"
onClick={deleteNode}
title="Delete node"
>
<Trash2 size={18} />
</Button>
</>
)}
</div>
</div>
{blockCost && (
<div className="p-3 text-right font-semibold">
Cost: {blockCost.cost_amount} / {blockCost.cost_type}
</div>
)}
{data.uiType !== BlockUIType.NOTE ? (
<div className="flex items-start justify-between p-3">
<div>
{data.inputSchema &&
generateInputHandles(data.inputSchema, data.uiType)}
</div>
<div className="flex-none">
{data.outputSchema &&
generateOutputHandles(data.outputSchema, data.uiType)}
</div>
</div>
) : (
<div>
{data.inputSchema &&
generateInputHandles(data.inputSchema, data.uiType)}
</div>
)}
{isOutputOpen && data.uiType !== BlockUIType.NOTE && (
<div
data-id="latest-output"
className="nodrag m-3 break-words rounded-md border-[1.5px] p-2"
>
{(data.executionResults?.length ?? 0) > 0 ? (
<>
<DataTable
title="Latest Output"
truncateLongData
data={data.executionResults!.at(-1)?.data || {}}
/>
<div className="flex justify-end">
<Button variant="ghost" onClick={handleOutputClick}>
View More
</Button>
</div>
</>
) : (
<span>No outputs yet</span>
)}
</div>
)}
{data.uiType !== BlockUIType.NOTE && (
<div className="mt-2.5 flex items-center pb-4 pl-4">
<Switch checked={isOutputOpen} onCheckedChange={toggleOutput} />
<span className="m-1 mr-4">Output</span>
{hasAdvancedFields && (
<>
<Switch onCheckedChange={toggleAdvancedSettings} />
<span className="m-1">Advanced</span>
</>
)}
{data.status && (
<Badge
variant="outline"
data-id={`badge-${id}-${data.status}`}
className={cn(data.status.toLowerCase(), "ml-auto mr-5")}
>
{data.status}
</Badge>
)}
</div>
)}
<InputModalComponent
title={activeKey ? `Enter ${beautifyString(activeKey)}` : undefined}
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onSave={handleModalSave}
defaultValue={inputModalValue}
key={activeKey}
/>
<OutputModalComponent
isOpen={isOutputModalOpen}
onClose={() => setIsOutputModalOpen(false)}
executionResults={data.executionResults?.toReversed() || []}
/>
</div>
);
}

View File

@@ -0,0 +1,92 @@
import { beautifyString } from "@/lib/utils";
import { Button } from "./ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "./ui/table";
import { Clipboard } from "lucide-react";
import { useToast } from "./ui/use-toast";
type DataTableProps = {
title?: string;
truncateLongData?: boolean;
data: { [key: string]: Array<any> };
};
export default function DataTable({
title,
truncateLongData,
data,
}: DataTableProps) {
const { toast } = useToast();
const maxChars = 100;
const copyData = (pin: string, data: string) => {
navigator.clipboard.writeText(data).then(() => {
toast({
title: `"${pin}" data copied to clipboard!`,
duration: 2000,
});
});
};
return (
<>
{title && <strong className="mt-2 flex justify-center">{title}</strong>}
<Table className="cursor-default select-text">
<TableHeader>
<TableRow>
<TableHead>Pin</TableHead>
<TableHead>Data</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Object.entries(data).map(([key, value]) => (
<TableRow className="group" key={key}>
<TableCell className="cursor-text">
{beautifyString(key)}
</TableCell>
<TableCell className="cursor-text">
<div className="flex min-h-9 items-center">
<Button
className="absolute right-1 top-auto m-1 hidden p-2 group-hover:block"
variant="outline"
size="icon"
onClick={() =>
copyData(
beautifyString(key),
value
.map((i) =>
typeof i === "object"
? JSON.stringify(i)
: String(i),
)
.join(", "),
)
}
title="Copy Data"
>
<Clipboard size={18} />
</Button>
{value
.map((i) => {
const text =
typeof i === "object" ? JSON.stringify(i) : String(i);
return truncateLongData && text.length > maxChars
? text.slice(0, maxChars) + "..."
: text;
})
.join(", ")}
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</>
);
}

View File

@@ -0,0 +1,636 @@
"use client";
import React, {
useState,
useCallback,
useEffect,
useRef,
MouseEvent,
createContext,
} from "react";
import {
ReactFlow,
ReactFlowProvider,
Controls,
Background,
Node,
OnConnect,
Connection,
MarkerType,
NodeChange,
EdgeChange,
useReactFlow,
applyEdgeChanges,
applyNodeChanges,
useViewport,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { CustomNode } from "./CustomNode";
import "./flow.css";
import { Link } from "@/lib/autogpt-server-api";
import { getTypeColor, filterBlocksByType } from "@/lib/utils";
import { history } from "./history";
import { CustomEdge } from "./CustomEdge";
import ConnectionLine from "./ConnectionLine";
import { Control, ControlPanel } from "@/components/edit/control/ControlPanel";
import { SaveControl } from "@/components/edit/control/SaveControl";
import { BlocksControl } from "@/components/edit/control/BlocksControl";
import {
IconPlay,
IconUndo2,
IconRedo2,
IconSquare,
IconOutput,
} from "@/components/ui/icons";
import { startTutorial } from "./tutorial";
import useAgentGraph from "@/hooks/useAgentGraph";
import { v4 as uuidv4 } from "uuid";
import { useRouter, usePathname, useSearchParams } from "next/navigation";
import { LogOut } from "lucide-react";
import RunnerUIWrapper, {
RunnerUIWrapperRef,
} from "@/components/RunnerUIWrapper";
// This is for the history, this is the minimum distance a block must move before it is logged
// It helps to prevent spamming the history with small movements especially when pressing on a input in a block
const MINIMUM_MOVE_BEFORE_LOG = 50;
type FlowContextType = {
visualizeBeads: "no" | "static" | "animate";
setIsAnyModalOpen: (isOpen: boolean) => void;
getNextNodeId: () => string;
};
export const FlowContext = createContext<FlowContextType | null>(null);
const FlowEditor: React.FC<{
flowID?: string;
template?: boolean;
className?: string;
}> = ({ flowID, template, className }) => {
const { addNodes, addEdges, getNode, deleteElements, updateNode } =
useReactFlow<CustomNode, CustomEdge>();
const [nodeId, setNodeId] = useState<number>(1);
const [copiedNodes, setCopiedNodes] = useState<CustomNode[]>([]);
const [copiedEdges, setCopiedEdges] = useState<CustomEdge[]>([]);
const [isAnyModalOpen, setIsAnyModalOpen] = useState(false);
const [visualizeBeads, setVisualizeBeads] = useState<
"no" | "static" | "animate"
>("animate");
const {
agentName,
setAgentName,
agentDescription,
setAgentDescription,
savedAgent,
availableNodes,
getOutputType,
requestSave,
requestSaveAndRun,
requestStopRun,
isRunning,
nodes,
setNodes,
edges,
setEdges,
} = useAgentGraph(flowID, template, visualizeBeads !== "no");
const router = useRouter();
const pathname = usePathname();
const initialPositionRef = useRef<{
[key: string]: { x: number; y: number };
}>({});
const isDragging = useRef(false);
// State to control if tutorial has started
const [tutorialStarted, setTutorialStarted] = useState(false);
// State to control if blocks menu should be pinned open
const [pinBlocksPopover, setPinBlocksPopover] = useState(false);
const runnerUIRef = useRef<RunnerUIWrapperRef>(null);
useEffect(() => {
const params = new URLSearchParams(window.location.search);
// If resetting tutorial
if (params.get("resetTutorial") === "true") {
localStorage.removeItem("shepherd-tour"); // Clear tutorial flag
router.push(pathname);
} else {
// Otherwise, start tutorial if conditions are met
const shouldStartTutorial = !localStorage.getItem("shepherd-tour");
if (
shouldStartTutorial &&
availableNodes.length > 0 &&
!tutorialStarted
) {
startTutorial(setPinBlocksPopover);
setTutorialStarted(true);
localStorage.setItem("shepherd-tour", "yes");
}
}
}, [availableNodes, tutorialStarted, router, pathname]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
const isUndo =
(isMac ? event.metaKey : event.ctrlKey) && event.key === "z";
const isRedo =
(isMac ? event.metaKey : event.ctrlKey) &&
(event.key === "y" || (event.shiftKey && event.key === "Z"));
if (isUndo) {
event.preventDefault();
handleUndo();
}
if (isRedo) {
event.preventDefault();
handleRedo();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, []);
const onNodeDragStart = (_: MouseEvent, node: Node) => {
initialPositionRef.current[node.id] = { ...node.position };
isDragging.current = true;
};
const onNodeDragEnd = (_: MouseEvent, node: Node | null) => {
if (!node) return;
isDragging.current = false;
const oldPosition = initialPositionRef.current[node.id];
const newPosition = node.position;
// Calculate the movement distance
if (!oldPosition || !newPosition) return;
const distanceMoved = Math.sqrt(
Math.pow(newPosition.x - oldPosition.x, 2) +
Math.pow(newPosition.y - oldPosition.y, 2),
);
if (distanceMoved > MINIMUM_MOVE_BEFORE_LOG) {
// Minimum movement threshold
history.push({
type: "UPDATE_NODE_POSITION",
payload: { nodeId: node.id, oldPosition, newPosition },
undo: () => updateNode(node.id, { position: oldPosition }),
redo: () => updateNode(node.id, { position: newPosition }),
});
}
delete initialPositionRef.current[node.id];
};
// Function to clear status, output, and close the output info dropdown of all nodes
// and reset data beads on edges
const clearNodesStatusAndOutput = useCallback(() => {
setNodes((nds) => {
const newNodes = nds.map((node) => ({
...node,
data: {
...node.data,
status: undefined,
isOutputOpen: false,
},
}));
return newNodes;
});
}, [setNodes]);
const onNodesChange = useCallback(
(nodeChanges: NodeChange<CustomNode>[]) => {
// Persist the changes
setNodes((prev) => applyNodeChanges(nodeChanges, prev));
// Remove all edges that were connected to deleted nodes
nodeChanges
.filter((change) => change.type === "remove")
.forEach((deletedNode) => {
const nodeID = deletedNode.id;
const deletedNodeData = nodes.find((node) => node.id === nodeID);
if (deletedNodeData) {
history.push({
type: "DELETE_NODE",
payload: { node: deletedNodeData },
undo: () => addNodes(deletedNodeData),
redo: () => deleteElements({ nodes: [{ id: nodeID }] }),
});
}
const connectedEdges = edges.filter((edge) =>
[edge.source, edge.target].includes(nodeID),
);
deleteElements({
edges: connectedEdges.map((edge) => ({ id: edge.id })),
});
});
},
[deleteElements, setNodes, nodes, edges, addNodes],
);
const formatEdgeID = useCallback((conn: Link | Connection): string => {
if ("sink_id" in conn) {
return `${conn.source_id}_${conn.source_name}_${conn.sink_id}_${conn.sink_name}`;
} else {
return `${conn.source}_${conn.sourceHandle}_${conn.target}_${conn.targetHandle}`;
}
}, []);
const onConnect: OnConnect = useCallback(
(connection: Connection) => {
// Check if this exact connection already exists
const existingConnection = edges.find(
(edge) =>
edge.source === connection.source &&
edge.target === connection.target &&
edge.sourceHandle === connection.sourceHandle &&
edge.targetHandle === connection.targetHandle,
);
if (existingConnection) {
console.warn("This exact connection already exists.");
return;
}
const edgeColor = getTypeColor(
getOutputType(nodes, connection.source!, connection.sourceHandle!),
);
const sourceNode = getNode(connection.source!);
const newEdge: CustomEdge = {
id: formatEdgeID(connection),
type: "custom",
markerEnd: {
type: MarkerType.ArrowClosed,
strokeWidth: 2,
color: edgeColor,
},
data: {
edgeColor,
sourcePos: sourceNode!.position,
isStatic: sourceNode!.data.isOutputStatic,
},
...connection,
source: connection.source!,
target: connection.target!,
};
addEdges(newEdge);
history.push({
type: "ADD_EDGE",
payload: { edge: newEdge },
undo: () => {
deleteElements({ edges: [{ id: newEdge.id }] });
},
redo: () => {
addEdges(newEdge);
},
});
clearNodesStatusAndOutput(); // Clear status and output on connection change
},
[
getNode,
addEdges,
deleteElements,
clearNodesStatusAndOutput,
nodes,
edges,
formatEdgeID,
getOutputType,
],
);
const onEdgesChange = useCallback(
(edgeChanges: EdgeChange<CustomEdge>[]) => {
// Persist the changes
setEdges((prev) => applyEdgeChanges(edgeChanges, prev));
// Propagate edge changes to node data
const addedEdges = edgeChanges.filter((change) => change.type === "add"),
replaceEdges = edgeChanges.filter(
(change) => change.type === "replace",
),
removedEdges = edgeChanges.filter((change) => change.type === "remove"),
selectedEdges = edgeChanges.filter(
(change) => change.type === "select",
);
if (addedEdges.length > 0 || removedEdges.length > 0) {
setNodes((nds) => {
const newNodes = nds.map((node) => ({
...node,
data: {
...node.data,
connections: [
// Remove node connections for deleted edges
...node.data.connections.filter(
(conn) =>
!removedEdges.some(
(removedEdge) => removedEdge.id === conn.edge_id,
),
),
// Add node connections for added edges
...addedEdges.map((addedEdge) => ({
edge_id: addedEdge.item.id,
source: addedEdge.item.source,
target: addedEdge.item.target,
sourceHandle: addedEdge.item.sourceHandle!,
targetHandle: addedEdge.item.targetHandle!,
})),
],
},
}));
return newNodes;
});
if (removedEdges.length > 0) {
clearNodesStatusAndOutput(); // Clear status and output on edge deletion
}
}
if (replaceEdges.length > 0) {
// Reset node connections for all edges
console.warn(
"useReactFlow().setRootEdges was used to overwrite all edges. " +
"Use addEdges, deleteElements, or reconnectEdge for incremental changes.",
replaceEdges,
);
setNodes((nds) =>
nds.map((node) => ({
...node,
data: {
...node.data,
connections: [
...replaceEdges.map((replaceEdge) => ({
edge_id: replaceEdge.item.id,
source: replaceEdge.item.source,
target: replaceEdge.item.target,
sourceHandle: replaceEdge.item.sourceHandle!,
targetHandle: replaceEdge.item.targetHandle!,
})),
],
},
})),
);
clearNodesStatusAndOutput();
}
},
[setNodes, clearNodesStatusAndOutput, setEdges],
);
const getNextNodeId = useCallback(() => {
return uuidv4();
}, []);
const { x, y, zoom } = useViewport();
const addNode = useCallback(
(blockId: string, nodeType: string) => {
const nodeSchema = availableNodes.find((node) => node.id === blockId);
if (!nodeSchema) {
console.error(`Schema not found for block ID: ${blockId}`);
return;
}
// Calculate the center of the viewport considering zoom
const viewportCenter = {
x: (window.innerWidth / 2 - x) / zoom,
y: (window.innerHeight / 2 - y) / zoom,
};
const newNode: CustomNode = {
id: nodeId.toString(),
type: "custom",
position: viewportCenter, // Set the position to the calculated viewport center
data: {
blockType: nodeType,
blockCosts: nodeSchema.costs,
title: `${nodeType} ${nodeId}`,
description: nodeSchema.description,
categories: nodeSchema.categories,
inputSchema: nodeSchema.inputSchema,
outputSchema: nodeSchema.outputSchema,
hardcodedValues: {},
connections: [],
isOutputOpen: false,
block_id: blockId,
isOutputStatic: nodeSchema.staticOutput,
uiType: nodeSchema.uiType,
},
};
addNodes(newNode);
setNodeId((prevId) => prevId + 1);
clearNodesStatusAndOutput(); // Clear status and output when a new node is added
history.push({
type: "ADD_NODE",
payload: { node: newNode.data },
undo: () => deleteElements({ nodes: [{ id: newNode.id }] }),
redo: () => addNodes(newNode),
});
},
[
nodeId,
availableNodes,
addNodes,
deleteElements,
clearNodesStatusAndOutput,
x,
y,
zoom,
],
);
const handleUndo = () => {
history.undo();
};
const handleRedo = () => {
history.redo();
};
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
// Prevent copy/paste if any modal is open or if the focus is on an input element
const activeElement = document.activeElement;
const isInputField =
activeElement?.tagName === "INPUT" ||
activeElement?.tagName === "TEXTAREA" ||
activeElement?.getAttribute("contenteditable") === "true";
if (isAnyModalOpen || isInputField) return;
if (event.ctrlKey || event.metaKey) {
if (event.key === "c" || event.key === "C") {
// Copy selected nodes
const selectedNodes = nodes.filter((node) => node.selected);
const selectedEdges = edges.filter((edge) => edge.selected);
setCopiedNodes(selectedNodes);
setCopiedEdges(selectedEdges);
}
if (event.key === "v" || event.key === "V") {
// Paste copied nodes
if (copiedNodes.length > 0) {
const oldToNewNodeIDMap: Record<string, string> = {};
const pastedNodes = copiedNodes.map((node, index) => {
const newNodeId = (nodeId + index).toString();
oldToNewNodeIDMap[node.id] = newNodeId;
return {
...node,
id: newNodeId,
position: {
x: node.position.x + 20, // Offset pasted nodes
y: node.position.y + 20,
},
data: {
...node.data,
status: undefined, // Reset status
executionResults: undefined, // Clear output data
},
};
});
setNodes((existingNodes) =>
// Deselect copied nodes
existingNodes.map((node) => ({ ...node, selected: false })),
);
addNodes(pastedNodes);
setNodeId((prevId) => prevId + copiedNodes.length);
const pastedEdges = copiedEdges.map((edge) => {
const newSourceId = oldToNewNodeIDMap[edge.source] ?? edge.source;
const newTargetId = oldToNewNodeIDMap[edge.target] ?? edge.target;
return {
...edge,
id: `${newSourceId}_${edge.sourceHandle}_${newTargetId}_${edge.targetHandle}_${Date.now()}`,
source: newSourceId,
target: newTargetId,
};
});
addEdges(pastedEdges);
}
}
}
},
[
isAnyModalOpen,
nodes,
edges,
copiedNodes,
setNodes,
addNodes,
copiedEdges,
addEdges,
nodeId,
],
);
useEffect(() => {
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [handleKeyDown]);
const onNodesDelete = useCallback(() => {
clearNodesStatusAndOutput();
}, [clearNodesStatusAndOutput]);
const editorControls: Control[] = [
{
label: "Undo",
icon: <IconUndo2 />,
onClick: handleUndo,
},
{
label: "Redo",
icon: <IconRedo2 />,
onClick: handleRedo,
},
{
label: !savedAgent
? "Please save the agent to run"
: !isRunning
? "Run"
: "Stop",
icon: !isRunning ? <IconPlay /> : <IconSquare />,
onClick: !isRunning
? () => runnerUIRef.current?.runOrOpenInput()
: requestStopRun,
disabled: !savedAgent,
},
{
label: "Runner Output",
icon: <LogOut size={18} strokeWidth={1.8} />,
onClick: () => runnerUIRef.current?.openRunnerOutput(),
},
];
return (
<FlowContext.Provider
value={{ visualizeBeads, setIsAnyModalOpen, getNextNodeId }}
>
<div className={className}>
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={{ custom: CustomNode }}
edgeTypes={{ custom: CustomEdge }}
connectionLineComponent={ConnectionLine}
onConnect={onConnect}
onNodesChange={onNodesChange}
onNodesDelete={onNodesDelete}
onEdgesChange={onEdgesChange}
onNodeDragStop={onNodeDragEnd}
onNodeDragStart={onNodeDragStart}
deleteKeyCode={["Backspace", "Delete"]}
minZoom={0.2}
maxZoom={2}
>
<Controls />
<Background />
<ControlPanel className="absolute z-10" controls={editorControls}>
<BlocksControl
pinBlocksPopover={pinBlocksPopover} // Pass the state to BlocksControl
blocks={availableNodes}
addBlock={addNode}
/>
<SaveControl
agentMeta={savedAgent}
onSave={(isTemplate) => requestSave(isTemplate ?? false)}
agentDescription={agentDescription}
onDescriptionChange={setAgentDescription}
agentName={agentName}
onNameChange={setAgentName}
/>
</ControlPanel>
</ReactFlow>
</div>
<RunnerUIWrapper
ref={runnerUIRef}
nodes={nodes}
setNodes={setNodes}
isRunning={isRunning}
requestSaveAndRun={requestSaveAndRun}
/>
</FlowContext.Provider>
);
};
const WrappedFlowEditor: typeof FlowEditor = (props) => (
<ReactFlowProvider>
<FlowEditor {...props} />
</ReactFlowProvider>
);
export default WrappedFlowEditor;

View File

@@ -0,0 +1,107 @@
import React, { FC, useEffect, useState } from "react";
import { Button } from "./ui/button";
import { Textarea } from "./ui/textarea";
import { Maximize2, Minimize2, Clipboard } from "lucide-react";
import { createPortal } from "react-dom";
import { toast } from "./ui/use-toast";
interface ModalProps {
isOpen: boolean;
onClose: () => void;
onSave: (value: string) => void;
title?: string;
defaultValue: string;
}
const InputModalComponent: FC<ModalProps> = ({
isOpen,
onClose,
onSave,
title,
defaultValue,
}) => {
const [tempValue, setTempValue] = useState(defaultValue);
const [isMaximized, setIsMaximized] = useState(false);
useEffect(() => {
if (isOpen) {
setTempValue(defaultValue);
setIsMaximized(false);
}
}, [isOpen, defaultValue]);
const handleSave = () => {
onSave(tempValue);
onClose();
};
const toggleSize = () => {
setIsMaximized(!isMaximized);
};
const copyValue = () => {
navigator.clipboard.writeText(tempValue).then(() => {
toast({
title: "Input value copied to clipboard!",
duration: 2000,
});
});
};
if (!isOpen) {
return null;
}
const modalContent = (
<div
id="modal-content"
className={`fixed rounded-lg border-[1.5px] bg-white p-5 ${
isMaximized ? "inset-[128px] flex flex-col" : `w-[90%] max-w-[800px]`
}`}
>
<h2 className="mb-4 text-center text-lg font-semibold">
{title || "Enter input text"}
</h2>
<div className="nowheel relative flex-grow">
<Textarea
className="h-full min-h-[200px] w-full resize-none"
value={tempValue}
onChange={(e) => setTempValue(e.target.value)}
/>
<div className="absolute bottom-2 right-2 flex space-x-2">
<Button onClick={copyValue} size="icon" variant="outline">
<Clipboard size={18} />
</Button>
<Button onClick={toggleSize} size="icon" variant="outline">
{isMaximized ? <Minimize2 size={18} /> : <Maximize2 size={18} />}
</Button>
</div>
</div>
<div className="mt-4 flex justify-end space-x-2">
<Button onClick={onClose} variant="outline">
Cancel
</Button>
<Button onClick={handleSave}>Save</Button>
</div>
</div>
);
return (
<>
{isMaximized ? (
createPortal(
<div className="fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
{modalContent}
</div>,
document.body,
)
) : (
<div className="nodrag fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
{modalContent}
</div>
)}
</>
);
};
export default InputModalComponent;

View File

@@ -0,0 +1,117 @@
import Link from "next/link";
import { Button } from "@/components/ui/button";
import React from "react";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import Image from "next/image";
import getServerUser from "@/hooks/getServerUser";
import ProfileDropdown from "./ProfileDropdown";
import {
IconCircleUser,
IconMenu,
IconPackage2,
IconRefresh,
IconSquareActivity,
IconWorkFlow,
} from "@/components/ui/icons";
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
import CreditButton from "@/components/CreditButton";
export async function NavBar() {
const isAvailable = Boolean(
process.env.NEXT_PUBLIC_SUPABASE_URL &&
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
);
const { user } = await getServerUser();
return (
<header className="sticky top-0 z-50 flex h-16 items-center gap-4 border-b bg-background px-4 md:px-6">
<div className="flex flex-1 items-center gap-4">
<Sheet>
<SheetTrigger asChild>
<Button
variant="outline"
size="icon"
className="shrink-0 md:hidden"
>
<IconMenu />
<span className="sr-only">Toggle navigation menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left">
<nav className="grid gap-6 text-lg font-medium">
<Link
href="/"
className="flex flex-row gap-2 text-muted-foreground hover:text-foreground"
>
<IconSquareActivity /> Monitor
</Link>
<Link
href="/build"
className="flex flex-row gap-2 text-muted-foreground hover:text-foreground"
>
<IconWorkFlow /> Build
</Link>
<Link
href="/marketplace"
className="flex flex-row gap-2 text-muted-foreground hover:text-foreground"
>
<IconPackage2 /> Marketplace
</Link>
</nav>
</SheetContent>
</Sheet>
<nav className="hidden md:flex md:flex-row md:items-center md:gap-5 lg:gap-6">
<Link
href="/"
className="flex flex-row items-center gap-2 text-muted-foreground hover:text-foreground"
>
<IconSquareActivity /> Monitor
</Link>
<Link
href="/build"
className="flex flex-row items-center gap-2 text-muted-foreground hover:text-foreground"
>
<IconWorkFlow /> Build
</Link>
<Link
href="/marketplace"
className="flex flex-row items-center gap-2 text-muted-foreground hover:text-foreground"
>
<IconPackage2 /> Marketplace
</Link>
</nav>
</div>
<div className="relative flex flex-1 justify-center">
<a
className="pointer-events-auto flex place-items-center gap-2"
href="https://news.agpt.co/"
target="_blank"
rel="noopener noreferrer"
>
By{" "}
<Image
src="/AUTOgpt_Logo_dark.png"
alt="AutoGPT Logo"
width={100}
height={20}
priority
/>
</a>
</div>
<div className="flex flex-1 items-center justify-end gap-4">
{isAvailable && user && <CreditButton />}
{isAvailable && !user && (
<Link
href="/login"
className="flex flex-row items-center gap-2 text-muted-foreground hover:text-foreground"
>
Log In
<IconCircleUser />
</Link>
)}
{isAvailable && user && <ProfileDropdown />}
</div>
</header>
);
}

View File

@@ -0,0 +1,85 @@
import { BlockIOSubSchema } from "@/lib/autogpt-server-api/types";
import { beautifyString, getTypeBgColor, getTypeTextColor } from "@/lib/utils";
import { FC } from "react";
import { Handle, Position } from "@xyflow/react";
import SchemaTooltip from "./SchemaTooltip";
type HandleProps = {
keyName: string;
schema: BlockIOSubSchema;
isConnected: boolean;
isRequired?: boolean;
side: "left" | "right";
};
const NodeHandle: FC<HandleProps> = ({
keyName,
schema,
isConnected,
isRequired,
side,
}) => {
const typeName: Record<string, string> = {
string: "text",
number: "number",
boolean: "true/false",
object: "object",
array: "list",
null: "null",
};
const typeClass = `text-sm ${getTypeTextColor(schema.type || "any")} ${side === "left" ? "text-left" : "text-right"}`;
const label = (
<div className="flex flex-grow flex-col">
<span className="text-m green -mb-1 text-gray-900">
{schema.title || beautifyString(keyName)}
{isRequired ? "*" : ""}
</span>
<span className={typeClass}>{typeName[schema.type] || "any"}</span>
</div>
);
const dot = (
<div
className={`m-1 h-4 w-4 border-2 bg-white ${isConnected ? getTypeBgColor(schema.type || "any") : "border-gray-300"} rounded-full transition-colors duration-100 group-hover:bg-gray-300`}
/>
);
if (side === "left") {
return (
<div key={keyName} className="handle-container">
<Handle
type="target"
position={Position.Left}
id={keyName}
className="background-color: white; border: 2px solid black; width: 15px; height: 15px; border-radius: 50%; bottom: -7px; left: 20%; group -ml-[26px]"
>
<div className="pointer-events-none flex items-center">
{dot}
{label}
</div>
</Handle>
<SchemaTooltip description={schema.description} />
</div>
);
} else {
return (
<div key={keyName} className="handle-container justify-end">
<Handle
type="source"
position={Position.Right}
id={keyName}
className="group -mr-[26px]"
>
<div className="pointer-events-none flex items-center">
{label}
{dot}
</div>
</Handle>
</div>
);
}
};
export default NodeHandle;

View File

@@ -0,0 +1,45 @@
import React, { FC } from "react";
import { Button } from "./ui/button";
import { NodeExecutionResult } from "@/lib/autogpt-server-api/types";
import DataTable from "./DataTable";
import { Separator } from "@/components/ui/separator";
interface OutputModalProps {
isOpen: boolean;
onClose: () => void;
executionResults: {
execId: string;
data: NodeExecutionResult["output_data"];
}[];
}
const OutputModalComponent: FC<OutputModalProps> = ({
isOpen,
onClose,
executionResults,
}) => {
if (!isOpen) {
return null;
}
return (
<div className="nodrag nowheel fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
<div className="w-[500px] max-w-[90%] rounded-lg border-[1.5px] bg-white p-5">
<strong>Output Data History</strong>
<div className="my-2 max-h-[384px] flex-grow overflow-y-auto rounded-md border-[1.5px] p-2">
{executionResults.map((data, i) => (
<>
<DataTable key={i} title={data.execId} data={data.data} />
<Separator />
</>
))}
</div>
<div className="mt-2.5 flex justify-end gap-2.5">
<Button onClick={onClose}>Close</Button>
</div>
</div>
</div>
);
};
export default OutputModalComponent;

View File

@@ -0,0 +1,54 @@
import { forwardRef, useState } from "react";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input, InputProps } from "@/components/ui/input";
import { cn } from "@/lib/utils";
const PasswordInput = forwardRef<HTMLInputElement, InputProps>(
({ className, ...props }, ref) => {
const [showPassword, setShowPassword] = useState(false);
const disabled =
props.value === "" || props.value === undefined || props.disabled;
return (
<div className="relative">
<Input
type={showPassword ? "text" : "password"}
className={cn("hide-password-toggle pr-10", className)}
ref={ref}
{...props}
/>
<Button
type="button"
variant="ghost"
size="sm"
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
onClick={() => setShowPassword((prev) => !prev)}
disabled={disabled}
>
{showPassword && !disabled ? (
<EyeIcon className="h-4 w-4" aria-hidden="true" />
) : (
<EyeOffIcon className="h-4 w-4" aria-hidden="true" />
)}
<span className="sr-only">
{showPassword ? "Hide password" : "Show password"}
</span>
</Button>
{/* hides browsers password toggles */}
<style>{`
.hide-password-toggle::-ms-reveal,
.hide-password-toggle::-ms-clear {
visibility: hidden;
pointer-events: none;
display: none;
}
`}</style>
</div>
);
},
);
PasswordInput.displayName = "PasswordInput";
export { PasswordInput };

View File

@@ -0,0 +1,53 @@
"use client";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "./ui/button";
import { useSupabase } from "./SupabaseProvider";
import { useRouter } from "next/navigation";
import useUser from "@/hooks/useUser";
const ProfileDropdown = () => {
const { supabase } = useSupabase();
const router = useRouter();
const { user, role, isLoading } = useUser();
if (isLoading) {
return null;
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 rounded-full">
<Avatar>
<AvatarImage
src={user?.user_metadata["avatar_url"]}
alt="User Avatar"
/>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => router.push("/profile")}>
Profile
</DropdownMenuItem>
{role === "admin" && (
<DropdownMenuItem onClick={() => router.push("/admin/dashboard")}>
Admin Dashboard
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={() => supabase?.auth.signOut()}>
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
export default ProfileDropdown;

View File

@@ -0,0 +1,27 @@
// components/RoleBasedAccess.tsx
import React from "react";
import useUser from "@/hooks/useUser";
interface RoleBasedAccessProps {
allowedRoles: string[];
children: React.ReactNode;
}
const RoleBasedAccess: React.FC<RoleBasedAccessProps> = ({
allowedRoles,
children,
}) => {
const { role, isLoading } = useUser();
if (isLoading) {
return <div>Loading...</div>;
}
if (!role || !allowedRoles.includes(role)) {
return null;
}
return <>{children}</>;
};
export default RoleBasedAccess;

View File

@@ -0,0 +1,141 @@
import React, {
useState,
useCallback,
forwardRef,
useImperativeHandle,
} from "react";
import RunnerInputUI from "./runner-ui/RunnerInputUI";
import RunnerOutputUI from "./runner-ui/RunnerOutputUI";
import { Node } from "@xyflow/react";
import { filterBlocksByType } from "@/lib/utils";
import { BlockIORootSchema } from "@/lib/autogpt-server-api/types";
interface RunnerUIWrapperProps {
nodes: Node[];
setNodes: React.Dispatch<React.SetStateAction<Node[]>>;
isRunning: boolean;
requestSaveAndRun: () => void;
}
export interface RunnerUIWrapperRef {
openRunnerInput: () => void;
openRunnerOutput: () => void;
runOrOpenInput: () => void;
}
const RunnerUIWrapper = forwardRef<RunnerUIWrapperRef, RunnerUIWrapperProps>(
({ nodes, setNodes, isRunning, requestSaveAndRun }, ref) => {
const [isRunnerInputOpen, setIsRunnerInputOpen] = useState(false);
const [isRunnerOutputOpen, setIsRunnerOutputOpen] = useState(false);
const getBlockInputsAndOutputs = useCallback(() => {
const inputBlocks = filterBlocksByType(
nodes,
(node) => node.data.block_id === "c0a8e994-ebf1-4a9c-a4d8-89d09c86741b",
);
const outputBlocks = filterBlocksByType(
nodes,
(node) => node.data.block_id === "363ae599-353e-4804-937e-b2ee3cef3da4",
);
const inputs = inputBlocks.map((node) => ({
id: node.id,
type: "input" as const,
inputSchema: node.data.inputSchema as BlockIORootSchema,
hardcodedValues: {
name: (node.data.hardcodedValues as any).name || "",
description: (node.data.hardcodedValues as any).description || "",
value: (node.data.hardcodedValues as any).value,
placeholder_values:
(node.data.hardcodedValues as any).placeholder_values || [],
limit_to_placeholder_values:
(node.data.hardcodedValues as any).limit_to_placeholder_values ||
false,
},
}));
const outputs = outputBlocks.map((node) => ({
id: node.id,
type: "output" as const,
outputSchema: node.data.outputSchema as BlockIORootSchema,
hardcodedValues: {
name: (node.data.hardcodedValues as any).name || "Output",
description:
(node.data.hardcodedValues as any).description ||
"Output from the agent",
value: (node.data.hardcodedValues as any).value,
},
result: (node.data.executionResults as any)?.at(-1)?.data?.output,
}));
return { inputs, outputs };
}, [nodes]);
const handleInputChange = useCallback(
(nodeId: string, field: string, value: string) => {
setNodes((nds) =>
nds.map((node) => {
if (node.id === nodeId) {
return {
...node,
data: {
...node.data,
hardcodedValues: {
...(node.data.hardcodedValues as any),
[field]: value,
},
},
};
}
return node;
}),
);
},
[setNodes],
);
const openRunnerInput = () => setIsRunnerInputOpen(true);
const openRunnerOutput = () => setIsRunnerOutputOpen(true);
const runOrOpenInput = () => {
const { inputs } = getBlockInputsAndOutputs();
if (inputs.length > 0) {
openRunnerInput();
} else {
requestSaveAndRun();
}
};
useImperativeHandle(ref, () => ({
openRunnerInput,
openRunnerOutput,
runOrOpenInput,
}));
return (
<>
<RunnerInputUI
isOpen={isRunnerInputOpen}
onClose={() => setIsRunnerInputOpen(false)}
blockInputs={getBlockInputsAndOutputs().inputs}
onInputChange={handleInputChange}
onRun={() => {
setIsRunnerInputOpen(false);
requestSaveAndRun();
}}
isRunning={isRunning}
/>
<RunnerOutputUI
isOpen={isRunnerOutputOpen}
onClose={() => setIsRunnerOutputOpen(false)}
blockOutputs={getBlockInputsAndOutputs().outputs}
/>
</>
);
},
);
RunnerUIWrapper.displayName = "RunnerUIWrapper";
export default RunnerUIWrapper;

View File

@@ -0,0 +1,39 @@
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Info } from "lucide-react";
import ReactMarkdown from "react-markdown";
const SchemaTooltip: React.FC<{ description?: string }> = ({ description }) => {
if (!description) return null;
return (
<TooltipProvider delayDuration={400}>
<Tooltip>
<TooltipTrigger asChild>
<Info className="rounded-full p-1 hover:bg-gray-300" size={24} />
</TooltipTrigger>
<TooltipContent className="tooltip-content max-w-xs">
<ReactMarkdown
components={{
a: ({ node, ...props }) => (
<a
target="_blank"
className="text-blue-400 underline"
{...props}
/>
),
}}
>
{description}
</ReactMarkdown>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};
export default SchemaTooltip;

View File

@@ -0,0 +1,65 @@
"use client";
import { createClient } from "@/lib/supabase/client";
import { SupabaseClient } from "@supabase/supabase-js";
import { useRouter } from "next/navigation";
import { createContext, useContext, useEffect, useState } from "react";
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
type SupabaseContextType = {
supabase: SupabaseClient | null;
isLoading: boolean;
};
const Context = createContext<SupabaseContextType | undefined>(undefined);
export default function SupabaseProvider({
children,
}: {
children: React.ReactNode;
}) {
const [supabase, setSupabase] = useState<SupabaseClient | null>(null);
const [isLoading, setIsLoading] = useState(true);
const router = useRouter();
useEffect(() => {
const initializeSupabase = async () => {
setIsLoading(true);
const client = createClient();
const api = new AutoGPTServerAPI();
setSupabase(client);
setIsLoading(false);
if (client) {
const {
data: { subscription },
} = client.auth.onAuthStateChange((event, session) => {
if (event === "SIGNED_IN") {
api.createUser();
}
router.refresh();
});
return () => {
subscription.unsubscribe();
};
}
};
initializeSupabase();
}, [router]);
return (
<Context.Provider value={{ supabase, isLoading }}>
{children}
</Context.Provider>
);
}
export const useSupabase = () => {
const context = useContext(Context);
if (context === undefined) {
throw new Error("useSupabase must be used inside SupabaseProvider");
}
return context;
};

View File

@@ -0,0 +1,67 @@
"use client";
import React, { useEffect, useState } from "react";
import { Button } from "./ui/button";
import { IconMegaphone } from "@/components/ui/icons";
const TallyPopupSimple = () => {
const [isFormVisible, setIsFormVisible] = useState(false);
useEffect(() => {
// Load Tally script
const script = document.createElement("script");
script.src = "https://tally.so/widgets/embed.js";
script.async = true;
document.head.appendChild(script);
// Setup event listeners for Tally events
const handleTallyMessage = (event: MessageEvent) => {
if (typeof event.data === "string") {
try {
const data = JSON.parse(event.data);
if (data.event === "Tally.FormLoaded") {
setIsFormVisible(true);
} else if (data.event === "Tally.PopupClosed") {
setIsFormVisible(false);
}
} catch (error) {
console.error("Error parsing Tally message:", error);
}
}
};
window.addEventListener("message", handleTallyMessage);
return () => {
document.head.removeChild(script);
window.removeEventListener("message", handleTallyMessage);
};
}, []);
if (isFormVisible) {
return null; // Hide the button when the form is visible
}
const resetTutorial = () => {
const url = `${window.location.origin}/build?resetTutorial=true`;
window.location.href = url;
};
return (
<div className="fixed bottom-6 right-6 z-50 flex items-center gap-4 p-3 transition-all duration-300 ease-in-out">
<Button variant="default" onClick={resetTutorial} className="mb-0">
Tutorial
</Button>
<Button
variant="default"
data-tally-open="3yx2L0"
data-tally-emoji-text="👋"
data-tally-emoji-animation="wave"
>
<IconMegaphone size="lg" />
<span className="sr-only">Reach Out</span>
</Button>
</div>
);
};
export default TallyPopupSimple;

View File

@@ -0,0 +1,149 @@
"use client";
import {
Dialog,
DialogContent,
DialogClose,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import {
MultiSelector,
MultiSelectorContent,
MultiSelectorInput,
MultiSelectorItem,
MultiSelectorList,
MultiSelectorTrigger,
} from "@/components/ui/multiselect";
import { Controller, useForm } from "react-hook-form";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { useState } from "react";
import { addFeaturedAgent } from "./actions";
import { Agent } from "@/lib/marketplace-api/types";
type FormData = {
agent: string;
categories: string[];
};
export const AdminAddFeaturedAgentDialog = ({
categories,
agents,
}: {
categories: string[];
agents: Agent[];
}) => {
const [selectedAgent, setSelectedAgent] = useState<string>("");
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const {
control,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<FormData>({
defaultValues: {
agent: "",
categories: [],
},
});
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline" size="sm">
Add Featured Agent
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Add Featured Agent</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-4">
<Controller
name="agent"
control={control}
rules={{ required: true }}
render={({ field }) => (
<div>
<label htmlFor={field.name}>Agent</label>
<Select
onValueChange={(value) => {
field.onChange(value);
setSelectedAgent(value);
}}
value={field.value || ""}
>
<SelectTrigger>
<SelectValue placeholder="Select an agent" />
</SelectTrigger>
<SelectContent>
{/* Populate with agents */}
{agents.map((agent) => (
<SelectItem key={agent.id} value={agent.id}>
{agent.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
/>
<Controller
name="categories"
control={control}
render={({ field }) => (
<MultiSelector
values={field.value || []}
onValuesChange={(values) => {
field.onChange(values);
setSelectedCategories(values);
}}
>
<MultiSelectorTrigger>
<MultiSelectorInput placeholder="Select categories" />
</MultiSelectorTrigger>
<MultiSelectorContent>
<MultiSelectorList>
{categories.map((category) => (
<MultiSelectorItem key={category} value={category}>
{category}
</MultiSelectorItem>
))}
</MultiSelectorList>
</MultiSelectorContent>
</MultiSelector>
)}
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<DialogClose asChild>
<Button
type="submit"
onClick={async () => {
// Handle adding the featured agent
await addFeaturedAgent(selectedAgent, selectedCategories);
// close the dialog
}}
>
Add
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,74 @@
import { Button } from "@/components/ui/button";
import {
getFeaturedAgents,
removeFeaturedAgent,
getCategories,
getNotFeaturedAgents,
} from "./actions";
import FeaturedAgentsTable from "./FeaturedAgentsTable";
import { AdminAddFeaturedAgentDialog } from "./AdminAddFeaturedAgentDialog";
import { revalidatePath } from "next/cache";
import * as Sentry from "@sentry/nextjs";
export default async function AdminFeaturedAgentsControl({
className,
}: {
className?: string;
}) {
// add featured agent button
// modal to select agent?
// modal to select categories?
// table of featured agents
// in table
// remove featured agent button
// edit featured agent categories button
// table footer
// Next page button
// Previous page button
// Page number input
// Page size input
// Total pages input
// Go to page button
const page = 1;
const pageSize = 10;
const agents = await getFeaturedAgents(page, pageSize);
const categories = await getCategories();
const notFeaturedAgents = await getNotFeaturedAgents();
return (
<div className={`flex flex-col gap-4 ${className}`}>
<div className="mb-4 flex justify-between">
<h3 className="text-lg font-semibold">Featured Agent Controls</h3>
<AdminAddFeaturedAgentDialog
categories={categories.unique_categories}
agents={notFeaturedAgents.agents}
/>
</div>
<FeaturedAgentsTable
agents={agents.agents}
globalActions={[
{
component: <Button>Remove</Button>,
action: async (rows) => {
"use server";
return await Sentry.withServerActionInstrumentation(
"removeFeaturedAgent",
{},
async () => {
const all = rows.map((row) => removeFeaturedAgent(row.id));
await Promise.all(all);
revalidatePath("/marketplace");
},
);
},
},
]}
/>
</div>
);
}

View File

@@ -0,0 +1,36 @@
import { Agent } from "@/lib/marketplace-api";
import AdminMarketplaceCard from "./AdminMarketplaceCard";
import { ClipboardX } from "lucide-react";
export default function AdminMarketplaceAgentList({
agents,
className,
}: {
agents: Agent[];
className?: string;
}) {
if (agents.length === 0) {
return (
<div className={className}>
<h3 className="text-lg font-semibold">Agents to review</h3>
<div className="flex flex-col items-center justify-center py-12 text-gray-500">
<ClipboardX size={48} />
<p className="mt-4 text-lg font-semibold">No agents to review</p>
</div>
</div>
);
}
return (
<div className={`flex flex-col gap-4 ${className}`}>
<div>
<h3 className="text-lg font-semibold">Agents to review</h3>
</div>
<div className="flex flex-col gap-4">
{agents.map((agent) => (
<AdminMarketplaceCard agent={agent} key={agent.id} />
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,113 @@
"use client";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import { approveAgent, rejectAgent } from "./actions";
import { Agent } from "@/lib/marketplace-api";
import Link from "next/link";
import { useState } from "react";
import { Input } from "@/components/ui/input";
function AdminMarketplaceCard({ agent }: { agent: Agent }) {
const [isApproved, setIsApproved] = useState(false);
const [isRejected, setIsRejected] = useState(false);
const [comment, setComment] = useState("");
const approveAgentWithId = approveAgent.bind(
null,
agent.id,
agent.version,
comment,
);
const rejectAgentWithId = rejectAgent.bind(
null,
agent.id,
agent.version,
comment,
);
const handleApprove = async (e: React.FormEvent) => {
e.preventDefault();
await approveAgentWithId();
setIsApproved(true);
};
const handleReject = async (e: React.FormEvent) => {
e.preventDefault();
await rejectAgentWithId();
setIsRejected(true);
};
return (
<>
{!isApproved && !isRejected && (
<Card key={agent.id} className="m-3 flex h-[300px] flex-col p-4">
<div className="mb-2 flex items-start justify-between">
<Link
href={`/marketplace/${agent.id}`}
className="text-lg font-semibold hover:underline"
>
{agent.name}
</Link>
<Badge variant="outline">v{agent.version}</Badge>
</div>
<p className="mb-2 text-sm text-gray-500">by {agent.author}</p>
<ScrollArea className="flex-grow">
<p className="mb-2 text-sm text-gray-600">{agent.description}</p>
<div className="mb-2 flex flex-wrap gap-1">
{agent.categories.map((category) => (
<Badge key={category} variant="secondary">
{category}
</Badge>
))}
</div>
<div className="flex flex-wrap gap-1">
{agent.keywords.map((keyword) => (
<Badge key={keyword} variant="outline">
{keyword}
</Badge>
))}
</div>
</ScrollArea>
<div className="mb-2 flex justify-between text-xs text-gray-500">
<span>
Created: {new Date(agent.createdAt).toLocaleDateString()}
</span>
<span>
Updated: {new Date(agent.updatedAt).toLocaleDateString()}
</span>
</div>
<div className="mb-4 flex justify-between text-sm">
<span>👁 {agent.views}</span>
<span> {agent.downloads}</span>
</div>
<div className="mt-auto space-y-2">
<div className="flex justify-end space-x-2">
<Input
type="text"
placeholder="Add a comment (optional)"
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
{!isRejected && (
<form onSubmit={handleReject}>
<Button variant="outline" type="submit">
Reject
</Button>
</form>
)}
{!isApproved && (
<form onSubmit={handleApprove}>
<Button type="submit">Approve</Button>
</form>
)}
</div>
</div>
</Card>
)}
</>
);
}
export default AdminMarketplaceCard;

View File

@@ -0,0 +1,114 @@
"use client";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { DataTable } from "@/components/ui/data-table";
import { Agent } from "@/lib/marketplace-api";
import { ColumnDef } from "@tanstack/react-table";
import { ArrowUpDown } from "lucide-react";
import { removeFeaturedAgent } from "./actions";
import { GlobalActions } from "@/components/ui/data-table";
export const columns: ColumnDef<Agent>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
},
{
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
accessorKey: "name",
},
{
header: "Description",
accessorKey: "description",
},
{
header: "Categories",
accessorKey: "categories",
},
{
header: "Keywords",
accessorKey: "keywords",
},
{
header: "Downloads",
accessorKey: "downloads",
},
{
header: "Author",
accessorKey: "author",
},
{
header: "Version",
accessorKey: "version",
},
{
header: "actions",
cell: ({ row }) => {
const handleRemove = async () => {
await removeFeaturedAgentWithId();
};
// const handleEdit = async () => {
// console.log("edit");
// };
const removeFeaturedAgentWithId = removeFeaturedAgent.bind(
null,
row.original.id,
);
return (
<div className="flex justify-end gap-2">
<Button variant="outline" size="sm" onClick={handleRemove}>
Remove
</Button>
{/* <Button variant="outline" size="sm" onClick={handleEdit}>
Edit
</Button> */}
</div>
);
},
},
];
export default function FeaturedAgentsTable({
agents,
globalActions,
}: {
agents: Agent[];
globalActions: GlobalActions<Agent>[];
}) {
return (
<DataTable
columns={columns}
data={agents}
filterPlaceholder="Search agents..."
filterColumn="name"
globalActions={globalActions}
/>
);
}

View File

@@ -0,0 +1,141 @@
"use server";
import MarketplaceAPI from "@/lib/marketplace-api";
import { revalidatePath } from "next/cache";
import * as Sentry from "@sentry/nextjs";
export async function approveAgent(
agentId: string,
version: number,
comment: string,
) {
return await Sentry.withServerActionInstrumentation(
"approveAgent",
{},
async () => {
const api = new MarketplaceAPI();
await api.approveAgentSubmission(agentId, version, comment);
console.debug(`Approving agent ${agentId}`);
revalidatePath("/marketplace");
},
);
}
export async function rejectAgent(
agentId: string,
version: number,
comment: string,
) {
return await Sentry.withServerActionInstrumentation(
"rejectAgent",
{},
async () => {
const api = new MarketplaceAPI();
await api.rejectAgentSubmission(agentId, version, comment);
console.debug(`Rejecting agent ${agentId}`);
revalidatePath("/marketplace");
},
);
}
export async function getReviewableAgents() {
return await Sentry.withServerActionInstrumentation(
"getReviewableAgents",
{},
async () => {
const api = new MarketplaceAPI();
return api.getAgentSubmissions();
},
);
}
export async function getFeaturedAgents(
page: number = 1,
pageSize: number = 10,
) {
return await Sentry.withServerActionInstrumentation(
"getFeaturedAgents",
{},
async () => {
const api = new MarketplaceAPI();
const featured = await api.getFeaturedAgents(page, pageSize);
console.debug(`Getting featured agents ${featured.agents.length}`);
return featured;
},
);
}
export async function getFeaturedAgent(agentId: string) {
return await Sentry.withServerActionInstrumentation(
"getFeaturedAgent",
{},
async () => {
const api = new MarketplaceAPI();
const featured = await api.getFeaturedAgent(agentId);
console.debug(`Getting featured agent ${featured.agentId}`);
return featured;
},
);
}
export async function addFeaturedAgent(
agentId: string,
categories: string[] = ["featured"],
) {
return await Sentry.withServerActionInstrumentation(
"addFeaturedAgent",
{},
async () => {
const api = new MarketplaceAPI();
await api.addFeaturedAgent(agentId, categories);
console.debug(`Adding featured agent ${agentId}`);
revalidatePath("/marketplace");
},
);
}
export async function removeFeaturedAgent(
agentId: string,
categories: string[] = ["featured"],
) {
return await Sentry.withServerActionInstrumentation(
"removeFeaturedAgent",
{},
async () => {
const api = new MarketplaceAPI();
await api.removeFeaturedAgent(agentId, categories);
console.debug(`Removing featured agent ${agentId}`);
revalidatePath("/marketplace");
},
);
}
export async function getCategories() {
return await Sentry.withServerActionInstrumentation(
"getCategories",
{},
async () => {
const api = new MarketplaceAPI();
const categories = await api.getCategories();
console.debug(
`Getting categories ${categories.unique_categories.length}`,
);
return categories;
},
);
}
export async function getNotFeaturedAgents(
page: number = 1,
pageSize: number = 100,
) {
return await Sentry.withServerActionInstrumentation(
"getNotFeaturedAgents",
{},
async () => {
const api = new MarketplaceAPI();
const agents = await api.getNotFeaturedAgents(page, pageSize);
console.debug(`Getting not featured agents ${agents.agents.length}`);
return agents;
},
);
}

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