chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:43:34 +08:00
commit 41fe82d331
768 changed files with 57351 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
alerts:
- rule: DEPLOYMENT_FAILED
- rule: DOMAIN_FAILED
databases:
- engine: PG
name: super-agi-main
num_nodes: 1
size: basic-xs
version: "12"
ingress:
rules:
- component:
name: superagi-backend
match:
path:
prefix: /api
name: superagi
services:
- dockerfile_path: DockerfileRedis
github:
branch: main
deploy_on_push: true
repo: TransformerOptimus/SuperAGI
internal_ports:
- 6379
instance_count: 1
instance_size_slug: basic-xs
source_dir: /
name: superagi-redis
- dockerfile_path: Dockerfile
envs:
- key: REDIS_URL
scope: RUN_TIME
value: superagi-redis:6379
- key: DB_URL
scope: RUN_TIME
value: ${super-agi-main.DATABASE_URL}
github:
branch: main
deploy_on_push: true
repo: TransformerOptimus/SuperAGI
http_port: 8001
instance_count: 1
instance_size_slug: basic-xs
run_command: /app/entrypoint.sh
source_dir: /
name: superagi-backend
- dockerfile_path: ./gui/DockerfileProd
github:
branch: main
deploy_on_push: true
repo: TransformerOptimus/SuperAGI
http_port: 3000
instance_count: 1
instance_size_slug: basic-xs
source_dir: ./gui
name: superagi-gui
workers:
- dockerfile_path: Dockerfile
envs:
- key: REDIS_URL
scope: RUN_TIME
value: superagi-redis:6379
- key: DB_URL
scope: RUN_TIME
value: ${super-agi-main.DATABASE_URL}
github:
branch: main
deploy_on_push: true
repo: TransformerOptimus/SuperAGI
instance_count: 1
instance_size_slug: basic-xs
run_command: celery -A superagi.worker worker --beat --loglevel=info
source_dir: /
name: superagi-celery
+73
View File
@@ -0,0 +1,73 @@
spec:
alerts:
- rule: DEPLOYMENT_FAILED
- rule: DOMAIN_FAILED
databases:
- engine: PG
name: super-agi-main
num_nodes: 1
size: basic-xs
version: "12"
ingress:
rules:
- component:
name: superagi-backend
match:
path:
prefix: /api
name: superagi
services:
- dockerfile_path: DockerfileRedis
git:
branch: main
repo_clone_url: https://github.com/TransformerOptimus/SuperAGI.git
internal_ports:
- 6379
instance_count: 1
instance_size_slug: basic-xs
source_dir: /
name: superagi-redis
- dockerfile_path: Dockerfile
envs:
- key: REDIS_URL
scope: RUN_TIME
value: superagi-redis:6379
- key: DB_URL
scope: RUN_TIME
value: ${super-agi-main.DATABASE_URL}
git:
branch: main
repo_clone_url: https://github.com/TransformerOptimus/SuperAGI.git
http_port: 8001
instance_count: 1
instance_size_slug: basic-xs
run_command: /app/entrypoint.sh
source_dir: /
name: superagi-backend
- dockerfile_path: ./gui/DockerfileProd
git:
branch: main
repo_clone_url: https://github.com/TransformerOptimus/SuperAGI.git
http_port: 3000
instance_count: 1
instance_size_slug: basic-xs
source_dir: ./gui
name: superagi-gui
workers:
- dockerfile_path: Dockerfile
envs:
- key: REDIS_URL
scope: RUN_TIME
value: superagi-redis:6379
- key: DB_URL
scope: RUN_TIME
value: ${super-agi-main.DATABASE_URL}
git:
branch: main
repo_clone_url: https://github.com/TransformerOptimus/SuperAGI.git
instance_count: 1
instance_size_slug: basic-xs
run_command: celery -A superagi.worker worker --beat --loglevel=info
source_dir: /
name: superagi-celery
+18
View File
@@ -0,0 +1,18 @@
# Ignore everything
**
# Allow files and directories
!/migrations
!/nginx
!/superagi
!/tgwui
!/tools
!/workspace
!/main.py
!/requirements.txt
!/entrypoint.sh
!/entrypoint_celery.sh
!/wait-for-it.sh
!/tools.json
!/install_tool_dependencies.sh
!/alembic.ini
+1
View File
@@ -0,0 +1 @@
*.sh text eol=lf
+148
View File
@@ -0,0 +1,148 @@
name: Bug report
description: Create a bug report for SuperAGI.
labels: ['status: needs triage']
body:
- type: markdown
attributes:
value: |
### ⚠️ Issue Creation Guideline
* Check out our [roadmap] and join our [discord] to discuss what's going on
* If you need help, you can ask in the [#general] section or in [#support]
* **Thoroughly search the [existing issues] before creating a new one**
* Read through our docs:
[roadmap]: https://github.com/users/TransformerOptimus/projects/5
[discord]: https://discord.gg/dXbRe5BHJC
[#general]: https://discord.com/channels/1107593006032355359/1107642413993959505
[#support]: https://discord.com/channels/1107593006032355359/1107645922797703198
[existing issues]: https://github.com/TransformerOptimus/SuperAGI/issues
- type: checkboxes
attributes:
label: ⚠️ Check for existing issues before proceeding. ⚠️
description: >
Please [search the history](https://github.com/TransformerOptimus/SuperAGI/issues)
to see if an issue already exists for the same problem.
options:
- label: I have searched the existing issues, and there is no existing issue for my problem
required: true
- type: markdown
attributes:
value: |
Please confirm that the issue you have is described well and precise in the title above ⬆️.
Think like this: What would you type if you were searching for the issue?
For example:
❌ - my SuperAGI agent keeps looping
✅ - After performing Write Tool, SuperAGI goes into a loop where it keeps trying to write the file.
Please help us help you by following these steps:
- Search for existing issues, adding a comment when you have the same or similar issue is tidier than "new issue" and
newer issues will not be reviewed earlier, this is dependent on the current priorities set by our wonderful team
- Ask on our Discord if your issue is known when you are unsure (https://discord.gg/dXbRe5BHJC)
- Provide relevant info:
- Provide Docker Logs(docker compose logs) whenever possible.
- If it's a pip/packages issue, mention this in the title and provide pip version, python version.
- type: dropdown
attributes:
label: Where are you using SuperAGI?
description: >
Please select the operating system you were using to run SuperAGI when this problem occurred.
options:
- Windows
- Linux
- MacOS
- Codespaces
- Web Version
- Other
validations:
required: true
nested_fields:
- type: text
attributes:
label: Specify the system
description: Please specify the system you are working on.
- type: dropdown
attributes:
label: Which branch of SuperAGI are you using?
description: |
Please select which version of SuperAGI you were using when this issue occurred.
If installed with git you can run `git branch` to see which version of Auto-GPT you are running.
options:
- Main
- Dev (branch)
validations:
required: true
- type: dropdown
attributes:
label: Do you use OpenAI GPT-3.5 or GPT-4?
description: >
If you are using SuperAGI with GPT-3.5, your problems may be caused by
the limitations of GPT-3.5 like incorrect Tool selection thus causing looping in the agent feed.
options:
- GPT-3.5
- GPT-3.5(16k)
- GPT-4
- GPT-4(32k)
validations:
required: true
- type: dropdown
attributes:
label: Which area covers your issue best?
description: >
Select the area related to the issue you are reporting.
options:
- Installation and setup
- Resource Manager
- Action Console
- Performance
- Marketplace
- Prompt
- Tools
- Agents
- Documentation
- Logging
- Other
validations:
required: true
autolabels: true
nested_fields:
- type: text
attributes:
label: Specify the area
description: Please specify the area you think is best related to the issue.
- type: textarea
attributes:
label: Describe your issue.
description: Describe the problem you are experiencing. Try to describe only the issue and phrase it short but clear. ⚠️ Provide NO other data in this field
validations:
required: true
- type: textarea
attributes:
label: How to replicate your Issue?
description: |
Mention Agent Name, Agent Description and Agent Goals, along with Model selected.
Provide any other data which might be relevant for us to replicate this issue.
⚠️ Provide NO other data in this field
validations:
required: false
- type: markdown
attributes:
value: |
⚠️ Please keep in mind that the log files may contain personal information such as credentials. Make sure you hide them before copy/pasting it! ⚠️
- type: input
attributes:
label: Upload Error Log Content
description: |
Upload the error log content, this can help us understand the issue better.
To do this, you can simply copy the logs from the terminal with which you did 'docker compose up' or in a new terminal,
enter 'docker compose logs' and copy/paste the error contents to this field.
⚠️ The activity log may contain personal data given to SuperAGI by you in prompt or input as well as
any personal information that SuperAGI collected out of files during last run. Please hide them before sharing. ⚠️
validations:
required: True
+34
View File
@@ -0,0 +1,34 @@
<!-- Thank you for submitting this PR! :) -->
<!-- Provide a general summary of your changes in the Title above ^, end with (close #<issue-no>) or (fix #<issue-no>) -->
### Description
<!-- The title might not be enough to convey how this change affects the user. -->
<!-- Describe the changes from a user's perspective -->
<!-- Changelog Section End -->
### Related Issues
<!-- Please make sure you have an issue associated with this Pull Request -->
<!-- And then add `(close #<issue-no>)` to the pull request title -->
<!-- Add the issue number below (e.g. #234) -->
### Solution and Design
<!-- How is this issue solved/fixed? What is the design? -->
<!-- It's better if we elaborate -->
### Test Plan
<!-- Describe how you tested this functionality. Include steps to reproduce, relevant test cases, and any other pertinent information. -->
### Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Docs update
### Checklist
- [ ] My pull request is atomic and focuses on a single change.
- [ ] I have read the contributing guide and my code conforms to the guidelines.
- [ ] I have documented my changes clearly and comprehensively.
- [ ] I have added the required tests.
+116
View File
@@ -0,0 +1,116 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Python CI
on:
push:
branches: [ "main", "dev" ]
pull_request:
branches: [ "main", "dev" ]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up Python 3.9
uses: actions/setup-python@v3
with:
python-version: "3.9"
- name: Cache Python dependencies
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
test:
permissions:
# Gives the action the necessary permissions for publishing new
# comments in pull requests.
pull-requests: write
# Gives the action the necessary permissions for pushing data to the
# python-coverage-comment-action branch, and for editing existing
# comments (to avoid publishing multiple comments in the same PR)
contents: write
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
python-version: ["3.9"]
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
submodules: true
- name: Configure git user SuperAGI-Bot
run: |
git config --global user.name "SuperAGI-Bot"
git config --global user.email "github-bot@superagi.com"
- name: Set up Python 3.9
uses: actions/setup-python@v3
with:
python-version: "3.9"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Test with pytest
run: |
pytest --cov=superagi --cov-branch --cov-report term-missing --cov-report xml \
tests/unit_tests -s
env:
CI: true
ENV: DEV
PLAIN_OUTPUT: True
REDIS_URL: "localhost:6379"
IS_TESTING: True
ENCRYPTION_KEY: "abcdefghijklmnopqrstuvwxyz123456"
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
+65
View File
@@ -0,0 +1,65 @@
name: "CodeQL"
on:
push:
branches: [ 'main', 'dev' ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ 'main' ]
schedule:
- cron: '48 0 * * 2'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
+23
View File
@@ -0,0 +1,23 @@
.idea
**/.env
**/.venv
config.yaml
__pycache__
superagi/models/__pycache__
superagi/controllers/__pycache__
**agent_dictvenv
**/__gitpycache__/
gui/node_modules
node_modules
gui/.next
.DS_Store
.DS_Store?
venv
workspace/output
workspace/input
celerybeat-schedule
../bfg-report*
superagi/tools/marketplace_tools/
superagi/tools/external_tools/
tests/unit_tests/resource_manager/test_path
/tools.json
+8
View File
@@ -0,0 +1,8 @@
repos:
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+79
View File
@@ -0,0 +1,79 @@
# ⚡ Contributing to SuperAGI
<p align="center">
<a href="https://superagi.com//#gh-light-mode-only">
<img src="https://superagi.com/wp-content/uploads/2023/05/Logo-dark.svg" width="318px" alt="SuperAGI logo" />
</a>
<a href="https://superagi.com//#gh-dark-mode-only">
<img src="https://superagi.com/wp-content/uploads/2023/05/Logo-light.svg" width="318px" alt="SuperAGI logo" />
</a>
</p>
First of all, thank you for taking the time to contribute to this project. We truly appreciate your contributions, whether it's bug reports, feature suggestions, or pull requests. Your time and effort are highly valued in this project. 🚀
This document provides guidelines and best practices to help you to contribute effectively. These are meant to serve as guidelines, not strict rules. We encourage you to use your best judgment and feel comfortable proposing changes to this document through a pull request.
For all contributions, a CLA (Contributor License Agreement) needs to be signed
[here](https://cla-assistant.io/TransformerOptimus/SuperAGI) before (or after) the pull request has been submitted.
**********************************Table of Content:**********************************
1. [Code of conduct](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#code-of-conduct)
2. [Quick Start](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#quick-start)
3. [Contributing Guidelines](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#contributing-guidelines)
1. [Reporting Bugs](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#reporting-bugs)
2. [New Feature or Suggesting Enhancements](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#new-feature-or-suggesting-enhancements)
4. [Testing](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#testing-changes)
5. [Pull Requests](https://github.com/TransformerOptimus/SuperAGI/blob/CONTRIBUTING.md#pull-requests)
## ✔️ Code of Conduct:
Please read our [Code of Conduct](https://github.com/TransformerOptimus/SuperAGI/blob/main/CODE_OF_CONDUCT.md) to understand the expectations we have for all contributors participating in this project. By participating, you agree to abide by our Code of Conduct.
## 🚀 Quick Start
You can quickly get started with contributing by searching for issues with the labels **"Good First Issue"** or **"Help Needed"** in the [Issues Section](https://github.com/TransformerOptimus/SuperAGI/Issues). If you think you can contribute, comment on the issue and we will assign it to you.
To set up your development environment, please follow the steps mentioned below :
1. Fork the repository and create a clone of the fork
2. Create a branch for a feature or a bug you are working on in your fork
3. Once you've created your branch, follow the instructions in the [README.MD](https://github.com/TransformerOptimus/SuperAGI/README.MD)
## Contributing Guidelines
### 🔍 Reporting Bugs
You can start working on an existing bug that has been reported and labeled as **"Bug"** in the Issues Section, and you can report your bugs in the following manner :
1. Title describing the issue clearly and concisely with relevant labels
2. Provide a detailed description of the problem and the necessary steps to reproduce the issue.
3. Include any relevant logs, screenshots, or other helpful information supporting the issue.
### :bulb: New Feature or Suggesting Enhancements
This section guides you through working on an enhancement **Including a completely New Feature** & **Enhancements to an existing functionality**.
Before getting started, Perform a search on Issues to see if the enhancement or feature has already been suggested and picked up. If the feature or enhancement is suggested and not picked up, comment on the issue and assign yourself to it.
If the feature or enhancement is not in the issues, find out whether your idea fits with the scope and aims of the project by looking at the [Roadmap](https://github.com/users/TransformerOptimus/projects/5/). If yes, raise an issue with the label **"Feature Request"** in the following manner:
1. Title describing the feature or enhancement in a clear and concise manner
2. Clearly describe the proposed enhancement, highlighting its benefits and potential drawbacks.
3. Provide examples and supporting information.
Once you have raised the issue and have gotten yourself assigned, you can start working on the feature or enhancement. Please make sure the feature or enhancement you're working on is placed on the Roadmap.
## Testing your Changes
Each method or the function of the code should have a unit test with the maximum coverage possible and on each Pull Request, we have GitHub Actions triggered, which
runs all the unit tests where all the tests should pass for merging the Pull Request.
## Pull Request
Now that you have worked on your code and tested it thoroughly, you can now go ahead and raise the pull request. Please make sure that the Pull Request adheres to the following guidelines:
1. The pull request is atomic and focuses on a single change.
2. You have read the contributing guide and your code conforms to the guidelines.
3. You have documented your changes clearly and comprehensively.
4. You have added the required tests.
+39
View File
@@ -0,0 +1,39 @@
# Stage 1: Compile image
FROM python:3.10-slim-bullseye AS compile-image
WORKDIR /app
RUN apt-get update && \
apt-get install --no-install-recommends -y wget libpq-dev gcc g++ && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
RUN python3.10 -c "import nltk; nltk.download('punkt')" && \
python3.10 -c "import nltk; nltk.download('averaged_perceptron_tagger')"
COPY . .
RUN chmod +x ./entrypoint.sh ./wait-for-it.sh ./install_tool_dependencies.sh ./entrypoint_celery.sh
# Stage 2: Build image
FROM python:3.10-slim-bullseye AS build-image
WORKDIR /app
RUN apt-get update && \
apt-get install --no-install-recommends -y libpq-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
COPY --from=compile-image /opt/venv /opt/venv
COPY --from=compile-image /app /app
COPY --from=compile-image /root/nltk_data /root/nltk_data
ENV PATH="/opt/venv/bin:$PATH"
EXPOSE 8001
+45
View File
@@ -0,0 +1,45 @@
# Define the CUDA SDK version you need
ARG CUDA_IMAGE="12.1.1-devel-ubuntu22.04"
FROM nvidia/cuda:${CUDA_IMAGE}
ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /app
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y git build-essential \
python3 python3-pip python3.10-venv libpq-dev gcc wget \
ocl-icd-opencl-dev opencl-headers clinfo \
libclblast-dev libopenblas-dev \
&& mkdir -p /etc/OpenCL/vendors && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd
# Create a virtual environment and activate it
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Python dependencies from requirements.txt
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Running nltk setup as you mentioned
RUN python3.10 -c "import nltk; nltk.download('punkt')" && \
python3.10 -c "import nltk; nltk.download('averaged_perceptron_tagger')"
# Copy the application code
COPY . .
ENV CUDA_DOCKER_ARCH=all
ENV LLAMA_CUBLAS=1
RUN CMAKE_ARGS="-DLLAMA_CUBLAS=on" pip install llama-cpp-python==0.2.7 --force-reinstall --upgrade --no-cache-dir
# Make necessary scripts executable
RUN chmod +x ./entrypoint.sh ./wait-for-it.sh ./install_tool_dependencies.sh ./entrypoint_celery.sh
# Set environment variable to point to the custom libllama.so
# ENV LLAMA_CPP_LIB=/app/llama.cpp/libllama.so
EXPOSE 8001
CMD ["./entrypoint.sh"]
+32
View File
@@ -0,0 +1,32 @@
FROM python:3.9
WORKDIR /app
#RUN apt-get update && apt-get install --no-install-recommends -y git wget libpq-dev gcc python3-dev && pip install psycopg2
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Downloads the tools
RUN python superagi/tool_manager.py
# Set executable permissions for install_tool_dependencies.sh
RUN chmod +x install_tool_dependencies.sh
# Install dependencies
RUN ./install_tool_dependencies.sh
# Downloads the tools
RUN python superagi/tool_manager.py
# Set executable permissions for install_tool_dependencies.sh
RUN chmod +x install_tool_dependencies.sh
# Install dependencies
RUN ./install_tool_dependencies.sh
CMD ["celery", "-A", "superagi.worker", "worker", "--beat","--loglevel=info"]
+1
View File
@@ -0,0 +1 @@
FROM redis/redis-stack-server:latest
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 TransformerOptimus
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
+242
View File
@@ -0,0 +1,242 @@
<p align="center">
<a href="https://superagi.com//#gh-light-mode-only">
<img src="https://superagi.com/wp-content/uploads/2023/05/Logo-dark.svg" width="318px" alt="SuperAGI logo" />
</a>
<a href="https://superagi.com//#gh-dark-mode-only">
<img src="https://superagi.com/wp-content/uploads/2023/05/Logo-light.svg" width="318px" alt="SuperAGI logo" />
</a>
</p>
<p align="center"><i>Open-source framework to build, manage and run useful Autonomous AI Agents</i></p>
<p align="center">
<a href="https://superagi.com"> <img src="https://superagi.com/wp-content/uploads/2023/08/Website.svg"></a>
<a href="https://app.superagi.com"> <img src="https://superagi.com/wp-content/uploads/2023/07/Cloud.svg"></a>
<a href="https://marketplace.superagi.com/"> <img src="https://superagi.com/wp-content/uploads/2023/08/Marketplace.svg"></a>
<a href="https://superagi.com/docs/"> <img src="https://superagi.com/wp-content/uploads/2023/08/Docs.svg"></a>
<a href="https://documenter.getpostman.com/view/28438662/2s9Xy6rqP5"> <img src="https://superagi.com/wp-content/uploads/2023/08/APIs.svg"></a>
</p>
<p align="center">
<a href="https://github.com/TransformerOptimus/SuperAGI/fork" target="blank">
<img src="https://img.shields.io/github/forks/TransformerOptimus/SuperAGI?style=for-the-badge" alt="SuperAGI forks"/>
</a>
<a href="https://github.com/TransformerOptimus/SuperAGI/stargazers" target="blank">
<img src="https://img.shields.io/github/stars/TransformerOptimus/SuperAGI?style=for-the-badge" alt="SuperAGI stars"/>
</a>
<a href='https://github.com/TransformerOptimus/SuperAGI/releases'>
<img src='https://img.shields.io/github/release/TransformerOptimus/SuperAGI?&label=Latest&style=for-the-badge'>
</a>
<a href="https://github.com/TransformerOptimus/SuperAGI/commits" target="blank">
<img src="https://img.shields.io/github/commits-since/TransformerOptimus/SuperAGI/v0.0.11.svg?style=for-the-badge" alt="SuperAGI Commits"/>
</a>
</p>
<p align="center"><b>Follow SuperAGI </b></p>
<p align="center">
<a href="https://twitter.com/_superAGI" target="blank">
<img src="https://img.shields.io/twitter/follow/_superAGI?label=Follow: _superAGI&style=social" alt="Follow _superAGI"/>
</a>
<a href="https://www.reddit.com/r/Super_AGI" target="_blank"><img src="https://img.shields.io/twitter/url?label=/r/Super_AGI&logo=reddit&style=social&url=https://github.com/TransformerOptimus/SuperAGI"/></a>
<a href="https://discord.gg/dXbRe5BHJC" target="blank">
<img src="https://img.shields.io/discord/1107593006032355359?label=Join%20SuperAGI&logo=discord&style=social" alt="Join SuperAGI Discord Community"/>
</a>
<a href="https://www.youtube.com/@_superagi" target="_blank"><img src="https://img.shields.io/twitter/url?label=Youtube&logo=youtube&style=social&url=https://github.com/TransformerOptimus/SuperAGI"/></a>
</p>
<p align="center"><b>Connect with the Creator </b></p>
<p align="center">
<a href="https://twitter.com/ishaanbhola" target="blank">
<img src="https://img.shields.io/twitter/follow/ishaanbhola?label=Follow: ishaanbhola&style=social" alt="Follow ishaanbhola"/>
</a>
</p>
<p align="center"><b>Share SuperAGI Repository</b></p>
<p align="center">
<a href="https://twitter.com/intent/tweet?text=Check%20this%20GitHub%20repository%20out.%20SuperAGI%20-%20Let%27s%20you%20easily%20build,%20manage%20and%20run%20useful%20autonomous%20AI%20agents.&url=https://github.com/TransformerOptimus/SuperAGI&hashtags=SuperAGI,AGI,Autonomics,future" target="blank">
<img src="https://img.shields.io/twitter/follow/_superAGI?label=Share Repo on Twitter&style=social" alt="Follow _superAGI"/></a>
<a href="https://t.me/share/url?text=Check%20this%20GitHub%20repository%20out.%20SuperAGI%20-%20Let%27s%20you%20easily%20build,%20manage%20and%20run%20useful%20autonomous%20AI%20agents.&url=https://github.com/TransformerOptimus/SuperAGI" target="_blank"><img src="https://img.shields.io/twitter/url?label=Telegram&logo=Telegram&style=social&url=https://github.com/TransformerOptimus/SuperAGI" alt="Share on Telegram"/></a>
<a href="https://api.whatsapp.com/send?text=Check%20this%20GitHub%20repository%20out.%20SuperAGI%20-%20Let's%20you%20easily%20build,%20manage%20and%20run%20useful%20autonomous%20AI%20agents.%20https://github.com/TransformerOptimus/SuperAGI"><img src="https://img.shields.io/twitter/url?label=whatsapp&logo=whatsapp&style=social&url=https://github.com/TransformerOptimus/SuperAGI" /></a> <a href="https://www.reddit.com/submit?url=https://github.com/TransformerOptimus/SuperAGI&title=Check%20this%20GitHub%20repository%20out.%20SuperAGI%20-%20Let's%20you%20easily%20build,%20manage%20and%20run%20useful%20autonomous%20AI%20agents.
" target="blank">
<img src="https://img.shields.io/twitter/url?label=Reddit&logo=Reddit&style=social&url=https://github.com/TransformerOptimus/SuperAGI" alt="Share on Reddit"/>
</a> <a href="mailto:?subject=Check%20this%20GitHub%20repository%20out.&body=SuperAGI%20-%20Let%27s%20you%20easily%20build,%20manage%20and%20run%20useful%20autonomous%20AI%20agents.%3A%0Ahttps://github.com/TransformerOptimus/SuperAGI" target="_blank"><img src="https://img.shields.io/twitter/url?label=Gmail&logo=Gmail&style=social&url=https://github.com/TransformerOptimus/SuperAGI"/></a> <a href="https://www.buymeacoffee.com/superagi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="23" width="100" style="border-radius:1px"></a>
</p>
<hr>
## What are we ?
A dev-first open source autonomous AI agent framework enabling developers to build, manage & run useful autonomous agents. You can run concurrent agents seamlessly, extend agent capabilities with tools. The agents efficiently perform a variety of tasks and continually improve their performance with each subsequent run.
### 💡 Features
- <b>Provision, Spawn & Deploy Autonomous AI Agents</b> - Create production-ready & scalable autonomous agents.
- <b>Extend Agent Capabilities with Toolkits</b> - Add Toolkits from our marketplace to your agent workflows.
- <b>Graphical User Interface</b> - Access your agents through a graphical user interface.
- <b>Action Console</b> - Interact with agents by giving them input and permissions.
- <b>Multiple Vector DBs</b> - Connect to multiple Vector DBs to enhance your agents performance.
- <b>Performance Telemetry</b> - Get insights into your agents performance and optimize accordingly.
- <b>Optimized Token Usage</b> - Control token usage to manage costs effectively.
- <b>Agent Memory Storage</b> - Enable your agents to learn and adapt by storing their memory.
- <b>Models</b> - Custom fine tuned models for business specific usecases.
- <b>Workflows</b> - Automate tasks with ease using ReAct LLM's predefined steps.
### 🛠 Toolkits
Toolkits allow SuperAGI Agents to interact with external systems and third-party plugins.
<a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Twitter.png height=50px width=50px alt="Twitter" valign="middle" title="Twitter"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Coding.png height=50px width=50px alt="Coding Tool" valign="middle" title="Coding Tool"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Insta.png height=50px width=50px alt="Instagram" valign="middle" title="Instagram"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Knowledge_tool.png height=50px width=50px alt="Knowledge Search" valign="middle" title="Knowledge Search"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113612.png height=50px width=50px alt="Email" valign="middle" title="Email"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113610.png height=50px width=50px alt="Jira" valign="middle" title="Jira"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113611.png height=50px width=50px alt="File Manager" valign="middle" title="File Manager"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113613.png height=50px width=50px alt="Google Search" valign="middle" title="Google Search"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113615.png height=50px width=50px alt="Dall-E" valign="middle" title="Dall-E"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113614.png height=50px width=50px alt="Github" valign="middle" title="Github"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113616.png height=50px width=50px alt="Web Interaction" valign="middle" title="Web Interaction"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/05/Group-113622.png height=50px width=50px alt="Duckduckgo" valign="middle" title="Duckduckgo"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Calendar_tool.png height=50px width=50px alt="Google Calendar" valign="middle" title="Google Calendar"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Search_tool.png height=50px width=50px alt="Google Calendar" valign="middle" title="Google Search"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Serp.png height=50px width=50px alt="Serp API" valign="middle" title="Serp API"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Searx.png height=50px width=50px alt="Searx" valign="middle" title="Searx "></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Web_scraper_logo.png height=50px width=50px alt="Web Scraper" valign="middle" title="Web Scraper"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Notion_logo.png height=50px width=50px alt="Notion" valign="middle" title="Notion"></a> <a href="https://marketplace.superagi.com/" target="_blank"><img src=https://superagi.com/wp-content/uploads/2023/08/Apollo_logo.png height=50px width=50px alt="Apollo" valign="middle" title="Apollo"></a>
### ⚙️ Installation
You can install superAGI using one of the following three approaches.
#### ☁️ SuperAGI cloud
To quickly start experimenting with agents without the hassle of setting up the system, try [Superagi Cloud](https://app.superagi.com/)
1. Visit [Superagi Cloud](https://app.superagi.com/) and log in using your github account.
2. In your account settings, go to "Model Providers" and add your API key.
You're all set! Start running your agents effortlessly.
#### 🖥️ Local
1. Open your terminal and clone the SuperAGI repository.
```
git clone https://github.com/TransformerOptimus/SuperAGI.git
```
2. Navigate to the cloned repository directory using the command:
```
cd SuperAGI
```
3. Create a copy of config_template.yaml, and name it config.yaml.
4. Ensure that Docker is installed on your system. You can download and install it from [here](https://docs.docker.com/get-docker/).
5. Once you have Docker Desktop running, run the following command in the SuperAGI directory:
a. For regular usage:
```
docker compose -f docker-compose.yaml up --build
```
b. If you want to use SuperAGI with Local LLMs and have GPU, run the following command:
```
docker compose -f docker-compose-gpu.yml up --build
```
6. Open your web browser and navigate to http://localhost:3000 to access SuperAGI.
#### 🌀 Digital Ocean
<p align="left">
<a href="https://cloud.digitalocean.com/apps/new?repo=https://github.com/TransformerOptimus/SuperAGI/tree/main"> <img src="https://www.deploytodo.com/do-btn-blue.svg"></a><br>Deploy SuperAGI to DigitalOcean with one click.
</p>
<a id="architecture">
### 🌐 Architecture
</a>
<details>
<summary>SuperAGI Architecture</summary>
![SuperAGI Architecture](https://superagi.com/wp-content/uploads/2023/09/SuperAGI-Architecture.png)
</details>
<details>
<summary>Agent Architecture</summary>
![Agent Architecture](https://superagi.com/wp-content/uploads/2023/06/Agent-Architecture.png)
</details>
<details>
<summary>Agent Workflow Architecture</summary>
![Agent Workflow Architecture](https://superagi.com/wp-content/uploads/2023/09/Workflow-Architecture.png)
</details>
<details>
<summary>Tools Architecture</summary>
![Tools Architecture](https://superagi.com/wp-content/uploads/2023/09/Tools-Architecture.png)
</details>
<details>
<summary>ER Diagram</summary>
![ER Diagram](https://superagi.com/wp-content/uploads/2023/09/ER-Diagram.png)
</details>
### 📚 Resources
* [Documentation](https://superagi.com/docs/)
* [YouTube Channel](https://www.youtube.com/@_SuperAGI/videos)
### 📖 Need Help?
Join our [Discord community](https://discord.gg/dXbRe5BHJC) for support and discussions.
[![Join us on Discord](https://invidget.switchblade.xyz/uJ3XUGsY2R)](https://discord.gg/uJ3XUGsY2R)
If you have questions or encounter issues, please don't hesitate to [create a new issue](https://github.com/TransformerOptimus/SuperAGI/issues/new/choose) to get support.
### 💻 Contribution
We ❤️ our contributors. Were committed to fostering an open, welcoming, and safe environment in the community.
If you'd like to contribute, start by reading our [Contribution Guide](https://github.com/TransformerOptimus/SuperAGI/blob/main/CONTRIBUTING.md).
We expect everyone participating in the community to abide by our [Code of Conduct](https://github.com/TransformerOptimus/SuperAGI/blob/main/CODE_OF_CONDUCT.md).
To get more idea on where we are heading, checkout our roadmap [here](https://github.com/users/TransformerOptimus/projects/5/views/1).
Explore some [good first issues](https://github.com/TransformerOptimus/SuperAGI/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to start contributing.
### 👩‍💻 Contributors
[![TransformerOptimus](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/133493246?v=4&w=50&h=50&mask=circle)](https://github.com/TransformerOptimus) [![Cptsnowcrasher](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/133322218?v=4&w=50&h=50&mask=circle)](https://github.com/Cptsnowcrasher) [![vectorcrow](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/133646556?v=4&w=50&h=50&mask=circle)](https://github.com/vectorcrow) [![Akki-jain](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/92881074?v=4&w=50&h=50&mask=circle)](https://github.com/Akki-jain) [![Autocop-Agent](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/129729746?v=4&w=50&h=50&mask=circle)](https://github.com/Autocop-Agent)[![COLONAYUSH](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/60507126?v=4&w=50&h=50&mask=circle)](https://github.com/COLONAYUSH)[![luciferlinx101](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/129729795?v=4&w=50&h=50&mask=circle)](https://github.com/luciferlinx101)[![mukundans89](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/101278493?v=4&w=50&h=50&mask=circle)](https://github.com/mukundans89)[![Fluder-Paradyne](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/121793617?v=4&w=50&h=50&mask=circle)](https://github.com/Fluder-Paradyne)[![nborthy](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/101320057?v=4&w=50&h=50&mask=circle)](https://github.com/nborthy)[![nihirr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/122777244?v=4&w=50&h=50&mask=circle)](https://github.com/nihirr)[![Tarraann](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/97586318?v=4&w=50&h=50&mask=circle)](https://github.com/Tarraann)[![neelayan7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/43145646?v=4&w=50&h=50&mask=circle)](https://github.com/neelayan7)[![Arkajit-Datta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61142632?v=4&w=50&h=50&mask=circle)](https://github.com/Arkajit-Datta)[![guangchen811](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/103159823?v=4&w=50&h=50&mask=circle)](https://github.com/guangchen811)[![juanfpo96](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14787156?v=4&w=50&h=50&mask=circle)](https://github.com/juanfpo96)[![iskandarreza](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/32027019?v=4&w=50&h=50&mask=circle)](https://github.com/iskandarreza)[![jpenalbae](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8380459?v=4&w=50&h=50&mask=circle)](https://github.com/jpenalbae)[![pallasite99](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/26508636?v=4&w=50&h=50&mask=circle)](https://github.com/pallasite99)[![xutpuu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11964505?v=4&w=50&h=50&mask=circle)](https://github.com/xutpuu)[![alexkreidler](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11166947?v=4&w=50&h=50&mask=circle)](https://github.com/alexkreidler)[![hanhyalex123](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/100895608?v=4&w=50&h=50&mask=circle)](https://github.com/hanhyalex123)[![ps4vs](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/91535358?v=4&w=50&h=50&mask=circle)](https://github.com/ps4vs)[![eltociear](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22633385?v=4&w=50&h=50&mask=circle)](https://github.com/eltociear)
[![shaiss](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/113060?v=4&w=50&h=50&mask=circle)](https://github.com/shaiss)
[![AdityaRajSingh1992](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/105219157?v=4&w=50&h=50&mask=circle)](https://github.com/AdityaRajSingh1992)
[![namansleeps2](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/134390870?v=4&w=50&h=50&mask=circle)](https://github.com/namansleeps22)
[![sirajperson](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/396941?v=4&w=50&h=50&mask=circle)](https://github.com/sirajperson)
[![hsm207](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/2398765?v=4&w=50&h=50&mask=circle)](https://github.com/hsm207)
[![unkn-wn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/43097991?v=4&w=50&h=50&mask=circle)](https://github.com/unkn-wn)
[![DMTarmey](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/590474?v=4&w=50&h=50&mask=circle)](https://github.com/DMTarmey)
[![Parth2506](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/122429822?v=4&w=50&h=50&mask=circle)](https://github.com/Parth2506)
[![platinaCoder](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/47349795?v=4&w=50&h=50&mask=circle)](https://github.com/platinaCoder)
[![anisha1607](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/60440541?v=4&w=50&h=50&mask=circle)](https://github.com/anisha1607)
[![jorgectf](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/46056498?v=4&w=50&h=50&mask=circle)](https://github.com/jorgectf)
[![PaulRBerg](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8782666?v=4&w=50&h=50&mask=circle)](https://github.com/PaulRBerg)
[![boundless-asura](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/122777244?v=4&w=50&h=50&mask=circle)](https://github.com/boundless-asura)
[![JPDucky](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/34105363?v=4&w=50&h=50&mask=circle)](https://github.com/JPDucky)
[![Vibhusha22](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/128478691?v=4&w=50&h=50&mask=circle)](https://github.com/Vibhusha22)
[![ai-akuma](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7444521?v=4&w=50&h=50&mask=circle)](https://github.com/ai-akuma)
[![rounak610](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/81288115?v=4&w=50&h=50&mask=circle)](https://github.com/rounak610)
[![AdarshJha619](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/53672264?v=4&w=50&h=50&mask=circle)](https://github.com/AdarshJha619)
[![ResoluteStoic](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/105219157?v=4&w=50&h=50&mask=circle)](https://github.com/ResoluteStoic)
[![JohnHunt999](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/137149331?v=4&w=50&h=50&mask=circle)](https://github.com/JohnHunt999)
[![Maverick-F35](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/138012351?v=4&w=50&h=50&mask=circle)](https://github.com/Maverick-F359)
[![jorgectf](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/46056498?v=4&w=50&h=50&mask=circle)](https://github.com/jorgectf)
[![AdityaSharma13064](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/138581531?v=4&w=50&h=50&mask=circle)](https://github.com/AdityaSharma13064)
[![lalitlj](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/138583454?v=4&w=50&h=50&mask=circle)](https://github.com/lalitlj)
[![andrew-kelly-neutralaiz](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/128111428?v=4&w=50&h=50&mask=circle)](https://github.com/andrew-kelly-neutralaiz)
[![sayan1101](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/139119661?v=4&w=50&h=50&mask=circle)](https://github.com/sayan1101)
<p align="center"><a href="https://github.com/TransformerOptimus/SuperAGI#"><img src="https://superagi.com/wp-content/uploads/2023/05/backToTopButton.png" alt="Back to top" height="29"/></a></p>
### ⚠️ Under Development!
This project is under active development and may still have issues. We appreciate your understanding and patience. If you encounter any problems, please check the open issues first. If your issue is not listed, kindly create a new issue detailing the error or problem you experienced. Thank you for your support!
+7
View File
@@ -0,0 +1,7 @@
# WeHub 来源说明
- 原始项目:`TransformerOptimus/SuperAGI`
- 原始仓库:https://github.com/TransformerOptimus/SuperAGI
- 导入方式:上游默认分支的最新快照
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
+109
View File
@@ -0,0 +1,109 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = migrations
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
# for all available tokens
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
prepend_sys_path = .
# timezone to use when rendering the date within the migration file
# as well as the filename.
# If specified, requires the python-dateutil library that can be
# installed by adding `alembic[tz]` to the pip requirements
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; This defaults
# to migrations/versions. When using multiple version
# directories, initial revisions must be specified with --version-path.
# The path separator used here should be the separator specified by "version_path_separator" below.
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
# version path separator; As mentioned above, this is the character used to split
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
# Valid values for version_path_separator are:
#
# version_path_separator = :
# version_path_separator = ;
# version_path_separator = space
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
# set to 'true' to search source files recursively
# in each "version_locations" directory
# new in Alembic version 1.10
# recursive_version_locations = false
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = postgresql://superagi:password@super__postgres:5432/super_agi_main
[post_write_hooks]
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner, against the "black" entrypoint
# hooks = black
# black.type = console_scripts
# black.entrypoint = black
# black.options = -l 79 REVISION_SCRIPT_FILENAME
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
+74
View File
@@ -0,0 +1,74 @@
import os
import sys
import subprocess
from time import sleep
import shutil
from sys import platform
from multiprocessing import Process
from superagi.lib.logger import logger
def check_command(command, message):
if not shutil.which(command):
logger.info(message)
sys.exit(1)
def run_npm_commands(shell=False):
os.chdir("gui")
try:
subprocess.run(["npm", "install"], check=True, shell=shell)
except subprocess.CalledProcessError:
logger.error(f"Error during '{' '.join(sys.exc_info()[1].cmd)}'. Exiting.")
sys.exit(1)
os.chdir("..")
def run_server(shell=False,a_name=None,a_description=None,goals=None):
tgwui_process = Process(target=subprocess.run, args=(["python", "test.py","--name",a_name,"--description",a_description,"--goals"]+goals,), kwargs={"shell": shell})
api_process = Process(target=subprocess.run, args=(["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"],), kwargs={"shell": shell})
celery_process = Process(target=subprocess.run, args=(["celery", "-A", "celery_app", "worker", "--loglevel=info"],), kwargs={"shell": shell})
ui_process = Process(target=subprocess.run, args=(["python", "test.py","--name",a_name,"--description",a_description,"--goals"]+goals,), kwargs={"shell": shell})
api_process.start()
celery_process.start()
ui_process.start()
return api_process, ui_process, celery_process
def cleanup(api_process, ui_process, celery_process):
logger.info("Shutting down processes...")
api_process.terminate()
ui_process.terminate()
celery_process.terminate()
logger.info("Processes terminated. Exiting.")
sys.exit(1)
if __name__ == "__main__":
check_command("node", "Node.js is not installed. Please install it and try again.")
check_command("npm", "npm is not installed. Please install npm to proceed.")
check_command("uvicorn", "uvicorn is not installed. Please install uvicorn to proceed.")
agent_name = input("Enter an agent name: ")
agent_description = input("Enter an agent description: ")
goals = []
while True:
goal = input("Enter a goal (or 'q' to quit): ")
if goal == 'q':
break
goals.append(goal)
isWindows = False
if platform == "win32" or platform == "cygwin":
isWindows = True
run_npm_commands(shell=isWindows)
try:
api_process, ui_process, celery_process = run_server(isWindows, agent_name, agent_description, goals)
while True:
try:
sleep(30)
except KeyboardInterrupt:
cleanup(api_process, ui_process, celery_process)
except Exception as e:
cleanup(api_process, ui_process, celery_process)
+125
View File
@@ -0,0 +1,125 @@
#####################------------------SYSTEM KEYS-------------------------########################
PINECONE_API_KEY: YOUR_PINECONE_API_KEY
PINECONE_ENVIRONMENT: YOUR_PINECONE_ENVIRONMENT
OPENAI_API_KEY: YOUR_OPEN_API_KEY
PALM_API_KEY: YOUR_PALM_API_KEY
REPLICATE_API_TOKEN: YOUR_REPLICATE_API_TOKEN
HUGGING_API_TOKEN: YOUR_HUGGING_FACE_API_TOKEN
# For locally hosted LLMs comment out the next line and uncomment the one after
# to configure a local llm point your browser to 127.0.0.1:7860 and click on the model tab in text generation web ui.
OPENAI_API_BASE: https://api.openai.com/v1
#OPENAI_API_BASE: "http://super__tgwui:5001/v1"
# "gpt-3.5-turbo-0301": 4032, "gpt-4-0314": 8092, "gpt-3.5-turbo": 4032, "gpt-4": 8092, "gpt-4-32k": 32768, "gpt-4-32k-0314": 32768, "llama":2048, "mpt-7b-storywriter":45000
MODEL_NAME: "gpt-3.5-turbo-0301"
# "gpt-3.5-turbo", , "gpt-4", "models/chat-bison-001"
RESOURCES_SUMMARY_MODEL_NAME: "gpt-3.5-turbo"
MAX_TOOL_TOKEN_LIMIT: 800
MAX_MODEL_TOKEN_LIMIT: 4032 # set to 2048 for llama
#DATABASE INFO
# redis details
DB_NAME: super_agi_main
DB_HOST: super__postgres
DB_USERNAME: superagi
DB_PASSWORD: password
DB_URL: postgresql://superagi:password@super__postgres:5432/super_agi_main
REDIS_URL: "super__redis:6379"
#STORAGE TYPE ("FILE" or "S3")
STORAGE_TYPE: "FILE"
#TOOLS
TOOLS_DIR: "superagi/tools"
#STORAGE INFO FOR FILES
RESOURCES_INPUT_ROOT_DIR: workspace/input/{agent_id}
RESOURCES_OUTPUT_ROOT_DIR: workspace/output/{agent_id}/{agent_execution_id} # For keeping resources at agent execution level
#RESOURCES_OUTPUT_ROOT_DIR: workspace/output/{agent_id} # For keeping resources at agent level
#S3 RELATED DETAILS ONLY WHEN STORAGE_TYPE IS "S3"
BUCKET_NAME:
INSTAGRAM_TOOL_BUCKET_NAME: #Public read bucket, Images generated by stable diffusion are put in this bucket and the public url of the same is generated.
AWS_ACCESS_KEY_ID:
AWS_SECRET_ACCESS_KEY:
#AUTH
ENV: 'DEV' #DEV,PROD, to use GITHUB OAUTH set to PROD
JWT_SECRET_KEY: 'secret'
expiry_time_hours: 1
#GITHUB OAUTH:
GITHUB_CLIENT_ID:
GITHUB_CLIENT_SECRET:
FRONTEND_URL: "http://localhost:3000"
#ENCRYPTION KEY, Replace this with your own key for production
ENCRYPTION_KEY: abcdefghijklmnopqrstuvwxyz123456
#WEAVIATE
# If you are using docker or web hosted uncomment the next two lines and comment the third one
# WEAVIATE_URL: YOUR_WEAVIATE_URL
# WEAVIATE_API_KEY: YOUR_WEAVIATE_API_KEY
WEAVIATE_USE_EMBEDDED: true
#####################------------------TOOLS KEY-------------------------########################
#If you have google api key and CSE key, use this
GOOGLE_API_KEY: YOUR_GOOGLE_API_KEY
SEARCH_ENGINE_ID: YOUR_SEARCH_ENIGNE_ID
# IF YOU DONT HAVE GOOGLE SEARCH KEY, YOU CAN USE SERPER.DEV KEYS
SERP_API_KEY: YOUR_SERPER_API_KEY
#ENTER YOUR EMAIL CREDENTIALS TO ACCESS EMAIL TOOL
EMAIL_ADDRESS: YOUR_EMAIL_ADDRESS
EMAIL_PASSWORD: YOUR_EMAIL_APP_PASSWORD #get the app password from (https://myaccount.google.com/apppasswords)
EMAIL_SMTP_HOST: smtp.gmail.com #Change the SMTP host if not using Gmail
EMAIL_SMTP_PORT: 587 #Change the SMTP port if not using Gmail
EMAIL_IMAP_SERVER: imap.gmail.com #Change the IMAP Host if not using Gmail
EMAIL_SIGNATURE: Email sent by SuperAGI
EMAIL_DRAFT_MODE_WITH_FOLDER: YOUR_DRAFTS_FOLDER
EMAIL_ATTACHMENT_BASE_PATH: YOUR_DIRECTORY_FOR_EMAIL_ATTACHMENTS
# GITHUB
GITHUB_USERNAME: YOUR_GITHUB_USERNAME
GITHUB_ACCESS_TOKEN: YOUR_GITHUB_ACCESS_TOKEN
#JIRA
JIRA_INSTANCE_URL: YOUR_JIRA_INSTANCE_URL
JIRA_USERNAME: YOUR_JIRA_EMAIL
JIRA_API_TOKEN: YOUR_JIRA_API_TOKEN
#SLACK
SLACK_BOT_TOKEN: YOUR_SLACK_BOT_TOKEN
# For running stable diffusion
STABILITY_API_KEY: YOUR_STABILITY_API_KEY
#Engine IDs that can be used: 'stable-diffusion-v1', 'stable-diffusion-v1-5','stable-diffusion-512-v2-0', 'stable-diffusion-768-v2-0','stable-diffusion-512-v2-1','stable-diffusion-768-v2-1','stable-diffusion-xl-beta-v2-2-2'
ENGINE_ID: "stable-diffusion-xl-beta-v2-2-2"
## To config a vector store for resources manager uncomment config below
## based on the vector store you want to use
## RESOURCE_VECTOR_STORE can be REDIS, PINECONE, CHROMA, QDRANT
#RESOURCE_VECTOR_STORE: YOUR_RESOURCE_VECTOR_STORE
#RESOURCE_VECTOR_STORE_INDEX_NAME: YOUR_RESOURCE_VECTOR_STORE_INDEX_NAME
## To use a custom redis
#REDIS_VECTOR_STORE_URL: YOUR_REDIS_VECTOR_STORE_URL
## To use qdrant for vector store in resources manager
#QDRANT_PORT: YOUR_QDRANT_PORT
#QDRANT_HOST_NAME: YOUR_QDRANT_HOST_NAME
## To use chroma for vector store in resources manager
#CHROMA_HOST_NAME: YOUR_CHROMA_HOST_NAME
#CHROMA_PORT: YOUR_CHROMA_PORT
## To use Qdrant for vector store
#QDRANT_HOST_NAME: YOUR_QDRANT_HOST_NAME
#QDRANT_PORT: YOUR_QDRANT_PORT
#GPU_LAYERS: GPU LAYERS THAT YOU WANT TO OFFLOAD TO THE GPU WHILE USING LOCAL LLMS
+76
View File
@@ -0,0 +1,76 @@
version: '3.8'
services:
backend:
volumes:
- "./:/app"
build: .
depends_on:
- super__redis
- super__postgres
networks:
- super_network
command: ["/app/wait-for-it.sh", "super__postgres:5432","-t","60","--","/app/entrypoint.sh"]
celery:
volumes:
- "./:/app"
- "${EXTERNAL_RESOURCE_DIR:-./workspace}:/app/ext"
build: .
depends_on:
- super__redis
- super__postgres
networks:
- super_network
command: ["/app/entrypoint_celery.sh"]
gui:
build:
context: ./gui
args:
NEXT_PUBLIC_API_BASE_URL: "/api"
networks:
- super_network
# volumes:
# - ./gui:/app
# - /app/node_modules/
# - /app/.next/
super__redis:
image: "redis/redis-stack-server:latest"
networks:
- super_network
# uncomment to expose redis port to host
# ports:
# - "6379:6379"
volumes:
- redis_data:/data
super__postgres:
image: "docker.io/library/postgres:latest"
environment:
- POSTGRES_USER=superagi
- POSTGRES_PASSWORD=password
- POSTGRES_DB=super_agi_main
volumes:
- superagi_postgres_data:/var/lib/postgresql/data/
networks:
- super_network
# uncomment to expose postgres port to host
# ports:
# - "5432:5432"
proxy:
image: nginx:stable-alpine
ports:
- "3000:80"
networks:
- super_network
depends_on:
- backend
- gui
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
super_network:
driver: bridge
volumes:
superagi_postgres_data:
redis_data:
+95
View File
@@ -0,0 +1,95 @@
version: '3.8'
services:
backend:
volumes:
- "./:/app"
build:
context: .
dockerfile: Dockerfile-gpu
depends_on:
- super__redis
- super__postgres
networks:
- super_network
command: ["/app/wait-for-it.sh", "super__postgres:5432","-t","60","--","/app/entrypoint.sh"]
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
celery:
volumes:
- "./:/app"
- "${EXTERNAL_RESOURCE_DIR:-./workspace}:/app/ext"
build:
context: .
dockerfile: Dockerfile-gpu
depends_on:
- super__redis
- super__postgres
networks:
- super_network
command: ["/app/entrypoint_celery.sh"]
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
gui:
build:
context: ./gui
args:
NEXT_PUBLIC_API_BASE_URL: "/api"
networks:
- super_network
# volumes:
# - ./gui:/app
# - /app/node_modules/
# - /app/.next/
super__redis:
image: "redis/redis-stack-server:latest"
networks:
- super_network
# uncomment to expose redis port to host
# ports:
# - "6379:6379"
volumes:
- redis_data:/data
super__postgres:
image: "docker.io/library/postgres:15"
environment:
- POSTGRES_USER=superagi
- POSTGRES_PASSWORD=password
- POSTGRES_DB=super_agi_main
volumes:
- superagi_postgres_data:/var/lib/postgresql/data/
networks:
- super_network
# uncomment to expose postgres port to host
# ports:
# - "5432:5432"
proxy:
image: nginx:stable-alpine
ports:
- "3000:80"
networks:
- super_network
depends_on:
- backend
- gui
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
super_network:
driver: bridge
volumes:
superagi_postgres_data:
redis_data:
+75
View File
@@ -0,0 +1,75 @@
version: '3.8'
services:
backend:
image: "superagidev/superagi:main"
depends_on:
- super__redis
- super__postgres
networks:
- super_network
env_file:
- config.yaml
command: ["/app/wait-for-it.sh", "super__postgres:5432","-t","60","--","/app/entrypoint.sh"]
celery:
image: "superagidev/superagi:main"
depends_on:
- super__redis
- super__postgres
networks:
- super_network
env_file:
- config.yaml
command: ["/app/entrypoint_celery.sh"]
volumes:
- "./workspace:/app/workspace"
gui:
image: "superagidev/superagi-frontend:main"
environment:
- NEXT_PUBLIC_API_BASE_URL=/api
networks:
- super_network
super__redis:
image: "redis/redis-stack-server:latest"
networks:
- super_network
# uncomment to expose redis port to host
# ports:
# - "6379:6379"
volumes:
- redis_data:/data
super__postgres:
image: "docker.io/library/postgres:latest"
environment:
- POSTGRES_USER=superagi
- POSTGRES_PASSWORD=password
- POSTGRES_DB=super_agi_main
volumes:
- superagi_postgres_data:/var/lib/postgresql/data/
networks:
- super_network
# uncomment to expose postgres port to host
# ports:
# - "5432:5432"
proxy:
image: nginx:stable-alpine
ports:
- "3000:80"
networks:
- super_network
depends_on:
- backend
- gui
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
super_network:
driver: bridge
volumes:
superagi_postgres_data:
redis_data:
+76
View File
@@ -0,0 +1,76 @@
version: '3.8'
services:
backend:
volumes:
- "./:/app"
build: .
depends_on:
- super__redis
- super__postgres
networks:
- super_network
command: ["/app/wait-for-it.sh", "super__postgres:5432","-t","60","--","/app/entrypoint.sh"]
celery:
volumes:
- "./:/app"
- "${EXTERNAL_RESOURCE_DIR:-./workspace}:/app/ext"
build: .
depends_on:
- super__redis
- super__postgres
networks:
- super_network
command: ["/app/entrypoint_celery.sh"]
gui:
build:
context: ./gui
args:
NEXT_PUBLIC_API_BASE_URL: "/api"
networks:
- super_network
# volumes:
# - ./gui:/app
# - /app/node_modules/
# - /app/.next/
super__redis:
image: "redis/redis-stack-server:latest"
networks:
- super_network
# uncomment to expose redis port to host
# ports:
# - "6379:6379"
volumes:
- redis_data:/data
super__postgres:
image: "docker.io/library/postgres:15"
environment:
- POSTGRES_USER=superagi
- POSTGRES_PASSWORD=password
- POSTGRES_DB=super_agi_main
volumes:
- superagi_postgres_data:/var/lib/postgresql/data/
networks:
- super_network
# uncomment to expose postgres port to host
# ports:
# - "5432:5432"
proxy:
image: nginx:stable-alpine
ports:
- "3000:80"
networks:
- super_network
depends_on:
- backend
- gui
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
super_network:
driver: bridge
volumes:
superagi_postgres_data:
redis_data:
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# Downloads the tools from marketplace and external tool repositories
python superagi/tool_manager.py
# Install dependencies
./install_tool_dependencies.sh
# Run Alembic migrations
alembic upgrade head
# Start the app
exec uvicorn main:app --host 0.0.0.0 --port 8001 --reload
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
# Downloads the tools
python superagi/tool_manager.py
# Install dependencies
./install_tool_dependencies.sh
exec celery -A superagi.worker worker --beat --loglevel=info
+13
View File
@@ -0,0 +1,13 @@
# Ignore everything
**
# Allow files and directories
!app
!pages
!public
!utils
!package.json
!next.config.js
!package-lock.json
!.eslintrc.json
!jsconfig.json
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
+21
View File
@@ -0,0 +1,21 @@
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Rebuild the source code only when needed
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_API_BASE_URL=/api
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_MIXPANEL_AUTH_ID
ENV NEXT_PUBLIC_MIXPANEL_AUTH_ID=$NEXT_PUBLIC_MIXPANEL_AUTH_ID
EXPOSE 3000
CMD ["npm", "run", "dev"]
+43
View File
@@ -0,0 +1,43 @@
FROM node:18-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# Rebuild the source code only when needed
FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ARG NEXT_PUBLIC_API_BASE_URL=/api
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
RUN npm run build
# Production image, copy all the files and run next
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup --system --gid 1001 supergroup
RUN adduser --system --uid 1001 superuser
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=superuser:supergroup /app/.next/standalone ./
COPY --from=builder --chown=superuser:supergroup /app/.next/static ./.next/static
USER superuser
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
+34
View File
@@ -0,0 +1,34 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm 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.js`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

+107
View File
@@ -0,0 +1,107 @@
:root {
--max-width: 1100px;
--border-radius: 12px;
--font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
--primary-glow: conic-gradient(
from 180deg at 50% 50%,
#16abff33 0deg,
#0885ff33 55deg,
#54d6ff33 120deg,
#0071ff33 160deg,
transparent 360deg
);
--secondary-glow: radial-gradient(
rgba(255, 255, 255, 1),
rgba(255, 255, 255, 0)
);
--tile-start-rgb: 239, 245, 249;
--tile-end-rgb: 228, 232, 233;
--tile-border: conic-gradient(
#00000080,
#00000040,
#00000030,
#00000020,
#00000010,
#00000010,
#00000080
);
--callout-rgb: 238, 240, 241;
--callout-border-rgb: 172, 175, 176;
--card-rgb: 180, 185, 188;
--card-border-rgb: 131, 134, 135;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
--secondary-glow: linear-gradient(
to bottom right,
rgba(1, 65, 255, 0),
rgba(1, 65, 255, 0),
rgba(1, 65, 255, 0.3)
);
--tile-start-rgb: 2, 13, 46;
--tile-end-rgb: 2, 5, 19;
--tile-border: conic-gradient(
#ffffff80,
#ffffff40,
#ffffff30,
#ffffff20,
#ffffff10,
#ffffff10,
#ffffff80
);
--callout-rgb: 20, 20, 20;
--callout-border-rgb: 108, 108, 108;
--card-rgb: 100, 100, 100;
--card-border-rgb: 200, 200, 200;
}
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
+14
View File
@@ -0,0 +1,14 @@
import './globals.css'
export const metadata = {
title: 'Super AGI',
description: 'Generated by create next app',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
+7
View File
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}
+7
View File
@@ -0,0 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
assetPrefix: process.env.NODE_ENV === "production" ? "/" : "./",
output: 'standalone'
};
module.exports = nextConfig;
+5225
View File
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
{
"name": "super-agi",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"export": "next export"
},
"dependencies": {
"axios": "^1.4.0",
"bootstrap": "^5.2.3",
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0",
"echarts": "^5.4.2",
"echarts-for-react": "^3.0.2",
"eslint": "8.40.0",
"eslint-config-next": "13.4.2",
"js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"mitt": "^3.0.0",
"mixpanel-browser": "^2.47.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"next": "13.4.2",
"react": "18.2.0",
"react-datetime": "^3.2.0",
"react-dom": "18.2.0",
"react-draggable": "^4.4.5",
"react-grid-layout": "^1.3.4",
"react-markdown": "^8.0.7",
"react-spinners": "^0.13.8",
"react-tippy": "^1.4.0",
"react-toastify": "^9.1.3"
}
}
+10
View File
@@ -0,0 +1,10 @@
.apm_dashboard_container {
display: flex;
flex-direction: column;
}
.apm_dashboard {
margin-top: 16px;
height: calc(100vh - 16vh);
overflow-y: auto;
}
+425
View File
@@ -0,0 +1,425 @@
import React, {useState, useEffect, useCallback, useRef} from 'react';
import Image from "next/image";
import style from "./Apm.module.css";
import 'react-toastify/dist/ReactToastify.css';
import {getActiveRuns, getAgentRuns, getAllAgents, getToolsUsage, getMetrics} from "@/pages/api/DashboardService";
import {formatNumber, formatTime, returnToolkitIcon} from "@/utils/utils";
import {BarGraph} from "./BarGraph.js";
import {WidthProvider, Responsive} from 'react-grid-layout';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import { Tooltip } from 'react-tippy';
const ResponsiveGridLayout = WidthProvider(Responsive);
export default function ApmDashboard() {
const [agentDetails, setAgentDetails] = useState([]);
const [tokenDetails, setTokenDetails] = useState([]);
const [runDetails, setRunDetails] = useState(0);
const [allAgents, setAllAgents] = useState([]);
const [dropdown1, setDropDown1] = useState(false);
const [dropdown2, setDropDown2] = useState(false);
const [dropdown3, setDropDown3] = useState(false);
const [selectedAgent, setSelectedAgent] = useState('Select an Agent');
const [selectedAgentIndex, setSelectedAgentIndex] = useState(-1);
const [selectedAgentRun, setSelectedAgentRun] = useState([]);
const [activeRuns, setActiveRuns] = useState([]);
const [selectedAgentDetails, setSelectedAgentDetails] = useState(null);
const [toolsUsed, setToolsUsed] = useState([]);
const [showToolTip, setShowToolTip] = useState(false);
const [toolTipIndex, setToolTipIndex] = useState(-1);
const initialLayout = [
{i: 'total_agents', x: 0, y: 0, w: 3, h: 1.5},
{i: 'total_tokens', x: 3, y: 0, w: 3, h: 1.5},
{i: 'total_runs', x: 6, y: 0, w: 3, h: 1.5},
{i: 'active_runs', x: 9, y: 0, w: 3, h: 2},
{i: 'most_used_tools', x: 9, y: 1, w: 3, h: 2},
{i: 'models_by_agents', x: 0, y: 1, w: 3, h: 2.5},
{i: 'runs_by_model', x: 3, y: 1, w: 3, h: 2.5},
{i: 'tokens_by_model', x: 6, y: 1, w: 3, h: 2.5},
{i: 'agent_details', x: 0, y: 2, w: 12, h: 2.5},
{i: 'total_tokens_consumed', x: 0, y: 3, w: 4, h: 2},
{i: 'total_calls_made', x: 4, y: 3, w: 4, h: 2},
{i: 'tokens_consumed_per_call', x: 8, y: 3, w: 4, h: 2},
];
const storedLayout = localStorage.getItem('myLayoutKey');
const [layout, setLayout] = useState(storedLayout !== null ? JSON.parse(storedLayout) : initialLayout);
const firstUpdate = useRef(true);
const onLayoutChange = (currentLayout) => {
setLayout(currentLayout);
};
const onClickLayoutChange = () => {
localStorage.setItem('myLayoutKey', JSON.stringify(initialLayout));
setLayout(initialLayout);
}
useEffect(() => {
if (!firstUpdate.current) {
localStorage.setItem('myLayoutKey', JSON.stringify(layout));
} else {
firstUpdate.current = false;
}
}, [layout]);
const assignDefaultDataPerModel = (data, modelList) => {
const modelsInData = data.map(item => item.name);
modelList.forEach((model) => {
if (!modelsInData.includes(model)) {
data.push({name: model, value: 0});
}
});
};
useEffect(() => {
const fetchData = async () => {
try {
const [metricsResponse, agentsResponse, activeRunsResponse, toolsUsageResponse] = await Promise.all([getMetrics(), getAllAgents(), getActiveRuns(), getToolsUsage()]);
const models = ['gpt-4', 'gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-4-32k', 'google-palm-bison-001', 'replicate-llama13b-v2-chat'];
assignDefaultDataPerModel(metricsResponse.data.agent_details.model_metrics, models);
assignDefaultDataPerModel(metricsResponse.data.tokens_details.model_metrics, models);
assignDefaultDataPerModel(metricsResponse.data.run_details.model_metrics, models);
setAgentDetails(metricsResponse.data.agent_details);
setTokenDetails(metricsResponse.data.tokens_details);
setRunDetails(metricsResponse.data.run_details);
setAllAgents(agentsResponse.data.agent_details);
setActiveRuns(activeRunsResponse.data);
setToolsUsed(toolsUsageResponse.data);
} catch (error) {
console.log(`Error in fetching data: ${error}`);
}
}
fetchData();
const interval = setInterval(fetchData, 10000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
console.log(toolsUsed)
}, [toolsUsed]);
const handleSelectedAgent = useCallback((index, name) => {
setDropDown1(false)
setDropDown2(false)
setDropDown3(false)
setSelectedAgent(name)
setSelectedAgentIndex(index)
const agentDetails = allAgents.find(agent => agent.agent_id === index);
setSelectedAgentDetails(agentDetails);
getAgentRuns(index).then((response) => {
const data = response.data;
setSelectedAgentRun(data);
}).catch((error) => console.error(`Error in fetching agent runs: ${error}`));
}, [allAgents]);
useEffect(() => handleSelectedAgent(selectedAgentIndex, selectedAgent), [allAgents]);
useEffect(() => {
if (allAgents.length > 0 && selectedAgent === 'Select an Agent') {
const lastAgent = allAgents[allAgents.length - 1];
handleSelectedAgent(lastAgent.agent_id, lastAgent.name);
}
}, [allAgents, selectedAgent, handleSelectedAgent]);
const setToolTipState = (state, index) => {
setShowToolTip(state)
setToolTipIndex(index)
}
return (
<div className={style.apm_dashboard_container}>
<div id="apm_dashboard" className={style.apm_dashboard}>
<div className="horizontal_space_between w_100 align_center padding_0_8">
<span className="text_14 mt_6 ml_6">Agent Performance Monitoring</span>
{/*<button onClick={onClickLayoutChange} className="primary_button">Reset</button>*/}
</div>
<ResponsiveGridLayout
className="layout"
layouts={{lg: layout}}
onLayoutChange={onLayoutChange}
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
cols={{lg: 12, md: 12, sm: 12, xs: 12, xxs: 12}}>
<div key="total_agents" className="display_column_container">
<span className="text_14 mb_8">Total Agents</span>
<div
className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24 mt_24">{formatNumber(agentDetails.total_agents)}</div>
</div>
<div key="total_tokens" className="display_column_container">
<span className="text_14 mb_8">Total tokens consumed</span>
<div
className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24 mt_24">{formatNumber(tokenDetails.total_tokens)}</div>
</div>
<div key="total_runs" className="display_column_container">
<span className="text_14 mb_8">Total runs</span>
<div
className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24 mt_24">{formatNumber(runDetails.total_runs)}</div>
</div>
<div key="models_by_agents" className="display_column_container">
<span className="text_14 mb_8">Number of Agents per model</span>
{agentDetails.model_metrics && agentDetails.model_metrics.length > 0
? <><BarGraph data={agentDetails.model_metrics} type="value" color="#3C7EFF"/>
<div className="horizontal_container mt_10">
<span className="bar_label_dot" style={{backgroundColor: '#3C7EFF'}}></span>
<span className="bar_label_text">Models</span>
</div>
</>
: <div className="vertical_container align_center mt_80 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Agents Found</span>
</div>}
</div>
<div key="runs_by_model" className="display_column_container">
<span className="text_14 mb_8">Number of Runs per Model</span>
{runDetails.model_metrics && runDetails.model_metrics.length > 0
? <><BarGraph data={runDetails.model_metrics} type="value" color="#3C7EFF"/>
<div className="horizontal_container mt_10">
<span className="bar_label_dot" style={{backgroundColor: '#3C7EFF'}}></span>
<span className="bar_label_text">Models</span>
</div>
</>
: <div className="vertical_container align_center mt_80 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Agents Found</span>
</div>}
</div>
<div key="tokens_by_model" className="display_column_container">
<span className="text_14 mb_8">Total Tokens consumed by models</span>
{tokenDetails.model_metrics && tokenDetails.model_metrics.length > 0
? <><BarGraph data={tokenDetails.model_metrics} type="value" color="#3C7EFF"/>
<div className="horizontal_container mt_10">
<span className="bar_label_dot" style={{backgroundColor: '#3C7EFF'}}></span>
<span className="bar_label_text">Models</span>
</div>
</>
: <div className="vertical_container align_center mt_80 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Agents Found</span>
</div>}
</div>
<div key="most_used_tools" className="display_column_container">
<span className="text_14 mb_8">Most used tools</span>
{toolsUsed.length === 0 ?
<div className="vertical_container align_center mt_70 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Used Tools Found</span>
</div> : <div className="scrollable_container">
<table className="table_css margin_0 padding_0">
<thead>
<tr style={{borderTop: 'none'}}>
<th className="table_header w_56">Tool</th>
<th className="table_header text_align_right w_22">Agents</th>
<th className="table_header text_align_right w_22">Calls</th>
</tr>
</thead>
</table>
<div className="overflow_auto w_100">
<table className="table_css margin_0">
<tbody>
{toolsUsed.map((tool, index) => (
<tr key={index}>
<td className="table_data" style={{width: '100%', display: 'flex', alignItems: 'center'}}>
<Image className="image_class bg_black" width={20} height={20}
src={returnToolkitIcon(tool.toolkit)} alt="tool-icon"/>
<span>{tool.tool_name}</span>
</td>
<td className="table_data text_align_right w_22 br_left_grey">{tool.unique_agents}</td>
<td className="table_data text_align_right w_22 br_left_grey">{tool.total_usage}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>}
</div>
<div key="agent_details" className="display_column_container">
<span className="text_14 mb_8">Agent Overview</span>
{allAgents.length === 0 ?
<div className="vertical_container align_center mt_50 w_100">
<img src="/images/no_permissions.svg" width={300} height={120} alt="No Data"/>
<span
className="text_12 color_white mt_6">{selectedAgent === 'Select an Agent' ? 'Please Select an Agent' :
<React.Fragment>No Runs found for <b>{selectedAgent}</b></React.Fragment>}</span>
</div> : <div className="scrollable_container mt_16">
<table className="table_css margin_0 padding_0">
<thead>
<tr style={{borderTop: 'none'}}>
<th className="table_header w_20">Agent Name</th>
<th className="table_header text_align_right w_10">Model</th>
<th className="table_header text_align_right w_12">Tokens Consumed</th>
<th className="table_header text_align_right w_6">Runs</th>
<th className="table_header text_align_right w_12">Avg tokens per run</th>
<th className="table_header text_align_right w_20">Tools</th>
<th className="table_header text_align_right w_10">Calls</th>
<th className="table_header text_align_right w_10">Avg Run Time</th>
</tr>
</thead>
</table>
<div className="overflow_auto w_100">
<table className="table_css margin_0">
<tbody>
{allAgents.map((run, i) => (
<tr key={i}>
<td className="table_data w_20">{run.name}</td>
<td className="table_data text_align_right w_10 br_left_grey">{run.model_name}</td>
<td className="table_data text_align_right w_12 br_left_grey">{formatNumber(run.total_tokens)}</td>
<td className="table_data text_align_right w_6 br_left_grey">{run.runs_completed}</td>
<td className="table_data text_align_right w_12 br_left_grey">
{run.runs_completed ? formatNumber((run.total_tokens / run.runs_completed).toFixed(1)) : '-'}
</td>
<td className="table_data text_align_right br_left_grey" style={{width: '20%'}}>
{run.tools_used && run.tools_used.slice(0, 3).map((tool, index) => (
<div key={index} className="tools_used">{tool}</div>
))}
{run.tools_used && run.tools_used.length > 3 &&
<div style={{display:'inline-flex'}}>
<Tooltip
position="top-start"
trigger="mouseenter"
arrow={true}
html={
<>
<div className="bg_primary br_8 padding_5">
{run.tools_used.slice(3).map((tool,index) =>
<div className="tools_used" key={index}>{tool}</div>
)}
</div>
</>
}
>
<div className="tools_used cursor_pointer">
+{run.tools_used.length - 3}
</div>
</Tooltip>
</div>
}
</td>
<td className="table_data text_align_right w_10 br_left_grey">{run.total_calls}</td>
<td className="table_data text_align_right w_10 br_left_grey">
{run.avg_run_time === 0 ? '-' : `${parseFloat((run.avg_run_time / 60).toFixed(1))} mins`}
</td>
</tr>))}
</tbody>
</table>
</div>
</div>}
</div>
<div key="active_runs" className="display_column_container">
<span className="text_14 mb_8">Active Runs</span>
<div className="scrollable_container gap_8">
{activeRuns.length === 0 ?
<div className="vertical_container align_center mt_24">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No active runs found</span>
</div> : activeRuns.map((run, index) => (
<div key={index} className="active_runs">
<span className="text_14">{run.name}</span>
<div style={{display: 'inline-flex', alignItems: 'center'}}>
<span className="text_12 mt_6">{run.agent_name} · <Image width={12} height={12} src="/images/schedule.svg" alt="schedule-icon"/>
{formatTime(run.created_at)}</span>
</div>
</div>
))}
</div>
</div>
<div key="total_tokens_consumed" className="display_column_container">
<div className="horizontal_space_between w_100">
<span className="text_14 mb_8">Tokens Consumed by Runs</span>
<div style={{position: 'relative', display: 'flex', flexDirection: 'column'}}>
{allAgents.length > 0 && <div>
<div className="text_14 mb_8 cursor_pointer" onClick={() => setDropDown2(!dropdown2)}>{selectedAgent}<img width={18} height={16}
src="/images/expand_more.svg"/></div>
{dropdown2 &&
<div className="custom_select_options padding_8 position_absolute r_0">
{allAgents.map((agent, index) => (
<div key={index} className="custom_select_option padding_8" onClick={() => handleSelectedAgent(agent.agent_id, agent.name)}>{agent.name}</div>))}
</div>}
</div>}
</div>
</div>
{selectedAgentRun.length > 0
? <><BarGraph data={selectedAgentRun} type="tokens_consumed" color="#3DFF7F"/>
<div className="horizontal_container mt_10">
<span className="bar_label_dot" style={{backgroundColor: '#3DFF7F'}}></span>
<span className="bar_label_text">Runs</span>
</div>
</>
: <div className="vertical_container align_center mt_80 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Runs Found</span>
</div>}
</div>
<div key="total_calls_made" className="display_column_container">
<div className="horizontal_space_between w_100">
<span className="text_14 mb_8">Calls Made by Runs</span>
<div className="vertical_containers position_relative">
{allAgents.length > 0 && <div>
<div className="text_14 mb_8 cursor_pointer"
onClick={() => setDropDown1(!dropdown1)}>{selectedAgent}<img width={18} height={16} src="/images/expand_more.svg"/>
</div>
{dropdown1 &&
<div className="custom_select_options padding_8 position_absolute r_0">
{allAgents.map((agent, index) => (
<div key={index} className="custom_select_option padding_8" onClick={() => handleSelectedAgent(agent.agent_id, agent.name)}>{agent.name}</div>))}
</div>}
</div>}
</div>
</div>
{selectedAgentRun.length > 0
? <><BarGraph data={selectedAgentRun} type="calls" color="#3DFF7F"/>
<div className="horizontal_container mt_10">
<span className="bar_label_dot" style={{backgroundColor: '#3DFF7F'}}></span>
<span className="bar_label_text">Runs</span>
</div>
</>
: <div className="vertical_container align_center mt_80 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Runs Found</span>
</div>}
</div>
<div key="tokens_consumed_per_call" className="display_column_container">
<div className="horizontal_space_between w_100">
<span className="text_14 mb_8">Average Tokens consumed in all calls per run </span>
<div className="vertical_containers position_relative">
{allAgents.length > 0 && <div>
<div className="text_14 mb_8 cursor_pointer"
onClick={() => setDropDown3(!dropdown3)}>{selectedAgent}<img width={18} height={16} src="/images/expand_more.svg"/>
</div>
{dropdown3 &&
<div className="custom_select_options padding_8 position_absolute r_0">
{allAgents.map((agent, index) => (
<div key={index} className="custom_select_option padding_8"
onClick={() => handleSelectedAgent(agent.agent_id, agent.name)}>{agent.name}</div>))}
</div>}
</div>}
</div>
</div>
{selectedAgentRun.length > 0
? <><BarGraph data={selectedAgentRun} type="tokens_per_call" color="#3DFF7F"/>
<div className="horizontal_container mt_10">
<span className="bar_label_dot" style={{backgroundColor: '#3DFF7F'}}></span>
<span className="bar_label_text">Runs</span>
</div>
</>
: <div className="vertical_container align_center mt_80 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Runs Found</span>
</div>}
</div>
</ResponsiveGridLayout>
</div>
</div>
);
}
+73
View File
@@ -0,0 +1,73 @@
import React, {useEffect, useRef} from "react";
import * as echarts from 'echarts';
export const BarGraph = ({data, type, color}) => {
const chartRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
const chartInstance = echarts.getInstanceByDom(chartRef.current);
const chart = chartInstance ? chartInstance : echarts.init(chartRef.current);
const option = {
color: color,
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: data.map(item => item.name),
axisLabel: {
interval: 0,
rotate: 45,
color: '#888'
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value) {
if (value >= 1000) {
return `${value / 1000}k`;
} else {
return value;
}
}
},
splitLine: {
lineStyle: {
color: 'rgba(255, 255, 255, 0.08)'
}
}
},
series: [{
data: data.map(item => type === 'tokens_per_call' ? (item.tokens_consumed / item.calls) : item[type]),
type: 'bar'
}],
responsive: true
};
chart.setOption(option);
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
chart.resize();
});
});
resizeObserver.observe(containerRef.current);
return () => resizeObserver.disconnect();
}, [data, type]);
return (
<div ref={containerRef} style={{width: '100%', height: '100%'}}>
<div ref={chartRef} style={{width: '100%', height: '100%'}}></div>
</div>
);
}
export default BarGraph;
+162
View File
@@ -0,0 +1,162 @@
import React, {useState, useEffect} from 'react';
import styles from './Agents.module.css';
import Image from 'next/image';
import {updatePermissions} from '@/pages/api/DashboardService';
import {formatTimeDifference} from '@/utils/utils';
function ActionBox({action, index, denied, reasons, handleDeny, handleSelection, setReasons}) {
const isDenied = denied[index];
return (
<div key={action.id} className={styles.history_box}
style={{background: '#272335', padding: '16px', cursor: 'default'}}>
<div style={{display: 'flex', flexDirection: 'column'}}>
{action.question && (<div className={styles.feed_title}>{action.question}</div>)}
{!action.question && (<div>Tool <b>{action.tool_name}</b> is seeking for Permissions</div>)}
{isDenied && (
<div style={{marginTop: '26px'}}>
<div>Provide Feedback <span style={{color: '#888888'}}>(Optional)</span></div>
<input style={{marginTop: '6px'}} type="text" value={reasons[index]} placeholder="Enter your input here"
className="input_medium"
onChange={(e) => {
const newReasons = [...reasons];
newReasons[index] = e.target.value;
setReasons(newReasons);
}}/>
</div>
)}
{isDenied ? (
<div style={{display: 'inline-flex', gap: '8px'}}>
<button onClick={() => handleDeny(index)} className="secondary_button mt_16" style={{paddingLeft: '10px'}}>
<Image width={12} height={12} src="/images/undo.svg" alt="check-icon"/>
<span className={styles.text_12_n}>Go Back</span>
</button>
<button onClick={() => handleSelection(index, false, action.id)} className="secondary_button mt_16"
style={{background: 'transparent', border: 'none'}}>
<span className={styles.text_12_n}>Proceed to Deny</span>
</button>
</div>
) : (
<div style={{display: 'inline-flex', gap: '8px'}}>
<button onClick={() => handleSelection(index, true, action.id)} className="secondary_button mt_16"
style={{paddingLeft: '10px'}}>
<Image width={12} height={12} src="/images/check.svg" alt="check-icon"/>
<span className={styles.text_12_n}>Approve</span>
</button>
<button onClick={() => handleDeny(index)} className="secondary_button mt_16"
style={{background: 'transparent', border: 'none'}}>
<Image width={16} height={16} src="/images/close.svg" alt="close-icon"/>
<span className={styles.text_12_n}>Deny</span>
</button>
</div>
)}
</div>
<div style={{display: 'flex', alignItems: 'center', paddingLeft: '0', paddingBottom: '0'}}
className={styles.tab_text}>
<div>
<Image width={12} height={12} src="/images/schedule.svg" alt="schedule-icon"/>
</div>
<div className={styles.history_info}>{formatTimeDifference(action.time_difference)}</div>
</div>
</div>
);
}
function HistoryBox({action}) {
return (
<div key={action.id} className={styles.history_box}
style={{background: '#272335', padding: '16px', cursor: 'default'}}>
<div style={{display: 'flex', flexDirection: 'column'}}>
<div>Permissions for <b>{action.tool_name}</b> was::</div>
{action.status && action.status === 'APPROVED' ? (
<button className="history_permission mt_16">
<Image width={12} height={12} src="/images/check.svg" alt="check-icon"/>
<span className={styles.text_12_n}>Approved</span>
</button>
) : (
<button className="history_permission mt_16">
<Image width={14} height={14} src="/images/close.svg" alt="close-icon"/>
<span className={styles.text_12_n}>Denied</span>
</button>
)}
{action.user_feedback != null &&
<div style={{display: 'flex', flexDirection: 'column'}}>
<div className="mt_16" style={{color: '#888888'}}>FeedBack</div>
<div className="mt_6 mb_8">{action.user_feedback}</div>
</div>
}
<div style={{display: 'flex', alignItems: 'center', paddingLeft: '0', paddingBottom: '0'}}
className={styles.tab_text}>
<div>
<Image width={12} height={12} src="/images/schedule.svg" alt="schedule-icon"/>
</div>
<div className={styles.history_info}>{formatTimeDifference(action.time_difference)}</div>
</div>
</div>
</div>
)
}
export default function ActionConsole({actions, pendingPermission, setPendingPermissions}) {
const [hiddenActions, setHiddenActions] = useState([]);
const [denied, setDenied] = useState([]);
const [reasons, setReasons] = useState([]);
const [localActionIds, setLocalActionIds] = useState([]);
useEffect(() => {
const updatedActions = actions?.filter((action) => !localActionIds.includes(action.id));
if (updatedActions && updatedActions.length > 0) {
setLocalActionIds((prevIds) => [...prevIds, ...updatedActions.map(({id}) => id)]);
setDenied((prevDenied) => prevDenied.map((value, index) => updatedActions[index] ? false : value));
setReasons((prevReasons) => prevReasons.map((value, index) => updatedActions[index] ? '' : value));
}
}, [actions]);
const handleDeny = (index) => {
setDenied((prevDenied) => {
const newDeniedState = [...prevDenied];
newDeniedState[index] = !newDeniedState[index];
return newDeniedState;
});
};
const handleSelection = (index, status, permissionId) => {
setHiddenActions((prevHiddenActions) => [...prevHiddenActions, index]);
const data = {
status: status,
user_feedback: reasons[index],
};
updatePermissions(permissionId, data).then((response) => {
if (response.status === 200)
setPendingPermissions(pendingPermission - 1)
});
};
return (
<>
{actions && actions.length > 0 ? (
<div className={styles.detail_body} style={{height: 'auto'}}>
{actions.map((action, index) => {
if (action.status === 'PENDING' && !hiddenActions.includes(index)) {
return (<ActionBox key={action.id} action={action} index={index} denied={denied} setReasons={setReasons}
reasons={reasons} handleDeny={handleDeny} handleSelection={handleSelection}/>);
} else if (action.status === 'APPROVED' || action.status === 'REJECTED') {
return (<HistoryBox key={action.id} action={action}/>);
}
return null;
})}
</div>
) : (
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '40px'}}>
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className={styles.feed_title} style={{marginTop: '8px'}}>No Actions to Display!</span>
</div>
)}
</>
);
}
+207
View File
@@ -0,0 +1,207 @@
import React, {useEffect, useRef, useState} from 'react';
import styles from './Agents.module.css';
import {getExecutionFeeds, getDateTime} from "@/pages/api/DashboardService";
import Image from "next/image";
import {loadingTextEffect, formatTimeDifference, convertWaitingPeriod, updateDateBasedOnValue} from "@/utils/utils";
import {EventBus} from "@/utils/eventBus";
import {ClipLoader} from 'react-spinners';
export default function ActivityFeed({selectedRunId, selectedView, setFetchedData, agent, selectedRunStatus}) {
const [loadingText, setLoadingText] = useState("Thinking");
const [feeds, setFeeds] = useState([]);
const feedContainerRef = useRef(null);
const [runStatus, setRunStatus] = useState("CREATED");
const [prevFeedsLength, setPrevFeedsLength] = useState(0);
const [scheduleDate, setScheduleDate] = useState(null);
const [scheduleTime, setScheduleTime] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [waitingPeriod, setWaitingPeriod] = useState(null);
const [errorMsg, setErrorMsg] = useState('');
useEffect(() => {
const interval = window.setInterval(function () {
if (selectedRunStatus !== "ERROR_PAUSED") {
fetchFeeds();
}
}, 5000);
return () => clearInterval(interval);
}, [selectedRunId, selectedRunStatus]);
function fetchDateTime() {
getDateTime(agent.id)
.then((response) => {
const {start_date, start_time} = response.data;
setScheduleDate(start_date);
setScheduleTime(start_time);
})
.catch((error) => {
console.error('Error fetching agent data:', error);
});
}
useEffect(() => {
loadingTextEffect('Thinking', setLoadingText, 250);
if (agent?.is_scheduled && !agent?.is_running) {
fetchDateTime();
}
}, []);
useEffect(() => {
if (feeds.length !== prevFeedsLength) {
if (feedContainerRef.current) {
setTimeout(() => {
if (feedContainerRef.current !== null) {
feedContainerRef.current.scrollTo({top: feedContainerRef.current.scrollHeight, behavior: 'smooth'});
setPrevFeedsLength(feeds.length);
}
}, 100);
}
}
}, [feeds]);
function scrollToTop() {
if (feedContainerRef.current) {
setTimeout(() => {
feedContainerRef.current.scrollTo({top: 0, behavior: 'smooth'});
}, 100);
}
}
useEffect(() => {
fetchFeeds();
}, [selectedRunId])
useEffect(() => {
EventBus.emit('reFetchAgents', {});
}, [runStatus])
function fetchFeeds() {
if (selectedRunId !== null) {
setIsLoading(true);
getExecutionFeeds(selectedRunId)
.then((response) => {
const data = response.data;
setFeeds(data.feeds);
setErrorMsg(data.errors)
setRunStatus(data.status);
setFetchedData(data.permissions);
setWaitingPeriod(data.waiting_period ? data.waiting_period : null)
EventBus.emit('resetRunStatus', {executionId: selectedRunId, status: data.status});
setIsLoading(false); //add this line
})
.catch((error) => {
console.error('Error fetching execution feeds:', error);
setIsLoading(false); // and this line
});
}
}
useEffect(() => {
const updateRunStatus = (eventData) => {
if (eventData.selectedRunId === selectedRunId) {
setRunStatus(eventData.status);
}
};
const refreshDate = () => {
fetchDateTime();
};
EventBus.on('updateRunStatus', updateRunStatus);
EventBus.on('refreshDate', refreshDate);
return () => {
EventBus.off('updateRunStatus', updateRunStatus);
EventBus.off('refreshDate', refreshDate);
};
});
return (<>
<div style={{overflowY: "auto", maxHeight: '80vh', position: 'relative'}} ref={feedContainerRef}>
<div style={{marginBottom: '55px'}}>
{agent?.is_scheduled && !agent?.is_running && !selectedRunId ?
<div style={{display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center'}}>
<Image width={72} height={72} src="/images/eventSchedule.png" alt="github"/>
<div style={{color: 'white', fontSize: '14px'}}>
This agent is scheduled to start on {scheduleDate}, at {scheduleTime}
</div>
</div> : <div>
{feeds && feeds.map((f, index) => (<div key={index} className={styles.history_box}
style={{background: '#272335', padding: '20px', cursor: 'default'}}>
<div style={{display: 'flex'}}>
{f.role === 'user' && <div className={styles.feed_icon}>💁</div>}
{f.role === 'system' && <div className={styles.feed_icon}>🛠 </div>}
{f.role === 'assistant' && <div className={styles.feed_icon}>💡</div>}
<div className={styles.feed_title}>{f?.feed || ''}</div>
</div>
<div className={styles.more_details_wrapper}>
{f.time_difference && <div className={styles.more_details}>
<div style={{display: 'flex', alignItems: 'center'}}>
<div>
<Image width={12} height={12} src="/images/schedule.svg" alt="schedule-icon"/>
</div>
<div className={styles.history_info}>
{formatTimeDifference(f.time_difference)}
</div>
</div>
</div>}
</div>
</div>))}
{runStatus === 'WAIT_STEP' &&
<div className="history_box padding_20 cursor_default bg_secondary">
<div style={{display: 'flex'}}>
<div className="fs_20 lh_24"></div>
<div className={styles.feed_title}>Waiting Block Initiated. The Agent will wait for {convertWaitingPeriod(waitingPeriod) || null}</div>
</div>
</div>}
{runStatus === 'RUNNING' &&
<div className="history_box padding_20 cursor_default bg_secondary">
<div style={{display: 'flex'}}>
<div className="fs_20">🧠</div>
<div className={styles.feed_title}><i>{loadingText}</i></div>
</div>
</div>}
{runStatus === 'COMPLETED' &&
<div className="history_box padding_20 cursor_default bg_secondary">
<div style={{display: 'flex'}}>
<div className="fs_20">🏁</div>
<div className={styles.feed_title}><i>All goals completed successfully!</i></div>
</div>
</div>}
{runStatus === 'ITERATION_LIMIT_EXCEEDED' &&
<div className="history_box padding_20 cursor_default bg_secondary">
<div style={{display: 'flex'}}>
<div className="fs_20"></div>
<div className={styles.feed_title}><i>Stopped: Maximum iterations exceeded!</i></div>
</div>
</div>}
{runStatus === 'ERROR_PAUSED' &&
<div className="history_box padding_20 cursor_default bg_secondary">
<div style={{display: 'flex'}}>
<div className="fs_20"></div>
<div className={styles.feed_title}>{errorMsg}</div>
</div>
</div>}
</div>
}
{feeds.length < 1 && !agent?.is_running && !agent?.is_scheduled &&
<div style={{
color: 'white',
fontSize: '14px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
textAlign: 'center',
width: '100%'
}}>The Agent is not scheduled</div>
}
</div>
{feedContainerRef.current && feedContainerRef.current.scrollTop >= 1200 &&
<div className="back_to_top" onClick={scrollToTop}
style={selectedView !== '' ? {right: 'calc(39% - 5vw)'} : {right: '39%'}}>
<Image width={15} height={15} src="/images/backtotop.svg" alt="back-to-top"/>
</div>}
</div>
</>)
}
File diff suppressed because it is too large Load Diff
+369
View File
@@ -0,0 +1,369 @@
import React, {useState, useEffect, useRef} from 'react';
import {setLocalStorageValue, convertToGMT, preventDefault, getUserClick} from "@/utils/utils";
import styles from "@/pages/Content/Agents/Agents.module.css";
import styles1 from "@/pages/Content/Agents/react-datetime.css";
import Image from "next/image";
import Datetime from "react-datetime";
import {toast} from "react-toastify";
import {agentScheduleComponent, createAndScheduleRun, updateSchedule} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import moment from 'moment';
export default function AgentSchedule({
internalId,
closeCreateModal,
type,
agentId,
setCreateModal,
setCreateEditModal,
env,
}) {
const [isRecurring, setIsRecurring] = useState(false);
const [timeDropdown, setTimeDropdown] = useState(false);
const [expiryDropdown, setExpiryDropdown] = useState(false);
const [startTime, setStartTime] = useState('');
const timeUnitArray = (env === 'PROD') ? ['Days', 'Hours'] : ['Days', 'Hours', 'Minutes'];
const [timeUnit, setTimeUnit] = useState(timeUnitArray[1]);
const [timeValue, setTimeValue] = useState(null);
const expiryTypeArray = ['Specific Date', 'After certain number of runs', 'No expiry'];
const [expiryType, setExpiryType] = useState(expiryTypeArray[1]);
const [expiryRuns, setExpiryRuns] = useState(0);
const [expiryDate, setExpiryDate] = useState(null);
const timeRef = useRef(null);
const expiryRef = useRef(null);
const [modalHeading, setModalHeading] = useState('Schedule Run')
const [modalButton, setModalButton] = useState('Create and Schedule Run')
const [localStartTime, setLocalStartTime] = useState('')
useEffect(() => {
function handleClickOutside(event) {
if (timeRef.current && !timeRef.current.contains(event.target)) {
setTimeDropdown(false)
}
if (expiryRef.current && !expiryRef.current.contains(event.target)) {
setExpiryDropdown(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
useEffect(() => {
if (type === "edit_schedule_agent") {
setModalHeading('Edit Schedule')
setModalButton("Update Schedule")
fetchAgentScheduleComponent();
}
}, []);
useEffect(() => {
if (internalId !== null) {
const agent_is_recurring = localStorage.getItem("agent_is_recurring_" + String(internalId));
if (agent_is_recurring) {
setIsRecurring(JSON.parse(agent_is_recurring));
}
const agent_time_unit = localStorage.getItem("agent_time_unit_" + String(internalId));
if (agent_time_unit) {
setTimeUnit(agent_time_unit);
}
const agent_time_value = localStorage.getItem("agent_time_value_" + String(internalId));
if (agent_time_value) {
setTimeValue(Number(agent_time_value));
}
const agent_expiry_type = localStorage.getItem("agent_expiry_type_" + String(internalId));
if (agent_expiry_type) {
setExpiryType(agent_expiry_type);
}
const agent_expiry_runs = localStorage.getItem("agent_expiry_runs_" + String(internalId));
if (agent_expiry_runs) {
setExpiryRuns(Number(agent_expiry_runs));
}
const agent_start_time = localStorage.getItem("agent_start_time_" + String(internalId));
if (agent_start_time) {
setStartTime(agent_start_time);
}
const agent_expiry_date = localStorage.getItem("agent_expiry_date_" + String(internalId));
if (agent_expiry_date) {
setExpiryDate(agent_expiry_date);
}
}
}, [internalId])
const handleDateTimeChange = (momentObj) => {
const expiryDate = convertToGMT(momentObj);
setLocalStorageValue("agent_expiry_date_" + String(internalId), expiryDate, setExpiryDate);
};
const handleTimeChange = (momentObj) => {
const startTime = convertToGMT(momentObj);
setLocalStartTime(typeof momentObj === 'string' ? '' : momentObj.toDate())
setLocalStorageValue("agent_start_time_" + String(internalId), startTime, setStartTime);
};
const toggleRecurring = () => {
setLocalStorageValue("agent_is_recurring_" + String(internalId), !isRecurring, setIsRecurring);
};
const handleTimeSelect = (index) => {
setLocalStorageValue("agent_time_unit_" + String(internalId), timeUnitArray[index], setTimeUnit);
setTimeDropdown(false);
}
const handleExpirySelect = (index) => {
setLocalStorageValue("agent_expiry_type_" + String(internalId), expiryTypeArray[index], setExpiryType);
setExpiryDropdown(false);
}
const handleDateChange = (event) => {
setLocalStorageValue("agent_time_value_" + String(internalId), event.target.value, setTimeValue);
};
const handleExpiryRuns = (event) => {
setLocalStorageValue("agent_expiry_runs_" + String(internalId), event.target.value, setExpiryRuns);
};
const addScheduledAgent = () => {
if ((startTime === '' || (isRecurring === true && (timeValue == null || (expiryType === "After certain number of runs" && (parseInt(expiryRuns, 10) < 1)) || (expiryType === "Specific date" && expiryDate == null))))) {
toast.error('Please input correct details', {autoClose: 1800});
return;
}
if (type === "create_agent") {
const scheduleData = {
"start_time": startTime,
"recurrence_interval": timeValue ? `${timeValue} ${timeUnit}` : null,
"expiry_runs": expiryType === 'After certain number of runs' ? parseInt(expiryRuns) : -1,
"expiry_date": expiryType === 'Specific Date' ? expiryDate : null,
}
EventBus.emit('handleAgentScheduling', scheduleData);
getUserClick('Agent Scheduled', {'Type': 'New Agent'})
} else {
if (type === "schedule_agent") {
const requestData = {
"agent_id": agentId,
"start_time": startTime,
"recurrence_interval": timeValue && isRecurring ? `${timeValue} ${timeUnit}` : null,
"expiry_runs": expiryType === 'After certain number of runs' && isRecurring ? parseInt(expiryRuns) : -1,
"expiry_date": expiryType === 'Specific Date' && isRecurring ? expiryDate : null,
};
createAndScheduleRun(requestData)
.then(response => {
const {schedule_id} = response.data;
toast.success('Scheduled successfully!', {autoClose: 1800});
setCreateModal();
getUserClick('Agent Scheduled', {'Type': 'Existing Agent'})
EventBus.emit('reFetchAgents', {});
setTimeout(() => {
EventBus.emit('refreshDate', {});
}, 1000)
})
.catch(error => {
console.error('Error:', error);
});
} else {
if (type === "edit_schedule_agent") {
fetchUpdateSchedule();
}
}
}
};
function checkTime() {
if (expiryDate === null) {
return true;
}
let date1 = expiryDate;
if (typeof expiryDate === 'string' && expiryDate.includes('/')) {
date1 = moment(expiryDate, 'DD/MM/YYYY').toDate();
} else if (typeof expiryDate === 'string') {
date1 = moment.utc(expiryDate, 'YYYY-MM-DD HH:mm:ss').local().toDate();
} else
return
let date2 = moment.utc(startTime, 'YYYY-MM-DD HH:mm:ss').local().toDate();
date1.setHours(0, 0, 0, 0);
date2.setHours(0, 0, 0, 0);
date1 = convertToGMT(date1);
date2 = convertToGMT(date2);
return date1 <= date2;
}
function fetchUpdateSchedule() {
if (expiryType === 'Specific Date' && checkTime()) {
toast.error('Expiry Date of agent is before Start Date')
return;
}
const requestData = {
"agent_id": agentId,
"start_time": startTime,
"recurrence_interval": timeValue && isRecurring ? `${timeValue} ${timeUnit}` : null,
"expiry_runs": expiryType === 'After certain number of runs' && isRecurring ? parseInt(expiryRuns) : -1,
"expiry_date": expiryType === 'Specific Date' && isRecurring ? (expiryDate && expiryDate.includes('/') ? convertToGMT(moment(expiryDate, 'DD/MM/YYYY').toDate()) : expiryDate) : null,
};
updateSchedule(requestData)
.then((response) => {
if (response.status === 200) {
toast.success('Schedule updated successfully', {autoClose: 1800});
EventBus.emit('refreshDate', {});
setCreateEditModal();
EventBus.emit('reFetchAgents', {});
} else {
toast.error('Error updating agent schedule', {autoClose: 1800});
}
})
.catch((error) => {
console.error('Error updating agent schedule:', error);
});
}
function fetchAgentScheduleComponent() {
agentScheduleComponent(agentId)
.then((response) => {
const {current_datetime, recurrence_interval, expiry_date, expiry_runs, start_date, start_time} = response.data;
setExpiryRuns(expiry_runs);
setExpiryDate(expiry_date);
if ((expiry_date || expiry_runs !== -1) && recurrence_interval !== null) {
setTimeValue(parseInt(recurrence_interval.substring(0, 1), 10))
setTimeUnit(recurrence_interval.substring(2,))
setIsRecurring(true);
setExpiryType(expiry_date ? 'Specific Date' : 'After certain number of runs');
} else {
setExpiryType('No expiry');
}
})
.catch((error) => {
console.error('Error fetching agent data:', error);
});
}
return (
<div>
<div className="modal" onClick={closeCreateModal}>
<div className="modal-content" style={{width: '35%'}} onClick={preventDefault}>
<div className={styles.detail_name}>{modalHeading}</div>
<div>
<label className={styles.form_label}>Select a date and time</label>
<div>
<Datetime className={`${styles1.className} ${styles.rdtPicker}`} onChange={handleTimeChange}
inputProps={{placeholder: new Date()}}
isValidDate={current => current.isAfter(Datetime.moment().subtract(1, 'day'))}/>
</div>
</div>
<div style={{display: 'flex', marginTop: '20px'}}>
<input className="checkbox" type="checkbox" checked={isRecurring} onChange={toggleRecurring}/>
<label className={styles.form_label} style={{marginLeft: '7px', cursor: 'pointer'}}
onClick={toggleRecurring}>
Recurring run
</label>
</div>
{isRecurring && (<div style={{marginTop: '20px'}}>
<div style={{color: "white", marginBottom: '10px'}}>Recurring run details</div>
<label className={styles.form_label}>Repeat every</label>
<div style={{display: 'flex', marginBottom: '20px'}}>
<div style={{width: '70%', marginRight: '5px'}}>
<input className="input_medium" type="number" value={timeValue} onChange={handleDateChange}
placeholder='Enter here'/>
</div>
<div style={{width: '30%'}}>
<div className="custom_select_container" onClick={() => setTimeDropdown(!timeDropdown)}
style={{width: '100%'}}>
{timeUnit}<Image width={20} height={21}
src={!timeDropdown ? '/images/dropdown_down.svg' : '/images/dropdown_up.svg'}
alt="expand-icon"/>
</div>
<div>
{timeDropdown && <div className="custom_select_options" ref={timeRef} style={{width: '137px'}}>
{timeUnitArray.map((timeUnit, index) => (
<div key={index} className="custom_select_option" onClick={() => handleTimeSelect(index)}
style={{padding: '12px 14px', maxWidth: '100%'}}>
{timeUnit}
</div>))}
</div>}
</div>
</div>
</div>
<label className={styles.form_label}>Recurring expiry</label>
<div>
<div style={{display: 'inline'}}>
<div style={{width: '100%', marginRight: '5px'}}>
<div className="custom_select_container" onClick={() => setExpiryDropdown(!expiryDropdown)}
style={{width: '100%'}}>
{expiryType}<Image width={20} height={21}
src={!expiryDropdown ? '/images/dropdown_down.svg' : '/images/dropdown_up.svg'}
alt="expand-icon"/>
</div>
<div>
{expiryDropdown && <div className="custom_select_options" ref={expiryRef}>
{expiryTypeArray.map((expiryType, index) => (
<div key={index} className="custom_select_option" onClick={() => handleExpirySelect(index)}
style={{padding: '12px 14px', maxWidth: '100%'}}>
{expiryType}
</div>))}
</div>}
</div>
</div>
{expiryType === 'After certain number of runs' && (
<div style={{width: '100%', marginTop: '10px'}}>
<input className="input_medium" type="number" value={expiryRuns} onChange={handleExpiryRuns}
placeholder="Enter the number of runs"/>
</div>
)}
{expiryType === 'Specific Date' && (
<div style={{width: '100%', marginTop: '10px'}}>
{type !== "edit_schedule_agent" &&
<Datetime timeFormat={false} className={`${styles1.className} ${styles.rdtPicker}`}
onChange={handleDateTimeChange} inputProps={{placeholder: new Date()}}
isValidDate={current => current.isAfter(moment(localStartTime))}/>}
{type === "edit_schedule_agent" && expiryDate && <div className={styles.form_label} style={{
display: 'flex',
fontSize: '14px',
justifyContent: 'space-between'
}}>
<div>The expiry date of the run
is {(new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10) == "Invalid Da" ? expiryDate : (new Date(`${expiryDate}Z`).toLocaleString()).substring(0, 10)}</div>
<div className="secondary_button" style={{cursor: 'pointer', height: '20px', fontSize: '12px'}}
onClick={() => setExpiryDate(null)}>Edit
</div>
</div>}
{type === "edit_schedule_agent" && !expiryDate &&
<Datetime timeFormat={false} className={`${styles1.className} ${styles.rdtPicker}`}
onChange={handleDateTimeChange} inputProps={{placeholder: new Date()}}
isValidDate={current => current.isAfter(moment(localStartTime))}/>}
</div>
)}
</div>
</div>
</div>)}
<div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '20px'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={closeCreateModal}>
Cancel
</button>
<button className={styles.run_button} style={{paddingLeft: '15px', paddingRight: '15px', height: '32px'}}
onClick={addScheduledAgent}>
{modalButton}
</button>
</div>
</div>
</div>
</div>
);
};
@@ -0,0 +1,125 @@
import React, {useEffect, useState} from "react";
import Image from "next/image";
import styles from '../Marketplace/Market.module.css';
import {fetchAgentTemplateListLocal} from "@/pages/api/DashboardService";
import AgentCreate from "@/pages/Content/Agents/AgentCreate";
import {setLocalStorageValue, openNewTab, getUserClick} from "@/utils/utils";
export default function AgentTemplatesList({
sendAgentData,
knowledge,
selectedProjectId,
fetchAgents,
toolkits,
organisationId,
internalId,
sendKnowledgeData,
env
}) {
const [agentTemplates, setAgentTemplates] = useState([])
const [createAgentClicked, setCreateAgentClicked] = useState(false)
const [sendTemplate, setSendTemplate] = useState(null)
useEffect(() => {
fetchAgentTemplateListLocal()
.then((response) => {
const data = response.data || [];
setAgentTemplates(data);
})
.catch((error) => {
console.error('Error fetching agent templates:', error);
});
}, [])
useEffect(() => {
if (internalId !== null) {
const agent_create_click = localStorage.getItem("agent_create_click_" + String(internalId)) || 'false';
if (agent_create_click) {
setCreateAgentClicked(JSON.parse(agent_create_click));
}
}
}, [internalId])
function redirectToCreateAgent() {
getUserClick('Agent Creating From Scratch', {})
setLocalStorageValue("agent_create_click_" + String(internalId), true, setCreateAgentClicked);
}
function openMarketplace() {
getUserClick('Going To Marketplace to see Agent Templates', {})
openNewTab(-4, "Marketplace", "Marketplace", false);
localStorage.setItem('marketplace_tab', 'market_agents');
}
function handleTemplateClick(item) {
getUserClick('Agent Creating Using Template', {'Template Name': item.name})
setSendTemplate(item);
setLocalStorageValue("agent_create_click_" + String(internalId), true, setCreateAgentClicked);
}
return (
<div>
{!createAgentClicked ?
<div>
<div className='row' style={{marginTop: '10px'}}>
<div className='col-12'>
<span className={styles.description_heading}
style={{
fontWeight: '400',
verticalAlign: 'text-top',
marginLeft: '10px',
fontSize: '16px'
}}>Choose a template</span>
<button className="primary_button" onClick={redirectToCreateAgent}
style={{float: 'right', marginRight: '3px'}}>&nbsp;Build From Scratch
</button>
</div>
</div>
<div className={styles.rowContainer}
style={{maxHeight: '78vh', overflowY: 'auto', marginTop: '10px', marginLeft: '3px'}}>
{agentTemplates.length > 0 ? <div className="marketplaceGrid3">
{agentTemplates.map((item) => (
<div className="market_containers cursor_pointer" key={item.id} style={{cursor: 'pointer', height: '85px'}}
onClick={() => handleTemplateClick(item)}>
<div style={{display: 'inline', overflow: 'auto'}}>
<div>{item.name}</div>
<div className={styles.tool_description}>{item.description}</div>
</div>
</div>
))}
<div className="market_containers cursor_pointer" style={{cursor: 'pointer', height: '85px', background: '#413C4F'}}
onClick={openMarketplace}>
<div style={{display: 'inline', overflow: 'auto'}}>
<div style={{display: 'flex', justifyContent: 'space-between', gap: '0.3vw'}}>
<div style={{order: '0'}}><Image style={{marginTop: '-3px'}} width={16} height={16}
src="/images/marketplace.svg"
alt="arrow-outward"/>&nbsp;&nbsp;Browse templates from marketplace
</div>
<div style={{order: '1'}}><Image style={{marginTop: '-3px'}} width={16} height={16}
src="/images/arrow_outward.svg" alt="arrow-outward"/></div>
</div>
<div className={styles.tool_description}>
SuperAGI marketplace offers a large selection of templates to choose from, so you are sure to find
one that is right for you!
</div>
</div>
</div>
</div> : <div className={styles.empty_templates}>
<div style={{textAlign: 'center'}}>
<Image width={100} height={100} src="/images/marketplace_empty.svg" alt="empty-templates"/>
<div style={{textAlign: 'center', color: 'white', marginTop: '15px', fontSize: '15px'}}>Browse templates
from marketplace
</div>
<div style={{marginTop: '15px', display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
<button className="primary_button" onClick={openMarketplace}>Go to marketplace</button>
</div>
</div>
</div>}
</div>
</div> : <AgentCreate sendKnowledgeData={sendKnowledgeData} knowledge={knowledge} internalId={internalId}
organisationId={organisationId} sendAgentData={sendAgentData}
selectedProjectId={selectedProjectId} fetchAgents={fetchAgents} toolkits={toolkits}
template={sendTemplate} env={env}/>}
</div>
)
};
+638
View File
@@ -0,0 +1,638 @@
import React, {useEffect, useState, useMemo} from 'react';
import Image from 'next/image';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import styles from './Agents.module.css';
import ActivityFeed from './ActivityFeed';
import TaskQueue from './TaskQueue';
import RunHistory from "./RunHistory";
import ActionConsole from "./ActionConsole";
import Details from "./Details";
import ResourceManager from "./ResourceManager";
import {createInternalId, getUserClick, preventDefault} from "@/utils/utils";
import {
getAgentDetails,
getAgentExecutions,
updateExecution,
addExecution,
getExecutionDetails,
saveAgentAsTemplate,
stopSchedule,
getDateTime,
deleteAgent, publishToMarketplace
} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import 'moment-timezone';
import AgentSchedule from "@/pages/Content/Agents/AgentSchedule";
export default function AgentWorkspace({env, agentId, agentName, selectedView, agents, internalId, sendAgentData}) {
const [leftPanel, setLeftPanel] = useState('activity_feed')
const [rightPanel, setRightPanel] = useState('details')
const [history, setHistory] = useState(true)
const [selectedRun, setSelectedRun] = useState(null)
const [runModal, setRunModal] = useState(false)
const [deleteModal, setDeleteModal] = useState(false)
const [goals, setGoals] = useState(null)
const [currentGoals, setCurrentGoals] = useState(null)
const [runName, setRunName] = useState("New Run")
const [agentDetails, setAgentDetails] = useState(null)
const [agentExecutions, setAgentExecutions] = useState(null)
const [dropdown, setDropdown] = useState(false);
const [fetchedData, setFetchedData] = useState(null);
const [instructions, setInstructions] = useState(['']);
const [currentInstructions, setCurrentInstructions] = useState(['']);
const [pendingPermission, setPendingPermissions] = useState(0);
const agent = agents.find(agent => agent.id === agentId);
const [createModal, setCreateModal] = useState(false);
const [createEditModal, setCreateEditModal] = useState(false);
const [createStopModal, setCreateStopModal] = useState(false);
const [agentScheduleDetails, setAgentScheduleDetails] = useState(null)
const [publishModal, setPublishModal] = useState(false);
const [publishModalState, setPublishModalState] = useState(false);
const closeCreateModal = () => {
setCreateModal(false);
setCreateEditModal(false);
setCreateStopModal(false);
};
const handleEditScheduleClick = () => {
setCreateEditModal(true);
setDropdown(false);
};
const handlePublishToMarketplace =() => {
if(agent?.is_scheduled && agentExecutions?.length < 1){
setDropdown(false)
setPublishModalState(true)
setPublishModal(true)
return;
}
publishToMarketplace(selectedRun?.id)
.then((response) => {
setDropdown(false)
setPublishModalState(false)
setPublishModal(true)
})
.catch((error) => {
console.error('Error publishing to marketplace:', error);
});
};
const handleStopScheduleClick = () => {
setCreateStopModal(true);
setCreateModal(false);
setDropdown(false);
};
function fetchStopSchedule() {//Stop Schedule
stopSchedule(agentId)
.then((response) => {
if (response.status === 200) {
toast.success('Schedule stopped successfully!', {autoClose: 1800});
setCreateStopModal(false);
EventBus.emit('reFetchAgents', {});
setAgentScheduleDetails(null)
}
})
.catch((error) => {
console.error('Error stopping agent schedule:', error);
});
};
const pendingPermissions = useMemo(() => {
if (!fetchedData) return 0;
setPendingPermissions(fetchedData.filter(permission => permission.status === "PENDING").length);
}, [fetchedData]);
const addInstruction = () => {
setInstructions((prevArray) => [...prevArray, 'new instructions']);
};
const handleInstructionDelete = (index) => {
const updatedInstructions = [...instructions];
updatedInstructions.splice(index, 1);
setInstructions(updatedInstructions);
};
const handleInstructionChange = (index, newValue) => {
const updatedInstructions = [...instructions];
updatedInstructions[index] = newValue;
setInstructions(updatedInstructions);
};
const addGoal = () => {
setGoals((prevArray) => [...prevArray, 'new goal']);
};
const handleGoalChange = (index, newValue) => {
const updatedGoals = [...goals];
updatedGoals[index] = newValue;
setGoals(updatedGoals);
};
const handleGoalDelete = (index) => {
const updatedGoals = [...goals];
updatedGoals.splice(index, 1);
setGoals(updatedGoals);
};
const handleRunNameChange = (event) => {
setRunName(event.target.value);
}
const handleCreateRun = () => {
if (runName.replace(/\s/g, '') === '') {
toast.error("Run name can't be blank", {autoClose: 1800});
return
}
setGoals(goals.length< 0 ? agentDetails.goal : goals)
setInstructions(instructions.length < 0 ? agentDetails.instruction : instructions)
if (goals.length <= 0) {
toast.error("Agent needs to have goals", {autoClose: 1800});
return
}
const executionData = {
"agent_id": agentId,
"name": runName,
"goal": goals,
"instruction": instructions
}
addExecution(executionData)
.then((response) => {
setRunModal(false);
fetchExecutions(agentId, response.data);
fetchAgentDetails(agentId, selectedRun?.id);
EventBus.emit('reFetchAgents', {});
toast.success("New run created", {autoClose: 1800});
getUserClick('Agent New Run Created Successfully',{'IsReRun' : true})
})
.catch((error) => {
console.error('Error creating execution:', error);
toast.error("Could not create run", {autoClose: 1800});
});
};
const handleDeleteAgent = () => {
deleteAgent(agentId)
.then((response) => {
setDeleteModal(false);
if (response.status === 200) {
EventBus.emit('reFetchAgents', {});
EventBus.emit('removeTab', {
element: {
id: agentId,
name: agentName,
contentType: "Agents",
internalId: internalId
}
})
toast.success("Agent Deleted Successfully", {autoClose: 1800});
} else {
toast.error("Agent Could not be Deleted", {autoClose: 1800});
}
})
.catch((error) => {
setDeleteModal(false);
toast.error("Agent Could not be Deleted", {autoClose: 1800});
console.error("Agent could not be deleted: ", error);
});
}
const closeRunModal = () => {
setRunName("New Run");
setRunModal(false);
};
const closeDeleteModal = () => {
setDeleteModal(false);
}
const updateRunStatus = (status) => {
const executionData = {"status": status};
updateExecution(selectedRun.id, executionData)
.then((response) => {
EventBus.emit('updateRunStatus', {selectedRunId: selectedRun.id, status: status});
if (status !== 'TERMINATED') {
fetchExecutions(agentId, response.data);
} else {
fetchExecutions(agentId);
}
EventBus.emit('reFetchAgents', {});
})
.catch((error) => {
console.error('Error updating execution:', error);
});
setDropdown(false);
};
useEffect(() => {
fetchAgentDetails(agentId, selectedRun?.id);
fetchExecutions(agentId);
fetchAgentScheduleComponent()
}, [agentId])
useEffect(() => {
// fetchExecutionDetails(selectedRun?.id);
fetchAgentDetails(agentId, selectedRun?.id);
}, [selectedRun?.id])
useEffect(() => {
if (agentDetails) {
setRightPanel(agentDetails.permission_type === 'RESTRICTED' ? 'action_console' : 'details');
}
}, [agentDetails])
function setNewRunDetails() {
getAgentDetails(agentId, -1)
.then((response) => {
setGoals(response.data.goal)
setInstructions(response.data.instruction)
setRunModal(true);
})
.catch((error) => {
console.error('Error fetching agent details:', error);
});
}
function fetchAgentDetails(agentId, runId) {
getAgentDetails(agentId, runId ? runId : -1)
.then((response) => {
const data = response.data
setAgentDetails(data);
})
.catch((error) => {
console.error('Error fetching agent details:', error);
});
}
function fetchAgentScheduleComponent() {
if (agent?.is_scheduled) {
getDateTime(agentId)
.then((response) => {
setAgentScheduleDetails(response.data)
})
.catch((error) => {
console.error('Error fetching agent data:', error);
});
}
}
function fetchExecutions(agentId, currentRun = null) {
getAgentExecutions(agentId)
.then((response) => {
let data = response.data
data = data.filter((run) => run.status !== 'TERMINATED');
setAgentExecutions(data);
setSelectedRun(currentRun ? currentRun : data[0]);
})
.catch((error) => {
console.error('Error fetching agent executions:', error);
});
}
// function fetchExecutionDetails(executionId) {
// getExecutionDetails(executionId || -1, agentId)
// .then((response) => {
// setGoals(response.data.goal);
// setCurrentGoals(response.data.goal);
// setInstructions(response.data.instruction);
// setCurrentInstructions(response.data.instruction);
// })
// .catch((error) => {
// console.error('Error fetching agent execution details:', error);
// });
// }
function saveAgentTemplate() {
saveAgentAsTemplate(agentId, selectedRun?.id ? selectedRun?.id : -1)
.then((response) => {
toast.success("Agent saved as template successfully", {autoClose: 1800});
})
.catch((error) => {
console.error('Error saving agent as template:', error);
});
setDropdown(false);
}
useEffect(() => {
const resetRunStatus = (eventData) => {
if (eventData.executionId === selectedRun.id) {
setSelectedRun((prevSelectedRun) => (
{...prevSelectedRun, status: eventData.status}
));
}
};
const refreshDate = () => {
fetchAgentScheduleComponent()
};
EventBus.on('resetRunStatus', resetRunStatus);
EventBus.on('refreshDate', refreshDate);
return () => {
EventBus.off('resetRunStatus', resetRunStatus);
EventBus.off('refreshDate', refreshDate);
};
});
return (<>
<div style={{display: 'flex'}}>
{history && selectedRun !== null &&
<RunHistory runs={agentExecutions} selectedRunId={selectedRun?.id} setSelectedRun={setSelectedRun}
setHistory={setHistory} setAgentExecutions={setAgentExecutions}/>}
<div style={{width: history ? '40%' : '60%'}}>
<div className={styles.detail_top}>
<div style={{display: 'flex'}}>
{!history && selectedRun !== null &&
<div style={{display: 'flex', alignItems: 'center', cursor: 'pointer', marginRight: '7px'}}
onClick={() => setHistory(true)}>
<div className={styles.run_history_button}><Image style={{marginTop: '-2px'}} width={16} height={16}
src="/images/update.svg"
alt="update-icon"/><span>&nbsp;Show run history</span>
</div>
</div>}
<div style={{display: 'flex', alignItems: 'center', marginLeft: '2px'}} className={styles.tab_text}>
{selectedRun && selectedRun.status === 'RUNNING' &&
<div style={{marginLeft: '-6px'}}><Image width={14} height={14} style={{mixBlendMode: 'exclusion'}}
src="/images/loading.gif" alt="loading-icon"/></div>}
<div className={styles.single_line_block} style={selectedRun && selectedRun.status === 'RUNNING' ? {
marginLeft: '7px',
maxWidth: '100px'
} : {marginLeft: '-8px', maxWidth: '100px'}}>{selectedRun?.name || ''}</div>
</div>
<div style={{marginLeft: '7px'}}>
<button onClick={() => setLeftPanel('activity_feed')} className={styles.tab_button}
style={leftPanel === 'activity_feed' ? {background: '#454254'} : {background: 'transparent'}}>Activity
Feed
</button>
</div>
{agentDetails && (agentDetails.agent_workflow === 'Dynamic Task Workflow' || agentDetails.agent_workflow === "Fixed Task Workflow") &&
<div style={{marginLeft: '7px'}}>
<button onClick={() => setLeftPanel('agent_workflow')} className={styles.tab_button}
style={leftPanel === 'agent_workflow' ? {background: '#454254'} : {background: 'transparent'}}>Task
Queue
</button>
</div>}
</div>
<div style={{display: 'flex'}}>
<div>
<button className={styles.run_button} onClick={setNewRunDetails}>
<Image width={14} height={14} src="/images/run_icon.svg" alt="run-icon"/>&nbsp;New Run
</button>
</div>
<button className="secondary_button" style={{padding: '8px', height: '31px'}}
onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)} >
<Image width={14} height={14} src="/images/three_dots.svg" alt="run-icon"/>
</button>
{dropdown && <div onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<ul className="dropdown_container w_180p" style={{marginTop: '31px', marginLeft: '-32px'}}>
{selectedRun && selectedRun.status === 'RUNNING' && <li className="dropdown_item" onClick={() => {
updateRunStatus("PAUSED")
}}>Pause</li>}
{selectedRun && (selectedRun.status === 'CREATED' || selectedRun.status === 'PAUSED' || selectedRun.status === 'ERROR_PAUSED') &&
<li className="dropdown_item" onClick={() => {
updateRunStatus("RUNNING")
}}>Resume</li>}
{agentExecutions && agentExecutions.length > 1 && <li className="dropdown_item" onClick={() => {
updateRunStatus("TERMINATED")
}}>Delete Run</li>}
{agentExecutions && selectedRun && (selectedRun.status === 'CREATED' || selectedRun.status === 'PAUSED' || selectedRun.status === 'RUNNING' || agentExecutions.length > 1 || selectedRun.status === 'ERROR_PAUSED') && <div className={styles.dropdown_separator}/>}
<li className="dropdown_item" onClick={() => saveAgentTemplate()}>Save as Template</li>
{agent && env === 'PROD' &&
<li className="dropdown_item" onClick={() => {
handlePublishToMarketplace()
}}>Publish to marketplace</li>}
<div className={styles.dropdown_separator} />
<li className="dropdown_item" onClick={() => sendAgentData({
id: agentId,
name: "Edit Agent",
contentType: "Edit_Agent",
internalId: createInternalId()
})}>Edit Agent</li>
<li className="dropdown_item" onClick={() => {
setDropdown(false);
setDeleteModal(true)
}}>Delete Agent
</li>
<div className={styles.dropdown_separator} />
{agent?.is_scheduled ? (<div>
<li className="dropdown_item" onClick={handleEditScheduleClick}>Edit Schedule</li>
<li className="dropdown_item" onClick={handleStopScheduleClick}>Stop Schedule</li>
</div>) : (<div>
{agent && !agent?.is_running && !agent?.is_scheduled &&
<li className="dropdown_item" onClick={() => {
setDropdown(false);
setCreateModal(true)
}}>Schedule Run</li>}
</div>)}
</ul>
</div>}
{createModal &&
<AgentSchedule env={env} internalId={internalId} closeCreateModal={closeCreateModal} type="schedule_agent"
agentId={agentId} setCreateModal={() => setCreateModal(false)}/>}
{createEditModal &&
<AgentSchedule env={env} internalId={internalId} closeCreateModal={closeCreateModal}
type="edit_schedule_agent"
agentId={agentId} setCreateEditModal={() => setCreateEditModal(false)}/>}
{createStopModal && (
<div className="modal" onClick={closeCreateModal}>
<div className="modal-content" style={{width: '35%'}} onClick={preventDefault}>
<div className={styles.detail_name}>Stop Schedule</div>
<label className={styles.form_label}>All further schedules of this agent will be stopped. Are you sure
you want to proceed?</label>
<div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '20px'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={closeCreateModal}>
Cancel
</button>
<button className={styles.run_button} style={{paddingLeft: '15px', paddingRight: '25px'}}
onClick={fetchStopSchedule}>
Stop Schedule
</button>
</div>
</div>
</div>
)}
</div>
</div>
<div className={styles.detail_body}>
{leftPanel === 'activity_feed' && <div className={styles.detail_content}>
<ActivityFeed selectedView={selectedView} selectedRunId={selectedRun?.id || null}
setFetchedData={setFetchedData} agent={agent} selectedRunStatus={selectedRun?.status || null}/>
</div>}
{leftPanel === 'agent_workflow' &&
<div className={styles.detail_content}><TaskQueue selectedRunId={selectedRun?.id || 0}/></div>}
</div>
</div>
<div style={{width: '40%'}}>
<div className={styles.detail_top}>
<div style={{display: 'flex', overflowX: 'scroll'}}>
{agentDetails && ((fetchedData && fetchedData.length > 0) || agentDetails.permission_type === 'RESTRICTED') && <div>
<button onClick={() => setRightPanel('action_console')} className={styles.tab_button}
style={rightPanel === 'action_console' ? {background: '#454254'} : {background: 'transparent'}}>
<Image style={{marginTop: '-1px'}} width={14} height={14} src="/images/action_console.svg"
alt="action-console-icon"/>&nbsp;Action Console &nbsp; {pendingPermission > 0 &&
<span className={styles.notification_circle}>{pendingPermission}</span>}
</button>
</div>}
{/*<div>*/}
{/* <button onClick={() => setRightPanel('feedback')} className={styles.tab_button} style={rightPanel === 'feedback' ? {background:'#454254'} : {background:'transparent'}}>*/}
{/* Feedback*/}
{/* </button>*/}
{/*</div>*/}
<div>
<button onClick={() => setRightPanel('details')} className={styles.tab_button}
style={rightPanel === 'details' ? {
background: '#454254',
paddingRight: '15px'
} : {background: 'transparent', paddingRight: '15px'}}>
<Image width={14} height={14} src="/images/info.svg" alt="details-icon"/>&nbsp;Details
</button>
</div>
<div>
<button onClick={() => setRightPanel('resource_manager')} className={styles.tab_button}
style={rightPanel === 'resource_manager' ? {
background: '#454254',
paddingRight: '15px'
} : {background: 'transparent', paddingRight: '15px'}}>
<Image width={14} height={14} src="/images/home_storage.svg" alt="manager-icon"/>&nbsp;Resource Manager
</button>
</div>
{/*<div>*/}
{/* <button onClick={() => setRightPanel('logs')} className={styles.tab_button} style={rightPanel === 'logs' ? {background:'#454254'} : {background:'transparent'}}>*/}
{/* Logs*/}
{/* </button>*/}
{/*</div>*/}
</div>
</div>
<div className={styles.detail_body} style={{paddingRight: '0'}}>
{rightPanel === 'action_console' && agentDetails && (
<div className={styles.detail_content}>
<ActionConsole key={JSON.stringify(fetchedData)} actions={fetchedData}
pendingPermission={pendingPermission} setPendingPermissions={setPendingPermissions}/>
</div>
)}
{rightPanel === 'details' && agentDetails && agentDetails !== null &&
<div className={styles.detail_content}><Details agentDetails1={agentDetails}
runCount={agentExecutions?.length || 0}
agentScheduleDetails={agentScheduleDetails} agent={agent}/>
</div>}
{rightPanel === 'resource_manager' &&
<div className={styles.detail_content}><ResourceManager agentId={agentId} runs={agentExecutions}/></div>}
</div>
</div>
{runModal && (<div className="modal" onClick={closeRunModal}>
<div className="modal-content" style={{width: '35%'}} onClick={preventDefault}>
<div className={styles.detail_name}>Run agent name</div>
<div>
<label className={styles.form_label}>Name</label>
<input className="input_medium" type="text" value={runName} onChange={handleRunNameChange}/>
</div>
{goals && goals.length > 0 && <div style={{marginTop: '15px'}}>
<div><label className={styles.form_label}>Goals</label></div>
{goals.map((goal, index) => (<div key={index} style={{
marginBottom: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<div style={{flex: '1'}}><input className="input_medium" type="text" value={goal}
onChange={(event) => handleGoalChange(index, event.target.value)}/></div>
{goals.length > 1 && <div>
<button className="secondary_button" style={{marginLeft: '4px', padding: '5px'}}
onClick={() => handleGoalDelete(index)}>
<Image width={20} height={21} src="/images/close.svg" alt="close-icon"/>
</button>
</div>}
</div>))}
<div>
<button className="secondary_button" onClick={addGoal}>+ Add</button>
</div>
</div>}
<div style={{marginTop: '15px'}}>
<div><label className={styles.form_label}>Instructions<span
style={{fontSize: '9px'}}>&nbsp;(optional)</span></label></div>
{instructions.map((goal, index) => (<div key={index} style={{
marginBottom: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<div style={{flex: '1'}}><input className="input_medium" type="text" value={goal}
onChange={(event) => handleInstructionChange(index, event.target.value)}/>
</div>
{instructions.length > 1 && <div>
<button className="secondary_button" style={{marginLeft: '4px', padding: '5px'}}
onClick={() => handleInstructionDelete(index)}>
<Image width={20} height={21} src="/images/close.svg" alt="close-icon"/>
</button>
</div>}
</div>))}
<div>
<button className="secondary_button" onClick={addInstruction}>+ Add</button>
</div>
</div>
<div style={{display: 'flex', justifyContent: 'flex-end'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={closeRunModal}>
Cancel
</button>
<button className={styles.run_button} style={{paddingLeft: '15px', paddingRight: '25px'}}
onClick={() => handleCreateRun()}>
<Image width={14} height={14} src="/images/run_icon.svg" alt="run-icon"/>&nbsp;Run
</button>
</div>
</div>
</div>)}
{deleteModal && (<div className="modal" onClick={closeDeleteModal}>
<div className="modal-content" style={{width: '502px', padding: '16px', gap: '24px'}} onClick={preventDefault}>
<div>
<label className={styles.delete_agent_modal_label}>Delete Agent</label>
</div>
<div>
<label className={styles.delete_modal_text}>All the runs and details of this agent will be deleted. Are you
sure you want to proceed?</label>
</div>
<div style={{display: 'flex', justifyContent: 'flex-end'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={closeDeleteModal}>
Cancel
</button>
<button className="primary_button" onClick={() => handleDeleteAgent()}>
Delete Agent
</button>
</div>
</div>
</div>)}
{publishModal && (<div className="modal" onClick={() => {setPublishModal(false)}}>
<div className="modal-content w_35" onClick={preventDefault}>
{publishModalState ? <div className={styles.detail_name}>Run the agent at least once to publish!</div> : <div className={styles.detail_name}>Template submitted successfully!</div>}
<div>
{!publishModalState ? <label className={styles.form_label}>Your template is under review. Please check the marketplace in 2-3 days. If your template is not visible on the marketplace, reach out to us on Discord&nbsp;
<a href="https://discord.com/channels/1107593006032355359/1143813784683692093" target="_blank" rel="noopener noreferrer">
#agent-templates-submission
</a> channel.</label> : <label className={styles.form_label}>Before publishing your agent to the marketplace, you need to run it at least once. To do this, click the New Run button (on the agent screen). Once the agent has run successfully, you can proceed to try publishing your template.</label>}
</div>
<div className={styles.modal_buttons}>
<button className="primary_button" onClick={() => {setPublishModal(false)}}>
Okay
</button>
</div>
</div>
</div>)}
</div>
<ToastContainer/>
</>);
}
+38
View File
@@ -0,0 +1,38 @@
import React from 'react';
import Image from "next/image";
import 'react-toastify/dist/ReactToastify.css';
import {createInternalId, getUserClick} from "@/utils/utils";
import mixpanel from 'mixpanel-browser'
export default function Agents({sendAgentData, agents}) {
return (<>
<div className="container">
<p className="text_14 mt_8 mb_12 ml_8">Agents</p>
<div className="w_100 mb_10">
<button className="secondary_button w_100" onClick={() => {
sendAgentData({
id: -1,
name: "new agent",
contentType: "Create_Agent",
internalId: createInternalId()
}); getUserClick('Agent Create Clicked', {'Click Position': 'Sidebar'})
}}>
+ Create Agent
</button>
</div>
{agents && agents.length > 0 ? <div className="vertical_selection_scroll w_100">
{agents.map((agent, index) => (
<div key={index}>
<div className="agent_box w_100" onClick={() => sendAgentData(agent)}>
{agent?.is_running && <Image width={14} height={14} className="mix_blend_mode" src="/images/loading.gif" alt="active-icon"/>}
<div className="text_ellipsis"><span className="agent_text text_ellipsis">{agent.name}</span></div>
{agent?.is_scheduled && <Image className="ml_4" width={17} height={17} src="/images/event_repeat.svg" alt="check-icon"/>}
</div>
</div>
))}
</div> : <div className="form_label mt_20 horizontal_container justify_center">No Agents found</div>}
</div>
</>
);
}
+488
View File
@@ -0,0 +1,488 @@
.container {
height: 100%;
width: 100%;
padding: 0 0 0 8px;
}
.title_box {
width: 100%;
padding: 8px;
display: flex;
align-items: center;
}
.title_text {
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 17px;
display: flex;
align-items: center;
color: white;
}
.wrapper {
margin-bottom: 5px;
width: 100%;
}
.agent_active {
display: flex;
flex-direction: row;
align-items: center;
padding: 0;
}
.text_block {
display:flex;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.form_label {
font-size: 13px;
margin-bottom: 4px;
font-weight: 500;
color: #888888;
line-height: 17px;
}
.page_title {
text-align: left;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 17px;
display: flex;
align-items: center;
color: white;
margin-bottom: 25px;
}
.tool_text {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.detail_top {
width: 100%;
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10px;
}
.detail_body {
width: 100%;
padding-right: 10px;
margin-bottom: 20px;
}
.detail_content {
height: calc(100vh - 140px);
border-radius: 8px;
overflow-y: scroll;
padding-bottom: 0;
}
.tab_button {
border: none;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px;
display: -webkit-inline-flex;
align-items: center;
justify-content: center;
}
.tab_text {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
padding: 8px;
}
.tab_button:hover {
background: #454254;
}
.run_history_button{
background: rgba(255, 255, 255, 0.14);
border: none;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 12px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px 8px 5px;
}
.run_button {
background: #62A168;
border: none;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 7px 10px 8px 5px;
height: 31px;
margin-right: 7px;
}
.run_button:hover {
background: #57825b;
}
.pause_button {
background: #F78166;
border: none;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px;
}
.pause_button:hover {
background: #C95034;
}
.history_box {
width: 100%;
padding: 10px;
color: white;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 14px;
border-radius: 8px;
cursor: pointer;
margin-bottom: 7px;
}
.notification_bubble {
width: 14px;
height: 14px;
background: #DC6261;
border-radius: 200px;
display: flex;
align-items: center;
text-align: center;
padding: 4px;
font-size: 9px;
order: 1;
}
.history_info {
font-style: normal;
font-weight: 400;
font-size: 10px;
line-height: 12px;
color: #888888;
margin-left: 4px;
margin-top: 3px;
}
.feed_title {
font-family: 'Source Code Pro';
margin-left: 10px;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: white;
white-space: pre-line;
word-wrap: break-word;
max-width: 95%;
}
.feed_icon {
font-size: 20px;
margin-top: 5px;
}
.custom_task_box {
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 14px;
color: white;
width: 100%;
border-radius: 8px;
margin-bottom: 7px;
padding: 15px 20px;
}
.console_icons {
margin: -3px 3px 0 0;
}
.detail_name {
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 19px;
color: #FFFFFF;
margin-bottom: 10px;
}
.separator {
height: 15px;
margin-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.agent_info_box {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 10px;
}
.agent_info_tools {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
}
.resources {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.agent_resources {
width: 100%;
margin-top: 10px;
}
.large_text_box {
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
}
.show_more_button {
margin-top: 10px;
cursor: pointer;
width: fit-content;
color: #888888;
}
.single_line_block {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 80%;
}
.three_dots {
margin-left: 5px;
background: transparent;
border: none;
border-radius: 8px;
}
.more_details {
display: flex;
align-items: center;
margin-right: 30px;
}
.more_details_wrapper {
display: flex;
align-items: center;
margin-top: 20px;
justify-content: flex-start;
}
.task_header {
color: rgb(255, 255, 255);
padding: 7px;
font-size: 13px;
font-weight: 500;
font-family: 'Source Code Pro';
}
.text_12_n
{
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 14px;
color: #FFFFFF;
}
.notification_circle
{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 4px;
width: fit-content;
padding: 2px 5px;
height: fit-content;
background: #DC6161;
border-radius: 16px;
font-style: normal;
font-weight: 500;
font-size: 10px;
line-height: 12px;
color: #FFFFFF;
}
.delete_agent_modal_label
{
color: #FFF;
font-size: 16px;
font-family: "Public Sans", sans-serif;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.delete_button
{
display: flex;
padding: 0px 12px;
align-items: flex-start;
gap: 4px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: #FFF;
color: #000;
font-size: 12px;
font-family: "Public Sans", sans-serif;
font-style: normal;
font-weight: 500;
line-height: normal;
}
.delete_modal_text
{
color: #888;
font-size: 12px;
font-family: "Roboto Flex", sans-serif;
font-style: normal;
font-weight: 400;
line-height: normal;
}
.rdtPicker input {
display: flex !important;
flex-direction: row !important;
align-items: center !important;
padding: 8px 14px 8px 14px !important;
gap: 4px !important;
border-radius: 8px !important;
width: 100% !important;
height: 32px !important;
background: #3B3B49 !important;
border: 1px solid #4A4A55 !important;
font-style: normal !important;
font-weight: 400 !important;
font-size: 12px !important;
line-height: 16px !important;
transition: 0.2s !important;
color: white !important;
}
.rdtPicker input:focus {
display: flex;
flex-direction: row;
align-items: center;
padding: 8px 14px 8px 14px;
gap: 4px;
border-radius: 8px;
width: 100%;
height: 32px;
background: #3B3B49;
border: 1px solid #4A4A55;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
transition: 0.2s;
color: white;
outline: none;
}
.permission_changes {
color: #888888 !important;
text-decoration: line-through;
pointerEvents: none !important;
}
.modal_buttons{
display: flex;
justify-content: flex-end;
margin-top: 20px
}
.modal_info_class{
margin-left: -5px;
margin-right: 5px;
}
.table_contents{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 40px;
width: 100%
}
.create_settings_button{
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px
}
.button_margin{
margin-top: -10px;
}
.dropdown_separator{
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
margin-left:-5px;
width: 180px
}
.dropdown_container_agent{
bottom: 40px;
z-index: 9999;
padding: 0px;
width: fit-content;
height: fit-content;
margin-right: 40px;
background: #3B3B49;
border-radius: 8px;
position: absolute;
box-shadow: 0 2px 7px rgba(0,0,0,.4), 0 0 2px rgba(0,0,0,.22);
}
.dropdown_item_agent{
height:30px;
paddingTop: 2px;
paddingBottom: 2px;
}
+224
View File
@@ -0,0 +1,224 @@
import React, {useEffect, useState, useRef} from 'react';
import styles from './Agents.module.css';
import Image from "next/image";
import {formatNumber} from "@/utils/utils";
import {EventBus} from "@/utils/eventBus";
export default function Details({agentDetails1, runCount, agentScheduleDetails, agent}) {
const [showGoals, setShowGoals] = useState(false);
const [showConstraints, setShowConstraints] = useState(false);
const [showInstructions, setShowInstructions] = useState(false);
const [filteredInstructions, setFilteredInstructions] = useState([]);
const [scheduleText, setScheduleText] = useState('');
const [agentDetails, setAgentDetails] = useState(null)
const goalBoxRef = useRef(null);
const instructionBoxRef = useRef(null);
const constrainBoxRef = useRef(null);
const [isOverflowing, setIsOverflowing] = useState([false, false, false]);
const info_text = {
marginLeft: '7px',
};
const info_text_secondary = {
marginLeft: '3px',
marginTop: '2px',
color: '#888888',
lineHeight: '13px',
fontSize: '11px'
};
useEffect(() => {
const newOverflowing = [...isOverflowing];
if (goalBoxRef.current) {
newOverflowing[0] = goalBoxRef.current.scrollHeight > goalBoxRef.current.clientHeight;
}
if (instructionBoxRef.current) {
newOverflowing[1] = instructionBoxRef.current.scrollHeight > instructionBoxRef.current.clientHeight;
}
if (constrainBoxRef.current) {
newOverflowing[2] = constrainBoxRef.current.scrollHeight > constrainBoxRef.current.clientHeight;
}
setIsOverflowing(newOverflowing);
}, [agentDetails?.goal, filteredInstructions, agentDetails?.constraints]);
const openToolkitTab = (toolId) => {
EventBus.emit('openToolkitTab', {toolId: toolId});
}
useEffect(() => {
if (Array.isArray(agentDetails?.instruction)) {
setFilteredInstructions(agentDetails.instruction.filter(instruction => instruction.trim() !== ''));
}
}, [agentDetails]);
useEffect(() => {
setAgentDetails(agentDetails1)
}, [agentDetails1]);
useEffect(() => {
if(!agentScheduleDetails){
setScheduleText('')
return
}
if (agent?.is_scheduled) {
if (agentScheduleDetails?.recurrence_interval !== null) {
if ((agentScheduleDetails?.expiry_runs === -1 || agentScheduleDetails?.expiry_runs == null) && agentScheduleDetails?.expiry_date !== null) {
let expiryDate;
if (agentScheduleDetails?.expiry_date) {
const [day, month, year] = agentScheduleDetails.expiry_date.split("/");
expiryDate = new Date(year, month - 1, day);
}
const tempScheduleText = `The agent is scheduled to run on ${agentScheduleDetails?.start_date} ${agentScheduleDetails?.start_time} and will recursively run after every ${agentScheduleDetails?.recurrence_interval} and will expire after ${
expiryDate ? new Intl.DateTimeFormat('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric'
}).format(expiryDate) : ''
}`;
setScheduleText(tempScheduleText);
} else if ((agentScheduleDetails?.expiry_runs > 0) && agentScheduleDetails?.expiry_date == null) {
setScheduleText('The agent is scheduled to run on ' + agentScheduleDetails?.start_date + ' ' + agentScheduleDetails?.start_time + ' and will recursively run after every ' + agentScheduleDetails?.recurrence_interval + ' and will expire after ' + agentScheduleDetails?.expiry_runs + ' runs')
} else {
setScheduleText('The agent is scheduled to run on ' + agentScheduleDetails?.start_date + ' ' + agentScheduleDetails?.start_time + ' and will recursively run after every ' + agentScheduleDetails?.recurrence_interval + ' and will never expire')
}
} else {
setScheduleText('The agent is scheduled to run on ' + agentScheduleDetails?.start_date + ' ' + agentScheduleDetails?.start_time)
}
}
}, [agentScheduleDetails]);
return (<>
<div className={styles.history_box} style={{background: '#272335', padding: '15px', cursor: 'default'}}>
<div className={styles.detail_name}>{agentDetails?.name || ''}</div>
<div>{agentDetails?.description || ''}</div>
<div className={styles.separator}></div>
<div
style={{display: 'flex', marginBottom: '5px', alignItems: 'center', justifyContent: 'flex-start', gap: '7.5%'}}>
<div>
<div className={styles.agent_info_box}>
<div><Image width={12} height={12} src="/images/runs_made.svg" alt="runs-icon"/></div>
<div style={info_text_secondary}>Total Runs</div>
</div>
<div className={styles.feed_title} style={{fontSize: '20px', marginLeft: '0'}}>{runCount || 0}</div>
</div>
<div>
<div className={styles.agent_info_box}>
<div><Image width={12} height={12} src="/images/calls_made.svg" alt="calls-icon"/></div>
<div style={info_text_secondary}>Total Calls</div>
</div>
<div className={styles.feed_title}
style={{fontSize: '20px', marginLeft: '0'}}>{formatNumber(agentDetails?.calls || 0)}</div>
</div>
<div>
<div className={styles.agent_info_box}>
<div><Image width={12} height={12} src="/images/tokens_consumed.svg" alt="tokens-icon"/></div>
<div style={info_text_secondary}>Tokens Consumed</div>
</div>
<div className={styles.feed_title}
style={{fontSize: '20px', marginLeft: '0'}}>{formatNumber(agentDetails?.tokens || 0)}</div>
</div>
</div>
<div className={styles.separator}></div>
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/flag.svg" alt="goals-icon"/></div>
<div style={info_text}>{agentDetails?.goal?.length || 0} Goals</div>
</div>
{agentDetails?.goal && agentDetails?.goal?.length > 0 && <div>
<div ref={goalBoxRef} className={styles.large_text_box} style={!showGoals ? {overflow: 'hidden', display: '-webkit-box'} : {}}>
{agentDetails?.goal?.map((goal, index) => (<div key={index} style={{marginTop: '0'}}>
<div>{index + 1}. {goal || ''}</div>
{index !== agentDetails?.goal?.length - 1 && <br/>}
</div>))}
</div>
{isOverflowing[0] && <div className={styles.show_more_button} onClick={() => setShowGoals(!showGoals)}>
{showGoals ? 'Show Less' : 'Show More'}
</div>}
</div>}
{filteredInstructions && filteredInstructions.length > 0 && <div>
<div className={styles.separator}></div>
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/instructions.svg" alt="instruction-icon"/></div>
<div style={info_text}>{filteredInstructions.length || 0} Instructions</div>
</div>
<div>
<div className={styles.large_text_box} ref={instructionBoxRef}
style={!showInstructions ? {overflow: 'hidden', display: '-webkit-box'} : {}}>
{filteredInstructions.map((instruction, index) => (<div key={index} style={{marginTop: '0'}}>
<div>{index + 1}. {instruction || ''}</div>
{index !== filteredInstructions.length - 1 && <br/>}
</div>))}
</div>
{isOverflowing[1] && <div className={styles.show_more_button}
onClick={() => setShowInstructions(!showInstructions)}>{showInstructions ? 'Show Less' : 'Show More'}</div>}
</div>
</div>}
{agentDetails && <div>{agentDetails.tools && agentDetails.tools.length > 0 && <div>
<div className={styles.separator}></div>
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/tools_dark.svg" alt="tools-icon"/></div>
<div style={info_text}>Tools assigned</div>
</div>
<div className={styles.agent_info_tools}>
{agentDetails?.tools?.map((tool, index) =>
(<div onClick={() => openToolkitTab(tool.id)} key={index} className="tool_container"
style={{marginTop: '0', marginBottom: '5px', cursor: 'pointer'}}>
<div className={styles.tool_text}>{tool.name || ''}</div>
</div>))}
</div>
</div>}</div>}
{agentDetails && <div>{agentDetails.constraints && agentDetails.constraints?.length > 0 && <div>
<div className={styles.separator}></div>
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/close_fullscreen.svg" alt="constraint-icon"/></div>
<div style={info_text}>{agentDetails?.constraints.length || 0} Constraints</div>
</div>
<div className={styles.large_text_box} ref={constrainBoxRef}
style={!showConstraints ? {overflow: 'hidden', display: '-webkit-box'} : {}}>
{agentDetails?.constraints?.map((constraint, index) => (<div key={index} style={{marginTop: '0'}}>
<div>{index + 1}. {constraint || ''}</div>
{index !== agentDetails.constraints.length - 1 && <br/>}
</div>))}
</div>
{isOverflowing[2] && <div className={styles.show_more_button}
onClick={() => setShowConstraints(!showConstraints)}>{showConstraints ? 'Show Less' : 'Show More'}</div>}
</div>}</div>}
<div className={styles.separator}></div>
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/fact_check.svg" alt="queue-icon"/></div>
<div style={info_text}>{agentDetails?.agent_workflow || ''}</div>
</div>
{agentDetails?.knowledge_name && <div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/books.svg" alt="book-icon"/></div>
<div style={info_text}>{agentDetails?.knowledge_name}</div>
</div>}
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/deployed_code.svg" alt="model-icon"/></div>
<div style={info_text}>{agentDetails?.model || ''}</div>
</div>
{/*<div className={styles.agent_info_box}>*/}
{/* <div><Image width={15} height={15} src="/images/cancel_presentation.svg" alt="exit-icon"/></div>*/}
{/* <div style={info_text}>{exit}</div>*/}
{/*</div>*/}
{/* <div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/overview.svg" alt="window-icon"/></div>
<div style={info_text}>{agentDetails?.memory_window || 0} milliseconds</div>
</div> */}
<div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/key.svg" alt="permission-type-icon"/></div>
<div style={info_text}>{agentDetails?.permission_type?.replace(/\s*\([^)]*\)/g, '') || ''}</div>
</div>
{agentDetails?.max_iterations && <div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/info.svg" alt="info-icon"/></div>
<div style={info_text}>Stop after {agentDetails.max_iterations} iterations</div>
</div>}
{agent?.is_scheduled && scheduleText && <div className={styles.agent_info_box}>
<div><Image width={15} height={15} src="/images/event_repeat.svg" alt="info-icon"/></div>
<div style={info_text}>{scheduleText}</div>
</div>}
</div>
</>)
}
+75
View File
@@ -0,0 +1,75 @@
import React, {useState, useMemo} from 'react';
import styles from './Agents.module.css';
import Image from "next/image";
import {downloadFile, downloadAllFiles, formatBytes, returnResourceIcon} from "@/utils/utils";
export default function ResourceList({files, channel, runs}) {
const [selectedRunId, setSelectedRunId] = useState(null);
const filesByRun = useMemo(() => runs.map(run => {
const relatedFiles = files.filter(file => file.agent_execution_id === run.id);
return relatedFiles.length !== 0 && {"run": run, "files": relatedFiles};
}).filter(Boolean), [files, runs]);
const downloadRunFiles = (run_id, name) => {
const runFiles = files.filter(file => file.agent_execution_id === run_id);
runFiles.length !== 0 && downloadAllFiles(runFiles, name);
}
const isAnyFileWithAgentId = files.some(file => file.agent_execution_id !== null);
const File = ({file, index}) => (
<div key={index} onClick={() => downloadFile(file.id, file.name)} className={styles.history_box}
style={{background: '#272335', padding: '0px 10px', width: '49.5%'}}>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'flex-start'}}>
<div><Image width={28} height={46} src={returnResourceIcon(file)} alt="file-icon"/></div>
<div style={{marginLeft: '5px', width: '100%'}}>
<div style={{fontSize: '11px'}} className={styles.single_line_block}>{file.name}</div>
<div style={{
color: '#888888',
fontSize: '9px'
}}>{file.type.split("/")[1]}{file.size !== '' ? `${formatBytes(file.size)}` : ''}</div>
</div>
</div>
</div>
)
return (
<div id="resource-list">
{channel === 'output' && (!isAnyFileWithAgentId || files.length <= 0 ?
<div className="vertical_container">
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className="feed_title mt_8">No Output files!</span>
</div>
:
<div id="division_by_runs">
{filesByRun.map((filesRun, index) => (
<div key={filesByRun.length - index - 1}>
<div className="horizontal_container justify_space_between cursor_pointer padding_8_6"
onClick={() => setSelectedRunId(filesRun.run.id === selectedRunId ? null : filesRun.run.id)}>
<div className="horizontal_container">
<Image src={selectedRunId === filesRun.run.id ? "/images/arrow_downward_dropdown.svg" : "/images/arrow_forward.svg"}
alt="arrow" width={14} height={14}/>
<span className="text_12 ml_8 text_ellipsis mxw_360">{filesRun.run.name}</span>
<div className="resource_manager_tip ml_8"><Image src="/images/bolt.svg" alt="bolt" width={10} height={10}/> <span
className="text_9">Run {filesByRun.length - index}</span></div>
</div>
<Image src="/images/download.svg" alt="download_icon" width={16} height={16} onClick={() => downloadRunFiles(filesRun.run.id, filesRun.run.name)}/>
</div>
{selectedRunId === filesRun.run.id && (
<div className="horizontal_space_between flex_wrap padding_2_8">
{filesRun.files.map((file, index) => <File key={index} file={file}/>)}
</div>
)}
</div>
))}
</div>
)}
{channel === 'input' &&
<div className="horizontal_space_between flex_wrap">
{files.map((file, index) => <File key={index} file={file}/>)}
</div>}
</div>
)
}
+117
View File
@@ -0,0 +1,117 @@
import React, {useState, useRef, useEffect} from 'react';
import styles from './Agents.module.css';
import Image from "next/image";
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {getResources, uploadFile} from "@/pages/api/DashboardService";
import {downloadAllFiles} from "@/utils/utils";
import ResourceList from "@/pages/Content/Agents/ResourceList";
export default function ResourceManager({agentId, runs}) {
const [output, setOutput] = useState([]);
const [input, setInput] = useState([]);
const [channel, setChannel] = useState('input')
const [isDragging, setIsDragging] = useState(false);
const fileInputRef = useRef(null);
function handleFile(files) {
if (files.length > 0) {
const sizeInMB = files[0].size / (1024 * 1024);
if (sizeInMB > 5) {
toast.error('File size should not exceed 5MB', {autoClose: 1800});
} else {
const fileData = {
"file": files[0],
"name": files[0].name,
"size": files[0].size,
"type": files[0].type,
};
uploadResource(fileData);
}
}
};
const handleFileInputChange = (event) => {
const files = event.target.files;
handleFile(files);
};
const handleDropAreaClick = () => {
fileInputRef.current.click();
};
const handleDragEnter = (event) => {
event.preventDefault();
setIsDragging(true);
};
const handleDragLeave = () => {
setIsDragging(false);
};
const handleDragOver = (event) => {
event.preventDefault();
};
const handleDrop = (event) => {
event.preventDefault();
setIsDragging(false);
const files = event.dataTransfer.files;
handleFile(files);
};
useEffect(() => {
fetchResources();
}, [agentId]);
function uploadResource(fileData) {
const formData = new FormData();
formData.append('file', fileData.file);
formData.append('name', fileData.name);
formData.append('size', fileData.size);
formData.append('type', fileData.type);
uploadFile(agentId, formData)
.then((response) => {
fetchResources();
toast.success('Resource added successfully', {autoClose: 1800});
})
.catch((error) => {
toast.error(error, {autoClose: 1800});
console.error('Error uploading resource:', error);
});
}
function fetchResources() {
getResources(agentId)
.then((response) => {
const resources = response.data;
const inputFiles = resources.filter((resource) => resource.channel === 'INPUT');
const outputFiles = resources.filter((resource) => resource.channel === 'OUTPUT');
setInput(inputFiles);
setOutput(outputFiles);
})
.catch((error) => {
console.error('Error fetching resources:', error);
});
}
return (<>
<div className="detail_top mb_10">
<button onClick={() => setChannel('input')} className={channel === 'input' ? 'tab_button_selected' : 'tab_button'}>Input</button>
<button onClick={() => setChannel('output')} className={channel === 'output' ? 'tab_button_selected' : 'tab_button'}>Output</button>
</div>
<div className="w_100 mr_10 mb_20">
{channel === 'input' &&
<div className={`file-drop-area ${isDragging ? 'dragging' : ''}`} onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave} onDragOver={handleDragOver} onDrop={handleDrop}
onClick={handleDropAreaClick}>
<p className="text_14 text_align_center">+ Choose or drop a file here</p>
<p className="text_12 text_align_center">Supported file formats are txt, pdf,
docx, epub, csv, pptx only</p>
<input type="file" ref={fileInputRef} style={{display: 'none'}} onChange={handleFileInputChange}/>
</div>}
<ResourceList files={channel === 'output' ? output : input} channel={channel} runs={runs}/>
</div>
<ToastContainer/>
</>)
}
+60
View File
@@ -0,0 +1,60 @@
import React, {useEffect} from 'react';
import styles from './Agents.module.css';
import Image from "next/image";
import {formatNumber, formatTimeDifference} from "@/utils/utils";
import {EventBus} from "@/utils/eventBus";
export default function RunHistory({runs, setHistory, selectedRunId, setSelectedRun, setAgentExecutions}) {
useEffect(() => {
const resetRunStatus = (eventData) => {
const updatedExecutions = runs.map((run) => {
if (run.id === eventData.executionId) {
return {...run, status: eventData.status};
}
return run;
});
setAgentExecutions(updatedExecutions);
};
EventBus.on('resetRunStatus', resetRunStatus);
return () => {
EventBus.off('resetRunStatus', resetRunStatus);
};
});
return (<>
<div className="w_20 h_100">
<div className="detail_top mt_8 mb_8">
<div className="text_12 horizontal_container padding_0 gap_6">
<Image width={16} height={16} src="/images/update.svg" alt="update-icon"/>
<div className="color_white lh_16">Run history</div>
</div>
<Image className="cursor_pointer" onClick={() => setHistory(false)} width={28} height={28} src="/images/close_history.svg" alt="close-history-icon"/>
</div>
<div className="detail_body mb_20">
{runs && runs.map((run) => (<div key={run.id} onClick={() => setSelectedRun(run)} className={selectedRunId === run.id ? 'history_box_selected padding_10' : 'history_box padding_10'}>
<div className="horizontal_container mb_14">
{run.status === 'RUNNING' && <Image className="mix_blend_mode mr_7" width={14} height={14} src="/images/loading.gif" alt="loading-icon"/>}
<div className="text_ellipsis">{run.name}</div>
{/*{run.notification_count > 0 && <div className={styles.notification_bubble}>{run.notification_count}</div>}*/}
</div>
<div className="horizontal_container align_center">
<div className="horizontal_container w_fit_content">
<Image width={12} height={12} src="/images/calls_made.svg" alt="call-icon"/>
<div className="text_10 ml_4">{formatNumber(run?.num_of_calls || 0)} Calls</div>
</div>
<div className="horizontal_container ml_10 w_fit_content">
<Image width={12} height={12} src="/images/schedule.svg" alt="schedule-icon"/>
<div className="text_10 ml_4">
{formatTimeDifference(run.time_difference)}
</div>
</div>
</div>
</div>))}
</div>
</div>
</>)
}
+69
View File
@@ -0,0 +1,69 @@
import React, {useEffect, useState} from 'react';
import styles from './Agents.module.css';
import 'react-toastify/dist/ReactToastify.css';
import {getExecutionTasks} from '@/pages/api/DashboardService';
import Image from "next/image";
export default function TaskQueue({selectedRunId}) {
const [tasks, setTasks] = useState({pending: [], completed: []});
useEffect(() => {
fetchTasks();
}, [selectedRunId]);
function fetchTasks() {
getExecutionTasks(selectedRunId)
.then((response) => {
setTasks({
pending: response.data.tasks,
completed: response.data.completed_tasks,
});
})
.catch((error) => {
console.error('Error fetching execution feeds:', error);
});
}
return (
<>
{tasks.pending.length <= 0 && tasks.completed.length <= 0 ? <div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
marginTop: '40px',
width: '100%'
}}>
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className={styles.feed_title} style={{marginTop: '8px'}}>No Tasks found!</span>
</div> : <div>
{tasks.pending.length > 0 && <div className={styles.task_header}>Pending Tasks</div>}
{tasks.pending.map((task, index) => (
<div key={index} className={styles.history_box}
style={{background: '#272335', padding: '20px', cursor: 'default'}}>
<div style={{display: 'flex'}}>
<div>
<Image width={14} height={14} style={{mixBlendMode: 'exclusion'}} src="/images/loading.gif"
alt="loading-icon"/>
</div>
<div className={styles.feed_title}>
{task.name}
</div>
</div>
</div>
))}
{tasks.completed.length > 0 && <div className={styles.task_header}>Completed Tasks</div>}
{tasks.completed.map((task, index) => (
<div key={index} className={styles.history_box}
style={{background: '#272335', padding: '20px', cursor: 'default'}}>
<div style={{display: 'flex'}}>
<div className={styles.feed_title} style={{marginLeft: '0'}}>
{task.name}
</div>
</div>
</div>
))}
</div>}
</>
);
}
+255
View File
@@ -0,0 +1,255 @@
.rdt {
position: relative;
}
.rdtPicker {
display: none;
position: absolute;
min-width: 250px;
padding: 4px;
margin-top: 1px;
z-index: 99999 !important;
background: #1F1B32;
box-shadow: 0 1px 3px rgba(0,0,0,.1);
border-radius: 8px;
color:white;
}
.rdtOpen .rdtPicker {
display: block;
}
.rdtStatic .rdtPicker {
box-shadow: none;
position: static;
}
.rdtPicker .rdtTimeToggle {
text-align: center;
}
.rdtPicker table {
width: 100%;
margin: 0;
}
.rdtPicker td,
.rdtPicker th {
text-align: center;
height: 28px;
}
.rdtPicker td {
cursor: pointer;
}
.rdtPicker td.rdtDay:hover,
.rdtPicker td.rdtHour:hover,
.rdtPicker td.rdtMinute:hover,
.rdtPicker td.rdtSecond:hover,
.rdtPicker .rdtTimeToggle:hover {
background: grey;
cursor: pointer;
}
.rdtPicker td.rdtOld,
.rdtPicker td.rdtNew {
color: #999999;
}
.rdtPicker td.rdtToday {
position: relative;
}
.rdtPicker td.rdtToday:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-bottom: 7px solid #428bca;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
.rdtPicker td.rdtActive,
.rdtPicker td.rdtActive:hover {
background-color: #428bca;
color:#fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.rdtPicker td.rdtActive.rdtToday:before {
border-bottom-color: #fff;
}
.rdtPicker td.rdtDisabled,
.rdtPicker td.rdtDisabled:hover {
background: none;
color: #999999;
cursor: not-allowed;
}
.rdtPicker td span.rdtOld {
color: #999999;
}
.rdtPicker td span.rdtDisabled,
.rdtPicker td span.rdtDisabled:hover {
background: none;
color: #999999;
cursor: not-allowed;
}
.rdtPicker th {
border-bottom: 1px solid #f9f9f9;
}
.rdtPicker .dow {
width: 14.2857%;
border-bottom: none;
cursor: default;
}
.rdtPicker th.rdtSwitch {
width: 100px;
}
.rdtPicker th.rdtSwitch:hover {
background-color: grey;
}
.rdtPicker th.rdtNext,
.rdtPicker th.rdtPrev {
font-size: 21px;
vertical-align: top;
}
.rdtSwitch:hover{
background-color: grey !important;
}
.rdtPrev:hover{
background-color: grey !important;
}
.rdtNext:hover{
background-color: grey !important;
}
.rdtPrev span,
.rdtNext span {
display: block;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
}
.rdtPicker th.rdtDisabled,
.rdtPicker th.rdtDisabled:hover {
background: none;
color: #999999;
cursor: not-allowed;
}
.rdtPicker thead tr:first-of-type th {
cursor: pointer;
}
.rdtPicker thead tr:first-of-type th:hover {
background: #eeeeee;
}
.rdtPicker tfoot {
border-top: 1px solid #f9f9f9;
}
.rdtPicker button {
border: none;
background: none;
cursor: pointer;
}
.rdtPicker button:hover {
background-color: grey;
}
.rdtPicker thead button {
width: 100%;
height: 100%;
}
td.rdtMonth,
td.rdtYear {
height: 50px;
width: 25%;
cursor: pointer;
}
td.rdtMonth:hover,
td.rdtYear:hover {
background: grey;
}
.rdtCounters {
display: inline-block;
}
.rdtCounters > div {
float: left;
}
.rdtCounter {
height: 100px;
}
.rdtCounter {
width: 40px;
}
.rdtCounterSeparator {
line-height: 100px;
}
.rdtCounter .rdtBtn {
height: 40%;
line-height: 40px;
cursor: pointer;
display: block;
margin-top: 5px !important;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none;
}
.rdtCounter .rdtBtn:hover {
background: grey;
}
.rdtCounter .rdtCount {
height: 20%;
font-size: 1.2em;
}
.rdtMilli {
vertical-align: middle;
padding-left: 8px;
width: 48px;
}
.rdtMilli input {
width: 100%;
font-size: 1.2em;
margin-top: 37px;
}
.rdtTime td {
cursor: default;
}
@@ -0,0 +1,46 @@
import React, {useState, useEffect} from 'react';
import KnowledgeForm from "@/pages/Content/Knowledge/KnowledgeForm";
export default function AddKnowledge({internalId, sendKnowledgeData}) {
const [knowledgeName, setKnowledgeName] = useState('');
const [knowledgeDescription, setKnowledgeDescription] = useState('');
const [selectedIndex, setSelectedIndex] = useState(null);
useEffect(() => {
const knowledge_name = localStorage.getItem("knowledge_name_" + String(internalId))
if (knowledge_name) {
setKnowledgeName(knowledge_name);
}
const knowledge_description = localStorage.getItem("knowledge_description_" + String(internalId))
if (knowledge_description) {
setKnowledgeDescription(knowledge_description);
}
const knowledge_index = localStorage.getItem("knowledge_index_" + String(internalId))
if (knowledge_index) {
setSelectedIndex(JSON.parse(knowledge_index));
}
}, [internalId])
return (<>
<div className="row">
<div className="col-3"></div>
<div className="col-6 col-6-scrollable">
<KnowledgeForm internalId={internalId}
knowledgeId={null}
knowledgeName={knowledgeName}
setKnowledgeName={setKnowledgeName}
knowledgeDescription={knowledgeDescription}
setKnowledgeDescription={setKnowledgeDescription}
selectedIndex={selectedIndex}
setSelectedIndex={setSelectedIndex}
isEditing={false}
setIsEditing={null}
sendKnowledgeData={sendKnowledgeData}
/>
</div>
<div className="col-3"></div>
</div>
</>)
}
+54
View File
@@ -0,0 +1,54 @@
import React from 'react';
import Image from "next/image";
import styles from '../Toolkits/Tool.module.css';
import styles1 from '../Agents/Agents.module.css'
import {createInternalId, getUserClick} from "@/utils/utils";
export default function Knowledge({sendKnowledgeData, knowledge}) {
return (
<>
<div className="container">
<p className="text_14 mt_8 mb_12 ml_8">Knowledges</p>
<div className="w_100 mb_10">
<button className="secondary_button w_100" onClick={() => {sendKnowledgeData({
id: -6,
name: "new knowledge",
contentType: "Add_Knowledge",
internalId: createInternalId()
}); getUserClick('Knowledge Added', {})}}>
+ Add Knowledge
</button>
</div>
{knowledge && knowledge.length > 0 ? (
<div className="vertical_selection_scroll">
<div className={styles.tool_container}>
{knowledge.map((item, index) => (
<div key={index} className={styles.tool_box} onClick={() => sendKnowledgeData({
id: item.id,
name: item.name,
contentType: "Knowledge",
internalId: createInternalId()
})}>
<div className="row">
<div className="col-12">
<div
style={{display: 'flex', alignItems: 'center', justifyContent: 'flex-start', padding: '5px'}}>
<div style={{marginLeft: '8px'}}>
<div className={styles.tool_name}>{item.name}&nbsp;{item.is_marketplace &&
<Image width={13} height={13} src="/images/widgets.svg" alt="markteplace-icon"/>}</div>
<div className="item_publisher">by {item.contributed_by}</div>
</div>
</div>
</div>
</div>
</div>)
)}
</div>
</div>
) : (<div className="form_label mt_20 horizontal_container justify_center">No Knowledge found</div>
)}
</div>
</>
);
}
@@ -0,0 +1,81 @@
.knowledge_label {
margin-bottom: 4px;
font-size: 12px;
color: #888888;
}
.knowledge_info {
font-size: 12px;
color: white;
}
.knowledge_info_box {
margin-bottom: 20px;
}
.knowledge_wrapper {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
}
.knowledge_db {
font-size: 12px;
color: #888888;
font-weight: normal;
height: auto;
max-width: 240px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.knowledge_db_name {
padding:12px 14px;
border-top: 1px solid #888888;
}
.knowledge_alert {
border-radius: 8px;
background-color: #423D52;
border-left: 4px solid #B3B2BB;
color: white;
font-size: 12px;
padding: 12px 14px;
display: flex;
justify-content: flex-start;
}
.database_container {
background-color: rgb(39, 35, 53);
width: calc(33% - 10px);
padding: 10px;
color: white;
font-style: normal;
font-weight: 400;
font-size: 15px;
border-radius: 8px;
cursor: pointer;
text-align: center;
}
.database_wrapper {
display: flex;
justify-content: flex-start;
gap: 10px;
flex-wrap: wrap;
}
.installed_knowledge_card_class {
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.14);
display: flex;
padding: 4px 8px;
align-items: center;
gap: 6px;
}
.knowledge_options_dropdown{
right: 25px;
width: 165px;
}
@@ -0,0 +1,220 @@
import React, {useEffect, useState} from 'react';
import styles1 from './Knowledge.module.css'
import {ToastContainer, toast} from "react-toastify";
import styles from "@/pages/Content/Toolkits/Tool.module.css";
import Image from "next/image";
import KnowledgeForm from "@/pages/Content/Knowledge/KnowledgeForm";
import {deleteCustomKnowledge, deleteMarketplaceKnowledge, getKnowledgeDetails} from "@/pages/api/DashboardService";
import {removeTab, returnToolkitIcon, setLocalStorageValue} from "@/utils/utils";
import {EventBus} from "@/utils/eventBus";
import Metrics from "@/pages/Content/Toolkits/Metrics";
export default function KnowledgeDetails({internalId, knowledgeId}) {
const [showDescription, setShowDescription] = useState(false);
const [dropdown, setDropdown] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [knowledgeName, setKnowledgeName] = useState('');
const [knowledgeDescription, setKnowledgeDescription] = useState('');
const [installationType, setInstallationType] = useState('');
const [model, setModel] = useState('');
const [tokenizer, setTokenizer] = useState('');
const [chunkSize, setChunkSize] = useState('');
const [vectorDatabase, setVectorDatabase] = useState('');
const [knowledgeDatatype, setKnowledgeDatatype] = useState('');
const [textSplitters, setTextSplitters] = useState('');
const [chunkOverlap, setChunkOverlap] = useState('');
const [dimension, setDimension] = useState('');
const [vectorDBIndex, setVectorDBIndex] = useState('');
const [activeTab, setActiveTab] = useState('metrics');
const uninstallKnowledge = () => {
setDropdown(false);
if (installationType === 'Marketplace') {
deleteMarketplaceKnowledge(knowledgeName)
.then((response) => {
console.log(response)
toast.success("Knowledge uninstalled successfully", {autoClose: 1800});
removeTab(knowledgeId, knowledgeName, "Knowledge", internalId);
EventBus.emit('reFetchKnowledge', {});
})
.catch((error) => {
toast.error("Unable to uninstall knowledge", {autoClose: 1800});
console.error('Error uninstalling knowledge:', error);
});
} else {
deleteCustomKnowledge(knowledgeId)
.then((response) => {
toast.success("Knowledge uninstalled successfully", {autoClose: 1800});
removeTab(knowledgeId, knowledgeName, "Knowledge", internalId);
EventBus.emit('reFetchKnowledge', {});
})
.catch((error) => {
toast.error("Unable to uninstall knowledge", {autoClose: 1800});
console.error('Error uninstalling knowledge:', error);
});
}
}
const viewKnowledge = () => {
setDropdown(false);
}
const editKnowledge = () => {
setIsEditing(true);
setDropdown(false);
}
useEffect(() => {
if (knowledgeId) {
getKnowledgeDetails(knowledgeId)
.then((response) => {
const data = response.data || [];
setKnowledgeName(data.name);
setKnowledgeDescription(data.description);
setInstallationType(data.installation_type);
setModel(data.model);
setTokenizer(data.tokenizer);
setChunkSize(data.chunk_size);
setVectorDatabase(data.vector_database);
setKnowledgeDatatype(data.data_type);
setTextSplitters(data.text_splitter);
setChunkOverlap(data.chunk_overlap);
setDimension(data.dimensions);
setVectorDBIndex(data.vector_database_index);
})
.catch((error) => {
console.error('Error fetching knowledge details:', error);
});
}
}, [internalId]);
return (<>
<div className="row">
<div className="col-12 col-6-scrollable">
<div className="horizontal_container align_start mb_20">
<div className="vertical_containers text_align_left mr_10 w_97">
<div className="text_17">{knowledgeName}</div>
<div className="text_12" >
{knowledgeDescription}
</div>
</div>
<div className="w_3">
<button className="secondary_button padding_8 h_31p"
onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<Image width={14} height={14} src="/images/three_dots.svg" alt="run-icon"/>
</button>
{dropdown && <div onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<ul className={`${"dropdown_container"} ${styles1.knowledge_options_dropdown}`}>
{installationType !== 'Marketplace' &&
// <li className="dropdown_item" onClick={viewKnowledge}>View in marketplace</li> :
<li className="dropdown_item" onClick={editKnowledge}>Edit details</li>}
<li className="dropdown_item" onClick={uninstallKnowledge}>Uninstall knowledge</li>
</ul>
</div>}
</div>
</div>
<div className="horizontal_container mb_10 border_bottom_grey pd_bottom_5">
<div className={activeTab === 'metrics' ? 'tab_button_small_selected' : 'tab_button_small'}
onClick={() => setActiveTab('metrics')}>
<div className="text_12 color_white padding_8">Metrics</div>
</div>
<div className={activeTab === 'configuration' ? 'tab_button_small_selected' : 'tab_button_small'}
onClick={() => setActiveTab('configuration')}>
<div className="text_12 color_white padding_8">Configuration</div>
</div>
</div>
{activeTab === 'metrics' && <div>
<Metrics knowledgeName={knowledgeName} />
</div>}
{ activeTab === "configuration" && <div className="row">
<div className="col-3" />
<div className="col-6">
{isEditing ?
<KnowledgeForm internalId={internalId}
knowledgeId={knowledgeId}
knowledgeName={knowledgeName}
setKnowledgeName={setKnowledgeName}
knowledgeDescription={knowledgeDescription}
setKnowledgeDescription={setKnowledgeDescription}
selectedIndex={vectorDBIndex}
setSelectedIndex={setVectorDBIndex}
isEditing={true}
setIsEditing={setIsEditing}
sendKnowledgeData={null}
/> :
<div>
{installationType === 'Marketplace' && <div className={`${styles1.knowledge_wrapper} ${"col-6"} ${"w_100"}`}>
<div className="w_50">
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Installation Type</label>
<div className={styles1.knowledge_info}>{installationType}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Model</label>
<div className={styles1.knowledge_info}>{model}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Tokenizer</label>
<div className={styles1.knowledge_info}>{tokenizer}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Chunk Size</label>
<div className={styles1.knowledge_info}>{chunkSize}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Vector Database</label>
<div className={styles1.knowledge_info}>{vectorDatabase}</div>
</div>
</div>
<div style={{width: '50%'}}>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Knowledge datatype</label>
<div className={styles1.knowledge_info}>{knowledgeDatatype}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Text splitters</label>
<div className={styles1.knowledge_info}>{textSplitters}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Chunk overlap</label>
<div className={styles1.knowledge_info}>{chunkOverlap}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Dimension</label>
<div className={styles1.knowledge_info}>{dimension}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Vector database index</label>
<div className={styles1.knowledge_info}>{vectorDBIndex?.name || ''}</div>
</div>
</div>
</div>}
{installationType === 'Custom' && <div className={styles1.knowledge_wrapper}>
<div style={{width: '50%'}}>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Installation Type</label>
<div className={styles1.knowledge_info}>{installationType}</div>
</div>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Vector database index</label>
<div className={styles1.knowledge_info}>{vectorDBIndex?.name || ''}</div>
</div>
</div>
<div style={{width: '50%'}}>
<div className={styles1.knowledge_info_box}>
<label className={styles1.knowledge_label}>Vector Database</label>
<div className={styles1.knowledge_info}>{vectorDatabase}</div>
</div>
</div>
</div>}
</div>}
</div>
<div className="col-3" />
</div>}
</div>
</div>
<ToastContainer/>
</>);
}
@@ -0,0 +1,269 @@
import React, {useState, useEffect, useRef} from 'react';
import styles1 from '@/pages/Content/Knowledge/Knowledge.module.css'
import {removeTab, setLocalStorageValue, setLocalStorageArray, createInternalId, getUserClick} from "@/utils/utils";
import styles from "@/pages/Content/Agents/Agents.module.css";
import Image from "next/image";
import {ToastContainer, toast} from "react-toastify";
import {addUpdateKnowledge, getValidIndices} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
export default function KnowledgeForm({
internalId,
knowledgeId,
knowledgeName,
setKnowledgeName,
knowledgeDescription,
setKnowledgeDescription,
selectedIndex,
setSelectedIndex,
isEditing,
setIsEditing,
sendKnowledgeData
}) {
const [addClickable, setAddClickable] = useState(true);
const indexRef = useRef(null);
const [indexDropdown, setIndexDropdown] = useState(false);
const [pinconeIndices, setPineconeIndices] = useState([]);
const [qdrantIndices, setQdrantIndices] = useState([]);
const [weaviateIndices, setWeaviateIndices] = useState([]);
useEffect(() => {
getValidIndices()
.then((response) => {
const data = response.data || [];
if (data) {
setPineconeIndices(data.pinecone || []);
setQdrantIndices(data.qdrant || []);
setWeaviateIndices(data.weaviate || []);
}
})
.catch((error) => {
console.error('Error fetching indices:', error);
});
}, []);
useEffect(() => {
function handleClickOutside(event) {
if (indexRef.current && !indexRef.current.contains(event.target)) {
setIndexDropdown(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
const handleNameChange = (event) => {
setLocalStorageValue("knowledge_name_" + String(internalId), event.target.value, setKnowledgeName);
};
const handleDescriptionChange = (event) => {
setLocalStorageValue("knowledge_description_" + String(internalId), event.target.value, setKnowledgeDescription);
};
function validationCheck() {
let isValid = true;
if (knowledgeName.replace(/\s/g, '') === '') {
toast.error("Knowledge name can't be blank", {autoClose: 1800});
isValid = false;
}
if (!selectedIndex) {
toast.error("Please select an index", {autoClose: 1800});
isValid = false;
}
return isValid;
}
const handleAddKnowledge = () => {
if (!validationCheck()) {
return
}
const knowledgeData = {
"id": 0,
"name": knowledgeName,
"description": knowledgeDescription,
"index_id": selectedIndex.id
}
addUpdateKnowledge(knowledgeData)
.then((response) => {
toast.success("Knowledge added successfully", {autoClose: 1800});
sendKnowledgeData({
id: response.data.id,
name: knowledgeName,
contentType: "Knowledge",
internalId: createInternalId()
});
EventBus.emit('reFetchKnowledge', {});
getUserClick('Knowledge Added Successfully', {})
})
.catch((error) => {
toast.error("Unable to add knowledge", {autoClose: 1800});
console.error('Error deleting knowledge:', error);
});
setAddClickable(false);
}
const handleUpdateKnowledge = () => {
if (!validationCheck()) {
return
}
const knowledgeData = {
"id": knowledgeId,
"name": knowledgeName,
"description": knowledgeDescription,
"index_id": selectedIndex.id
}
addUpdateKnowledge(knowledgeData)
.then((response) => {
toast.success("Knowledge updated successfully", {autoClose: 1800});
EventBus.emit('reFetchKnowledge', {});
})
.catch((error) => {
toast.error("Unable to update knowledge", {autoClose: 1800});
console.error('Error deleting knowledge:', error);
});
setIsEditing(false);
setAddClickable(false);
}
const handleIndexSelect = (index) => {
if(index.is_valid_state) {
setLocalStorageArray("knowledge_index_" + String(internalId), index, setSelectedIndex);
setIndexDropdown(false);
}
else{
toast.error('Select valid index', {autoClose: 1800})
}
}
const checkIndexValidity = (validState) => {
let errorMessage = "";
let isValid = true;
if (!validState) {
isValid = false;
errorMessage = "The configured index is either empty or has marketplace knowledge";
}
return [isValid, errorMessage];
}
return (<>
<div>
<div className={styles.page_title}>{isEditing ? 'Edit knowledge' : 'Add a new knowledge'}</div>
</div>
<div style={{marginTop: '10px'}}>
<div className={styles1.knowledge_alert}>
<div style={{marginRight: '5px', marginLeft: '-5px'}}>
<Image width={20} height={20} src='/images/info.svg' alt="info-icon"/>
</div>
<div>
Currently we support Open AI text-embedding-ada-002 model knowledge only. Please make sure you add the same.
</div>
</div>
</div>
<div style={{marginTop: '10px'}}>
<div>
<label className={styles.form_label}>Knowledge name</label>
<input className="input_medium" type="text" value={knowledgeName} onChange={handleNameChange}/>
</div>
<div style={{marginTop: '15px'}}>
<label className={styles.form_label}>Description</label><br/>
<label className={styles.form_label}>This description will be passed to the agent as knowledge context.</label>
<textarea className="textarea_medium" rows={3} value={knowledgeDescription} onChange={handleDescriptionChange}/>
</div>
<div style={{marginTop: '15px'}}>
<label className={styles.form_label}>Collection/Index</label><br/>
<div className="dropdown_container_search" style={{width: '100%'}}>
<div className="custom_select_container" onClick={() => setIndexDropdown(!indexDropdown)}
style={{width: '100%', color: !selectedIndex ? '#888888' : ''}}>
{selectedIndex?.name || 'Select Index'}<Image width={20} height={21}
src={!indexDropdown ? '/images/dropdown_down.svg' : '/images/dropdown_up.svg'}
alt="expand-icon"/>
</div>
<div>
{indexDropdown && <div className="custom_select_options" ref={indexRef} style={{width: '100%'}}>
<div className={styles1.knowledge_label} style={{padding: '12px 14px', maxWidth: '100%'}}>Select an
existing vector database collection/index to install the knowledge
</div>
{pinconeIndices && pinconeIndices.length > 0 &&
<div className={styles1.knowledge_db} style={{maxWidth: '100%'}}>
<div className={styles1.knowledge_db_name}>Pinecone</div>
{pinconeIndices.map((index) => (<div key={index.id} className="custom_select_option index_options"
onClick={() => handleIndexSelect(index)}>
<div style={!checkIndexValidity(index.is_valid_state)[0] ? {
color: '#888888',
textDecoration: 'line-through',
pointerEvents : 'none',
} : {}}>{index.name}</div>
{!checkIndexValidity(index.is_valid_state)[0] &&
<div>
<Image width={15} height={15} src="/images/info.svg" alt="info-icon"
title={checkIndexValidity(index.is_valid_state)[1]}/>
</div>}
</div>))}
</div>}
{qdrantIndices && qdrantIndices.length > 0 &&
<div className={styles1.knowledge_db} style={{maxWidth: '100%'}}>
<div className={styles1.knowledge_db_name}>Qdrant</div>
{qdrantIndices.map((index) => (<div key={index.id} className="custom_select_option index_options"
onClick={() => handleIndexSelect(index)}>
<div style={!checkIndexValidity(index.is_valid_state)[0] ? {
color: '#888888',
textDecoration: 'line-through',
pointerEvents : 'none',
} : {}}>{index.name}</div>
{!checkIndexValidity(index.is_valid_state)[0] &&
<div>
<Image width={15} height={15} src="/images/info.svg" alt="info-icon"
title={checkIndexValidity(index.is_valid_state)[1]}/>
</div>}
</div>))}
</div>}
{weaviateIndices && weaviateIndices.length > 0 &&
<div className={styles1.knowledge_db} style={{maxWidth: '100%'}}>
<div className={styles1.knowledge_db_name}>Weaviate</div>
{weaviateIndices.map((index) => (<div key={index.id} className="custom_select_option index_options"
onClick={() => handleIndexSelect(index)}>
<div style={!checkIndexValidity(index.is_valid_state)[0] ? {
color: '#888888',
textDecoration: 'line-through',
pointerEvents : 'none',
} : {}}>{index.name}</div>
{!checkIndexValidity(index.is_valid_state)[0] &&
<div>
<Image width={15} height={15} src="/images/info.svg" alt="info-icon"
title={checkIndexValidity(index.is_valid_state)[1]}/>
</div>}
</div>))}
</div>}
</div>}
</div>
</div>
</div>
{isEditing ? <div style={{marginTop: '15px', display: 'flex', justifyContent: 'flex-end'}}>
<button style={{marginRight: '7px'}} className="secondary_button" onClick={() => setIsEditing(false)}>Cancel
</button>
<button disabled={!addClickable} className="primary_button" onClick={handleUpdateKnowledge}>Update Changes
</button>
</div> : <div style={{marginTop: '15px', display: 'flex', justifyContent: 'flex-end'}}>
<button style={{marginRight: '7px'}} className="secondary_button"
onClick={() => removeTab(-6, "new knowledge", "Add_Knowledge", internalId)}>Cancel
</button>
<button disabled={!addClickable} className="primary_button" onClick={handleAddKnowledge}>Add knowledge</button>
</div>}
</div>
<ToastContainer/>
</>)
}
@@ -0,0 +1,219 @@
import React, {useEffect, useState} from 'react';
import Image from "next/image";
import styles from '.././Toolkits/Tool.module.css';
import styles1 from '../Agents/Agents.module.css'
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import styles2 from "./Market.module.css"
import {fetchAgentTemplateConfig, installAgentTemplate} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import axios from 'axios';
import {getUserClick, loadingTextEffect} from "@/utils/utils";
export default function AgentTemplate({template, env}) {
const [tools, setTools] = useState([])
const [agentWorkflow, setAgentWorkflow] = useState('')
const [templateModel, setTemplateModel] = useState('')
const [rightPanel, setRightPanel] = useState('overview')
const [goals, setGoals] = useState([])
const [instructions, setInstructions] = useState([])
const [installed, setInstalled] = useState('')
const [constraints, setConstraints] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Template Details")
const [isInstalled, setIsInstalled] = useState(false)
useEffect(() => {
loadingTextEffect('Loading Template Details', setLoadingText, 500);
if (window.location.href.toLowerCase().includes('marketplace')) {
setInstalled('Sign in to install')
axios.get(`https://app.superagi.com/api/agent_templates/marketplace/template_details/${template.id}`)
.then((response) => {
const data = response.data || [];
setValues(data)
})
.catch((error) => {
console.error('Error fetching agent templates:', error);
});
} else {
setInstalled(template && template.is_installed ? 'Installed' : 'Install');
fetchAgentTemplateConfig(template.id)
.then((response) => {
const data = response.data || [];
setValues(data)
})
.catch((error) => {
console.error('Error fetching template details:', error);
});
}
}, []);
function setValues(data) {
setAgentWorkflow(data.agent_workflow_name)
setTemplateModel(data.configs.model.value)
setGoals(data.configs.goal.value)
setConstraints(data.configs.constraints.value)
setTools(data.configs.tools.value)
setInstructions(data.configs.instruction ? data.configs.instruction.value : null);
setIsLoading(false)
}
function handleInstallClick() {
setIsInstalled(true)
getUserClick("Agent Template Installed",{"Agent Template Name": template.name})
if (window.location.href.toLowerCase().includes('marketplace')) {
localStorage.setItem('agent_to_install', template.id);
if (env === 'PROD') {
window.open(`https://app.superagi.com/`, '_self');
} else {
window.location.href = '/';
}
return;
}
if (template && template.is_installed) {
toast.error("Template is already installed", {autoClose: 1800});
return;
}
installAgentTemplate(template.id)
.then((response) => {
toast.success("Template installed", {autoClose: 1800});
setInstalled('Installed');
})
.catch((error) => {
console.error('Error installing template:', error);
});
}
function handleBackClick() {
EventBus.emit('goToMarketplace', {});
}
return (
<>
<div>
{!isLoading ? <div className="row" style={{marginLeft: 'auto'}}>
<div className={styles2.back_button} style={{margin: '8px 0', padding: '2px'}}
onClick={() => handleBackClick()}>
<Image src="/images/arrow_back.svg" alt="back_button" width={14} height={12}/>
<span className={styles2.back_button_text}>Back</span>
</div>
<div className="col-3" style={{maxHeight: '84vh', overflowY: 'auto', padding: '0'}}>
<div className={styles2.left_container}>
<span className={styles2.top_heading}>{template.name}</span>
<span style={{fontSize: '12px', marginTop: '15px',}} className={styles.tool_publisher}>By SuperAGI <Image
width={14} height={14} src="/images/is_verified.svg" alt="is_verified"/>&nbsp;{'\u00B7'}&nbsp;<Image
width={14} height={14} src="/images/upload_icon.svg" alt="upload-icon"/></span>
{isInstalled || (template && template.is_installed) ? (
<button
className="primary_button"
style={{
marginTop: '15px',
width: '100%',
background: 'rgba(255, 255, 255, 0.14)',
color: '#FFFFFF'
}}
>
<Image width={14} height={14} src="/images/tick.svg" alt="tick-icon"/>&nbsp; Installed
</button>
) : (
<button
className="primary_button"
style={{
marginTop: '15px',
width: '100%',
background: '#FFF',
color: '#000'
}}
onClick={() => handleInstallClick()}
>
<Image width={14} height={14} src="/images/upload_icon_dark.svg" alt="upload-icon"/>&nbsp; Install
</button>
)}
<hr className={styles2.horizontal_line}/>
<span className={styles2.description_text}>{template.description}</span>
<hr className={styles2.horizontal_line}/>
<span style={{fontSize: '12px',}} className={styles.tool_publisher}>Tools</span>
<div className={styles1.agent_info_tools} style={{marginTop: '10px'}}>
{tools.map((tool, index) => (
<div key={index} className="tool_container" style={{marginTop: '0', marginBottom: '5px'}}>
<div className={styles1.tool_text}>{tool || ''}</div>
</div>))}
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Agent Workflow</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{agentWorkflow}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Model(s)</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateModel}</div>
</div>
<hr className={styles2.horizontal_line}/>
<span style={{fontSize: '12px',}} className={styles.tool_publisher}>Last updated</span>
<span className={styles2.description_text}>{template.updated_at}</span>
</div>
</div>
<div className="col-9" style={{paddingLeft: '8px'}}>
{/*<div className={styles2.left_container} style={{marginBottom:'5px'}}>*/}
{/* <div className="row">*/}
{/* <div className="col-4">*/}
{/* <button onClick={() => setRightPanel('overview')} className={styles2.tab_button} style={rightPanel === 'overview' ? {background:'#454254',paddingRight:'15px'} : {background:'transparent',paddingRight:'15px'}}>*/}
{/* &nbsp;Overview*/}
{/* </button>*/}
{/* <button onClick={() => setRightPanel('tool_view')} className={styles2.tab_button} style={rightPanel === 'tool_view' ? {background:'#454254',paddingRight:'15px'} : {background:'transparent',paddingRight:'15px'}}>*/}
{/* &nbsp;Toolkits Included*/}
{/* </button>*/}
{/* </div>*/}
{/* </div>*/}
{/*</div>*/}
<div style={{maxHeight: '84vh', overflowY: 'auto'}}>
<div className={styles2.left_container} style={{marginBottom: '8px'}}>
<div>
<span className={styles2.description_heading}
style={{fontWeight: '400'}}>{goals.length}&nbsp;Goals</span><br/><br/>
{goals.map((goal, index) => (<div key={index} style={{marginTop: '0'}}>
<div className={styles2.description_text}>{index + 1}. {goal || ''}</div>
{index !== goals.length - 1}
</div>))}
</div>
</div>
{instructions && instructions.length > 0 &&
<div className={styles2.left_container} style={{marginBottom: '8px'}}>
<div>
<span className={styles2.description_heading}
style={{fontWeight: '400'}}>{instructions.length} Instructions</span><br/><br/>
{instructions.map((instruction, index) => (
<div key={index} style={{marginTop: '0'}}>
<div className={styles2.description_text}>{index + 1}. {instruction || ''}</div>
</div>))}
</div>
</div>}
<div className={styles2.left_container} style={{marginBottom: '8px'}}>
<div>
<span className={styles2.description_heading}
style={{fontWeight: '400'}}>{constraints.length}&nbsp;Constraints</span><br/><br/>
{constraints.map((goal, index) => (<div key={index} style={{marginTop: '0'}}>
<div className={styles2.description_text}>{index + 1}. {goal || ''}</div>
{index !== constraints.length - 1}
</div>))}
</div>
</div>
</div>
</div>
</div> : <div style={{display: 'flex', justifyContent: 'center', alignItems: 'center', height: '75vh'}}>
<div className="signInInfo" style={{fontSize: '16px', fontFamily: 'Source Code Pro'}}>{loadingText}</div>
</div>}
</div>
<ToastContainer/>
</>
);
}
@@ -0,0 +1,363 @@
import React, {useEffect, useRef, useState} from 'react';
import Image from "next/image";
import styles from '.././Toolkits/Tool.module.css';
import styles1 from '../Agents/Agents.module.css';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import styles2 from "./Market.module.css"
import styles3 from "../Knowledge/Knowledge.module.css"
import {EventBus} from "@/utils/eventBus";
import axios from 'axios';
import {
deleteMarketplaceKnowledge,
fetchKnowledgeTemplateOverview,
getValidMarketplaceIndices,
installKnowledgeTemplate
} from "@/pages/api/DashboardService";
import {loadingTextEffect} from "@/utils/utils";
export default function KnowledgeTemplate({template, env}) {
const [installed, setInstalled] = useState('');
const [dropdown, setDropdown] = useState(false);
const [templateData, setTemplateData] = useState([]);
const [markdownContent, setMarkdownContent] = useState('');
const indexRef = useRef(null);
const [indexDropdown, setIndexDropdown] = useState(false);
const [pinconeIndices, setPineconeIndices] = useState([]);
const [qdrantIndices, setQdrantIndices] = useState([]);
const [weaviateIndices, setWeaviateIndices] = useState([]);
useEffect(() => {
getValidMarketplaceIndices(template.name)
.then((response) => {
const data = response.data || [];
if (data) {
setPineconeIndices(data.pinecone || []);
setQdrantIndices(data.qdrant || []);
setWeaviateIndices(data.weaviate || [])
}
})
.catch((error) => {
console.error('Error fetching indices:', error);
});
}, []);
useEffect(() => {
function handleClickOutside(event) {
if (indexRef.current && !indexRef.current.contains(event.target)) {
setIndexDropdown(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
useEffect(() => {
if (template) {
setInstalled(template.is_installed ? 'Installed' : 'Install');
}
if (window.location.href.toLowerCase().includes('marketplace')) {
setInstalled('Sign in to install');
axios.get(`https://app.superagi.com/api/knowledges/marketplace/get/details/${template.name}`)
.then((response) => {
const data = response.data || [];
setTemplateData(data);
if (data) {
setMarkdownContent(data.readme);
}
})
.catch((error) => {
console.error('Error fetching template details:', error);
});
} else {
fetchKnowledgeTemplateOverview(template.name)
.then((response) => {
const data = response.data || [];
setTemplateData(data);
if (data) {
setMarkdownContent(data.readme);
}
})
.catch((error) => {
console.error('Error fetching template details:', error);
});
}
}, []);
const handleInstallClick = (index) => {
const indexId = index.id
if(!index.is_valid_state){
toast.error("Select valid index", {autoClose : 1800})
return
}
if (template && template.is_installed) {
toast.error("Template is already installed", {autoClose: 1800});
return;
}
setInstalled("Installing");
if (window.location.href.toLowerCase().includes('marketplace')) {
localStorage.setItem('knowledge_to_install', template.name);
localStorage.setItem('knowledge_index_to_install', indexId);
if (env === 'PROD') {
window.open(`https://app.superagi.com/`, '_self');
} else {
window.location.href = '/';
}
return;
}
setIndexDropdown(false);
installKnowledgeTemplate(template.name, indexId)
.then((response) => {
toast.success("Knowledge installed", {autoClose: 1800});
setInstalled('Installed');
EventBus.emit('reFetchKnowledge', {});
})
.catch((error) => {
toast.error("Error installing Knowledge: ", {autoClose: 1800});
console.error('Error installing Knowledge:', error);
setInstalled('Install');
});
}
function handleBackClick() {
EventBus.emit('goToMarketplace', {});
}
const uninstallKnowledge = () => {
deleteMarketplaceKnowledge(template.name)
.then((response) => {
console.log(response)
toast.success("Knowledge uninstalled successfully", {autoClose: 1800});
handleBackClick()
})
.catch((error) => {
toast.error("Unable to uninstall knowledge", {autoClose: 1800});
console.error('Error uninstalling knowledge:', error);
});
}
const checkIndexValidity = (validState, validDimension) => {
let errorMessage = "";
let isValid = true;
if (!validState && validDimension) {
isValid = false;
errorMessage = "The configured index already consists of custom knowledge";
} else if ((!validState && !validDimension) || (validState && !validDimension)) {
isValid = false;
errorMessage = "The dimension of the configured index does not match the dimensions of the selected knowledge";
}
return [isValid, errorMessage];
}
const installClicked = () => {
setIndexDropdown(!indexDropdown)
if (window.location.href.toLowerCase().includes('marketplace')) {
if (env === 'PROD') {
window.open(`https://app.superagi.com/`, '_self');
} else {
window.location.href = '/';
}
return;
}
}
return (
<>
<div>
<div className="row" style={{marginLeft: 'auto'}}>
<div className={styles2.back_button} style={{margin: '8px 0', padding: '2px'}}
onClick={() => handleBackClick()}>
<Image src="/images/arrow_back.svg" alt="back_button" width={14} height={12}/>
<span className={styles2.back_button_text}>Back</span>
</div>
<div className="col-3" style={{maxHeight: '84vh', overflowY: 'auto', padding: '0'}}>
<div className={styles2.left_container}>
<span className={styles2.top_heading}>{templateData?.name}</span>
<span style={{fontSize: '12px', marginTop: '15px',}}
className={styles.tool_publisher}>by {templateData?.contributed_by}&nbsp;{'\u00B7'}&nbsp;<Image
width={14} height={14} src="/images/upload_icon.svg"
alt="upload-icon"
style={{marginBottom: '1px'}}/>&nbsp;{'\u00B7'}&nbsp;{templateData?.install_number || 0}</span>
{!template?.is_installed && <div className="dropdown_container_search" style={{width: '100%'}}>
<div className="primary_button" onClick={installClicked}
style={{marginTop: '15px', cursor: 'pointer', width: '100%'}}>
<Image width={14} height={14} src="/images/upload_icon_dark.svg" alt="upload-icon"/>&nbsp;
<span>{installed}</span>{installed === 'Installing' && <span className="loader ml_10"></span>}
</div>
<div>
{indexDropdown && installed === 'Install' &&
<div className="custom_select_options" ref={indexRef} style={{width: '100%', maxHeight: '500px'}}>
<div className={styles3.knowledge_label} style={{padding: '12px 14px', maxWidth: '100%'}}>Select
an existing vector database collection/index to install the knowledge
</div>
{pinconeIndices && pinconeIndices.length > 0 &&
<div className={styles3.knowledge_db} style={{maxWidth: '100%'}}>
<div className={styles3.knowledge_db_name}>Pinecone</div>
{pinconeIndices.map((index) => (<div key={index.id} className="custom_select_option"
onClick={() => handleInstallClick(index)} style={{
padding: '12px 14px',
maxWidth: '100%',
display: 'flex',
justifyContent: 'space-between'
}}>
<div style={!checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[0] ? {
color: '#888888',
textDecoration: 'line-through',
pointerEvents : 'none',
} : {}}>{index.name}</div>
{!checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[0] &&
<div>
<Image width={15} height={15} src="/images/info.svg" alt="info-icon"
title={checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[1]}/>
</div>}
</div>))}
</div>}
{qdrantIndices && qdrantIndices.length > 0 &&
<div className={styles3.knowledge_db} style={{maxWidth: '100%'}}>
<div className={styles3.knowledge_db_name}>Qdrant</div>
{qdrantIndices.map((index) => (<div key={index.id} className="custom_select_option"
onClick={() => handleInstallClick(index)} style={{
padding: '12px 14px',
maxWidth: '100%',
display: 'flex',
justifyContent: 'space-between'
}}>
<div style={!checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[0] ? {
color: '#888888',
textDecoration: 'line-through',
pointerEvents : 'none',
} : {}}>{index.name}</div>
{!checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[0] &&
<div>
<Image width={15} height={15} src="/images/info.svg" alt="info-icon"
title={checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[1]}/>
</div>}
</div>))}
</div>}
{weaviateIndices && weaviateIndices.length > 0 &&
<div className={styles3.knowledge_db} style={{maxWidth: '100%'}}>
<div className={styles3.knowledge_db_name}>Weaviate</div>
{weaviateIndices.map((index) => (<div key={index.id} className="custom_select_option"
onClick={() => handleInstallClick(index)} style={{
padding: '12px 14px',
maxWidth: '100%',
display: 'flex',
justifyContent: 'space-between'
}}>
<div style={!checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[0] ? {
color: '#888888',
textDecoration: 'line-through',
pointerEvents : 'none',
} : {}}>{index.name}</div>
{!checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[0] &&
<div>
<Image width={15} height={15} src="/images/info.svg" alt="info-icon"
title={checkIndexValidity(index.is_valid_state, index.is_valid_dimension)[1]}/>
</div>}
</div>))}
</div>}
</div>}
</div>
</div>}
{template?.is_installed &&
<div style={{width: '100%', display: 'flex', justifyContent: 'flex-start', marginTop: '15px'}}>
<div className="secondary_button" style={{cursor: 'default', width: '85%'}}>
<Image width={14} height={14} src="/images/tick.svg" alt="tick-icon"/>&nbsp;{installed}
</div>
<div style={{width: '5%', marginLeft: '10px'}}>
<button className="secondary_button" style={{padding: '8px', height: '31px'}}
onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<Image width={14} height={14} src="/images/three_dots.svg" alt="run-icon"/>
</button>
{dropdown && <div onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<ul className="dropdown_container" style={{marginTop: '0', width: '165px'}}>
<li className="dropdown_item" onClick={uninstallKnowledge}>Uninstall knowledge</li>
</ul>
</div>}
</div>
</div>}
<hr className={styles2.horizontal_line}/>
<span className={styles2.description_text}>{templateData?.description}</span>
<hr className={styles2.horizontal_line}/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Model</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.model}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Knowledge datatype</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.data_type}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Tokenizer</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.tokenizer}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Chunk size</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.chunk_size}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Chunk overlap</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.chunk_overlap}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Text splitter</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.text_splitter}</div>
</div>
<br/>
<span style={{fontSize: '12px'}} className={styles.tool_publisher}>Dimensions</span>
<div className="tool_container" style={{marginTop: '10px', width: 'fit-content'}}>
<div className={styles1.tool_text}>{templateData?.dimensions}</div>
</div>
<hr className={styles2.horizontal_line}/>
<span style={{fontSize: '12px',}} className={styles.tool_publisher}>Last updated</span>
<span className={styles2.description_text}>{templateData?.updated_at}</span>
</div>
</div>
<div className="col-9" style={{paddingLeft: '8px'}}>
<div style={{overflowY: 'scroll', height: '84vh'}}>
<div className={styles2.left_container}
style={{marginBottom: '5px', color: 'white', padding: '16px'}}>
<span className="text_20_bold">Overview</span><br/>
{/*{templateData?.overview.map((item, index) => (<div key={index} style={{marginTop: '0'}}>*/}
{/* <div className={styles2.description_text}>{index + 1}. {item || ''}</div>*/}
{/* {index !== item.length - 1}*/}
{/*</div>))}*/}
<span className={styles2.sub_text}>{templateData?.overview}</span>
</div>
</div>
</div>
</div>
</div>
<ToastContainer/>
</>
);
}
+119
View File
@@ -0,0 +1,119 @@
import React, {useState, useEffect} from 'react';
import Image from "next/image";
import styles from './Market.module.css';
import MarketKnowledge from './MarketKnowledge';
import MarketAgent from './MarketAgent';
import MarketTools from './MarketTools';
import MarketModels from '../Models/MarketModels';
import ToolkitTemplate from './ToolkitTemplate';
import ModelTemplate from "../Models/ModelTemplate";
import {EventBus} from "@/utils/eventBus";
import AgentTemplate from "./AgentTemplate";
import KnowledgeTemplate from "./KnowledgeTemplate";
import {setLocalStorageValue, setLocalStorageArray} from "@/utils/utils";
export default function Market({env, getModels, sendModelData}) {
const [activeTab, setActiveTab] = useState('market_tools');
const [itemClicked, setItemClicked] = useState(false);
const [templateData, setTemplateData] = useState([]);
const [detailType, setDetailType] = useState('');
useEffect(() => {
const marketplace_tab = localStorage.getItem('marketplace_tab');
if (marketplace_tab) {
setActiveTab(marketplace_tab);
}
const item_clicked = localStorage.getItem('market_item_clicked');
const detail_type = localStorage.getItem('market_detail_type');
const market_item = localStorage.getItem('market_item');
if (item_clicked) {
setItemClicked(JSON.parse(item_clicked));
if (detail_type) {
setDetailType(item_clicked === 'true' ? detail_type : '');
setTemplateData(item_clicked === 'true' ? JSON.parse(market_item) : []);
}
}
EventBus.on('openTemplateDetails', handleOpenTemplateDetails);
EventBus.on('goToMarketplace', handleBackClick);
return () => {
EventBus.off('openTemplateDetails', handleOpenTemplateDetails);
EventBus.off('goToMarketplace', handleBackClick);
};
}, []);
const switchTab = (tab) => {
setActiveTab(tab);
localStorage.setItem('marketplace_tab', tab);
};
const tabData = [
{ id: 'market_tools', label: 'Tools', image: '/images/tools_light.svg' },
{ id: 'market_knowledge', label: 'Knowledge', image: '/images/knowledge.svg' },
{ id: 'market_agents', label: 'Agent Templates', image: '/images/agents_light.svg' },
{ id: 'market_models', label: 'Models', image: '/images/models.svg' },
];
const renderTab = (tab) => {
if(tab.id === 'market_models' && !(window.location.href.toLowerCase().includes('marketplace')))
return
return (
<button
key={tab.id}
onClick={() => switchTab(tab.id)}
className={activeTab === tab.id ? 'tab_button_selected' : 'tab_button'}
>
<Image width={14} height={14} src={tab.image} alt={`${tab.label}-icon`} />
<span>{tab.label}</span>
</button>
);
};
const handleOpenTemplateDetails = ({ item, contentType }) => {
localStorage.setItem('market_detail_type', contentType);
setDetailType(contentType);
localStorage.setItem('market_item', JSON.stringify(item));
setTemplateData(item);
localStorage.setItem('market_item_clicked', true);
setItemClicked(true);
};
const handleBackClick = () => {
localStorage.setItem('market_item_clicked', false);
setItemClicked(false);
};
return (
<div>
{!itemClicked ? (
<div className={styles.empty_state}>
<div style={{ width: '100%', display: 'flex', flexDirection: 'column' }}>
<div className={styles.detail_top}>
<div style={{ display: 'flex', overflowX: 'scroll', marginLeft: '8px' }}>
{tabData.map(renderTab)}
</div>
</div>
<div>
{activeTab === 'market_tools' && <MarketTools />}
{activeTab === 'market_knowledge' && <MarketKnowledge />}
{activeTab === 'market_agents' && <MarketAgent />}
{activeTab === 'market_models' && <MarketModels />}
</div>
</div>
</div>
) : (
<div style={{padding: '0 3px'}}>
{detailType === 'agent_template' && <AgentTemplate env={env} template={templateData}/>}
{detailType === 'knowledge_template' && <KnowledgeTemplate env={env} template={templateData}/>}
{detailType === 'tool_template' && <ToolkitTemplate env={env} template={templateData}/>}
{detailType === 'model_template' && <ModelTemplate env={env} template={templateData} getModels={getModels} sendModelData={sendModelData} />}
</div>
)}
</div>
);
}
@@ -0,0 +1,552 @@
.container {
height: 100%;
width: 100%;
padding: 0 8px;
}
.title_box {
width: 100%;
padding: 8px;
display: flex;
align-items: center;
}
.title_text {
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 17px;
display: flex;
align-items: center;
color: white;
}
.wrapper {
margin-bottom: 5px;
width: 100%;
}
.agent_box {
width: 120px;
justify-content: center;
display: flex;
float: left;
flex-direction: row;
align-items: center;
padding: 3px 3px;
gap: 6px;
border-radius: 8px;
flex: none;
order: 0;
flex-grow: 0;
cursor: pointer;
margin-right: 10px;
}
.agent_box:hover {
background-color: #494856;
}
.agent_active {
display: flex;
flex-direction: row;
align-items: center;
padding: 0;
}
.agent_text {
font-style: normal;
font-weight: 400;
font-size: 13px;
line-height: 15px;
align-items: center;
color: white;
flex: none;
order: 1;
flex-grow: 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.text_block {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.form_label {
font-size: 13px;
margin-bottom: 4px;
font-weight: 500;
color: #888888;
line-height: 17px;
}
.page_title {
text-align: left;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 17px;
display: flex;
align-items: center;
color: white;
}
.tool_text {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.detail_top {
width: 100%;
height: 50px;
display: flex;
align-items: center;
margin-bottom: 10px;
justify-content: space-between;
}
.detail_body {
width: 100%;
padding-right: 10px;
}
.detail_content {
height: calc(100vh - 140px);
border-radius: 8px;
overflow-y: scroll;
padding-bottom: 0;
}
.tab_button {
border: none;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px;
}
.tab_text {
font-style: normal;
font-weight: 400;
font-size: 11px;
line-height: 15px;
color: #FFFFFF;
padding: 8px;
display: flex;
align-items: center;
}
.tab_button:hover {
background: #454254;
}
.run_button {
background: #62A168;
border: none;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px 8px 5px;
}
.run_button:hover {
background: #57825b;
}
.pause_button {
background: #F78166;
border: none;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px;
}
.pause_button:hover {
background: #C95034;
}
.history_box {
background-color: rgb(39, 35, 53);
width: 100%;
padding: 10px;
color: white;
font-style: normal;
font-weight: 400;
font-size: 15px;
line-height: 120px;
border-radius: 8px;
cursor: pointer;
margin-bottom: 7px;
text-align: center;
}
.notification_bubble {
width: 14px;
height: 14px;
background: #DC6261;
border-radius: 200px;
display: flex;
align-items: center;
text-align: center;
padding: 4px;
font-size: 9px;
order: 1;
}
.history_info {
font-style: normal;
font-weight: 400;
font-size: 10px;
line-height: 12px;
color: #888888;
margin-left: 4px;
margin-top: 3px;
}
.feed_title {
font-family: 'Source Code Pro';
margin-left: 10px;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: white;
white-space: pre-line;
}
.feed_icon {
font-size: 20px;
margin-top: 5px;
}
.custom_task_box {
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 14px;
color: white;
width: 100%;
border-radius: 8px;
margin-bottom: 5px;
padding: 15px 20px;
}
.console_icons {
margin: -3px 3px 0 0;
}
.detail_name {
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 19px;
color: #FFFFFF;
margin-bottom: 10px;
}
.separator {
height: 15px;
margin-bottom: 15px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.agent_info_box {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 10px;
}
.agent_info_tools {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
}
.resources {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
gap: 6px;
}
.agent_resources {
width: 100%;
margin-top: 10px;
}
.large_text_box {
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
}
.show_more_button {
margin-top: 10px;
cursor: pointer;
width: fit-content;
color: #888888;
}
.single_line_block {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 80%;
}
.three_dots {
margin-left: 5px;
background: transparent;
border: none;
border-radius: 8px;
}
.more_details {
display: flex;
align-items: center;
margin-right: 30px;
}
.more_details_wrapper {
display: flex;
align-items: center;
margin-top: 20px;
justify-content: flex-start;
}
.empty_state {
width: 100%;
height: 100%;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: space-between;
}
.main_workspace {
height: 100%;
padding: 0 10px 10px 0;
}
.featured_text {
color: white;
}
.search_box {
}
.search_box input {
width: 160px;
height: 25px;
font-size: x-small;
padding: 5px;
border: 1px solid rgb(96, 96, 96);
border-radius: 6px;
background-color: #454254;
}
.market_tool {
display: flex;
height: 105px;
color: white;
font-size: small;
padding: 12px;
width: 33% !important;
background-color: rgb(39, 35, 53);
border-radius: 8px;
flex-direction: column;
}
.tool_description {
line-height:16px;
margin-top: 5px;
color: #888888;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.rowContainer {
height: 100%;
}
.top_heading {
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 24px;
color: #FFFFFF;
}
.description_text {
font-size: 12px;
line-height: 14px;
color: #FFFFFF;
margin-bottom: 16px;
}
.left_container {
padding: 20px;
background: rgba(0, 0, 0, 0.2);
border-radius: 6px;
margin-bottom: 4vh;
}
.description_heading {
font-size: 20px;
color: #FFFFFF;
font-weight: 600;
}
.back_button {
font-weight: 500;
font-size: 12px;
color: #888888;
cursor: pointer;
margin-bottom: 8px;
}
.sub_text {
font-weight: 400;
font-size: 12px;
color: #888888;
}
.vertical_line{
width: 0;
height: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
flex: none;
margin-left:8px;
}
.topbar_heading{
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 18px;
color: #FFFFFF;
margin-left:8px;
pointer-events: none;
}
.empty_templates {
display: flex;
margin-top: 60px;
justify-content: center;
}
.horizontal_line {
margin: 20px 0 20px -20px;
border: 1px solid #ffffff20;
width: calc(100% + 40px);
display: flex;
height: 0;
}
.back_button_text {
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 14px;
color: #888888;
margin-left: 4px;
}
.marketplace_public_button{
display: flex;
justify-content: flex-end;
align-items: center;
width:100%;
padding-right:8px;
}
.marketplace_public_content{
height:92.5vh;
width:99vw;
background: rgba(255, 255, 255, 0.08) ;
margin-left:8px;
border-radius: 8px
}
.marketplace_public_container{
height:6.5vh;
display:flex;
width:100%;
}
.markdown_style{
color:white;
}
.markdown_style img {
max-width: 100%;
height: auto;
}
.markdown_container{
height:68vh;
overflow-y:scroll;
overflow-x:hidden;
}
.settings_tab_button_clicked{
background: #454254;
padding-right: 15px
}
.settings_tab_button{
background: transparent;
padding-right: 15px
}
.settings_tab_img{
margin-top: -1px;
}
.checkboxGroup {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
height: 15vh;
}
.checkboxLabel {
display: flex;
align-items: center;
width: 15vw;
cursor:pointer
}
.checkboxText {
font-weight: 400;
font-size: 12px;
color: #FFF;
margin-left:5px;
}
@@ -0,0 +1,71 @@
import React, {useEffect, useState} from "react";
import Image from "next/image";
import {fetchAgentTemplateList} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import {loadingTextEffect, getUserClick} from "@/utils/utils";
import axios from 'axios';
export default function MarketAgent() {
const [agentTemplates, setAgentTemplates] = useState([])
const [showMarketplace, setShowMarketplace] = useState(false);
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Agent Templates");
useEffect(() => {
loadingTextEffect('Loading Agent Templates', setLoadingText, 500);
if (window.location.href.toLowerCase().includes('marketplace')) {
setShowMarketplace(true);
axios.get('https://app.superagi.com/api/agent_templates/marketplace/list')
.then((response) => {
const data = response.data || [];
setAgentTemplates(data);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching agent templates:', error);
});
} else {
fetchAgentTemplateList()
.then((response) => {
const data = response.data || [];
setAgentTemplates(data);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching agent templates:', error);
});
}
}, []);
function handleTemplateClick(item) {
getUserClick("Marketplace Agent Template Viewed", {"Agent Template Name": item.name})
const contentType = 'agent_template';
EventBus.emit('openTemplateDetails', {item, contentType});
}
return (
<div className={showMarketplace ? 'ml_8' : 'ml_3'}>
<div className="w_100 overflowY_auto mxh_78vh">
{!isLoading ? <div>
{agentTemplates.length > 0 ? <div className="marketplaceGrid3">{agentTemplates.map((item, index) => (
<div className="market_containers cursor_pointer" key={item.id} onClick={() => handleTemplateClick(item)}>
<div className="vertical_containers overflow_auto">
<div>{item.name}</div>
<div className="color_gray lh_16">by SuperAgi&nbsp;<Image width={14} height={14}
src="/images/is_verified.svg"
alt="is_verified"/></div>
<div className="text_ellipsis mt_8 color_gray">{item.description}</div>
</div>
</div>
))}</div> : <div className="center_container mt_40">
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className="feed_title mt_8">No Agent Templates found!</span>
</div>}
</div> : <div className="horizontal_container_center h_75vh">
<div className="signInInfo text_16 ff_sourceCode">{loadingText}</div>
</div>}
</div>
</div>
)
};
@@ -0,0 +1,83 @@
import React, {useEffect, useState} from "react";
import Image from "next/image";
import styles from './Market.module.css';
import styles1 from '../Knowledge/Knowledge.module.css';
import {EventBus} from "@/utils/eventBus";
import {loadingTextEffect} from "@/utils/utils";
import axios from 'axios';
import {fetchKnowledgeTemplateList} from "@/pages/api/DashboardService";
export default function MarketKnowledge() {
const [knowledgeTemplates, setKnowledgeTemplates] = useState([])
const [showMarketplace, setShowMarketplace] = useState(false);
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Knowledge Templates");
useEffect(() => {
loadingTextEffect('Loading Knowledge Templates', setLoadingText, 500);
if (window.location.href.toLowerCase().includes('marketplace')) {
setShowMarketplace(true);
axios.get(`https://app.superagi.com/api/knowledges/marketplace/list/0`)
.then((response) => {
const data = response.data || [];
setKnowledgeTemplates(data);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching knowledge templates:', error);
});
} else {
fetchKnowledgeTemplateList()
.then((response) => {
const data = response.data || [];
setKnowledgeTemplates(data);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching knowledge templates:', error);
});
}
}, []);
function handleTemplateClick(item) {
const contentType = 'knowledge_template';
EventBus.emit('openTemplateDetails', {item, contentType});
}
return (
<div className={showMarketplace ? 'ml_8' : 'ml_3'}>
<div className="w_100 overflowY_auto mxh_78vh">
{!isLoading ? <div>
{knowledgeTemplates.length > 0 ? <div className="marketplaceGrid3">{knowledgeTemplates.map((item, index) => (
<div className="market_containers cursor_pointer" key={item.id} style={{cursor: 'pointer', display: 'block'}}
onClick={() => handleTemplateClick(item)}>
<div style={{display: 'inline', overflow: 'auto'}}>
<div className="horizontal_space_between">
<span>{item.name}</span>
{item.is_installed &&
<div className={styles1.installed_knowledge_card_class}>{'\u2713'}&nbsp;Installed</div>}
</div>
<div style={{
color: '#888888',
lineHeight: '16px',
display: 'flex',
alignItems: 'center',
marginTop: item.is_installed ? '-2.5%' : ''
}}>by {item.contributed_by}&nbsp;{'\u00B7'}&nbsp;<Image
width={14} height={14} src="/images/upload_icon.svg" alt="upload-icon"/>&nbsp;{item.install_number}
</div>
<div className="text_ellipsis mt_6 color_gray">{item.description}</div>
</div>
</div>
))}</div> : <div className="center_container mt_40">
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className="feed_title mt_8" style={{marginTop: '8px'}}>No Knowledge found!</span>
</div>}
</div> : <div className="horizontal_container_center h_75vh">
<div className="signInInfo text_16 ff_sourceCode">{loadingText}</div>
</div>}
</div>
</div>
)
}
@@ -0,0 +1,73 @@
import React, {useEffect, useState} from "react";
import Image from "next/image";
import {fetchToolTemplateList} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import {loadingTextEffect, excludedToolkits, returnToolkitIcon} from "@/utils/utils";
import axios from 'axios';
export default function MarketTools() {
const [toolTemplates, setToolTemplates] = useState([])
const [showMarketplace, setShowMarketplace] = useState(false);
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Toolkits");
useEffect(() => {
loadingTextEffect('Loading Toolkits', setLoadingText, 500);
if (window.location.href.toLowerCase().includes('marketplace')) {
setShowMarketplace(true);
axios.get('https://app.superagi.com/api/toolkits/marketplace/list/0')
.then((response) => {
const data = response.data || [];
const filteredData = data?.filter((item) => !excludedToolkits().includes(item.name));
setToolTemplates(filteredData);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching tool templates:', error);
});
} else {
fetchToolTemplateList()
.then((response) => {
const data = response.data || [];
const filteredData = data?.filter((item) => !excludedToolkits().includes(item.name));
setToolTemplates(filteredData);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching tools:', error);
});
}
}, []);
function handleTemplateClick(item) {
const contentType = 'tool_template';
EventBus.emit('openTemplateDetails', {item, contentType});
}
return (
<div className={showMarketplace ? 'ml_8' : 'ml_3'}>
<div className="w_100 overflowY_auto mxh_78vh">
{!isLoading ? <div>
{toolTemplates.length > 0 ? <div className="marketplaceGrid3">{toolTemplates.map((item) => (
<div className="market_containers cursor_pointer" key={item.id} onClick={() => handleTemplateClick(item)}>
<div className="horizontal_container overflow_auto">
<Image className="tool_icon" width={40} height={40} src={returnToolkitIcon(item.name)} alt="tool-icon"/>
<div className="ml_12 mb_8">
<div>{item.name}</div>
<div className="color_gray lh_16">by SuperAgi&nbsp;<Image width={14} height={14} src="/images/is_verified.svg" alt="is_verified"/></div>
</div>
</div>
<div className="text_ellipsis mt_6 color_gray">{item.description}</div>
</div>
))}</div> : <div className="center_container mt_40">
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className="feed_title mt_8">No Tools found!</span>
</div>}
</div> : <div className="horizontal_container_center h_75vh">
<div className="signInInfo text_16 ff_sourceCode">{loadingText}</div>
</div>}
</div>
</div>
)
};
@@ -0,0 +1,38 @@
import React from 'react';
import Image from "next/image";
import styles from './Market.module.css';
import Market from './Market';
export default function MarketplacePublic({env}) {
const handleSignupClick = () => {
if (env === 'PROD') {
const url = localStorage.getItem('marketplace_tab') === 'market_models' ? 'https://models.superagi.com/' : 'https://app.superagi.com/';
window.open(url, '_self');
} else {
window.location.href = '/';
}
};
return (
<div style={{height: '100vh', width: '100%'}}>
<div className={styles.marketplace_public_container}>
<div className="superAgiLogo" style={{paddingLeft: '15px'}}><Image width={132} height={24}
style={{cursor: 'pointer'}}
onClick={handleSignupClick}
src="/images/sign-in-logo.svg"
alt="super-agi-logo"/>
<div className={styles.vertical_line}/>
<div className={styles.topbar_heading}>&nbsp;Marketplace</div>
</div>
<div className={styles.marketplace_public_button}>
<button className="primary_button" onClick={handleSignupClick}>Sign Up/Sign In</button>
</div>
</div>
<div className={styles.marketplace_public_content}>
<Market env={env}/>
</div>
</div>
);
};
@@ -0,0 +1,203 @@
import React, {useEffect, useState} from 'react';
import Image from "next/image";
import styles from '.././Toolkits/Tool.module.css';
import styles3 from '../Agents/Agents.module.css';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import styles2 from "./Market.module.css"
import {
checkToolkitUpdate,
fetchToolTemplateOverview,
installToolkitTemplate,
updateMarketplaceToolTemplate
} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import ReactMarkdown from 'react-markdown';
import axios from 'axios';
import {returnToolkitIcon} from "@/utils/utils";
export default function ToolkitTemplate({template, env}) {
const [rightPanel, setRightPanel] = useState('tool_view')
const [installed, setInstalled] = useState('')
const [markdownContent, setMarkdownContent] = useState('');
useEffect(() => {
if(template.is_installed && !window.location.href.toLowerCase().includes('marketplace')) {
checkToolkitUpdate(template.name).then((response) => {
setInstalled(response.data ? 'Update' : 'Installed');
})
.catch((error) => {
console.error('Error fetching update details:', error);
});
}
else{
setInstalled(window.location.href.toLowerCase().includes('marketplace') ? 'Sign in to install' : 'Install');
}
fetchReadme()
}, []);
function handleInstallClick() {
if (window.location.href.toLowerCase().includes('marketplace')) {
localStorage.setItem('toolkit_to_install', template.name);
if (env === 'PROD') {
window.open(`https://app.superagi.com/`, '_self');
} else {
window.location.href = '/';
}
return;
}
if(installed === "Update"){
updateMarketplaceToolTemplate(template.name)
.then((response) => {
toast.success("Toolkit Updated", {autoClose: 1800});
setInstalled('Installed');
})
.catch((error) => {
console.error('Error installing Toolkit:', error);
});
return;
}
if (template && template.is_installed) {
toast.error("Toolkit is already installed", {autoClose: 1800});
return;
}
installToolkitTemplate(template.name)
.then((response) => {
toast.success("Toolkit installed", {autoClose: 1800});
setInstalled('Installed');
})
.catch((error) => {
console.error('Error installing Toolkit', error);
});
}
function handleBackClick() {
EventBus.emit('goToMarketplace', {});
}
function fetchReadme() {
if (window.location.href.toLowerCase().includes('marketplace')) {
axios.get(`https://app.superagi.com/api/toolkits/marketplace/readme/${template.name}`)
.then((response) => {
setMarkdownContent(response.data || '');
setRightPanel(response.data ? 'overview' : 'tool_view');
})
.catch((error) => {
setRightPanel('tool_view');
console.error('Error fetching template details:', error);
});
} else {
fetchToolTemplateOverview(template.name)
.then((response) => {
setMarkdownContent(response.data || '');
setRightPanel(response.data ? 'overview' : 'tool_view');
})
.catch((error) => {
setRightPanel('tool_view');
console.error('Error fetching template details:', error);
});
}
}
return (
<>
<div>
<div className="row" style={{marginLeft: 'auto'}}>
<div className={styles2.back_button} style={{margin: '8px 0', padding: '2px'}}
onClick={() => handleBackClick()}>
<Image src="/images/arrow_back.svg" alt="back_button" width={14} height={12}/>
<span className={styles2.back_button_text}>Back</span>
</div>
<div className="col-3" style={{maxHeight: '84vh', overflowY: 'auto', padding: '0'}}>
<div className={styles2.left_container}>
<div style={{marginBottom: '15px'}}>
<Image style={{borderRadius: '25px', background: 'black'}} width={50} height={50}
src={returnToolkitIcon(template.name)} alt="tool-icon"/>
</div>
<span className={styles2.top_heading}>{template.name}</span>
<span style={{fontSize: '12px', marginTop: '15px',}} className={styles.tool_publisher}>By SuperAGI <Image
width={14} height={14} src="/images/is_verified.svg"
alt="is_verified"/>&nbsp;{'\u00B7'}&nbsp;<Image width={14} height={14}
src="/images/upload_icon.svg"
alt="upload-icon"/></span>
<button className="primary_button" style={{
marginTop: '15px',
width: '100%',
background: template && template.is_installed && installed !== 'Update' ? 'rgba(255, 255, 255, 0.14)' : '#FFF',
color: template && template.is_installed && installed !== 'Update' ? '#FFFFFF' : '#000'
}} onClick={() => handleInstallClick()}>
{(template && template.is_installed && installed !== 'Update') ?
<Image width={14} height={14} src="/images/tick.svg" alt="tick-icon"/> :
<Image width={14} height={14} src="/images/upload_icon_dark.svg"
alt="upload-icon"/>}&nbsp;{installed}</button>
<hr className={styles2.horizontal_line}/>
<span className={styles2.description_text}>{template.description}</span>
<hr className={styles2.horizontal_line}/>
<span style={{fontSize: '12px',}} className={styles.tool_publisher}>Last updated</span>
<span className={styles2.description_text}>{template.updated_at}</span>
</div>
</div>
<div className="col-9" style={{paddingLeft: '8px'}}>
<div>
<div className={styles2.left_container} style={{marginBottom: '5px', padding: '8px'}}>
<div className="row">
<div className="col-4">
{markdownContent && markdownContent !== '' &&
<button onClick={() => setRightPanel('overview')} className={styles2.tab_button}
style={rightPanel === 'overview' ? {
background: '#454254',
paddingRight: '15px'
} : {background: 'transparent', paddingRight: '15px'}}>
&nbsp;Overview
</button>}
<button onClick={() => setRightPanel('tool_view')}
className={styles2.tab_button} style={rightPanel === 'tool_view' ? {
background: '#454254',
paddingRight: '15px'
} : {background: 'transparent', paddingRight: '15px'}}>
&nbsp;Tools Included
</button>
</div>
</div>
</div>
{rightPanel === 'overview' &&
<div className={styles2.left_container} style={{marginBottom: '8px'}}>
<div className={styles2.markdown_container}>
{markdownContent && markdownContent !== '' ? <ReactMarkdown
className={styles2.markdown_style}>{markdownContent}</ReactMarkdown> :
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
marginTop: '40px',
width: '100%'
}}>
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className={styles3.feed_title} style={{marginTop: '8px'}}>No Overview to display!</span>
</div>
}
</div>
</div>}
{rightPanel === 'tool_view' && <div>
<div style={{overflowY: 'scroll', height: '70vh'}}>
{template.tools.map((value, index) => (
<div key={index} className={styles2.left_container}
style={{marginBottom: '5px', color: 'white', padding: '16px'}}>
<span className={styles2.description_text}>{value.name}</span><br/>
<span className={styles2.sub_text}>{value.description}</span>
</div>
))}
</div>
</div>}
</div>
</div>
</div>
</div>
<ToastContainer/>
</>
);
}
+17
View File
@@ -0,0 +1,17 @@
import React, {useEffect, useState} from "react";
import ModelForm from "./ModelForm";
export default function AddModel({internalId, getModels, sendModelData, env}){
return(
<div id="add_model">
<div className="row">
<div className="col-3" />
<div className="col-6 col-6-scrollable">
<ModelForm internalId={internalId} getModels={getModels} sendModelData={sendModelData} env={env}/>
</div>
<div className="col-3" />
</div>
</div>
)
}
@@ -0,0 +1,129 @@
import React, {useState, useEffect} from "react";
import Image from "next/image";
import {openNewTab, modelIcon, modelGetAuth} from "@/utils/utils";
import {fetchApiKey, storeModel} from "@/pages/api/DashboardService";
import {toast} from "react-toastify";
export default function AddModelMarketPlace({ template, getModels, sendModelData }){
const [modelTokenLimit, setModelTokenLimit] = useState(4096);
const [modelVersion, setModelVersion] = useState('');
const [modelEndpoint, setModelEndpoint] = useState('');
const [tokenError, setTokenError] = useState(false);
const [templateData, setTemplateData] = useState(template);
const [isLoading, setIsLoading] = useState(false);
const [providerId, setProviderId] = useState(1);
const [disableInstall, setDisableInstall] = useState(false);
useEffect(() => {
if(modelVersion === '' && modelEndpoint === '')
setDisableInstall(true)
else
setDisableInstall(false)
},[modelVersion, modelEndpoint])
useEffect(()=>{
console.log(templateData)
checkModelProvider().then().catch();
},[])
const checkModelProvider = async () => {
if(templateData){
const response = await fetchApiKey(templateData.provider);
console.log(response.data)
if(response.data.length === 0) {
setTokenError(true)
return true
}
else {
setTokenError(false)
setProviderId(response.data[0].id)
return false
}
}
}
const storeModelDetails = () => {
storeModel(templateData.model_name, templateData.description, modelEndpoint, providerId, modelTokenLimit, "Marketplace", modelVersion).then((response) =>{
setIsLoading(false)
let data = response.data
if (data.error) {
toast.error(data.error,{autoClose: 1800});
} else if (data.success) {
toast.success(data.success,{autoClose: 1800});
getModels()
console.log(data)
handleModelSuccess({id: data.model_id, name: templateData.model_name})
}
}).catch((error) => {
console.log("SORRY, There was an error storing the model details" + error);
setIsLoading(false)
});
}
const handleModelSuccess = (model) => {
model.contentType = 'Model'
sendModelData(model)
}
return(
<div id="add_model_marketplace" className="row text_12 color_gray">
<div className="col-3" />
<div className="col-6 col-6-scrollable">
{templateData && <div className="vertical_containers">
<span className="text_16 color_white">Add Model</span>
<div className="vertical_containers tag_container mt_24">
<span className="text_14 color_white">{templateData.model_name}</span>
<div className="horizontal_container mt_8">
<span className="mr_8">By {templateData.model_name.includes('/') ? templateData.model_name.split('/')[0] : templateData.provider}</span>·
<Image className="ml_8" width={18} height={18} src={modelIcon(templateData.provider)} alt="logo-icon" />
<span className="ml_4">{templateData.provider}</span>
</div>
</div>
{templateData.provider === 'Hugging Face' && <div className="vertical_containers">
<span className="mt_24">{templateData.provider} Model Endpoint</span>
<input className="input_medium mt_8" type="text" placeholder="Enter Model Endpoint URL"
onChange={(event) => setModelEndpoint(event.target.value)}/>
</div>}
{templateData.provider === 'Replicate' && <div className="vertical_containers">
<span className="mt_24">{templateData.provider} Version</span>
<input className="input_medium mt_8" type="text" placeholder="Enter Model Version"
onChange={(event) => setModelVersion(event.target.value)}/>
</div>}
<span className="mt_24">Token Limit</span>
<input className="input_medium mt_8" type="number" placeholder="Enter the Token Limit" value={modelTokenLimit}
onChange={(event) => setModelTokenLimit(+event.target.value)} />
{tokenError && <div className="horizontal_container align_start error_box mt_24 gap_6">
<Image width={16} height={16} src="/images/icon_error.svg" alt="error-icon" />
<div className="vertical_containers">
<span className="text_12 color_white lh_16">The <b>{templateData.provider}</b> auth token is not added to your settings. In order to start using the model, you need to add the auth token to your settings. You can find the auth token in the <b>{templateData.provider}</b> dashboard. </span>
<div className="horizontal_container mt_16">
<button className="primary_button_small" onClick={() => openNewTab(-3, "Settings", "Settings", false)}>Add auth token</button>
<button className="secondary_button_small ml_8"
onClick={() => window.open(modelGetAuth(templateData.provider),"_blank")}>Get auth token<Image src="/images/open_in_new.svg" alt="deploy_icon" width={12} height={12} className="ml_4" />
</button>
</div>
</div>
</div>}
{templateData.provider === 'Hugging Face' && <div className="horizontal_container align_start info_box mt_24 gap_6">
<Image width={16} height={16} src="/images/icon_info.svg" alt="error-icon" />
<div className="vertical_containers">
<span className="text_12 color_white lh_16">In order to get the endpoint for this model, you will need to deploy it on your Replicate dashboard. Once you have deployed your model on Hugging Face, you will be able to access the endpoint through the Hugging Face dashboard. The endpoint is a URL that you can use to send requests to your model.</span>
<button className="secondary_button_small w_fit_content mt_16"
onClick={() => window.open("https://ui.endpoints.huggingface.co/", "_blank")}>Deploy<Image src="/images/open_in_new.svg" alt="deploy_icon" width={12} height={12} className="ml_4" /></button>
</div>
</div>}
<button className="primary_button w_fit_content align_self_end mt_24" disabled={tokenError}
onClick={() => storeModelDetails()}>Install</button>
</div>}
</div>
<div className="col-3" />
</div>
)
}
+93
View File
@@ -0,0 +1,93 @@
import React, {useState, useEffect} from "react";
import styles from "@/pages/Content/Marketplace/Market.module.css";
import Image from "next/image";
import {loadingTextEffect, modelIcon, returnToolkitIcon} from "@/utils/utils";
import {EventBus} from "@/utils/eventBus";
import {fetchMarketPlaceModel} from "@/pages/api/DashboardService";
import axios from "axios";
export default function MarketModels(){
const [showMarketplace, setShowMarketplace] = useState(false);
const [isLoading, setIsLoading] = useState(false)
const [loadingText, setLoadingText] = useState("Loading Models");
const [modelTemplates, setModelTemplates] = useState([]);
// useEffect(() => {
// loadingTextEffect('Loading Models', setLoadingText, 500);
// setIsLoading(true)
// if (window.location.href.toLowerCase().includes('marketplace')) {
// axios.get('https://app.superagi.com/api/models_controller/get/models_details')
// .then((response) => {
// setModelTemplates(response.data)
// })
// }
// else {
// fetchMarketPlaceModel().then((response) => {
// setModelTemplates(response.data)
// })
// }
// },[])
// useEffect(() => {
// if(modelTemplates.length > 0)
// setIsLoading(true)
// else
// setIsLoading(false)
// }, [modelTemplates])
function handleTemplateClick(item) {
const contentType = 'model_template';
EventBus.emit('openTemplateDetails', {item, contentType});
}
useEffect(() => {
loadingTextEffect('Loading Models', setLoadingText, 500);
setIsLoading(true)
setTimeout(() => {
setIsLoading(false)
}, 2500);
}, []);
return(
<div>
<div className={`${"h_calc_sub_60"} ${"w_99vw"} ${isLoading ? 'display_none' : 'display_block'}`}>
<iframe
id="marketplace_models"
src="https://models.superagi.com/marketplace"
width="100%"
height="100%"
frameBorder="0"
allowFullScreen
></iframe>
</div>
{isLoading && <div className="horizontal_container_center h_75vh">
<div className="signInInfo text_16 ff_sourceCode">{loadingText}</div>
</div>}
</div>
// <div id="market_models" className={showMarketplace ? 'ml_8' : 'ml_3'}>
// <div className="w_100 overflowY_auto mxh_78vh">
// {isLoading ? <div>
// {modelTemplates.length > 0 ? <div className="marketplaceGrid">{modelTemplates.map((item) => (
// <div className="market_containers cursor_pointer" key={item.id} onClick={() => handleTemplateClick(item)}>
// <div>{item.model_name && item.model_name.includes('/') ? item.model_name.split('/')[1] : item.model_name}</div>
// <div className="horizontal_container color_gray">
// <span>by { item.model_name && item.model_name.includes('/') ? item.model_name.split('/')[0] : item.provider }</span>
// <Image className="mr_8 ml_4" width={14} height={14} src="/images/is_verified.svg" alt="is_verified" />·
// <Image className="ml_8 mr_4" width={16} height={16} src={modelIcon(item.provider)} alt="source-icon" />
// <span className="mr_8">{item.provider}</span>·
// <Image className="ml_8 mr_4" width={15} height={15} src="/images/upload_icon.svg" alt="download-icon" />
// <span>{item.installs}</span>
// </div>
// <div className="text_ellipsis mt_14 color_gray">{item.description}</div>
// </div>
// ))}</div> : <div className="center_container mt_40">
// <Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
// <span className="feed_title mt_8">No Models found!</span>
// </div>}
// </div> : <div className="horizontal_container_center h_75vh">
// <div className="signInInfo text_16 ff_sourceCode">{loadingText}</div>
// </div>}
// </div>
// </div>
)
}
+46
View File
@@ -0,0 +1,46 @@
import React, {useState, useEffect} from "react";
import Image from "next/image";
import ModelMetrics from "./ModelMetrics";
import ModelInfo from "./ModelInfo";
import {fetchModel} from "@/pages/api/DashboardService";
import {loadingTextEffect} from "@/utils/utils";
export default function ModelDetails({modelId, modelName}){
const [modelDetails, setModelDetails] = useState([])
const [selectedOption, setSelectedOption] = useState('metrics')
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Models");
useEffect(() => {
loadingTextEffect('Loading Models', setLoadingText, 500);
const fetchModelDetails = async () => {
try {
const response = await fetchModel(modelId);
setModelDetails(response.data)
setIsLoading(false)
} catch(error) {
console.log(`Error Fetching the Details of the Model ${modelName}`, error)
}
};
fetchModelDetails().then().catch();
},[])
return(
<div id="model_details" className="col-12 padding_5 overflowY_auto h_calc92">
{!isLoading && <div className="vertical_containers padding_16_8">
<span className="text_16">{ modelDetails.name ? (modelDetails.name.split('/')[1] || modelDetails.name) : ""}</span>
<span className="text_12 color_gray mt_8 lh_18">{modelDetails.description}</span>
<div className="horizontal_container gap_4 mt_16 mb_2">
<button className={selectedOption === 'metrics' ? 'tab_button_selected' : 'tab_button'}
onClick={() => setSelectedOption('metrics')}>Metrics</button>
<button className={selectedOption === 'details' ? 'tab_button_selected' : 'tab_button'}
onClick={() => setSelectedOption('details')}>Details</button>
</div>
</div>}
{selectedOption === 'metrics' && !isLoading && <ModelMetrics modelDetails={modelDetails} />}
{selectedOption === 'details' && !isLoading && <ModelInfo modelDetails={modelDetails} />}
{isLoading && <div className="loading_container h_75vh"><div className="signInInfo loading_text">{loadingText}</div></div>}
</div>
)
}
+217
View File
@@ -0,0 +1,217 @@
import React, {useEffect, useRef, useState} from "react";
import {removeTab, openNewTab, createInternalId, getUserClick} from "@/utils/utils";
import Image from "next/image";
import {fetchApiKey, storeModel, testModel, verifyEndPoint} from "@/pages/api/DashboardService";
import {BeatLoader, ClipLoader} from "react-spinners";
import {ToastContainer, toast} from 'react-toastify';
export default function ModelForm({internalId, getModels, sendModelData, env}){
const models = env === 'DEV' ? ['OpenAI', 'Replicate', 'Hugging Face', 'Google Palm', 'Local LLM'] : ['OpenAI', 'Replicate', 'Hugging Face', 'Google Palm'];
const [selectedModel, setSelectedModel] = useState('Select a Model');
const [modelName, setModelName] = useState('');
const [modelDescription, setModelDescription] = useState('');
const [modelTokenLimit, setModelTokenLimit] = useState(4096);
const [modelEndpoint, setModelEndpoint] = useState('');
const [modelDropdown, setModelDropdown] = useState(false);
const [modelVersion, setModelVersion] = useState('');
const [modelContextLength, setContextLength] = useState(4096);
const [tokenError, setTokenError] = useState(false);
const [lockAddition, setLockAddition] = useState(true);
const [isLoading, setIsLoading] = useState(false)
const [modelStatus, setModelStatus] = useState(null);
const [createClickable, setCreateClickable] = useState(true);
const modelRef = useRef(null);
useEffect(() => {
function handleClickOutside(event) {
if (modelRef.current && !modelRef.current.contains(event.target)) {
setModelDropdown(false)
}
}
},[]);
useEffect(() => {
const fetchMyAPI = async () => {
const error = await checkModelProvider(selectedModel)
if(selectedModel !== 'Select a Model' && !error)
setLockAddition(false)
else
setLockAddition(true)
}
fetchMyAPI();
},[selectedModel])
const handleModelSelect = async (index) => {
setSelectedModel(models[index])
setModelDropdown(false);
}
const checkModelProvider = async (model_provider) => {
const response = await fetchApiKey(model_provider);
console.log(response.data)
if(selectedModel !== 'Select a Model'){
if(response.data.length === 0) {
setTokenError(true)
return true
}
else {
setTokenError(false)
return false
}
}
}
const handleAddModel = () =>{
setIsLoading(true)
fetchApiKey(selectedModel).then((response) =>{
if(response.data.length > 0)
{
const modelProviderId = response.data[0].id
verifyEndPoint(response.data[0].api_key, modelEndpoint, selectedModel).then((response) =>{
if(response.data.success)
storeModelDetails(modelProviderId)
else{
toast.error("The Endpoint is not Valid",{autoClose: 1800});
setIsLoading(false);
}
}).catch((error) => {
console.log("Error Message:: " + error)
})
}
})
}
const handleModelStatus = async () => {
try {
setCreateClickable(false);
const response = await testModel();
if(response.status === 200) {
setModelStatus(true);
setCreateClickable(true);
} else {
setModelStatus(false);
setCreateClickable(true);
}
} catch(error) {
console.log("Error Message:: " + error);
setModelStatus(false);
setCreateClickable(true);
}
}
const handleModelSuccess = (model) => {
model.contentType = 'Model'
sendModelData(model)
}
const storeModelDetails = (modelProviderId) => {
storeModel(modelName,modelDescription, modelEndpoint, modelProviderId, modelTokenLimit, "Custom", modelVersion, modelContextLength).then((response) =>{
setIsLoading(false)
let data = response.data
if (data.error) {
toast.error(data.error,{autoClose: 1800});
} else if (data.success) {
toast.success(data.success,{autoClose: 1800});
getModels()
handleModelSuccess({id: data.model_id, name: modelName})
}
}).catch((error) => {
console.log("SORRY, There was an error storing the model details:", error);
setIsLoading(false)
});
}
return(
<div id="model_form" className="vertical_containers text_12">
<div className="page_title mt_10">Add new model</div>
<span className="mt_4">Name</span>
<input className="input_medium mt_8" type="text" placeholder="Enter Model Name"
onChange={(event) => setModelName(event.target.value)}/>
<span className="mt_24">Description</span>
<textarea className="textarea_medium mt_8" placeholder="Write a Description"
onChange={(event) => setModelDescription(event.target.value)}/>
<span className="mt_24">Model Provider</span>
<div className="dropdown_container_search mt_8 w_100">
<div className="custom_select_container w_100" onClick={() => setModelDropdown(!modelDropdown)}>
{selectedModel}
<Image width={20} height={21} src={!modelDropdown ? '/images/dropdown_down.svg' : '/images/dropdown_up.svg'} alt="expand-icon"/>
</div>
<div>
{modelDropdown && <div className="custom_select_options w_100" ref={modelRef}>
{models.map((model, index) => (
<div key={index} className="custom_select_option" onClick={() => {setModelStatus(null); handleModelSelect(index)}} style={{padding: '12px 14px', maxWidth: '100%'}}>
{model}
</div>))}
</div>}
</div>
</div>
{tokenError && <div className="horizontal_container align_start error_box mt_24 gap_6">
<Image width={16} height={16} src="/images/icon_error.svg" alt="error-icon" />
<div className="vertical_containers">
<span className="text_12 color_white lh_16">The <b>{selectedModel}</b> auth token is not added to your settings. In order to start using the model, you need to add the auth token to your settings. You can find the auth token in the <b>{selectedModel}</b> dashboard. </span>
<div className="horizontal_container mt_16">
<button className="primary_button_small" onClick={() => {openNewTab(-3, "Settings", "Settings", false); getUserClick('Get Auth Token CLicked',{})}}>Add auth token</button>
<button className="secondary_button_small ml_8"
onClick={() => window.open(modelGetAuth(selectedModel), "_blank")}>Get auth token<Image src="/images/open_in_new.svg" alt="deploy_icon" width={12} height={12} className="ml_4" /></button>
</div>
</div>
</div>}
{(selectedModel === 'Hugging Face') && <div className="mt_24">
<span>Model Endpoint URL</span>
<input className="input_medium mt_8" type="text" placeholder="Enter Model Endpoint URL"
onChange={(event) => setModelEndpoint(event.target.value)}/>
</div>}
{(selectedModel === 'Replicate') && <div className="mt_24">
<span>Model Version</span>
<input className="input_medium mt_8" type="text" placeholder="Enter Model Version"
onChange={(event) => setModelVersion(event.target.value)}/>
</div>}
{(selectedModel === 'Local LLM') && <div className="mt_24">
<span>Model Context Length</span>
<input className="input_medium mt_8" type="number" placeholder="Enter Model Context Length" value={modelContextLength}
onChange={(event) => setContextLength(event.target.value)}/>
</div>}
<div className="mt_24">
<span>Token Limit</span>
<input className="input_medium mt_8" type="number" placeholder="Enter Model Token Limit" value={modelTokenLimit}
onChange={(event) => setModelTokenLimit(parseInt(event.target.value, 10))}/>
</div>
{selectedModel === 'Local LLM' && modelStatus===false && <div className="horizontal_container align_start error_box mt_24 gap_6">
<Image width={16} height={16} src="/images/icon_error.svg" alt="error-icon" />
<div className="vertical_containers">
<span className="text_12 color_white lh_16">Test model failed</span>
</div>
</div>}
{selectedModel === 'Local LLM' && modelStatus===true && <div className="horizontal_container align_start success_box mt_24 gap_6">
<Image width={16} height={16} src="/images/icon_info.svg"/>
<div className="vertical_containers">
<span className="text_12 color_white lh_16">Test model successful</span>
</div>
</div>}
<div className="horizontal_container justify_space_between w_100 mt_24">
{selectedModel==='Local LLM' && <button className="secondary_button flex_none" disabled={!createClickable}
onClick={() => {handleModelStatus();}}>{createClickable ? 'Test Model' : 'Testing model...'}</button>}
<div className="horizontal_container justify_end">
<button className="secondary_button mr_7"
onClick={() => removeTab(-5, "new model", "Add_Model", internalId)}>Cancel</button>
<button className='primary_button' onClick={handleAddModel} disabled={lockAddition || isLoading || (selectedModel==='Local LLM' && !modelStatus)}>
{isLoading ? <><span>Adding Model &nbsp;</span><ClipLoader size={16} color={"#000000"} /></> : 'Add Model'}
</button>
</div>
</div>
<ToastContainer className="text_16"/>
</div>
)
}
+33
View File
@@ -0,0 +1,33 @@
import React, {useState, useEffect} from "react";
import Image from "next/image";
export default function ModelInfo(modelDetails){
const [modelData, setModelData] = useState(modelDetails?.modelDetails)
return(
<div id="model_info" className="text_12">
<hr className="horizontal_line padding_0" />
<div className="row">
<div className="col-3" />
<div className="col-6 col-6-scrollable vertical_containers">
<span>Installation Type</span>
<div className="horizontal_container mt_8 color_white gap_4">
{modelData === 'Marketplace' && <Image width={16} height={16} src="/images/marketplace_logo.png" alt="marketplace_logo" />}
<span>{modelData.type}</span>
</div>
<span className="mt_24">Model Provider</span>
<span className="mt_8 color_white">{modelData.model_provider}</span>
{modelData.end_point && <div className="vertical_containers">
<span className="mt_24">Model Endpoint</span>
<span className="mt_8 color_white">{modelData.end_point}</span>
</div>}
<span className="mt_24">Token Limit</span>
<input className="input_medium mt_8" type="number" placeholder="Enter Model Token Limit" value={modelData.token_limit} disabled/>
</div>
<div className="col-3" />
</div>
</div>
)
}
+91
View File
@@ -0,0 +1,91 @@
import React, {useState, useEffect} from "react";
import {fetchModelData} from "@/pages/api/DashboardService";
import {formatDateTime, formatNumber, returnToolkitIcon} from "@/utils/utils";
import Image from "next/image";
export default function ModelMetrics(modelDetails) {
const [modelData, setModelData] = useState(modelDetails);
const [modelMeta, setModelMeta] = useState([])
const [modelRunData, setModelRunData] = useState([])
useEffect(()=>{
setModelData(modelDetails.modelDetails);
},[modelDetails])
useEffect(()=>{
getModelInfo()
},[modelData, modelDetails])
const getModelInfo = () =>{
if(modelData.name !== undefined){
fetchModelData(modelData.name).then((response)=>{
console.log(response)
setModelMeta(response.data)
setModelRunData(response.data.runs)
})
}
}
return(
<div id="model_metrics" className="overflowY_scroll">
<div className="my_rows">
<div className="my_col_4 display_column_container">
<span className="text_14 mb_8">Total Calls</span>
<div className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24">{modelMeta.total_calls}</div>
</div>
<div className="my_col_4 display_column_container">
<span className="text_14 mb_8">Total Tokens</span>
<div className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24">{formatNumber(modelMeta.total_tokens)}</div>
</div>
<div className="my_col_4 display_column_container">
<span className="text_14 mb_8">Total Agents</span>
<div className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24">{modelMeta.total_agents}</div>
</div>
</div>
<div className="my_rows mt_8">
<div className="my_col_12 display_column_container h_60vh">
<span className="text_14 mb_8">Call Logs</span>
{modelRunData.length === 0 ?
<div className="vertical_container align_center mt_70 w_100">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No Used Tools Found</span>
</div> : <div className="scrollable_container">
<table className="table_css margin_0 padding_0">
<thead>
<tr style={{borderTop: 'none'}}>
<th className="table_header w_20">Log Timestamp</th>
<th className="table_header w_20">Agent Name</th>
<th className="table_header w_20">Run Name</th>
<th className="table_header w_20">Tool</th>
<th className="table_header text_align_right w_20">Tokens Used</th>
</tr>
</thead>
</table>
<div className="overflow_auto w_100">
<table className="table_css margin_0">
<tbody>
{modelRunData.map((data, index) => (
<tr key={index}>
<td className="table_data w_20">{formatDateTime(data.created_at)}</td>
<td className="table_data w_20 br_left_grey">{data.agent_name}</td>
<td className="table_data w_20 br_left_grey">{data.agent_execution_name}</td>
<td className="table_data w_20 br_left_grey horizontal_container">
{data.tool_used && <Image className="image_class bg_black" width={20} height={20}
src={returnToolkitIcon(data.toolkit_name)} alt="tool-icon"/>}
<span>{data.tool_used ? data.tool_used : '-NA-'}</span>
</td>
<td className="table_data text_align_right w_20 br_left_grey">{formatNumber(data.tokens_consumed)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>}
</div>
</div>
</div>
)
}
+61
View File
@@ -0,0 +1,61 @@
import React, {useEffect, useState} from 'react';
import Image from "next/image";
import {EventBus} from "@/utils/eventBus";
import {getFormattedDate, modelIcon} from "@/utils/utils";
import AddModelMarketPlace from "./AddModelMarketPlace";
export default function ModelTemplate({env, template, getModels, sendModelData}){
const [isInstalled, setIsInstalled] = useState(false);
function handleBackClick() {
EventBus.emit('goToMarketplace', {});
}
function handleInstallClick() {
if (window.location.href.toLowerCase().includes('marketplace')) {
if (env === 'PROD') {
window.open(`https://app.superagi.com/`, '_self');
} else {
window.location.href = '/';
}
}
else {
setIsInstalled(true)
}
}
return (
<div id="model_template">
<div className="back_button mt_16 mb_16" onClick={() => isInstalled ? setIsInstalled(false) : handleBackClick()}>
<Image src="/images/arrow_back.svg" alt="back_button" width={14} height={12}/>
<span className="text_12 color_gray fw_500 ml_4">Back</span>
</div>
{ !isInstalled ? (<div className="gridContainer">
<div className="col_3 display_column_container padding_16">
<span className="text_20 color_white">{template.model_name}</span>
<span className="text_12 color_gray mt_4">by {template.model_name.includes('/') ? template.model_name.split('/')[0] : template.provider}</span>
<button className="primary_button w_100 mt_16" disabled={template.is_installed} onClick={() => handleInstallClick()}>
<Image width={16} height={16} src={template.is_installed ? '/images/tick.svg' : '/images/marketplace_download.svg'} alt="download-icon" />
<span className="ml_8">{template.is_installed ? 'Installed' : 'Install'}</span>
</button>
<hr className="horizontal_line" />
<span className="text_12 color_white lh_18">{template.description}</span>
<hr className="horizontal_line" />
<span className="text_12 color_gray">Model Provider</span>
<div className="tags mt_8">
<Image width={18} height={18} src={modelIcon(template.provider)} alt="logo-icon" />
<span className="text_12 color_white ml_4">{template.provider}</span>
</div>
<hr className="horizontal_line" />
<span className="text_12 color_gray">Updated At</span>
<span className="text_12 color_white mt_8">{getFormattedDate(template.updated_at)}</span>
</div>
<div className="col_9 display_column_container padding_16 color_white text_12 lh_18" dangerouslySetInnerHTML={{ __html: template.model_features }} />
</div> ):(
<AddModelMarketPlace template={template} getModels={getModels} sendModelData={sendModelData}/>
)}
</div>
)
}
+37
View File
@@ -0,0 +1,37 @@
import React from "react";
import 'react-toastify/dist/ReactToastify.css';
import {createInternalId, getUserClick} from "@/utils/utils";
export default function Models({sendModelData, models}){
const handleModelSelect = (model) => {
getUserClick('Existing Model Clicked', {})
model.contentType = 'Model'
sendModelData(model)
}
return(
<div id="models">
<div className="container">
<p className="text_14 mt_8 mb_12 ml_8">Models</p>
<div className="w_100 mb_10">
<button className="secondary_button w_100"
onClick={() => {sendModelData({id: -5, name: "new model", contentType: "Add_Model", internalId: createInternalId()}); getUserClick('Add Model Clicked',{})}}>
+ Add Model
</button>
</div>
{models && models.length > 0 ? <div className="vertical_selection_scroll w_100">
{models.map((model, index) => (
<div key={index}>
<div className="sidebar_box flex_dir_col align_start w_100" onClick={() => handleModelSelect(model)}>
<div className="text_ellipsis"><span className="text_13 lh_18 color_white text_ellipsis">{model.name.split('/')[1] || model.name}</span></div>
<div className="text_12 color_gray mt_4">by {model.name.includes('/') ? model.name.split('/')[0] : model.model_provider} · {model.model_provider}</div>
</div>
</div>
))}
</div> : <div className="form_label mt_20 horizontal_container justify_center">No Models found</div>}
</div>
</div>
)
}
+76
View File
@@ -0,0 +1,76 @@
import React, {useState, useEffect} from 'react';
import {addTool} from "@/pages/api/DashboardService";
import {removeTab, setLocalStorageValue} from "@/utils/utils";
import {ToastContainer, toast} from "react-toastify";
export default function AddTool({internalId}) {
const [githubURL, setGithubURl] = useState('');
const [addClickable, setAddClickable] = useState(true);
const handleURLChange = (event) => {
setLocalStorageValue("tool_github_" + String(internalId), event.target.value, setGithubURl);
};
const handleAddTool = () => {
if (githubURL.replace(/\s/g, '') === '') {
toast.error("Github URL can't be blank", {autoClose: 1800});
return
}
setAddClickable(false);
const toolData = {
"github_link": githubURL
}
addTool(toolData)
.then((response) => {
if (response.status === 200) {
toast.success('Tool will be installed in a while', {autoClose: 1800});
setAddClickable(true);
setLocalStorageValue("tool_github_" + String(internalId), '', setGithubURl);
}
})
.catch((error) => {
if (error.response && error.response.status === 400) {
console.error('Error adding tool:', error);
toast.error('Invalid Github URL', {autoClose: 1800});
} else {
console.error('Error adding tool:', error);
toast.error(error.message, {autoClose: 1800});
}
setAddClickable(true);
setLocalStorageValue("tool_github_" + String(internalId), '', setGithubURl);
});
};
useEffect(() => {
if (internalId !== null) {
const github_url = localStorage.getItem("tool_github_" + String(internalId))
if (github_url) {
setGithubURl(github_url);
}
}
}, [internalId])
return (<>
<div className="row">
<div className="col-3"></div>
<div className="col-6 col-6-scrollable">
<div className="page_title mt_10">Add a new tool</div>
<label className="form_label_13">Github Repository URL</label><br/>
<label className="form_label_13">Paste your toolkits Github repo url here and we will sync & install</label>
<input placeholder="Enter URL here" className="input_medium" type="text" value={githubURL} onChange={handleURLChange}/>
<div className="horizontal_container justify_end mt_14">
<button className="secondary_button mr_7" onClick={() => removeTab(-2, "new tool", "Add_Toolkit", internalId)}>Cancel
</button>
<button disabled={!addClickable} className="primary_button" onClick={handleAddTool}>Add tool</button>
</div>
</div>
<div className="col-3"></div>
</div>
<ToastContainer/>
</>)
}
+145
View File
@@ -0,0 +1,145 @@
import React, {useState, useEffect, useRef} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {
getApiKeys, getToolMetrics, getToolLogs, getKnowledgeMetrics, getKnowledgeLogs
} from "@/pages/api/DashboardService";
import {
loadingTextEffect,
} from "@/utils/utils";
export default function Metrics({toolName, knowledgeName}) {
const [apiKeys, setApiKeys] = useState([]);
const [totalTokens, setTotalTokens] = useState(0)
const [totalAgentsUsing, setTotalAgentsUsing] = useState(0)
const [totalCalls, setTotalCalls] = useState(0)
const [callLogs, setCallLogs] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Metrics");
const metricsData = [
{ label: 'Total Calls', value: totalCalls },
{ label: 'Total Agents Using', value: totalAgentsUsing }
];
useEffect(() => {
loadingTextEffect('Loading Metrics', setLoadingText, 500);
}, []);
useEffect(() => {
if(toolName && !knowledgeName){
fetchToolMetrics()
fetchToolLogs()
return;
}
if(!toolName && knowledgeName){
fetchKnowledgeMetrics()
fetchKnowledgeLogs()
return;
}
}, [toolName, knowledgeName]);
const fetchToolMetrics = () => {
getToolMetrics(toolName)
.then((response) => {
setTotalAgentsUsing(response.data.tool_unique_agents ? response.data.tool_unique_agents : 0)
setTotalCalls(response.data.tool_calls ? response.data.tool_calls : 0)
setIsLoading(false)
})
.catch((error) => {
console.error('Error fetching Metrics', error);
});
}
const fetchToolLogs = () => {
getToolLogs(toolName)
.then((response) => {
setCallLogs(response.data ? response.data : [])
setIsLoading(false)
})
.catch((error) => {
console.error('Error fetching Metrics', error);
});
}
const fetchKnowledgeMetrics = () => {
getKnowledgeMetrics(knowledgeName)
.then((response) => {
setTotalAgentsUsing(response.data.knowledge_unique_agents ? response.data.knowledge_unique_agents : 0)
setTotalCalls(response.data.knowledge_calls ? response.data.knowledge_calls : 0)
setIsLoading(false)
})
.catch((error) => {
console.error('Error fetching Metrics', error);
});
}
const fetchKnowledgeLogs = () => {
getKnowledgeLogs(knowledgeName)
.then((response) => {
setCallLogs(response.data ? response.data : [])
setIsLoading(false)
})
.catch((error) => {
console.error('Error fetching Metrics', error);
});
}
return (<>
<div className="row">
<div className="col-12 padding_5 ">
{!isLoading ?
<div>
<div className="display_flex_container gap_5">
{metricsData.map((metric, index) => (
<div className="display_column_container" key={index}>
<span className="text_14">{metric.label}</span>
<div className="text_60_bold display_flex justify_center align_center w_100 h_100 mb_24">
{metric.value}
</div>
</div>
))}
</div>
<div className="display_column_container mt_5">
<span className="text_14">Call Logs</span>
{callLogs.length > 0 ? <div className="scrollable_container pd_bottom_5 br_8 bg_none">
<table className="w_100 margin_0 padding_0">
<thead>
<tr className="border_top_none text_align_left border_bottom_none">
<th className="table_header w_15">Log Timestamp</th>
<th className="table_header w_15">Agent Name</th>
<th className="table_header w_40">Run Name</th>
<th className="table_header w_15">Model</th>
<th className="table_header w_15">Tokens Used</th>
</tr>
</thead>
</table>
<div className="overflow_auto w_100">
<table className="table_css margin_0">
<tbody>
{callLogs.map((item, index) => (
<tr key={index} className="text_align_left">
<td className="table_data w_15">{item.created_at}</td>
<td className="table_data w_15 br_left_grey">{item.agent_name}</td>
<td className="table_data w_40 br_left_grey">{item.agent_execution_name}</td>
<td className="table_data w_15 br_left_grey">{item.model}</td>
<td className="table_data w_15 br_left_grey">{item.tokens_consumed}</td>
</tr>
))}
</tbody>
</table>
</div>
</div> :
<div className="vertical_container align_center mt_90 w_100 mb_90">
<img src="/images/no_permissions.svg" width={190} height={74} alt="No Data"/>
<span className="text_12 color_white mt_6">No logs to show!</span>
</div>}
</div>
</div>
: <div className="loading_container">
<div className="signInInfo loading_text">{loadingText}</div>
</div>}
</div>
</div>
<ToastContainer/>
</>)
}
+231
View File
@@ -0,0 +1,231 @@
.tool_filter {
font-size: 14px;
padding: 5px 8px;
border-radius: 8px;
cursor: pointer;
margin-right: 10px;
min-width: 55px;
text-align: center;
}
.tab_button {
width: 100%;
border: none;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
border-radius: 8px;
padding: 8px 10px;
}
.selected {
background: #EBEBEB;
color: #111111;
}
.not_selected {
background: transparent;
color: #666666;
}
.tool_container {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
overflow-y: scroll;
gap: 10px;
}
.tool_name {
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 21px;
color: #FEFEFE;
margin-top: -2px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.tool_publisher {
font-style: normal;
font-weight: 500;
font-size: 9px;
line-height: 12px;
display: flex;
align-items: center;
color: #666666;
margin-top: 2px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.tool_description {
line-height:16px;
margin-top: 5px;
color: #888888;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.toolkit_description {
line-height:16px;
margin-top: 5px;
color: #888888;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
.tool_box {
cursor: pointer;
width: 100%;
border-radius: 8px;
}
.tag_box {
display: flex;
margin: 15px 0 0 0;
flex-wrap: wrap;
}
.more_button{
margin-left: 4px;
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 8px 10px;
gap: 6px;
width: 36px;
height: 32px;
background: none;
border: none;
border-radius: 8px;
cursor: pointer;
}
.tool_box:hover{
background-color: #494856;
}
.image_class{
background: #FFFFFF80;
border-radius: 20px;
}
.CodeMirror{
background-color: #291A66 !important;
border: 1px solid #4A4A55 !important;
}
.code_head{
display: flex;
flex-direction: column;
justify-content: center;
height: 26px;
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 18px;
color: #FFFFFF;
background: rgba(255, 255, 255, 0.2);
}
.button_class{
margin-top:20px;
float: right;
display: flex;
gap: 5px;
}
.tool1_box {
height: 30px;
font-size: 12px;
text-align: center;
display: flex;
align-items: center;
padding: 8px;
border-radius: 8px;
cursor: pointer;
}
.tab_text {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
padding: 8px;
}
.tools_container{
text-align: center;
font-size: 12px;
color: white;
}
.feed_title {
font-family: 'Source Code Pro';
margin-left: 10px;
font-style: normal;
font-weight: 500;
font-size: 12px;
line-height: 15px;
color: white;
white-space: pre-line;
word-wrap: break-word;
max-width: 95%;
}
.search_box {
width: 100%;
}
.search_box input {
width: 100%;
height: 25px;
font-size: x-small;
padding: 5px;
border: 1px solid rgb(96, 96, 96);
border-radius: 6px;
background-color: #454254;
}
.search_box textarea {
width: 100%;
min-height: 60px;
max-height: 120px;
font-size: x-small;
padding: 5px;
border: 1px solid rgb(96, 96, 96);
border-radius: 6px;
background-color: #454254;
}
.show_more_button {
cursor: pointer;
color: white;
}
.tools_included{
font-size: small;
background-color:rgb(39,35,53);
border-radius: 8px;
width:100%;
height: auto;
text-align: left;
padding:13px;
margin-bottom: 6px;
}
@@ -0,0 +1,236 @@
import React, {useEffect, useRef, useState} from 'react';
import Image from 'next/image';
import {ToastContainer, toast} from 'react-toastify';
import {
updateToolConfig,
getToolConfig,
authenticateGoogleCred,
authenticateTwitterCred
} from "@/pages/api/DashboardService";
import styles from './Tool.module.css';
import {setLocalStorageValue, setLocalStorageArray, returnToolkitIcon, convertToTitleCase} from "@/utils/utils";
import Metrics from "@/pages/Content/Toolkits/Metrics";
export default function ToolkitWorkspace({env, toolkitDetails, internalId}) {
const [activeTab, setActiveTab] = useState('metrics')
const [showDescription, setShowDescription] = useState(false)
const [apiConfigs, setApiConfigs] = useState([]);
const [toolsIncluded, setToolsIncluded] = useState([]);
const [loading, setLoading] = useState(true);
const authenticateToolkits = ['Google Calendar Toolkit', 'Twitter Toolkit'];
const [toolDropdown, setToolDropdown] = useState(false);
const toolRef = useRef(null);
const [currTool, setCurrTool] = useState(false);
let handleKeyChange = (event, index) => {
const updatedData = [...apiConfigs];
updatedData[index].value = event.target.value;
setLocalStorageArray('api_configs_' + String(internalId), updatedData, setApiConfigs);
};
function getGoogleToken(client_data) {
var redirect_uri = "";
if (env == "PROD") {
redirect_uri = 'https://app.superagi.com/api/google/oauth-tokens';
} else {
redirect_uri = "http://localhost:3000/api/google/oauth-tokens";
}
const client_id = client_data.client_id
const scope = 'https://www.googleapis.com/auth/calendar';
window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${client_id}&redirect_uri=${redirect_uri}&access_type=offline&approval_prompt=force&response_type=code&scope=${scope}&state=${toolkitDetails.id}`;
}
function getTwitterToken(oauth_data) {
window.location.href = `https://api.twitter.com/oauth/authenticate?oauth_token=${oauth_data.oauth_token}`
}
useEffect(() => {
if (toolkitDetails !== null) {
if (toolkitDetails.tools) {
setToolsIncluded(toolkitDetails.tools);
setCurrTool(toolkitDetails.tools[0].name)
}
getToolConfig(toolkitDetails.name)
.then((response) => {
const localStoredConfigs = localStorage.getItem('api_configs_' + String(internalId));
const apiConfigs = response.data || [];
setApiConfigs(localStoredConfigs ? JSON.parse(localStoredConfigs) : apiConfigs);
})
.catch((error) => {
console.log('Error fetching API data:', error);
})
.finally(() => {
setLoading(false);
});
}
}, [toolkitDetails]);
const handleUpdateChanges = async () => {
if(apiConfigs.some(config => config?.is_required && !config.value)){
toast.error("Please input necessary details", 1800)
return
}
const updatedConfigData = apiConfigs.map((config) => ({
key: config.key,
value: config.value,
}));
updateToolConfig(toolkitDetails.name, updatedConfigData)
.then((response) => {
toast.success('Toolkit configuration updated', {autoClose: 1800});
})
.catch((error) => {
toast.error('Unable to update Toolkit configuration', {autoClose: 1800});
console.error('Error updating tool config:', error);
});
};
const handleAuthenticateClick = async (toolkitName) => {
handleUpdateChanges();
if (toolkitName === "Google Calendar Toolkit") {
authenticateGoogleCred(toolkitDetails.id)
.then((response) => {
localStorage.setItem("google_calendar_toolkit_id", toolkitDetails.id)
getGoogleToken(response.data);
})
.catch((error) => {
toast.error('Unable to authenticate tool', {autoClose: 1800});
console.error('Error fetching data:', error);
});
} else if (toolkitName === "Twitter Toolkit") {
authenticateTwitterCred(toolkitDetails.id)
.then((response) => {
localStorage.setItem("twitter_toolkit_id", toolkitDetails.id)
getTwitterToken(response.data);
})
.catch((error) => {
toast.error('Unable to authenticate tool', {autoClose: 1800});
console.error('Error fetching data: ', error);
});
}
};
useEffect(() => {
if (internalId !== null) {
const active_tab = localStorage.getItem('toolkit_tab_' + String(internalId));
if (active_tab) {
setActiveTab(active_tab);
}
}
}, [internalId]);
useEffect(() => {
function handleClickOutside(event) {
if (toolRef.current && !toolRef.current.contains(event.target)) {
setToolDropdown(false)
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
return (<>
<div className="row">
<div className="col-12 col-6-scrollable">
<div className={styles.tools_container}>
<div className="horizontal_container align_start mb_20">
<Image src={returnToolkitIcon(toolkitDetails?.name)} alt="toolkit-icon" width={45} height={45} className="tool_icon" />
<div className="vertical_containers ml_15 text_align_left mr_10">
<div className="text_17">{toolkitDetails.name}</div>
<div className={styles.toolkit_description} style={!showDescription ? {overflow: 'hidden'} : {display: 'block'}}>
{`${showDescription ? toolkitDetails.description : toolkitDetails.description.slice(0, 70)}`}
{toolkitDetails.description.length > 70 &&
<span className={styles.show_more_button} onClick={() => setShowDescription(!showDescription)}>
{showDescription ? '...less' : '...more'}
</span>}
</div>
</div>
</div>
<div className="horizontal_container mb_10 border_bottom_grey pd_bottom_5">
<div className="w_50 display_flex_container">
<div className={activeTab === 'metrics' ? 'tab_button_small_selected' : 'tab_button_small'}
onClick={() => setLocalStorageValue('toolkit_tab_' + String(internalId), 'metrics', setActiveTab)}>
<div className="text_12 color_white padding_8">Metrics</div>
</div>
<div className={activeTab === 'configuration' ? 'tab_button_small_selected' : 'tab_button_small'}
onClick={() => setLocalStorageValue('toolkit_tab_' + String(internalId), 'configuration', setActiveTab)}>
<div className="text_12 color_white padding_8">Configuration</div>
</div>
<div className={activeTab === 'tools_included' ? 'tab_button_small_selected' : 'tab_button_small'}
onClick={() => setLocalStorageValue('toolkit_tab_' + String(internalId), 'tools_included', setActiveTab)}>
<div className="text_12 color_white padding_8">Tools Included</div>
</div>
</div>
{!loading && activeTab === 'metrics' && <div className="display_flex_container w_50 justify_end">
<div className="dropdown_container_search ">
<div className="custom_select_container w_180p" onClick={() => setToolDropdown(!toolDropdown)}>
{currTool}<Image width={20} height={21}
src={!toolDropdown ? '/images/dropdown_down.svg' : '/images/dropdown_up.svg'}
alt="expand-icon"/>
</div>
<div>
{toolDropdown && <div className="custom_select_options text_align_left w_100" ref={toolRef}>
{toolsIncluded.map((tool, index) => (
<div key={index} className="custom_select_option mxw_100 padding_12_14" onClick={() => {setCurrTool(tool.name); setToolDropdown(false)}}>
{tool.name}
</div>))}
</div>}
</div>
</div>
</div>}
</div>
<div className="row">
<div className="col-3"></div>
<div className="col-6">
{!loading && activeTab === 'configuration' && <div>
{apiConfigs.length > 0 ? (apiConfigs.map((config, index) => (
<div key={index}>
<div className="vertical_containers w_100 color_gray mb_20 text_align_left">
<label className="mb_6">{convertToTitleCase(config.key)}</label>
<div className={styles.search_box}>
{config?.key_type !== "file" && <input className="color_white" type={config?.is_secret ? 'password' : 'text'} value={config.value || ''} onChange={(event) => handleKeyChange(event, index)}/>}
{config?.key_type === "file" && <textarea className="color_white" type={config?.is_secret ? 'password' : 'text'} value={config.value || ''} onChange={(event) => handleKeyChange(event, index)}/>}
</div>
</div>
</div>
))) : (<div className="vertical_container mt_40 w_100">
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className="feed_title mt_8">No Keys found!</span>
</div>)}
{apiConfigs.length > 0 && (
<div className="horizontal_space_between">
{authenticateToolkits.includes(toolkitDetails.name) &&
<button className="primary_button w_fit_content" onClick={() => handleAuthenticateClick(toolkitDetails.name)}>Authenticate Tool</button>}
<button className="primary_button" onClick={handleUpdateChanges}>Update Changes</button>
</div>)}
</div>}
{activeTab === 'tools_included' && <div>
{toolsIncluded.map((tool, index) => (
<div key={index} className={styles.tools_included}>
<div>
<div className="color_white">{tool.name}</div>
<div className="color_gray mt_5">{tool.description}</div>
</div>
</div>
))}
</div>}
</div>
<div className="col-3"></div>
</div>
{activeTab === 'metrics' && <div>
<Metrics toolName={currTool} />
</div>}
</div>
</div>
</div>
<ToastContainer/>
</>);
}
+48
View File
@@ -0,0 +1,48 @@
import React from 'react';
import Image from "next/image";
import {createInternalId, returnToolkitIcon, excludedToolkits} from "@/utils/utils";
export default function Toolkits({sendToolkitData, toolkits, env}) {
return (
<>
<div className="container">
<p className="text_14 mt_8 mb_12 ml_8">Toolkits</p>
{env !== 'PROD' && <div className="w_100 mb_10">
<button className="secondary_button w_100" onClick={() => sendToolkitData({
id: -2,
name: "new tool",
contentType: "Add_Toolkit",
internalId: createInternalId()
})}>
+ Add Tool
</button>
</div>}
{toolkits && toolkits.length > 0 ? (
<div className={`${env === "PROD" ? 'h_calc_add40' : 'h_80vh'} ${"overflowY_scroll"}`}>
{toolkits.map((tool, index) =>
tool.name !== null && !excludedToolkits().includes(tool.name) && (
<div key={index} className="item_box mb_10" onClick={() => sendToolkitData(tool)}>
<div className="row">
<div className="col-12">
<div className="item_container padding_5">
<Image className="image_class bg_black" width={30} height={30}
src={returnToolkitIcon(tool.name)}
alt="tool-icon"/>
<div className="ml_8">
<div className="item_name">{tool.name}</div>
<div className="item_publisher">by SuperAGI</div>
</div>
</div>
</div>
</div>
</div>
)
)}
</div>
) : (
<div className="form_label mt_20 horizontal_container justify_center">No Toolkits found</div>
)}
</div>
</>
);
}
+503
View File
@@ -0,0 +1,503 @@
import React, {useEffect, useRef, useState} from 'react';
import Agents from '../Content/Agents/Agents';
import Knowledge from '../Content/Knowledge/Knowledge';
import AddKnowledge from '../Content/Knowledge/AddKnowledge';
import KnowledgeDetails from '../Content/Knowledge/KnowledgeDetails';
import AgentWorkspace from '../Content/Agents/AgentWorkspace';
import AgentCreate from '../Content/Agents/AgentCreate';
import ToolkitWorkspace from '../Content/./Toolkits/ToolkitWorkspace';
import Toolkits from '../Content/./Toolkits/Toolkits';
import Settings from "./Settings/Settings";
import styles from './Dashboard.module.css';
import ApmDashboard from "../Content/APM/ApmDashboard";
import AddModel from "../Content/Models/AddModel";
import Models from "../Content/Models/Models";
import ModelDetails from "../Content/Models/ModelDetails";
import Image from "next/image";
import {EventBus} from "@/utils/eventBus";
import {
getAgents,
getToolKit,
getKnowledge,
getLastActiveAgent,
sendGoogleCreds,
sendTwitterCreds,
fetchModels,
} from "@/pages/api/DashboardService";
import Market from "../Content/Marketplace/Market";
import AgentTemplatesList from '../Content/Agents/AgentTemplatesList';
import {useRouter} from 'next/router';
import querystring from 'querystring';
import styles1 from '../Content/Agents/Agents.module.css';
import AddTool from "@/pages/Content/Toolkits/AddTool";
import {createInternalId, resetLocalStorage, preventDefault, getUserClick} from "@/utils/utils";
import AddDatabase from "@/pages/Dashboard/Settings/AddDatabase";
import DatabaseDetails from "@/pages/Dashboard/Settings/DatabaseDetails";
export default function Content({env, selectedView, selectedProjectId, organisationId}) {
const [tabs, setTabs] = useState([]);
const [selectedTab, setSelectedTab] = useState(null);
const [agents, setAgents] = useState(null);
const [toolkits, setToolkits] = useState(null);
const [knowledge, setKnowledge] = useState(null);
const tabContainerRef = useRef(null);
const [toolkitDetails, setToolkitDetails] = useState({});
const [models, setModels] = useState([]);
const [starModal, setStarModal] = useState(false);
const router = useRouter();
const multipleTabContentTypes = ['Create_Agent', 'Add_Toolkit', 'Add_Knowledge', 'Add_Database', 'Add_Model', 'Edit_Agent'];
const [isApmOpened, setIsApmOpened] = useState(false);
const [prevView, setPrevView] = useState(null);
useEffect(() => {
if (prevView !== selectedView) {
const apmTab = tabs.find(tab => tab.contentType === 'APM');
setIsApmOpened(!!apmTab);
setPrevView(selectedView);
}
}, [selectedView, tabs, prevView]);
async function fetchAgents() {
try {
const response = await getAgents(selectedProjectId);
const data = response.data || [];
const updatedData = data.map(item => {
return {...item, contentType: "Agents"};
});
setAgents(updatedData);
} catch (error) {
console.error('Error fetching agents:', error);
}
}
function getAgentList() {
fetchAgents()
.then(() => {
console.log('Agents fetched successfully!');
})
.catch((error) => {
console.error('Error fetching agents:', error);
});
}
async function fetchToolkits() {
try {
const response = await getToolKit();
const data = response.data || [];
const updatedData = data.map(item => {
let updatedName = item.name === "Web Scrapper Toolkit" ? "Web Scraper Toolkit" : item.name;
return {...item,name: updatedName, contentType: "Toolkits", isOpen: false, internalId: createInternalId()};
});
setToolkits(updatedData);
} catch (error) {
console.error('Error fetching toolkits:', error);
}
}
function getToolkitList() {
fetchToolkits()
.then(() => {
console.log('Toolkits fetched successfully!');
})
.catch((error) => {
console.error('Error fetching toolkits:', error);
});
}
async function getModels() {
try{
const response = await fetchModels();
console.log(response.data)
setModels(response.data)
} catch(error){
console.error('Error fetching models:', error);
}
}
async function fetchKnowledge() {
try {
const response = await getKnowledge();
const data = response.data || [];
const updatedData = data.map(item => {
return {...item, contentType: "Knowledge", internalId: createInternalId()};
});
setKnowledge(updatedData);
} catch (error) {
console.error('Error fetching knowledge:', error);
}
}
function getKnowledgeList() {
fetchKnowledge()
.then(() => {
console.log('Knowledge fetched successfully!');
})
.catch((error) => {
console.error('Error fetching knowledge:', error);
});
}
useEffect(() => {
getAgentList();
getToolkitList();
getModels();
}, [selectedProjectId])
useEffect(() => {
getKnowledgeList();
}, [organisationId])
const cancelTab = (index, contentType, internalId) => {
let updatedTabs = [...tabs];
if (selectedTab === index) {
updatedTabs.splice(index, 1);
if (index === 0 && tabs.length === 1) {
setSelectedTab(null);
} else {
const newIndex = index === tabs.length - 1 ? index - 1 : index;
setSelectedTab(newIndex);
}
} else {
if (selectedTab > index) {
setSelectedTab(selectedTab - 1);
}
updatedTabs.splice(index, 1);
}
if(contentType === "APM")
getUserClick('APM Closed',{})
resetLocalStorage(contentType, internalId);
setTabs(updatedTabs);
};
const addTab = (element) => {
let addedTabIndex = null;
if (element.contentType === "Toolkits") {
setToolkitDetails(element);
}
const isExistingTab = tabs.some(
(tab) => tab.id === element.id && tab.name === element.name && tab.contentType === element.contentType && !multipleTabContentTypes.includes(element.contentType)
);
if (!isExistingTab) {
const updatedTabs = [...tabs, element];
setTabs(updatedTabs);
addedTabIndex = updatedTabs.length - 1;
setSelectedTab(addedTabIndex);
} else {
const existingTabIndex = tabs.findIndex(
(tab) => tab.id === element.id && tab.name === element.name && tab.contentType === element.contentType
);
setSelectedTab(existingTabIndex);
}
};
const selectTab = (element, index) => {
setSelectedTab(index);
if (element.contentType === "Toolkits") {
setToolkitDetails(element);
}
};
useEffect(() => {
if (tabContainerRef.current) {
const tabElement = tabContainerRef.current.querySelector(`[data-tab-id="${selectedTab}"]`);
if (tabElement) {
const containerScrollLeft = tabContainerRef.current.scrollLeft;
const tabOffsetLeft = tabElement.offsetLeft;
const containerWidth = tabContainerRef.current.offsetWidth;
if (tabOffsetLeft < containerScrollLeft || tabOffsetLeft >= containerScrollLeft + containerWidth) {
tabContainerRef.current.scrollLeft = tabOffsetLeft;
}
}
}
const queryParams = router.asPath.split('?')[1];
const parsedParams = querystring.parse(queryParams);
parsedParams["toolkit_id"] = toolkitDetails.toolkit_id;
if (window.location.href.indexOf("twitter_creds") > -1) {
parsedParams["toolkit_id"] = localStorage.getItem("twitter_toolkit_id") || null;
const params = JSON.stringify(parsedParams)
sendTwitterCreds(params)
.then((response) => {
console.log("Authentication completed successfully");
})
.catch((error) => {
console.error("Error fetching data: ", error);
})
}
if (window.location.href.indexOf("google_calendar_creds") > -1) {
const toolkit_id = localStorage.getItem("google_calendar_toolkit_id") || null;
let data = Object.keys(parsedParams)[0];
let params = JSON.parse(data);
sendGoogleCreds(params, toolkit_id)
.then((response) => {
console.log("Authentication completed successfully");
})
.catch((error) => {
console.error("Error fetching data: ", error);
})
}
}, [selectedTab]);
useEffect(() => {
const openNewTab = (eventData) => {
addTab(eventData.element);
};
const openToolkitTab = (eventData) => {
const toolkit = toolkits?.find((toolkit) => toolkit.tools.some((tool) => tool.id === eventData.toolId));
if (toolkit) {
localStorage.setItem('toolkit_tab_' + String(toolkit.internalId), 'tools_included');
addTab(toolkit);
}
}
const removeTab = (eventData) => {
const element = eventData.element;
const tabIndex = tabs.findIndex(
(tab) => tab.id === element.id &&
tab.name === element.name &&
tab.contentType === element.contentType &&
tab.internalId === element.internalId
);
cancelTab(tabIndex, element.contentType, element.internalId);
};
EventBus.on('openNewTab', openNewTab);
EventBus.on('reFetchAgents', getAgentList);
EventBus.on('reFetchKnowledge', getKnowledgeList);
EventBus.on('removeTab', removeTab);
EventBus.on('openToolkitTab', openToolkitTab);
return () => {
EventBus.off('openNewTab', openNewTab);
EventBus.off('reFetchAgents', getAgentList);
EventBus.off('reFetchKnowledge', getKnowledgeList);
EventBus.off('removeTab', removeTab);
};
});
async function fetchLastActive() {
try {
const response = await getLastActiveAgent(selectedProjectId);
addTab(response.data);
} catch (error) {
console.error('Error fetching last active agent:', error);
}
}
function getLastActive() {
fetchLastActive()
.then(() => {
console.log('Last active agent fetched successfully!');
})
.catch((error) => {
console.error('Error fetching last active agent:', error);
});
}
const openGithubRepo = () => {
window.open('https://github.com/TransformerOptimus/SuperAGI', '_blank');
localStorage.setItem('repo_starred', 'starred');
setStarModal(false);
};
const closeStarModal = () => {
const closedTime = Date.now();
localStorage.setItem('popup_closed_time', JSON.stringify(closedTime));
setStarModal(false);
};
useEffect(() => {
const last_closed_time = localStorage.getItem('popup_closed_time');
const minTime = 4 * 24 * 60 * 60 * 1000;
const repo_starred = localStorage.getItem('repo_starred');
if (!repo_starred && Date.now() - JSON.parse(last_closed_time) > minTime) {
setStarModal(true);
}
}, []);
return (<>
<div style={{display: 'flex', height: '100%'}}>
{(selectedView === 'agents' || selectedView === 'toolkits' || selectedView === 'knowledge' || selectedView === 'models') &&
<div className={styles.item_list} style={{width: '13vw'}}>
{selectedView === 'agents' && <div><Agents sendAgentData={addTab} agents={agents}/></div>}
{selectedView === 'toolkits' && <div><Toolkits env={env} sendToolkitData={addTab} toolkits={toolkits}/></div>}
{selectedView === 'knowledge' && <div><Knowledge sendKnowledgeData={addTab} knowledge={knowledge}/></div>}
{selectedView === 'models' && <div><Models sendModelData={addTab} models={models} /></div>}
</div>}
{tabs.length <= 0 ? <div className={styles.main_workspace} style={selectedView === '' ? {
width: '93.5vw',
paddingLeft: '10px'
} : {width: '80.5vw'}}>
<div className={styles.empty_state}>
<div>
<div><Image width={264} height={144} src="/images/watermark.png" alt="empty-state"/></div>
<div style={{width: '100%', display: 'flex', justifyContent: 'center', marginTop: '30px'}}>
<button onClick={() => {addTab({
id: -1,
name: "new agent",
contentType: "Create_Agent",
internalId: createInternalId()
}); getUserClick('Agent Create Clicked', {'Click Position': 'Content'})}} className={styles.empty_state_button}>
Create new agent&nbsp;<Image width={17} height={17} src="/images/arrow_forward_secondary.svg"
alt="forward-arrow"/>
</button>
</div>
{agents && agents.length > 0 &&
<div style={{width: '100%', display: 'flex', justifyContent: 'center', marginTop: '12px'}}>
<button onClick={getLastActive} className={styles.empty_state_button}>
View last active agent&nbsp;<Image width={17} height={17} src="/images/arrow_forward_secondary.svg"
alt="forward-arrow"/>
</button>
</div>}
{env !== 'PROD' &&
<div style={{width: '100%', display: 'flex', justifyContent: 'center', marginTop: '12px'}}>
<button onClick={() => addTab({
id: -2,
name: "new tool",
contentType: "Add_Toolkit",
internalId: createInternalId()
})} className={styles.empty_state_button}>
Add custom tool&nbsp;<Image width={17} height={17} src="/images/arrow_forward_secondary.svg"
alt="forward-arrow"/>
</button>
</div>}
<div style={{width: '100%', display: 'flex', justifyContent: 'center', marginTop: '12px'}}>
<button onClick={() => addTab({id: -3, name: "Settings", contentType: "Settings"})}
className={styles.empty_state_button}>
Go to settings&nbsp;<Image width={17} height={17} src="/images/arrow_forward_secondary.svg"
alt="forward-arrow"/>
</button>
</div>
</div>
</div>
</div> : <div className={styles.main_workspace}
style={(selectedView === 'agents' || selectedView === 'toolkits' || selectedView === 'knowledge' || selectedView === 'models') ? {width: '80.5vw'} : {width: '100%'}}>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%'}}>
<div className={styles.tabs} ref={tabContainerRef}>
{tabs.map((tab, index) => (
<div data-tab-id={index} key={index}
className={`${styles.tab_box} ${selectedTab === index ? styles.tab_box_selected : ''}`}
onClick={() => {
selectTab(tab, index)
}}>
<div style={{display: 'flex', order: '0', overflowX: 'hidden'}}>
{(tab.contentType === 'Agents' || tab.contentType === 'Create_Agent' || tab.contentType === 'Edit_Agent') &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/agents_light.svg"
alt="agent-icon"/></div>}
{(tab.contentType === 'Toolkits' || tab.contentType === 'Add_Toolkit') &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/tools_light.svg"
alt="tools-icon"/></div>}
{(tab.contentType === 'Knowledge' || tab.contentType === 'Add_Knowledge') &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/knowledge.svg"
alt="knowledge-icon"/></div>}
{(tab.contentType === 'Database' || tab.contentType === 'Add_Database') &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/database.svg"
alt="database-icon"/></div>}
{tab.contentType === 'Settings' &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/settings.svg"
alt="settings-icon"/></div>}
{tab.contentType === 'Marketplace' &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/marketplace.svg"
alt="marketplace-icon"/></div>}
{tab.contentType === 'APM' &&
<div className={styles.tab_active}><Image width={13} height={13} src="/images/apm.svg"
alt="apm-icon"/></div>}
<div style={{marginLeft: '8px'}}><span className={styles.tab_text}>{tab.name}</span></div>
</div>
<div onClick={(e) => {
e.stopPropagation();
cancelTab(index, tab.contentType, tab.internalId || 0)
}} className={styles.tab_active} style={{order: '1'}}><Image width={13} height={13}
src="/images/close.svg"
alt="close-icon"/></div>
</div>
))}
</div>
</div>
<div className={styles.tab_detail}
style={tabs.length > 0 ? {backgroundColor: '#2F2C40', overflowX: 'hidden'} : {}}>
<div style={{padding: '0 5px 5px 5px'}}>
{tabs.map((tab, index) => (
<div key={index}>
{selectedTab === index && <div>
{tab.contentType === 'Agents' &&
<AgentWorkspace env={env} internalId={tab.internalId || index} agentId={tab.id} agentName={tab.name}
selectedView={selectedView}
agents={agents} fetchAgents={getAgentList} sendAgentData={addTab} />}
{tab.contentType === 'Toolkits' &&
<ToolkitWorkspace env={env} internalId={tab.internalId || index}
toolkitDetails={toolkitDetails}/>}
{tab.contentType === 'Knowledge' &&
<KnowledgeDetails internalId={tab.internalId || index} knowledgeId={tab.id}/>}
{tab.contentType === 'Database' &&
<DatabaseDetails internalId={tab.internalId || index} databaseId={tab.id}/>}
{tab.contentType === 'Knowledge' &&
<KnowledgeDetails internalId={tab.internalId || index} knowledgeId={tab.id}/>}
{tab.contentType === 'Database' &&
<DatabaseDetails internalId={tab.internalId || index} databaseId={tab.id}/>}
{tab.contentType === 'Settings' &&
<Settings organisationId={organisationId} sendDatabaseData={addTab}/>}
{tab.contentType === 'Marketplace' && <Market env={env} selectedView={selectedView} getModels={getModels} sendModelData={addTab} />}
{tab.contentType === 'Add_Toolkit' && <AddTool internalId={tab.internalId || index}/>}
{tab.contentType === 'Add_Knowledge' &&
<AddKnowledge internalId={tab.internalId || index} sendKnowledgeData={addTab}/>}
{tab.contentType === 'Add_Database' &&
<AddDatabase internalId={tab.internalId || index} sendDatabaseDetailsData={addTab}/>}
{tab.contentType === 'Create_Agent' &&
<AgentTemplatesList knowledge={knowledge} internalId={tab.internalId || index}
organisationId={organisationId} sendKnowledgeData={addTab}
sendAgentData={addTab} selectedProjectId={selectedProjectId}
fetchAgents={getAgentList} toolkits={toolkits} env={env} />}
{tab.contentType === 'APM' && <ApmDashboard />}
{tab.contentType === 'Edit_Agent' &&
<AgentCreate knowledge={knowledge} internalId={tab.internalId || index}
organisationId={organisationId} sendKnowledgeData={addTab}
sendAgentData={addTab} selectedProjectId={selectedProjectId} editAgentId={tab.id}
fetchAgents={getAgentList} toolkits={toolkits} template={null} edit={true} agents={agents}/>}
{tab.contentType === 'Add_Model' && <AddModel internalId={tab.internalId} getModels={getModels} sendModelData={addTab} env={env}/>}
{tab.contentType === 'Model' && <ModelDetails modelId={tab.id} modelName={tab.name} />}
</div>}
</div>
))}
</div>
</div>
</div>}
{starModal && (<div className="modal" onClick={closeStarModal}>
<div className="modal-content" style={{width: '35%'}} onClick={preventDefault}>
<div className={styles1.detail_name} style={{width: '100%', textAlign: 'center'}}>Support the project by
leaving a star on GitHub repository
</div>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
<button className="secondary_button" style={{marginTop: '10px', width: 'fit-content'}}
onClick={openGithubRepo}>
Leave a star on GitHub
</button>
</div>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
<div className="cancel_action" onClick={closeStarModal}>
Ill do it later
</div>
</div>
</div>
</div>)}
</div>
</>
);
}
+126
View File
@@ -0,0 +1,126 @@
.logo {
cursor: default;
}
.empty_state {
background-color: #2F2C40;
width: 100%;
height: 100%;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.section {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 8px;
font-weight: 400;
cursor: pointer;
width: 100%;
font-size: 9px;
padding: 10px 0;
text-align: center;
}
.button_icon {
margin-bottom: 4px;
}
.selected {
background-color: #333141;
}
.section:hover {
background-color: #333141;
}
.sideBar_image {
margin-left: 10px;
margin-top: -1px;
}
.item_list {
height: 100%;
position: relative;
}
.main_workspace {
height: 100%;
padding: 0 10px 10px 10px;
display: flex;
flex-wrap: wrap;
}
.tab_detail {
height: calc(100% - 36px);
width: 100%;
border-radius: 8px;
overflow-y: hidden;
position: relative;
}
.tabs {
width: 98.5%;
display: flex;
overflow-x: scroll;
height: 36px;
}
.tab_box {
display: flex;
flex-direction: row;
align-items: center;
padding: 6px 8px 10px 8px;
gap: 6px;
cursor: pointer;
width: 175px;
min-width: 175px;
height: 100%;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
justify-content: space-between;
}
.tab_box_selected {
background-color: #2F2C40;
}
.tab_active {
display: flex;
flex-direction: row;
align-items: center;
padding: 0;
margin-top: 1px;
}
.tab_text {
font-style: normal;
font-weight: 400;
font-size: 13px;
line-height: 15px;
align-items: center;
color: white;
flex: none;
order: 1;
flex-grow: 0;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.empty_state_button {
border: none;
border-radius: 8px;
background: transparent;
color: white;
padding: 5px 20px;
}
.empty_state_button:hover {
background: #494856;
}
+407
View File
@@ -0,0 +1,407 @@
import React, {useState, useEffect} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import agentStyles from "@/pages/Content/Agents/Agents.module.css";
import {
createInternalId,
removeTab,
returnDatabaseIcon,
setLocalStorageArray,
setLocalStorageValue
} from "@/utils/utils";
import knowledgeStyles from "@/pages/Content/Knowledge/Knowledge.module.css";
import styles from "@/pages/Content/Marketplace/Market.module.css";
import Image from "next/image";
import styles1 from "@/pages/Content/Agents/Agents.module.css";
import {connectPinecone, connectQdrant, connectWeaviate, fetchVectorDBList} from "@/pages/api/DashboardService";
export default function AddDatabase({internalId, sendDatabaseDetailsData}) {
const [activeView, setActiveView] = useState('select_database');
const [vectorDatabases, setVectorDatabases] = useState(null);
const [selectedDB, setSelectedDB] = useState('');
const [databaseName, setDatabaseName] = useState('database name');
const [collections, setCollections] = useState(['']);
const [pineconeApiKey, setPineconeApiKey] = useState('');
const [pineconeEnvironment, setPineconeEnvironment] = useState('');
const [qdrantApiKey, setQdrantApiKey] = useState('');
const [qdrantURL, setQdrantURL] = useState('');
const [weaviateApiKey, setWeaviateApiKey] = useState('');
const [weaviateURL, setWeaviateURL] = useState('');
const [qdrantPort, setQdrantPort] = useState(8001);
const [connectText, setConnectText] = useState('Connect');
useEffect(() => {
const active_view = localStorage.getItem('add_database_tab_' + String(internalId));
if (active_view) {
setActiveView(active_view);
}
const db_name = localStorage.getItem('db_name_' + String(internalId));
if (db_name) {
setDatabaseName(db_name);
}
const db_collections = localStorage.getItem('db_collections_' + String(internalId));
if (db_collections) {
setCollections(JSON.parse(db_collections));
}
const pinecone_api = localStorage.getItem('pincone_api_' + String(internalId));
if (pinecone_api) {
setPineconeApiKey(pinecone_api);
}
const pinecone_env = localStorage.getItem('pinecone_env_' + String(internalId));
if (pinecone_env) {
setPineconeEnvironment(pinecone_env);
}
const qdrant_api = localStorage.getItem('qdrant_api_' + String(internalId));
if (qdrant_api) {
setQdrantApiKey(qdrant_api);
}
const qdrant_url = localStorage.getItem('qdrant_url_' + String(internalId));
if (qdrant_url) {
setQdrantURL(qdrant_url);
}
const qdrant_port = localStorage.getItem('qdrant_port_' + String(internalId));
if (qdrant_port) {
setQdrantPort(Number(qdrant_port));
}
const weaviate_api = localStorage.getItem('weaviate_api_' + String(internalId));
if (weaviate_api) {
setWeaviateApiKey(weaviate_api);
}
const weaviate_url = localStorage.getItem('weaviate_url_' + String(internalId));
if (weaviate_url) {
setWeaviateURL(weaviate_url);
}
}, [internalId]);
useEffect(() => {
fetchVectorDBList()
.then((response) => {
const data = response.data || [];
setVectorDatabases(data);
const selected_db = localStorage.getItem('selected_db_' + String(internalId));
setSelectedDB(selected_db ? selected_db : data[0].name || '');
})
.catch((error) => {
console.error('Error fetching vector databases:', error);
});
}, [internalId]);
const handleNameChange = (event) => {
setLocalStorageValue('db_name_' + String(internalId), event.target.value, setDatabaseName);
}
const handlePineconeAPIKeyChange = (event) => {
setLocalStorageValue('pincone_api_' + String(internalId), event.target.value, setPineconeApiKey);
}
const handlePineconeEnvironmentChange = (event) => {
setLocalStorageValue('pinecone_env_' + String(internalId), event.target.value, setPineconeEnvironment);
}
const handleQdrantAPIKeyChange = (event) => {
setLocalStorageValue('qdrant_api_' + String(internalId), event.target.value, setQdrantApiKey);
}
const handleQdrantURLChange = (event) => {
setLocalStorageValue('qdrant_url_' + String(internalId), event.target.value, setQdrantURL);
}
const handleQdrantPortChange = (event) => {
setLocalStorageValue('qdrant_port_' + String(internalId), event.target.value, setQdrantPort);
}
const handleWeaviateAPIKeyChange = (event) => {
setLocalStorageValue('weaviate_api_' + String(internalId), event.target.value, setWeaviateApiKey);
}
const handleWeaviateURLChange = (event) => {
setLocalStorageValue('weaviate_url_' + String(internalId), event.target.value, setWeaviateURL);
}
const addCollection = () => {
setLocalStorageArray("db_collections_" + String(internalId), [...collections, 'collection name'], setCollections);
};
const handleCollectionChange = (index, newValue) => {
const updatedCollections = [...collections];
updatedCollections[index] = newValue;
setLocalStorageArray("db_collections_" + String(internalId), updatedCollections, setCollections);
};
const handleCollectionDelete = (index) => {
const updatedCollections = [...collections];
updatedCollections.splice(index, 1);
setLocalStorageArray("db_collections_" + String(internalId), updatedCollections, setCollections);
};
const connectResponse = (data) => {
if (!data) {
return;
}
toast.success("Database connected successfully", {autoClose: 1800});
setConnectText("Connected");
sendDatabaseDetailsData({id: data.id, name: data.name, contentType: "Database", internalId: createInternalId()});
}
const connectDatabase = () => {
if (databaseName.replace(/\s/g, '') === '') {
toast.error("Database name can't be blank", {autoClose: 1800});
return;
}
if(collections.length === 1 && collections[0].length < 1){
toast.error("Atleast add 1 Collection/Index", {autoClose: 1800});
return;
}
if (selectedDB === 'Pinecone') {
if (pineconeApiKey.replace(/\s/g, '') === '') {
toast.error("Pinecone API key is empty", {autoClose: 1800});
return;
}
if (pineconeEnvironment.replace(/\s/g, '') === '') {
toast.error("Pinecone environment is empty", {autoClose: 1800});
return;
}
setConnectText("Connecting...");
const pineconeData = {
"name": databaseName,
"collections": collections,
"api_key": pineconeApiKey,
"environment": pineconeEnvironment,
}
connectPinecone(pineconeData)
.then((response) => {
connectResponse(response.data);
})
.catch((error) => {
toast.error("Unable to connect database", {autoClose: 1800});
console.error('Error fetching vector databases:', error);
setConnectText("Connect");
});
}
if (selectedDB === 'Qdrant') {
if (qdrantApiKey.replace(/\s/g, '') === '') {
toast.error("Qdrant API key is empty", {autoClose: 1800});
return;
}
if (qdrantURL.replace(/\s/g, '') === '') {
toast.error("Qdrant URL is empty", {autoClose: 1800});
return;
}
if (String(qdrantPort).replace(/\s/g, '') === '') {
toast.error("Qdrant port can't be blank", {autoClose: 1800});
return;
}
setConnectText("Connecting...");
const qdrantData = {
"name": databaseName,
"collections": collections,
"api_key": qdrantApiKey,
"url": qdrantURL,
"port": qdrantPort
}
connectQdrant(qdrantData)
.then((response) => {
connectResponse(response.data);
})
.catch((error) => {
toast.error("Unable to connect database", {autoClose: 1800});
console.error('Error fetching vector databases:', error);
setConnectText("Connect");
});
}
if (selectedDB === 'Weaviate') {
if (weaviateApiKey.replace(/\s/g, '') === '') {
toast.error("Weaviate API key is empty", {autoClose: 1800});
return;
}
if (weaviateURL.replace(/\s/g, '') === '') {
toast.error("Weaviate URL is empty", {autoClose: 1800});
return;
}
setConnectText("Connecting...");
const weaviateData = {
"name": databaseName,
"collections": collections,
"api_key": weaviateApiKey,
"url": weaviateURL,
}
connectWeaviate(weaviateData)
.then((response) => {
connectResponse(response.data);
})
.catch((error) => {
toast.error("Unable to connect database", {autoClose: 1800});
console.error('Error fetching vector databases:', error);
setConnectText("Connect");
});
}
}
const proceedAddDatabase = () => {
if (selectedDB === null) {
toast.error("Please select a database", {autoClose: 1800});
return;
}
setLocalStorageValue('add_database_tab_' + String(internalId), 'form_database', setActiveView)
}
return (<>
<div className="row">
<div className="col-3"></div>
{activeView === 'select_database' &&
<div className="col-6" style={{overflowY: 'scroll', height: 'calc(100vh - 92px)', padding: '25px 20px'}}>
<div className="title_wrapper">
<div className={agentStyles.page_title}>Choose a vector database</div>
</div>
<div className={knowledgeStyles.database_wrapper}>
{vectorDatabases?.map((item, index) => (
<div key={index}
style={item.name === selectedDB ? {border: '1px solid #9B9AA1'} : {border: '1px solid rgb(39, 35, 53)'}}
className={knowledgeStyles.database_container}
onClick={() => setLocalStorageValue('selected_db_' + String(internalId), item.name, setSelectedDB)}>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '20px'}}>
<Image width={40} height={40} src={returnDatabaseIcon(item.name)} alt=""/>
</div>
<div className={styles.text_block}
style={{width: '100%', marginBottom: '10px', textAlign: 'center'}}>{item.name}</div>
</div>))}
</div>
<div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '15px'}}>
<button onClick={() => removeTab(-7, "new database", "Add_Database", internalId)}
className="secondary_button" style={{marginRight: '10px'}}>
Cancel
</button>
<button className="primary_button" onClick={proceedAddDatabase}>
Proceed
</button>
</div>
</div>}
{activeView === 'form_database' &&
<div className="col-6" style={{overflowY: 'scroll', height: 'calc(100vh - 92px)', padding: '25px 20px'}}>
<div className={styles.back_button} style={{margin: '8px 0', padding: '2px'}}
onClick={() => setLocalStorageValue('add_database_tab_' + String(internalId), 'select_database', setActiveView)}>
<Image src="/images/arrow_back.svg" alt="back_button" width={14} height={12}/>
<span className={styles.back_button_text}>Back</span>
</div>
<div className="title_wrapper">
<div className={agentStyles.page_title}>Connect new vector database</div>
</div>
<div className="database_box">
<div style={{display: 'flex', justifyContent: 'flex-start', alignItems: 'center'}}>
<div style={{marginLeft: '15px'}}>
<Image src={returnDatabaseIcon(selectedDB)} alt="database-icon" width={40} height={40}/>
</div>
<div style={{marginLeft: '15px', fontSize: '14px', marginTop: '23px'}} className={agentStyles.page_title}>
<p>{selectedDB}</p></div>
</div>
</div>
<div style={{marginTop: '15px'}}>
<label className={styles1.form_label}>Name</label>
<input className="input_medium" type="text" value={databaseName} onChange={handleNameChange}/>
</div>
<div style={{marginTop: '15px'}}>
<div>{selectedDB === 'Weaviate' ? <label className={styles.form_label}>Class/Collection/Index</label> : <label className={styles.form_label}>Collection/Index</label>}</div>
{collections.map((collection, index) => (<div key={index} style={{
marginBottom: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<div style={{flex: '1'}}>
<input className="input_medium" type="text" value={collection}
onChange={(event) => handleCollectionChange(index, event.target.value)}/>
</div>
{collections.length > 1 && <div>
<button className="secondary_button" style={{marginLeft: '4px', padding: '5px'}}
onClick={() => handleCollectionDelete(index)}>
<Image width={20} height={21} src="/images/close.svg" alt="close-icon"/>
</button>
</div>}
</div>))}
<div>
<button className="secondary_button" onClick={addCollection}>+ Add</button>
</div>
</div>
{selectedDB === 'Pinecone' && <div>
<div style={{marginTop: '15px'}}>
<label className={styles1.form_label}>Pinecone API key</label>
<input className="input_medium" type="password" value={pineconeApiKey}
onChange={handlePineconeAPIKeyChange}/>
</div>
<div style={{marginTop: '15px'}}>
<label className={styles1.form_label}>Pinecone environment</label>
<input className="input_medium" type="text" value={pineconeEnvironment}
onChange={handlePineconeEnvironmentChange}/>
</div>
</div>}
{selectedDB === 'Qdrant' && <div>
<div style={{marginTop: '15px'}}>
<label className={styles1.form_label}>Qdrant API key</label>
<input className="input_medium" type="password" value={qdrantApiKey} onChange={handleQdrantAPIKeyChange}/>
</div>
<div style={{marginTop: '15px'}}>
<label className={styles1.form_label}>Qdrant URL</label>
<input className="input_medium" type="text" value={qdrantURL} onChange={handleQdrantURLChange}/>
</div>
<div style={{marginTop: '15px'}}>
<label className={styles1.form_label}>Port</label>
<input className="input_medium" type="number" value={qdrantPort} onChange={handleQdrantPortChange}/>
</div>
</div>}
{selectedDB === 'Weaviate' && <div>
<div className="mt_15">
<label className={styles1.form_label}>Weaviate API key</label>
<input className="input_medium" type="password" value={weaviateApiKey} onChange={handleWeaviateAPIKeyChange}/>
</div>
<div className="mt_15">
<label className={styles1.form_label}>Weaviate URL</label>
<input className="input_medium" type="text" value={weaviateURL} onChange={handleWeaviateURLChange}/>
</div>
</div>}
<div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '15px'}}>
<button onClick={() => removeTab(-7, "new database", "Add_Database", internalId)}
className="secondary_button" style={{marginRight: '10px'}}>
Cancel
</button>
<button className="primary_button" onClick={connectDatabase}>
{connectText}
</button>
</div>
</div>}
<div className="col-3"></div>
</div>
<ToastContainer/>
</>)
}
+286
View File
@@ -0,0 +1,286 @@
import React, {useState, useEffect, useRef} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import agentStyles from "@/pages/Content/Agents/Agents.module.css";
import {
createApiKey, deleteApiKey,
editApiKey, getApiKeys,
} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import {createInternalId, loadingTextEffect, preventDefault, removeTab, returnToolkitIcon} from "@/utils/utils";
import Image from "next/image";
import styles from "@/pages/Content/Marketplace/Market.module.css";
import styles1 from "@/pages/Content/Knowledge/Knowledge.module.css";
export default function ApiKeys() {
const [apiKeys, setApiKeys] = useState([]);
const [keyName, setKeyName] = useState('');
const [editKey, setEditKey] = useState('');
const apiKeyRef = useRef(null);
const editKeyRef = useRef(null);
const [editKeyId, setEditKeyId] = useState(-1);
const [deleteKey, setDeleteKey] = useState('')
const [isLoading, setIsLoading] = useState(true)
const [activeDropdown, setActiveDropdown] = useState(null);
const [editModal, setEditModal] = useState(false);
const [deleteKeyId, setDeleteKeyId] = useState(-1);
const [deleteModal, setDeleteModal] = useState(false);
const [createModal, setCreateModal] = useState(false);
const [displayModal, setDisplayModal] = useState(false);
const [apiKeyGenerated, setApiKeyGenerated] = useState('');
const [loadingText, setLoadingText] = useState("Loading Api Keys");
useEffect(() => {
loadingTextEffect('Loading Api Keys', setLoadingText, 500);
fetchApiKeys()
}, []);
const handleModelApiKey = (event) => {
setKeyName(event.target.value);
};
const handleEditApiKey = (event) => {
setEditKey(event.target.value);
};
const createApikey = () => {
if(!keyName){
toast.error("Enter key name", {autoClose: 1800});
return;
}
createApiKey({name : keyName})
.then((response) => {
setApiKeyGenerated(response.data.api_key)
toast.success("Api Key Generated", {autoClose: 1800});
setCreateModal(false);
setDisplayModal(true);
fetchApiKeys();
})
.catch((error) => {
console.error('Error creating api key', error);
});
}
const handleCopyClick = async () => {
if (apiKeyRef.current) {
try {
await navigator.clipboard.writeText(apiKeyRef.current.value);
toast.success("Key Copied", {autoClose: 1800});
} catch (err) {
toast.error('Failed to Copy', {autoClose: 1800});
}
}
};
const fetchApiKeys = () => {
getApiKeys()
.then((response) => {
const formattedData = response.data.map(item => {
return {
...item,
created_at: `${new Date(item.created_at).getDate()}-${["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"][new Date(item.created_at).getMonth()]}-${new Date(item.created_at).getFullYear()}`
};
});
setApiKeys(formattedData)
setIsLoading(false)
})
.catch((error) => {
console.error('Error fetching Api Keys', error);
});
}
const handleEditClick = () => {
if(editKeyRef.current.value.length <1){
toast.error("Enter valid key name", {autoClose: 1800});
return;
}
editApiKey({id: editKeyId,name : editKey})
.then((response) => {
toast.success("Api Key Edited", {autoClose: 1800});
fetchApiKeys();
setEditModal(false);
setEditKey('')
setEditKeyId(-1)
})
.catch((error) => {
console.error('Error editing api key', error);
});
}
const handleDeleteClick = () => {
deleteApiKey(deleteKeyId)
.then((response) => {
toast.success("Api Key Deleted", {autoClose: 1800});
fetchApiKeys();
setDeleteModal(false);
setDeleteKeyId(-1)
setDeleteKey('')
})
.catch((error) => {
toast.error("Error deleting api key", {autoClose: 1800});
console.error('Error deleting api key', error);
});
}
return (<>
<div className="row">
<div className="col-2"></div>
<div className="col-8 col-6-scrollable">
{!isLoading ? <div>
<div className="title_wrapper mb_15">
<div className={styles.page_title}>API Keys</div>
{apiKeys && apiKeys.length > 0 && !isLoading &&
<button className={`${'primary_button mr_20'} ${agentStyles.button_margin}`} onClick={() => {setCreateModal(true); setKeyName('')}}>
Create Key
</button>}
</div>
<div>
<label className={agentStyles.form_label}>Your secret API keys are important and should be kept safe. Do not share them with anyone or expose them in any case.</label>
<label className={agentStyles.form_label}>To help keep your API keys safe, you can store them in a secure location, rotate them regularly, and use different API keys for different applications. By following these tips, you can help protect your account and your data.</label>
{apiKeys.length < 1 && <div className={agentStyles.table_contents}>
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className={`${styles.feed_title} ${'mt_8'}`}>No API Keys created!</span>
<div className={agentStyles.create_settings_button}>
<button className="primary_button" onClick={() => {setCreateModal(true); setKeyName('')}}>Create Key
</button>
</div>
</div>}
{apiKeys.length > 0 && <div className="scrollable_container table_container">
<table className="table_css margin_0 padding_0">
<thead>
<tr className="border_top_none">
<th className="table_header w_60">Name</th>
<th className="table_header w_18">Key</th>
<th className="table_header w_18">Created Date</th>
<th className="table_header w_4"></th>
</tr>
</thead>
</table>
<div className="overflow_auto w_100">
<table className="table_css margin_0">
<tbody>
{apiKeys.map((item, index) => (
<tr key={index}>
<td className="table_data w_60">{item.name}</td>
<td className="table_data w_18">{item.key.slice(0, 2) + "****" + item.key.slice(-4)}</td>
<td className="table_data w_18">{item.created_at}</td>
<td className="table_data w_4 cursor_pointer" onMouseLeave={() => setActiveDropdown(null)} onClick={() => {
if (activeDropdown === index) {
setActiveDropdown(null);
} else {
setActiveDropdown(index);
}
}}>
<Image className="rotate_90" width={16} height={16} src="/images/three_dots.svg" alt="run-icon"/>
<div style={activeDropdown === index ? {display: 'block'} : {display: 'none'}} onMouseLeave={() => setActiveDropdown(null)}>
<ul className="dropdown_container">
<li className="dropdown_item" onClick={() => {setEditKey(item.name); setEditKeyId(item.id); setEditModal(true); setActiveDropdown(null);}}>Edit</li>
<li className="dropdown_item" onClick={() => {setDeleteKeyId(item.id); setDeleteKey(item.name) ; setDeleteModal(true); setActiveDropdown(null);}}>Delete</li>
</ul> </div></td>
</tr>
))}
</tbody>
</table>
</div>
</div>}
</div>
</div> : <div className="loading_container">
<div className="signInInfo loading_text">{loadingText}</div>
</div>}
</div>
<div className="col-2"></div>
</div>
{createModal && (<div className="modal" onClick={() => setCreateModal(false)}>
<div className="modal-content w_35" onClick={preventDefault}>
<div className={styles.detail_name}>Create new API Key</div>
<div>
<label className={styles.form_label}>Name</label>
<input placeholder="Enter key name" className="input_medium" type="text" value={keyName} onChange={handleModelApiKey}/>
</div>
<div className={agentStyles.modal_buttons}>
<button className="secondary_button mr_10" onClick={() => setCreateModal(false)}>
Cancel
</button>
<button className="primary_button" onClick={() => createApikey()}>
Create Key
</button>
</div>
</div>
</div>)}
{displayModal && apiKeyGenerated && (<div className="modal" onClick={() => setDisplayModal(false)}>
<div className="modal-content w_35" onClick={preventDefault}>
<div className={styles.detail_name}>{keyName} is created</div>
<div>
<div className="mt_15 mb_25">
<div className={styles1.knowledge_alert}>
<div className={agentStyles.modal_info_class}>
<Image width={20} height={20} src='/images/info.svg' alt="info-icon"/>
</div>
<div>
Your secret API keys are sensitive pieces of information that should be kept confidential. Do not share them with anyone, and do not expose them in any way. If your secret API keys are compromised, someone could use them to access your API and make unauthorized changes to your data. This secret key is only displayed once for security reasons. Please save it in a secure location where you can access it easily.
</div>
</div>
</div>
<div>
<div className="title_wrapper">
<div className="flex_1"><input ref={apiKeyRef} className="input_medium" type="text" value={apiKeyGenerated} disabled />
</div>
<div>
<button className="secondary_button ml_4 padding_5" onClick={handleCopyClick}>
<Image width={20} height={21} src="/images/copy_icon.svg" alt="copy-icon"/>
</button>
</div>
</div>
</div>
</div>
<div className={agentStyles.modal_buttons}>
<button className="primary_button" onClick={() => setDisplayModal(false)}>
OK
</button>
</div>
</div>
</div>)}
{editModal && (<div className="modal" onClick={() => {setEditModal(false); setEditKey(''); setEditKeyId(-1)}}>
<div className="modal-content w_35" onClick={preventDefault}>
<div className={styles.detail_name}>Edit API Key</div>
<div>
<label className={styles.form_label}>Name</label>
<input ref={editKeyRef} placeholder={editKey} className="input_medium" type="text" onChange={handleEditApiKey}/>
</div>
<div className={agentStyles.modal_buttons}>
<button className="secondary_button mr_10" onClick={() => {setEditModal(false); setEditKey(''); setEditKeyId(-1)}}>
Cancel
</button>
<button className="primary_button" onClick={() => handleEditClick()}>
Update Changes
</button>
</div>
</div>
</div>)}
{deleteModal && (<div className="modal" onClick={() => {setDeleteModal(false); setDeleteKeyId(-1); setDeleteKey('')}}>
<div className="modal-content w_35" onClick={preventDefault}>
<div className={styles.detail_name}>Delete {deleteKey} Key</div>
<div>
<label className={styles.form_label}>Deleting this API key will make it unusable. Any API requests made using this key will be rejected. Are you sure you want to proceed?</label>
</div>
<div className={agentStyles.modal_buttons}>
<button className="secondary_button mr_10" onClick={() => {setDeleteModal(false); setDeleteKeyId(-1); setDeleteKey('')}}>
Cancel
</button>
<button className="primary_button" onClick={() => handleDeleteClick()}>
Delete Key
</button>
</div>
</div>
</div>)}
<ToastContainer/>
</>)
}
+182
View File
@@ -0,0 +1,182 @@
import React, {useState, useEffect} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import agentStyles from "@/pages/Content/Agents/Agents.module.css";
import {createInternalId, formatTimeDifference, loadingTextEffect, preventDefault} from "@/utils/utils";
import styles from "@/pages/Content/Marketplace/Market.module.css";
import knowledgeStyles from "@/pages/Content/Knowledge/Knowledge.module.css";
import Image from "next/image";
import {deleteVectorDB, getVectorDatabases} from "@/pages/api/DashboardService";
export default function Database({sendDatabaseData}) {
const [vectorDB, setVectorDB] = useState([]);
const [isLoading, setIsLoading] = useState(true)
const [loadingText, setLoadingText] = useState("Loading Databases");
const [dropdown, setDropdown] = useState([]);
const [deleteModal, setDeleteModal] = useState(false);
const [selectedDatabase, setSelectedDatabase] = useState(null);
function fetchDatabases() {
setIsLoading(true);
getVectorDatabases()
.then((response) => {
const data = response.data || [];
setVectorDB(data);
setIsLoading(false);
})
.catch((error) => {
console.error('Error fetching vector databases:', error);
});
}
useEffect(() => {
loadingTextEffect('Loading Databases', setLoadingText, 500);
fetchDatabases();
}, []);
useEffect(() => {
setDropdown(Array(vectorDB.length).fill(false));
}, [vectorDB]);
const setDropdownWithIndex = (index, state) => {
setDropdown((prevDropdown) => {
const newDropdown = [...prevDropdown];
newDropdown[index] = state;
return newDropdown;
});
}
const openDeleteModal = (e, index) => {
e.stopPropagation();
setDeleteModal(true);
setSelectedDatabase(vectorDB[index]);
setDropdownWithIndex(index, false);
}
const deleteDatabase = (databaseId) => {
setDeleteModal(false);
deleteVectorDB(databaseId)
.then((response) => {
toast.success("Database deleted successfully", {autoClose: 1800});
fetchDatabases();
})
.catch((error) => {
toast.error("Unable to delete database", {autoClose: 1800});
console.error('Error fetching knowledge templates:', error);
});
}
return (<>
<div className="row">
<div className="col-2"></div>
<div className="col-8" style={{overflowY: 'scroll', height: 'calc(100vh - 92px)', padding: '25px 20px'}}>
<div className="title_wrapper">
<div className={agentStyles.page_title}>Vector Database</div>
{vectorDB && vectorDB.length > 0 && !isLoading &&
<button className="primary_button" onClick={() => sendDatabaseData({
id: -7,
name: "new database",
contentType: "Add_Database",
internalId: createInternalId()
})} style={{marginTop: '-10px', marginRight: '20px'}}>
Add
</button>}
</div>
<div>
<div className={styles.rowContainer} style={{maxHeight: '78vh', overflowY: 'auto'}}>
{!isLoading ? <div>
{vectorDB && vectorDB.length > 0 ? <div className={knowledgeStyles.database_wrapper}>
{vectorDB.map((item, index) => (<div key={index} className={knowledgeStyles.database_container}
onClick={() => sendDatabaseData({
id: item.id,
name: item.name,
contentType: "Database",
internalId: createInternalId()
})}>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}>
<div style={{display: 'flex', order: '0'}}>
<div className={styles.text_block}>{item.name}</div>
</div>
<div style={{order: '1'}}>
<button className="three_dots_vertical" style={{padding: '8px', height: '31px'}}
onMouseEnter={() => setDropdownWithIndex(index, true)}
onMouseLeave={() => setDropdownWithIndex(index, false)}>
<Image width={14} height={14} src="/images/three_dots_vertical.svg" alt="run-icon"/>
</button>
{dropdown[index] && <div onMouseEnter={() => setDropdownWithIndex(index, true)}
onMouseLeave={() => setDropdownWithIndex(index, false)}>
<ul className="dropdown_container" style={{marginLeft: '-15px'}}>
<li className="dropdown_item" onClick={(e) => openDeleteModal(e, index)}>Delete database</li>
</ul>
</div>}
</div>
</div>
<div style={{display: 'flex', alignItems: 'center', justifyContent: 'flex-start'}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<div>
<Image width={12} height={12} src="/images/stack.svg" alt="database-icon"/>
</div>
<div className={styles.history_info}>
{item.db_type}
</div>
</div>
<div style={{display: 'flex', alignItems: 'center', marginLeft: '10px'}}>
<div>
<Image width={12} height={12} src="/images/schedule.svg" alt="schedule-icon"/>
</div>
<div className={styles.history_info}>
Added {formatTimeDifference(item.updated_at)}
</div>
</div>
</div>
</div>))}</div> : <div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
marginTop: '40px',
width: '100%'
}}>
<Image width={150} height={60} src="/images/no_permissions.svg" alt="no-permissions"/>
<span className={styles.feed_title} style={{marginTop: '8px'}}>No vector database added!</span>
<div style={{display: 'flex', justifyContent: 'center', alignItems: 'center', marginTop: '10px'}}>
<button className="primary_button" onClick={() => sendDatabaseData({
id: -7,
name: "new database",
contentType: "Add_Database",
internalId: createInternalId()
})}>Add
</button>
</div>
</div>}
</div> : <div style={{display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50vh'}}>
<div className="signInInfo" style={{fontSize: '16px', fontFamily: 'Source Code Pro'}}>{loadingText}</div>
</div>}
</div>
</div>
</div>
<div className="col-2"></div>
</div>
{deleteModal && (<div className="modal" onClick={() => setDeleteModal(false)}>
<div className="modal-content" style={{width: '35%'}} onClick={preventDefault}>
<div className={styles.detail_name}>Delete {selectedDatabase.name}</div>
<div>
<label className={styles.form_label}>Deleting database will delete all the corresponding knowledge also. Do
you want to delete database?</label>
</div>
<div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '20px'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={() => setDeleteModal(false)}>
Cancel
</button>
<button className="primary_button" onClick={() => deleteDatabase(selectedDatabase.id)}>
Delete
</button>
</div>
</div>
</div>)}
<ToastContainer/>
</>)
}
@@ -0,0 +1,204 @@
import React, {useState, useEffect} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import agentStyles from "@/pages/Content/Agents/Agents.module.css";
import {removeTab, returnDatabaseIcon, setLocalStorageArray, preventDefault} from "@/utils/utils";
import knowledgeStyles from "@/pages/Content/Knowledge/Knowledge.module.css";
import styles from "@/pages/Content/Marketplace/Market.module.css";
import Image from "next/image";
import {deleteVectorDB, getVectorDBDetails, updateVectorDB} from "@/pages/api/DashboardService";
export default function DatabaseDetails({internalId, databaseId}) {
const [dropdown, setDropdown] = useState(false);
const [deleteModal, setDeleteModal] = useState(false);
const [collections, setCollections] = useState([]);
const [initialCollections, setInitialCollections] = useState([]);
const [hasChanges, setHasChanges] = useState(false);
const [databaseDetails, setDatabaseDetails] = useState([]);
useEffect(() => {
if (databaseId) {
getVectorDBDetails(databaseId)
.then((response) => {
const data = response.data || [];
setDatabaseDetails(data);
if (data) {
const localIndices = localStorage.getItem("db_details_collections_" + String(internalId));
const indices = data.indices || [];
setCollections(localIndices ? JSON.parse(localIndices) : indices);
setInitialCollections(indices);
}
})
.catch((error) => {
console.error('Error deleting database:', error);
});
}
}, [internalId]);
useEffect(() => {
if (JSON.stringify(collections) !== JSON.stringify(initialCollections)) {
setHasChanges(true);
} else {
setHasChanges(false);
}
}, [collections]);
const addCollection = () => {
setLocalStorageArray("db_details_collections_" + String(internalId), [...collections, 'collection name'], setCollections);
};
const handleCollectionChange = (index, newValue) => {
const updatedCollections = [...collections];
updatedCollections[index] = newValue;
setLocalStorageArray("db_details_collections_" + String(internalId), updatedCollections, setCollections);
};
const handleCollectionDelete = (index) => {
const updatedCollections = [...collections];
updatedCollections.splice(index, 1);
setLocalStorageArray("db_details_collections_" + String(internalId), updatedCollections, setCollections);
};
const deleteDatabase = () => {
setDeleteModal(false);
deleteVectorDB(databaseId)
.then((response) => {
toast.success("Database deleted successfully", {autoClose: 1800});
removeTab(databaseId, databaseDetails?.name, "Database", internalId);
})
.catch((error) => {
toast.error("Unable to delete database", {autoClose: 1800});
console.error('Error deleting database:', error);
});
}
const revertChanges = () => {
setCollections(initialCollections);
setHasChanges(false);
};
const updateChanges = () => {
updateVectorDB(databaseId, collections)
.then((response) => {
toast.success("Database updated successfully", {autoClose: 1800});
setInitialCollections(collections);
setHasChanges(false);
})
.catch((error) => {
toast.error("Unable to update database", {autoClose: 1800});
console.error('Error fetching knowledge templates:', error);
});
};
return (<>
<div className="row">
<div className="col-3"></div>
<div className="col-6" style={{overflowY: 'scroll', height: 'calc(100vh - 92px)', padding: '25px 20px'}}>
<div className="title_wrapper">
<div className={agentStyles.page_title}>{databaseDetails?.name}</div>
<div>
<button className="secondary_button" style={{padding: '8px', height: '31px', marginTop: '-20px'}}
onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<Image width={14} height={14} src="/images/three_dots.svg" alt="run-icon"/>
</button>
{dropdown && <div onMouseEnter={() => setDropdown(true)} onMouseLeave={() => setDropdown(false)}>
<ul className="dropdown_container">
<li className="dropdown_item" onClick={() => {
setDropdown(false);
setDeleteModal(true)
}}>Delete database
</li>
</ul>
</div>}
</div>
</div>
<div className="database_box">
<div style={{display: 'flex', justifyContent: 'flex-start', order: '0', alignItems: 'center'}}>
<div style={{marginLeft: '15px'}}>
<Image src={returnDatabaseIcon(databaseDetails?.db_type)} alt="database-icon" width={40} height={40}/>
</div>
<div style={{marginLeft: '15px', fontSize: '14px', marginTop: '23px'}}
className={agentStyles.page_title}>{databaseDetails?.db_type}</div>
</div>
</div>
<div style={{marginTop: '15px'}}>
<div><label className={styles.form_label}>Collection/Index</label></div>
{collections.map((collection, index) => (<div key={index} style={{
marginBottom: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<div style={{flex: '1'}}>
<input className="input_medium" type="text" value={collection}
onChange={(event) => handleCollectionChange(index, event.target.value)}/>
</div>
{collections.length > 1 && <div>
<button className="secondary_button" style={{marginLeft: '4px', padding: '5px'}}
onClick={() => handleCollectionDelete(index)}>
<Image width={20} height={21} src="/images/close.svg" alt="close-icon"/>
</button>
</div>}
</div>))}
<div>
<button className="secondary_button" onClick={addCollection}>+ Add</button>
</div>
</div>
{databaseDetails?.db_type === 'Pinecone' && <div>
<div style={{marginTop: '15px'}}>
<label className={knowledgeStyles.knowledge_label}>Pinecone API key</label>
<div className={knowledgeStyles.knowledge_info}>{databaseDetails?.api_key}</div>
</div>
<div style={{marginTop: '15px'}}>
<label className={knowledgeStyles.knowledge_label}>Pinecone environment</label>
<div className={knowledgeStyles.knowledge_info}>{databaseDetails?.environment}</div>
</div>
</div>}
{databaseDetails?.db_type === 'Qdrant' && <div>
<div style={{marginTop: '15px'}}>
<label className={knowledgeStyles.knowledge_label}>Qdrant API key</label>
<div className={knowledgeStyles.knowledge_info}>{databaseDetails?.api_key}</div>
</div>
<div style={{marginTop: '15px'}}>
<label className={knowledgeStyles.knowledge_label}>Qdrant URL</label>
<div className={knowledgeStyles.knowledge_info}>{databaseDetails?.url}</div>
</div>
<div style={{marginTop: '15px'}}>
<label className={knowledgeStyles.knowledge_label}>Port</label>
<div className={knowledgeStyles.knowledge_info}>{databaseDetails?.port}</div>
</div>
</div>}
{hasChanges && <div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '15px'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={revertChanges}>
Cancel
</button>
<button className="primary_button" onClick={updateChanges}>
Update
</button>
</div>}
</div>
<div className="col-3"></div>
</div>
{deleteModal && (<div className="modal" onClick={() => setDeleteModal(false)}>
<div className="modal-content" style={{width: '35%'}} onClick={preventDefault}>
<div className={styles.detail_name}>Delete {databaseDetails?.name}</div>
<div>
<label className={styles.form_label}>Deleting database will delete all the corresponding knowledge also. Do
you want to delete database?</label>
</div>
<div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '20px'}}>
<button className="secondary_button" style={{marginRight: '10px'}} onClick={() => setDeleteModal(false)}>
Cancel
</button>
<button className="primary_button" onClick={deleteDatabase}>
Delete
</button>
</div>
</div>
</div>)}
<ToastContainer/>
</>)
}
+118
View File
@@ -0,0 +1,118 @@
import React, {useState, useEffect, useRef} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import agentStyles from "@/pages/Content/Agents/Agents.module.css";
import {storeApiKey, fetchApiKeys, validateLLMApiKey, fetchApiKey} from "@/pages/api/DashboardService";
import {EventBus} from "@/utils/eventBus";
import {getUserClick, removeTab} from "@/utils/utils";
import Image from "next/image";
export default function Model({organisationId}) {
const [temperature, setTemperature] = useState(0.5);
const [models, setModels] = useState([
{'name':'Open AI API key','logo':'/images/openai_logo.svg','source':'OpenAi', 'api_key': ''},
{'name':'Hugging Face auth token','logo':'/images/huggingface_logo.svg','source':'Hugging Face', 'api_key': ''},
{'name':'Replicate auth token','logo':'/images/replicate_logo.svg','source':'Replicate', 'api_key': ''},
{'name':'Google Palm API key','logo':'/images/google_palm_logo.svg','source':'Google Palm', 'api_key': ''}
]);
const [updatedModels, setUpdatedModels] = useState([]);
useEffect(() => {
fetchApiKeys().then((response) => {
if(response.data.length > 0) {
response.data.forEach(item => {
const index = models.findIndex(model => model.source.toLowerCase() === item.provider.toLowerCase());
if (index !== -1) {
const newModels = [...models];
newModels[index].api_key = item.api_key;
setModels(newModels);
}
});
}
})
},[])
const saveSettings = () => {
updatedModels.forEach(model => {
if (model.api_key === null || model.api_key.replace(/\s/g, '') === '') {
toast.error("API key is empty", {autoClose: 1800});
return
}
validateLLMApiKey(model.source, model.api_key)
.then((response) => {
if (response.data.status === "success") {
storeKey(model.source, model.api_key)
}
else {
toast.error(`Invalid API key for ${model.source}`, {autoClose: 1800});
}
});
});
};
const storeKey = (model_provider, api_key) => {
if(model_provider === 'OpenAi')
model_provider = 'OpenAI'
storeApiKey(model_provider,api_key).then((response) => {
if(response.status === 200) {
getUserClick('API Key Updated', {'Model': model_provider})
toast.success(`Successfully Stored the API Key of ${model_provider}`, {autoClose: 1800})
}
else
toast.error("Error", {autoClose: 1800})
})
}
const handleInputChange = (source, value) => {
const updatedModel = models.find(model => model.source === source);
if (updatedModel) {
updatedModel.api_key = value;
setUpdatedModels(prevModels => {
const existingIndex = prevModels.findIndex(model => model.source === source);
if (existingIndex !== -1) {
return [
...prevModels.slice(0, existingIndex),
updatedModel,
...prevModels.slice(existingIndex + 1)
];
} else {
return [...prevModels, updatedModel];
}
});
}
};
useEffect(() => {
console.log(updatedModels)
},[updatedModels])
const handleTemperatureChange = (event) => {
setTemperature(event.target.value);
};
return (
<>
<div className="row">
<div className="col-3"></div>
<div className="col-6 col-6-scrollable">
{models.map(model => (
<div key={model.name}>
<div className="horizontal_container align_center mt_24 gap_8">
<Image width={16} height={16} src={model.logo} alt={`${model.name}-icon`} />
<span className="text_13 color_gray">{model.name}</span>
</div>
<input placeholder={`Enter your ${model.name}`} className="input_medium mt_8" type="password" value={model.api_key}
onChange={(event) => handleInputChange(model.source, event.target.value)}/>
</div>
))}
{updatedModels.length > 0 && <div style={{display: 'flex', justifyContent: 'flex-end', marginTop: '15px'}}>
<button onClick={() => removeTab(-3, "Settings", "Settings", 0)} className="secondary_button mr_10">Cancel</button>
<button className="primary_button" onClick={saveSettings}>Update Changes</button>
</div>}
</div>
<div className="col-3"></div>
</div>
<ToastContainer/>
</>
)
}
+57
View File
@@ -0,0 +1,57 @@
import React, {useState, useEffect} from 'react';
import 'react-toastify/dist/ReactToastify.css';
import styles from "@/pages/Content/Marketplace/Market.module.css";
import Image from "next/image";
import Model from "@/pages/Dashboard/Settings/Model";
import Database from "@/pages/Dashboard/Settings/Database";
import ApiKeys from "@/pages/Dashboard/Settings/ApiKeys";
import Webhooks from "@/pages/Dashboard/Settings/Webhooks";
export default function Settings({organisationId, sendDatabaseData}) {
const [activeTab, setActiveTab] = useState('model');
useEffect(() => {
const settings_tab = localStorage.getItem('settings_tab');
if (settings_tab) {
setActiveTab(settings_tab);
}
}, []);
const switchTab = (tab) => {
setActiveTab(tab);
localStorage.setItem('settings_tab', tab);
};
return (<>
<div className={styles.empty_state}>
<div className="vertical_containers w_100">
<div className={styles.detail_top}>
<div style={{display: 'flex', overflowX: 'scroll', marginLeft: '8px', gap:'4px'}}>
<button onClick={() => switchTab('model')} className={activeTab === 'model' ? 'tab_button_selected' : 'tab_button'}>
<Image width={14} height={14} src="/images/model_light.svg" alt="model-icon"/>
<span>Model Providers</span>
</button>
<button onClick={() => switchTab('database')} className={activeTab === 'database' ? 'tab_button_selected' : 'tab_button'}>
<Image width={14} height={14} src="/images/database.svg" alt="database-icon"/>
<span>Database</span>
</button>
<button onClick={() => switchTab('apikeys')} className={activeTab === 'apikeys' ? 'tab_button_selected' : 'tab_button'}>
<Image width={14} height={14} src="/images/key_white.svg" alt="api-key-icon"/>
<span>API Keys</span>
</button>
<button onClick={() => switchTab('webhooks')} className={activeTab === 'webhooks' ? 'tab_button_selected' : 'tab_button'}>
<Image className={styles.settings_tab_img} width={14} height={14} src="/images/webhook_icon.svg"
alt="database-icon"/>&nbsp;Webhooks
</button>
</div>
</div>
<div>
{activeTab === 'model' && <Model organisationId={organisationId}/>}
{activeTab === 'database' && <Database sendDatabaseData={sendDatabaseData} organisationId={organisationId}/>}
{activeTab === 'apikeys' && <ApiKeys />}
{activeTab === 'webhooks' && <Webhooks />}
</div>
</div>
</div>
</>)
}
+148
View File
@@ -0,0 +1,148 @@
import React, {useState, useEffect} from 'react';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import agentStyles from "@/pages/Content/Agents/Agents.module.css";
import {
editWebhook,
getWebhook, saveWebhook,
} from "@/pages/api/DashboardService";
import {loadingTextEffect, removeTab} from "@/utils/utils";
import styles from "@/pages/Content/Marketplace/Market.module.css";
export default function Webhooks() {
const [webhookUrl, setWebhookUrl] = useState('');
const [webhookId, setWebhookId] = useState(-1);
const [isLoading, setIsLoading] = useState(true)
const [existingWebhook, setExistingWebhook] = useState(false)
const [isEdtiting, setIsEdtiting] = useState(false)
const [loadingText, setLoadingText] = useState("Loading Webhooks");
const [selectedCheckboxes, setSelectedCheckboxes] = useState([]);
const checkboxes = [
{ label: 'Agent is running', value: 'RUNNING' },
{ label: 'Agent run is paused', value: 'PAUSED' },
{ label: 'Agent run is completed', value: 'COMPLETED' },
{ label: 'Agent is terminated ', value: 'TERMINATED' },
{ label: 'Agent run max iteration reached', value: 'MAX ITERATION REACHED' },
];
useEffect(() => {
loadingTextEffect('Loading Webhooks', setLoadingText, 500);
fetchWebhooks();
}, []);
const handleWebhookChange = (event) => {
setWebhookUrl(event.target.value);
};
const handleSaveWebhook = () => {
if(!webhookUrl || webhookUrl.trim() === ""){
toast.error("Enter valid webhook", {autoClose: 1800});
return;
}
if(isEdtiting){
editWebhook(webhookId, { url: webhookUrl, filters: {status: selectedCheckboxes}})
.then((response) => {
setIsEdtiting(false)
fetchWebhooks()
toast.success("Webhook edited successfully", {autoClose: 1800});
})
.catch((error) => {
console.error('Error fetching webhook', error);
});
return;
}
saveWebhook({name : "Webhook 1", url: webhookUrl, headers: {}, filters: {status: selectedCheckboxes}})
.then((response) => {
setExistingWebhook(true)
setWebhookId(response.data.id)
toast.success("Webhook created successfully", {autoClose: 1800});
})
.catch((error) => {
toast.error("Unable to create webhook", {autoClose: 1800});
console.error('Error saving webhook', error);
});
}
const fetchWebhooks = () => {
getWebhook()
.then((response) => {
setIsLoading(false)
if(response.data){
setWebhookUrl(response.data.url)
setExistingWebhook(true)
setWebhookId(response.data.id)
setSelectedCheckboxes(response.data.filters.status)
}
else{
setWebhookUrl('')
setExistingWebhook(false)
setWebhookId(-1)
}
})
.catch((error) => {
console.error('Error fetching webhook', error);
});
}
const toggleCheckbox = (value) => {
if (selectedCheckboxes.includes(value)) {
setSelectedCheckboxes(selectedCheckboxes.filter((item) => item !== value));
} else {
setSelectedCheckboxes([...selectedCheckboxes, value]);
}
};
return (<>
<div className="row">
<div className="col-3"></div>
<div className="col-6 col-6-scrollable">
{!isLoading ? <div>
<div className="title_wrapper mb_15">
<div className={styles.page_title}>Webhooks</div>
{existingWebhook &&
<button className="primary_button" onClick={() => {setExistingWebhook(false);setIsEdtiting(true)} } >
Edit
</button>}
</div>
<div>
<label className={agentStyles.form_label}>Destination URL</label>
<input disabled={existingWebhook ? true : false} className="input_medium" placeholder="Enter your destination url" type="text" value={webhookUrl}
onChange={handleWebhookChange}/>
<br />
<label className={agentStyles.form_label}>Events to include</label>
<div className={styles.checkboxGroup} >
{checkboxes.map((checkbox) => (
<label key={checkbox.value} className={styles.checkboxLabel}>
<input
disabled={existingWebhook ? true : false}
className="checkbox"
type="checkbox"
value={checkbox.value}
checked={selectedCheckboxes.includes(checkbox.value)}
onChange={() => toggleCheckbox(checkbox.value)}
/>
<span className={styles.checkboxText}>&nbsp;{checkbox.label}</span>
</label>
))}
</div>
</div>
{!existingWebhook && <div className="justify_end display_flex_container mt_15">
<button onClick={() => removeTab(-3, "Settings", "Settings", 0)} className="secondary_button mr_10">
Cancel
</button>
<button className="primary_button" onClick={handleSaveWebhook}>
{isEdtiting ? "Update" : "Create"}
</button>
</div>}
</div> : <div className="loading_container">
<div className="signInInfo loading_text">{loadingText}</div>
</div>}
</div>
<div className="col-3"></div>
</div>
<ToastContainer/>
</>)
}
+44
View File
@@ -0,0 +1,44 @@
import React, {useState} from 'react';
import Image from 'next/image';
import styles from './Dashboard.module.css';
import {getUserClick, openNewTab} from "@/utils/utils";
export default function SideBar({onSelectEvent, env}) {
const [sectionSelected, setSelection] = useState('');
const sections = [
{ name: 'agents', icon: '/images/agents_light.svg' },
{ name: 'toolkits', icon: '/images/tools_light.svg' },
{ name: 'apm', icon: '/images/apm.svg' },
{ name: 'knowledge', icon: '/images/knowledge.svg' },
{ name: 'models', icon: '/images/models.svg'},
];
const handleClick = (value) => {
getUserClick(value + "SIDEBAR ICON", {})
setSelection(value);
onSelectEvent(value);
if (value === 'apm') {
openNewTab(-9, "APM", "APM", false);
}
};
return (
<div className="side_bar">
<Image width={72} height={56} className="cursor_default mt_4 mb_4"
src={env === 'PROD' ? '/images/superagi_logo_beta.png' : '/images/superagi_logo.png'} alt="super-agi-logo"/>
{sections.map((section) => (
<div key={section.name} className="w_100 mb_5">
<div onClick={() => handleClick(sectionSelected !== section.name ? section.name : '')}
className={`${styles.section} ${sectionSelected === section.name ? styles.selected : ''}`}>
<div className={styles.button_icon}>
<Image width={17} height={17} src={section.icon} alt={`${section.name}-icon`}/>
</div>
{section.name === 'apm' ? <div>APM</div> : <div>{section.name.charAt(0).toUpperCase() + section.name.slice(1)}</div>}
</div>
</div>
))}
</div>
);
}
+115
View File
@@ -0,0 +1,115 @@
import React, {useState} from 'react';
import Image from 'next/image';
import styles from './Dashboard.module.css';
import {useRouter} from 'next/router';
import {ToastContainer, toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import {refreshUrl, openNewTab, getUserClick} from "@/utils/utils";
import Cookies from 'js-cookie';
export default function TopBar({selectedProject, userName, env}) {
const [dropdown, setDropdown] = useState(false);
const router = useRouter();
const [showDropdown, setShowDropdown] = useState(false)
const [selectedImagePath, setSelectedImagePath] = useState('/images/agents_icon_dropdown.svg')
const [selectedOption, setSelectedOption] = useState('Agents')
const logoutUser = () => {
setDropdown(false);
if (typeof window === 'undefined') {
return;
}
getUserClick('Logged Out',{})
// localStorage.removeItem('accessToken');
Cookies.set('accessToken', '', { expires: new Date(0),domain: '.superagi.com', path: '/'});
Cookies.set('Source', 'app.superagi', {domain: '.superagi.com', path: '/'});
Cookies.set('mixpanel_initialized', 'false', {domain: '.superagi.com', path: '/'});
refreshUrl();
router.reload();
};
function handleClick (option) {
if (option === 'Models') {
setSelectedImagePath("/images/models_icon_dropdown.svg")
setSelectedOption('Models')
window.location.href = 'https://models.superagi.com'
} else {
setSelectedImagePath("/images/agents_icon_dropdown.svg")
setSelectedOption('Agents')
}
setShowDropdown(false)
}
return (
<div className="top_bar">
<div className="top_left">
<div className="top_bar_section cursor_default">
{env === 'PROD' && false && <div className="horizontal_container">
<div onClick={() => setShowDropdown(!showDropdown)} className="horizontal_container align-middle cursor-pointer">
<Image className="mr_8" width={20} height={20} src={selectedImagePath} alt="models-icon" />
<span className="text_dropdown text_dropdown_18">{selectedOption}</span>
<Image className="ml_8" width={14} height={14} src="/images/arrow_down.svg" alt="down_arrow" />
</div>
{showDropdown && <div className="dropdown_container_models mt_130">
<ul className="padding_0 margin_0">
<li className="dropdown_item text_dropdown_15" onClick={() => handleClick('Models')}>
<Image className="mr_8" width={20} height={20} src="/images/models_icon_dropdown.svg" alt="models-icon" />
<span className="text_dropdown">Models</span>
</li>
<li className="dropdown_item text_dropdown_15" onClick={() => handleClick('Agents')}>
<Image className="mr_8" width={20} height={20} src="/images/agents_icon_dropdown.svg" alt="agents-icon" />
<span className="text_dropdown">Agents</span>
</li>
</ul>
</div>}
</div>}
</div>
{env === 'PROD' && false && <div className="vertical_divider ml_12 mr_20 responsiveness" />}
<div className="top_bar_section top_bar_input cursor_default">
<div className="horizontal_container">
<Image width={14} height={14} src="/images/project.svg" alt="project-icon"/>
<div className="top_bar_font">{selectedProject?.name || ''}</div>
</div>
</div>
<div className="top_bar_section ml_7 cursor_pointer">
<Image width={14} height={14} src="/images/widgets.svg" alt="widgets-icon"/>
<div className="top_bar_font" onClick={() => openNewTab(-4, "Marketplace", "Marketplace", false)}>Marketplace</div>
</div>
</div>
<div className="top_right">
<div className="horizontal_container gap_20">
<div className="horizontal_container w_fit_content cursor_pointer gap_4" onClick={() => {window.open("https://superagi.com/docs", "_blank"); getUserClick('SuperAGI Docs Visited', {})}}>
<Image width={20} height={20} src="/images/docs_icon.svg" alt="docs-icon" />
<p className="top_bar_font">Docs</p>
</div>
<div className="horizontal_container w_fit_content cursor_pointer gap_4" onClick={() => window.open("https://discord.com/invite/dXbRe5BHJC", "_blank")}>
<Image width={20} height={20} src="/images/discord.svg" alt="discord-icon" />
<p className="top_bar_font">Get Help</p>
</div>
<div className="horizontal_container w_fit_content cursor_pointer gap_4" onClick={() => window.open("https://github.com/TransformerOptimus/SuperAGI", "_blank")}>
<Image width={20} height={20} src="/images/github_white.svg" alt="github-icon" />
<p className="top_bar_font">Github</p>
</div>
</div>
<div className="horizontal_bar mr_22 ml_22" />
<Image onClick={() => {openNewTab(-3, "Settings", "Settings", false); getUserClick('Settings Viewed', {})}} className="top_right_icon" width={16} height={16} src="/images/settings.svg" alt="dropdown-icon"/>
<div className="top_right_icon" onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}>
<Image width={20} height={20} src="/images/profile_pic.png" alt="dropdown-icon"/>
</div>
{dropdown && env === 'PROD' &&
<div className="top_bar_profile_dropdown mt_30" onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}>
<ul className="dropdown_container w_120p">
{userName && <li className="dropdown_item" onClick={() => setDropdown(false)}>{userName}</li>}
<li className="dropdown_item" onClick={logoutUser}>Logout</li>
</ul>
</div>}
</div>
<ToastContainer/>
</div>
)
}
+1897
View File
File diff suppressed because it is too large Load Diff
+315
View File
@@ -0,0 +1,315 @@
import React, {useState, useEffect} from 'react';
import SideBar from './Dashboard/SideBar';
import Content from './Dashboard/Content';
import TopBar from './Dashboard/TopBar';
import 'bootstrap/dist/css/bootstrap.css';
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
import './_app.css'
import Head from 'next/head';
import Image from "next/image";
import {
getOrganisation,
getProject,
validateAccessToken,
checkEnvironment,
addUser,
installToolkitTemplate, installAgentTemplate, installKnowledgeTemplate, getFirstSignup
} from "@/pages/api/DashboardService";
import {githubClientId, mixpanelId} from "@/pages/api/apiConfig";
import {
getGithubClientId
} from "@/pages/api/DashboardService";
import {useRouter} from 'next/router';
import querystring from 'querystring';
import {refreshUrl, loadingTextEffect, getUTMParametersFromURL, setLocalStorageValue, getUserClick, sendGAEvent} from "@/utils/utils";
import MarketplacePublic from "./Content/Marketplace/MarketplacePublic"
import {toast} from "react-toastify";
import mixpanel from 'mixpanel-browser';
import Cookies from 'js-cookie';
export default function App() {
const [selectedView, setSelectedView] = useState('');
const [applicationState, setApplicationState] = useState("LOADING");
const [selectedProject, setSelectedProject] = useState(null);
const [userName, setUserName] = useState('');
const [organisationId, setOrganisationId] = useState(null);
const [env, setEnv] = useState('DEV');
const [loadingText, setLoadingText] = useState("Initializing SuperAGI");
const router = useRouter();
const [showMarketplace, setShowMarketplace] = useState(false);
const excludedKeys = [
'repo_starred',
'popup_closed_time',
'twitter_toolkit_id',
'accessToken',
'agent_to_install',
'toolkit_to_install',
'google_calendar_toolkit_id',
'knowledge_to_install',
'knowledge_index_to_install',
'myLayoutKey'
];
function fetchOrganisation(userId) {
getOrganisation(userId)
.then((response) => {
setOrganisationId(response.data.id);
})
.catch((error) => {
console.error('Error fetching project:', error);
});
}
const installFromMarketplace = () => {
const toolkitName = localStorage.getItem('toolkit_to_install') || null;
const agentTemplateId = localStorage.getItem('agent_to_install') || null;
const knowledgeTemplateName = localStorage.getItem('knowledge_to_install') || null;
const knowledgeIndexId = localStorage.getItem('knowledge_index_to_install') || null;
if (knowledgeTemplateName !== null && knowledgeIndexId !== null) {
installKnowledgeTemplate(knowledgeTemplateName, knowledgeIndexId)
.then((response) => {
toast.success("Template installed", {autoClose: 1800});
})
.catch((error) => {
console.error('Error installing template:', error);
});
localStorage.removeItem('knowledge_to_install');
localStorage.removeItem('knowledge_index_to_install');
}
if (toolkitName !== null) {
installToolkitTemplate(toolkitName)
.then((response) => {
toast.success("Template installed", {autoClose: 1800});
})
.catch((error) => {
console.error('Error installing template:', error);
});
localStorage.removeItem('toolkit_to_install');
}
if (agentTemplateId !== null) {
installAgentTemplate(agentTemplateId)
.then((response) => {
toast.success("Template installed", {autoClose: 1800});
})
.catch((error) => {
console.error('Error installing template:', error);
});
localStorage.removeItem('agent_to_install');
}
}
useEffect(() => {
handleMarketplace()
loadingTextEffect('Initializing SuperAGI', setLoadingText, 500);
checkEnvironment()
.then((response) => {
const env = response.data.env;
setEnv(env);
const mixpanelInitialized = Cookies.get('mixpanel_initialized') === 'true'
if (typeof window !== 'undefined') {
if(response.data.env === 'PROD' && mixpanelId()) {
mixpanel.init(mixpanelId(), {debug: false, track_pageview: !mixpanelInitialized, persistence: 'localStorage'});
}
localStorage.setItem('applicationEnvironment', env);
}
if (response.data.env === 'PROD') {
setApplicationState("NOT_AUTHENTICATED");
const queryParams = router.asPath.split('?')[1];
const parsedParams = querystring.parse(queryParams);
let access_token = parsedParams.access_token || null;
let first_login = parsedParams.first_time_login || ''
const utmParams = getUTMParametersFromURL();
if (utmParams) {
sessionStorage.setItem('utm_source', utmParams.utm_source);
sessionStorage.setItem('utm_medium', utmParams.utm_medium);
sessionStorage.setItem('campaign', utmParams.utm_campaign);
}
const signupSource = sessionStorage.getItem('utm_source');
const signupMedium = sessionStorage.getItem('utm_medium');
const singupCampaign = sessionStorage.getItem('campaign');
if (typeof window !== 'undefined' && access_token) {
// localStorage.setItem('accessToken', access_token);
Cookies.set('accessToken', access_token, {domain: '.superagi.com', path: '/'});
refreshUrl();
}
validateAccessToken()
.then((response) => {
setUserName(response.data.name || '');
sendGAEvent(response.data.email, 'Signed Up Successfully', {'utm_source': signupSource || '', 'utm_medium': signupMedium || '', 'campaign': singupCampaign || ''})
if(mixpanelId())
mixpanel.identify(response.data.email)
if(first_login === 'True') {
getUserClick('New Sign Up', {})
}
else {
if (first_login === 'False')
getUserClick('User Logged In', {})
}
if(signupSource) {
handleSignUpSource(signupSource)
}
fetchOrganisation(response.data.id);
Cookies.set('mixpanel_initialized', 'true', {domain: '.superagi.com', path: '/'});
})
.catch((error) => {
console.error('Error validating access token:', error);
});
} else {
handleLocalEnviroment()
}
})
.catch((error) => {
console.error('Error fetching project:', error);
});
}, []);
useEffect(() => {
if (organisationId !== null) {
getProject(organisationId)
.then((response) => {
setSelectedProject(response.data[0]);
})
.catch((error) => {
console.error('Error fetching project:', error);
});
}
}, [organisationId]);
useEffect(() => {
if (selectedProject !== null) {
const source = Cookies.get('Source')
if (source === 'models.superagi')
window.open('https://models.superagi.com/', '_self');
else
setApplicationState("AUTHENTICATED");
}
}, [selectedProject]);
const handleSelectionEvent = (data) => {
setSelectedView(data);
};
async function signInUser() {
let github_client_id = githubClientId();
// If `github_client_id` does not exist, make the API call
if (!github_client_id) {
const response = await getGithubClientId();
github_client_id = response.data.github_client_id;
}
if(!github_client_id) {
console.error('Error fetching github client id make sure to set it in the config file');
}
else {
window.open(`https://github.com/login/oauth/authorize?scope=user:email&client_id=${github_client_id}`, '_self')
}
}
const handleLocalEnviroment = () => {
const userData = {
"name": "SuperAGI User",
"email": "super6@agi.com",
"password": "pass@123",
}
addUser(userData)
.then((response) => {
setUserName(response.data.name);
fetchOrganisation(response.data.id);
})
.catch((error) => {
console.error('Error adding user:', error);
});
};
const handleSignUpSource = (signup) => {
getFirstSignup(signup)
.then((response) => {
})
.catch((error) => {
console.error('Error validating source:', error);
})
};
const handleMarketplace = () => {
if (window.location.href.toLowerCase().includes('marketplace')) {
setShowMarketplace(true);
} else {
installFromMarketplace();
}
};
useEffect(() => {
const clearLocalStorage = () => {
Object.keys(localStorage).forEach((key) => {
if (!excludedKeys.includes(key)) {
localStorage.removeItem(key);
}
});
};
window.addEventListener('beforeunload', clearLocalStorage);
window.addEventListener('unload', clearLocalStorage);
return () => {
window.removeEventListener('beforeunload', clearLocalStorage);
window.removeEventListener('unload', clearLocalStorage);
};
}, []);
return (
<div className="app">
<Head>
<title>SuperAGI</title>
{/* eslint-disable-next-line @next/next/no-page-custom-font */}
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet"/>
{/* eslint-disable-next-line @next/next/no-page-custom-font */}
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"/>
</Head>
{showMarketplace && <div className="projectStyle"><MarketplacePublic env={env}/></div>}
{applicationState === 'AUTHENTICATED' && !showMarketplace ? (<div className="projectStyle">
<div className="sideBarStyle">
<SideBar onSelectEvent={handleSelectionEvent} env={env}/>
</div>
<div className="workSpaceStyle">
<div className="topBarStyle">
<TopBar selectedProject={selectedProject} organisationId={organisationId} userName={userName} env={env}/>
</div>
<div className="contentStyle">
<Content env={env} organisationId={organisationId} selectedView={selectedView}
selectedProjectId={selectedProject?.id || ''}/>
</div>
</div>
</div>) : !showMarketplace ? (<div className="signInStyle">
<div className="signInTopBar">
<div className="superAgiLogo"><Image width={132} height={72} src="/images/sign-in-logo.svg"
alt="super-agi-logo"/></div>
</div>
<div className="signInCenter">
{applicationState === 'NOT_AUTHENTICATED' && !showMarketplace ? <div className="signInWrapper">
<button className="signInButton" onClick={signInUser}>
<Image width={20} height={20} src="/images/github.svg" alt="github"/>&nbsp;Continue with Github
</button>
<div className="signInInfo">
By continuing, you agree to Super AGIs Terms of Service and Privacy Policy, and to receive important
updates.
</div>
</div> : <div className="signInWrapper" style={{background: 'transparent'}}>
<div className="signInInfo" style={{fontSize: '16px', fontFamily: 'Source Code Pro'}}>{loadingText}</div>
</div>}
</div>
</div>) : true}
</div>
);
}
+406
View File
@@ -0,0 +1,406 @@
import api from './apiConfig';
export const getOrganisation = (userId) => {
return api.get(`/organisations/get/user/${userId}`);
};
export const getGithubClientId = () => {
return api.get(`/get/github_client_id`);
};
export const addUser = (userData) => {
return api.post(`/users/add`, userData);
};
export const getProject = (organisationId) => {
return api.get(`/projects/get/organisation/${organisationId}`);
};
export const getAgents = (projectId) => {
return api.get(`/agents/get/project/${projectId}`);
};
export const getToolKit = () => {
return api.get(`/toolkits/get/local/list`);
};
export const getTools = () => {
return api.get(`/tools/list`);
};
export const getAgentDetails = (agentId, agentExecutionId) => {
return api.get(`/agent_executions_configs/details/agent_id/${agentId}/agent_execution_id/${agentExecutionId}`);
};
export const getAgentExecutions = (agentId) => {
return api.get(`/agentexecutions/get/agent/${agentId}`);
};
export const getExecutionFeeds = (executionId) => {
return api.get(`/agentexecutionfeeds/get/execution/${executionId}`);
};
export const getExecutionTasks = (executionId) => {
return api.get(`/agentexecutionfeeds/get/tasks/${executionId}`);
};
export const createAgent = (agentData, scheduledCreate) => {
return api.post(scheduledCreate ? `/agents/schedule` : `/agents/create`, agentData);
};
export const addAgentRun = (agentData) => {
return api.post( `/agentexecutions/add_run`, agentData);
};
export const addTool = (toolData) => {
return api.post(`/toolkits/get/local/install`, toolData);
};
export const updateExecution = (executionId, executionData) => {
return api.put(`/agentexecutions/update/${executionId}`, executionData);
};
export const editAgentTemplate = (agentTemplateId, agentTemplateData) => {
return api.put(`/agent_templates/update_agent_template/${agentTemplateId}`, agentTemplateData)
};
export const addExecution = (executionData) => {
return api.post(`/agentexecutions/add`, executionData);
};
export const getResources = (agentId) => {
return api.get(`/resources/get/all/${agentId}`);
};
export const getLastActiveAgent = (projectId) => {
return api.get(`/agentexecutions/get/latest/agent/project/${projectId}`);
};
export const uploadFile = (agentId, formData) => {
return api.post(`/resources/add/${agentId}`, formData);
};
export const validateAccessToken = () => {
return api.get(`/validate-access-token`);
};
export const validateLLMApiKey = (model_source, model_api_key) => {
return api.post(`/validate-llm-api-key`, {model_source, model_api_key});
};
export const checkEnvironment = () => {
return api.get(`/configs/get/env`);
};
export const getOrganisationConfig = (organisationId, key) => {
return api.get(`/configs/get/organisation/${organisationId}/key/${key}`);
};
export const updateOrganisationConfig = (organisationId, configData) => {
return api.post(`/configs/add/organisation/${organisationId}`, configData);
};
export const fetchAgentTemplateList = () => {
return api.get('/agent_templates/list?template_source=marketplace');
};
export const fetchAgentTemplateDetails = (templateId) => {
return api.get(`/agent_templates/get/${templateId}`);
};
export const getToolConfig = (toolKitName) => {
return api.get(`/tool_configs/get/toolkit/${toolKitName}`);
};
export const updateToolConfig = (toolKitName, configData) => {
return api.post(`/tool_configs/add/${toolKitName}`, configData);
};
export const fetchAgentTemplateListLocal = () => {
return api.get('/agent_templates/list?template_source=local');
};
export const saveAgentAsTemplate = (agentId, executionId) => {
return api.post(`/agent_templates/save_agent_as_template/agent_id/${agentId}/agent_execution_id/${executionId}`);
};
export const fetchAgentTemplateConfig = (templateId) => {
return api.get(`/agent_templates/get/${templateId}?template_source=marketplace`);
};
export const installAgentTemplate = (templateId) => {
return api.post(`/agent_templates/download?agent_template_id=${templateId}`);
};
export const fetchAgentTemplateConfigLocal = (templateId) => {
return api.get(`/agent_templates/agent_config?agent_template_id=${templateId}`);
};
export const updatePermissions = (permissionId, data) => {
return api.put(`/agentexecutionpermissions/update/status/${permissionId}`, data)
};
export const deleteAgent = (agentId) => {
return api.put(`/agents/delete/${agentId}`)
};
export const authenticateGoogleCred = (toolKitId) => {
return api.get(`/google/get_google_creds/toolkit_id/${toolKitId}`);
};
export const authenticateTwitterCred = (toolKitId) => {
return api.get(`/twitter/get_twitter_creds/toolkit_id/${toolKitId}`);
};
export const sendTwitterCreds = (twitter_creds) => {
return api.post(`/twitter/send_twitter_creds/${twitter_creds}`);
};
export const sendGoogleCreds = (google_creds, toolkit_id) => {
return api.post(`/google/send_google_creds/toolkit_id/${toolkit_id}`, google_creds);
};
export const fetchToolTemplateList = () => {
return api.get(`/toolkits/get/list?page=0`);
};
export const fetchKnowledgeTemplateList = () => {
return api.get(`/knowledges/get/list?page=0`);
};
export const fetchToolTemplateOverview = (toolTemplateName) => {
return api.get(`/toolkits/marketplace/readme/${toolTemplateName}`);
};
export const updateMarketplaceToolTemplate = (templateName) => {
return api.put(`/toolkits/update/${templateName}`);
};
export const installToolkitTemplate = (templateName) => {
return api.get(`/toolkits/get/install/${templateName}`);
};
export const checkToolkitUpdate = (templateName) => {
return api.get(`/toolkits/check_update/${templateName}`);
};
export const getExecutionDetails = (executionId, agentId) => {
return api.get(`/agent_executions_configs/details/agent/${agentId}/agent_execution/${executionId}`);
};
export const stopSchedule = (agentId) => {
return api.post(`/agents/stop/schedule?agent_id=${agentId}`);
};
export const createAndScheduleRun = (requestData) => {
return api.post(`/agentexecutions/schedule`, requestData);
};
export const agentScheduleComponent = (agentId) => {
return api.get(`/agents/get/schedule_data/${agentId}`);
};
export const updateSchedule = (requestData) => {
return api.put(`/agents/edit/schedule`, requestData);
};
export const getDateTime = (agentId) => {
return api.get(`/agents/get/schedule_data/${agentId}`);
};
export const getMetrics = () => {
return api.get(`/analytics/metrics`)
};
export const getAllAgents = () => {
return api.get(`/analytics/agents/all`)
};
export const getAgentRuns = (agent_id) => {
return api.get(`analytics/agents/${agent_id}`);
};
export const getActiveRuns = () => {
return api.get(`analytics/runs/active`);
};
export const getToolsUsage = () => {
return api.get(`analytics/tools/used`);
};
export const modelInfo = (model) => {
return api.get(`analytics/model_details/${model}`)
}
export const getLlmModels = () => {
return api.get(`organisations/llm_models`);
};
export const getAgentWorkflows = () => {
return api.get(`organisations/agent_workflows`);
};
export const fetchVectorDBList = () => {
return api.get(`/vector_dbs/get/list`);
};
export const getVectorDatabases = () => {
return api.get(`/vector_dbs/user/list`);
};
export const getVectorDBDetails = (vectorDBId) => {
return api.get(`/vector_dbs/db/details/${vectorDBId}`);
};
export const deleteVectorDB = (vectorDBId) => {
return api.post(`/vector_dbs/delete/${vectorDBId}`);
};
export const updateVectorDB = (vectorDBId, newIndices) => {
return api.put(`/vector_dbs/update/vector_db/${vectorDBId}`, newIndices);
};
export const connectPinecone = (pineconeData) => {
return api.post(`/vector_dbs/connect/pinecone`, pineconeData);
};
export const connectQdrant = (qdrantData) => {
return api.post(`/vector_dbs/connect/qdrant`, qdrantData);
};
export const connectWeaviate = (weaviateData) => {
return api.post(`/vector_dbs/connect/weaviate`, weaviateData);
};
export const getKnowledge = () => {
return api.get(`/knowledges/user/list`);
};
export const getKnowledgeDetails = (knowledgeId) => {
return api.get(`/knowledges/user/get/details/${knowledgeId}`);
};
export const deleteCustomKnowledge = (knowledgeId) => {
return api.post(`/knowledges/delete/${knowledgeId}`);
};
export const deleteMarketplaceKnowledge = (knowledgeName) => {
return api.post(`/knowledges/uninstall/${knowledgeName}`);
};
export const addUpdateKnowledge = (knowledgeData) => {
return api.post(`/knowledges/add_or_update/data`, knowledgeData);
};
export const getValidIndices = () => {
return api.get(`/vector_db_indices/user/valid_indices`);
};
export const getValidMarketplaceIndices = (knowledgeName) => {
return api.get(`/vector_db_indices/marketplace/valid_indices/${knowledgeName}`);
};
export const fetchKnowledgeTemplateOverview = (knowledgeName) => {
return api.get(`/knowledges/marketplace/get/details/${knowledgeName}`);
};
export const installKnowledgeTemplate = (knowledgeName, indexId) => {
return api.get(`/knowledges/install/${knowledgeName}/index/${indexId}`);
};
export const createApiKey = (apiName) => {
return api.post(`/api-keys`, apiName);
};
export const getApiKeys = () => {
return api.get(`/api-keys`);
};
export const editApiKey = (apiDetails) => {
return api.put(`/api-keys`, apiDetails);
};
export const deleteApiKey = (apiId) => {
return api.delete(`/api-keys/${apiId}`);
};
export const saveWebhook = (webhook) => {
return api.post(`/webhook/add`, webhook);
};
export const getWebhook = () => {
return api.get(`/webhook/get`);
};
export const editWebhook = (webhook_id, webook_data) => {
return api.post(`/webhook/edit/${webhook_id}`, webook_data);
};
export const publishToMarketplace = (executionId) => {
return api.post(`/agent_templates/publish_template/agent_execution_id/${executionId}`);
};
export const storeApiKey = (model_provider, model_api_key) => {
return api.post(`/models_controller/store_api_keys`, {model_provider, model_api_key});
}
export const fetchApiKeys = () => {
return api.get(`/models_controller/get_api_keys`);
}
export const fetchApiKey = (model_provider) => {
return api.get(`/models_controller/get_api_key?model_provider=${model_provider}`);
}
export const verifyEndPoint = (model_api_key, end_point, model_provider) => {
return api.get(`/models_controller/verify_end_point`, {
params: { model_api_key, end_point, model_provider }
});
}
export const storeModel = (model_name, description, end_point, model_provider_id, token_limit, type, version, context_length) => {
return api.post(`/models_controller/store_model`,{model_name, description, end_point, model_provider_id, token_limit, type, version, context_length});
}
export const testModel = () => {
return api.get(`/models_controller/test_local_llm`);
}
export const fetchModels = () => {
return api.get(`/models_controller/fetch_models`);
}
export const fetchModel = (model_id) => {
return api.get(`/models_controller/fetch_model/${model_id}`);
}
export const fetchModelData = (model) => {
return api.post(`/models_controller/fetch_model_data`, { model: model })
}
export const fetchMarketPlaceModel = () => {
return api.get(`/models_controller/get/list`)
}
export const getToolMetrics = (toolName) => {
return api.get(`analytics/tools/${toolName}/usage`)
}
export const getToolLogs = (toolName) => {
return api.get(`analytics/tools/${toolName}/logs`)
}
export const publishTemplateToMarketplace = (agentData) => {
return api.post(`/agent_templates/publish_template`, agentData);
};
export const getKnowledgeMetrics = (knowledgeName) => {
return api.get(`analytics/knowledge/${knowledgeName}/usage`)
}
export const getKnowledgeLogs = (knowledgeName) => {
return api.get(`analytics/knowledge/${knowledgeName}/logs`)
}
export const getFirstSignup = (source) => {
return api.post(`/users/first_login_source/${source}`,);
};
+50
View File
@@ -0,0 +1,50 @@
import axios from 'axios';
import Cookies from "js-cookie";
const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID;
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8001';
const GOOGLE_ANALYTICS_MEASUREMENT_ID = process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID;
const GOOGLE_ANALYTICS_API_SECRET = process.env.GOOGLE_ANALYTICS_API_SECRET;
const MIXPANEL_AUTH_ID = process.env.NEXT_PUBLIC_MIXPANEL_AUTH_ID
export const baseUrl = () => {
return API_BASE_URL;
};
export const githubClientId = () => {
return GITHUB_CLIENT_ID;
};
export const analyticsMeasurementId = () => {
return GOOGLE_ANALYTICS_MEASUREMENT_ID;
};
export const analyticsApiSecret = () => {
return GOOGLE_ANALYTICS_API_SECRET;
};
export const mixpanelId = () => {
return MIXPANEL_AUTH_ID;
};
const api = axios.create({
baseURL: API_BASE_URL,
headers: {
common: {
'Content-Type': 'application/json',
},
},
});
api.interceptors.request.use(config => {
if (typeof window !== 'undefined') {
// const accessToken = localStorage.getItem("accessToken");
const accessToken = Cookies.get("accessToken");
if (accessToken) {
config.headers['Authorization'] = `Bearer ${accessToken}`;
}
}
return config;
});
export default api;

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