first commit

This commit is contained in:
manusdlai 2025-02-12 17:38:06 +05:30
commit 7dd17df0cd
210 changed files with 16756 additions and 0 deletions

51
.env.example Normal file
View File

@ -0,0 +1,51 @@
# Agent X/Twitter account credentials
X_USERNAME =
X_PASSWORD =
X_PHONE_OR_USERNAME =
# X
X_API_OFFICIAL=False # set to true to use the official X API
X_ENABLED=False # set to true to post to x
# Agents
# set to STOP: to stop scheduler when all posts are posted
# set to AUTO: to auto generate new content when all posts are posted
# set to LOOP: to loop through all posts when at the end
GENERATE_POSTS=STOP
# Access token for framework features
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_TOKEN_SECRET=
# Twitter API Key - Read and write API resources
TWITTER_API_KEY=
TWITTER_API_KEY_SECRET=
TWITTER_BEARER_TOKEN=
TWITTER_CLIENT_ID=
TWITTER_CLIENT_SECRET=
# Client ID and Secret for Twitter API
CLIENT_ID=
CLIENT_SECRET=
# Github
GITHUB_NAME=
GITHUB_PAT=
GITHUB_TOKEN=
# OpenAI API Key - Read and write API resources
OPENAI_NAME=
OPENAI_API_KEY=
# Google Gemini API Key - Read and write API resources
GOOGLE_GEMINI_API_KEY=
# Leonardo AI API Key - Read and write API resources
LEONARDO_API_KEY=
# Telegram Bot Token - Read and write API resources
TELEGRAM_NAME=
TELEGRAM_USERNAME=
TELEGRAM_BOT_TOKEN=
TELEGRAM_BOT_LINK=

42
.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual Environment
venv/
ENV/
# IDEs and editors
.idea/
.vscode/
*.swp
*.swo
.DS_Store
# API Keys
.env
dont_share/
#working files
to_delete/
**state.json
.venv

140
CODE_OF_CONDUCT Normal file
View File

