├── .env.template ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.cjs ├── prisma ├── migrations │ ├── 20240220121214_init │ │ └── migration.sql │ ├── 20240221134650_alter_agent_tools │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── public ├── agents_images │ └── sailor.png ├── crewai_logo.png ├── next.svg └── vercel.svg ├── requirements.txt ├── src ├── app │ ├── agents │ │ └── page.tsx │ ├── api │ │ ├── graphql │ │ │ ├── crew_ai.js │ │ │ ├── crew_ai.py │ │ │ ├── resolvers.js │ │ │ ├── route.ts │ │ │ └── schema.ts │ │ └── upload_agent_image │ │ │ └── route.ts │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ ├── missions │ │ └── page.tsx │ └── page.tsx ├── components │ ├── bottom_nav.tsx │ ├── inputs │ │ ├── file.tsx │ │ └── mission_tasks_editor.tsx │ ├── max_width_wrapper.tsx │ ├── modals │ │ ├── agent_modal.tsx │ │ ├── mission_modal.tsx │ │ ├── new_agent_modal.tsx │ │ └── new_mission_modal.tsx │ ├── side_nav.tsx │ ├── top_nav.tsx │ └── ui │ │ ├── hero.tsx │ │ └── tasks_accordions.tsx ├── data │ ├── consts.ts │ └── data.ts ├── hook │ ├── use_navigation.tsx │ └── use_scroll.tsx ├── styles │ └── globals.css ├── types │ ├── agent.ts │ ├── mission.ts │ └── task.ts └── utils │ ├── apollo_client.ts │ ├── apollo_wrapper.tsx │ ├── graphql_queries.ts │ └── prisma.ts ├── tailwind.config.ts └── tsconfig.json /.env.template: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="postgresql://postgres:postgres@localhost:5432/crew_ai_visualizer?schema=public" 8 | 9 | GEMINI_API_KEY="" 10 | 11 | PYTHON_SITE_PACKAGES="" 12 | 13 | CREW_AI_PY_FILE="" 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | # Python 40 | venv/ 41 | __pycache__/ 42 | 43 | # PyCharm 44 | .idea/ 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Eng. Elias 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CrewAI Simplified App 2 | 3 | This application provides a simplified user interface for leveraging the power of CrewAI, a cutting-edge framework for orchestrating role-playing autonomous AI agents. With this app, users can streamline the process of creating and managing AI crews without the need for coding. 4 | 5 | [![CrewAI Visualizer](https://img.youtube.com/vi/ZVZucnzccpk/0.jpg)](https://www.youtube.com/watch?v=ZVZucnzccpk) 6 | 7 | ## Features 8 | 9 | - **Intuitive UI**: The app offers a user-friendly interface, allowing users to easily create and manage AI crews. 10 | - **Role-Based Agent Design**: Customize agents with specific roles, goals, and tools through a simple form-based approach. 11 | - **Task Management**: Define tasks and assign them to agents dynamically. 12 | - **Sequential and Hierarchical Processes**: Choose between sequential or hierarchical processes for task execution, depending on your workflow needs. 13 | - **Save Output**: Save the output for future reference or analysis. 14 | - **Connection to LLM model**: for this version I used Gemini model and I plan to add more models in the future. 15 | 16 | ## Getting Started 17 | 18 | To get started with the CrewAI Simplified App, follow these simple steps: 19 | 20 | 1. **Installation**: Clone the repository and install dependencies using npm or yarn: 21 | 22 | ```bash 23 | git clone https://github.com/Eng-Elias/CrewAI-Visualizer.git 24 | cd CrewAI-Visualizer 25 | npm install 26 | ``` 27 | 28 | 2. **Create Python Virtual Enviroment**: create Python venv, activate the venv and install the requirements. 29 | 30 | Create venv: 31 | 32 | ```bash 33 | python -m venv venv 34 | ``` 35 | 36 | To activate the virtual environment on Windows: 37 | 38 | ```bash 39 | .\venv\Scripts\activate 40 | ``` 41 | 42 | To activate the virtual environment on Linux or Mac: 43 | 44 | ```bash 45 | source venv/bin/activate 46 | ``` 47 | 48 | Install the requirements: 49 | 50 | ```bash 51 | pip install -r requirements.txt 52 | ``` 53 | 54 | 3. **Configuration**: Set up your environment variables in a `.env` file: 55 | 56 | Just rename .env.template to .env and set your values: 57 | 58 | ```plaintext 59 | DATABASE_URL="postgresql://postgres:postgres@localhost:5432/ crew_ai_visualizer?schema=public" 60 | 61 | GEMINI_API_KEY="" 62 | 63 | PYTHON_SITE_PACKAGES="" 64 | 65 | CREW_AI_PY_FILE="" 66 | ``` 67 | 68 | 4. **Start the Development Server**: Run the following command to start the development server: 69 | 70 | ```bash 71 | npm run dev 72 | ``` 73 | 74 | 5. **Access the App**: Once the development server is running, access the app in your browser at `http://localhost:3000`. 75 | 76 | ## Usage 77 | 78 | 1. **Create a New Crew**: By adding agents. 79 | 80 | 2. **Customize Agents**: Fill in the information for each agent, including role, goal, backstory, tools, allow_deligation, and verbose. 81 | 82 | 3. **Define Missions**: Fill mission information including name, crew, verbose, process and add tasks with their details (name, description, agent). 83 | 84 | 4. **Execute Mission**: Once your mission is set up, execute it to start the execution process. 85 | 86 | 5. **View Results**: View the output of completed missions within the app. 87 | 88 | ## Contributing 89 | 90 | We welcome contributions to the CrewAI Simplified App. If you'd like to contribute, please follow these steps: 91 | 92 | 1. Fork the repository. 93 | 2. Create a new branch for your feature or improvement. 94 | 3. Add your feature or improvement. 95 | 4. Submit a pull request. 96 | 97 | ## Tech Stack 98 | 99 | This app is built using TypeScript, Prisma, GraphQL, Next.js, and node-calls-python to execute Python code from Node.js and get the result in addition to use Gemini as LLM. 100 | 101 | ## License 102 | 103 | This application is open-source and is released under the MIT License. See the [LICENSE](LICENSE) file for details. 104 | 105 | ## To Do 106 | 107 | - [ ] Add more tools for agents either from LangChain community or create new useful tools. 108 | - [ ] Add more LLM options like ChatGPT and local LLMs. 109 | 110 | ## Credits 111 | 112 | Special thanks to [João Moura](https://github.com/joaomdmoura) the creator of [CrewAI](https://github.com/joaomdmoura/crewAI) for providing the underlying framework for AI crew orchestration. 113 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crew-ai-visualizer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "next dev", 8 | "build": "next build", 9 | "start": "next start", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "@apollo/client": "^3.9.5", 14 | "@apollo/experimental-nextjs-app-support": "^0.8.0", 15 | "@apollo/server": "^4.10.0", 16 | "@as-integrations/next": "^3.0.0", 17 | "@material-tailwind/react": "^2.1.9", 18 | "@prisma/client": "^5.9.1", 19 | "framer-motion": "^11.0.8", 20 | "graphql": "^16.8.1", 21 | "next": "14.1.0", 22 | "node-calls-python": "^1.8.2", 23 | "react": "^18", 24 | "react-dom": "^18", 25 | "sweetalert2": "^11.10.5", 26 | "sweetalert2-react-content": "^5.0.7", 27 | "tw-elements": "^1.1.0", 28 | "tw-elements-react": "^1.0.0-alpha2", 29 | "uuid": "^9.0.1" 30 | }, 31 | "devDependencies": { 32 | "@ianvs/prettier-plugin-sort-imports": "^4.1.0", 33 | "@iconify/react": "^4.1.1", 34 | "@types/node": "^20.11.19", 35 | "@types/react": "^18", 36 | "@types/react-dom": "^18", 37 | "@types/uuid": "^9.0.8", 38 | "@typescript-eslint/eslint-plugin": "^6.7.3", 39 | "@typescript-eslint/parser": "^6.7.3", 40 | "autoprefixer": "^10.0.1", 41 | "eslint": "^8", 42 | "eslint-config-next": "14.1.0", 43 | "postcss": "^8", 44 | "prisma": "^5.9.1", 45 | "tailwindcss": "^3.3.0", 46 | "ts-node": "^10.9.2", 47 | "typescript": "^5.3.3" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /prisma/migrations/20240220121214_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "AgentTool" AS ENUM ('DUCK_DUCK_GO_SEARCH', 'PUBMED', 'PYTHON_REPL', 'SEMANTIC_SCHOLER', 'STACK_EXCHANGE', 'WIKIDATA', 'WIKIPEDIA', 'YAHOO_FINANCE', 'YUOUTUBE_SEARCH'); 3 | 4 | -- CreateEnum 5 | CREATE TYPE "MissionProcess" AS ENUM ('SEQUENTIAL', 'HIERARCHICAL'); 6 | 7 | -- CreateTable 8 | CREATE TABLE "Agent" ( 9 | "id" SERIAL NOT NULL, 10 | "role" TEXT NOT NULL, 11 | "goal" TEXT NOT NULL, 12 | "backstory" TEXT, 13 | "tools" "AgentTool"[] DEFAULT ARRAY[]::"AgentTool"[], 14 | "allowDelegation" BOOLEAN NOT NULL DEFAULT false, 15 | "verbose" BOOLEAN NOT NULL DEFAULT false, 16 | "image" TEXT, 17 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 18 | "updatedAt" TIMESTAMP(3) NOT NULL, 19 | 20 | CONSTRAINT "Agent_pkey" PRIMARY KEY ("id") 21 | ); 22 | 23 | -- CreateTable 24 | CREATE TABLE "Mission" ( 25 | "id" SERIAL NOT NULL, 26 | "name" TEXT NOT NULL, 27 | "tasks" JSONB[] DEFAULT ARRAY[]::JSONB[], 28 | "verbose" BOOLEAN NOT NULL DEFAULT false, 29 | "process" "MissionProcess" NOT NULL DEFAULT 'SEQUENTIAL', 30 | "result" TEXT NOT NULL DEFAULT '', 31 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 32 | "updatedAt" TIMESTAMP(3) NOT NULL, 33 | 34 | CONSTRAINT "Mission_pkey" PRIMARY KEY ("id") 35 | ); 36 | 37 | -- CreateTable 38 | CREATE TABLE "_AgentToMission" ( 39 | "A" INTEGER NOT NULL, 40 | "B" INTEGER NOT NULL 41 | ); 42 | 43 | -- CreateIndex 44 | CREATE UNIQUE INDEX "_AgentToMission_AB_unique" ON "_AgentToMission"("A", "B"); 45 | 46 | -- CreateIndex 47 | CREATE INDEX "_AgentToMission_B_index" ON "_AgentToMission"("B"); 48 | 49 | -- AddForeignKey 50 | ALTER TABLE "_AgentToMission" ADD CONSTRAINT "_AgentToMission_A_fkey" FOREIGN KEY ("A") REFERENCES "Agent"("id") ON DELETE CASCADE ON UPDATE CASCADE; 51 | 52 | -- AddForeignKey 53 | ALTER TABLE "_AgentToMission" ADD CONSTRAINT "_AgentToMission_B_fkey" FOREIGN KEY ("B") REFERENCES "Mission"("id") ON DELETE CASCADE ON UPDATE CASCADE; 54 | -------------------------------------------------------------------------------- /prisma/migrations/20240221134650_alter_agent_tools/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - The values [PUBMED,PYTHON_REPL,STACK_EXCHANGE] on the enum `AgentTool` will be removed. If these variants are still used in the database, this will fail. 5 | 6 | */ 7 | -- AlterEnum 8 | BEGIN; 9 | CREATE TYPE "AgentTool_new" AS ENUM ('DUCK_DUCK_GO_SEARCH', 'SEMANTIC_SCHOLER', 'WIKIDATA', 'WIKIPEDIA', 'YAHOO_FINANCE', 'YUOUTUBE_SEARCH'); 10 | ALTER TABLE "Agent" ALTER COLUMN "tools" DROP DEFAULT; 11 | ALTER TABLE "Agent" ALTER COLUMN "tools" TYPE "AgentTool_new"[] USING ("tools"::text::"AgentTool_new"[]); 12 | ALTER TYPE "AgentTool" RENAME TO "AgentTool_old"; 13 | ALTER TYPE "AgentTool_new" RENAME TO "AgentTool"; 14 | DROP TYPE "AgentTool_old"; 15 | ALTER TABLE "Agent" ALTER COLUMN "tools" SET DEFAULT ARRAY[]::"AgentTool"[]; 16 | COMMIT; 17 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "postgresql" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | enum AgentTool { 14 | DUCK_DUCK_GO_SEARCH 15 | SEMANTIC_SCHOLER 16 | WIKIDATA 17 | WIKIPEDIA 18 | YAHOO_FINANCE 19 | YUOUTUBE_SEARCH 20 | } 21 | 22 | model Agent { 23 | id Int @id @default(autoincrement()) 24 | role String 25 | goal String 26 | backstory String? 27 | tools AgentTool[] @default([]) 28 | allowDelegation Boolean @default(false) 29 | verbose Boolean @default(false) 30 | image String? 31 | 32 | createdAt DateTime @default(now()) 33 | updatedAt DateTime @updatedAt 34 | 35 | missions Mission[] 36 | } 37 | 38 | enum MissionProcess { 39 | SEQUENTIAL 40 | HIERARCHICAL 41 | } 42 | 43 | model Mission { 44 | id Int @id @default(autoincrement()) 45 | name String 46 | crew Agent[] 47 | tasks Json[] @default([]) 48 | verbose Boolean @default(false) 49 | process MissionProcess @default(SEQUENTIAL) 50 | result String @default("") 51 | 52 | createdAt DateTime @default(now()) 53 | updatedAt DateTime @updatedAt 54 | } 55 | -------------------------------------------------------------------------------- /public/agents_images/sailor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazingnerd/CrewAI-UI/cd041b569371ad0fcac5a5a534f41c3269d3aa90/public/agents_images/sailor.png -------------------------------------------------------------------------------- /public/crewai_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazingnerd/CrewAI-UI/cd041b569371ad0fcac5a5a534f41c3269d3aa90/public/crewai_logo.png -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.9.3 2 | aiosignal==1.3.1 3 | annotated-types==0.6.0 4 | anyio==4.3.0 5 | appdirs==1.4.4 6 | attrs==23.2.0 7 | backoff==2.2.1 8 | beautifulsoup4==4.12.3 9 | cachetools==5.3.2 10 | certifi==2024.2.2 11 | cffi==1.16.0 12 | charset-normalizer==3.3.2 13 | click==8.1.7 14 | colorama==0.4.6 15 | crewai==0.11.2 16 | curl_cffi==0.6.0b9 17 | dataclasses-json==0.6.4 18 | Deprecated==1.2.14 19 | distro==1.9.0 20 | docstring-parser==0.15 21 | duckduckgo_search==4.4.3 22 | frozendict==2.4.0 23 | frozenlist==1.4.1 24 | google-ai-generativelanguage==0.4.0 25 | google-api-core==2.17.1 26 | google-auth==2.28.0 27 | google-generativeai==0.3.2 28 | googleapis-common-protos==1.62.0 29 | greenlet==3.0.3 30 | grpcio==1.60.1 31 | grpcio-status==1.60.1 32 | h11==0.14.0 33 | html5lib==1.1 34 | httpcore==1.0.3 35 | httpx==0.26.0 36 | idna==3.6 37 | importlib-metadata==6.11.0 38 | instructor==0.5.2 39 | jsonpatch==1.33 40 | jsonpointer==2.4 41 | langchain==0.1.8 42 | langchain-community==0.0.21 43 | langchain-core==0.1.24 44 | langchain-experimental==0.0.52 45 | langchain-google-genai==0.0.9 46 | langchain-openai==0.0.5 47 | langsmith==0.1.3 48 | lxml==5.1.0 49 | markdown-it-py==3.0.0 50 | marshmallow==3.20.2 51 | mdurl==0.1.2 52 | mediawikiapi==1.2 53 | multidict==6.0.5 54 | multitasking==0.0.11 55 | mypy-extensions==1.0.0 56 | nest-asyncio==1.6.0 57 | numpy==1.26.4 58 | openai==1.12.0 59 | opentelemetry-api==1.22.0 60 | opentelemetry-exporter-otlp-proto-common==1.22.0 61 | opentelemetry-exporter-otlp-proto-http==1.22.0 62 | opentelemetry-proto==1.22.0 63 | opentelemetry-sdk==1.22.0 64 | opentelemetry-semantic-conventions==0.43b0 65 | packaging==23.2 66 | pandas==2.2.0 67 | peewee==3.17.1 68 | proto-plus==1.23.0 69 | protobuf==4.25.3 70 | pyasn1==0.5.1 71 | pyasn1-modules==0.3.0 72 | pycparser==2.21 73 | pydantic==2.6.1 74 | pydantic_core==2.16.2 75 | Pygments==2.17.2 76 | python-dateutil==2.8.2 77 | python-dotenv==1.0.1 78 | pytz==2024.1 79 | PyYAML==6.0.1 80 | regex==2023.12.25 81 | requests==2.31.0 82 | rich==13.7.0 83 | rsa==4.9 84 | semanticscholar==0.7.0 85 | six==1.16.0 86 | sniffio==1.3.0 87 | soupsieve==2.5 88 | SQLAlchemy==2.0.27 89 | StackAPI==0.3.0 90 | tenacity==8.2.3 91 | tiktoken==0.5.2 92 | tqdm==4.66.2 93 | typer==0.9.0 94 | typing-inspect==0.9.0 95 | typing_extensions==4.9.0 96 | tzdata==2024.1 97 | urllib3==2.2.1 98 | webencodings==0.5.1 99 | wikibase-rest-api-client==0.1.3 100 | wikipedia==1.4.0 101 | wrapt==1.16.0 102 | xmltodict==0.13.0 103 | yarl==1.9.4 104 | yfinance==0.2.36 105 | youtube-search==2.1.2 106 | zipp==3.17.0 107 | -------------------------------------------------------------------------------- /src/app/agents/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import AgentModal from "@/components/modals/agent_modal"; 4 | import NewAgentModal from "@/components/modals/new_agent_modal"; 5 | import { Agent } from "@/types/agent"; 6 | import { GET_AGENTS } from "@/utils/graphql_queries"; 7 | import { useQuery } from "@apollo/client"; 8 | import { Icon } from "@iconify/react"; 9 | import { Alert, Button, IconButton } from "@material-tailwind/react"; 10 | import { useState } from "react"; 11 | 12 | const AgentsPage = () => { 13 | const [showAgentModal, setShowAgentModal] = useState(false); 14 | const [showNewAgentModal, setShowNewAgentModal] = useState(false); 15 | 16 | const [selectedAgent, setSelectedAgent] = useState(); 17 | 18 | const { loading, error, data, refetch } = useQuery(GET_AGENTS); 19 | 20 | if (loading) { 21 | return ( 22 | 30 | ); 31 | } 32 | 33 | return ( 34 |
35 |
36 | { 41 | setShowNewAgentModal(true); 42 | }} 43 | > 44 | 45 | 46 | { 50 | refetch(); 51 | }} 52 | /> 53 |
54 |
55 | {error && ( 56 |
57 | 61 | } 62 | className="w-fit" 63 | > 64 | {error?.message ?? "An error occurred."} 65 | 66 |
67 | )} 68 | {data?.agents.map((agent: Agent, i: number) => ( 69 |
70 |
75 | Agent 80 |
81 |
82 | {agent.role} 83 |
84 |
85 | Goal: {agent.goal} 86 |
87 |
88 | 98 |
99 |
100 | ))} 101 | { 106 | refetch(); 107 | }} 108 | onUploadImage={() => { 109 | refetch(); 110 | }} 111 | onDeleteAgent={() => { 112 | refetch(); 113 | }} 114 | /> 115 |
116 |
117 | ); 118 | }; 119 | 120 | export default AgentsPage; 121 | -------------------------------------------------------------------------------- /src/app/api/graphql/crew_ai.js: -------------------------------------------------------------------------------- 1 | import prisma from "@/utils/prisma"; 2 | 3 | const nodecallspython = require("node-calls-python"); 4 | 5 | const py = nodecallspython.interpreter; 6 | 7 | py.addImportPath(process.env.PYTHON_SITE_PACKAGES); 8 | 9 | export function runMission(id) { 10 | const crewaiPath = process.env.CREW_AI_PY_FILE; 11 | return py 12 | .import(crewaiPath) 13 | .then(async function (pymodule) { 14 | const mission = await prisma.mission.findFirst({ 15 | where: { id }, 16 | include: { crew: true }, 17 | }); 18 | if (mission) { 19 | const result = await py.call(pymodule, "run_mission", mission); 20 | const missionWithResult = prisma.mission.update({ 21 | where: { id }, 22 | data: { result }, 23 | }); 24 | return missionWithResult; 25 | } else { 26 | throw Error("Mission doest not exist"); 27 | } 28 | }) 29 | .catch((err) => console.log(err)); 30 | } 31 | -------------------------------------------------------------------------------- /src/app/api/graphql/crew_ai.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from textwrap import dedent 4 | from crewai import Agent, Task, Crew, Process 5 | from langchain_google_genai import ChatGoogleGenerativeAI 6 | from langchain_community.tools import DuckDuckGoSearchRun 7 | from langchain_community.tools.semanticscholar.tool import SemanticScholarQueryRun 8 | from langchain_community.tools.wikidata.tool import WikidataAPIWrapper, WikidataQueryRun 9 | from langchain_community.tools import WikipediaQueryRun 10 | from langchain_community.utilities import WikipediaAPIWrapper 11 | from langchain_community.tools.yahoo_finance_news import YahooFinanceNewsTool 12 | from langchain_community.tools import YouTubeSearchTool 13 | from dotenv import load_dotenv 14 | 15 | load_dotenv() 16 | 17 | process_type = { 18 | "SEQUENTIAL": Process.sequential, 19 | "HIERARTICAL": Process.hierarchical, 20 | } 21 | 22 | tool_dict = { 23 | "DUCK_DUCK_GO_SEARCH": DuckDuckGoSearchRun(), 24 | "SEMANTIC_SCHOLER": SemanticScholarQueryRun(), 25 | "WIKIDATA": WikidataQueryRun(api_wrapper=WikidataAPIWrapper()), 26 | "WIKIPEDIA": WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()), 27 | "YAHOO_FINANCE": YahooFinanceNewsTool(), 28 | "YUOUTUBE_SEARCH": YouTubeSearchTool(), 29 | } 30 | 31 | 32 | def run_mission(mission): 33 | try: 34 | llm = ChatGoogleGenerativeAI( 35 | model="gemini-pro", 36 | verbose=True, 37 | temperature=0.5, 38 | google_api_key=os.getenv("GEMINI_API_KEY"), 39 | ) 40 | 41 | agents = [ 42 | Agent( 43 | role=agent["role"], 44 | goal=agent["goal"], 45 | backstory=agent["backstory"], 46 | allow_delegation=agent["allowDelegation"], 47 | verbose=agent["verbose"], 48 | tools=[tool_dict[tool] for tool in agent["tools"]], 49 | llm=llm, 50 | ) 51 | for agent in mission["crew"] 52 | ] 53 | 54 | tasks = [ 55 | Task( 56 | description=dedent(task["description"]), 57 | agent=( 58 | [agent for agent in agents if agent.role == task["agent"]["role"]][ 59 | 0 60 | ] 61 | if task["agent"] 62 | else None 63 | ), 64 | ) 65 | for task in mission["tasks"] 66 | ] 67 | 68 | crew = Crew( 69 | agents=agents, 70 | tasks=tasks, 71 | verbose=mission["verbose"], 72 | process=process_type[mission["process"]], 73 | manager_llm=llm, 74 | ) 75 | 76 | result = crew.kickoff() 77 | return result 78 | except Exception as e: 79 | print(e) 80 | -------------------------------------------------------------------------------- /src/app/api/graphql/resolvers.js: -------------------------------------------------------------------------------- 1 | import { Process } from "@/data/consts"; 2 | import prisma from "@/utils/prisma"; 3 | import { runMission } from "./crew_ai"; 4 | 5 | const resolvers = { 6 | Query: { 7 | agents: () => { 8 | return prisma.agent.findMany(); 9 | }, 10 | agent: (id) => { 11 | return prisma.agent.findFirst({ 12 | where: { 13 | id: id, 14 | }, 15 | }); 16 | }, 17 | missions: async () => { 18 | const missions = await prisma.mission.findMany({ 19 | include: { 20 | crew: true, 21 | }, 22 | }); 23 | return missions; 24 | }, 25 | mission: (id) => { 26 | return prisma.mission.findFirst({ 27 | where: { 28 | id: id, 29 | }, 30 | }); 31 | }, 32 | }, 33 | Mutation: { 34 | createAgent: async (parent, body, context, info) => { 35 | const agent = await prisma.agent.create({ data: body }); 36 | return agent; 37 | }, 38 | updateAgent: async (parent, body, context, info) => { 39 | const updatedAgent = await prisma.agent.update({ 40 | where: { id: body.id }, 41 | data: body, 42 | }); 43 | return updatedAgent; 44 | }, 45 | deleteAgent: async (parent, body, context, info) => { 46 | await prisma.agent.delete({ where: { id: body.id } }); 47 | return { deleted: true }; 48 | }, 49 | createMission: async (parent, body, context, info) => { 50 | const { name, verbose, process } = body; 51 | const crew = await prisma.agent.findMany({ 52 | where: { 53 | id: { 54 | in: body.crew, 55 | }, 56 | }, 57 | select: { 58 | id: true, 59 | }, 60 | }); 61 | const tasks = []; 62 | for (let task of body.tasks) { 63 | let agent = null; 64 | if (task.agent) { 65 | agent = crew.find((a) => a.id === task.agent) ?? null; 66 | } 67 | tasks.push({ 68 | ...task, 69 | agent, 70 | }); 71 | } 72 | const mission = await prisma.mission.create({ 73 | data: { 74 | name, 75 | verbose: !!verbose, 76 | process: process ?? Process.SEQUENTIAL, 77 | crew: { connect: crew }, 78 | tasks, 79 | result: "", 80 | }, 81 | }); 82 | return mission; 83 | }, 84 | updateMission: async (parent, body, context, info) => { 85 | const { id, name, verbose, process } = body; 86 | const crew = await prisma.agent.findMany({ 87 | where: { 88 | id: { 89 | in: body.crew, 90 | }, 91 | }, 92 | }); 93 | const tasks = []; 94 | if (body.tasks) { 95 | for (let task of body.tasks) { 96 | let agent = null; 97 | if (task.agent) { 98 | agent = crew.find((a) => a.id === task.agent) ?? null; 99 | } 100 | tasks.push({ 101 | ...task, 102 | agent, 103 | }); 104 | } 105 | } 106 | const mission = await prisma.mission.update({ 107 | where: { 108 | id, 109 | }, 110 | include: { 111 | crew: true, 112 | }, 113 | data: { 114 | name, 115 | verbose: verbose, 116 | process: process, 117 | crew: { set: crew.map((agent) => ({ id: agent.id })) }, 118 | tasks, 119 | result: "", 120 | }, 121 | }); 122 | return mission; 123 | }, 124 | deleteMission: async (parent, body, context, info) => { 125 | await prisma.mission.delete({ where: { id: body.id } }); 126 | return { deleted: true }; 127 | }, 128 | runMission: async (parent, body, context, info) => { 129 | const result = await runMission(body.id); 130 | return result; 131 | }, 132 | }, 133 | }; 134 | 135 | export default resolvers; 136 | -------------------------------------------------------------------------------- /src/app/api/graphql/route.ts: -------------------------------------------------------------------------------- 1 | import { startServerAndCreateNextHandler } from "@as-integrations/next"; 2 | import { ApolloServer } from "@apollo/server"; 3 | import { NextRequest, NextResponse } from "next/server"; 4 | import typeDefs from "./schema"; 5 | import resolvers from "./resolvers"; 6 | 7 | const server = new ApolloServer({ 8 | resolvers, 9 | typeDefs, 10 | }); 11 | 12 | const handler = startServerAndCreateNextHandler(server, { 13 | context: async (req, res) => ({ 14 | req, 15 | res, 16 | dataSources: {}, 17 | }), 18 | }); 19 | export async function GET(request: NextRequest) { 20 | return handler(request); 21 | } 22 | export async function POST(request: NextRequest) { 23 | return handler(request); 24 | } 25 | -------------------------------------------------------------------------------- /src/app/api/graphql/schema.ts: -------------------------------------------------------------------------------- 1 | const typeDefs = `#graphql 2 | enum AgentTool { 3 | DUCK_DUCK_GO_SEARCH 4 | SEMANTIC_SCHOLER 5 | WIKIDATA 6 | WIKIPEDIA 7 | YAHOO_FINANCE 8 | YUOUTUBE_SEARCH 9 | } 10 | 11 | type Agent { 12 | id: ID! 13 | role: String! 14 | goal: String! 15 | backstory: String 16 | tools: [AgentTool!]! 17 | allowDelegation: Boolean! 18 | verbose: Boolean! 19 | image: String 20 | missions: [Mission!] 21 | } 22 | 23 | input AgentInput { 24 | id: ID! 25 | } 26 | 27 | type DeleteOutput { 28 | deleted: Boolean! 29 | } 30 | 31 | type Task { 32 | name: String! 33 | description: String! 34 | agent: Agent 35 | } 36 | 37 | input TaskInput { 38 | name: String! 39 | description: String! 40 | agent: Int 41 | } 42 | 43 | type Mission { 44 | id: ID! 45 | name: String! 46 | crew: [Agent!] 47 | tasks: [Task] 48 | verbose: Boolean 49 | process: MissionProcess 50 | result: String 51 | } 52 | 53 | enum MissionProcess { 54 | SEQUENTIAL 55 | HIERARCHICAL 56 | } 57 | 58 | type Query { 59 | agents(filter: String): [Agent!]! 60 | agent(id: Int!): Agent 61 | missions(filter: String): [Mission!]! 62 | mission(id: Int!): Mission 63 | } 64 | 65 | type Mutation { 66 | createAgent( 67 | role: String! 68 | goal: String! 69 | backstory: String 70 | tools: [AgentTool!] = [] 71 | allowDelegation: Boolean = false 72 | verbose: Boolean = false 73 | ): Agent! 74 | 75 | updateAgent( 76 | id: Int! 77 | role: String 78 | goal: String 79 | backstory: String 80 | tools: [AgentTool!] 81 | allowDelegation: Boolean 82 | verbose: Boolean 83 | ): Agent! 84 | 85 | deleteAgent(id: Int!): DeleteOutput 86 | 87 | createMission( 88 | name: String! 89 | crew: [Int!] = [] 90 | tasks: [TaskInput!] = [] 91 | verbose: Boolean = false 92 | process: MissionProcess = "SEQUENTIAL" 93 | ): Mission! 94 | 95 | updateMission( 96 | id: Int! 97 | name: String 98 | crew: [Int!] 99 | tasks: [TaskInput!] 100 | verbose: Boolean 101 | process: MissionProcess 102 | ): Mission 103 | 104 | deleteMission(id: Int!): DeleteOutput 105 | 106 | runMission(id: Int!): Mission 107 | } 108 | `; 109 | 110 | export default typeDefs; 111 | -------------------------------------------------------------------------------- /src/app/api/upload_agent_image/route.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs"; 3 | import { v4 as uuidv4 } from "uuid"; 4 | import { NextRequest, NextResponse } from "next/server"; 5 | import prisma from "@/utils/prisma"; 6 | 7 | export async function POST(request: NextRequest) { 8 | const data = await request.formData(); 9 | const file: File | null = data.get("image") as unknown as File; 10 | 11 | if (!file) { 12 | return NextResponse.json({ 13 | success: false, 14 | error: "There is no file or the file is corrupted", 15 | }); 16 | } 17 | 18 | // With the file data in the buffer, you can do whatever you want with it. 19 | // For this, we'll just write it to the filesystem in a new location 20 | const bytes = await file.arrayBuffer(); 21 | const buffer = Buffer.from(bytes); 22 | 23 | const filename = `${uuidv4()}-${new Date().getTime()}-${file.name}`; 24 | const storagePath = path.join( 25 | process.cwd(), 26 | "public", 27 | "agents_images", 28 | filename 29 | ); 30 | fs.writeFileSync(storagePath, buffer, { flag: "w" }); 31 | 32 | const imageURL = `/agents_images/${filename}`; 33 | await prisma.agent.update({ 34 | // @ts-ignore 35 | where: { id: Number.parseInt(data.get("agent_id")) }, 36 | data: { 37 | image: imageURL, 38 | }, 39 | }); 40 | 41 | return NextResponse.json({ success: true, url: imageURL }); 42 | } 43 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazingnerd/CrewAI-UI/cd041b569371ad0fcac5a5a534f41c3269d3aa90/src/app/favicon.ico -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | @layer utilities { 30 | .text-balance { 31 | text-wrap: balance; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "@/styles/globals.css"; 2 | 3 | import type { Metadata } from "next"; 4 | import { Inter } from "next/font/google"; 5 | 6 | import BottomNav from "@/components/bottom_nav"; 7 | import MaxWidthWrapper from "@/components/max_width_wrapper"; 8 | import SideNav from "@/components/side_nav"; 9 | import TopNav from "@/components/top_nav"; 10 | import { ApolloWrapper } from "@/utils/apollo_wrapper"; 11 | 12 | const inter = Inter({ subsets: ["latin"] }); 13 | 14 | export const metadata: Metadata = { 15 | title: "CrewAI Visualizer", 16 | description: "Interactive user interface for CrewAI package.", 17 | }; 18 | 19 | export default function RootLayout({ 20 | children, 21 | }: { 22 | children: React.ReactNode; 23 | }) { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 |
33 |
37 | {children} 38 |
39 |
40 |
41 |
42 | 43 |
44 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/app/missions/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import MissionModal from "@/components/modals/mission_modal"; 4 | import NewMissionModal from "@/components/modals/new_mission_modal"; 5 | import { Mission } from "@/types/mission"; 6 | import { GET_MISSIONS } from "@/utils/graphql_queries"; 7 | import { useQuery } from "@apollo/client"; 8 | import { Icon } from "@iconify/react/dist/iconify.js"; 9 | import { Button, IconButton } from "@material-tailwind/react"; 10 | import { useState } from "react"; 11 | 12 | const MissionsPage = () => { 13 | const [showMissionModal, setShowMissionModal] = useState(false); 14 | const [showNewMissionModal, setShowNewMissionModal] = useState(false); 15 | 16 | const [selectedMission, setSelectedMission] = useState(); 17 | 18 | const { loading, error, data, refetch } = useQuery(GET_MISSIONS); 19 | 20 | if (loading) { 21 | return ( 22 | 30 | ); 31 | } 32 | 33 | return ( 34 |
35 |
36 | { 41 | setShowNewMissionModal(true); 42 | }} 43 | > 44 | 45 | 46 | { 50 | refetch(); 51 | }} 52 | /> 53 |
54 |
55 | {data?.missions.map((mission: Mission, i: number) => ( 56 |
57 |
{ 60 | setSelectedMission(mission); 61 | setShowMissionModal(true); 62 | }} 63 | > 64 |

{mission.name}

65 |
66 |
67 | ))} 68 | { 73 | refetch(); 74 | }} 75 | onRunMission={() => { 76 | refetch(); 77 | }} 78 | onDeleteMission={() => { 79 | refetch(); 80 | }} 81 | /> 82 |
83 |
84 | ); 85 | }; 86 | 87 | export default MissionsPage; 88 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { HeroSection } from "@/components/ui/hero"; 2 | 3 | export default function Home() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/bottom_nav.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Link from "next/link"; 4 | import useNavigation from "@/hook/use_navigation"; 5 | import { Icon } from "@iconify/react"; 6 | 7 | const BottomNav = () => { 8 | const { isMissionsActive, isAgentsActive } = useNavigation(); 9 | 10 | return ( 11 |
14 |
15 | 19 | 20 | 21 | 22 | 26 | 27 | 28 |
29 |
30 | ); 31 | }; 32 | 33 | export default BottomNav; 34 | -------------------------------------------------------------------------------- /src/components/inputs/file.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function TWFileInput(props: { 4 | accept?: string; 5 | disabled?: boolean; 6 | multiple?: boolean; 7 | onChange?: (event: React.ChangeEvent) => void; 8 | }) { 9 | const { accept, disabled, multiple, onChange = () => {} } = props; 10 | return ( 11 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/inputs/mission_tasks_editor.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { selectTheme } from "@/data/consts"; 4 | import { Agent } from "@/types/agent"; 5 | import { Mission } from "@/types/mission"; 6 | import { Task } from "@/types/task"; 7 | import { Button } from "@material-tailwind/react"; 8 | import React, { useState } from "react"; 9 | import { TESelect } from "tw-elements-react"; 10 | 11 | interface MissionTaskEditorProps { 12 | mission: Mission; 13 | agents: Array; 14 | onMissionChange: (updatedMission: Mission) => void; 15 | } 16 | 17 | const MissionTaskEditor: React.FC = ({ 18 | mission, 19 | agents = [], 20 | onMissionChange, 21 | }) => { 22 | const [newTaskName, setNewTaskName] = useState(""); 23 | const [newTaskAgent, setNewTaskAgent] = useState(null); 24 | const [newTaskDescription, setNewTaskDescription] = useState(""); 25 | 26 | const handleAddTask = () => { 27 | const newTask: Task = { 28 | name: newTaskName, 29 | description: newTaskDescription, 30 | agent: newTaskAgent, 31 | }; 32 | const updatedTasks = [...(mission?.tasks ?? []), newTask]; 33 | const updatedMission: Mission = { ...mission, tasks: updatedTasks }; 34 | onMissionChange(updatedMission); 35 | setNewTaskName(""); 36 | setNewTaskDescription(""); 37 | }; 38 | 39 | const handleRemoveTask = (index: number) => { 40 | const updatedTasks = [...(mission?.tasks ?? [])]; 41 | updatedTasks.splice(index, 1); 42 | const updatedMission: Mission = { ...mission, tasks: updatedTasks }; 43 | onMissionChange(updatedMission); 44 | }; 45 | 46 | return ( 47 |
48 |

Mission Tasks

49 | {mission?.tasks?.map((task, index) => ( 50 |
51 |
52 |

{task.name}

53 | 60 |
61 |
{task.description}
62 |
63 | Agent: 64 | 65 | {task.agent?.role ?? "No Agent"} 66 | 67 |
68 |
69 | ))} 70 |
71 |
72 | Add New Task: 73 |
74 |
75 |
76 | 77 | setNewTaskName(e.target.value)} 82 | className="border border-gray-300 text-black rounded px-3 py-1" 83 | /> 84 |
85 |
86 | 87 |
88 |