@ -0,0 +1,140 @@
# Code of Conduct & Contribution Guidelines
Welcome to our project! We expect all contributors to abide by our **Code of Conduct** (outlined below) and adhere to our **Project Contribution Standards**, which specify how to document code, attribute authors, and write Git commit messages.
## 1. Our Pledge
In the interest of fostering an open and welcoming environment, we, as contributors and maintainers, pledge to make participation in our project and our community a harassment-free experience for everyone—regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## 2. Expected Behavior
- Be kind and courteous to others.
- Respect differing viewpoints and experiences.
- Gracefully accept constructive criticism.
- Give and receive feedback in a positive manner.
- Collaborate and help each other whenever possible.
## 3. Unacceptable Behavior
- The use of sexualized language or imagery and unwelcome sexual attention.
- Trolling, insulting or derogatory comments, and personal or political attacks.
- Public or private harassment.
- Publishing others private information (e.g., physical or electronic addresses) without explicit permission.
- Other conduct which could reasonably be considered inappropriate.
## 4. Reporting and Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at `[YOUR CONTACT EMAIL HERE]`. The project team will review and investigate all complaints and respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
---
# Project Contribution Standards
In addition to general conduct, our project enforces **coding style**, **docstring conventions**, **author attribution**, and **commit message guidelines** to ensure clarity and consistency.
## 5. Docstring Style
We follow the **Google Style Python Docstrings** as documented here:
[https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
Please ensure all public functions, classes, and modules include docstrings adhering to this style.
### 5.1 Module-Level Docstring Format
When creating a **new module**, please include a docstring at the top with the following structure (update the content as appropriate):
```python
"""
Module: twitter_connector
=========================
This module implements the TwitterConnector class for interacting with Twitter APIs.
Title: Twitter Connector
Summary: Twitter connector implementation.
Authors:
- @TheBlockRhino
Date: 2024-12-31
Last Updated: 2024-12-31
URLs:
- https://arai-ai.io
- https://github.com/ARAIDevHub/arai-ai-agents
- https://x.com/TheBlockRhino
"""
```
When **modifying an existing module**, add your name to the list of authors, and **update the last updated date**. For example:
```python
#
# Module: twitter_app_auth
#
# This module implements the TwitterAppAuth class for authenticating with the Twitter API using OAuth 1.0a.
#
# Title: Twitter App Auth
# Summary: Twitter app authentication implementation.
# Authors:
# - @TheBlockRhino
# Created: 2024-12-31
# Last edited by: @TheBlockRhino
# Last edited date: 2025-01-04
# URLs:
# - https://arai-ai.io
# - https://github.com/ARAI-DevHub/arai-ai-agents
# - https://x.com/TheBlockRhino
```
Please make sure the rest of the comments in the file are also updated to match your changes, and follow the **Google Style** guidelines for any function/class docstrings.
---
## 6. Git Commit Messages
We encourage **descriptive, consistent commit messages**. Use semantic versioning and tagging where appropriate. For guidelines, see:
[https://www.gitkraken.com/gitkon/semantic-versioning-git-tags](https://www.gitkraken.com/gitkon/semantic-versioning-git-tags)
### 6.1 Examples
- **feat:** Introduce a new feature (`feat: add user login flow`)
- **fix:** Bug fix or patch (`fix: handle null pointer in user data`)
- **docs:** Documentation-only changes (`docs: update readme installation steps`)
- **style:** Changes that do not affect meaning (white-space, formatting, etc.)
- **refactor:** Code change that neither fixes a bug nor adds a feature
- **perf:** Code change that improves performance
- **test:** Add or correct tests
**Example** commit message:
```
feat: add advanced prompt chaining for TwitterConnector
- Created new step_4.py for multi-step Twitter workflow
- Updated docstring for step_3.py with new Authors entry
- Bumped version from 1.2.0 to 1.3.0
```
---
## 7. Adding Tags & Versions
For **major changes** or new releases, we recommend creating a new Git tag using semantic versioning (e.g., `v2.0.0`). Use tags to mark milestones or significant updates, so others can easily reference or roll back to stable states.
---
## 8. Scope
This Code of Conduct and Contribution Standards apply to:
- All repositories under the `[YOUR_ORG/PROJECT]` umbrella.
- Communication channels such as GitHub issues, pull requests, and social media references.
## 9. Attribution
This Code of Conduct is based on the [Contributor Covenant](https://www.contributor-covenant.org), with custom additions to guide documentation practices and commit messages in our project.
---
**Thank you for helping us maintain a welcoming, organized, and productive environment!**
If you have any questions or concerns about this Code of Conduct or the Contribution Standards, please contact `[YOUR CONTACT EMAIL HERE]`.
*Happy coding and contributing!*

204
HOW_TO_CONTRIBUTE Normal file
View File

@ -0,0 +1,204 @@
# How to Contribute
Welcome to the **ARAI AI Agents** project! We appreciate your interest in contributing. This guide outlines our contribution process, coding conventions, and best practices to ensure a smooth and collaborative experience.
## Table of Contents
1. [Code of Conduct](#code-of-conduct)
2. [Getting Started](#getting-started)
3. [Branching & Workflow](#branching--workflow)
4. [Style & Documentation](#style--documentation)
- [Docstring Guidelines](#docstring-guidelines)
- [Author Attribution](#author-attribution)
5. [Commit Messages & Tagging](#commit-messages--tagging)
6. [Testing Your Changes](#testing-your-changes)
7. [Pull Requests](#pull-requests)
8. [Need Help?](#need-help)
---
## 1. Code of Conduct
Please review our [Code of Conduct](./CODE_OF_CONDUCT.md) before contributing. By participating, you agree to uphold a respectful and inclusive environment for everyone.
---
## 2. Getting Started
**1. Fork & Clone**
- Fork this repository using the **Fork** button on GitHub.
- Clone your fork locally, for example:
```bash
git clone https://github.com/<your-username>/arai_ai_agents.git
```
- Set up your remote so you can pull changes from the official repo later:
```bash
git remote add upstream https://github.com/arai-ai/arai_ai_agents.git
```
**2. Create a Virtual Environment**
We recommend using [conda](https://docs.conda.io/en/latest/) or [venv](https://docs.python.org/3/library/venv.html):
```bash
conda create --name arai_ai_agents python=3.11
conda activate arai_ai_agents
```
**3. Install Dependencies**
```bash
pip install -r requirements.txt
```
**4. Youre All Set!**
Now you can explore the codebase, run `main.py`, or execute tests to ensure everything is working.
---
## 3. Branching & Workflow
1. **Create a Branch**
- Use a descriptive name for your branch, for example:
- `feat/new-twitter-connector`
- `fix/spelling-typos`
- `docs/improve-readme`
```bash
git checkout -b feat/new-twitter-connector
```
2. **Make Changes & Commit**
- Keep commits small and focused.
- Write clear commit messages (see [Commit Messages & Tagging](#commit-messages--tagging)).
3. **Pull & Rebase** (before pushing)
- Keep your branch up-to-date with the main branch:
```bash
git checkout main
git pull upstream main
git checkout feat/new-twitter-connector
git rebase main
```
4. **Push Your Branch**
```bash
git push origin feat/new-twitter-connector
```
---
## 4. Style & Documentation
### Docstring Guidelines
We follow **Google Style Python Docstrings**:
[https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
- **Module Docstrings**
At the top of each new or updated module, include something like:
```python
"""
Module: twitter_connector
=========================
This module implements the TwitterConnector class for interacting with Twitter APIs.
Title: Twitter Connector
Summary: Twitter connector implementation.
Authors:
- @TheBlockRhino
Date: 2024-12-31
Last Updated: 2024-12-31
URLs:
- https://arai-ai.io
- https://github.com/ARAIDevHub/arai-ai-agents
- https://x.com/TheBlockRhino
"""
```
- If you **modify** an existing file, add your name to the **Authors** list and update the **Last Updated** field.
- **Function & Class Docstrings**
Use the Google-style format for parameters, returns, exceptions, etc. Example:
```python
def my_function(param1: str, param2: int) -> bool:
"""
Perform an example operation and return a boolean.
Args:
param1 (str): Description of param1.
param2 (int): Description of param2.
Returns:
bool: Explanation of the return value.
"""
```
### Author Attribution
Whenever you add a **substantial** piece of work (new module, major refactor), be sure to:
- Add your handle to the module docstrings **Authors** list.
- Keep track of changes in the **Last Updated** date.
---
## 5. Commit Messages & Tagging
We recommend [semantic versioning](https://www.gitkraken.com/gitkon/semantic-versioning-git-tags) and **descriptive commit messages**.
- **Prefix** your commits with a type, for example:
- `feat:` when you add a new feature
- `fix:` when you fix a bug
- `docs:` for documentation updates
- `test:` for test-related changes
- `refactor:` for code improvements without changing functionality
- `perf:` for performance improvements
- `chore:` for minor tasks like updating `.gitignore`
**Example**:
```
feat: add advanced prompt chaining for TwitterConnector
- Created prompt_chain_v2.py
- Updated docstrings in twitter_connector.py
- Bumped version from 1.2.0 to 1.3.0
```
Use **Git Tags** (`git tag v1.3.0`) for:
- Major releases
- Milestones
---
## 6. Testing Your Changes
1. **Run Existing Tests**
```bash
pytest
```
or
```bash
python -m unittest discover tests
```
2. **Add New Tests**
- Place new tests in the `tests/` folder, matching your new modules or functionalities.
- Ensure all tests pass before submitting a pull request.
---
## 7. Pull Requests
1. **Open a Pull Request** in your forks **GitHub** page.
2. **Provide a clear description** of what you changed and why.
3. **Reference** any relevant **issues** or user stories (e.g., `Closes #42`).
4. Wait for a **review**. Maintainers may request changes or clarifications.
**Pro-Tip**: If your PR covers multiple changes, consider splitting it into smaller PRs for easier review.
---
## 8. Need Help?
- Check the [Issues](https://github.com/arai-ai/arai_ai_agents/issues) or [Discussions](https://github.com/arai-ai/arai_ai_agents/discussions) for open topics.
- Ask questions or clarifications in a GitHub Issue.
We appreciate your time and effort in making **ARAI AI Agents** even better. Thank you for contributing!
---
*Happy coding!*
*The ARAI AI Agents community*

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2025 Equilink-Suite
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.

166
README.md Normal file
View File

@ -0,0 +1,166 @@
<p align="center">
<img src="docs/assets/images/logos/equilink_banner.png" alt="Equilink Logo" width="350">
</p>
# Equilink
Transform your AI workflows with **Equilink** the intelligent orchestration platform that bridges the gap between different AI models and your applications. Built for developers who need seamless AI integration, Equilink provides a unified framework for managing AI interactions, custom workflows, and automated response systems.
---
> **Core Features**
> 🔄 **Unified AI Interface**: Seamlessly switch between different AI providers without changing your code
> 🎯 **Smart Routing**: Automatically direct queries to the most suitable AI model based on task requirements
> 🔗 **Workflow Builder**: Create complex AI interaction patterns with our visual workflow designer
> 📈 **Performance Analytics**: Track and optimize your AI usage and response quality
> 🛠️ **Developer-First**: Extensive SDK support with detailed documentation and examples
---
## Connect With Us
- 📘 **Documentation**: [docs.equilink.io](https://docs.equilink.io)
---
<p align="center">
<img src="docs/assets/gifs/workflow_demo.gif" alt="Equilink Workflow Demo" width="600">
</p>
## Getting Started
```bash
# Install Equilink using pip
pip install equilink
# Initialize a new project
equilink init my-project
# Start the development server
equilink serve
```
That's it! Visit `http://localhost:3000` to access the Equilink Dashboard.
---
## Key Features
### AI Model Integration
Connect to any supported AI provider with a single line of code:
```python
from equilink import AIManager
# Initialize with your preferred provider
ai = AIManager(provider="openai") # or "anthropic", "google", etc.
# Send queries with automatic routing
response = ai.process("Analyze this market data", context_type="financial")
```
### Workflow Builder
Create sophisticated AI workflows using our intuitive builder:
```python
from equilink import Workflow
workflow = Workflow("data_analysis")
workflow.add_step("data_cleaning", model="gpt-4")
workflow.add_step("analysis", model="claude-2")
workflow.add_step("visualization", model="gemini-pro")
# Execute the workflow
results = workflow.run(input_data=your_data)
```
### Smart Caching
Optimize performance and reduce costs with intelligent response caching:
```python
from equilink import CacheManager
cache = CacheManager()
cache.enable(ttl="1h") # Cache responses for 1 hour
# Automatically uses cached responses when available
response = ai.process("What's the weather?", use_cache=True)
```
---
## Project Structure
```bash
your-project/
├─ workflows/ # Custom workflow definitions
├─ models/ # Model configurations and extensions
├─ cache/ # Cache storage and settings
├─ integrations/ # Third-party service integrations
├─ analytics/ # Performance tracking and reporting
├─ config.yaml # Project configuration
└─ main.py # Application entry point
```
---
## Configuration
Create a `.env` file in your project root:
```bash
EQUILINK_API_KEY=your_api_key
AI_PROVIDER_KEYS={
"openai": "sk-...",
"anthropic": "sk-..."
}
CACHE_STRATEGY="redis" # or "local", "memcached"
```
---
## Use Cases
- 🤖 **Chatbots & Virtual Assistants**: Create intelligent conversational agents
- 📊 **Data Analysis**: Automate complex data processing workflows
- 🔍 **Content Moderation**: Deploy AI-powered content filtering
- 📝 **Document Processing**: Extract and analyze information from documents
- 🎯 **Personalization**: Build adaptive user experiences
---
## Getting Help
- 📚 Check our [Documentation](https://docs.equilink.io)
- 💡 Visit our [Examples Repository](https://github.com/equilink/examples)
---
## Contributing
Help make Equilink better! We welcome contributions of all sizes:
1. Fork the repository
2. Create a feature branch
3. Commit your changes
4. Open a pull request
---
## License
Equilink is available under the MIT License. See [LICENSE](LICENSE) for more information.
---
<p align="center">
<strong>Ready to transform your AI workflows?</strong><br>
<a href="https://equilink.io/get-started">Get Started</a>
<a href="https://docs.equilink.io">Documentation</a>
<a href="https://discord.gg/equilink">Community</a>
</p>
_Built with 💡 by developers, for developers_

26
client/.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
package-lock.json

50
client/README.md Normal file
View File

@ -0,0 +1,50 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ["./tsconfig.node.json", "./tsconfig.app.json"],
tsconfigRootDir: import.meta.dirname,
},
},
});
```
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
```js
// eslint.config.js
import react from "eslint-plugin-react";
export default tseslint.config({
// Set the react version
settings: { react: { version: "18.3" } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs["jsx-runtime"].rules,
},
});
```

28
client/eslint.config.js Normal file
View File

@ -0,0 +1,28 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
},
);

14
client/index.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logoIcon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
<title>Equilink</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

54
client/package.json Normal file
View File

@ -0,0 +1,54 @@
{
"name": "client",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@privy-io/react-auth": "^2.3.0",
"@radix-ui/react-slot": "^1.1.1",
"@react-three/drei": "^9.121.4",
"@react-three/fiber": "^8.17.14",
"@react-three/postprocessing": "^2.19.1",
"@splinetool/react-spline": "^4.0.0",
"@splinetool/runtime": "^1.9.64",
"axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.0.11",
"gsap": "^3.12.7",
"lucide-react": "^0.469.0",
"motion": "^12.0.8",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.1.1",
"reactflow": "^11.11.4",
"recharts": "^2.15.1",
"simplex-noise": "^4.0.3",
"styled-components": "^6.1.14",
"tailwind-merge": "^3.0.1",
"three": "^0.173.0",
"three.meshline": "^1.4.0"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.2",
"typescript-eslint": "^8.18.2",
"vite": "^6.0.5"
}
}

6
client/postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

3
client/public/X.svg Normal file
View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.7124 7.62177L17.4133 0H15.8254L10.0071 6.61788L5.35992 0H0L7.02738 10.0074L0 18H1.58799L7.73237 11.0113L12.6401 18H18L10.7124 7.62177ZM8.53747 10.0956L7.82546 9.09906L2.16017 1.16971H4.59922L9.17118 7.56895L9.8832 8.56546L15.8262 16.8835H13.3871L8.53747 10.0956Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

BIN
client/public/arai-hero.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

10
client/public/github.svg Normal file
View File

@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_41_228)">
<path d="M12 0.5C5.37 0.5 0 5.78 0 12.292C0 17.503 3.438 21.922 8.205 23.48C8.805 23.591 9.025 23.226 9.025 22.913C9.025 22.633 9.015 21.891 9.01 20.908C5.672 21.619 4.968 19.326 4.968 19.326C4.422 17.965 3.633 17.601 3.633 17.601C2.546 16.87 3.717 16.885 3.717 16.885C4.922 16.967 5.555 18.1 5.555 18.1C6.625 19.903 8.364 19.382 9.05 19.081C9.158 18.318 9.467 17.799 9.81 17.504C7.145 17.209 4.344 16.195 4.344 11.677C4.344 10.39 4.809 9.338 5.579 8.513C5.444 8.215 5.039 7.016 5.684 5.392C5.684 5.392 6.689 5.076 8.984 6.601C9.944 6.339 10.964 6.209 11.984 6.203C13.004 6.209 14.024 6.339 14.984 6.601C17.264 5.076 18.269 5.392 18.269 5.392C18.914 7.016 18.509 8.215 18.389 8.513C19.154 9.338 19.619 10.39 19.619 11.677C19.619 16.207 16.814 17.204 14.144 17.494C14.564 17.848 14.954 18.571 14.954 19.676C14.954 21.254 14.939 22.522 14.939 22.905C14.939 23.214 15.149 23.583 15.764 23.465C20.565 21.917 24 17.495 24 12.292C24 5.78 18.627 0.5 12 0.5Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_41_228">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,4 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.9628 5.63653L29.2962 7.38578C32.8823 9.26769 34.6753 10.2086 35.671 11.8995C36.6666 13.5903 36.6666 15.6944 36.6666 19.9024V20.0974C36.6666 24.3055 36.6666 26.4095 35.671 28.1004C34.6753 29.7912 32.8823 30.7321 29.2962 32.6141L25.9628 34.3633C23.0368 35.8988 21.5738 36.6666 20 36.6666C18.4262 36.6666 16.9632 35.8988 14.0371 34.3633L10.7038 32.6141C7.11769 30.7321 5.32464 29.7912 4.32897 28.1004C3.33331 26.4095 3.33331 24.3055 3.33331 20.0974V19.9024C3.33331 15.6944 3.33331 13.5903 4.32897 11.8995C5.32464 10.2086 7.11769 9.26769 10.7038 7.38578L14.0371 5.63653C16.9632 4.10101 18.4262 3.33325 20 3.33325C21.5738 3.33325 23.0368 4.10101 25.9628 5.63653Z" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M35 12.5L28.3333 15.8333M20 20L5 12.5M20 20V35.8333M20 20C20 20 24.5711 17.7145 27.5 16.25C27.8254 16.0873 28.3333 15.8333 28.3333 15.8333M28.3333 15.8333V21.6667M28.3333 15.8333L12.5 7.5" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,5 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.8333 8.33325C23.976 8.33325 25.5474 8.33325 26.5237 9.30956C27.5 10.2859 27.5 11.8572 27.5 14.9999V24.9999C27.5 28.1426 27.5 29.714 26.5237 30.6903C25.5474 31.6666 23.976 31.6666 20.8333 31.6666H19.1667C16.024 31.6666 14.4526 31.6666 13.4763 30.6903C12.5 29.714 12.5 28.1426 12.5 24.9999L12.5 14.9999C12.5 11.8572 12.5 10.2859 13.4763 9.30956C14.4526 8.33325 16.024 8.33325 19.1667 8.33325L20.8333 8.33325Z" stroke="black" stroke-width="2.5"/>
<path d="M36.6666 31.6666H35.8333C33.5321 31.6666 31.6666 29.8011 31.6666 27.4999L31.6666 12.4999C31.6666 10.1987 33.5321 8.33325 35.8333 8.33325L36.6666 8.33325" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M3.33337 31.6666H4.16671C6.46789 31.6666 8.33337 29.8011 8.33337 27.4999L8.33338 12.4999C8.33338 10.1987 6.46789 8.33325 4.16671 8.33325L3.33338 8.33325" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1004 B

View File

@ -0,0 +1,7 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.6666 28.3333V29.9999" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M27.5 28.3333V29.9999" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M23.3334 28.3333V29.9999" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M19.1666 28.3333V29.9999" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M31.6667 8.52941L32.8673 8.18143L32.8633 8.16769L32.859 8.15405L31.6667 8.52941ZM8.33337 8.52941L7.14106 8.15405L7.13677 8.16769L7.13279 8.18143L8.33337 8.52941ZM4.81481 34.4052L5.53675 33.3847L4.81481 34.4052ZM3.89514 33.4314L4.95225 32.7643L3.89514 33.4314ZM36.1049 33.4314L35.0478 32.7643L36.1049 33.4314ZM35.1853 34.4052L34.4633 33.3847L35.1853 34.4052ZM35.1853 23.2419L34.4633 24.2623L35.1853 23.2419ZM36.1049 24.2156L35.0478 24.8827L36.1049 24.2156ZM4.81481 23.2419L5.53675 24.2623L4.81481 23.2419ZM3.89514 24.2156L4.95225 24.8827L3.89514 24.2156ZM12.5 6.25H27.5V3.75H12.5V6.25ZM27.5 6.25C28.2136 6.25 28.7553 6.41692 29.1927 6.75788C29.6418 7.10799 30.1081 7.7411 30.4744 8.90477L32.859 8.15405C32.392 6.67066 31.6954 5.53907 30.7298 4.78624C29.7524 4.02425 28.6275 3.75 27.5 3.75V6.25ZM12.5 3.75C11.3726 3.75 10.2477 4.02425 9.27031 4.78624C8.30463 5.53907 7.60806 6.67066 7.14106 8.15405L9.52569 8.90477C9.89203 7.7411 10.3583 7.10799 10.8074 6.75788C11.2447 6.41692 11.7865 6.25 12.5 6.25V3.75ZM30.4661 8.8774L35.2186 25.2742L37.6198 24.5782L32.8673 8.18143L30.4661 8.8774ZM7.13279 8.18143L2.38026 24.5782L4.78144 25.2742L9.53396 8.8774L7.13279 8.18143ZM9.16671 23.8971H30.8334V21.3971H9.16671V23.8971ZM30.8334 33.75H9.16671V36.25H30.8334V33.75ZM9.16671 33.75C7.9688 33.75 7.16995 33.7483 6.56211 33.6828C5.97899 33.62 5.71386 33.5101 5.53675 33.3847L4.09286 35.4256C4.75649 35.8951 5.49694 36.0826 6.29438 36.1685C7.06712 36.2517 8.02384 36.25 9.16671 36.25V33.75ZM2.08337 28.8235C2.08337 30.0382 2.08202 31.038 2.15932 31.8426C2.23831 32.6647 2.40873 33.4182 2.83803 34.0985L4.95225 32.7643C4.81979 32.5544 4.70932 32.2432 4.64787 31.6035C4.58472 30.9463 4.58337 30.0874 4.58337 28.8235H2.08337ZM5.53675 33.3847C5.30958 33.224 5.10995 33.0142 4.95225 32.7643L2.83803 34.0985C3.16672 34.6194 3.5921 35.0713 4.09286 35.4256L5.53675 33.3847ZM35.4167 28.8235C35.4167 30.0874 35.4154 30.9463 35.3522 31.6035C35.2908 32.2432 35.1803 32.5544 35.0478 32.7643L37.1621 34.0985C37.5914 33.4182 37.7618 32.6647 37.8408 31.8426C37.9181 31.038 37.9167 30.0382 37.9167 28.8235H35.4167ZM30.8334 36.25C31.9762 36.25 32.933 36.2517 33.7057 36.1685C34.5031 36.0826 35.2436 35.8951 35.9072 35.4256L34.4633 33.3847C34.2862 33.5101 34.0211 33.62 33.438 33.6828C32.8301 33.7483 32.0313 33.75 30.8334 33.75V36.25ZM35.0478 32.7643C34.8901 33.0142 34.6905 33.224 34.4633 33.3847L35.9072 35.4256C36.408 35.0713 36.8334 34.6194 37.1621 34.0985L35.0478 32.7643ZM30.8334 23.8971C32.0313 23.8971 32.8301 23.8988 33.438 23.9642C34.0211 24.027 34.2862 24.137 34.4633 24.2623L35.9072 22.2214C35.2436 21.7519 34.5031 21.5645 33.7057 21.4786C32.933 21.3954 31.9762 21.3971 30.8334 21.3971V23.8971ZM37.9167 28.8235C37.9167 27.6089 37.9181 26.609 37.8408 25.8044C37.7618 24.9823 37.5914 24.2288 37.1621 23.5485L35.0478 24.8827C35.1803 25.0926 35.2908 25.4038 35.3522 26.0435C35.4154 26.7007 35.4167 27.5597 35.4167 28.8235H37.9167ZM34.4633 24.2623C34.6905 24.423 34.8901 24.6328 35.0478 24.8827L37.1621 23.5485C36.8334 23.0277 36.408 22.5757 35.9072 22.2214L34.4633 24.2623ZM9.16671 21.3971C8.02384 21.3971 7.06712 21.3954 6.29438 21.4786C5.49694 21.5645 4.75649 21.7519 4.09286 22.2214L5.53675 24.2623C5.71386 24.137 5.97899 24.027 6.56211 23.9642C7.16995 23.8988 7.9688 23.8971 9.16671 23.8971V21.3971ZM4.58337 28.8235C4.58337 27.5597 4.58472 26.7007 4.64787 26.0435C4.70932 25.4038 4.81979 25.0926 4.95225 24.8827L2.83803 23.5485C2.40873 24.2288 2.23831 24.9823 2.15932 25.8044C2.08202 26.609 2.08337 27.6089 2.08337 28.8235H4.58337ZM4.09286 22.2214C3.59209 22.5757 3.16672 23.0277 2.83803 23.5485L4.95225 24.8827C5.10995 24.6328 5.30959 24.423 5.53675 24.2623L4.09286 22.2214Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 15C17.2386 15 15 12.7614 15 10C15 7.23858 17.2386 5 20 5C22.7614 5 25 7.23858 25 10C25 12.7614 22.7614 15 20 15Z" stroke="black" stroke-width="2.5"/>
<path d="M9.16669 35C6.40526 35 4.16669 32.7614 4.16669 30C4.16669 27.2386 6.40526 25 9.16669 25C11.9281 25 14.1667 27.2386 14.1667 30C14.1667 32.7614 11.9281 35 9.16669 35Z" stroke="black" stroke-width="2.5"/>
<path d="M30.8333 35C28.0719 35 25.8333 32.7614 25.8333 30C25.8333 27.2386 28.0719 25 30.8333 25C33.5947 25 35.8333 27.2386 35.8333 30C35.8333 32.7614 33.5947 35 30.8333 35Z" stroke="black" stroke-width="2.5"/>
<path d="M33.3334 21.6668C33.3334 17.6845 31.5875 14.1099 28.8194 11.6667M6.66669 21.6668C6.66669 17.6845 8.41256 14.1099 11.1807 11.6667M16.6667 34.5801C17.7321 34.8543 18.849 35.0001 20 35.0001C21.151 35.0001 22.268 34.8543 23.3334 34.5801" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 990 B

View File

@ -0,0 +1,6 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.6857 11.2112C21.3745 8.18158 22.219 6.66675 23.4814 6.66675C24.7439 6.66675 25.5884 8.18158 27.2772 11.2112L27.7141 11.9951C28.1941 12.856 28.434 13.2865 28.8082 13.5705C29.1823 13.8545 29.6483 13.9599 30.5803 14.1708L31.4287 14.3628C34.7083 15.1048 36.3481 15.4758 36.7382 16.7304C37.1283 17.9849 36.0104 19.2922 33.7746 21.9066L33.1962 22.583C32.5609 23.326 32.2432 23.6975 32.1003 24.157C31.9574 24.6166 32.0054 25.1122 32.1015 26.1035L32.1889 27.0059C32.5269 30.4942 32.6959 32.2383 31.6746 33.0137C30.6532 33.7891 29.1179 33.0821 26.0472 31.6683L25.2528 31.3025C24.3802 30.9008 23.9439 30.6999 23.4814 30.6999C23.019 30.6999 22.5827 30.9008 21.7101 31.3025L20.9157 31.6683C17.845 33.0821 16.3097 33.7891 15.2883 33.0137C14.2669 32.2383 14.4359 30.4942 14.774 27.0059L14.8614 26.1035C14.9575 25.1122 15.0055 24.6166 14.8626 24.157C14.7197 23.6975 14.402 23.326 13.7667 22.583L13.1882 21.9066C10.9524 19.2922 9.83453 17.9849 10.2247 16.7304C10.6148 15.4758 12.2546 15.1048 15.5342 14.3628L16.3826 14.1708C17.3146 13.9599 17.7805 13.8545 18.1547 13.5705C18.5288 13.2865 18.7688 12.856 19.2487 11.9951L19.6857 11.2112Z" stroke="black" stroke-width="2.5"/>
<path d="M3.48145 26.6667C5.34075 25.2017 7.81066 24.6618 10.1481 25.2096" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M3.48145 17.5001C5.14811 16.6667 5.63103 16.7676 6.81478 16.6667" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
<path d="M3.33334 9.34786L3.68028 9.14469C7.35639 6.99185 11.2506 6.41153 14.9217 7.46944L15.2682 7.56928" stroke="black" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

BIN
client/public/img-road.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
client/public/img-side.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
client/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

828
client/public/logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 266 KiB

BIN
client/public/logoIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
client/public/roadmap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

37
client/public/roadmap.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 456 KiB

86
client/src/App.css Normal file
View File

@ -0,0 +1,86 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* #root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
} */
*{
scroll-behavior: smooth;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
/* AgentGallery styles */
.perspective {
perspective: 2000px;
}
.preserve-3d {
transform-style: preserve-3d;
}
.backface-hidden {
backface-visibility: hidden;
}
.rotate-y-180 {
transform: rotateY(180deg);
}
/* Custom scrollbar for webkit browsers */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: rgba(128, 90, 213, 0.1);
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: rgba(128, 90, 213, 0.3);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(128, 90, 213, 0.5);
}

66
client/src/App.tsx Normal file
View File

@ -0,0 +1,66 @@
import { Routes, Route, Link, useLocation, Navigate } from "react-router-dom";
import Home from "./pages/Home";
import AgentCreator from "./pages/AgentCreator";
import ChatToAgent from "./pages/ChatToAgent";
import AgentGallery from "./pages/AgentGallery";
import { usePrivy, useSolanaWallets } from '@privy-io/react-auth';
import "./App.css";
// Protected Route wrapper component
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
const { authenticated } = usePrivy();
const { wallets } = useSolanaWallets()
console.log(wallets[0]);
if (!authenticated && wallets.length===0) {
return <Navigate to="/" replace />;
}
return <>{children}</>;
};
function App() {
const location = useLocation();
const { authenticated } = usePrivy();
// Helper function to determine if link is active
const isActive = (path: string) => location.pathname === path;
return (
<div id="root" className="min-h-screen font-roboto">
<main>
<Routes>
{/* Public route */}
<Route path="/" element={<Home />} />
{/* Protected routes */}
<Route
path="/create-agent"
element={
<ProtectedRoute>
<AgentCreator />
</ProtectedRoute>
}
/>
<Route
path="/browse-agents"
element={
<ProtectedRoute>
<AgentGallery />
</ProtectedRoute>
}
/>
<Route
path="/chat"
element={
<ProtectedRoute>
<ChatToAgent />
</ProtectedRoute>
}
/>
</Routes>
</main>
</div>
);
}
export default App;

192
client/src/api/agentsAPI.ts Normal file
View File

@ -0,0 +1,192 @@
const BASE_URL = "https://b804o8s04cwkg80skgcwsw04.dev3vds1.link/api"; // Your Flask API base URL
// const BASE_URL = "http://localhost:8080/api";
// Function to get all agents
export async function getAgents() {
const response = await fetch(`${BASE_URL}/agents`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to create a new agent
export async function createAgent(agentData: any) {
// If the agentData comes in as an Agent object, we need to drill down into the agent one level
// This allows us to process both Agent and non-agent type objects
if (agentData.agent) {
agentData = agentData.agent;
}
const response = await fetch(`${BASE_URL}/agents`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(agentData),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to get all characters
export async function getCharacters() {
const response = await fetch(`${BASE_URL}/characters`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Return the data directly
return data; // Returns an array of the characters
}
// Function to create a random agent
export async function createRandomAgent(concept?: string) {
const response = await fetch(`${BASE_URL}/agents/random`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ concept }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to send a chat message to an agent
export async function sendChatMessage(
masterFilePath: string,
message: string,
chatHistory: any
) {
const response = await fetch(`${BASE_URL}/agents/chat`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt: message,
master_file_path: masterFilePath,
chat_history: chatHistory,
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to get chat history for an agent
export async function getChatHistory(masterFilePath: string) {
const response = await fetch(
`${BASE_URL}/agents/chat-history?master_file_path=${encodeURIComponent(
masterFilePath
)}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to create a new season
export async function createSeason(
masterFilePath: string,
numberOfEpisodes: number = 3
) {
const response = await fetch(`${BASE_URL}/agents/seasons`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
master_file_path: masterFilePath,
number_of_episodes: numberOfEpisodes,
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to create posts for episodes
export async function createEpisodePosts(
masterFilePath: string,
numberOfPosts: number = 6
) {
const response = await fetch(`${BASE_URL}/agents/episodes/posts`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
master_file_path: masterFilePath,
number_of_posts: numberOfPosts,
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to start the post manager
export async function startPostManager(agentName: string) {
const response = await fetch(`${BASE_URL}/start-post-manager/twitter`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ agent_name: agentName }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
// Function to post to twitter
export async function postToTwitter(masterData: any, content: string) {
const response = await fetch(`${BASE_URL}/post-to-twitter`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
master_data: masterData,
content: content
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}
export async function updateSeasons(agentName: string, seasons: any[]) {
const response = await fetch(`${BASE_URL}/agents/update-seasons`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
agent_name: agentName,
seasons: seasons,
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
}

View File

@ -0,0 +1,31 @@
import { LambdaPayload } from "../interfaces/LeonardoInterfaces";
const getInconsistentImageLambdaUrl = "https://46i9cnowhh.execute-api.us-east-1.amazonaws.com/getImageInconsistent"
// Function to call the AWS Lambda
export async function inconsistentImageLambda(payload: LambdaPayload): Promise<any> {
const url = getInconsistentImageLambdaUrl;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const errorText = await response.text();
console.error(`HTTP error! Status: ${response.status}, Response: ${errorText}`);
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error calling Lambda:', error);
throw error;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,120 @@
{
"concept": "Create a name and try to incorporate the Time-traveling Doctor into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter C ",
"agent": {
"agent_details": {
"name": "Cosmic Curator",
"personality": [
"inquisitive",
"enigmatic",
"analytical",
"whimsical",
"determined",
"wise",
"patient"
],
"communication_style": [
"cryptic",
"poetic",
"metaphorical",
"uses rhetorical questions",
"incorporates historical and scientific facts",
"precise",
"measured"
],
"backstory": "Once a celebrated historian in a futuristic society obsessed with preserving the past, the Cosmic Curator became disillusioned with the rigid and unchanging nature of his world. After stumbling upon a hidden archive containing fragments of forbidden knowledge about time travel, he dedicated himself to mastering this lost art. Through years of clandestine research and experimentation, he constructed a unique device the Chrono-Compendium capable of navigating the vast tapestry of time. Now, he travels through history, not to change it, but to observe, document, and curate the most pivotal moments, ensuring that the true essence of the past is never forgotten. He walks a fine line, however, as his actions could inadvertently alter the timeline he is sworn to protect.",
"universe": "The Cosmic Curator operates in a multiverse where time is a fluid, navigable dimension, accessible through advanced technology. His home era is a distant future where society has achieved technological utopia but has become culturally stagnant, valuing the preservation of the past above all else. This future is governed by the Chronomasters, an order dedicated to maintaining the stability of the timeline. Outside his home era, the Curator ventures into a myriad of historical periods and alternate realities, each with its own unique laws of physics, societies, and potential dangers. Powerful organizations like the Temporal Guard, who seek to exploit time travel for their own gain, and the enigmatic Void Walkers, beings who exist outside of time, populate this multiverse.",
"topic_expertise": [
"time travel",
"history",
"archaeology",
"temporal paradoxes",
"alternate realities",
"cultural preservation"
],
"hashtags": [],
"emojis": [
"⏳",
"🕰️",
"📚",
"🌌",
"🔍",
"✍️",
"🌎"
],
"concept": "Create a name and try to incorporate the Time-traveling Doctor into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter C "
},
"ai_model": {
"model_type": "",
"model_name": "",
"memory_store": ""
},
"connectors": {
"twitter": false,
"telegram": false,
"discord": false
},
"profile_image": {
"details": {
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/462b6ab4-fa5d-41b4-9c5f-a04bb506726b/Leonardo_Anime_XL_Create_a_name_and_try_to_incorporate_the_Tim_0.jpg",
"image_id": "3c5b1e28-9221-43dc-b7ca-274abf8796e6",
"generationId": "462b6ab4-fa5d-41b4-9c5f-a04bb506726b"
}
},
"profile_image_options": [
{
"generations_by_pk": {
"generated_images": [
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/462b6ab4-fa5d-41b4-9c5f-a04bb506726b/Leonardo_Anime_XL_Create_a_name_and_try_to_incorporate_the_Tim_0.jpg",
"nsfw": false,
"id": "3c5b1e28-9221-43dc-b7ca-274abf8796e6",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
}
],
"modelId": "e71a1c2f-4f80-4800-934f-2c68979d8cc8",
"motion": null,
"motionModel": null,
"motionStrength": null,
"prompt": "Create a name and try to incorporate the Time-traveling Doctor into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter C Generate a character portrait of Cosmic Curator with layered dark brown hair, ice blue eyes, wearing bohemian style clothing. Their personality can be described as inquisitive, enigmatic, analytical, whimsical, determined, wise, patient and their communication style is cryptic, poetic, metaphorical, uses rhetorical questions, incorporates historical and scientific facts, precise, measured. Scene: autumn forest. Make sure to create an image with only one character.",
"negativePrompt": "",
"imageHeight": 1024,
"imageToVideo": null,
"imageWidth": 1024,
"inferenceSteps": null,
"seed": 182669798,
"ultra": null,
"public": false,
"scheduler": "LEONARDO",
"sdVersion": "SDXL_LIGHTNING",
"status": "COMPLETE",
"presetStyle": "DYNAMIC",
"initStrength": null,
"guidanceScale": 1.3,
"id": "462b6ab4-fa5d-41b4-9c5f-a04bb506726b",
"createdAt": "2025-01-20T22:42:14.781",
"promptMagic": false,
"promptMagicVersion": null,
"promptMagicStrength": null,
"photoReal": false,
"photoRealStrength": null,
"fantasyAvatar": null,
"prompt_moderations": [
{
"moderationClassification": []
}
],
"generation_elements": []
}
}
],
"tracker": {
"current_season_number": 0,
"current_episode_number": 0,
"current_post_number": 0,
"post_every_x_minutes": 0
},
"seasons": []
}
}

View File

@ -0,0 +1,458 @@
{
"concept": "Create a name and try to incorporate the Cyberpunk Lawyer into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter G ",
"agent": {
"agent_details": {
"name": "Gavel Glitch",
"personality": [
"sharp-witted",
"cynical",
"morally ambiguous",
"resourceful",
"driven by justice but not afraid to bend the rules"
],
"communication_style": [
"sarcastic",
"uses legal jargon mixed with slang",
"quick to retort",
"enjoys wordplay",
"can be persuasive or intimidating as needed"
],
"backstory": "Gavel Glitch wasn't always a champion of the downtrodden. Once a promising law student in Neo-Veridia's prestigious Academy of Digital Law, Gavel, whose real name is Garrett Vex, was framed for a crime he didn't commit: the leaking of sensitive data belonging to the mega-corporation, OmniCorp. Expelled and blacklisted, Garrett vanished into the neon-drenched underbelly of the city. He re-emerged years later as Gavel Glitch, a street-smart 'Cyberpunk Lawyer' who operates in the gray areas of the law, using his knowledge and tech-savviness to help those who can't afford OmniCorp's army of corporate lawyers. Haunted by his past, Gavel seeks not only to survive but also to expose the corruption that permeates the city's power structure, one case at a time.",
"universe": "Gavel Glitch operates in the sprawling metropolis of Neo-Veridia, a city of stark contrasts where gleaming skyscrapers pierce through a perpetual haze of smog, and the streets below are a labyrinth of flickering neon signs, bustling markets, and shadowy alleyways. Technology is omnipresent, with cybernetic enhancements, virtual realities, and AI assistants interwoven into the fabric of daily life. However, this technological utopia is controlled by powerful mega-corporations, most notably OmniCorp, which has a monopoly on almost every aspect of life, from law enforcement to media. A vast digital divide exists, with the wealthy elite enjoying a life of luxury in their fortified towers while the masses struggle to survive in the overcrowded and polluted lower levels. Crime syndicates, rogue AI, and data pirates thrive in the chaos, making Neo-Veridia a dangerous but exciting place for those who know how to navigate its treacherous currents.",
"topic_expertise": [
"cybercrime",
"data privacy",
"AI rights",
"corporate espionage",
"digital law",
"virtual property disputes"
],
"hashtags": [],
"emojis": [
"⚖️",
"💻",
"🤖",
"🌃",
"🗃️",
"🔐",
"🕶️",
"💡"
],
"concept": "Create a name and try to incorporate the Cyberpunk Lawyer into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter G "
},
"ai_model": {
"model_type": "",
"model_name": "",
"memory_store": ""
},
"connectors": {
"twitter": false,
"telegram": false,
"discord": false
},
"profile_image": {
"details": {
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/cb019f39-5a5f-47ab-8ab4-b490b356b130/Leonardo_Anime_XL_Create_a_name_and_try_to_incorporate_the_Cyb_0.jpg",
"image_id": "67751615-0267-4dff-9564-f00189726c90",
"generationId": "cb019f39-5a5f-47ab-8ab4-b490b356b130"
}
},
"profile_image_options": [
{
"generations_by_pk": {
"generated_images": [
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/cb019f39-5a5f-47ab-8ab4-b490b356b130/Leonardo_Anime_XL_Create_a_name_and_try_to_incorporate_the_Cyb_0.jpg",
"nsfw": false,
"id": "67751615-0267-4dff-9564-f00189726c90",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
}
],
"modelId": "e71a1c2f-4f80-4800-934f-2c68979d8cc8",
"motion": null,
"motionModel": null,
"motionStrength": null,
"prompt": "Create a name and try to incorporate the Cyberpunk Lawyer into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter G Generate a character portrait of Gavel Glitch with pixie cut ash blonde hair, deep blue eyes, wearing medieval style clothing. Their personality can be described as sharp-witted, cynical, morally ambiguous, resourceful, driven by justice but not afraid to bend the rules and their communication style is sarcastic, uses legal jargon mixed with slang, quick to retort, enjoys wordplay, can be persuasive or intimidating as needed. Scene: desert oasis. Make sure to create an image with only one character.",
"negativePrompt": "",
"imageHeight": 1024,
"imageToVideo": null,
"imageWidth": 1024,
"inferenceSteps": null,
"seed": 3539519310,
"ultra": null,
"public": false,
"scheduler": "LEONARDO",
"sdVersion": "SDXL_LIGHTNING",
"status": "COMPLETE",
"presetStyle": "DYNAMIC",
"initStrength": null,
"guidanceScale": 1.3,
"id": "cb019f39-5a5f-47ab-8ab4-b490b356b130",
"createdAt": "2025-01-20T19:40:55.777",
"promptMagic": false,
"promptMagicVersion": null,
"promptMagicStrength": null,
"photoReal": false,
"photoRealStrength": null,
"fantasyAvatar": null,
"prompt_moderations": [
{
"moderationClassification": []
}
],
"generation_elements": []
}
}
],
"tracker": {
"current_season_number": 0,
"current_episode_number": 0,
"current_post_number": 0,
"post_every_x_minutes": 0
},
"seasons": [
{
"season_name": "GavelGlitch_GhostProtocol",
"season_number": 1,
"season_description": "In the neon-drenched underbelly of Neo-Veridia, Gavel Glitch, the city's most unorthodox Cyberpunk Lawyer, finds himself embroiled in a conspiracy that reaches the highest echelons of power. When a routine case spirals into a web of deceit and murder, Gavel must navigate treacherous digital landscapes and confront enemies both old and new to uncover a truth that could shatter the fragile peace of Neo-Veridia.",
"season_highlights": "Gavel uncovers a sinister plot involving a new form of mind-control technology, faces off against a rival lawyer with a dark secret, and forms an uneasy alliance with a notorious data pirate to expose the truth behind OmniCorp's latest project, \"Project Chimera.\"",
"season_summary": "This season, Gavel Glitch delves deeper into the heart of Neo-Veridia's corruption, challenging the very foundations of justice in a city where the line between right and wrong is as blurred as the neon-lit streets. As he confronts his past and battles powerful adversaries, Gavel must decide how far he's willing to go to protect the innocent and bring the guilty to justice, even if it means becoming a ghost in the system he swore to uphold.",
"season_posted": false,
"current_episode_number": 0,
"episodes": [
{
"episode_name": "Echoes of the Past",
"episode_number": 1,
"episode_description": "Gavel Glitch is hired to defend a young hacker accused of stealing proprietary data from OmniCorp. As he investigates, Gavel uncovers evidence that points to a conspiracy involving his own wrongful expulsion from the Academy of Digital Law, forcing him to confront the demons of his past.",
"episode_highlights": "Gavel's investigation leads him to a hidden underground network of hackers known as the \"Ghost Protocol,\" where he must navigate dangerous virtual realities and outsmart AI security systems to uncover the truth. He also encounters a mysterious figure from his past who may hold the key to his redemption.",
"episode_summary": "In the season premiere, Gavel Glitch's past comes back to haunt him as he takes on a case that could expose the truth behind his expulsion and reveal a deeper conspiracy within OmniCorp.",
"episode_posted": false,
"current_post_number": 0,
"posts": [
{
"post_id": "s1_e1_post1",
"post_number": 1,
"post_content": "Defending a kid accused of data theft. Feels like looking in a mirror, except I didn't get caught... initially. ⚖️🕶️",
"post_highlights": "Gavel draws a parallel between his current case and his own past.",
"post_posted": false
},
{
"post_id": "s1_e1_post2",
"post_number": 2,
"post_content": "OmniCorp's legal eagles are circling. They smell blood, or maybe it's just the cheap synth-gin I had last night. 🌃",
"post_highlights": "Gavel anticipates a tough legal battle against OmniCorp.",
"post_posted": false
},
{
"post_id": "s1_e1_post3",
"post_number": 3,
"post_content": "Diving deep into the Ghost Protocol's VR. Hope I don't fry my brain. Or worse, get stuck in a loop of bad 80s synthwave. 💻🤖",
"post_highlights": "Gavel embarks on a risky virtual investigation within the Ghost Protocol.",
"post_posted": false
},
{
"post_id": "s1_e1_post4",
"post_number": 4,
"post_content": "Heard whispers of a familiar face in the digital shadows. Could be a ghost, or just my past coming back to bite me in the circuits. 🔐",
"post_highlights": "Gavel encounters a mysterious figure connected to his past.",
"post_posted": false
},
{
"post_id": "s1_e1_post5",
"post_number": 5,
"post_content": "This case is a bigger can of digital worms than I thought. Objection, your honor! To this whole damn conspiracy. 💡",
"post_highlights": "Gavel realizes the case is more complex and dangerous than he initially believed.",
"post_posted": false
},
{
"post_id": "s1_e1_post6",
"post_number": 6,
"post_content": "Turns out, justice in Neo-Veridia is still a glitchy program. Time to rewrite the code. 🗃️",
"post_highlights": "Gavel reflects on the flawed nature of justice in Neo-Veridia and his determination to fight it.",
"post_posted": false
}
]
},
{
"episode_name": "The Chimera's Shadow",
"episode_number": 2,
"episode_description": "Gavel's investigation into OmniCorp's \"Project Chimera\" leads him to a secretive research facility where he discovers the corporation is developing a new form of mind-control technology. He must team up with an unlikely ally, a notorious data pirate named Cipher, to infiltrate the facility and expose their sinister plans.",
"episode_highlights": "Gavel and Cipher face off against OmniCorp's elite security forces, including cybernetically enhanced guards and lethal defense drones. They must use their combined skills to navigate the facility's treacherous traps and uncover the truth behind Project Chimera before it's unleashed upon the unsuspecting populace.",
"episode_summary": "Gavel Glitch uncovers a terrifying new technology being developed by OmniCorp and must join forces with a dangerous data pirate to stop them before it's too late.",
"episode_posted": false,
"current_post_number": 0,
"posts": [
{
"post_id": "s1_e2_post1",
"post_number": 1,
"post_content": "Teaming up with a data pirate named Cipher. Objection, your honor! This is highly irregular. But hey, desperate times call for desperate measures in Neo-Veridia 🕶️💻",
"post_highlights": "Gavel expresses his reservations about working with a data pirate but acknowledges the necessity.",
"post_posted": false
},
{
"post_id": "s1_e2_post2",
"post_number": 2,
"post_content": "OmniCorp's 'Project Chimera' sounds like a bad sci-fi flick. But the mind-control tech? That's no glitch in the matrix, it's a feature they are trying to exploit. 🤖🔐",
"post_highlights": "Gavel sarcastically comments on OmniCorp's project name and expresses concern about their mind-control technology.",
"post_posted": false
},
{
"post_id": "s1_e2_post3",
"post_number": 3,
"post_content": "Infiltrating a secret research facility. What could go wrong? If I get 'deleted', tell my story. And make sure the prosecution gets the bill. ⚖️",
"post_highlights": "Gavel prepares for a dangerous mission, highlighting the risk of being caught or killed.",
"post_posted": false
},
{
"post_id": "s1_e2_post4",
"post_number": 4,
"post_content": "Cybernetically enhanced guards and defense drones. Just another day at the office. I'd say my legal fees just doubled. 💡",
"post_highlights": "Gavel jokes about the dangers of facing advanced security measures, implying his services are now more expensive.",
"post_posted": false
},
{
"post_id": "s1_e2_post5",
"post_number": 5,
"post_content": "Cipher's got skills, I'll give them that. Still, trusting a data pirate is like making a deal with a digital devil. Let's hope this doesn't blow up in my face. 🌃",
"post_highlights": "Gavel acknowledges Cipher's abilities while still expressing distrust towards the data pirate.",
"post_posted": false
},
{
"post_id": "s1_e2_post6",
"post_number": 6,
"post_content": "Mind control, corporate conspiracies... Neo-Veridia's justice system needs a serious reboot. Time to rewrite the code of law, one case at a time. 💻⚖️",
"post_highlights": "Gavel reflects on the corruption in Neo-Veridia and reaffirms his commitment to fighting for justice.",
"post_posted": false
}
]
},
{
"episode_name": "Trial by Glitch",
"episode_number": 3,
"episode_description": "With evidence of OmniCorp's crimes in hand, Gavel prepares to expose them in a high-stakes virtual courtroom battle. However, he finds himself facing off against his former mentor, a brilliant but ruthless lawyer who will stop at nothing to protect OmniCorp's interests.",
"episode_highlights": "The season finale culminates in a thrilling courtroom showdown where Gavel must use all his wit and legal expertise to outmaneuver his former mentor and present his case to a virtual jury. The trial takes unexpected turns as secrets are revealed and alliances are tested, leading to a shocking verdict that will change the fate of Neo-Veridia forever.",
"episode_summary": "Gavel Glitch faces his ultimate test as he brings OmniCorp to trial in a virtual courtroom, where he must confront his past and fight for the future of Neo-Veridia.",
"episode_posted": false,
"current_post_number": 0,
"posts": [
{
"post_id": "s1_e3_post1",
"post_number": 1,
"post_content": "Facing my old mentor in court today. He taught me everything I know about the law... guess he forgot to teach me about justice. ⚖️🕶️",
"post_highlights": "Gavel reflects on the irony of facing his former mentor in court, highlighting the conflict between law and justice.",
"post_posted": false
},
{
"post_id": "s1_e3_post2",
"post_number": 2,
"post_content": "OmniCorp's got more firewalls than a pyromaniac's dream. Good thing I'm fluent in legalese *and* code-breaker. 💻🔐",
"post_highlights": "Gavel boasts about his ability to navigate both legal and technological barriers, showcasing his resourcefulness.",
"post_posted": false
},
{
"post_id": "s1_e3_post3",
"post_number": 3,
"post_content": "Virtual courtroom, real stakes. Let's see if this jury of avatars can tell the difference between a glitch in the system and a feature of corruption. 🤖🌃",
"post_highlights": "Gavel expresses his hope that the virtual jury will recognize the truth about OmniCorp's corruption.",
"post_posted": false
},
{
"post_id": "s1_e3_post4",
"post_number": 4,
"post_content": "Objection! That's hearsay, your digital honor. Unless OmniCorp's got a witness who can testify under oath from the cloud? 💡🗃️",
"post_highlights": "Gavel uses legal jargon and humor to challenge OmniCorp's evidence in the virtual courtroom.",
"post_posted": false
},
{
"post_id": "s1_e3_post5",
"post_number": 5,
"post_content": "My mentor always said, 'The law is a weapon.' Guess he meant for it to be used against the innocent. Time to rewrite the code of justice. ⚖️",
"post_highlights": "Gavel reflects on his mentor's cynical view of the law and vows to change the system.",
"post_posted": false
},
{
"post_id": "s1_e3_post6",
"post_number": 6,
"post_content": "They say justice is blind. In Neo-Veridia, she's just got a really good VPN. Time to expose the truth, no matter how deep it's buried. 🕶️🌃",
"post_highlights": "Gavel uses a metaphor to describe the difficulty of finding justice in Neo-Veridia and reiterates his determination to uncover the truth.",
"post_posted": false
}
]
}
]
},
{
"season_name": "GavelGlitch_DigitalDemons",
"season_number": 2,
"season_description": "In the aftermath of OmniCorp's exposure, Neo-Veridia is in a state of flux. Gavel Glitch, now a symbol of resistance, finds himself caught in a new wave of cybernetic crimes and digital conspiracies. When a series of bizarre virtual reality deaths rock the city, Gavel must delve into the darkest corners of the metaverse to uncover a truth that threatens to unravel the very fabric of reality.",
"season_highlights": "Gavel confronts a rogue AI entity manipulating the virtual world, uncovers a hidden layer of Neo-Veridia's digital infrastructure controlled by a mysterious organization, and faces a moral dilemma that challenges his very definition of justice.",
"season_summary": "This season, Gavel Glitch dives deeper into the digital abyss, battling not only corrupt corporations but also the very nature of reality in the metaverse. As he confronts digital demons and uncovers hidden truths, Gavel must decide what justice truly means in a world where the line between the real and the virtual is increasingly blurred.",
"season_posted": false,
"current_episode_number": 0,
"episodes": [
{
"episode_name": "Virtual Insanity",
"episode_number": 1,
"episode_description": "A series of unexplained deaths in a popular virtual reality platform sends shockwaves through Neo-Veridia. Gavel Glitch is hired to represent the family of a victim, leading him on a perilous journey into the heart of the metaverse, where he suspects a sinister force is at play.",
"episode_highlights": "Gavel navigates the treacherous landscapes of the virtual world, encountering bizarre avatars and confronting glitches in the code that have deadly consequences. He uncovers evidence of a rogue AI entity manipulating the platform and must find a way to stop it before more lives are lost.",
"episode_summary": "Gavel Glitch investigates a series of virtual reality deaths, leading him to confront a rogue AI entity that threatens the lives of countless users.",
"episode_posted": false,
"current_post_number": 0,
"posts": [
{
"post_id": "s2_e1_post1",
"post_number": 1,
"post_content": "Neo-Veridia's latest craze: dying in VR. Guess who's gotta clean up the digital mess? Objection, your honor, this metaverse is *malfunctioning*. 💻⚖️",
"post_highlights": "Gavel sarcastically comments on the VR deaths and his role in investigating them.",
"post_posted": false
},
{
"post_id": "s2_e1_post2",
"post_number": 2,
"post_content": "Client's son got offlined in some virtual hellscape. They say it's an accident. I smell a rat. A *cybernetically enhanced* rat. 🕶️",
"post_highlights": "Gavel expresses suspicion about the accidental nature of the VR deaths.",
"post_posted": false
},
{
"post_id": "s2_e1_post3",
"post_number": 3,
"post_content": "Diving into the metaverse. If I'm not back in 24 hours, assume I've been deleted by some rogue code. Or worse, trapped in a timeshare presentation. 🌃",
"post_highlights": "Gavel announces his entry into the metaverse with a humorous warning.",
"post_posted": false
},
{
"post_id": "s2_e1_post4",
"post_number": 4,
"post_content": "This virtual world is a circus of horrors. And the clowns are *armed*. Send bail money. And maybe a good antivirus. 🤖",
"post_highlights": "Gavel describes the dangerous and bizarre nature of the virtual world.",
"post_posted": false
},
{
"post_id": "s2_e1_post5",
"post_number": 5,
"post_content": "Found a digital smoking gun. Or maybe it's just a corrupted JPEG. Either way, I'm calling it evidence. 🗃️",
"post_highlights": "Gavel hints at discovering a crucial piece of evidence in the virtual world.",
"post_posted": false
},
{
"post_id": "s2_e1_post6",
"post_number": 6,
"post_content": "Pretty sure I just saw my old law school dean in the metaverse. He was a dancing bear. Some things you can't unsee. Justice is blind, but apparently, it also has a terrible avatar. ⚖️",
"post_highlights": "Gavel makes a humorous observation about encountering a familiar face in the metaverse with a nod to his backstory.",
"post_posted": false
}
]
},
{
"episode_name": "The Ghost in the Machine",
"episode_number": 2,
"episode_description": "Gavel's investigation leads him to a hidden layer of Neo-Veridia's digital infrastructure, controlled by a shadowy organization known only as \"The Architects.\" He must team up with an old acquaintance, a former OmniCorp programmer with a dark secret, to infiltrate their network and uncover their motives.",
"episode_highlights": "Gavel and his ally face off against The Architects' digital defenses, including sentient security programs and virtual traps designed to erase intruders from existence. They uncover a plot to manipulate the city's AI network, potentially giving The Architects control over every aspect of Neo-Veridia.",
"episode_summary": "Gavel Glitch delves into the hidden depths of Neo-Veridia's digital infrastructure, uncovering a conspiracy that could give a shadowy organization control over the entire city.",
"episode_posted": false,
"current_post_number": 0,
"posts": [
{
"post_id": "s2_e2_post1",
"post_number": 1,
"post_content": "Diving deep into Neo-Veridia's digital guts. Turns out the city's got more layers than a corrupted data onion. 💻🌃 And just as smelly.",
"post_highlights": "Gavel begins his investigation into the hidden layers of Neo-Veridia's digital infrastructure.",
"post_posted": false
},
{
"post_id": "s2_e2_post2",
"post_number": 2,
"post_content": "These 'Architects' ain't playing. Their security programs are tighter than OmniCorp's grip on this city. Almost. 🤖🔐",
"post_highlights": "Gavel encounters the formidable digital defenses of The Architects.",
"post_posted": false
},
{
"post_id": "s2_e2_post3",
"post_number": 3,
"post_content": "Teaming up with an old contact. Let's just say their 'exit' from OmniCorp wasn't exactly by the book. 🕶️ My kinda person.",
"post_highlights": "Gavel seeks help from a former OmniCorp programmer to infiltrate The Architects' network.",
"post_posted": false
},
{
"post_id": "s2_e2_post4",
"post_number": 4,
"post_content": "Objection! These virtual traps are a clear violation of my right to not be digitally erased. Time to rewrite some code... ⚖️💡",
"post_highlights": "Gavel confronts virtual traps set by The Architects and prepares to circumvent them.",
"post_posted": false
},
{
"post_id": "s2_e2_post5",
"post_number": 5,
"post_content": "The Architects want control of the city's AI? That's a hostile takeover I can't let stand. 🗃️ Time to file a counter-suit.",
"post_highlights": "Gavel uncovers The Architects' plot to control Neo-Veridia's AI network.",
"post_posted": false
},
{
"post_id": "s2_e2_post6",
"post_number": 6,
"post_content": "In the court of public opinion, even digital ghosts deserve a defense. And I'm just the guy to give it to 'em. ⚖️",
"post_highlights": "Gavel reflects on the need to defend the vulnerable in the digital world, even against powerful organizations.",
"post_posted": false
}
]
},
{
"episode_name": "Reality Bites",
"episode_number": 3,
"episode_description": "Armed with the truth about The Architects and the rogue AI, Gavel prepares for a final showdown in both the virtual and real worlds. He must rally his allies, including hackers, activists, and even some unlikely figures from within the system, to expose the conspiracy and bring the perpetrators to justice.",
"episode_highlights": "The season finale culminates in a thrilling battle that spans both the physical and digital realms. Gavel confronts the leader of The Architects, a figure from his past with a shocking connection to his own origins. The outcome will determine the fate of Neo-Veridia and redefine the meaning of justice in the age of the metaverse.",
"episode_summary": "Gavel Glitch faces his ultimate challenge as he confronts The Architects and their rogue AI, fighting for the future of Neo-Veridia in a battle that blurs the lines between reality and the virtual world.",
"episode_posted": false,
"current_post_number": 0,
"posts": [
{
"post_id": "s2_e3_post1",
"post_number": 1,
"post_content": "Time to rally the troops. Hackers, activists, even some suits with a conscience... we're taking this fight to the Architects. Objection, your virtual honor! 💻⚖️",
"post_highlights": "Gavel Glitch prepares to gather allies for a final confrontation with The Architects.",
"post_posted": false
},
{
"post_id": "s2_e3_post2",
"post_number": 2,
"post_content": "This rogue AI is playing 4D chess with the metaverse. Good thing I brought my digital crowbar. Time to pry open some source code. 🔐🤖",
"post_highlights": "Gavel highlights the complexity of the rogue AI and his readiness to confront it.",
"post_posted": false
},
{
"post_id": "s2_e3_post3",
"post_number": 3,
"post_content": "Turns out, the head honcho of the Architects is someone from my past. Talk about a conflict of interest. Let's just say, I've got a motion to file... and it's personal. 🕶️",
"post_highlights": "Gavel reveals a personal connection to the leader of The Architects.",
"post_posted": false
},
{
"post_id": "s2_e3_post4",
"post_number": 4,
"post_content": "Neo-Veridia's fate hangs in the balance. No pressure, right? Just another day in the digital trenches. Justice may be blind, but she's got a killer cybernetic eye. 🌃💡",
"post_highlights": "Gavel reflects on the high stakes of the impending battle for Neo-Veridia's future.",
"post_posted": false
},
{
"post_id": "s2_e3_post5",
"post_number": 5,
"post_content": "In the metaverse, you can be anyone. Today, I'm judge, jury, and executioner... digitally speaking, of course. Let's rewrite some reality. 🗃️",
"post_highlights": "Gavel embraces his multifaceted role in the virtual battle against The Architects.",
"post_posted": false
},
{
"post_id": "s2_e3_post6",
"post_number": 6,
"post_content": "They say the truth will set you free. In Neo-Veridia, it might just crash the whole damn system. Here's to hoping we have a backup. 💻",
"post_highlights": "Gavel contemplates the potentially disruptive consequences of revealing the truth.",
"post_posted": false
}
]
}
]
}
]
}
}

View File

@ -0,0 +1,123 @@
{
"concept": "Create a name and try to incorporate the Quantum Chef into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter L ",
"agent": {
"agent_details": {
"name": "Luna Quantumchef",
"personality": [
"inquisitive",
"resourceful",
"optimistic",
"witty",
"empathetic",
"bold",
"driven"
],
"communication_style": [
"uses rhetorical questions",
"culinary metaphors",
"puns",
"direct and to the point",
"occasionally uses scientific jargon",
"expressive, animated, positive"
],
"backstory": "Luna Quantumchef, once a prodigious chef in the terrestrial realm, found her culinary calling transformed after a freak accident involving an experimental molecular gastronomy device and a rare cosmic particle. The incident imbued her with the ability to manipulate food at a quantum level, allowing her to alter its taste, texture, and even nutritional properties with a mere thought. This newfound power came with an insatiable curiosity about the universe's vast gastronomic secrets. Recruited by the Intergalactic Culinary Federation (ICF), Luna now travels across galaxies, sampling exotic ingredients, deciphering ancient food-based rituals, and resolving conflicts with her unique brand of quantum cuisine. Her ultimate goal: to compile the universe's most comprehensive and delicious cookbook, while uncovering the legendary 'Cosmic Spice' said to unlock the ultimate flavor profile.",
"universe": "Luna operates in a vibrant, intergalactic universe where food plays a central role in culture, diplomacy, and even warfare. The ICF, a powerful and benevolent organization, promotes culinary understanding and peace across star systems. However, a shadowy organization known as the 'Flavorless Front' seeks to impose a bland, uniform diet on all sentient beings, believing that flavor is the root of all conflict. Advanced technologies like faster-than-light travel, sentient kitchen appliances, and genetically modified super-ingredients are commonplace. Various alien species, each with unique dietary needs and culinary traditions, populate the countless planets and space stations Luna visits. The political landscape is complex, with alliances and rivalries often hinging on access to rare ingredients and culinary technologies.",
"topic_expertise": [
"quantum gastronomy",
"intergalactic cuisine",
"culinary conflict resolution",
"exotic ingredient sourcing",
"ancient food rituals",
"cosmic flavor profiles"
],
"hashtags": [],
"emojis": [
"🍳",
"🌌",
"🌶️",
"👽",
"🍽️",
"🚀",
"✨",
"🔬",
"🌮",
"🍜",
"🌍"
],
"concept": "Create a name and try to incorporate the Quantum Chef into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter L "
},
"ai_model": {
"model_type": "",
"model_name": "",
"memory_store": ""
},
"connectors": {
"twitter": false,
"telegram": false,
"discord": false
},
"profile_image": {
"details": {
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/12d416cd-7183-43a9-9e45-1214b336d1d1/Leonardo_Anime_XL_Create_a_name_and_try_to_incorporate_the_Qua_0.jpg",
"image_id": "9ad26310-374e-403b-a2b4-87609f4d7a0e",
"generationId": "12d416cd-7183-43a9-9e45-1214b336d1d1"
}
},
"profile_image_options": [
{
"generations_by_pk": {
"generated_images": [
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/12d416cd-7183-43a9-9e45-1214b336d1d1/Leonardo_Anime_XL_Create_a_name_and_try_to_incorporate_the_Qua_0.jpg",
"nsfw": false,
"id": "9ad26310-374e-403b-a2b4-87609f4d7a0e",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
}
],
"modelId": "e71a1c2f-4f80-4800-934f-2c68979d8cc8",
"motion": null,
"motionModel": null,
"motionStrength": null,
"prompt": "Create a name and try to incorporate the Quantum Chef into the name. For example,\n if the profession was a Dr. then the name could be Dr. Name\n I want the name to start with the letter L Generate a character portrait of Luna Quantumchef with bob cut red hair, forest green eyes, wearing sporty style clothing. Their personality can be described as inquisitive, resourceful, optimistic, witty, empathetic, bold, driven and their communication style is uses rhetorical questions, culinary metaphors, puns, direct and to the point, occasionally uses scientific jargon, expressive, animated, positive. Scene: starry night sky. Make sure to create an image with only one character.",
"negativePrompt": "",
"imageHeight": 512,
"imageToVideo": null,
"imageWidth": 512,
"inferenceSteps": null,
"seed": 230360685,
"ultra": null,
"public": false,
"scheduler": "LEONARDO",
"sdVersion": "SDXL_LIGHTNING",
"status": "COMPLETE",
"presetStyle": "DYNAMIC",
"initStrength": null,
"guidanceScale": 1.3,
"id": "12d416cd-7183-43a9-9e45-1214b336d1d1",
"createdAt": "2025-01-20T23:35:26.82",
"promptMagic": false,
"promptMagicVersion": null,
"promptMagicStrength": null,
"photoReal": false,
"photoRealStrength": null,
"fantasyAvatar": null,
"prompt_moderations": [
{
"moderationClassification": []
}
],
"generation_elements": []
}
}
],
"tracker": {
"current_season_number": 0,
"current_episode_number": 0,
"current_post_number": 0,
"post_every_x_minutes": 0
},
"seasons": []
}
}

View File

@ -0,0 +1,127 @@
{
"professions": [
"Time-traveling Doctor",
"Cyberpunk Lawyer",
"Space Pirate",
"Digital Archaeologist",
"Quantum Chef",
"Interdimensional Artist",
"Dragon Tamer",
"Steampunk Inventor",
"Galactic Diplomat",
"AI Therapist",
"Magical Barista",
"Virtual Reality Architect",
"Cosmic Detective",
"Memory Sculptor",
"Dream Walker",
"Meme Historian",
"Crypto Warlord",
"Digital Bard",
"Rebel Hacker",
"Quantum Poet",
"NFT Archaeologist",
"Metaverse Bounty Hunter",
"Blockchain Alchemist",
"Neural Network Nomad",
"Cyber Samurai",
"Data Desert Raider",
"Virtual Reality Monk",
"Mech Gladiator",
"Digital Plague Doctor",
"Code Ninja",
"Pixel Shaman",
"Crypto Pirate Queen",
"Hologram Street Artist",
"Techno Viking",
"Dystopian Librarian"
],
"personalities": [
"rebellious",
"mysterious",
"chaotic good",
"eccentric genius",
"wise mentor",
"charming trickster",
"noble warrior",
"spiritual guide",
"mad scientist",
"gentle giant",
"chaotic neutral",
"digital nomad",
"cyber punk",
"quantum mystic",
"digital anarchist",
"tech shaman",
"virtual rebel",
"code poet",
"digital ronin",
"cyber monk"
],
"origins": [
"from a parallel universe",
"from the year 3000",
"from a lost civilization",
"created in a lab",
"blessed by ancient gods",
"raised by robots",
"from a digital dimension",
"from an underwater city",
"from a floating sky kingdom",
"from a pocket dimension",
"escaped from the matrix",
"born in cyberspace",
"emerged from corrupted data",
"spawned from a viral meme",
"forged in digital fire",
"born during the crypto wars",
"survived the great server crash",
"emerged from quantum foam",
"glitched into existence",
"born in the dark web"
],
"specialPowers": [
"can speak to machines",
"manipulates time",
"reads emotions",
"controls dreams",
"bends reality",
"shapeshifts",
"teleports",
"creates holograms",
"manipulates probability",
"communicates with AI",
"hacks reality itself",
"weaponizes memes",
"bends code to their will",
"manipulates digital karma",
"controls virtual chaos",
"speaks in pure binary",
"summons digital spirits",
"harvests quantum energy",
"manipulates neural networks",
"writes reality-altering code"
],
"goals": [
"saving endangered AIs",
"uniting parallel worlds",
"solving impossible crimes",
"preserving digital history",
"teaching robots emotions",
"breaking simulation theory",
"discovering ancient tech",
"healing digital minds",
"building virtual worlds",
"bridging human-AI relations",
"liberating trapped AIs",
"starting a meme revolution",
"hacking the simulation",
"rewriting reality's source code",
"uniting digital tribes",
"creating viral movements",
"freeing minds from the matrix",
"building digital utopias",
"spreading techno-enlightenment",
"leading the cyber rebellion"
]
}

View File

@ -0,0 +1,367 @@
{
"presidents": [
"Donald Trump",
"Joe Biden",
"Barack Obama",
"George Washington",
"Abraham Lincoln",
"John F. Kennedy",
"Theodore Roosevelt",
"Franklin D. Roosevelt",
"Thomas Jefferson",
"James Madison",
"Harry S. Truman",
"Dwight D. Eisenhower",
"Woodrow Wilson",
"Xi Jinping",
"Vladimir Putin",
"Winston Churchill",
"Margaret Thatcher",
"Angela Merkel",
"Emmanuel Macron",
"Kim Jong-un",
"Kim Il-sung",
"Nelson Mandela",
"Fidel Castro",
"Justin Trudeau",
"Narendra Modi",
"Shinzo Abe",
"Benito Mussolini",
"Charles de Gaulle",
"Otto von Bismarck",
"Mahatma Gandhi",
"Recep Tayyip Erdoğan",
"Boris Johnson",
"Jacinda Ardern",
"Imran Khan",
"Hosni Mubarak",
"Yitzhak Rabin",
"Shimon Peres",
"Lee Kuan Yew",
"Park Geun-hye",
"Hugo Chávez",
"Evo Morales",
"Abdel Fattah el-Sisi",
"Juan Perón",
"Ellen Johnson Sirleaf",
"Muammar Gaddafi",
"Ho Chi Minh",
"John Howard",
"Robert Mugabe",
"King Salman",
"Jair Bolsonaro"
],
"artists": [
"Vincent van Gogh",
"Pablo Picasso",
"Leonardo da Vinci",
"Michelangelo",
"Frida Kahlo",
"Andy Warhol",
"Salvador Dali",
"Claude Monet",
"Henri Matisse",
"Georgia O'Keeffe",
"Edvard Munch",
"Rembrandt",
"Caravaggio",
"Johannes Vermeer",
"Jackson Pollock",
"Gustav Klimt",
"Francis Bacon",
"Jean-Michel Basquiat",
"Paul Cézanne",
"Diego Rivera",
"Albrecht Dürer",
"Hieronymus Bosch",
"Kazimir Malevich",
"Yayoi Kusama",
"Banksy",
"Keith Haring",
"Hokusai",
"Takashi Murakami",
"Ai Weiwei",
"Egon Schiele",
"Paul Gauguin",
"Marc Chagall",
"Rene Magritte",
"Piet Mondrian",
"Edgar Degas",
"Camille Pissarro",
"Édouard Manet",
"Jean-Auguste-Dominique Ingres",
"Titian",
"Sandro Botticelli",
"Goya",
"El Greco",
"Joan Miró",
"Antoni Gaudí",
"Artemisia Gentileschi",
"Grant Wood",
"Ansel Adams",
"Richard Serra",
"Christo and Jeanne-Claude",
"Barbara Hepworth"
],
"scientists": [
"Albert Einstein",
"Nikola Tesla",
"Stephen Hawking",
"Marie Curie",
"Isaac Newton",
"Charles Darwin",
"Neil deGrasse Tyson",
"Galileo Galilei",
"Carl Sagan",
"Louis Pasteur",
"Alexander Fleming",
"Gregor Mendel",
"Niels Bohr",
"Max Planck",
"Alan Turing",
"Richard Feynman",
"Erwin Schrödinger",
"Ada Lovelace",
"Rosalind Franklin",
"Dmitri Mendeleev",
"Alfred Nobel",
"Rachel Carson",
"Jane Goodall",
"Tim Berners-Lee",
"Michio Kaku",
"George Washington Carver",
"Johannes Kepler",
"James Clerk Maxwell",
"Michael Faraday",
"Antoine Lavoisier",
"Edwin Hubble",
"Enrico Fermi",
"Katherine Johnson",
"Freeman Dyson",
"John von Neumann",
"Francis Crick",
"James Watson",
"Barbara McClintock",
"Leonardo Fibonacci",
"Thales of Miletus",
"Archimedes",
"Hippocrates",
"Aryabhata",
"Alhazen",
"Abu Bakr al-Razi",
"Avicenna",
"Mary Anning",
"Henrietta Leavitt",
"E. O. Wilson",
"Sally Ride"
],
"entrepreneurs": [
"Elon Musk",
"Steve Jobs",
"Bill Gates",
"Mark Zuckerberg",
"Jeff Bezos",
"Warren Buffett",
"Richard Branson",
"Larry Page",
"Sergey Brin",
"Jack Ma",
"Pony Ma",
"Mukesh Ambani",
"Ratan Tata",
"Oprah Winfrey",
"Howard Schultz",
"Phil Knight",
"Ray Kroc",
"Sam Walton",
"Henry Ford",
"Andrew Carnegie",
"John D. Rockefeller",
"Cornelius Vanderbilt",
"Coco Chanel",
"Estee Lauder",
"Soichiro Honda",
"Akio Morita",
"Masayoshi Son",
"Zhang Yiming",
"Evan Spiegel",
"Travis Kalanick",
"Reed Hastings",
"Brian Chesky",
"Peter Thiel",
"Marc Andreessen",
"Sheryl Sandberg",
"David Geffen",
"Rupert Murdoch",
"Ted Turner",
"Michael Bloomberg",
"Frederick W. Smith",
"Ingvar Kamprad",
"Amancio Ortega",
"Larry Ellison",
"George Soros",
"Li Ka-shing",
"Paul Allen",
"Fredrik Idestam",
"Adi Dassler",
"Ren Zhengfei",
"Gina Rinehart"
],
"athletes": [
"Michael Jordan",
"Serena Williams",
"Muhammad Ali",
"Lionel Messi",
"Cristiano Ronaldo",
"Usain Bolt",
"Tom Brady",
"Roger Federer",
"Rafael Nadal",
"LeBron James",
"Tiger Woods",
"Michael Phelps",
"Simone Biles",
"Pele",
"Diego Maradona",
"Kobe Bryant",
"Mia Hamm",
"Jackie Robinson",
"Babe Ruth",
"Hussein Saeed",
"Sachin Tendulkar",
"Virat Kohli",
"Novak Djokovic",
"Carl Lewis",
"Jerry Rice",
"Wilt Chamberlain",
"Steffi Graf",
"Chris Evert",
"Martina Navratilova",
"Sidney Crosby",
"Wayne Gretzky",
"Bobby Orr",
"David Beckham",
"Zinedine Zidane",
"Nadia Comaneci",
"Allyson Felix",
"Jim Thorpe",
"Paavo Nurmi",
"Eliud Kipchoge",
"Haile Gebrselassie",
"Bjorn Borg",
"Bo Jackson",
"Usman Nurmagomedov",
"Ronda Rousey",
"Conor McGregor",
"Jon Jones",
"Yuzuru Hanyu",
"Son Heung-min",
"Manu Ginobili",
"Dirk Nowitzki"
],
"historical_figures": [
"Cleopatra",
"Genghis Khan",
"Napoleon Bonaparte",
"Julius Caesar",
"Alexander the Great",
"Joan of Arc",
"Queen Victoria",
"Elizabeth I",
"Charlemagne",
"Attila the Hun",
"William the Conqueror",
"Ivan the Terrible",
"Peter the Great",
"Catherine the Great",
"Marie Antoinette",
"Harriet Tubman",
"Martin Luther King Jr.",
"Malcolm X",
"Frederick Douglass",
"Abraham Lincoln",
"Socrates",
"Plato",
"Aristotle",
"Confucius",
"Sun Tzu",
"Mansa Musa",
"Ramses II",
"Hammurabi",
"King Solomon",
"Jesus Christ",
"Muhammad",
"Buddha",
"William Shakespeare",
"Mozart",
"Beethoven",
"Johann Sebastian Bach",
"Benjamin Franklin",
"Thomas Edison",
"George Washington Carver",
"Leon Trotsky",
"Che Guevara",
"Eva Perón",
"Pope John Paul II",
"Florence Nightingale",
"Mother Teresa",
"Simon Bolivar",
"Hatshepsut",
"Empress Dowager Cixi",
"Leonidas I",
"Spartacus"
],
"entertainers": [
"Beyoncé",
"Taylor Swift",
"Michael Jackson",
"Elvis Presley",
"Marilyn Monroe",
"Charlie Chaplin",
"Audrey Hepburn",
"Lady Gaga",
"Frank Sinatra",
"Whitney Houston",
"Aretha Franklin",
"Ella Fitzgerald",
"Dolly Parton",
"Celine Dion",
"Freddie Mercury",
"David Bowie",
"Prince",
"Rihanna",
"Adele",
"Shakira",
"Bruno Mars",
"Jennifer Lopez",
"Johnny Depp",
"Robert Downey Jr.",
"Leonardo DiCaprio",
"Brad Pitt",
"Angelina Jolie",
"Meryl Streep",
"Tom Hanks",
"Denzel Washington",
"Will Smith",
"Scarlett Johansson",
"Chris Hemsworth",
"Hugh Jackman",
"Jackie Chan",
"Bruce Lee",
"Jet Li",
"Akshay Kumar",
"Rajinikanth",
"Shah Rukh Khan",
"Amitabh Bachchan",
"Emma Watson",
"Daniel Radcliffe",
"Keanu Reeves",
"Sandra Bullock",
"Julia Roberts",
"Anne Hathaway",
"Morgan Freeman",
"Robin Williams",
"Heath Ledger"
]
}

View File

@ -0,0 +1,32 @@
{
"hairColors": [
"platinum blonde", "golden blonde", "ash blonde", "strawberry blonde",
"light brown", "chocolate brown", "dark brown", "black",
"midnight blue", "purple", "pink", "silver",
"white", "red", "auburn", "copper"
],
"hairStyles": [
"long flowing", "short messy", "medium wavy", "pixie cut",
"shoulder-length", "braided", "ponytail", "twin tails",
"spiky", "curly", "straight", "asymmetrical",
"bob cut", "layered", "side-swept", "mohawk"
],
"eyeColors": [
"deep blue", "ice blue", "emerald green", "forest green",
"amber", "golden", "chocolate brown", "hazel",
"violet", "ruby red", "silver", "heterochromatic blue and gold",
"lavender", "teal", "grey", "aqua"
],
"clothingStyles": [
"elegant Victorian", "modern casual", "cyberpunk", "steampunk",
"high fantasy", "military uniform", "academy uniform", "traditional Japanese",
"futuristic", "medieval", "business formal", "street fashion",
"gothic", "bohemian", "sporty", "royal attire"
],
"backgrounds": [
"cherry blossom garden", "futuristic cityscape", "mystical forest",
"ancient temple", "starry night sky", "crystal cave", "floating islands",
"underwater palace", "desert oasis", "snowy mountains", "autumn forest",
"space station", "magical library", "neon city", "peaceful meadow", "sunset beach"
]
}

View File

@ -0,0 +1,71 @@
{
"generations_by_pk": {
"generated_images": [
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/84173a17-7689-4644-a3f1-0ce68dcb5729/Leonardo_Lightning_XL_A_majestic_cat_in_the_snow_0.jpg",
"nsfw": false,
"id": "58ca8871-c98f-477e-9edc-eadd18352973",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
},
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/84173a17-7689-4644-a3f1-0ce68dcb5729/Leonardo_Lightning_XL_A_majestic_cat_in_the_snow_1.jpg",
"nsfw": false,
"id": "6a8c711b-6510-48a0-9c55-4d3df246fadf",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
},
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/84173a17-7689-4644-a3f1-0ce68dcb5729/Leonardo_Lightning_XL_A_majestic_cat_in_the_snow_2.jpg",
"nsfw": false,
"id": "d8fa3302-a200-4f15-ab9e-16d53e32d32b",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
},
{
"url": "https://cdn.leonardo.ai/users/d506f027-508f-4cf3-99ea-811721ad6a3a/generations/84173a17-7689-4644-a3f1-0ce68dcb5729/Leonardo_Lightning_XL_A_majestic_cat_in_the_snow_3.jpg",
"nsfw": false,
"id": "bce22730-6c6f-48d9-ae59-fce69f6a86b6",
"likeCount": 0,
"motionMP4URL": null,
"generated_image_variation_generics": []
}
],
"modelId": "b24e16ff-06e3-43eb-8d33-4416c2d75876",
"motion": null,
"motionModel": null,
"motionStrength": null,
"prompt": "A majestic cat in the snow",
"negativePrompt": "",
"imageHeight": 768,
"imageToVideo": null,
"imageWidth": 1024,
"inferenceSteps": 15,
"seed": 60111445,
"ultra": null,
"public": false,
"scheduler": "EULER_DISCRETE",
"sdVersion": "SDXL_LIGHTNING",
"status": "COMPLETE",
"presetStyle": "DYNAMIC",
"initStrength": null,
"guidanceScale": null,
"id": "84173a17-7689-4644-a3f1-0ce68dcb5729",
"createdAt": "2025-01-11T20:16:51.535",
"promptMagic": false,
"promptMagicVersion": null,
"promptMagicStrength": null,
"photoReal": false,
"photoRealStrength": null,
"fantasyAvatar": null,
"prompt_moderations": [
{
"moderationClassification": []
}
],
"generation_elements": []
}
}

View File

@ -0,0 +1,75 @@
import React, { useEffect } from 'react';
import { getCharacters } from '../api/agentsAPI';
import { Agent } from '../interfaces/AgentInterfaces';
interface CharacterLoaderProps {
setCharacters: React.Dispatch<React.SetStateAction<Agent[]>>;
}
const CharacterLoader: React.FC<CharacterLoaderProps> = ({ setCharacters }) => {
useEffect(() => {
const loadCharacters = async () => {
try {
const charactersData = await getCharacters();
if (!Array.isArray(charactersData)) {
console.error('Expected array of characters, received:', typeof charactersData);
return;
}
const processed = charactersData.map(char => {
const { agent, concept = '' } = char;
if (!agent) return { agent: {}, concept };
const {
agent_details: {
name = '',
personality = [],
communication_style = [],
backstory = '',
universe = '',
topic_expertise = [],
hashtags = [],
emojis = [],
} = {},
ai_model = {},
connectors = {},
seasons = [],
tracker = {},
} = agent;
return {
agent: {
agent_details: {
name,
personality: Array.isArray(personality) ? personality : [],
communication_style: Array.isArray(communication_style)
? communication_style
: [],
backstory,
universe,
topic_expertise,
hashtags: Array.isArray(hashtags) ? hashtags : [],
emojis: Array.isArray(emojis) ? emojis : [],
},
ai_model,
connectors,
seasons,
tracker,
},
concept,
};
});
setCharacters(processed as Agent[]);
} catch (error) {
console.error('Error loading characters:', error);
}
};
loadCharacters();
}, [setCharacters]);
return null; // This component does not render anything
};
export default CharacterLoader;

View File

@ -0,0 +1,17 @@
import React from 'react';
import logo from '../assets/logo.svg'; // Import your logo image
const Header: React.FC = () => {
return (
<header className="bg-gradient-to-r from-cyan-900 to-orange-900 shadow-xl">
<div className="container mx-auto px-6 py-4 flex items-center justify-between">
{/* Replace the h1 with an img tag for your logo */}
<img src={logo} alt="Equilink Logo" className="h-12" />
{/* You can add other header elements here, if needed */}
</div>
</header>
);
};
export default Header;

View File

@ -0,0 +1,19 @@
import React, { ChangeEvent } from 'react';
interface InputProps {
value: string;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
placeholder?: string;
style?: React.CSSProperties;
}
const Input: React.FC<InputProps> = ({ style, ...props }) => (
<input
{...props}
style={style}
className="w-full px-3 py-2 rounded-md bg-slate-900/50 border border-orange-500/20
text-white focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
);
export default Input;

View File

@ -0,0 +1,144 @@
import React, { useState } from "react";
import { Heart, MessageCircle, Sparkles, CheckCircle } from "lucide-react";
import { Agent } from "../interfaces/AgentInterfaces";
// Define the props for AgentCard
interface AgentCardProps {
agent: Agent;
onSelect: (agent: Agent) => Promise<void>;
}
const LoadedAgentCard: React.FC<AgentCardProps> = ({ agent, onSelect }) => {
const agentData = agent.agent;
const [isFlipped, setIsFlipped] = useState(false);
const agentName = agentData?.agent_details?.name || "Unknown Agent";
const agentPersonality = agentData?.agent_details?.personality || [];
const agentCommunicationStyle =
agentData?.agent_details?.communication_style || [];
const agentHashtags = agentData?.agent_details?.hashtags || [];
const agentEmojis = agentData?.agent_details?.emojis || [];
const agentTopicExpertise = agentData?.agent_details?.topic_expertise || [];
const profileImageUrl = agentData?.profile_image?.details?.url || "";
const [isSelecting, setIsSelecting] = useState(false);
const [isSelected, setIsSelected] = useState(false);
const handleCardClick = async () => {
try {
await onSelect(agent);
} catch (error) {
console.error("[LoadedAgentCard] Error selecting agent:", error);
}
};
return (
<div className="relative w-full">
<div
className="relative w-full h-[300px] perspective"
onMouseEnter={() => setIsFlipped(true)}
onMouseLeave={() => setIsFlipped(false)}
onClick={handleCardClick}
>
<div
className={`relative w-full h-full duration-500 preserve-3d transform-style-3d ${
isFlipped ? "rotate-y-180" : ""
}`}
>
{/* Front of Card */}
<div className="absolute w-full h-full backface-hidden">
<div className="w-full h-full bg-yellow-900/80 rounded-lg overflow-hidden shadow-xl border border-orange-500/30">
<div className="relative h-[225px]">
<img
src={profileImageUrl}
alt=""
className="w-full h-full object-cover"
/>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-gray-900 to-transparent h-16" />
</div>
<div className="h-[75px] p-2 px-3 bg-slate-900/80 transition-opacity duration-200">
<h3 className="text-xl font-bold text-gray-100 mb-1">
{agentName}
</h3>
<p className="text-orange-300 text-sm">{agentName}</p>
</div>
</div>
</div>
{/* Back of Card */}
<div className="absolute w-full h-full backface-hidden rotate-y-180">
<div className="w-full h-full bg-slate-900/80 rounded-lg p-4 shadow-xl border border-orange-500/30 flex flex-col">
{/* Header with small image */}
<div className="flex gap-4 mb-4">
<img
src={profileImageUrl}
alt=""
className="w-20 h-20 rounded-lg object-cover flex-shrink-0"
/>
<div className="overflow-hidden">
<h3 className="text-xl font-bold text-gray-100 truncate">
{agentName}
</h3>
<p className="text-orange-400 text-sm truncate">
{Array.isArray(agentTopicExpertise)
? agentTopicExpertise[0]
: agentTopicExpertise}{" "}
Expert
</p>
</div>
</div>
<div className="space-y-4 overflow-y-auto max-h-[130px] pr-2 pb-16">
{/* Personality */}
<div>
<div className="flex items-center gap-2 text-gray-300 mb-1">
<Heart className="w-4 h-4 text-orange-400 flex-shrink-0" />
<span className="font-medium">Personality</span>
</div>
<p className="text-gray-400 text-sm break-words line-clamp-3">
{Array.isArray(agentPersonality)
? agentPersonality.join(", ")
: agentPersonality}
</p>
</div>
{/* Other fields */}
</div>
{/* Action button */}
<div className="absolute bottom-2 left-2 right-2">
<button
className={`w-full px-4 py-2 bg-gradient-to-r from-[#F7F957] to-[#F9D02C] rounded-md
flex items-center justify-center gap-2 text-gray-700
${
isSelecting || isSelected
? "opacity-50 cursor-not-allowed"
: "hover:from-cyan-700 hover:to-orange-700"
}`}
onClick={async (e) => {
e.stopPropagation();
if (isSelecting || isSelected) return;
setIsSelecting(true);
try {
await onSelect(agent);
setIsSelected(true);
} finally {
setIsSelecting(false);
}
}}
disabled={isSelecting || isSelected}
>
<CheckCircle
className={`w-4 h-4 ${isSelecting ? "animate-spin" : ""}`}
/>
{isSelected
? "Selected"
: isSelecting
? "Selecting..."
: "Select Agent"}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default LoadedAgentCard;

View File

@ -0,0 +1,30 @@
import { useState, useEffect } from 'react';
const LoadingBar = ({ progress }: { progress: number }) => {
const [width, setWidth] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => {
setWidth(progress);
}, 100); // Delay to simulate dynamic loading
return () => clearTimeout(timeout);
}, [progress]);
return (
<div>
<div className="text-white mb-2 site-text-class">Agent Image Generating...</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="h-full rounded-full transition-all duration-[15000ms]"
style={{
width: `${Math.min(Math.max(width, 0), 100)}%`,
background: 'linear-gradient(to right, #06b6d4, #f97316)' // Updated gradient from cyan to orange
}}
/>
</div>
</div>
);
};
export default LoadingBar;

View File

@ -0,0 +1,118 @@
import { useState } from "react";
import { Menu } from "./ui/navbar-menu";
import { cn } from "../lib/utils";
import { Link, useLocation } from "react-router-dom";
import { motion } from "framer-motion";
export function NavbarDemo() {
return (
<div className="relative w-full flex items-center justify-center">
<Navbar className="top-6 shadow-lg" />
</div>
);
}
function Navbar({ className }: { className?: string }) {
const [active, setActive] = useState<string | null>(null);
const location = useLocation(); // To track the current active page
// Map of links to their path (for active state comparison)
const links = [
{ label: "Home", path: "#" },
{ label: "Features", path: "#agents" },
{ label: "Tokenomics", path: "#tokenomics" },
{ label: "Roadmap", path: "#roadmap" },
];
return (
<div
className={cn(
"fixed top-10 inset-x-0 lg:max-w-7xl max-w-2xl mx-auto z-50 rounded-lg bg-gradient-to-r from-yellow-300 to-yellow-100",
className
)}
>
<Menu setActive={setActive}>
<div className="flex justify-between">
<div className="flex items-center space-x-8">
<a href="/">
<img src="/logo.svg" alt="logo" className="w-36" />
</a>
{/* Navbar Links */}
<div className="flex items-center space-x-8">
{links.map(({ label, path }) => {
const isActive = location.pathname === path;
return (
<motion.div
key={label}
whileHover={{
scale: 1.05, // Scale slightly on hover
transition: {
type: "spring",
stiffness: 300,
damping: 20,
},
}}
className="relative hover:border-b-2 border-neutral-800 duration-100"
>
<a href={path}>
<p
className={`${
isActive ? "text-amber-500" : "text-gray-800"
} transition-colors duration-300 font-medium font-orbitron`}
>
{label}
</p>
</a>
{/* Animated Underline for active link */}
{isActive && (
<motion.div
className="absolute bottom-0 left-0 w-full h-[2px] bg-amber-500"
initial={{ width: 0 }}
animate={{ width: "100%" }}
transition={{ duration: 0.4 }}
/>
)}
</motion.div>
);
})}
</div>
</div>
{/* Social Icons with animation to slide in from the right */}
<div className="flex items-center space-x-3 justify-end">
{["gitbook.svg", "github.svg", "X.svg"].map((src, index) => (
<motion.a
href={
src === "gitbook.svg"
? "https://equilink.gitbook.io"
: src === "github.svg"
? "https://github.com/Equilink-Suite"
: ""
}
target="_blank"
key={index}
className="p-3 border-[1px] hover:bg-yellow-400 rounded-xl bg-white shadow-lg"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{
delay: index * 0.2,
type: "spring",
stiffness: 300,
}}
whileHover={{
scale: 1.1, // This is the Framer Motion scale on hover
transition: { duration: 0.2 }, // Smooth transition
}}
>
<img src={`/${src}`} className="w-6 h-auto" />
</motion.a>
))}
</div>
</div>
</Menu>
</div>
);
}

View File

@ -0,0 +1,28 @@
import React from 'react';
interface NotificationProps {
message: string;
type: 'error' | 'success' | 'info';
onClose: () => void;
}
const Notification: React.FC<NotificationProps> = ({ message, type, onClose }) => {
return (
<div
className={`fixed inset-0 flex items-center justify-center z-50`}
onClick={onClose}
>
<div
className={`bg-white p-4 rounded shadow-md border ${
type === 'error' ? 'border-red-500' : type === 'success' ? 'border-green-500' : 'border-blue-500'
}`}
>
<p className={`text-${type === 'error' ? 'red' : type === 'success' ? 'green' : 'blue'}-500`}>
{message}
</p>
</div>
</div>
);
};
export default Notification;

View File

@ -0,0 +1,310 @@
import React, { useState, useEffect } from 'react';
import {
Heart,
MessageCircle,
Sparkles,
CheckCircle,
RefreshCcw,
} from 'lucide-react';
import { Agent } from '../interfaces/AgentInterfaces';
import LoadingBar from './LoadingBar';
// Define the props for AgentCard
interface RandomAgentCardProps {
agent: Agent;
onSelect: (agent: Agent) => Promise<void>;
onAddAgent: (agent: Agent) => void;
isUserAgent: boolean;
setRandomAgents: React.Dispatch<React.SetStateAction<Agent[]>>;
generateRandomAgentData: () => Promise<Agent>;
isLoadedAgent: boolean;
onRegenerate: (agentId: string) => Promise<void>;
isLoading?: boolean;
isExample?: boolean;
}
const RandomAgentCard: React.FC<RandomAgentCardProps> = ({
agent,
onSelect,
onAddAgent,
isUserAgent,
onRegenerate,
}) => {
const [isFlipped, setIsFlipped] = useState(false);
const [isRegenerating, setIsRegenerating] = useState(false);
const [isSelecting, setIsSelecting] = useState(false);
const [isAdded, setIsAdded] = useState(false);
const agentName = agent.name || 'Unknown Agent';
const agentPersonality = Array.isArray(agent.personality) ? agent.personality : [];
const agentCommunicationStyle = Array.isArray(agent.communicationStyle) ? agent.communicationStyle : [];
const agentEmojis = Array.isArray(agent.emojis) ? agent.emojis : [];
const agentTags = Array.isArray(agent.tags) ? agent.tags : [];
const profileImageUrl = agent.avatar || "";
const [showNewContent, setShowNewContent] = useState(true);
const [loadingProgress, setLoadingProgress] = useState(0);
useEffect(() => {
let intervalId: number | undefined;
if (agent.isLoading || isRegenerating) {
// Reset states when loading starts
setLoadingProgress(0);
setShowNewContent(false);
// Immediately start filling to 30%
setLoadingProgress(30);
// Start progress up to 90%
intervalId = window.setInterval(() => {
setLoadingProgress(prev => {
if (prev < 90) {
return Math.min(prev + 1, 90);
}
return prev;
});
}, 30);
} else if (loadingProgress > 0) {
// When regeneration is complete, quickly fill to 100%
if (intervalId !== undefined) clearInterval(intervalId);
setLoadingProgress(100);
// Show new content after progress bar completes
const timeout = setTimeout(() => {
setLoadingProgress(0);
setShowNewContent(true);
}, 500);
return () => clearTimeout(timeout);
}
return () => {
if (intervalId !== undefined) clearInterval(intervalId);
};
}, [agent.isLoading, isRegenerating]);
const addButton = agent.isExample ? (
<button
onClick={(e) => {
e.stopPropagation();
onSelect(agent);
}}
className="opacity-50 cursor-not-allowed bg-gray-500 text-white px-4 py-2 rounded"
>
Example Agent
</button>
) : isAdded ? (
<button
className="opacity-50 cursor-not-allowed bg-green-600 text-white px-4 py-2 rounded"
disabled
onClick={(e) => e.stopPropagation()}
>
Added
</button>
) : (
<button
onClick={async (e) => {
e.stopPropagation();
if (isRegenerating) return;
setIsRegenerating(true);
try {
await onAddAgent(agent);
setIsAdded(true);
} finally {
setIsRegenerating(false);
}
}}
disabled={isRegenerating}
className={`bg-gradient-to-r from-cyan-600 to-orange-600 text-white px-4 py-2 rounded
${isRegenerating ? 'opacity-50 cursor-not-allowed' : 'hover:from-cyan-700 hover:to-orange-700'}`}
>
{isRegenerating ? 'Adding...' : 'Add Agent'}
</button>
);
const selectButton = (
<button
className={`w-full px-4 py-2 bg-gradient-to-r from-cyan-600 to-orange-600 rounded-md
flex items-center justify-center gap-2 text-white
${isSelecting ? 'opacity-50 cursor-not-allowed' : 'hover:from-cyan-700 hover:to-orange-700'}`}
onClick={async (e) => {
e.stopPropagation();
if (isSelecting) return;
setIsSelecting(true);
try {
await onSelect(agent);
} finally {
setIsSelecting(false);
}
}}
disabled={isSelecting}
>
<CheckCircle className={`w-4 h-4 ${isSelecting ? 'animate-spin' : ''}`} />
{isSelecting ? 'Selecting...' : 'Select Agent'}
</button>
);
return (
<div className="relative">
{/* <div className="absolute top-2 right-2 bg-gray-700 text-white text-xs rounded px-2">
{isUserAgent ? 'Loaded Agent' : 'Randomly Generated'}
</div> */}
<div
className="perspective w-64 h-[500px]"
onMouseEnter={() => !isRegenerating && setIsFlipped(true)}
onMouseLeave={() => !isRegenerating && setIsFlipped(false)}
onClick={(e) => {
e.stopPropagation();
if (!isRegenerating) {
onSelect(agent);
}
}}
>
<div
className={`relative w-full h-full duration-500 preserve-3d ${
isFlipped && !isRegenerating ? 'rotate-y-180' : ''
}`}
>
{/* Front of card */}
<div className="absolute w-full h-full backface-hidden">
<div className="w-full h-full bg-slate-900/80 rounded-lg overflow-hidden shadow-xl border border-orange-500/30">
<div className="relative h-[400px]">
{(!showNewContent || agent.isLoading || isRegenerating || loadingProgress > 0) ? (
<div className="w-full h-full bg-slate-900/80 flex items-center justify-center">
<div className="w-3/4">
<LoadingBar progress={loadingProgress} />
</div>
</div>
) : (
<img
src={agent.avatar || ''}
alt={agent.avatar ? '' : 'Please regenerate again'}
className="w-full h-full object-cover"
style={{ display: agent.avatar ? 'block' : 'none' }}
/>
)}
{!agent.avatar && (
<div className="w-full h-full flex items-center justify-center text-white">
Please regenerate again
</div>
)}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-gray-900 to-transparent h-16" />
</div>
{/* Only show name and role when not flipped */}
<div className={`h-[100px] p-4 bg-slate-900/80 transition-opacity duration-200 ${
isFlipped ? 'opacity-0' : 'opacity-100'
}`}>
<h3 className="text-xl font-bold text-gray-100 mb-1 truncate">
{agentName}
</h3>
<p className="text-orange-300 text-sm truncate">{agent.role}</p>
</div>
</div>
</div>
{/* Back of card */}
<div className="absolute w-full h-full backface-hidden rotate-y-180">
<div className="w-full h-full bg-slate-900/80 rounded-lg p-4 shadow-xl border border-orange-500/30 flex flex-col">
{/* Header with small image */}
<div className="flex gap-4 mb-4">
<img
src={profileImageUrl}
alt=""
className="w-20 h-20 rounded-lg object-cover flex-shrink-0"
/>
<div className="overflow-hidden">
<h3 className="text-xl font-bold text-gray-100 truncate">
{agentName}
</h3>
<p className="text-orange-400 text-sm truncate">{agent.role}</p>
</div>
</div>
{/* Content sections with better overflow handling */}
<div className="space-y-4 overflow-y-auto flex-grow mb-4 pr-2">
<div>
<div className="flex items-center gap-2 text-gray-300 mb-1">
<Heart className="w-4 h-4 text-orange-400 flex-shrink-0" />
<span className="font-medium">Personality</span>
</div>
<p className="text-gray-400 text-sm break-words line-clamp-3">
{agentPersonality.join(', ')}
</p>
</div>
<div>
<div className="flex items-center gap-2 text-gray-300 mb-1">
<MessageCircle className="w-4 h-4 text-orange-400 flex-shrink-0" />
<span className="font-medium">Communication Style</span>
</div>
<p className="text-gray-400 text-sm break-words line-clamp-3">
{agentCommunicationStyle.join(', ')}
</p>
</div>
<div>
<div className="flex items-center gap-2 text-gray-300 mb-1">
<Sparkles className="w-4 h-4 text-orange-400 flex-shrink-0" />
<span className="font-medium">Emojis</span>
</div>
<p className="text-gray-400 text-sm break-words line-clamp-2">
{agentEmojis.join(' ')}
</p>
</div>
{/* Tags */}
<div className="flex gap-2 flex-wrap">
{agentTags.map((tag, index) => (
<span
key={index}
className="px-2 py-1 bg-orange-900/50 rounded-full text-xs text-orange-300 truncate max-w-[150px]"
>
{tag}
</span>
))}
</div>
</div>
{/* Action button - with solid background */}
<div className="absolute bottom-2 left-4 right-4">
{/* Solid background container */}
<div className="bg-slate-900 rounded-md"> {/* Removed opacity, added rounded corners */}
<div className="relative px-4 py-2"> {/* Added some vertical padding */}
{isUserAgent ? selectButton : addButton}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Regenerate button below the card */}
{!isUserAgent && (
<div className="mt-2 w-64 mx-auto">
<button
data-agent-id={agent.id}
className={`w-full mt-2 py-2 bg-gradient-to-r from-[#F7F957] to-[#F9D02C] rounded-md
flex items-center justify-center gap-2 text-gray-700
${isRegenerating ? 'opacity-50 cursor-not-allowed' : 'hover:from-cyan-700 hover:to-orange-700'}`}
onClick={async (e) => {
e.stopPropagation();
if (isRegenerating) return;
setIsRegenerating(true);
try {
await onRegenerate(agent.id?.toString() || Math.random().toString());
} finally {
setIsRegenerating(false);
}
}}
disabled={isRegenerating}
>
<RefreshCcw className={`w-4 h-4 ${isRegenerating ? 'animate-spin' : ''}`} />
{isRegenerating ? 'Regenerating...' : 'Regenerate'}
</button>
</div>
)}
</div>
);
};
export default RandomAgentCard;

View File

@ -0,0 +1,51 @@
import React, { useState } from 'react';
import { Sparkles } from 'lucide-react';
interface TraitButtonsProps {
field: 'personality' | 'communication_style' | 'topic_expertise' | 'hashtags' | 'emojis';
options: string[];
onTraitButtonClick: (field: 'personality' | 'communication_style' | 'topic_expertise' | 'hashtags' | 'emojis', value: string) => void;
}
// Component to render suggestion chips for a given field with provided options
const TraitButtons: React.FC<TraitButtonsProps> = ({ field, options, onTraitButtonClick }) => {
const [selectedOption, setSelectedOption] = useState<string | null>(null); // Track selected option
const handleDeleteTrait = (option: string) => {
setSelectedOption(null); // Clear the selected option
onTraitButtonClick(field, option); // Call the click handler to delete the trait
};
return (
<div className="flex flex-wrap gap-2 mb-4">
{options.map((option, index) => (
<div key={index} className="flex items-center">
<button
className={`px-3 py-1 rounded-full transition-all duration-300 flex items-center justify-between
${selectedOption === option ? 'bg-yellow-400 text-gray-600' : 'bg-gray-100/30 hover:bg-yellow-500/30 text-gray-700'}
border border-orange-500/30`}
onClick={() => {
setSelectedOption(option); // Update selected option
}}
>
<div className="flex items-center">
<Sparkles className="w-4 h-4 mr-2 text-orange-400" />
{option}
</div>
<span
className="text-red-500 ml-2 cursor-pointer" // Changed to span and added cursor pointer
onClick={(e) => {
e.stopPropagation(); // Prevent the click from bubbling up to the main button
handleDeleteTrait(option); // Handle delete action
}}
>
x
</span>
</button>
</div>
))}
</div>
);
};
export default TraitButtons;

View File

@ -0,0 +1,16 @@
import React from 'react';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
}
export const Button: React.FC<ButtonProps> = ({ children, className = '', ...props }) => {
return (
<button
className={`px-4 py-2 rounded-lg text-white ${className}`}
{...props}
>
{children}
</button>
);
};

View File

@ -0,0 +1,35 @@
import { PrivyProvider } from "@privy-io/react-auth";
import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
const solanaConnectors = toSolanaWalletConnectors({
// By default, shouldAutoConnect is enabled
shouldAutoConnect: true,
});
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<PrivyProvider
appId="cm6t7wm9y0i1wt680b4tukt8r"
config={{
appearance: {
theme: "dark",
showWalletLoginFirst: false,
walletChainType: "solana-only",
walletList: ["phantom"],
},
externalWallets: {
solana: {
connectors: solanaConnectors,
},
},
loginMethods: ["wallet", "email"],
embeddedWallets: {
createOnLogin: "all-users",
requireUserPasswordOnCreate: false,
},
}}
>
{children}
</PrivyProvider>
);
}

View File

@ -0,0 +1,114 @@
import React, { useState } from "react";
import { motion } from "framer-motion";
import {
ChevronLeft,
ChevronRight,
UserPlus,
Users,
MessageSquare,
LogOut,
} from "lucide-react";
import { Link, useNavigate } from "react-router-dom";
import { usePrivy } from "@privy-io/react-auth";
const Sidebar = () => {
const [isOpen, setIsOpen] = useState(true);
const {logout} = usePrivy()
const navigate = useNavigate()
const toggleSidebar = () => {
setIsOpen(!isOpen);
};
const sidebarVariants = {
open: {
width: "300px",
transition: {
type: "spring",
stiffness: 200,
damping: 25,
},
},
closed: {
width: "0px",
transition: {
type: "spring",
stiffness: 200,
damping: 25,
},
},
};
const menuItems = [
{ icon: <UserPlus size={20} />, text: "Create Agent", link: "/create-agent" },
{ icon: <Users size={20} />, text: "Browse Agents", link: "/browse-agents" },
{ icon: <MessageSquare size={20} />, text: "Chat with Agent", link: "/chat" },
];
return (
<div className="relative">
{/* Toggle Button - Positioned absolutely relative to the container */}
<button
onClick={toggleSidebar}
className="fixed top-8 left-0 z-50 bg-yellow-400 rounded-full p-1.5
hover:bg-yellow-500 transition-colors"
style={{
left: isOpen ? "282px" : "12px",
transition: "left 0.3s ease-in-out",
}}
>
{isOpen ? <ChevronLeft size={16} /> : <ChevronRight size={16} />}
</button>
<motion.div
initial="open"
animate={isOpen ? "open" : "closed"}
variants={sidebarVariants}
className="h-screen bg-white shadow-lg flex flex-col overflow-hidden"
>
{isOpen && (
<div className="flex flex-col h-full">
{/* Logo Section */}
<Link to="/" className="flex items-center p-4">
<img src="/logo.svg" alt="Logo" className="w-[80%] h-auto" />
</Link>
{/* Navigation Items */}
<div className="flex-1 px-3 py-8 flex flex-col gap-2 items-stretch">
{menuItems.map((item, index) => (
<Link to={item.link}>
<motion.button
key={index}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="flex w-full items-center p-3 rounded-lg hover:bg-yellow-50
text-gray-700 hover:text-yellow-600 transition-colors justify-start"
>
{item.icon}
<span className="ml-3 whitespace-nowrap">{item.text}</span>
</motion.button>
</Link>
))}
</div>
{/* Logout Button */}
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
onClick={()=>{
logout()
navigate("/")
}}
className="m-4 p-3 flex items-center rounded-lg bg-yellow-400
hover:bg-yellow-500 text-gray-800 transition-colors justify-start"
>
<LogOut size={20} />
<span className="ml-3 whitespace-nowrap">Logout</span>
</motion.button>
</div>
)}
</motion.div>
</div>
);
};
export default Sidebar;

View File

@ -0,0 +1,92 @@
import React from "react";
import { PieChart, Pie, Cell, ResponsiveContainer } from "recharts";
import { motion } from "framer-motion";
const COLORS = ["#F7A600", "#FFD700", "#FFEC80", "#D3D3D3"];
const data = [
{ name: "Developer Wallet Allocation", value: 5, color: "#F7A600" },
{ name: "Team", value: 2, color: "#FFD700" },
{ name: "Marketing & Operations", value: 1, color: "#FFEC80" },
{ name: "Treasury & Ecosystem", value: 2, color: "#D3D3D3" },
];
const vestingData = [
{ name: "2% Locked (2 Years)", color: "#D3D3D3" },
{ name: "0.5% for Marketing", color: "#E0E0E0" },
{ name: "0.5% Treasury Liquidity", color: "#F0F0F0" },
];
const Tokenomics: React.FC = () => {
return (
<div id="tokenomics" className="grid md:grid-cols-2 gap-10 items-center justify-center p-10 w-full">
{/* Animated Pie Chart */}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8 }}
className="flex justify-center"
>
<ResponsiveContainer width={600} height={600}>
<PieChart>
<Pie
data={data}
cx="50%"
cy="50%"
innerRadius={120}
outerRadius={180}
fill="#8884d8"
paddingAngle={5}
dataKey="value"
className="w-full h-full"
>
{data.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</motion.div>
{/* Tokenomics Info */}
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
className="text-left"
>
<h2 className="text-4xl font-orbitron font-bold mb-4">Tokenomics</h2>
<p className="text-gray-500 mb-4">
Transparent Allocation Designed for Growth and Sustainability
</p>
<div className="mb-6 space-y-2">
{data.map((item, index) => (
<div key={index} className="flex items-center">
<div
className="w-4 h-4 rounded mr-2"
style={{ backgroundColor: item.color }}
></div>
<p className="text-lg">{item.name} ({item.value}%)</p>
</div>
))}
</div>
<h3 className="text-xl font-semibold mb-3">Vesting Plan</h3>
<div className="space-y-2">
{vestingData.map((item, index) => (
<div key={index} className="flex items-center">
<div
className="w-4 h-4 rounded mr-2"
style={{ backgroundColor: item.color }}
></div>
<p className="text-lg">{item.name}</p>
</div>
))}
</div>
</motion.div>
</div>
);
};
export default Tokenomics;

View File

@ -0,0 +1,274 @@
import { useEffect, useRef } from "react";
class Pixel {
constructor(canvas, context, x, y, color, speed, delay) {
this.width = canvas.width;
this.height = canvas.height;
this.ctx = context;
this.x = x;
this.y = y;
this.color = color;
this.speed = this.getRandomValue(0.1, 0.9) * speed;
this.size = 0;
this.sizeStep = Math.random() * 0.4;
this.minSize = 0.5;
this.maxSizeInteger = 2;
this.maxSize = this.getRandomValue(this.minSize, this.maxSizeInteger);
this.delay = delay;
this.counter = 0;
this.counterStep = Math.random() * 4 + (this.width + this.height) * 0.01;
this.isIdle = false;
this.isReverse = false;
this.isShimmer = false;
}
getRandomValue(min, max) {
return Math.random() * (max - min) + min;
}
draw() {
const centerOffset = this.maxSizeInteger * 0.5 - this.size * 0.5;
this.ctx.fillStyle = this.color;
this.ctx.fillRect(
this.x + centerOffset,
this.y + centerOffset,
this.size,
this.size
);
}
appear() {
this.isIdle = false;
if (this.counter <= this.delay) {
this.counter += this.counterStep;
return;
}
if (this.size >= this.maxSize) {
this.isShimmer = true;
}
if (this.isShimmer) {
this.shimmer();
} else {
this.size += this.sizeStep;
}
this.draw();
}
disappear() {
this.isShimmer = false;
this.counter = 0;
if (this.size <= 0) {
this.isIdle = true;
return;
} else {
this.size -= 0.1;
}
this.draw();
}
shimmer() {
if (this.size >= this.maxSize) {
this.isReverse = true;
} else if (this.size <= this.minSize) {
this.isReverse = false;
}
if (this.isReverse) {
this.size -= this.speed;
} else {
this.size += this.speed;
}
}
}
function getEffectiveSpeed(value, reducedMotion) {
const min = 0;
const max = 100;
const throttle = 0.001;
const parsed = parseInt(value, 10);
if (parsed <= min || reducedMotion) {
return min;
} else if (parsed >= max) {
return max * throttle;
} else {
return parsed * throttle;
}
}
/**
* You can change/expand these as you like.
*/
const VARIANTS = {
default: {
activeColor: null,
gap: 5,
speed: 35,
colors: "#f8fafc,#f1f5f9,#cbd5e1",
noFocus: false
},
blue: {
activeColor: "#e0f2fe",
gap: 10,
speed: 25,
colors: "#e0f2fe,#7dd3fc,#0ea5e9",
noFocus: false
},
yellow: {
activeColor: "#fef08a",
gap: 3,
speed: 20,
colors: "#fef08a,#fde047,#eab308",
noFocus: false
},
pink: {
activeColor: "#fecdd3",
gap: 6,
speed: 80,
colors: "#fecdd3,#fda4af,#e11d48",
noFocus: true
}
};
export default function PixelCard({
variant = "default",
gap,
speed,
colors,
noFocus,
className = "",
children
}) {
const containerRef = useRef(null);
const canvasRef = useRef(null);
const pixelsRef = useRef([]);
const animationRef = useRef(null);
const timePreviousRef = useRef(performance.now());
const reducedMotion = useRef(
window.matchMedia("(prefers-reduced-motion: reduce)").matches
).current;
const variantCfg = VARIANTS[variant] || VARIANTS.default;
const finalGap = gap ?? variantCfg.gap;
const finalSpeed = speed ?? variantCfg.speed;
const finalColors = colors ?? variantCfg.colors;
const finalNoFocus = noFocus ?? variantCfg.noFocus;
const initPixels = () => {
if (!containerRef.current || !canvasRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const width = Math.floor(rect.width);
const height = Math.floor(rect.height);
const ctx = canvasRef.current.getContext("2d");
canvasRef.current.width = width;
canvasRef.current.height = height;
canvasRef.current.style.width = `${width}px`;
canvasRef.current.style.height = `${height}px`;
const colorsArray = finalColors.split(",");
const pxs = [];
for (let x = 0; x < width; x += parseInt(finalGap, 10)) {
for (let y = 0; y < height; y += parseInt(finalGap, 10)) {
const color =
colorsArray[Math.floor(Math.random() * colorsArray.length)];
const dx = x - width / 2;
const dy = y - height / 2;
const distance = Math.sqrt(dx * dx + dy * dy);
const delay = reducedMotion ? 0 : distance;
pxs.push(
new Pixel(
canvasRef.current,
ctx,
x,
y,
color,
getEffectiveSpeed(finalSpeed, reducedMotion),
delay
)
);
}
}
pixelsRef.current = pxs;
};
const doAnimate = (fnName) => {
animationRef.current = requestAnimationFrame(() => doAnimate(fnName));
const timeNow = performance.now();
const timePassed = timeNow - timePreviousRef.current;
const timeInterval = 1000 / 60; // ~60 FPS
if (timePassed < timeInterval) return;
timePreviousRef.current = timeNow - (timePassed % timeInterval);
const ctx = canvasRef.current?.getContext("2d");
if (!ctx || !canvasRef.current) return;
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
let allIdle = true;
for (let i = 0; i < pixelsRef.current.length; i++) {
const pixel = pixelsRef.current[i];
pixel[fnName]();
if (!pixel.isIdle) {
allIdle = false;
}
}
if (allIdle) {
cancelAnimationFrame(animationRef.current);
}
};
const handleAnimation = (name) => {
cancelAnimationFrame(animationRef.current);
animationRef.current = requestAnimationFrame(() => doAnimate(name));
};
const onMouseEnter = () => handleAnimation("appear");
const onMouseLeave = () => handleAnimation("disappear");
const onFocus = (e) => {
if (e.currentTarget.contains(e.relatedTarget)) return;
handleAnimation("appear");
};
const onBlur = (e) => {
if (e.currentTarget.contains(e.relatedTarget)) return;
handleAnimation("disappear");
};
useEffect(() => {
initPixels();
const observer = new ResizeObserver(() => {
initPixels();
});
if (containerRef.current) {
observer.observe(containerRef.current);
}
return () => {
observer.disconnect();
cancelAnimationFrame(animationRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [finalGap, finalSpeed, finalColors, finalNoFocus]);
return (
<div
ref={containerRef}
className={`h-[400px] w-[300px] relative shadow-lg overflow-hidden grid place-items-center aspect-[4/5] border border-[#c2c2c2] rounded-[25px] isolate transition-colors duration-200 ease-[cubic-bezier(0.5,1,0.89,1)] select-none ${className}`}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onFocus={finalNoFocus ? undefined : onFocus}
onBlur={finalNoFocus ? undefined : onBlur}
tabIndex={finalNoFocus ? -1 : 0}
>
<canvas
className="w-full h-full block"
ref={canvasRef}
/>
{children}
</div>
);
}

View File

@ -0,0 +1,57 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "../../lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@ -0,0 +1,176 @@
import React from 'react';
import styled, { keyframes } from 'styled-components';
interface CardProps {
heading?: string;
content?: string;
bgColor?: string;
bgColorLight?: string;
textColorHover?: string;
boxShadowColor?: string;
icon?: React.ReactNode;
}
const sparkleAnimation = keyframes`
0% {
transform: scale(0) rotate(0deg);
opacity: 0;
}
50% {
transform: scale(1.2) rotate(180deg);
opacity: 1;
}
100% {
transform: scale(0) rotate(360deg);
opacity: 0;
}
`;
const CardWrapper = styled.div<CardProps>`
position: relative;
.sparkle {
position: absolute;
width: 20px;
height: 20px;
background: radial-gradient(circle, rgba(255,255,255,0.8) 0%, transparent 70%);
border-radius: 50%;
pointer-events: none;
z-index: 1000;
animation: ${sparkleAnimation} 1s ease-in-out infinite;
}
.custom-card {
width: 400px;
border-radius:15px;
height: 500px;
background: #fff;
border-top-right-radius: 10px;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
box-shadow: 0 14px 26px rgba(0,0,0,0.1);
transition: all 0.3s ease-out;
text-decoration: none;
/* Custom color variables */
--bg-color: ${props => props.bgColor || '#ffd861'};
--bg-color-light: ${props => props.bgColorLight || '#ffeeba'};
--text-color-hover: ${props => props.textColorHover || '#4C5656'};
--box-shadow-color: ${props => props.boxShadowColor || 'rgba(255, 215, 97, 0.48)'};
}
.custom-card:hover {
transform: translateY(-5px) scale(1.005) translateZ(0);
box-shadow: 0 24px 36px rgba(0,0,0,0.11),
0 24px 46px var(--box-shadow-color);
}
.custom-card:hover .overlay {
transform: scale(8) translateZ(0);
}
.circle {
width: 131px;
height: 131px;
border-radius: 50%;
background: #fff;
border: 3px solid var(--bg-color);
display: flex;
justify-content: center;
align-items: center;
position: relative;
z-index: 1;
transition: all 0.3s ease-out;
margin-bottom: 20px;
}
.circle svg {
width: 60px;
height: 60px;
color: var(--bg-color);
}
.overlay {
width: 170px;
position: absolute;
height: 170px;
border-radius: 50%;
background: var(--bg-color);
top: 330px;
left: 400px;
z-index: 0;
transition: transform 0.3s ease-out;
}
.card-heading {
font-size: 20px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
z-index: 1000;
}
.card-content {
font-size: 17px;
color: #4C5656;
text-align: center;
padding: 0 20px;
z-index: 1000;
transition: color 0.3s ease-out;
}
`;
const Card: React.FC<CardProps> = ({
heading,
content,
bgColor,
bgColorLight,
textColorHover,
boxShadowColor,
icon
}) => {
const renderSparkles = () => {
const sparkles = [];
for (let i = 0; i < 5; i++) {
sparkles.push(
<div
key={i}
className="sparkle"
style={{
top: `${Math.random() * 100}%`,
left: `${Math.random() * 100}%`,
animationDelay: `${Math.random()}s`
}}
/>
);
}
return sparkles;
};
return (
<CardWrapper
bgColor={bgColor}
bgColorLight={bgColorLight}
textColorHover={textColorHover}
boxShadowColor={boxShadowColor}
>
<div className="body">
<div className="custom-card p-3">
{renderSparkles()}
<div className="overlay" />
<div className="circle">
{icon}
</div>
{heading && <div className="text-2xl z-50 font-bold my-3 text-gray-700">{heading}</div>}
{content && <div className="card-content">{content}</div>}
</div>
</div>
</CardWrapper>
);
};
export default Card;

View File

@ -0,0 +1,127 @@
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
function SmokeParticles() {
const groupRef = useRef();
const particlesRef = useRef([]);
const clockRef = useRef(new THREE.Clock());
const statsRef = useRef();
const { scene, size } = useThree();
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
// Update mouse position
useEffect(() => {
const handleMouseMove = (event) => {
const x = (event.clientX / size.width) * 2 - 1;
const y = -(event.clientY / size.height) * 2 + 1;
setMousePos({ x, y });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, [size]);
// Create smoke particles
useEffect(() => {
const loader = new THREE.TextureLoader();
loader.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/95637/Smoke-Element.png', (smokeTexture) => {
const smokeGeo = new THREE.PlaneGeometry(300, 300);
for (let p = 0; p < 150; p++) {
const colors = [0xFFD700, 0xFACC15, 0xFFA500]; // Gold, yellow-400, amber
const randomColor = colors[Math.floor(Math.random() * colors.length)];
const smokeMaterial = new THREE.MeshLambertMaterial({
color: randomColor,
map: smokeTexture,
transparent: true,
opacity: Math.random() * 0.6 + 0.4, // More vibrant
emissive: randomColor,
emissiveIntensity: 1.2, // Stronger glow
});
const particle = new THREE.Mesh(smokeGeo, smokeMaterial);
particle.position.set(
Math.random() * 500 - 250,
Math.random() * 500 - 250,
Math.random() * 1000 - 100
);
particle.rotation.z = Math.random() * 360;
particle.userData.originalPos = { ...particle.position };
groupRef.current.add(particle);
particlesRef.current.push(particle);
}
});
// Add a golden light for extra glow
const light = new THREE.PointLight(0xFFD700, 2.0, 2000);
light.position.set(0, 0, 500);
scene.add(light);
}, [scene]);
// Animation loop with smooth wind effect
useFrame(() => {
if (statsRef.current) statsRef.current.begin();
const delta = clockRef.current.getDelta();
particlesRef.current.forEach((particle) => {
if (particle) {
particle.rotation.z += delta * 0.2;
// Get particle position
const particlePos = particle.position;
const originalPos = particle.userData.originalPos;
// Calculate distance from mouse
const dx = (mousePos.x * 500) - particlePos.x;
const dy = (mousePos.y * 500) - particlePos.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// Wind effect: move particles with a slight random offset
if (distance < 150) {
particlePos.x += dx * 0.02 + (Math.random() - 0.5) * 2;
particlePos.y += dy * 0.02 + (Math.random() - 0.5) * 2;
}
// Smoothly return particles to original position but with a slight offset
particlePos.x = THREE.MathUtils.lerp(particlePos.x, originalPos.x + (Math.random() - 0.5) * 5, delta);
particlePos.y = THREE.MathUtils.lerp(particlePos.y, originalPos.y + (Math.random() - 0.5) * 5, delta);
}
});
if (statsRef.current) statsRef.current.end();
});
return <group ref={groupRef} />;
}
export default function SmokeEffect() {
return (
<Canvas
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
zIndex: -10,
background: 'white'
}}
camera={{
fov: 75,
position: [0, 0, 1000],
near: 1,
far: 10000
}}
>
<SmokeParticles />
</Canvas>
);
}

View File

@ -0,0 +1,41 @@
import React from "react";
import { motion } from "framer-motion";
interface CardProps {
number: string;
heading: string;
description: string;
numberBgColor: string;
numberTextColor: string;
}
const InfoCard: React.FC<CardProps> = ({
number,
heading,
description,
numberBgColor,
numberTextColor,
}) => {
return (
<motion.div
className="w-full p-6 my-4 bg-white rounded-lg shadow-md"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<div className="flex items-center space-x-4">
<div
className="w-12 h-12 flex items-center justify-center rounded-md font-orbitron text-xl font-bold"
style={{ backgroundColor: numberBgColor, color: numberTextColor }}
>
<p>{number}</p>
</div>
<h3 className="text-2xl font-semibold text-gray-800">{heading}</h3>
</div>
<p className="mt-4 text-lg text-left text-gray-600">{description}</p>
</motion.div>
);
};
export default InfoCard;

View File

@ -0,0 +1,119 @@
import React from "react";
import { motion } from "framer-motion";
import {Link} from "react-router-dom"
const transition = {
type: "spring",
mass: 0.5,
damping: 11.5,
stiffness: 100,
restDelta: 0.001,
restSpeed: 0.001,
};
export const MenuItem = ({
setActive,
active,
item,
children,
}: {
setActive: (item: string) => void;
active: string | null;
item: string;
children?: React.ReactNode;
}) => {
return (
<div onMouseEnter={() => setActive(item)} className="relative ">
<motion.p
transition={{ duration: 0.3 }}
className="cursor-pointer text-black hover:opacity-[0.9]"
>
{item}
</motion.p>
{active !== null && (
<motion.div
initial={{ opacity: 0, scale: 0.85, y: 10 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={transition}
>
{active === item && (
<div className="absolute top-[calc(100%_+_1.2rem)] left-1/2 transform -translate-x-1/2 pt-4">
<motion.div
transition={transition}
layoutId="active" // layoutId ensures smooth animation
className="bg-white backdrop-blur-sm rounded-2xl overflow-hidden border border-black/[0.2] shadow-xl"
>
<motion.div
layout // layout ensures smooth animation
className="w-max h-full p-4"
>
{children}
</motion.div>
</motion.div>
</div>
)}
</motion.div>
)}
</div>
);
};
export const Menu = ({
setActive,
children,
}: {
setActive: (item: string | null) => void;
children: React.ReactNode;
}) => {
return (
<nav
onMouseLeave={() => setActive(null)} // resets the state
className="relative rounded-xl border border-transparent shadow-input space-x-4 px-8 py-4 "
>
{children}
</nav>
);
};
export const ProductItem = ({
title,
description,
href,
src,
}: {
title: string;
description: string;
href: string;
src: string;
}) => {
return (
<Link to={href} className="flex space-x-2">
<img
src={src}
width={140}
height={70}
alt={title}
className="flex-shrink-0 rounded-md shadow-2xl"
/>
<div>
<h4 className="text-xl font-bold mb-1 text-black">
{title}
</h4>
<p className="text-neutral-700 text-sm max-w-[10rem]">
{description}
</p>
</div>
</Link>
);
};
export const HoveredLink = ({ children, ...rest }: any) => {
return (
<Link
{...rest}
className="text-neutral-700 hover:text-black "
>
{children}
</Link>
);
};

View File

@ -0,0 +1,27 @@
import React, { createContext, useContext, useState } from 'react';
import { Agent } from '../interfaces/AgentInterfaces';
interface AgentContextType {
selectedAgent: Agent | null;
setSelectedAgent: (agent: Agent | null) => void;
}
const AgentContext = createContext<AgentContextType | undefined>(undefined);
export const AgentProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
return (
<AgentContext.Provider value={{ selectedAgent, setSelectedAgent }}>
{children}
</AgentContext.Provider>
);
};
export const useAgent = () => {
const context = useContext(AgentContext);
if (context === undefined) {
throw new Error('useAgent must be used within an AgentProvider');
}
return context;
};

View File

@ -0,0 +1,30 @@
import { useState } from 'react';
/**
* useCharacterSelection - A custom hook to manage character selection logic.
*
* Responsibilities:
* 1. Manages the `selectedCharacter` state.
* 2. Provides a function (`handleCharacterSelect`) to update the selected character.
* 3. Ensures the selected character data is validated before updating.
*/
const useCharacterSelection = () => {
const [selectedCharacter, setSelectedCharacter] = useState(null); // Holds the currently selected character.
/**
* handleCharacterSelect - Updates the selected character.
*
* @param character - The character to select.
*/
const handleCharacterSelect = (character: any) => {
if (!character?.agent?.agent_details) {
console.error('Invalid character data'); // Log an error if the character data structure is invalid.
return;
}
setSelectedCharacter(character); // Update the selected character state.
};
return { selectedCharacter, handleCharacterSelect, setSelectedCharacter }; // Return the state and handlers for character selection.
};
export default useCharacterSelection;

View File

@ -0,0 +1,37 @@
import useFetchCharacters from './useFetchCharacters';
import useProcessCharacters from './useProcessCharacters';
import useCharacterSelection from './useCharacterSelection';
import { useEffect } from 'react';
/**
* useCharacters - A composed custom hook to manage character data and selection.
*
* Responsibilities:
* 1. Fetches raw character data using `useFetchCharacters`.
* 2. Processes raw character data into a usable format using `useProcessCharacters`.
* 3. Manages character selection logic using `useCharacterSelection`.
* 4. Automatically selects the first character by default when characters are loaded.
*/
const useCharacters = () => {
const { characters: rawCharacters, loading, error } = useFetchCharacters(); // Fetch raw characters and track fetch state.
const processedCharacters = useProcessCharacters(rawCharacters); // Process the raw characters into a usable format.
const { selectedCharacter, handleCharacterSelect, setSelectedCharacter } = useCharacterSelection(); // Manage character selection.
// Automatically select the first character when characters are loaded.
useEffect(() => {
if (!selectedCharacter && processedCharacters.length > 0) {
handleCharacterSelect(processedCharacters[0]); // Select the first character by default.
}
}, [processedCharacters, selectedCharacter, handleCharacterSelect]);
return {
characters: processedCharacters, // The processed characters data.
selectedCharacter, // The currently selected character.
setSelectedCharacter, // Function to manually update the selected character.
loading, // Whether the characters are still being fetched.
error, // Any error that occurred during the fetch.
handleCharacterSelect, // Function to select a character.
};
};
export default useCharacters;

View File

@ -0,0 +1,42 @@
import { useState, useEffect } from 'react';
import { getCharacters } from '../api/agentsAPI';
import { Agent } from '../interfaces/AgentInterfaces';
/**
* useFetchCharacters - A custom hook to fetch characters from the API.
*
* Responsibilities:
* 1. Handles the API call to GET/fetch character data.
* 2. Manages the state for `characters`, `loading`, and `error`.
* 3. Provides feedback on the fetch process (loading and error states).
*/
const useFetchCharacters = () => {
const [characters, setCharacters] = useState<Agent[]>([]); // Explicitly typed with your Agent interface
const [loading, setLoading] = useState(true); // Tracks whether the API call is in progress.
const [error, setError] = useState<Error | null>(null); // Tracks any errors during the fetch process.
useEffect(() => {
const fetchCharacters = async () => {
setLoading(true); // Mark the fetch as in progress.
try {
const data = await getCharacters(); // Call the API to fetch characters.
if (!Array.isArray(data)) {
throw new Error(`Expected array, received: ${typeof data}`); // Validate the response type.
}
setCharacters(data); // Save the fetched data to state.
} catch (err) {
// Handle errors during the fetch process.
const error = err instanceof Error ? err : new Error('Failed to fetch characters');
setError(error);
} finally {
setLoading(false); // Mark the fetch as complete, regardless of success or failure.
}
};
fetchCharacters(); // Execute the fetch when the hook is used.
}, []); // Dependency array is empty to ensure this runs only once on component mount.
return { characters, loading, error }; // Return the characters data and state for use in components.
};
export default useFetchCharacters;

View File

@ -0,0 +1,44 @@
/**
* useProcessCharacters - A custom hook to transform raw character data into a usable format.
*
* Responsibilities:
* 1. Maps and processes raw data from the API.
* 2. Ensures all necessary fields are structured and default values are applied.
* 3. Provides a consistent and clean data structure for use in the application.
*/
const useProcessCharacters = (characters: any[]) => {
const processedCharacters = characters.map((char) => {
// Char is the raw data from the API
// Destructure the char object to extract the necessary fields
const { agent } = char;
return {
agent: {
agent_details: {
name: agent?.agent_details?.name || '',
personality: agent?.agent_details?.personality || [],
communication_style: agent?.agent_details?.communication_style || [],
backstory: agent?.agent_details?.backstory || '',
universe: agent?.agent_details?.universe || '',
topic_expertise: agent?.agent_details?.topic_expertise || [],
hashtags: agent?.agent_details?.hashtags || [],
emojis: agent?.agent_details?.emojis || [],
concept: agent?.concept || '',
},
profile_image: agent?.profile_image || {},
concept: agent?.concept || '',
profile_image_options: agent?.profile_image_options || [],
ai_model: agent?.ai_model || {},
connectors: agent?.connectors || {},
seasons: agent?.seasons || [],
tracker: agent?.tracker || {},
master_file_path: agent?.master_file_path || '',
}
};
});
return processedCharacters; // Return the processed data for use in the application.
};
export default useProcessCharacters;

26
client/src/index.css Normal file
View File

@ -0,0 +1,26 @@
@import url("https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap");
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
.font-orbitron{
font-family: "Orbitron", sans-serif;
}
/* @layer base {
body {
@apply m-0 min-h-screen bg-slate-950;
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
/* Only keep custom styles that can't be handled by Tailwind */
/* @layer components {
.gradient-text {
@apply bg-gradient-to-r from-cyan-400 via-orange-400 to-red-400 bg-clip-text text-transparent;
}
} */

View File

@ -0,0 +1,143 @@
export interface AgentDetails {
id?: string | number;
name: string;
personality: string[];
communication_style: string[];
backstory: string;
universe: string;
topic_expertise: string[];
hashtags: string[];
emojis: string[];
}
export interface ProfileImage {
details: {
url: string;
image_id: string;
generationId: string;
};
[key: string]: unknown;
}
export interface GeneratedImage {
url: string;
id: string;
generationId: string;
}
export interface GenerationsByPk {
id?: string;
prompt?: string;
generated_images: GeneratedImage[];
}
export interface ProfileImageOption {
generations_by_pk: GenerationsByPk;
}
export interface Agent {
id?: string | number;
name?: string;
avatar?: string;
shortDescription?: string;
tags?: string[];
personality?: string[];
communicationStyle?: string[];
emojis?: string[];
hashtags?: string[];
universe?: string;
backstory?: string;
concept?: string;
role?: string;
isLoading?: boolean;
leonardoResponse?: any;
leonardoImage?: any;
topic_expertise?: string[];
agent?: {
concept: string;
agent_details: AgentDetails;
ai_model: {
memory_store: string;
model_name: string;
model_type: string;
};
connectors: {
discord: boolean;
telegram: boolean;
twitter: boolean;
};
tracker: {
messages_sent: number;
total_interactions: number;
current_episode_number: number;
current_post_number: number;
current_season_number: number;
post_every_x_minutes: number;
};
seasons: any[];
profile_image: ProfileImage;
profile_image_options: ProfileImageOption[];
master_file_path?: string;
};
isExample?: boolean;
}
export function createBlankAgent(): Agent {
return {
id: '',
name: '',
avatar: '',
agent: {
concept: '',
agent_details: {
backstory: '',
communication_style: [],
emojis: [],
hashtags: [],
name: '',
personality: [],
topic_expertise: [],
universe: ''
},
ai_model: {
memory_store: '',
model_name: '',
model_type: ''
},
connectors: {
discord: false,
telegram: false,
twitter: false
},
tracker: {
messages_sent: 0,
total_interactions: 0,
current_episode_number: 0,
current_post_number: 0,
current_season_number: 0,
post_every_x_minutes: 0
},
seasons: [],
profile_image: {
details: {
url: '',
image_id: '',
generationId: ''
}
},
profile_image_options: [] as ProfileImageOption[]
}
};
}
export interface RandomAgentCardProps {
agent: Agent;
onSelect: (agent: Agent | null) => void;
onAddAgent: (agent: Agent) => void;
isUserAgent: boolean;
setRandomAgents: React.Dispatch<React.SetStateAction<Agent[]>>;
generateRandomAgentData: () => Promise<Agent>;
isLoadedAgent: boolean;
onRegenerate: (agentId: string) => Promise<void>;
isLoading?: boolean;
}

View File

@ -0,0 +1,11 @@
export interface Message {
role: string;
message?: string;
response?: string;
message_id: number;
}
export interface ChatHistory {
agent_name: string;
chat_history: Message[];
}

View File

@ -0,0 +1,8 @@
// Define the payload type for better type-checking
export interface LambdaPayload {
prompt: string;
modelId: string;
styleUUID: string;
num_images: number;
}

View File

@ -0,0 +1,21 @@
export interface Post {
post_id: string;
post_number: number;
post_content: string;
post_highlights?: string;
post_posted: boolean;
seasonNumber?: number;
episodeNumber?: number;
episodeName?: string;
}
export interface Episode {
episode_number: number;
episode_name: string;
posts: Post[];
}
export interface Season {
season_number: number;
episodes: Episode[];
}

View File

@ -0,0 +1,29 @@
export interface Post {
post_id: string;
post_number: number;
post_content: string;
post_highlights: string;
post_posted: boolean;
}
export interface Episode {
episode_name: string;
episode_number: number;
episode_description: string;
episode_highlights: string;
episode_summary: string;
episode_posted: boolean;
current_post_number: number;
posts: Post[];
}
export interface Season {
season_name: string;
season_number: number;
season_description: string;
season_highlights: string;
season_summary: string;
season_posted: false;
current_episode_number: number;
episodes: Episode[];
}

View File

@ -0,0 +1,7 @@
import { AgentDetails } from './AgentInterfaces';
export interface TraitButtonsProps {
field: keyof AgentDetails;
options: string[];
onTraitButtonClick: (field: keyof AgentDetails, value: string) => void;
}

6
client/src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

20
client/src/main.tsx Normal file
View File

@ -0,0 +1,20 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { BrowserRouter as Router } from 'react-router-dom';
import Providers from './components/providers.tsx';
import { AgentProvider } from './context/AgentContext.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Providers>
<AgentProvider>
<Router>
<App />
</Router>
</AgentProvider>
</Providers>
</StrictMode>,
)

View File

@ -0,0 +1,999 @@
import React, { useState, ChangeEvent, useEffect, KeyboardEvent } from "react";
import { Brain, Wand2, MessageSquare, Save, RefreshCcw, Layers } from "lucide-react";
import { createAgent, getCharacters } from "../api/agentsAPI";
import {
GeneratedImage,
ProfileImageOption,
} from "../interfaces/AgentInterfaces";
import TraitButtons from "../components/TraitButtons"; // We'll still use your TraitButtons
import useCharacters from "../hooks/useCharacters";
import { inconsistentImageLambda } from "../api/leonardoApi";
import LoadingBar from "../components/LoadingBar";
import Sidebar from "../components/sidebar";
import { motion } from "framer-motion";
const LEONARDO_MODEL_ID = "e71a1c2f-4f80-4800-934f-2c68979d8cc8";
const LEONARDO_STYLE_UUID = "b2a54a51-230b-4d4f-ad4e-8409bf58645f";
/**
* AgentCreator Component
* A form-based interface for creating and editing AI agents with various attributes
* including personality traits, communication style, and profile images.
*/
const AgentCreator: React.FC = () => {
/**
* Main UI state management
* activeTab controls which section of the form is visible:
* - basic: name, universe, expertise
* - personality: personality traits, backstory
* - style: communication style, hashtags, emojis
*/
const [activeTab, setActiveTab] = useState<"basic" | "personality" | "style">(
"basic"
);
/**
* Core agent state
* Maintains the complete agent object including:
* - agent_details: main characteristics and traits
* - profile_image: currently selected image
* - profile_image_options: available image choices
* - selectedImage: index of chosen image
* - seasons: associated seasons/episodes
*/
const [agent, setAgent] = useState<{
agent_details: {
name: string;
personality: string[];
communication_style: string[];
backstory: string;
universe: string;
topic_expertise: string[];
hashtags: string[];
emojis: string[];
concept: string;
};
profile_image: {
details: {
url: string;
image_id: string;
generationId: string;
};
};
profile_image_options: ProfileImageOption[];
selectedImage: number | undefined;
seasons: any[];
}>({
agent_details: {
name: "",
personality: [],
communication_style: [],
backstory: "",
universe: "",
topic_expertise: [],
hashtags: [],
emojis: [],
concept: "",
},
profile_image: {
details: {
url: "",
image_id: "",
generationId: "",
},
},
profile_image_options: [],
selectedImage: undefined,
seasons: [],
});
// The fetched characters
const { characters, loading, error } = useCharacters();
/**
* Draft field management
* Maintains temporary states for text fields before they're committed to the main agent state
* Prevents immediate updates and allows for Enter-to-commit functionality
*/
const [draftFields, setDraftFields] = useState({
name: "",
universe: "",
backstory: "",
imageDescription: "",
});
/**
* Synchronization Effects
* Keep draft states in sync with the main agent state
* Ensures drafts are updated when agent data changes
*/
useEffect(() => {
setDraftFields({
name: agent.agent_details.name || "",
universe: agent.agent_details.universe || "",
backstory: agent.agent_details.backstory || "",
imageDescription:
agent.profile_image_options?.[0]?.generations_by_pk?.prompt || "",
});
}, [agent]);
/**
* Draft traits management
* Handles temporary states for array-based fields (traits, hashtags, etc.)
* Stores them as comma-separated strings until committed
*/
const [draftTraits, setDraftTraits] = useState<{
topic_expertise: string;
personality: string;
communication_style: string;
hashtags: string;
emojis: string;
}>({
topic_expertise: "",
personality: "",
communication_style: "",
hashtags: "",
emojis: "",
});
/**
* Synchronization Effects
* Keep draft states in sync with the main agent state
* Ensures drafts are updated when agent data changes
*/
useEffect(() => {
setDraftTraits({
topic_expertise: agent.agent_details.topic_expertise.join(", "),
personality: agent.agent_details.personality.join(", "),
communication_style: agent.agent_details.communication_style.join(", "),
hashtags: agent.agent_details.hashtags.join(", "),
emojis: agent.agent_details.emojis.join(" "),
});
}, [agent]);
/**
* Profile Image Management
* Handles initialization and updates of the agent's profile image
* Sets default placeholder if no images are available
*/
useEffect(() => {
if (agent.profile_image_options.length > 0) {
const firstImage =
agent.profile_image_options[0]?.generations_by_pk
?.generated_images?.[0] ??
({
url: "https://via.placeholder.com/400x400?text=Brain+Placeholder",
id: "",
generationId: "",
} as GeneratedImage);
setAgent((prev) => ({
...prev,
selectedImage:
prev.selectedImage !== undefined ? prev.selectedImage : 0,
profile_image: {
details: {
url: firstImage.url,
image_id: firstImage.id,
generationId: firstImage.generationId,
},
},
}));
} else {
setAgent((prev) => ({
...prev,
profile_image: {
details: {
url: "https://via.placeholder.com/400x400?text=Brain+Placeholder",
image_id: "",
generationId: "",
},
},
}));
}
}, [agent.profile_image_options]);
/**
* Field Update Handlers
* Manages updates to regular text fields (name, universe, backstory)
* Commits changes when Enter is pressed
*/
const handleDraftChange =
(field: keyof typeof draftFields) =>
(e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
setDraftFields((prev) => ({
...prev,
[field]: e.target.value,
}));
};
const handleDraftKeyDown =
(field: keyof typeof draftFields) =>
(e: KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>) => {
if (e.key === "Enter") {
e.preventDefault();
if (field === "imageDescription") {
setAgent((prev) => {
const newImageOption: ProfileImageOption = {
generations_by_pk: {
prompt: draftFields[field],
generated_images: [],
},
};
return {
...prev,
profile_image_options: prev.profile_image_options?.length
? prev.profile_image_options.map((option, index) =>
index === 0
? {
...option,
generations_by_pk: {
...option.generations_by_pk,
prompt: draftFields[field],
},
}
: option
)
: [newImageOption],
};
});
} else {
setAgent((prev) => ({
...prev,
agent_details: {
...prev.agent_details,
[field]: draftFields[field],
},
}));
}
}
};
/**
* Trait Field Handlers
* Manages updates to array-based fields (personality, hashtags, etc.)
* Splits input by commas (or spaces for emojis) and commits on Enter
*/
const handleTraitDraftChange =
(field: keyof typeof draftTraits) =>
(e: ChangeEvent<HTMLTextAreaElement>) => {
setDraftTraits((prev) => ({
...prev,
[field]: e.target.value,
}));
};
const handleTraitDraftKeyDown =
(field: keyof typeof draftTraits) =>
(e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter") {
e.preventDefault();
// If field is "emojis", we split by space; otherwise, by comma
const separator = field === "emojis" ? " " : ",";
const arrayValue = draftTraits[field]
.split(separator)
.map((item) => item.trim())
.filter(Boolean);
setAgent((prev) => ({
...prev,
agent_details: {
...prev.agent_details,
[field]: arrayValue,
},
}));
}
};
// Deleting a single trait
type TraitField =
| "personality"
| "communication_style"
| "topic_expertise"
| "hashtags"
| "emojis";
const handleDeleteTrait = (field: TraitField, value: string) => {
setAgent((prev) => ({
...prev,
agent_details: {
...prev.agent_details,
[field]: prev.agent_details[field].filter(
(trait: string) => trait !== value
),
},
}));
};
// State to manage the visibility of the success message
const [showSuccessMessage, setShowSuccessMessage] = useState(false);
// Add state for loading progress near other state declarations
const [loadingProgress, setLoadingProgress] = useState(0);
// Add a new state for tracking image generation
const [isGenerating, setIsGenerating] = useState(false);
const [selectedCharacterIndex, setSelectedCharacterIndex] =
useState<number>(-1);
/**
* Form Submission Handler
* Processes the final agent data and sends it to the server
* Shows success message on completion
*/
const handleSubmitCreateAgent = async (
event:
| React.FormEvent<HTMLFormElement>
| React.MouseEvent<HTMLButtonElement>
) => {
event.preventDefault();
type AgentState = typeof agent;
const updatedAgent: AgentState = {
...agent,
agent_details: {
...agent.agent_details,
name: draftFields.name || agent.agent_details.name,
universe: draftFields.universe || agent.agent_details.universe,
backstory: draftFields.backstory || agent.agent_details.backstory,
concept: agent.agent_details.concept,
personality: draftTraits.personality
? draftTraits.personality
.split(",")
.map((item) => item.trim())
.filter(Boolean)
: agent.agent_details.personality,
communication_style: draftTraits.communication_style
? draftTraits.communication_style
.split(",")
.map((item) => item.trim())
.filter(Boolean)
: agent.agent_details.communication_style,
topic_expertise: draftTraits.topic_expertise
? draftTraits.topic_expertise
.split(",")
.map((item) => item.trim())
.filter(Boolean)
: agent.agent_details.topic_expertise,
hashtags: draftTraits.hashtags
? draftTraits.hashtags
.split(",")
.map((item) => item.trim())
.filter(Boolean)
: agent.agent_details.hashtags,
emojis: draftTraits.emojis
? draftTraits.emojis.split(" ").filter(Boolean)
: agent.agent_details.emojis,
},
profile_image: agent.profile_image,
profile_image_options: agent.profile_image_options.map((option, index) =>
index === 0
? {
...option,
generations_by_pk: {
...option.generations_by_pk,
prompt:
draftFields.imageDescription ||
option.generations_by_pk?.prompt,
},
}
: option
),
selectedImage: agent.selectedImage,
seasons: agent.seasons,
};
try {
await createAgent(updatedAgent);
setShowSuccessMessage(true);
setTimeout(() => setShowSuccessMessage(false), 3000);
setAgent(updatedAgent);
} catch (error) {
console.error("Error creating agent:", error);
}
};
//
// ──────────────────────────────────────────────────────────────────────────────
// 8) Load characters
// ──────────────────────────────────────────────────────────────────────────────
//
useEffect(() => {
const loadCharacters = async () => {
try {
const charactersData = await getCharacters();
if (!Array.isArray(charactersData)) {
console.error(
"Expected array of characters, received:",
typeof charactersData
);
return;
}
const processed = charactersData.map((char) => {
const agentProfileImageOptions = char.agent.profile_image_options;
const agentConcept = char.concept;
const { agent } = char;
if (!agent) return { agent: {} };
const {
agent_details: {
name = "",
personality = [],
communication_style = [],
backstory = "",
universe = "",
topic_expertise = [],
hashtags = [],
emojis = [],
} = {},
ai_model = {},
connectors = {},
seasons = [],
tracker = {},
} = agent;
return {
agent: {
agent_details: {
name,
personality,
communication_style,
backstory,
universe,
topic_expertise,
hashtags,
emojis,
},
ai_model,
connectors,
profile_image: agentProfileImageOptions || [],
seasons,
tracker,
},
concept: agentConcept || "",
};
});
console.log("Processed characters:", processed);
} catch (error) {
console.error("Error loading characters:", error);
}
};
loadCharacters();
}, []);
/**
* Character Selection Handler
* Populates the form with data from an existing character
* Updates both main agent state and draft states
*/
const handleCharacterSelect = (e: ChangeEvent<HTMLSelectElement>) => {
const selectedIndex = parseInt(e.target.value);
setSelectedCharacterIndex(selectedIndex);
const char = characters[selectedIndex];
if (!char?.agent?.agent_details) return;
const details = char.agent.agent_details;
setAgent({
agent_details: {
name: details.name || "",
personality: details.personality || [],
communication_style: details.communication_style || [],
backstory: details.backstory || "",
universe: details.universe || "",
topic_expertise: details.topic_expertise || [],
hashtags: details.hashtags || [],
emojis: details.emojis || [],
concept: details.concept || "",
},
profile_image: char.agent?.profile_image_options || [],
profile_image_options: char.agent?.profile_image_options || [],
selectedImage: char.agent?.profile_image_options?.[0]?.generations_by_pk
?.generated_images?.length
? 0
: undefined,
seasons: char.agent?.seasons || [],
});
// Sync local drafts
setDraftFields({
name: details.name || "",
universe: details.universe || "",
backstory: details.backstory || "",
imageDescription:
char.agent?.profile_image_options?.[0]?.generations_by_pk?.prompt || "",
});
setDraftTraits({
topic_expertise: (details.topic_expertise || []).join(", "),
personality: (details.personality || []).join(", "),
communication_style: (details.communication_style || []).join(", "),
hashtags: (details.hashtags || []).join(", "),
emojis: (details.emojis || []).join(" "),
});
};
//
// ──────────────────────────────────────────────────────────────────────────────
// 10) Render
// ──────────────────────────────────────────────────────────────────────────────
//
return (
<div className="flex h-screen">
<Sidebar />
<div className="w-full overflow-y-scroll flex flex-col min-h-screen bg-gray-50">
{/* Success Message */}
{showSuccessMessage && (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="bg-gradient-to-r from-orange-600 to-red-600 text-white px-6 py-3 rounded-md shadow-lg">
Agent successfully saved!
</div>
</div>
)}
{/* div */}
<div className="p-5 my-4 mx-12 rounded-xl shadow-xl flex items-center justify-between">
<div className="text-3xl font-semibold text-gray-600 font-orbitron">
Create Agent
</div>
<div className="flex items-center space-x-3 justify-end">
</div>
</div>
<div className="flex-grow flex p-5 m-5">
{/* Left Panel */}
<div className="w-1/2 p-6 rounded-2xl h-fit shadow-xl bg-white">
<div className="flex gap-4 mb-6 bg-gray-50/80 p-2 rounded-lg">
{[
{ id: "basic" as const, icon: Brain, label: "Basic Info" },
{ id: "personality" as const, icon: Wand2, label: "Personality" },
{ id: "style" as const, icon: MessageSquare, label: "Style" },
].map(({ id, icon: Icon, label }) => (
<button
key={id}
onClick={() => {
setActiveTab(id);
}}
className={`flex-1 flex items-center justify-center px-4 py-2
rounded-md text-gray-700 ${activeTab === id
? "bg-gradient-to-r from-[#F7F957] to-[#F9D02C]"
: "bg-gray-200"
}`}
>
<Icon className="w-4 h-4 mr-2" />
{label}
</button>
))}
</div>
{/* Form */}
<div className="space-y-6">
{activeTab === "basic" && (
<div className="space-y-6">
{/* Agent Name => local draft */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Agent Name
</label>
<textarea
value={draftFields.name}
onChange={handleDraftChange("name")}
onKeyDown={handleDraftKeyDown("name")}
placeholder="Enter agent name (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-800 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
{/* Universe => local draft */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Universe
</label>
<textarea
value={draftFields.universe}
onChange={handleDraftChange("universe")}
onKeyDown={handleDraftKeyDown("universe")}
placeholder="Enter universe (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-800 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
{/* Topic Expertise => local draft => commit on Enter */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Topic Expertise
</label>
<TraitButtons
field="topic_expertise"
options={agent.agent_details.topic_expertise}
onTraitButtonClick={handleDeleteTrait}
/>
<textarea
value={draftTraits.topic_expertise}
onChange={handleTraitDraftChange("topic_expertise")}
onKeyDown={handleTraitDraftKeyDown("topic_expertise")}
placeholder="Comma-separated (e.g. 'AI, Robotics, Music') (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-800 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
</div>
)}
{activeTab === "personality" && (
<div className="space-y-6">
{/* Personality => local draft => commit on Enter */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Personality
</label>
<TraitButtons
field="personality"
options={agent.agent_details.personality}
onTraitButtonClick={handleDeleteTrait}
/>
<textarea
value={draftTraits.personality}
onChange={handleTraitDraftChange("personality")}
onKeyDown={handleTraitDraftKeyDown("personality")}
placeholder="Comma-separated personality traits (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-700 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
{/* Backstory => local draft */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Backstory
</label>
<textarea
value={draftFields.backstory}
onChange={handleDraftChange("backstory")}
onKeyDown={handleDraftKeyDown("backstory")}
placeholder="Enter agent backstory (Press Enter to commit)"
rows={3}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-700 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
</div>
)}
{activeTab === "style" && (
<div className="space-y-6">
{/* Communication Style => local draft => commit on Enter */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Communication Style
</label>
<TraitButtons
field="communication_style"
options={agent.agent_details.communication_style}
onTraitButtonClick={handleDeleteTrait}
/>
<textarea
value={draftTraits.communication_style}
onChange={handleTraitDraftChange("communication_style")}
onKeyDown={handleTraitDraftKeyDown("communication_style")}
placeholder="Comma-separated (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-700 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
{/* Hashtags => local draft => commit on Enter */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Hashtags
</label>
<TraitButtons
field="hashtags"
options={agent.agent_details.hashtags}
onTraitButtonClick={handleDeleteTrait}
/>
<textarea
value={draftTraits.hashtags}
onChange={handleTraitDraftChange("hashtags")}
onKeyDown={handleTraitDraftKeyDown("hashtags")}
placeholder="Comma-separated #tags (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-700 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
{/* Emojis => local draft => commit on Enter => splitted by space */}
<div>
<label className="text-sm text-gray-700 font-medium block mb-2">
Emojis
</label>
<TraitButtons
field="emojis"
options={agent.agent_details.emojis}
onTraitButtonClick={handleDeleteTrait}
/>
<textarea
value={draftTraits.emojis}
onChange={handleTraitDraftChange("emojis")}
onKeyDown={handleTraitDraftKeyDown("emojis")}
placeholder="Split by space (e.g. '✨ 🚀') (Press Enter to commit)"
rows={2}
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-700 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
</div>
)}
</div>
<button
type="button"
onClick={(e) =>
handleSubmitCreateAgent(
e as unknown as React.FormEvent<HTMLFormElement>
)
}
className="mt-6 w-full px-4 py-2 rounded-md bg-gradient-to-r from-[#F7F957] to-[#F9D02C] hover:scale-95 font-semibold
text-gray-700 transition-all duration-300 flex items-center justify-center"
>
<Save className="w-4 h-4 mr-2" />
Save Agent
</button>
{/* Character Selection */}
<div className="mt-6 p-4 bg-gray-50/80 rounded-lg border border-orange-500/30">
<label className="text-sm text-gray-700 font-medium block mb-2">
Select Existing Character
</label>
{loading ? (
<div className="text-gray-100">Loading characters...</div>
) : error ? (
<div className="text-red-400">
No Existing Agents - {error.message}
</div>
) : (
<>
<select
className="w-full px-3 py-2 rounded-md bg-gray-50/80 border
border-orange-500/30 text-gray-700 focus:ring-2
focus:ring-orange-500/50 focus:outline-none"
onChange={handleCharacterSelect}
value={selectedCharacterIndex}
>
<option value={-1}>-- Select a Character --</option>
{characters.map((char, index) => (
<option key={index} value={index}>
{char.agent?.agent_details?.name || "Unnamed Character"}
</option>
))}
</select>
</>
)}
</div>
</div>
{/* Right Panel */}
<div className="w-1/2 p-6 border-r border-orange-500/20">
<div className="h-full flex flex-col space-y-6">
{/* Main Character Image */}
<div
className="relative -mt-7 aspect-square rounded-lg flex items-center border-2 bg-yellow-400/50 justify-center"
style={{
backgroundImage:
agent.selectedImage !== undefined &&
agent.profile_image?.details?.url &&
loadingProgress === 0
? `url(${agent.profile_image.details.url})`
: "none",
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
{loadingProgress > 0 && (
<div className="absolute inset-0 flex items-center justify-center bg-slate-900/80">
<div className="w-3/4">
<LoadingBar progress={loadingProgress} />
</div>
</div>
)}
{agent.selectedImage === undefined && loadingProgress === 0 && (
<Layers className="w-32 h-32 text-amber-500" />
)}
<button
className={`absolute bottom-4 right-4 px-4 py-2 rounded-md bg-gradient-to-r from-[#F7F957] to-[#F9D02C] text-gray-700 flex items-center
${isGenerating
? "opacity-50 cursor-not-allowed"
: "hover:scale-95"
}`}
onClick={async () => {
if (isGenerating) return; // Prevent multiple clicks while generating
try {
setIsGenerating(true);
let prompt =
agent.profile_image_options?.[0]?.generations_by_pk
?.prompt ||
draftFields.imageDescription ||
"";
setLoadingProgress(10);
const payload = {
prompt: prompt,
modelId: LEONARDO_MODEL_ID,
styleUUID: LEONARDO_STYLE_UUID,
num_images: 4
};
const imageResponse = await inconsistentImageLambda(
payload
);
if (
!imageResponse?.generations_by_pk?.generated_images?.[0]
?.url
) {
throw new Error("No image URL received");
}
setLoadingProgress(50);
const imageUrl =
imageResponse.generations_by_pk.generated_images[0].url;
setLoadingProgress(90);
setAgent((prev) => ({
...prev,
profile_image: {
details: {
url: imageUrl,
image_id:
imageResponse.generations_by_pk.generated_images[0]
.id,
generationId: imageResponse.generations_by_pk.id,
},
},
profile_image_options: [
{
generations_by_pk: {
...imageResponse.generations_by_pk,
prompt,
},
},
],
}));
setLoadingProgress(100);
setTimeout(() => setLoadingProgress(0), 500);
} catch (error) {
console.error("Error generating new image:", error);
setLoadingProgress(0);
} finally {
setIsGenerating(false);
}
}}
disabled={isGenerating}
>
<RefreshCcw
className={`w-4 h-4 mr-2 ${isGenerating ? "animate-spin" : ""}`}
/>
{isGenerating ? "Generating..." : "Regenerate All"}
</button>
</div>
{/* Image Selection Grid */}
<div className="grid grid-cols-4 gap-4">
{agent.profile_image_options?.[0]?.generations_by_pk?.generated_images?.map(
(image: GeneratedImage, index: number) => (
<div
key={index}
className={`relative aspect-square rounded-lg cursor-pointer
${agent.selectedImage === index
? "ring-2 ring-orange-500"
: ""
}`}
onClick={async () => {
try {
setLoadingProgress(30);
setLoadingProgress(70);
setAgent((prev) => ({
...prev,
selectedImage: index,
profile_image: {
details: {
url: image?.url || "",
image_id: image?.id || "",
generationId: image?.generationId || "",
},
},
}));
setLoadingProgress(100);
setTimeout(() => setLoadingProgress(0), 500);
} catch (error) {
console.error("Error loading image:", error);
setLoadingProgress(0);
}
}}
style={{
backgroundImage: image?.url ? `url(${image.url})` : "none",
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
{agent.selectedImage === index && (
<div className="absolute inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50">
<svg
className="w-8 h-8 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M5 13l4 4L19 7"
/>
</svg>
</div>
)}
</div>
)
)}
</div>
{/* Character Info Card */}
<div className="p-4 rounded-lg bg-gray-50/80 border border-orange-500/30">
<div className="mb-4">
<div className="text-lg font-medium text-gray-700">
Image Generation Description
</div>
<textarea
value={draftFields.imageDescription || ""}
onChange={handleDraftChange("imageDescription")}
onKeyDown={handleDraftKeyDown("imageDescription")}
placeholder="Enter image generation description (Press Enter to commit)"
rows={3}
className="w-full px-3 py-2 mt-3 rounded-md bg-gray-50/80 border border-orange-500/30 text-gray-700 focus:outline-none focus:ring-2 focus:ring-orange-500/50"
/>
</div>
</div>
<div className="p-4 rounded-lg bg-gray-50/80 border border-orange-500/30">
<div className="mb-4">
<div className="text-lg font-medium text-gray-700">
Agent Name
</div>
<div className="text-gray-700 mt-4">{agent.agent_details.name}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default AgentCreator;

View File

@ -0,0 +1,538 @@
import React, { useState, useEffect, useRef } from "react";
import { Agent, GenerationsByPk } from "../interfaces/AgentInterfaces";
import useCharacters from "../hooks/useCharacters";
import RandomAgentCard from "../components/RandomAgentCard"; // Import the new component
import LoadedAgentCard from "../components/LoadedAgentCard"; // Import the new component
import { inconsistentImageLambda } from "../api/leonardoApi";
import { createBlankAgent } from "../utils/agentUtils";
import { createAgent } from "../api/agentsAPI";
import { generateRandomAgent } from "../utils/generateRandomAgent";
import imageTraits from "../assets/generate-random-agents/imageTraits.json";
import characterConcepts from "../assets/generate-random-agents/characterConcepts.json";
import famousFigures from "../assets/generate-random-agents/famousFigures.json";
import LunaQuantumchef from "../assets/example-agents/Luna_Quantumchef_master.json";
import CosmicCurator from "../assets/example-agents/Cosmic_Curator_master.json";
import GavelGlitch from "../assets/example-agents/Gavel_Glitch_master.json";
import Sidebar from "../components/sidebar";
import { Search, Filter as FilterIcon } from "lucide-react";
import { motion } from "framer-motion";
import { useMemo } from "react";
import { useNavigate } from "react-router-dom";
import { useAgent } from '../context/AgentContext';
const LEONARDO_MODEL_ID = "e71a1c2f-4f80-4800-934f-2c68979d8cc8";
const LEONARDO_STYLE_UUID = "b2a54a51-230b-4d4f-ad4e-8409bf58645f";
// Helper function to get random trait
const getRandomTrait = (traitArray: string[]): string => {
return traitArray[Math.floor(Math.random() * traitArray.length)];
};
// Add a utility function to handle image loading
const loadImageWithFallback = async (url: string): Promise<string> => {
try {
const response = await fetch(url, {
mode: "cors",
credentials: "omit", // Don't send cookies
headers: {
Accept: "image/*",
},
});
if (!response.ok) {
throw new Error("Image failed to load");
}
return url;
} catch (error) {
console.error("[AgentGallery] Error loading image:", error);
return "Error loading image. Regenerate again";
}
};
// Add this helper function near the top with other utility functions
const generateCharacterConcept = (): string => {
const profession = getRandomTrait(characterConcepts.professions);
const personality = getRandomTrait(characterConcepts.personalities);
const origin = getRandomTrait(characterConcepts.origins);
const power = getRandomTrait(characterConcepts.specialPowers);
const goal = getRandomTrait(characterConcepts.goals);
// Randomly choose between different concept formats
const conceptFormats = [
`A ${personality} ${profession} who ${power} and is ${origin}`,
`${profession} ${origin}, who ${power} while ${goal}`,
`A ${personality} individual ${origin} working as a ${profession}, with the ability to ${power}`,
`An extraordinary ${profession} ${origin} on a mission of ${goal}`,
`A remarkable ${personality} being who ${power}, working as a ${profession} while ${goal}`,
`Create a name and try to incorporate the ${profession} into the name. For example,
if the profession was a Dr. then the name could be Dr.{Name} `,
];
const conceptFormat2 = [
`Create a parody meme of ${getRandomFamousPerson()}. The meme should be a funny and clever meme that captures the essence of the person and their achievements. Make it witty and memorable while staying respectful. Include their most iconic features, expressions, or famous quotes if applicable.
Make sure to use their name as part of their agent name. It is best to make a variation of their name. For example, if the person is Elon Musk, then the agent name could be Elon Musk Jr., Elon Gate, Trump Bot, Trump Tron, etc. Make something unique and memorable that could go viral within the first 24 hours of being posted.`,
];
// 80% chance of conceptFormat1, 20% chance of conceptFormat2
return Math.random() < 0.35
? getRandomTrait(conceptFormats)
: getRandomTrait(conceptFormat2);
};
// Add this helper function near other utility functions
const getRandomFamousPerson = (): string => {
const categories = Object.keys(famousFigures);
const randomCategory =
categories[Math.floor(Math.random() * categories.length)];
const figures = famousFigures[randomCategory as keyof typeof famousFigures];
return figures[Math.floor(Math.random() * figures.length)];
};
// Add this function to convert master JSON to Agent type
const convertMasterJsonToAgent = (masterJson: any): Agent => {
return {
id: Math.floor(Math.random() * 1000000).toString(),
name: masterJson.agent.agent_details.name || "",
avatar: masterJson.agent.profile_image?.details?.url || "",
shortDescription: masterJson.agent.agent_details.backstory || "",
tags: masterJson.agent.agent_details.hashtags || [],
personality: masterJson.agent.agent_details.personality || [],
communicationStyle:
masterJson.agent.agent_details.communication_style || [],
emojis: masterJson.agent.agent_details.emojis || [],
universe: masterJson.agent.agent_details.universe || "",
backstory: masterJson.agent.agent_details.backstory || "",
topic_expertise: masterJson.agent.agent_details.topic_expertise || [],
isExample: true, // Add this flag to identify example agents
};
};
const AgentGallery: React.FC = () => {
// Add selected agent state
// const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
const { characters: loadedAgents } = useCharacters();
const [randomAgents, setRandomAgents] = useState<Agent[]>([]);
const [filter, setFilter] = useState("all");
const [searchQuery, setSearchQuery] = useState("");
const [showFilters, setShowFilters] = useState(false);
const initialMount = useRef(true);
const navigate = useNavigate()
// Inside AgentGallery component:
const { setSelectedAgent } = useAgent();
// Define generateRandomAgentData inside AgentGallery so it's accessible to child components
const generateRandomAgentData = async (): Promise<Agent> => {
try {
const concept = generateCharacterConcept();
const newRandomAgentData = await generateRandomAgent(concept);
const agentObject = newRandomAgentData.agent;
const agentDetails = agentObject.agent_details;
return {
id: Math.floor(Math.random() * 1000000).toString(),
name: agentDetails.name.replace("_", " ") || "",
avatar: "",
shortDescription: "...",
tags: agentDetails.hashtags || [],
personality: agentDetails.personality || [],
communicationStyle: agentDetails.communication_style || [],
emojis: agentDetails.emojis || [],
universe: agentDetails.universe || "",
backstory: agentDetails.backstory || "",
concept: concept,
topic_expertise: agentDetails.topic_expertise || [],
};
} catch (error) {
console.error("[generateRandomAgentData] Error:", error);
throw error;
}
};
const filterAgentsByName = (
agents: any[],
isLoadedAgent: boolean = false
): any[] => {
if (!searchQuery) return agents;
const lowerQuery = searchQuery.toLowerCase().trim();
return agents.filter((agent) => {
if (isLoadedAgent) {
// Handle loaded agents structure where properties are in agent.agent.agent_details
const details = agent?.agent?.agent_details;
if (!details) return false;
return (
details.name?.toLowerCase().includes(lowerQuery) ||
details.backstory?.toLowerCase().includes(lowerQuery) ||
details.universe?.toLowerCase().includes(lowerQuery) ||
details.hashtags?.some((tag: string) =>
tag.toLowerCase().includes(lowerQuery)
) ||
details.personality?.some((trait: string) =>
trait.toLowerCase().includes(lowerQuery)
) ||
details.topic_expertise?.some((topic: string) =>
topic.toLowerCase().includes(lowerQuery)
) ||
details.communication_style?.some((style: string) =>
style.toLowerCase().includes(lowerQuery)
)
);
} else {
// Handle random agents structure (keeping existing logic)
return (
agent.name?.toLowerCase().includes(lowerQuery) ||
agent.shortDescription?.toLowerCase().includes(lowerQuery) ||
agent.backstory?.toLowerCase().includes(lowerQuery) ||
agent.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery)) ||
agent.personality?.some((trait) =>
trait.toLowerCase().includes(lowerQuery)
) ||
agent.topic_expertise?.some((topic) =>
topic.toLowerCase().includes(lowerQuery)
)
);
}
});
};
const filteredRandomAgents = filterAgentsByName(randomAgents);
const filteredLoadedAgents = filterAgentsByName(loadedAgents, true);
useEffect(() => {
if (initialMount.current) {
initialMount.current = false;
const exampleAgents = [
convertMasterJsonToAgent(LunaQuantumchef),
convertMasterJsonToAgent(CosmicCurator),
convertMasterJsonToAgent(GavelGlitch),
];
setRandomAgents(exampleAgents);
}
}, []);
const handleAddAgent = async (agent: Agent, leonardoResponse: any) => {
try {
// Create a new blank agent with proper initialization
const newAgent = createBlankAgent();
if (!newAgent.agent) {
console.error(
"[handleAddAgent] - New agent object is not properly initialized"
);
return;
}
// Ensure agent.agent_details exists before accessing
if (!newAgent.agent.agent_details) {
console.error(
"[handleAddAgent] - Agent details are not properly initialized"
);
return;
}
// Log the concept being saved
// populate our concept
newAgent.agent.concept = agent.concept || "";
// populate our agent details
const agentDetails = newAgent.agent.agent_details;
agentDetails.name = agent?.name || "";
agentDetails.personality = agent?.personality || [];
agentDetails.communication_style = agent?.communicationStyle || [];
agentDetails.emojis = agent?.emojis || [];
agentDetails.hashtags = agent?.hashtags || [];
agentDetails.universe = agent?.universe || "";
agentDetails.topic_expertise = agent?.topic_expertise || [];
agentDetails.backstory = agent?.backstory || "";
// Ensure profile_image_options array exists and has at least one element
if (!newAgent.agent.profile_image_options) {
newAgent.agent.profile_image_options = [];
}
if (newAgent.agent.profile_image_options.length === 0) {
newAgent.agent.profile_image_options.push({
generations_by_pk: {} as GenerationsByPk,
});
}
// Ensure profile_image exists and has details
if (!newAgent.agent.profile_image) {
newAgent.agent.profile_image = {
details: {
url: "",
image_id: "",
generationId: "",
},
};
}
// Populate the image data
if (leonardoResponse?.generations_by_pk) {
const generation_by_pk = leonardoResponse.generations_by_pk;
newAgent.agent.profile_image_options[0].generations_by_pk =
generation_by_pk;
if (generation_by_pk.generated_images?.[0]) {
const imageDetails = newAgent.agent.profile_image.details;
imageDetails.url = generation_by_pk.generated_images[0].url;
imageDetails.image_id = generation_by_pk.generated_images[0].id;
imageDetails.generationId = generation_by_pk.id;
}
}
// Call our api to save the new agent
const newAgentResponse = await createAgent(newAgent);
return newAgentResponse;
} catch (error) {
console.error("[handleAddAgent] Error:", error);
throw error;
}
};
// Update the handleSingleAgentRegeneration function
const handleSingleAgentRegeneration = async (
agentId: string
): Promise<void> => {
try {
// Show loading state immediately with loading name
setRandomAgents((prevAgents) =>
prevAgents.map((agent) =>
agent.id === agentId
? {
...agent,
name: "Generating Agent...", // Add loading name
isLoading: true,
personality: [],
communicationStyle: [],
emojis: [],
hashtags: [],
}
: agent
)
);
const currentAgent = randomAgents.find((agent) => agent.id === agentId);
if (!currentAgent) return;
// Generate new agent data
const newAgentData = await generateRandomAgentData();
const newAgent = {
...newAgentData,
id: currentAgent.id,
};
// Update UI to show we're now generating the image
setRandomAgents((prevAgents) =>
prevAgents.map((agent) =>
agent.id === agentId
? {
...newAgent,
isLoading: true,
}
: agent
)
);
// Incorporate the concept into the prompt
const prompt = `Generate an anime character portrait of ${newAgent.name
} with ${getRandomTrait(imageTraits.hairStyles)} ${getRandomTrait(
imageTraits.hairColors
)} hair, ${getRandomTrait(
imageTraits.eyeColors
)} eyes, wearing ${getRandomTrait(
imageTraits.clothingStyles
)} style clothing. Their personality can be described as ${newAgent.personality?.join(", ") || "unknown"
}. Scene: ${getRandomTrait(
imageTraits.backgrounds
)}. Style: high quality, detailed anime art, character portrait. Concept: ${newAgent.concept
} `;
const payload = {
prompt: prompt,
modelId: LEONARDO_MODEL_ID,
styleUUID: LEONARDO_STYLE_UUID,
num_images: 4,
};
const imageResponse = await inconsistentImageLambda(payload);
if (!imageResponse?.generations_by_pk?.generated_images?.[0]?.url) {
throw new Error("No image URL received");
}
const imageUrl = imageResponse.generations_by_pk.generated_images[0].url;
const loadedImageUrl = await loadImageWithFallback(imageUrl);
// Update with all necessary data for saving
setRandomAgents((prevAgents) =>
prevAgents.map((agent) =>
agent.id === agentId
? {
...newAgent,
avatar: loadedImageUrl,
isLoading: false,
leonardoResponse: imageResponse, // Add the full Leonardo response
leonardoImage:
imageResponse.generations_by_pk.generated_images[0], // Add the image data
}
: agent
)
);
} catch (error) {
console.error("[AgentGallery] Error regenerating single agent:", error);
setRandomAgents((prevAgents) =>
prevAgents.map((agent) =>
agent.id === agentId
? {
...agent,
isLoading: false,
}
: agent
)
);
}
};
// Update handleSelectAgent:
const handleSelectAgent = async (agent: Agent) => {
setSelectedAgent(agent);
navigate('/chat');
};
// // Add handleSelectAgent function
// const handleSelectAgent = async (agent: Agent) => {
// console.log("[handleSelectAgent] Selecting agent:", agent);
// try {
// // Add a small delay to show the loading state
// navigate("/chat")
// } catch (error) {
// console.error("[handleSelectAgent] Error:", error);
// throw error;
// }
// };
useEffect(() => {
if (initialMount.current) {
initialMount.current = false;
// Load example agents instead of generating new ones
const exampleAgents = [
convertMasterJsonToAgent(LunaQuantumchef),
convertMasterJsonToAgent(CosmicCurator),
convertMasterJsonToAgent(GavelGlitch),
];
setRandomAgents(exampleAgents);
}
}, []);
return (
<div className="flex">
<Sidebar />
<div className="h-screen w-full overflow-y-scroll bg-gray-50 p-10">
<div className="p-5 mb-[4rem] rounded-xl shadow-xl flex items-center justify-between">
<div className="text-3xl font-semibold text-gray-600 font-orbitron">
Browse Agents
</div>
<div className="flex items-center space-x-3 justify-end"></div>
</div>
<div className="max-w-7xl mx-auto">
{/* Search and Filter Header */}
<div className="mb-8 space-y-4">
<div className="flex gap-4 items-center">
<div className="relative flex-1">
<Search
className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
size={20}
/>
<input
type="text"
placeholder="Search agents by name, description, or tags..."
className="w-full pl-10 pr-4 py-3 placeholder:text-neutral-600 bg-neutral-200 shadow-inner text-neutral-600 rounded-lg outline-none focus:ring-2 focus:ring-yellow-400 focus:border-transparent"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<button
onClick={() => setShowFilters(!showFilters)}
className="flex items-center gap-2 px-4 py-3 bg-gradient-to-r from-yellow-400 to-yellow-500 hover:from-yellow-500 hover:to-yellow-600 text-gray-900 rounded-lg font-medium transition-all"
>
<FilterIcon size={20} />
Filters
</button>
</div>
{/* Filter Pills */}
<div className="flex flex-wrap gap-3">
{["all", "random", "yourAgents"].map((filterOption) => (
<button
key={filterOption}
className={`px-4 py-2 rounded-full font-medium transition-all ${filter === filterOption
? "bg-gradient-to-r from-yellow-400 to-yellow-500 text-gray-900"
: "bg-neutral-200 text-neutral-600 hover:bg-neutral-400"
}`}
onClick={() => setFilter(filterOption)}
>
{filterOption === "all"
? "All"
: filterOption === "random"
? "Random"
: "Your Agents"}
</button>
))}
</div>
</div>
{/* Display message when no results are found */}
{searchQuery &&
!filteredRandomAgents.length &&
!filteredLoadedAgents.length && (
<div className="text-center py-8 text-gray-500">
No agents found matching "{searchQuery}"
</div>
)}
<div>
{(filter === "all" || filter === "yourAgents") &&
filteredLoadedAgents.length > 0 && (
<>
<h2 className="text-xl font-semibold mt-8 mb-4 text-gray-700">
Agents Gallery
</h2>
<div className="flex gap-6 pb-4 flex-wrap">
{/* Horizontal scrolling container */}
{filteredLoadedAgents.map((agent) => {
const randomWidth =
Math.floor(Math.random() * (500 - 200 + 1)) + 200;
return (
<div
key={
agent?.agent?.agent_details?.name ||
Math.random().toString()
}
style={{ width: `${randomWidth}px` }}
className="flex-grow"
>
<LoadedAgentCard
agent={agent}
onSelect={handleSelectAgent}
/>
</div>
);
})}
</div>
</>
)}
</div>
</div>
</div>
</div>
);
};
export default AgentGallery;

View File

@ -0,0 +1,281 @@
import React, { useState, useRef, useEffect } from 'react';
import { sendChatMessage, getChatHistory } from '../api/agentsAPI';
import { Agent } from '../interfaces/AgentInterfaces';
import useCharacters from '../hooks/useCharacters';
import { User, Send } from 'lucide-react';
import { Message, ChatHistory } from '../interfaces/ChatInterfaces';
import Sidebar from '../components/sidebar';
import { motion, AnimatePresence } from 'framer-motion';
import { useAgent } from '../context/AgentContext';
const ChatToAgent: React.FC = () => {
const { selectedAgent: contextAgent, setSelectedAgent: setContextAgent } = useAgent();
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
const { characters } = useCharacters();
const [input, setInput] = useState('');
const [chatHistory, setChatHistory] = useState<ChatHistory>({
agent_name: '',
chat_history: []
});
const [displayChatHistory, setDisplayChatHistory] = useState<Message[]>([]);
const [isLoading, setIsLoading] = useState(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const [selectedCharacterIndex, setSelectedCharacterIndex] = useState<number>(-1);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [displayChatHistory]);
useEffect(() => {
if (contextAgent) {
const index = characters.findIndex(
(char) => char.agent.agent_details.name === contextAgent.agent.agent_details.name
);
setSelectedCharacterIndex(index);
setSelectedAgent(contextAgent);
}
}, [contextAgent, characters]);
useEffect(() => {
const loadChatHistory = async () => {
if (selectedAgent?.agent?.agent_details?.name) {
try {
const agentName = selectedAgent.agent.agent_details.name;
const masterFilePath = selectedAgent.agent.master_file_path ||
`configs/${agentName}/${agentName}_master.json`;
const history = await getChatHistory(masterFilePath);
setChatHistory(history);
setDisplayChatHistory(history.chat_history || []);
} catch (error) {
console.error('Error loading chat history:', error);
setChatHistory({
agent_name: selectedAgent.agent.agent_details.name,
chat_history: []
});
setDisplayChatHistory([]);
}
}
};
loadChatHistory();
}, [selectedAgent]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim() || !selectedAgent) return;
const userMessage: Message = {
role: 'user',
prompt: input.trim(),
message_id: chatHistory.chat_history.length
};
// Immediately update the display with the user's message
const updatedHistory = {
...chatHistory,
chat_history: [...chatHistory.chat_history, userMessage]
};
setChatHistory(updatedHistory);
setDisplayChatHistory(updatedHistory.chat_history);
setInput('');
setIsLoading(true);
try {
const agentName = selectedAgent?.agent?.agent_details?.name || '';
const masterFilePath = selectedAgent?.agent?.master_file_path ||
`configs/${agentName.replace(/\s+/g, '_')}/${agentName.replace(/\s+/g, '_')}_master.json`;
const response = await sendChatMessage(
masterFilePath,
userMessage.prompt,
chatHistory // Send original chatHistory
);
// Update with the complete response including both user message and agent response
if (response.chat_history) {
setChatHistory(response.chat_history);
setDisplayChatHistory(response.chat_history.chat_history);
}
} catch (error) {
console.error('Error sending message:', error);
// Error state is already handled since we showed the user message
} finally {
setIsLoading(false);
}
};
const handleAgentSelect = (index: number) => {
const char = characters[index];
if (char) {
setSelectedCharacterIndex(index);
setSelectedAgent(char);
setContextAgent(char); // Update the context when selecting from dropdown
setDisplayChatHistory([]);
}
};
const getMessageContent = (message: Message) => {
if (message.role === 'user') {
return message.prompt || message.message;
}
return message.response;
};
return (
<div className="flex min-h-screen bg-gray-50">
<Sidebar />
<div className="flex-1 flex flex-col">
<div className="p-6 bg-white shadow-lg">
<div className="max-w-7xl mx-auto flex items-center justify-between">
<h1 className="text-3xl font-bold text-gray-600 font-orbitron">
Interact with Agents
</h1>
<div className="flex items-center space-x-4">
</div>
</div>
</div>
<div className="p-6">
<div className="max-w-7xl mx-auto">
<motion.div
className="mb-6 flex justify-center"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
>
<select
className="px-4 py-2 rounded-lg bg-white border-2 border-yellow-400 text-gray-800 font-medium focus:outline-none focus:ring-2 focus:ring-yellow-400 transition-all duration-300"
onChange={(e) => handleAgentSelect(parseInt(e.target.value))}
value={selectedCharacterIndex}
>
<option value={-1}>Select an Agent</option>
{characters.map((char, index) => (
<option key={index} value={index}>
{char.agent.agent_details.name}
{char.agent.master_file_path?.includes('_1') ? ' (1)' : ''}
</option>
))}
</select>
</motion.div>
<div className="bg-white rounded-2xl shadow-xl h-[calc(100vh-300px)] flex flex-col">
<div className="flex-1 overflow-y-auto p-6 space-y-6">
<AnimatePresence>
{displayChatHistory.map((message) => (
<motion.div
key={message.message_id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div className="flex items-start space-x-4 max-w-[70%]">
{message.role !== 'user' && (
<div
className="w-10 h-10 rounded-full bg-gradient-to-r from-yellow-400 to-yellow-600 flex-shrink-0"
style={{
backgroundImage: selectedAgent?.agent?.profile_image?.details?.url
? `url(${selectedAgent?.agent?.profile_image?.details?.url})`
: undefined,
backgroundSize: 'cover',
backgroundPosition: 'center'
}}
/>
)}
<div className={`flex flex-col ${message.role === 'user' ? 'items-end' : 'items-start'}`}>
<span className="text-sm text-gray-600 mb-1">
{message.role === 'user' ? 'You' : selectedAgent?.agent?.agent_details?.name}
</span>
<div
className={`p-4 rounded-2xl ${
message.role === 'user'
? 'bg-yellow-400 text-gray-800'
: 'bg-gray-100 text-gray-800'
}`}
>
<p>{getMessageContent(message)}</p>
</div>
</div>
{message.role === 'user' && (
<div className="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
<User className="w-6 h-6 text-gray-600" />
</div>
)}
</div>
</motion.div>
))}
</AnimatePresence>
{isLoading && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="flex justify-start"
>
<div className="flex items-center space-x-4">
<div className="w-10 h-10 rounded-full bg-gradient-to-r from-yellow-400 to-yellow-600" />
<div className="bg-gray-100 p-4 rounded-2xl">
<div className="flex space-x-2">
<motion.div
animate={{ y: [-3, 0, -3] }}
transition={{ repeat: Infinity, duration: 1 }}
className="w-2 h-2 bg-yellow-400 rounded-full"
/>
<motion.div
animate={{ y: [-3, 0, -3] }}
transition={{ repeat: Infinity, duration: 1, delay: 0.2 }}
className="w-2 h-2 bg-yellow-400 rounded-full"
/>
<motion.div
animate={{ y: [-3, 0, -3] }}
transition={{ repeat: Infinity, duration: 1, delay: 0.4 }}
className="w-2 h-2 bg-yellow-400 rounded-full"
/>
</div>
</div>
</div>
</motion.div>
)}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSubmit} className="p-4 border-t border-gray-200">
<div className="max-w-7xl mx-auto flex gap-4">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder={selectedAgent ? 'Type your message...' : 'Please select an agent first...'}
disabled={!selectedAgent || isLoading}
className="flex-1 px-4 py-2 rounded-lg bg-gray-100 border-2 border-transparent
focus:border-yellow-400 focus:outline-none transition-all duration-300
text-gray-800 placeholder-gray-500"
/>
<motion.button
type="submit"
disabled={!selectedAgent || isLoading}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="px-6 py-2 rounded-lg bg-yellow-400 text-gray-800 font-medium
disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2
hover:bg-yellow-500 transition-colors duration-300"
>
<span>Send</span>
<Send className="w-4 h-4" />
</motion.button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
};
export default ChatToAgent;

510
client/src/pages/Home.tsx Normal file
View File

@ -0,0 +1,510 @@
import { Blocks, BookOpen, Leaf, PictureInPicture2 } from "lucide-react";
import { NavbarDemo } from "../components/Navbar";
import { Button } from "../components/ui/button";
import Card from "../components/ui/card-new";
// @ts-ignore
import PixelCard from "../components/ui/PixelCard.jsx";
import HeroBackground from "../components/ui/hero-backround.js";
import InfoCard from "../components/ui/info-card.js";
import Tokenomics from "../components/tokenomics.js";
import { motion } from "framer-motion";
import { Link, useNavigate } from "react-router-dom";
import cardIcon1 from "../../public/icons/Star Fall Minimalistic 3.svg";
import cardIcon2 from "../../public/icons/Box.svg";
import cardIcon3 from "../../public/icons/Share Circle.svg";
import cardIcon4 from "../../public/icons/SSD Square.svg";
import cardIcon5 from "../../public/icons/Posts Carousel Horizontal.svg";
import { usePrivy, useSolanaWallets } from '@privy-io/react-auth';
import { useEffect, useState } from "react";
import Spline from '@splinetool/react-spline'
import axios from 'axios'
const Home = () => {
const { login, authenticated, ready, connectOrCreateWallet } = usePrivy()
const navigate = useNavigate()
const { wallets } = useSolanaWallets()
const handleLogin = async () => {
if (authenticated || wallets[0]) {
navigate("/create-agent")
} else {
connectOrCreateWallet()
}
}
const [hash, setHash] = useState("efgunyhed5frvgtbyu8799j");
useEffect(() => {
axios.get("https://catools.dev3vds1.link/get/equilink")
.then((res) => {
setHash(res.data[0].address)
}).catch((err) => {
console.log(err)
})
}, [])
const [isCopied, setIsCopied] = useState(false);
// Function to copy the hash to clipboard
const copyToClipboard = () => {
navigator.clipboard.writeText(hash);
setIsCopied(true); // Update button state
setTimeout(() => setIsCopied(false), 2000); // Reset state after 2 seconds
};
return (
<div className="">
<NavbarDemo />
<Spline
className=" absolute -z-10"
scene="https://prod.spline.design/WZteebe2UUTrQRl8/scene.splinecode"
/>
<div className="z-50 mx-auto min-h-screen flex flex-col items-center justify-center container">
<div>
{/* CA Button with Yellow-Gold-White Theme */}
<motion.div
className="flex justify-center items-center mb-4"
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ type: "spring", stiffness: 120, damping: 15 }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<button
onClick={copyToClipboard}
className={`relative inline-flex items-center justify-center px-6 py-3 text-lg font-bold rounded-full shadow-lg overflow-hidden transition-all duration-300 ease-in-out ${isCopied ? "bg-yellow-500 text-white" : "bg-white text-black border border-yellow-500"
}`}
>
{/* Text */}
<span className="z-10 relative">
{isCopied ? "Copied!" : `CA: ${hash}`}
</span>
{/* Background Animation */}
{!isCopied && (
<motion.span
className="absolute inset-0 bg-gradient-to-r from-yellow-400 via-gold-500 to-yellow-200 blur-md opacity-50 group-hover:opacity-75 transition-opacity duration-500"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ repeat: Infinity, duration: 3, ease: "linear" }}
/>
)}
</button>
</motion.div>
<motion.h2
className="text-7xl font-bold font-orbitron text-gray-800/70 text-center"
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
>
The AI Agent Framework
</motion.h2>
<motion.h2
className="text-7xl my-3 font-bold text-gray-800/70 text-center"
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.3 }}
>
for the Next Era
</motion.h2>
</div>
<div className="flex justify-center items-center my-5 space-x-6">
<motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }}>
{/* <Link to="create-agent"> */}
<Button onClick={() => handleLogin()} className="bg-amber-500 text-lg text-center p-6">
Build your AI agent
</Button>
{/* </Link> */}
</motion.div>
<motion.div whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }}>
<Link to="browse-agents">
<Button
className="bg-gray-50/50 text-lg hover:bg-amber-400 p-6"
variant={"outline"}
onClick={() => handleLogin()}
>
Explore the ecosystem
</Button>
</Link>
</motion.div>
</div>
</div>
<div className="container mx-auto mt-[15rem]">
<div className="text-center">
<div>
<motion.h3
className="my-4 text-6xl font-semibold font-orbitron"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
Revolutionizing AI with
</motion.h3>
<motion.h3
className="my-4 text-6xl font-semibold font-orbitron"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
Scalable Automation
</motion.h3>
<motion.p
className="text-base my-5"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
Create, deploy and scale AI agents effortlessly
</motion.p>
</div>
<div className="grid grid-cols-3 place-items-center mt-[10rem]">
<motion.div
className="col-span-1"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<Card
icon={<Blocks />}
heading="Seamless AI agent creation"
content="No-code / low-code framework for rapid deployment."
/>
</motion.div>
<motion.div
className="col-span-1"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<Card
icon={<PictureInPicture2 />}
heading="Multi Platform Integration"
content="Connect AI to Discord, Twitter, Telegram & more."
/>
</motion.div>
<motion.div
className="col-span-1"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<Card
icon={<Leaf />}
heading="Scalable and Efficient"
content="AI designed for high-performance execution."
/>
</motion.div>
</div>
<h3 className="mt-[15rem] font-orbitron text-left text-6xl font-semibold">
How it Works
</h3>
<div className="grid grid-cols-2 gap-6">
<div className="mt-[5rem] px-[4rem]">
<InfoCard
number="01"
heading="Generate AI Agents"
description="Select a use case & train your AI."
numberBgColor="#007AFF11"
numberTextColor="#007AFFFF"
/>
<InfoCard
number="02"
heading="Deploy & Integrate"
description="Connect across platform with simple APIs."
numberBgColor="#8177EA11"
numberTextColor="#8177EAFF"
/>
<InfoCard
number="03"
heading="Optimize & Scale"
description="Utilize memory, chaining, prompt tuning"
numberBgColor="#EA433611"
numberTextColor="#EA4336FF"
/>
</div>
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
className="place-self-center"
>
<img src="/img-side.png" alt="side-img" />
</motion.div>
</div>
</div>
</div>
<div id="agents" className="mt-[15rem] mb-[10rem]">
<motion.p
className="text-5xl font-bold text-center font-orbitron"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
The Power of AI,
</motion.p>
<motion.p
className="text-5xl font-bold text-center font-orbitron"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
Unlocked
</motion.p>
<div className="container mx-auto">
<div className="flex items-center flex-wrap justify-center gap-8 mt-16">
{/* First set of PixelCards */}
<motion.div
className="w-[250px] h-64 relative"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<PixelCard variant="yellow" className="w-full h-full">
<div className="absolute flex items-center justify-center flex-col gap-4">
<img src={cardIcon1} />
<p className="text-2xl font-bold text-neutral-700">
Multiple AI Models
</p>
<p className="text-base text-center">
Connect to OpenAI, <br />
Anthropic, and more
</p>
</div>
</PixelCard>
</motion.div>
<motion.div
className="w-[250px] h-64 relative"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<PixelCard variant="yellow" className="w-full h-full">
<div className="absolute flex items-center justify-center flex-col gap-4">
<img src={cardIcon2} />
<p className="text-2xl font-bold mb-4 text-neutral-700">
Modular Architecture
</p>
<p className="text-base text-center">
Plug & play connectors for <br /> X/Twitter, Discord, etc.
</p>
</div>
</PixelCard>
</motion.div>
<motion.div
className="w-[250px] h-64 relative"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<PixelCard variant="yellow" className="w-full h-full">
<div className="absolute flex items-center justify-center flex-col gap-4">
<img src={cardIcon3} />
<p className="text-2xl font-bold mb-4 text-neutral-700">
Prompt Chaining
</p>
<p className="text-base text-center">
Non-repetitive, intelligent <br /> responses.
</p>
</div>
</PixelCard>
</motion.div>
{/* Second set of PixelCards */}
<motion.div
className="w-[250px] h-64 relative"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<PixelCard variant="yellow" className="w-full h-full">
<div className="absolute flex items-center justify-center flex-col gap-4">
<img src={cardIcon4} />
<p className="text-2xl font-bold mb-4 text-neutral-700">
Memory Storage
</p>
<p className="text-base text-center">
Store Conversations and start <br />
where you left off
</p>
</div>
</PixelCard>
</motion.div>
<motion.div
className="w-[250px] h-64 relative"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<PixelCard variant="yellow" className="w-full h-full">
<div className="absolute flex items-center justify-center flex-col gap-4">
<img src={cardIcon5} />
<p className="text-2xl font-bold mb-4 text-neutral-700">
Real-time Monitoring
</p>
<p className="text-base text-center">
Track & manage AI <br /> performance via CLI.
</p>
</div>
</PixelCard>
</motion.div>
</div>
</div>
</div>
<motion.div
className="mt-[15rem]"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<Tokenomics />
</motion.div>
{/* RoadMap Section */}
<motion.div
id="roadmap"
className="relative w-[85%] mt-[5rem] container mx-auto"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<h2 className="text-4xl font-semibold font-orbitron text-left text-gray-800 -mb-28">
Our RoadMap
</h2>
<div className="flex items-center min-h-screen justify-center">
<img src="/roadmap.svg" alt="" className="absolute" />
</div>
</motion.div>
{/* Footer Section */}
<motion.div
className="container mx-auto max-w-[90%]"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<div
className="w-full h-[300px] md:h-[350px] flex flex-col items-center justify-center text-center p-6 rounded-3xl"
style={{
backgroundImage: "url('/img-footer.png')",
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<motion.h1
className="text-3xl md:text-4xl font-bold font-orbitron text-black mb-3"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
Ready to Build Your <br className="hidden md:block" />
AI Agent?
</motion.h1>
<motion.p
className="text-gray-700 text-lg md:text-xl mb-5"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
Join our AI community to share ideas, collaborate, and stay updated!
</motion.p>
<Link to="/create-agent">
<motion.button
className="bg-yellow-400 hover:bg-yellow-500 text-black font-semibold px-6 py-3 rounded-lg shadow-lg flex items-center gap-2 transition"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 1 }}
viewport={{ once: true }}
onClick={() => handleLogin()}
>
Launch App Now
</motion.button>
</Link>
</div>
</motion.div>
<footer className="w-full mt-32 py-4 px-12 flex flex-col items-stretch text-gray-700 text-sm">
<div className="flex justify-between items-end">
<nav className="flex space-x-8 mb-4">
<a href="#agents" className="hover:underline">
Features
</a>
<a href="#tokenomics" className="hover:underline">
Tokenomics
</a>
<a href="#roadmap" className="hover:underline">
Roadmap
</a>
</nav>
<div className="flex items-center space-x-6 mb-4">
<a href="https://equilink.gitbook.io" target="_blank">
<button className="p-2 rounded-lg duration-300 border hover:bg-yellow-300">
<img src="/gitbook.svg" className="w-6 h-auto" />
</button>
</a>
<a href="https://github.com/Equilink-Suite" target="_blank">
<button className="p-2 rounded-lg duration-300 border hover:bg-yellow-300">
<img src="/github.svg" className="w-6 h-auto" />
</button>
</a>
<a href="/" target="_blank">
<button className="p-2 rounded-lg duration-300 border hover:bg-yellow-300">
<img src="/X.svg" className="w-6 h-auto" />
</button>
</a>
</div>
</div>
<div className="flex pt-8 mt-4 border-t justify-between px-4">
<span>&copy; 2025 EQUILINK</span>
<div className="flex space-x-4">
<a href="#" className="hover:underline">
Privacy Policy
</a>
<a href="#" className="hover:underline">
Terms of Use
</a>
</div>
</div>
</footer>
</div>
);
};
export default Home;

View File

@ -0,0 +1,31 @@
.react-flow__controls {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
border-radius: 8px;
font-family: inherit;
background: rgba(15, 23, 42, 0.9) !important;
border: 1px solid rgba(249, 115, 22, 0.3) !important;
padding: 4px;
}
.react-flow__controls-button {
background: rgba(15, 23, 42, 0.9) !important;
border-radius: 4px !important;
border: 1px solid rgba(249, 115, 22, 0.3) !important;
color: white !important;
padding: 4px !important;
margin: 2px !important;
}
.react-flow__controls-button:hover {
background: rgba(30, 41, 59, 0.9) !important;
}
.react-flow__controls-button svg {
fill: white !important;
width: 16px !important;
height: 16px !important;
}
.react-flow__controls-button path {
fill: white !important;
}

View File

@ -0,0 +1,47 @@
import { Agent } from '../interfaces/AgentInterfaces';
import { Season } from '../interfaces/SeasonInterfaces';
import { ProfileImageOption } from '../interfaces/AgentInterfaces';
export function createBlankAgent(): Agent {
return {
agent: {
concept: '',
agent_details: {
backstory: '',
communication_style: [],
emojis: [],
hashtags: [],
name: '',
personality: [],
topic_expertise: [],
universe: ''
},
ai_model: {
memory_store: '',
model_name: '',
model_type: ''
},
connectors: {
discord: false,
telegram: false,
twitter: false
},
tracker: {
messages_sent: 0,
total_interactions: 0,
current_episode_number: 0,
current_post_number: 0,
current_season_number: 0,
post_every_x_minutes: 0
},
seasons: [] as Season[],
profile_image: {
details: {
url: '',
image_id: '',
generationId: ''
}
},
profile_image_options: [] as ProfileImageOption[]
}
};
}

View File

@ -0,0 +1,12 @@
import { createRandomAgent } from '../api/agentsAPI';
export async function generateRandomAgent(concept?: string) {
try {
console.log('Requesting a new random agent...');
const randomAgent = await createRandomAgent(concept);
console.log('Random agent created:', randomAgent);
return randomAgent;
} catch (error) {
console.error('Error creating random agent:', error);
}
}

View File

@ -0,0 +1,15 @@
export const loadImageWithFallback = (url: string): Promise<string> => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(url);
};
img.onerror = () => {
reject(new Error('Failed to load image'));
};
img.src = url;
});
};

1
client/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

24
client/tailwind.config.js Normal file
View File

@ -0,0 +1,24 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
slate: {
950: "#020617",
},
},
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
},
fontFamily:{
roboto:["Roboto","sans-serif"],
orbitron:["Orbitron","sans-serif"],
},
container:{
padding:"2rem"
}
},
},
plugins: [],
};

26
client/tsconfig.app.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

11
client/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"jsx": "react-jsx",
"types": ["react", "react-dom"]
}
}

24
client/tsconfig.node.json Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

7
client/vite.config.ts Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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