Showing preview only (408K chars total). Download the full file or copy to clipboard to get everything.
Repository: yoheinakajima/babyagi
Branch: main
Commit: fa8930ebe72a
Files: 65
Total size: 386.8 KB
Directory structure:
gitextract_nxx39r7_/
├── .gitignore
├── .replit
├── CNAME
├── CODE_READINESS_ANALYSIS.md
├── MANIFEST.in
├── README.md
├── babyagi/
│ ├── __init__.py
│ ├── api/
│ │ └── __init__.py
│ ├── dashboard/
│ │ ├── __init__.py
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── style.css
│ │ │ └── js/
│ │ │ ├── dashboard.js
│ │ │ ├── function_details.js
│ │ │ ├── function_graph.js
│ │ │ ├── log_dashboard.js
│ │ │ └── log_graph.js
│ │ └── templates/
│ │ ├── base.html
│ │ ├── chat.html
│ │ ├── function_details.html
│ │ ├── function_graph.html
│ │ ├── function_graph_3d.html
│ │ ├── function_graph_mermaid.html
│ │ ├── index.html
│ │ ├── log_page.html
│ │ ├── log_relationship_graph.html
│ │ └── logs_dashboard.html
│ └── functionz/
│ ├── __init__.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── execution.py
│ │ ├── framework.py
│ │ └── registration.py
│ ├── db/
│ │ ├── __init__.py
│ │ ├── base_db.py
│ │ ├── db_router.py
│ │ ├── local_db.py
│ │ └── models.py
│ └── packs/
│ ├── default/
│ │ ├── ai_functions.py
│ │ ├── default_functions.py
│ │ ├── function_calling_chat.py
│ │ └── os.py
│ ├── drafts/
│ │ ├── choose_or_create_function.py
│ │ ├── code_writing_functions.py
│ │ ├── generate_function.py
│ │ ├── react_agent.py
│ │ ├── self_build.py
│ │ ├── self_build2.py
│ │ └── user_db.py
│ └── plugins/
│ ├── airtable.py
│ ├── augie.py
│ ├── e2b.py
│ ├── firecrawl.py
│ ├── harmonic.py
│ ├── payman.py
│ ├── serpapi.py
│ ├── voilanorbert.py
│ └── wokelo.py
├── examples/
│ ├── custom_flask_example.py
│ ├── custom_route_example.py
│ ├── quickstart_example.py
│ ├── self_build_example.py
│ ├── simple_example.py
│ └── trigger_example.py
├── main.py
├── pyproject.toml
├── requirements.txt
└── setup.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Custom ignores
funztionz.db
encryption_key.json
================================================
FILE: .replit
================================================
entrypoint = "main.py"
modules = ["python-3.11"]
[nix]
channel = "stable-24_05"
[unitTest]
language = "python3"
[gitHubImport]
requiredFiles = [".replit", "replit.nix"]
[deployment]
run = ["python3", "main.py"]
deploymentTarget = "cloudrun"
[[ports]]
localPort = 5000
externalPort = 5000
[[ports]]
localPort = 8000
externalPort = 8000
[[ports]]
localPort = 8080
externalPort = 80
================================================
FILE: CNAME
================================================
babyagi.org
================================================
FILE: CODE_READINESS_ANALYSIS.md
================================================
# BabyAGI Code Readiness Analysis
**Analysis Date:** January 2026
**Repository:** yoheinakajima/babyagi
**Verdict:** **NOT PRODUCTION READY**
---
## Executive Summary
BabyAGI is an experimental self-building autonomous agent framework built on a custom "functionz" function management system. **The author explicitly states this is not meant for production use**, and this analysis confirms that assessment. While the project demonstrates innovative ideas around self-building agents, it has significant issues that must be addressed before recommending it for general use.
### Quick Assessment
| Category | Score | Status |
|----------|-------|--------|
| Security | 2/10 | **Critical Issues** |
| Testing | 0/10 | **No Tests** |
| Documentation | 4.5/10 | **Moderate** |
| Error Handling | 6.5/10 | **Mixed** |
| Dependencies | 3/10 | **Poor** |
| Code Quality | 5/10 | **Experimental** |
| **Overall Readiness** | **3/10** | **Not Ready** |
---
## 1. Project Overview
### What is BabyAGI?
BabyAGI is an experimental framework for a self-building autonomous agent. The core philosophy is that "the optimal way to build a general autonomous agent is to build the simplest thing that can build itself."
**Key Components:**
- **Functionz Framework**: Core engine for storing, managing, and executing functions from a database
- **Flask Dashboard**: Web UI for function management, monitoring, and logs
- **REST API**: Endpoints for programmatic function management
- **Function Packs**: Pre-built function libraries (default, drafts, plugins)
- **Self-Building Agents**: Experimental features for AI-powered function generation
### Author's Own Assessment
From the README:
> *"This is a framework built by Yohei who has never held a job as a developer. The purpose of this repo is to share ideas and spark discussion and for experienced devs to play with. **Not meant for production use**. Use with caution."*
---
## 2. Critical Issues Requiring Immediate Attention
### 2.1 Security Vulnerabilities
#### CRITICAL: Arbitrary Code Execution (RCE)
**Location:** `babyagi/functionz/core/execution.py:44, 122`
The framework uses `exec()` to execute function code stored in the database without any sandboxing or validation:
```python
exec(function_version['code'], local_scope)
```
**Risk:** Anyone who can write to the database can execute arbitrary code on the host system.
#### CRITICAL: SQL Injection
**Location:** `babyagi/functionz/packs/drafts/user_db.py:251`
Raw SQL is constructed using f-strings:
```python
alter_stmt = f'ALTER TABLE {table_name} ADD COLUMN {new_column.name} {new_column.type}'
user_db.engine.execute(alter_stmt)
```
**Risk:** Complete database compromise through malicious table names.
#### CRITICAL: Encryption Key Exposure
**Location:** `babyagi/functionz/db/models.py:28`
The encryption key is printed to stdout/logs:
```python
print(f"Using encryption key: {ENCRYPTION_KEY}")
```
**Risk:** All encrypted secrets can be decrypted if logs are accessible.
#### HIGH: Secrets Injection Without Scoping
**Location:** `babyagi/functionz/core/execution.py:158-162`
ALL stored secret keys are injected into every function's execution scope:
```python
local_scope.update(secret_keys) # All secrets available to any function
```
**Risk:** Any function can access all stored credentials.
### 2.2 No Test Coverage
**Finding:** Zero tests exist in the entire codebase.
- No `test_*.py` files
- No `tests/` directory
- No pytest, unittest, or any test framework configured
- No CI/CD pipeline
**Impact:** No automated verification that the code works correctly. Any change could introduce regressions without detection.
### 2.3 Dependency Management Chaos
**Finding:** Three conflicting dependency systems:
1. `requirements.txt` (pip)
2. `pyproject.toml` (Poetry)
3. `setup.py` (setuptools)
**Critical Problems:**
- `poetry.lock` only tracks 11 packages; core dependencies like SQLAlchemy, cryptography, scikit-learn are missing
- Four critical packages have NO version constraints: `cryptography`, `scikit-learn`, `litellm`, `openai`
- Version conflicts: setup.py says Python >=3.6, pyproject.toml says >=3.10.0,<3.12
- Package versions out of sync (setup.py: 0.1.2, pyproject.toml: 0.0.8)
---
## 3. Complete Issue Inventory
### Security Issues (16 found)
| Severity | Issue | Location |
|----------|-------|----------|
| CRITICAL | Arbitrary code execution via exec() | execution.py:44,122 |
| CRITICAL | SQL injection vulnerability | user_db.py:251 |
| CRITICAL | Encryption key printed to logs | models.py:28 |
| CRITICAL | Plaintext encryption key file | models.py:15-20 |
| HIGH | All secrets injected to all functions | execution.py:158-162 |
| HIGH | Unvalidated pip install of packages | execution.py:19 |
| HIGH | Insufficient input validation | execution.py:170-174 |
| HIGH | Weak secret storage mechanism | local_db.py:235-259 |
| MEDIUM | Debug logging of secret operations | local_db.py:236-244 |
| MEDIUM | Database file permissions unset | local_db.py:14 |
| MEDIUM | No CSRF protection | api/__init__.py |
| MEDIUM | No rate limiting | api/__init__.py |
| MEDIUM | Unvalidated dynamic imports | execution.py:32-35 |
| MEDIUM | Duplicate method definitions | local_db.py:235,248 |
| LOW | No timeout on code execution | execution.py:55-141 |
| LOW | No authentication on API/dashboard | Multiple files |
### Code Quality Issues
| Issue | Location | Impact |
|-------|----------|--------|
| Silent exception suppression | `__init__.py:122-123` | Errors hidden from users |
| print() instead of logging | Multiple files | Inconsistent logging |
| No custom exception classes | Entire codebase | Poor error semantics |
| Extensive DEBUG print statements | drafts/*.py | Development code in repo |
### Incomplete/Experimental Features
The `drafts/` directory contains experimental features explicitly marked as incomplete:
- `generate_function.py` - 674 lines with 26+ DEBUG statements
- `self_build.py` / `self_build2.py` - Self-building agent experiments
- `choose_or_create_function.py` - Function selection logic
- `react_agent.py` - ReAct agent implementation
From README: *"These draft features are experimental concepts and may not function as intended. They require significant improvements and should be used with caution."*
---
## 4. Documentation Assessment
### Strengths
- Well-structured README with clear quick start
- Good examples in `examples/` directory
- Progressive complexity from basic to advanced features
- Clear warnings about experimental status
### Gaps
- No API documentation (no OpenAPI/Swagger spec)
- Limited docstrings (56% coverage, but minimal Args/Returns)
- No architecture documentation
- No troubleshooting guide
- No generated documentation (Sphinx, MkDocs, etc.)
**Score: 4.5/10**
---
## 5. Error Handling Assessment
### Strengths
- No bare `except:` clauses - good practice
- Widespread try/except coverage in API layer
- Proper re-raising in critical execution paths
- Good logging in API/dashboard modules
### Weaknesses
- Silent exception suppression in `__init__.py` (lines 54-56, 122-123)
- Inconsistent use of print() vs logging module
- No custom exception classes
- Encryption failures silently return None
**Score: 6.5/10**
---
## 6. Architecture Assessment
### Strengths
- Clean modular structure (core, db, api, dashboard, packs)
- Separation of concerns between components
- Decorator-based registration pattern
- Versioning system for functions
- Trigger-based automation capability
### Concerns
- Global singleton pattern for Functionz instance
- Tight coupling between execution engine and database
- Dynamic `exec()` of database code is inherently risky
- No sandboxing or isolation of function execution
---
## 7. Recommendations for Production Readiness
### Must Fix Before Any Use
1. **Remove exec() or add sandboxing** - Consider using RestrictedPython or containerized execution
2. **Fix SQL injection** - Use parameterized queries exclusively
3. **Stop logging encryption key** - Remove the print statement immediately
4. **Add scope-based secret injection** - Only inject secrets required by each function
5. **Add authentication** - Protect API and dashboard endpoints
6. **Add test suite** - Minimum 80% coverage on core components
### Should Fix
6. **Consolidate dependency management** - Pick one system (recommend Poetry)
7. **Pin all dependencies** - Especially security-critical packages
8. **Replace print() with logging** - Consistent logging configuration
9. **Add custom exceptions** - Improve error semantics
10. **Add input validation** - Type and value validation on all inputs
### Nice to Have
11. **Add API documentation** - OpenAPI/Swagger specification
12. **Set up CI/CD** - Automated testing and security scanning
13. **Add execution timeouts** - Prevent infinite loops
14. **Add rate limiting** - Prevent abuse
15. **Document architecture** - Help contributors understand the system
---
## 8. Conclusion
**BabyAGI is an interesting experimental project that demonstrates innovative ideas about self-building autonomous agents.** However, it has critical security vulnerabilities, no tests, and dependency management issues that make it unsuitable for any production use or recommendation to others.
### Who Should Use This?
- **Researchers** exploring self-building agent concepts
- **Experienced developers** who can identify and work around the issues
- **Contributors** who want to help improve the framework
### Who Should NOT Use This?
- Anyone building production systems
- Developers who need reliable, tested code
- Projects that require security compliance
- Teams without security expertise to mitigate the risks
### Bottom Line
The author is transparent about the experimental nature of this project. **Respect that warning.** If you want to experiment with the concepts, understand that you're working with early-stage research code that has significant issues. If you need a production-ready agent framework, look elsewhere or contribute to making BabyAGI production-ready.
---
## Appendix: Files Reviewed
### Core Framework
- `babyagi/__init__.py` (140 lines)
- `babyagi/functionz/core/framework.py` (149 lines)
- `babyagi/functionz/core/execution.py` (254 lines)
- `babyagi/functionz/core/registration.py` (266 lines)
### Database Layer
- `babyagi/functionz/db/base_db.py` (62 lines)
- `babyagi/functionz/db/local_db.py` (259 lines)
- `babyagi/functionz/db/db_router.py` (301 lines)
- `babyagi/functionz/db/models.py` (~130 lines)
### API/Dashboard
- `babyagi/api/__init__.py` (158 lines)
- `babyagi/dashboard/__init__.py` (132 lines)
### Function Packs
- `babyagi/functionz/packs/default/*.py`
- `babyagi/functionz/packs/drafts/*.py`
- `babyagi/functionz/packs/plugins/*.py`
### Configuration
- `requirements.txt`
- `pyproject.toml`
- `setup.py`
- `poetry.lock`
- `README.md`
================================================
FILE: MANIFEST.in
================================================
# Include the README and LICENSE
include README.md
include LICENSE
# Include all package directories and their contents
recursive-include babyagi *
# Include examples
recursive-include examples *
# Exclude specific sensitive or unwanted files
exclude encryption_key.json
exclude funztionz.db
# Exclude other unnecessary files
global-exclude *.pyc
global-exclude __pycache__/
# Exclude .git and .gitignore
global-exclude .git
global-exclude .gitignore
# Exclude .egg-info directory and files
global-exclude *.egg-info
global-exclude *.egg-info/*
================================================
FILE: README.md
================================================
# BabyAGI
> [!NOTE]
> The original BabyAGI from March 2023 introduced task planning as a method for developing autonomous agents. This project has been archived and moved to the [babyagi_archive](https://github.com/yoheinakajima/babyagi_archive) repo (September 2024 snapshot).
> [!CAUTION]
> This is a framework built by Yohei who has never held a job as a developer. The purpose of this repo is to share ideas and spark discussion and for experienced devs to play with. Not meant for production use. Use with cautioun.
---
This newest BabyAGI is an experimental framework for a self-building autonomous agent. Earlier efforts to expand BabyAGI have made it clear that the optimal way to build a general autonomous agent is to build the simplest thing that can build itself.
Check out [this introductory X/Twitter thread](https://x.com/yoheinakajima/status/1840678823681282228) for a simple overview.
The core is a new function framework (**functionz**) for storing, managing, and executing functions from a database. It offers a graph-based structure for tracking imports, dependent functions, and authentication secrets, with automatic loading and comprehensive logging capabilities. Additionally, it comes with a dashboard for managing functions, running updates, and viewing logs.
## Table of Contents
- [Quick Start](#quick-start)
- [Basic Usage](#basic-usage)
- [Function Metadata](#function-metadata)
- [Function Loading](#function-loading)
- [Key Dependencies](#key-dependencies)
- [Execution Environment](#execution-environment)
- [Log](#log)
- [Dashboard](#dashboard)
- [Pre-loaded Functions](#pre-loaded-functions)
- [Future/Draft Features](#futuredraft-features)
- [API Reference](#api-reference)
- [Contributing](#contributing)
- [License](#license)
## Quick Start
To quickly check out the dashboard and see how it works:
1. **Install BabyAGI:**
```bash
pip install babyagi
```
2. **Import BabyAGI and load the dashboard:**
```python
import babyagi
if __name__ == "__main__":
app = babyagi.create_app('/dashboard')
app.run(host='0.0.0.0', port=8080)
```
3. **Navigate to the dashboard:**
Open your browser and go to `http://localhost:8080/dashboard` to access the BabyAGI dashboard.
## Basic Usage
Start by importing `babyagi` and registering your functions. Here's how to register two functions, where one depends on the other:
```python
import babyagi
# Register a simple function
@babyagi.register_function()
def world():
return "world"
# Register a function that depends on 'world'
@babyagi.register_function(dependencies=["world"])
def hello_world():
x = world()
return f"Hello {x}!"
# Execute the function
print(babyagi.hello_world()) # Output: Hello world!
if __name__ == "__main__":
app = babyagi.create_app('/dashboard')
app.run(host='0.0.0.0', port=8080)
```
## Function Metadata
Functions can be registered with metadata to enhance their capabilities and manage their relationships. Here's a more comprehensive example of function metadata, showing logical usage of all fields:
```python
import babyagi
@babyagi.register_function(
imports=["math"],
dependencies=["circle_area"],
key_dependencies=["openai_api_key"],
metadata={
"description": "Calculates the volume of a cylinder using the circle_area function."
}
)
def cylinder_volume(radius, height):
import math
area = circle_area(radius)
return area * height
```
**Available Metadata Fields:**
- `imports`: List of external libraries the function depends on.
- `dependencies`: List of other functions this function depends on.
- `key_dependencies`: List of secret keys required by the function.
- `metadata["description"]`: A description of what the function does.
## Function Loading
In addition to using `register_function`, you can use `load_function` to load plugins or draft packs of functions. BabyAGI comes with built-in function packs, or you can load your own packs by pointing to the file path.
You can find available function packs in `babyagi/functionz/packs`.
**Loading Custom Function Packs:**
```python
import babyagi
# Load your custom function pack
babyagi.load_functions("path/to/your/custom_functions.py")
```
This approach makes function building and management easier by organizing related functions into packs.
## Key Dependencies
You can store `key_dependencies` directly from your code or manage them via the dashboard.
**Storing Key Dependencies from Code:**
```python
import babyagi
# Add a secret key
babyagi.add_key_wrapper('openai_api_key', 'your_openai_api_key')
```
**Adding Key Dependencies via Dashboard:**
Navigate to the dashboard and use the **add_key_wrapper** feature to securely add your secret keys.
## Execution Environment
BabyAGI automatically loads essential function packs and manages their dependencies, ensuring a seamless execution environment. Additionally, it logs all activities, including the relationships between functions, to provide comprehensive tracking of function executions and dependencies.
### Log
BabyAGI implements a comprehensive logging system to track all function executions and their interactions. The logging mechanism ensures that every function call, including its inputs, outputs, execution time, and any errors, is recorded for monitoring and debugging purposes.
**Key Logging Features:**
- **Execution Tracking:** Logs when a function starts and finishes execution, including the function name, arguments, keyword arguments, and execution time.
- **Error Logging:** Captures and logs any errors that occur during function execution, providing detailed error messages for troubleshooting.
- **Dependency Management:** Automatically resolves and logs dependencies between functions, ensuring that all required functions and libraries are loaded before execution.
- **Trigger Logging:** Logs the execution of triggered functions, detailing which functions were triggered by others and their respective execution outcomes.
- **Comprehensive Records:** Maintains a history of all function executions, enabling users to review past activities, understand function relationships, and analyze performance metrics.
**How Triggers Work:**
Triggers are mechanisms that allow certain functions to be automatically executed in response to specific events or actions within the system. For example, when a function is added or updated, a trigger can initiate the generation of a description for that function.
Triggers enhance the autonomy of BabyAGI by enabling automated workflows and reducing the need for manual intervention. However, it's essential to manage triggers carefully to avoid unintended recursive executions or conflicts between dependent functions.
## Dashboard
The BabyAGI dashboard offers a user-friendly interface for managing functions, monitoring executions, and handling configurations. Key features include:
- **Function Management:** Register, deregister, and update functions directly from the dashboard.
- **Dependency Visualization:** View and manage dependencies between functions to understand their relationships.
- **Secret Key Management:** Add and manage secret keys securely through the dashboard interface.
- **Logging and Monitoring:** Access comprehensive logs of function executions, including inputs, outputs, and execution times.
- **Trigger Management:** Set up triggers to automate function executions based on specific events or conditions.
**Accessing the Dashboard:**
After running your application, navigate to `http://localhost:8080/dashboard` to access the BabyAGI dashboard.
## Pre-loaded Functions Summary
BabyAGI includes two pre-loaded function packs:
1. **Default Functions (`packs/default_functions.py`):**
- **Function Execution:** Run, add, update, or retrieve functions and versions.
- **Key Management:** Add and retrieve secret keys.
- **Triggers:** Add triggers to execute functions based on others.
- **Logs:** Retrieve logs with optional filters.
2. **AI Functions (`packs/ai_generator.py`):**
- **AI Description & Embeddings:** Auto-generate descriptions and embeddings for functions.
- **Function Selection:** Find or choose similar functions based on prompts.
## Running a Self-Building Agent
BabyAGI includes two experimental self-building agents, showcasing how the framework can help a self-building coding agent leverage existing functions to write new ones.
### 1. `process_user_input` in the `code_writing_functions` pack
This function first determines whether to use an existing function or generate new ones. If new functions are needed, it breaks them down into smaller reusable components and combines them into a final function.
Try this:
~~~python
import babyagi
babyagi.add_key_wrapper('openai_api_key', os.environ['OPENAI_API_KEY'])
babyagi.load_functions("drafts/code_writing_functions")
babyagi.process_user_input("Grab today's score from ESPN and email it to test@test.com")
~~~
When you run this, you will see the functions being generated in the shell and new functions will be available in the dashboard once completed.
### 2. `self_build` in the `self_build` pack
This function takes a user description and generates X distinct tasks that a user might ask an AI assistant. Each task is processed by `process_user_input`, creating new functions if no existing ones suffice.
Try this:
~~~python
import babyagi
babyagi.add_key_wrapper('openai_api_key', os.environ['OPENAI_API_KEY'])
babyagi.load_functions("drafts/code_writing_functions")
babyagi.load_functions("drafts/self_build")
babyagi.self_build("A sales person at an enterprise SaaS company.", 3)
~~~
This will generate 3 distinct tasks a salesperson might ask an AI assistant and create functions to handle those.
*The functions will be generated and stored in the dashboard, but note that the generated code is minimal and may need improvement.

**Warning:** These draft features are experimental concepts and may not function as intended. They require significant improvements and should be used with caution.
## Contributing
Contributions are greatly appreciatedly, but candidly I have not been great at managing PRs. Please be patient as things will move slow while I am working on this alone (on nights and weekends). I may start by building a small core crew before collaborating with a larger group.
If you are a dev, investor, friend of open-source and interesting supporting AI work I do, please fill [this form](https://forms.gle/UZLyT75HQULr8XNUA) (I have a few fun initiatives coming up!)
## License
BabyAGI is released under the MIT License. See the [LICENSE](LICENSE) file for more details.
================================================
FILE: babyagi/__init__.py
================================================
# babyagi/__init__.py
from flask import Flask, g
from .functionz.core.framework import Functionz
from .dashboard import create_dashboard
from .api import create_api_blueprint
import os
import importlib.util
import traceback
import sys
# Singleton instance of the functionz framework
_func_instance = Functionz()
def get_func_instance():
return _func_instance
def create_app(dashboard_route='/dashboard'):
app = Flask(__name__)
# Remove leading slash if present to avoid double slashes
if dashboard_route.startswith('/'):
dashboard_route = dashboard_route[1:]
# Create and register the dashboard blueprint with dashboard_route
dashboard_blueprint = create_dashboard(_func_instance, dashboard_route)
# Create and register the API blueprint
api_blueprint = create_api_blueprint()
# Register the blueprints
app.register_blueprint(dashboard_blueprint, url_prefix=f'/{dashboard_route}')
app.register_blueprint(api_blueprint) # Mounted at '/api' as defined in the blueprint
# Store the dashboard route for use in templates
app.config['DASHBOARD_ROUTE'] = dashboard_route
# Ensure the Functionz instance is accessible in the request context
@app.before_request
def set_functionz():
g.functionz = _func_instance
g.dashboard_route = dashboard_route # Optional, if needed globally
return app
# Function to register functions using the babyagi framework
def register_function(*args, **kwargs):
def wrapper(func):
try:
_func_instance.register_function(*args, **kwargs)(func)
setattr(sys.modules[__name__], func.__name__, func)
#print(f"Function '{func.__name__}' registered successfully.")
except Exception as e:
print(f"Error registering function '{func.__name__}': {e}")
traceback.print_exc()
return func
return wrapper
# Function to load additional function packs
def load_functions(pack_name_or_path):
#print(f"Attempting to load function pack: {pack_name_or_path}")
if os.path.exists(pack_name_or_path):
try:
spec = importlib.util.spec_from_file_location("custom_pack", pack_name_or_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
#print(f"Custom pack loaded from {pack_name_or_path}")
except Exception as e:
#print(f"Failed to load custom pack from path '{pack_name_or_path}': {e}")
traceback.print_exc()
else:
try:
print(f"Assuming '{pack_name_or_path}' is an internal pack...")
_func_instance.load_function_pack(pack_name_or_path)
print(f"Internal pack '{pack_name_or_path}' loaded successfully.")
except Exception as e:
print(f"Failed to load internal pack '{pack_name_or_path}': {e}")
traceback.print_exc()
def use_blueprints(app, dashboard_route='/dashboard'):
"""
Registers the babyagi blueprints with the provided Flask app.
Args:
app (Flask): The Flask application instance.
dashboard_route (str): The route prefix for the dashboard.
"""
# Remove leading slash if present
if dashboard_route.startswith('/'):
dashboard_route = dashboard_route[1:]
# Create blueprints
dashboard_blueprint = create_dashboard(_func_instance, dashboard_route)
api_blueprint = create_api_blueprint()
# Register blueprints
app.register_blueprint(dashboard_blueprint, url_prefix=f'/{dashboard_route}')
app.register_blueprint(api_blueprint) # Mounted at '/api' as defined in the blueprint
# Store the dashboard route for use in templates
app.config['DASHBOARD_ROUTE'] = dashboard_route
# Ensure the Functionz instance is accessible in the request context
@app.before_request
def set_functionz():
g.functionz = _func_instance
g.dashboard_route = dashboard_route # Optional, if needed globally
def __getattr__(name):
"""
Dynamic attribute access for the babyagi module.
If a function with the given name exists in the database,
return a callable that executes the function via the executor.
"""
try:
if _func_instance.get_function(name):
# Return a callable that executes the function via the executor
return lambda *args, **kwargs: _func_instance.executor.execute(name, *args, **kwargs)
except Exception as e:
pass
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
# Auto-load default function packs when babyagi is imported
try:
print("Attempting to load default function packs...")
# Uncomment if needed
_func_instance.load_function_pack('default/default_functions')
_func_instance.load_function_pack('default/ai_functions')
_func_instance.load_function_pack('default/os')
_func_instance.load_function_pack('default/function_calling_chat')
except Exception as e:
print(f"Error loading default function packs: {e}")
traceback.print_exc()
print("babyagi/__init__.py loaded")
================================================
FILE: babyagi/api/__init__.py
================================================
# babyagi/api/__init__.py
from flask import Blueprint, jsonify, request, g
from datetime import datetime
from io import StringIO
import logging
import os
import sys
import importlib.util
logger = logging.getLogger(__name__)
def create_api_blueprint():
api = Blueprint('api', __name__, url_prefix='/api')
# Removed the before_request function since g.functionz is set in the main app
@api.route('/functions')
def get_functions():
logger.debug("Accessing /api/functions route.")
try:
functions = g.functionz.get_all_functions()
logger.debug(f"Retrieved {len(functions)} functions.")
return jsonify(functions)
except Exception as e:
logger.error(f"Error in get_functions: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/function/<function_name>')
def get_function(function_name):
logger.debug(f"Accessing /api/function/{function_name} route.")
try:
function = g.functionz.db.get_function(function_name)
if not function:
logger.warning(f"Function '{function_name}' not found.")
return jsonify({"error": f"Function '{function_name}' not found."}), 404
return jsonify(function)
except Exception as e:
logger.error(f"Error getting function {function_name}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/function/<function_name>', methods=['PUT'])
def update_function(function_name):
logger.debug(f"Accessing /api/function/{function_name} [PUT] route.")
try:
data = request.get_json()
if not data or 'code' not in data:
logger.warning("No 'code' provided in request data.")
return jsonify({"error": "No 'code' provided in request data."}), 400
g.functionz.update_function(function_name, code=data['code'])
logger.info(f"Function '{function_name}' updated successfully.")
return jsonify({"status": "success"})
except Exception as e:
logger.error(f"Error updating function {function_name}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/execute/<function_name>', methods=['POST'])
def execute_function(function_name):
logger.debug(f"Accessing /api/execute/{function_name} [POST] route.")
try:
params = request.get_json() or {}
if function_name == 'execute_function_wrapper':
# Special handling for execute_function_wrapper
inner_function_name = params.pop('function_name', None)
args = params.pop('args', [])
kwargs = params.pop('kwargs', {})
result = g.functionz.executor.execute(function_name, inner_function_name, *args, **kwargs)
else:
# Normal execution for other functions
result = g.functionz.executor.execute(function_name, **params)
logger.info(f"Function '{function_name}' executed successfully.")
return jsonify(result)
except Exception as e:
logger.error(f"Error executing function {function_name}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/function/<function_name>/versions')
def get_function_versions(function_name):
logger.debug(f"Accessing /api/function/{function_name}/versions route.")
try:
versions = g.functionz.get_function_versions(function_name)
logger.debug(f"Retrieved {len(versions)} versions for function '{function_name}'.")
return jsonify(versions)
except Exception as e:
logger.error(f"Error getting versions for function {function_name}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/function/<function_name>/activate/<version>', methods=['POST'])
def activate_function_version(function_name, version):
logger.debug(f"Accessing /api/function/{function_name}/activate/{version} [POST] route.")
try:
g.functionz.activate_function_version(function_name, int(version))
logger.info(f"Version {version} of function '{function_name}' activated successfully.")
return jsonify({"status": "success"})
except ValueError:
logger.warning(f"Invalid version number provided: {version}")
return jsonify({"error": "Invalid version number."}), 400
except Exception as e:
logger.error(f"Error activating version {version} for function {function_name}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/logs/<function_name>')
@api.route('/logs', defaults={'function_name': None})
def get_logs(function_name):
logger.debug(f"Accessing /api/logs/{function_name if function_name else 'all'} route.")
try:
start_date_str = request.args.get('start_date')
end_date_str = request.args.get('end_date')
triggered_by_log_id_str = request.args.get('triggered_by_log_id') # New filter
start_date = datetime.fromisoformat(start_date_str) if start_date_str else None
end_date = datetime.fromisoformat(end_date_str) if end_date_str else None
triggered_by_log_id = int(triggered_by_log_id_str) if triggered_by_log_id_str else None # Convert to int if provided
logs = g.functionz.db.get_logs(function_name, start_date, end_date, triggered_by_log_id)
if function_name:
logger.debug(f"Retrieved {len(logs)} logs for function '{function_name}'.")
else:
logger.debug(f"Retrieved {len(logs)} logs for all functions.")
return jsonify(logs)
except ValueError:
logger.warning("Invalid date format or triggered_by_log_id provided.")
return jsonify({"error": "Invalid date format or triggered_by_log_id. Use ISO format for dates and integer for triggered_by_log_id."}), 400
except Exception as e:
logger.error(f"Error getting logs for function '{function_name}': {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/log_bundle/<int:log_id>')
def get_log_bundle(log_id):
logger.debug(f"Accessing /api/log_bundle/{log_id} route.")
try:
logs = g.functionz.db.get_log_bundle(log_id)
return jsonify({'logs': logs})
except Exception as e:
logger.error(f"Error getting log bundle for log_id '{log_id}': {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
@api.route('/triggers/<function_name>', methods=['GET'])
def get_triggers(function_name):
logger.debug(f"Accessing /api/triggers/{function_name} [GET] route.")
try:
triggers = g.functionz.get_triggers_for_function(function_name)
trigger_list = [
getattr(trigger.triggering_function, 'name', 'any function')
for trigger in triggers
]
logger.debug(f"Retrieved {len(trigger_list)} triggers for function '{function_name}'.")
return jsonify(trigger_list)
except Exception as e:
logger.error(f"Error getting triggers for function {function_name}: {str(e)}", exc_info=True)
return jsonify({"error": str(e)}), 500
logger.info("API blueprint created successfully.")
return api
================================================
FILE: babyagi/dashboard/__init__.py
================================================
# babyagi/dashboard/__init__.py
from flask import Blueprint, render_template, g, send_from_directory
import logging
import os
logger = logging.getLogger(__name__)
def create_dashboard(func_instance, dashboard_route):
if func_instance is None:
raise ValueError("func_instance cannot be None")
if dashboard_route is None:
raise ValueError("dashboard_route cannot be None")
dashboard = Blueprint('dashboard', __name__,
template_folder='templates',
static_folder='static',
static_url_path='/dashboard/static')
logger.info("Creating dashboard blueprint...")
@dashboard.before_request
def before_request():
"""Set up the necessary context before each request."""
g.functionz = func_instance
g.dashboard_route = dashboard_route
logger.debug("Set g.functionz and g.dashboard_route for the request context.")
@dashboard.route('/')
def dashboard_home():
logger.info("Accessing dashboard home page.")
try:
logger.debug(f"Dashboard Route: {g.dashboard_route}")
return render_template('index.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in dashboard_home: {str(e)}", exc_info=True)
return f"Error loading dashboard: {str(e)}", 500
@dashboard.route('/function/<function_name>')
def function_detail(function_name):
logger.info(f"Accessing function detail for: {function_name}")
try:
function = g.functionz.db.get_function(function_name)
if not function:
logger.warning(f"Function '{function_name}' not found.")
return f"Function '{function_name}' not found.", 404
return render_template('function_details.html', function_name=function_name, dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in function_detail: {str(e)}", exc_info=True)
return f"Error loading function detail: {str(e)}", 500
@dashboard.route('/graph')
def function_graph():
logger.info("Accessing function relationship graph page.")
try:
return render_template('function_graph.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in function_graph: {str(e)}", exc_info=True)
return f"Error loading function graph: {str(e)}", 500
@dashboard.route('/mermaid')
def function_graph_mermaid():
logger.info("Accessing mermaid function relationship graph page.")
try:
return render_template('function_graph_mermaid.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in function_graph_mermaid: {str(e)}", exc_info=True)
return f"Error loading mermaid function graph: {str(e)}", 500
@dashboard.route('/3d')
def function_graph_3d():
logger.info("Accessing 3D function relationship graph page.")
try:
return render_template('function_graph_3d.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in function_graph_3d: {str(e)}", exc_info=True)
return f"Error loading 3D function graph: {str(e)}", 500
@dashboard.route('/logs')
def logs_dashboard():
logger.info("Accessing logs dashboard.")
try:
return render_template('logs_dashboard.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in logs_dashboard: {str(e)}", exc_info=True)
return f"Error loading logs dashboard: {str(e)}", 500
@dashboard.route('/log/<int:log_id>')
def log_page(log_id):
logger.info(f"Accessing log page for Log ID {log_id}.")
try:
return render_template(
'log_page.html',
log_id=log_id,
dashboard_route=g.dashboard_route # Pass the dashboard route if needed
)
except Exception as e:
logger.error(f"Error in log_page for Log ID {log_id}: {str(e)}", exc_info=True)
return f"Error loading log page for Log ID {log_id}: {str(e)}", 500
@dashboard.route('/log_graph')
def log_relationship_graph():
logger.info("Accessing log relationship graph.")
try:
return render_template('log_relationship_graph.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in log_relationship_graph: {str(e)}", exc_info=True)
return f"Error loading log relationship graph: {str(e)}", 500
@dashboard.route('/chat')
def chat_page():
logger.info("Accessing chat page.")
try:
return render_template('chat.html', dashboard_route=g.dashboard_route)
except Exception as e:
logger.error(f"Error in chat_page: {str(e)}", exc_info=True)
return f"Error loading chat page: {str(e)}", 500
@dashboard.route('/<path:filename>')
def serve_static_files(filename):
"""Serve static files from the dashboard's static folder."""
logger.debug(f"Serving static file: {filename}")
try:
return send_from_directory(dashboard.static_folder, filename)
except Exception as e:
logger.error(f"Error serving static file '{filename}': {str(e)}", exc_info=True)
return "File not found.", 404
logger.info("Dashboard blueprint created successfully.")
return dashboard
logger.info("Dashboard __init__.py loaded successfully.")
================================================
FILE: babyagi/dashboard/static/css/style.css
================================================
/* static/css/style.css */
/* Global Variables */
:root {
/* Color variables */
--primary-color: #3498db;
--secondary-color: #2c3e50;
--background-color: #f0f4f8;
--text-color: #333333;
--border-color: #e0e0e0;
--hover-color: #f8f9fa;
--card-bg-color: #ffffff;
--bg-color: #f0f4f8;
--text-color: #333;
--accent-color: #3498db;
--hover-color: #f7fbfc;
--card-bg: #ffffff;
}
/* Global Styles */
body, html {
margin: 0;
padding: 0;
font-family: 'Inter', Arial, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
font-size: 12px;
line-height: 1.4;
}
.container {
width: 100%;
max-width: 100%;
padding: 20px;
margin: 0 auto;
box-sizing: border-box;
}
.details-container {
width: 100%;
max-width: 1600px;
padding: 20px;
margin: 0 auto;
box-sizing: border-box;
}
/* Hide elements with 'mobile-only' class on screens wider than 768px */
@media (min-width: 768px) {
.mobile-only {
display: none;
}
}
/* Hide elements with 'desktop-only' class on screens narrower than 768px */
@media (max-width: 767px) {
.desktop-only {
display: none;
}
}
/* Headers */
h1 {
color: var(--secondary-color);
font-size: 1.5em;
font-weight: 600;
margin-bottom: 20px;
text-align: left;
}
h1 small {
font-size: 0.7em;
font-weight: normal;
margin-left: 10px;
}
h1 small a {
color: var(--primary-color);
text-decoration: none;
}
/* Navigation Menu Styles */
nav {
background-color: var(--secondary-color);
padding: 10px 0;
margin-bottom: 20px;
}
nav ul {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: left;
flex-wrap: wrap;
}
nav li {
margin: 0 15px;
}
nav a {
color: #ffffff;
text-decoration: none;
font-weight: 500;
font-size: 14px;
}
nav a:hover {
text-decoration: underline;
color: var(--primary-color);
}
/* Breadcrumb Styles */
.breadcrumb {
margin-bottom: 20px;
}
.breadcrumb a {
color: var(--primary-color);
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
/* Search Bar Styles */
#searchInput {
width: 100%;
max-width: 1200px; /* Match the container's max-width */
padding: 10px;
margin-bottom: 20px;
font-size: 14px;
border: 1px solid var(--border-color);
border-radius: 4px;
box-sizing: border-box;
}
/* Table Styles */
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
background-color: var(--card-bg-color);
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
table-layout: fixed;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--border-color);
vertical-align: top;
}
td {
word-wrap: break-word;
overflow-wrap: break-word;
}
th {
background-color: var(--secondary-color);
color: white;
font-weight: 500;
white-space: nowrap;
cursor: pointer;
}
th.sortable:hover {
background-color: var(--primary-color);
}
tr:hover {
background-color: var(--hover-color);
}
.function-name {
color: var(--primary-color);
font-weight: 500;
text-decoration: none;
}
.function-name:hover {
text-decoration: underline;
}
.function-link, .function-name {
color: var(--primary-color);
font-weight: 500;
text-decoration: none;
}
.function-link:hover, .function-name:hover {
text-decoration: underline;
}
.small-text {
font-size: 0.92em;
color: #666;
}
.params-list, .dependencies-list, .imports-list, .triggers-list {
list-style-type: none;
padding: 0;
margin: 0;
}
.params-list li, .dependencies-list li, .imports-list, .triggers-list li {
margin-bottom: 2px;
}
/* Card Styles (for mobile view) */
.function-grid {
display: none;
grid-template-columns: 1fr;
gap: 15px;
padding: 10px;
}
.function-card {
background-color: var(--background-color);
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 15px;
}
.function-meta {
color: #666;
margin-bottom: 10px;
}
.function-description {
margin-bottom: 10px;
}
.params-title {
font-weight: 500;
margin-top: 8px;
margin-bottom: 3px;
}
.log-info {
display: flex;
justify-content: space-between;
color: #666;
border-top: 1px solid var(--border-color);
padding-top: 8px;
margin-top: 8px;
}
/* Function Detail Page Styles */
.function-detail {
background-color: var(--card-bg-color);
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.section-title {
font-size: 1.2em;
font-weight: bold;
margin-top: 20px;
margin-bottom: 10px;
color: var(--primary-color);
}
.button {
background-color: var(--primary-color);
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
}
.button:hover {
background-color: var(--hover-color);
}
#functionLogs {
max-height:600px;
overflow-y:scroll;
}
.log-entry, .execution-result {
background-color: #f8f9fa;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
line-height: 1.4;
overflow-x: auto;
}
.param-input {
margin-bottom: 10px;
}
.param-input label {
display: block;
margin-bottom: 5px;
}
.param-input input, .param-input textarea {
width: 100%;
padding: 5px;
border: 1px solid var(--border-color);
border-radius: 4px;
}
.breadcrumb {
margin-bottom: 20px;
}
.breadcrumb a {
color: var(--primary-color);
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
.two-column {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.column {
flex: 1;
min-width: 300px;
}
.detail-item {
background-color: #f8f9fa;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
line-height: 1.4;
overflow-x: auto;
}
.detail-label {
display: inline-block;
min-width: 120px;
font-weight: bold;
color: var(--primary-color);
margin-right: 10px;
}
.detail-value {
display: inline-block;
}
.detail-list {
margin: 5px 0 5px 130px;
padding-left: 20px;
}
.CodeMirror {
font-size: 12px;
border: 1px solid var(--border-color);
border-radius: 4px;
margin-bottom: 5px;
}
/* Version History */
.version-item {
padding: 10px;
border-bottom: 1px solid #ccc;
background-color: #f8f9fa;
margin-bottom: 5px;
word-wrap: break-word;
white-space: pre-wrap;
max-height:600px;
}
.version-item.active {
background-color: #e0f7fa;
}
.version-item pre {
background-color: #f1f1f1;
padding: 10px;
border-radius: 4px;
font-size: 12px;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
line-height: 1.4;
}
#toggleVersionHistory {
margin-top: 10px;
}
#versionHistory {
margin-top: 10px;
}
/* Media Queries */
@media (max-width: 768px) {
/* Hide the table in mobile, show the card grid */
table {
display: none;
}
.function-grid {
display: grid;
}
.two-column {
flex-direction: column;
}
.container, #searchInput, .function-detail, .column, input, body {
padding: 10px;
max-width: 100%;
}
.param-input{
margin:10px;
}
}
================================================
FILE: babyagi/dashboard/static/js/dashboard.js
================================================
/* static/js/dashboard.js */
// Assume that dashboardRoute, apiFunctionsUrl, and apiLogsUrl are defined in the HTML template
let currentSort = { key: null, direction: 'asc' };
let allFunctions = []; // This will hold the functions data globally
function filterTable() {
const input = document.getElementById("searchInput").value.toLowerCase();
const table = document.getElementById("functionTable");
const rows = table.getElementsByTagName("tr");
const grid = document.getElementById("functionGrid");
const cards = grid.getElementsByClassName("function-card");
for (let i = 1; i < rows.length; i++) { // Start at 1 to skip header row
let match = false;
const cells = rows[i].getElementsByTagName("td");
for (let j = 0; j < cells.length; j++) {
if (cells[j].innerText.toLowerCase().includes(input)) {
match = true;
break;
}
}
rows[i].style.display = match ? "" : "none";
}
// Filter cards for mobile view
for (let i = 0; i < cards.length; i++) {
if (cards[i].innerText.toLowerCase().includes(input)) {
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
function sortTable(key) {
const direction = currentSort.key === key && currentSort.direction === 'asc' ? 'desc' : 'asc';
currentSort = { key, direction };
const sortedFunctions = [...allFunctions].sort((a, b) => {
let valA, valB;
if (key === 'name') {
valA = a.name.toLowerCase();
valB = b.name.toLowerCase();
} else if (key === 'created_date') {
valA = new Date(a.created_date);
valB = new Date(b.created_date);
} else if (key === 'total_logs') {
valA = a.total_logs || 0;
valB = b.total_logs || 0;
} else if (key === 'last_log_date') {
valA = new Date(a.last_log_date || 0);
valB = new Date(b.last_log_date || 0);
}
if (direction === 'asc') return valA > valB ? 1 : -1;
return valA < valB ? 1 : -1;
});
populateDashboard(sortedFunctions);
}
async function fetchLogs(functionName) {
try {
const response = await fetch(`${apiLogsUrl}${encodeURIComponent(functionName)}`);
if (!response.ok) {
throw new Error('Failed to fetch logs');
}
const logs = await response.json();
return {
total_logs: logs.length,
last_log_date: logs.length > 0 ? logs[logs.length - 1].timestamp : null
};
} catch (error) {
console.error(`Error fetching logs for ${functionName}:`, error);
return { total_logs: 0, last_log_date: null };
}
}
function updateLogsAsync(tasks) {
const promises = tasks.map(([functionName, row]) => {
return fetchLogs(functionName).then(({ total_logs, last_log_date }) => {
const totalLogsCell = row.querySelector('.total-logs');
const lastLogDateCell = row.querySelector('.last-log-date');
totalLogsCell.textContent = total_logs;
lastLogDateCell.textContent = last_log_date ? new Date(last_log_date).toLocaleDateString() : 'N/A';
// Update function in allFunctions array
const func = allFunctions.find(f => f.name === functionName);
if (func) {
func.total_logs = total_logs;
func.last_log_date = last_log_date;
}
});
});
return Promise.all(promises);
}
async function populateDashboard(functions) {
allFunctions = functions; // Store functions globally for sorting
const tableBody = document.querySelector('#functionTable tbody');
const grid = document.getElementById('functionGrid');
const logTasks = [];
tableBody.innerHTML = '';
grid.innerHTML = '';
for (const func of functions) {
const row = document.createElement('tr');
const description = func.metadata && func.metadata.description ? func.metadata.description : 'No description available';
const createdDate = new Date(func.created_date).toLocaleDateString();
row.innerHTML = `
<td><a href="${dashboardRoute}/function/${encodeURIComponent(func.name)}" class="function-name">${func.name}</a></td>
<td class="small-text">v${func.version}</td>
<td>${description}</td>
<td>${formatParams(func.input_parameters)}</td>
<td>${formatParams(func.output_parameters)}</td>
<td>${formatList(func.dependencies, 'dependencies-list', dashboardRoute)}</td>
<td>${formatList(func.imports, 'imports-list')}</td>
<td>${formatList(func.triggers, 'triggers-list', dashboardRoute)}</td>
<td class="small-text">${createdDate}</td>
<td class="small-text total-logs">Loading...</td>
<td class="small-text last-log-date">Loading...</td>
`;
tableBody.appendChild(row);
// Populate card view (for mobile)
const card = document.createElement('div');
card.className = 'function-card';
card.innerHTML = `
<a href="${dashboardRoute}function/${encodeURIComponent(func.name)}" class="function-name">${func.name}</a>
<div class="function-meta">v${func.version} | Created: ${createdDate}</div>
<div class="function-description">${description}</div>
<div class="params-title">Input Parameters:</div>
${formatParams(func.input_parameters)}
<div class="params-title">Output Parameters:</div>
${formatParams(func.output_parameters)}
<div class="params-title">Dependencies:</div>
${formatList(func.dependencies, 'dependencies-list', dashboardRoute)}
<div class="params-title">Imports:</div>
${formatList(func.imports, 'imports-list')}
<div class="params-title">Triggers:</div>
${formatList(func.triggers, 'triggers-list', dashboardRoute)}
<div class="log-info">
<span>Total Logs: <span class="total-logs">Loading...</span></span>
<span>Last Log: <span class="last-log-date">Loading...</span></span>
</div>
`;
grid.appendChild(card);
logTasks.push([func.name, row]);
}
updateLogsAsync(logTasks);
}
function formatParams(params) {
if (!params || params.length === 0) {
return '<span class="small-text"></span>';
}
return '<ul class="params-list">' +
params.map(param => `<li>${param.name}: <span class="small-text">${param.type}</span></li>`).join('') +
'</ul>';
}
function formatList(items, className, dashboardRoute) {
if (!items || items.length === 0) {
return '<span class="small-text">-</span>';
}
return '<ul class="' + className + '">' +
items.map(item => {
if (className === 'dependencies-list' || className === 'triggers-list') {
return `<li><a href="${dashboardRoute}/function/${encodeURIComponent(item)}" class="function-link">${item}</a></li>`;
}
return `<li>${item}</li>`;
}).join('') +
'</ul>';
}
// Fetch functions data and populate the dashboard
async function fetchFunctionsAndPopulate() {
try {
const response = await fetch(apiFunctionsUrl);
if (!response.ok) {
throw new Error('Failed to fetch functions');
}
const data = await response.json();
populateDashboard(data);
} catch (error) {
console.error('Error fetching functions:', error);
document.querySelector('.container').innerHTML += `<p style="color: red;">Error loading functions. Please try refreshing the page.</p>`;
}
}
// Call the function when the page loads
document.addEventListener('DOMContentLoaded', () => {
fetchFunctionsAndPopulate();
// Add event listener for search input
const searchInput = document.getElementById("searchInput");
searchInput.addEventListener('input', filterTable);
});
================================================
FILE: babyagi/dashboard/static/js/function_details.js
================================================
// At the top of function_details.js
// Ensure apiRoutes is defined
window.apiRoutes = window.apiRoutes || {};
// Helper function to get the API route
function getApiRoute(routeName, ...args) {
if (typeof apiRoutes[routeName] === 'function') {
return apiRoutes[routeName](...args);
} else {
return apiRoutes[routeName];
}
}
window.getApiRoute = getApiRoute;
let functionData;
let codeEditor;
// Expose necessary functions to the global scope
window.loadFunctionDetails = loadFunctionDetails;
window.loadFunctionLogs = loadFunctionLogs;
window.initCodeEditor = initCodeEditor;
window.displayFunctionDetails = displayFunctionDetails;
window.createExecutionForm = createExecutionForm;
window.updateFunction = updateFunction;
window.executeFunction = executeFunction;
window.toggleVersionHistory = toggleVersionHistory;
window.loadFunctionVersions = loadFunctionVersions;
window.activateVersion = activateVersion;
function loadFunctionDetails() {
fetch(getApiRoute('getFunction'))
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
functionData = data;
console.log("functionData",functionData)
displayFunctionDetails();
createExecutionForm();
initCodeEditor();
})
.catch(error => {
console.error('Error:', error);
document.getElementById('functionDetails').innerHTML = `<p>Error loading function details: ${error.message}</p>`;
});
}
function loadFunctionLogs() {
fetch(getApiRoute('getLogs'))
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(logs => {
let logsHtml = logs.map(log => `
<div class="log-entry">
<strong>Timestamp:</strong> ${new Date(log.timestamp).toLocaleString()}<br>
<strong>Message:</strong> ${log.message}<br>
<strong>Params:</strong> ${JSON.stringify(log.params)}<br>
<strong>Output:</strong> ${JSON.stringify(log.output)}<br>
<strong>Time spent:</strong> ${log.time_spent} seconds
</div>
`).join('');
document.getElementById('functionLogs').innerHTML = logsHtml;
})
.catch(error => {
console.error('Error:', error);
document.getElementById('functionLogs').innerHTML = `<p>Error loading logs: ${error.message}</p>`;
});
}
function initCodeEditor() {
const editorElement = document.getElementById('codeEditor'); // Example: Get the editor element
// Check if the editor element exists
if (!editorElement) {
console.error("Code editor textarea not found!");
return;
}
// Destroy the previous CodeMirror instance if it exists
if (codeEditor) {
codeEditor.toTextArea(); // Converts the editor back into a textarea
codeEditor = null; // Clear the reference
}
// Initialize the new CodeMirror instance
codeEditor = CodeMirror.fromTextArea(editorElement, {
mode: "python",
theme: "monokai",
lineNumbers: true,
indentUnit: 4,
tabSize: 4,
indentWithTabs: false,
autofocus: true
});
console.log("initCodeEditor executing");
console.log("window.functionData.code", functionData.code);
console.log("window.functionData", functionData);
// Set the current function code (if available)
if (functionData && functionData.code) {
codeEditor.setValue(functionData.code);
} else {
codeEditor.setValue(""); // Clear editor if no code
}
// Refresh CodeMirror to fix display issues
setTimeout(() => {
codeEditor.refresh();
}, 200); // Short delay to ensure the editor is fully visible before refreshing
}
function displayFunctionDetails() {
document.getElementById('functionName').textContent = functionData.name;
let detailsHtml = `
<div class="detail-item">
<span class="detail-label">Version:</span>
<span class="detail-value">${functionData.version}</span>
</div>
<div class="detail-item">
<span class="detail-label">Created Date:</span>
<span class="detail-value">${new Date(functionData.created_date).toLocaleString()}</span>
</div>
<div class="detail-item">
<span class="detail-label">Description:</span>
<span class="detail-value">${functionData.metadata.description || 'No description available'}</span>
</div>
<div class="detail-item">
<span class="detail-label">Dependencies:</span>
<span class="detail-value">$
<span class="detail-value">
${functionData.dependencies.length ? functionData.dependencies.map(dep => `
<a href="${dashboardRoute}/function/${encodeURIComponent(dep)}" class="function-name">${dep}</a>
`).join(', ') : 'None'}
</span>
</span>
</div>
<div class="detail-item">
<span class="detail-label">Imports:</span>
<span class="detail-value">${functionData.imports ? functionData.imports.join(', ') : 'None'}</span>
</div>
<div class="detail-item">
<span class="detail-label">Triggers:</span>
<span class="detail-value">${functionData.triggers.join(', ') || 'None'}</span>
</div>
<div class="detail-item">
<span class="detail-label">Input Parameters:</span>
<ul class="detail-list">
${functionData.input_parameters.map(param => `<li>${param.name} (${param.type})</li>`).join('')}
</ul>
</div>
<div class="detail-item">
<span class="detail-label">Output Parameters:</span>
<ul class="detail-list">
${functionData.output_parameters.map(param => `<li>${param.name} (${param.type})</li>`).join('')}
</ul>
</div>
`;
document.getElementById('functionDetails').innerHTML = detailsHtml;
}
function createExecutionForm() {
let formHtml = '';
if (functionData.name === 'execute_function_wrapper') {
formHtml += `
<div class="param-input">
<label for="function_name">function_name (str):</label>
<input type="text" id="function_name" name="function_name">
</div>
<div class="param-input">
<label for="args">args (comma-separated values):</label>
<input type="text" id="args" name="args">
</div>
<div class="param-input">
<label for="kwargs">kwargs (JSON format):</label>
<textarea id="kwargs" name="kwargs" rows="5" cols="50"></textarea>
</div>
`;
} else {
functionData.input_parameters.forEach(param => {
if (param.type === 'json') {
formHtml += `
<div class="param-input">
<label for="${param.name}">${param.name} (${param.type}):</label>
<textarea id="${param.name}" name="${param.name}" rows="5" cols="50"></textarea>
</div>
`;
} else {
formHtml += `
<div class="param-input">
<label for="${param.name}">${param.name} (${param.type}):</label>
<input type="text" id="${param.name}" name="${param.name}">
</div>
`;
}
});
}
document.getElementById('executionForm').innerHTML = formHtml;
}
function updateFunction() {
const updatedCode = codeEditor.getValue();
fetch(getApiRoute('updateFunction'), {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
code: updatedCode,
}),
})
.then(response => response.json())
.then(data => {
alert('Function updated successfully');
functionData.code = updatedCode;
if (data.version) {
functionData.version = data.version;
}
displayFunctionDetails();
})
.catch((error) => {
console.error('Error:', error);
alert('Error updating function');
});
}
function executeFunction() {
let params = {};
if (functionData.name === 'execute_function_wrapper') {
const functionNameInput = document.getElementById('function_name').value.trim();
if (!functionNameInput) {
alert('Function name is required.');
return;
}
const args = document.getElementById('args').value ?
document.getElementById('args').value.split(',').map(arg => arg.trim()) : [];
let kwargs = {};
const kwargsValue = document.getElementById('kwargs').value.trim();
if (kwargsValue) {
try {
kwargs = JSON.parse(kwargsValue);
} catch (e) {
alert('Invalid JSON input for kwargs. Please check your input.');
return;
}
}
params = {
function_name: functionNameInput,
args: args,
kwargs: kwargs
};
} else {
for (const param of functionData.input_parameters) {
let value = document.getElementById(param.name).value.trim();
// Skip empty inputs
if (value === '') continue;
try {
switch (param.type) {
case 'int':
value = parseInt(value, 10);
if (isNaN(value)) throw new Error(`Invalid integer for ${param.name}`);
break;
case 'float':
value = parseFloat(value);
if (isNaN(value)) throw new Error(`Invalid float for ${param.name}`);
break;
case 'bool':
value = value.toLowerCase();
if (value !== 'true' && value !== 'false') throw new Error(`Invalid boolean for ${param.name}. Use 'true' or 'false'.`);
value = value === 'true';
break;
case 'date':
value = new Date(value);
if (isNaN(value.getTime())) throw new Error(`Invalid date for ${param.name}`);
break;
case 'list':
value = value.split(',').map(item => item.trim());
break;
case 'json':
value = JSON.parse(value);
break;
}
params[param.name] = value;
} catch (error) {
alert(`Error with parameter ${param.name}: ${error.message}`);
return;
}
}
}
fetch(getApiRoute('executeFunction'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
})
.then(response => {
if (!response.ok) {
return response.text().then(text => {
throw new Error(text || response.statusText);
});
}
return response.json();
})
.then(data => {
document.getElementById('executionResult').innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
})
.catch((error) => {
console.error('Error:', error);
document.getElementById('executionResult').innerHTML = `<pre class="error">Error executing function: ${error.message}</pre>`;
});
}
let isVersionHistoryVisible = false;
function toggleVersionHistory() {
const versionHistory = document.getElementById('versionHistory');
if (isVersionHistoryVisible) {
versionHistory.style.display = 'none';
document.getElementById('toggleVersionHistory').textContent = 'Show Versions';
} else {
loadFunctionVersions();
versionHistory.style.display = 'block';
document.getElementById('toggleVersionHistory').textContent = 'Hide Versions';
}
isVersionHistoryVisible = !isVersionHistoryVisible;
}
function loadFunctionVersions() {
fetch(getApiRoute('getFunctionVersions'))
.then(response => response.json())
.then(versions => {
const versionHistoryDiv = document.getElementById('versionHistory');
let versionHtml = versions.map(version => `
<div class="version-item ${version.is_active ? 'active' : ''}">
<strong>Version ${version.version}</strong> - Created: ${new Date(version.created_date).toLocaleString()}
<pre>${version.code}</pre>
<button class="button" onclick="activateVersion(${version.version})">Activate Version</button>
</div>
`).join('');
versionHistoryDiv.innerHTML = versionHtml;
})
.catch(error => console.error('Error loading versions:', error));
}
function activateVersion(version) {
const url = getApiRoute('activateVersion', version);
fetch(url, {
method: 'POST',
})
.then(response => response.json())
.then(data => {
alert('Version activated successfully');
loadFunctionDetails();
loadFunctionVersions();
})
.catch(error => console.error('Error activating version:', error));
}
window.addEventListener('load', function() {
loadFunctionDetails();
loadFunctionLogs();
});
================================================
FILE: babyagi/dashboard/static/js/function_graph.js
================================================
// function_graph.js
let cy;
document.addEventListener('DOMContentLoaded', () => {
cy = cytoscape({
container: document.getElementById('graph'),
style: [
{
selector: 'node',
style: {
'shape': 'rectangle',
'background-color': '#E6F2FF',
'label': 'data(id)',
'text-valign': 'center',
'text-halign': 'center',
'text-wrap': 'wrap',
'text-max-width': '150px',
'width': 'label',
'height': 'label',
'padding': '12px',
'font-weight': 'bold',
'font-size': '16px',
'color': '#333333',
'text-outline-color': '#FFFFFF',
'text-outline-width': 1,
'cursor': 'pointer'
}
},
{
selector: 'node[type="import"]',
style: {
'background-color': '#E6FFF2'
}
},
{
selector: 'node[type="trigger"]',
style: {
'background-color': '#FFE6E6'
}
},
{
selector: 'node:hover',
style: {
'background-opacity': 0.8
}
},
{
selector: 'edge',
style: {
'width': 1,
'line-color': '#999999',
'target-arrow-color': '#999999',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
'arrow-scale': 1.5
}
},
{
selector: 'edge[type="dependency"]',
style: {
'width': 2
}
},
{
selector: 'edge[type="trigger"]',
style: {
'line-style': 'dashed',
'label': 'trigger',
'font-size': '12px',
'text-rotation': 'autorotate',
'text-margin-y': -10
}
}
],
layout: {
name: 'cose',
idealEdgeLength: 50,
nodeOverlap: 20,
refresh: 20,
fit: true,
padding: 30,
randomize: false,
componentSpacing: 80,
nodeRepulsion: 450000,
edgeElasticity: 100,
nestingFactor: 5,
gravity: 80,
numIter: 1000,
initialTemp: 200,
coolingFactor: 0.95,
minTemp: 1.0
},
wheelSensitivity: 0.2
});
fetch('/api/functions')
.then(response => response.json())
.then(data => {
const elements = [];
const imports = new Set();
data.forEach(func => {
elements.push({ data: { id: func.name, type: 'function' } });
func.dependencies.forEach(dep => {
elements.push({ data: { id: dep, type: 'function' } });
elements.push({ data: { source: func.name, target: dep, type: 'dependency' } });
});
func.triggers.forEach(trigger => {
elements.push({ data: { id: trigger, type: 'trigger' } });
elements.push({ data: { source: func.name, target: trigger, type: 'trigger' } });
});
func.imports.forEach(imp => imports.add(imp));
});
imports.forEach(imp => {
elements.push({ data: { id: imp, type: 'import' } });
});
data.forEach(func => {
func.imports.forEach(imp => {
elements.push({ data: { source: func.name, target: imp, type: 'import' } });
});
});
cy.add(elements);
cy.layout({ name: 'cose' }).run();
cy.fit(40);
cy.zoom({
level: cy.zoom() * 1.3,
renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 }
});
});
cy.on('tap', 'node', function(evt) {
const node = evt.target;
if (node.data('type') === 'function' || node.data('type') === 'trigger') {
showFunctionOverlay(node.id());
}
});
cy.on('zoom pan', () => {
closeOverlay();
});
});
function showFunctionOverlay(functionName) {
const overlay = document.getElementById('overlay');
const content = document.getElementById('overlay-content');
fetch(`/api/function/${functionName}`)
.then(response => response.json())
.then(data => {
content.innerHTML = `
<h3>${data.name}</h3>
<div id="tab-buttons">
<button class="tab-button active" onclick="showTab('description', '${data.name}')">Description</button>
<button class="tab-button" onclick="showTab('code', '${data.name}')">Code</button>
<button class="tab-button" onclick="showTab('logs', '${data.name}')">Logs</button>
</div>
<div id="tab-content">
<div id="description-tab">
<p><strong>Description:</strong> ${data.metadata.description || 'No description available.'}</p>
<p><strong>Version:</strong> ${data.version}</p>
</div>
<div id="code-tab" style="display: none;"></div>
<div id="logs-tab" style="display: none;"></div>
</div>
`;
const node = cy.$id(functionName);
const renderedPosition = node.renderedPosition();
overlay.style.left = `${renderedPosition.x + 10}px`;
overlay.style.top = `${renderedPosition.y + 10}px`;
overlay.style.display = 'block';
});
}
function showTab(tabName, functionName) {
const tabs = ['description', 'code', 'logs'];
tabs.forEach(tab => {
const tabElement = document.getElementById(`${tab}-tab`);
const tabButton = document.querySelector(`button.tab-button:nth-child(${tabs.indexOf(tab) + 1})`);
if (tab === tabName) {
tabElement.style.display = 'block';
tabButton.classList.add('active');
} else {
tabElement.style.display = 'none';
tabButton.classList.remove('active');
}
});
if (tabName === 'code' && document.getElementById('code-tab').innerHTML === '') {
showCode(functionName);
} else if (tabName === 'logs' && document.getElementById('logs-tab').innerHTML === '') {
showLogs(functionName);
}
}
function showCode(functionName) {
fetch(`/api/function/${functionName}`)
.then(response => response.json())
.then(data => {
const codeTab = document.getElementById('code-tab');
codeTab.innerHTML = `
<h4>Code:</h4>
<pre><code>${data.code}</code></pre>
`;
});
}
function showLogs(functionName) {
fetch(`/api/logs/${functionName}`)
.then(response => response.json())
.then(data => {
const logsTab = document.getElementById('logs-tab');
logsTab.innerHTML = `
<h4>Logs:</h4>
<pre>${JSON.stringify(data, null, 2)}</pre>
`;
});
}
function closeOverlay() {
document.getElementById('overlay').style.display = 'none';
}
================================================
FILE: babyagi/dashboard/static/js/log_dashboard.js
================================================
let currentSort = { key: 'timestamp', direction: 'desc' }; // Set default sort to 'timestamp' descending
let allLogs = []; // Holds all fetched logs
let filteredLogs = []; // Holds logs after filtering
let rootLogs = []; // Holds the root logs after building the tree
// Fetch unique values for function names and log types to populate filter dropdowns
async function populateFilters() {
try {
const response = await fetch(apiLogsUrl);
if (!response.ok) {
throw new Error('Failed to fetch logs for filters');
}
let logs = await response.json();
// **Sort logs by timestamp descending (most recent first)**
logs.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
allLogs = logs;
filteredLogs = logs;
const functionFilter = document.getElementById('functionFilter');
const logTypeFilter = document.getElementById('logTypeFilter');
const uniqueFunctions = [...new Set(logs.map(log => log.function_name))].sort();
uniqueFunctions.forEach(func => {
const option = document.createElement('option');
option.value = func;
option.textContent = func;
functionFilter.appendChild(option);
});
const uniqueLogTypes = [...new Set(logs.map(log => log.log_type))].sort();
uniqueLogTypes.forEach(type => {
const option = document.createElement('option');
option.value = type;
option.textContent = capitalizeFirstLetter(type);
logTypeFilter.appendChild(option);
});
// Build the tree structure
rootLogs = buildLogTree(filteredLogs);
renderLogs();
} catch (error) {
console.error('Error populating filters:', error);
alert('Failed to load logs for filters. Please try again later.');
}
}
// Build log tree based on parent_log_id
function buildLogTree(logs) {
const logsById = {};
const rootLogs = [];
// Initialize logsById mapping and add children array to each log
logs.forEach(log => {
log.children = [];
logsById[log.id] = log;
});
// Build the tree
logs.forEach(log => {
if (log.parent_log_id !== null) {
const parentLog = logsById[log.parent_log_id];
if (parentLog) {
parentLog.children.push(log);
} else {
// Parent log not found, treat as root
rootLogs.push(log);
}
} else {
rootLogs.push(log);
}
});
return rootLogs;
}
// Render logs in table and grid formats
function renderLogs() {
renderTable();
renderGrid();
}
// Render Logs Table (Desktop View)
function renderTable() {
const tableBody = document.querySelector('#logTable tbody');
tableBody.innerHTML = '';
rootLogs.forEach(log => {
renderLogRow(tableBody, log, 0);
});
}
// Recursive function to render each log row and its children
function renderLogRow(tableBody, log, depth, parentRowId) {
const row = document.createElement('tr');
const rowId = 'log-' + log.id;
row.id = rowId;
// If it's a child row, add a class to indicate it's a child
if (parentRowId) {
row.classList.add('child-of-log-' + parentRowId);
row.style.display = 'none'; // Hide child rows by default
}
// Check if log has children
const hasChildren = log.children && log.children.length > 0;
// Create expand/collapse icon
let toggleIcon = '';
if (hasChildren) {
toggleIcon = `<span class="toggle-icon" data-log-id="${log.id}" style="cursor:pointer;">[+]</span> `;
}
row.innerHTML = `
<td><a href="${dashboardRoute}/log/${log.id}" class="function-link">${log.id}</a></td>
<td><a href="${dashboardRoute}/function/${encodeURIComponent(log.function_name)}" class="function-link">${log.function_name}</a></td>
<td style="padding-left:${depth * 20}px">${toggleIcon}${log.message}</td>
<td>${new Date(log.timestamp).toLocaleString()}</td>
<td>${capitalizeFirstLetter(log.log_type)}</td>
<td>${log.time_spent ? log.time_spent.toFixed(3) : 'N/A'}</td>
<td>${log.parent_log_id !== null ? log.parent_log_id : 'N/A'}</td>
<td>${log.triggered_by_log_id !== null ? log.triggered_by_log_id : 'N/A'}</td>
`;
tableBody.appendChild(row);
// Add event listener for toggle
if (hasChildren) {
row.querySelector('.toggle-icon').addEventListener('click', function() {
toggleChildRows(log.id);
// Update the icon
const icon = this;
if (icon.textContent === '[+]') {
icon.textContent = '[-]';
} else {
icon.textContent = '[+]';
}
});
}
// Recursively render children
if (hasChildren) {
log.children.forEach(childLog => {
renderLogRow(tableBody, childLog, depth + 1, log.id);
});
}
}
// Function to toggle child rows
function toggleChildRows(parentLogId) {
const childRows = document.querySelectorAll('.child-of-log-' + parentLogId);
childRows.forEach(row => {
if (row.style.display === 'none') {
row.style.display = '';
} else {
row.style.display = 'none';
// Recursively hide any child rows
const childLogId = row.id.replace('log-', '');
toggleChildRows(childLogId);
// Reset the toggle icon of child rows to '[+]'
const toggleIcon = row.querySelector('.toggle-icon');
if (toggleIcon) {
toggleIcon.textContent = '[+]';
}
}
});
}
// Render Logs Grid (Mobile View)
function renderGrid() {
const logGrid = document.getElementById('logGrid');
logGrid.innerHTML = '';
rootLogs.forEach(log => {
renderLogCard(logGrid, log, 0);
});
}
// Recursive function to render log cards and their children
function renderLogCard(container, log, depth) {
const card = document.createElement('div');
card.className = 'log-card';
card.style.marginLeft = (depth * 20) + 'px';
card.innerHTML = `
<h5>ID: <a href="${dashboardRoute}/log/${log.id}" class="function-link">${log.id}</a></h5>
<div class="log-meta">Function: <a href="${dashboardRoute}/function/${encodeURIComponent(log.function_name)}">${log.function_name}</a> | ${new Date(log.timestamp).toLocaleString()}</div>
<div class="log-details"><strong>Message:</strong> ${log.message}</div>
<div class="log-details"><strong>Log Type:</strong> ${capitalizeFirstLetter(log.log_type)}</div>
<div class="log-details"><strong>Time Spent:</strong> ${log.time_spent ? log.time_spent.toFixed(3) : 'N/A'} seconds</div>
<div class="log-details"><strong>Parent Log ID:</strong> ${log.parent_log_id !== null ? log.parent_log_id : 'N/A'}</div>
<div class="log-details"><strong>Triggered By Log ID:</strong> ${log.triggered_by_log_id !== null ? log.triggered_by_log_id : 'N/A'}</div>
`;
container.appendChild(card);
// Recursively render children
if (log.children && log.children.length > 0) {
log.children.forEach(childLog => {
renderLogCard(container, childLog, depth + 1);
});
}
}
// Capitalize the first letter of a string
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
// Sort logs based on a key
function sortLogs(key) {
if (currentSort.key === key) {
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
} else {
currentSort.key = key;
currentSort.direction = 'asc';
}
// Sort root logs
rootLogs.sort((a, b) => {
let valA = a[key];
let valB = b[key];
// Handle null or undefined values
if (valA === null || valA === undefined) valA = '';
if (valB === null || valB === undefined) valB = '';
// If sorting by timestamp, convert to Date
if (key === 'timestamp') {
valA = new Date(valA);
valB = new Date(valB);
}
// If sorting by time_spent or IDs, ensure numerical comparison
if (key === 'time_spent' || key === 'id' || key === 'parent_log_id' || key === 'triggered_by_log_id') {
valA = Number(valA);
valB = Number(valB);
}
if (valA > valB) return currentSort.direction === 'asc' ? 1 : -1;
if (valA < valB) return currentSort.direction === 'asc' ? -1 : 1;
return 0;
});
renderLogs();
}
// Apply Filters
function applyFilters() {
const functionFilter = document.getElementById('functionFilter').value.toLowerCase();
const logTypeFilter = document.getElementById('logTypeFilter').value.toLowerCase();
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
// First, filter the logs
filteredLogs = allLogs.filter(log => {
let matchesFunction = true;
let matchesLogType = true;
let matchesStartDate = true;
let matchesEndDate = true;
if (functionFilter) {
matchesFunction = log.function_name.toLowerCase().includes(functionFilter);
}
if (logTypeFilter) {
matchesLogType = log.log_type.toLowerCase() === logTypeFilter;
}
if (startDate) {
matchesStartDate = new Date(log.timestamp) >= new Date(startDate);
}
if (endDate) {
// Add one day to endDate to include the entire end day
const endDateObj = new Date(endDate);
endDateObj.setDate(endDateObj.getDate() + 1);
matchesEndDate = new Date(log.timestamp) < endDateObj;
}
return matchesFunction && matchesLogType && matchesStartDate && matchesEndDate;
});
// Rebuild the tree
rootLogs = buildLogTree(filteredLogs);
renderLogs();
}
// Reset Filters
function resetFilters() {
document.getElementById('functionFilter').value = '';
document.getElementById('logTypeFilter').value = '';
document.getElementById('startDate').value = '';
document.getElementById('endDate').value = '';
filteredLogs = allLogs;
// Rebuild the tree
rootLogs = buildLogTree(filteredLogs);
renderLogs();
}
// Initialize the logs dashboard
document.addEventListener('DOMContentLoaded', () => {
populateFilters();
});
================================================
FILE: babyagi/dashboard/static/js/log_graph.js
================================================
document.addEventListener('DOMContentLoaded', () => {
loadLogs();
});
async function loadLogs() {
try {
const response = await fetch(apiLogsUrl);
if (!response.ok) {
throw new Error('Failed to fetch logs');
}
const logs = await response.json();
renderGraph(logs);
} catch (error) {
console.error('Error loading logs:', error);
alert('Failed to load logs. Please try again later.');
}
}
function renderGraph(logs) {
// Process logs and build graph data
const elements = logs.map(log => ({
data: { id: log.id, label: log.function_name }
}));
// Add edges for relationships
logs.forEach(log => {
if (log.parent_log_id) {
elements.push({
data: {
id: `parent-${log.id}`,
source: log.parent_log_id,
target: log.id,
label: 'Parent'
}
});
}
if (log.triggered_by_log_id) {
elements.push({
data: {
id: `trigger-${log.id}`,
source: log.triggered_by_log_id,
target: log.id,
label: 'Triggered By'
}
});
}
});
// Initialize Cytoscape graph
var cy = cytoscape({
container: document.getElementById('graph'),
elements: elements,
style: [
{
selector: 'node',
style: {
'label': 'data(label)',
'background-color': '#666',
'text-valign': 'center',
'text-halign': 'center',
'color': '#fff',
'text-outline-width': 2,
'text-outline-color': '#666'
}
},
{
selector: 'edge',
style: {
'width': 2,
'line-color': '#ccc',
'target-arrow-color': '#ccc',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier'
}
}
],
layout: {
name: 'breadthfirst',
directed: true,
padding: 10
}
});
// Add event listeners if needed
}
================================================
FILE: babyagi/dashboard/templates/base.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}PythonFunc Dashboard{% endblock %}</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<!-- Include external CSS -->
<link rel="stylesheet" href="{{ url_for('dashboard.static', filename='css/style.css') }}">
{% endblock %}
</head>
<body>
<!-- Include the navigation menu outside the container for styling purposes -->
<nav>
<ul>
<li><a href="{{ url_for('dashboard.dashboard_home') }}">Home</a></li>
<li><a href="{{ url_for('dashboard.function_graph') }}">Graph</a></li>
<li><a href="{{ url_for('dashboard.function_graph_mermaid') }}">Mermaid Graph</a></li>
<li><a href="{{ url_for('dashboard.function_graph_3d') }}">3D Graph</a></li>
<li><a href="{{ url_for('dashboard.logs_dashboard') }}">Logs</a></li>
<li><a href="{{ url_for('dashboard.chat_page') }}">Chat</a></li>
<!--<li><a href="{{ url_for('dashboard.log_relationship_graph') }}">Log Graph</a></li>-->
</ul>
</nav>
<div class="container">
<!-- Breadcrumb -->
{% block breadcrumb %}{% endblock %}
<!-- Main content -->
{% block content %}{% endblock %}
</div>
{% block scripts %}{% endblock %}
</body>
</html>
================================================
FILE: babyagi/dashboard/templates/chat.html
================================================
{% extends "base.html" %}
{% block title %}Chat Application{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > Chat
</div>
{% endblock %}
{% block content %}
<div class="header-container">
<h1>Function-Enhanced Chat Application</h1>
<!-- Additional header content can be added here -->
</div>
<div class="main-content">
<!-- Function Selection Section -->
<div class="function-selection">
<h2>Select Available Functions</h2>
<input type="text" id="searchBar" placeholder="Search functions..." class="search-bar">
<div id="functionList" class="function-list">
<!-- Function items will be populated here -->
</div>
</div>
<!-- Chat Section -->
<div class="chat-container">
<!-- Available Functions Display -->
<div id="availableFunctionsContainer" class="available-functions-container">
<h3>Available Functions:</h3>
<div id="availableFunctionsList" class="available-functions-list">
<!-- Selected functions will be displayed here -->
</div>
</div>
<div id="chatWindow" class="chat-window">
<!-- Chat messages will be displayed here -->
</div>
<div class="chat-input-container">
<textarea id="userMessage" placeholder="Type your message here..." rows="2"></textarea>
<button id="sendMessage" class="btn btn-success">Send</button>
</div>
</div>
</div>
<!-- Function Details Modal -->
<div id="functionModal" class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<div id="modalContent">
<!-- Function details will be displayed here -->
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<!-- Include Marked.js for Markdown parsing -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
/* Basic Styles */
.header-container {
margin-bottom: 20px;
}
.main-content {
display: flex;
flex-direction: column;
}
.function-selection {
margin-bottom: 20px;
flex-shrink: 0;
}
.search-bar {
width: 100%;
padding: 5px;
margin-bottom: 10px;
box-sizing: border-box;
}
.function-list {
overflow-y: auto;
max-height: calc(100vh - 400px); /* Adjust based on header and other elements */
}
.function-item {
border-bottom: 1px solid #ddd;
padding: 5px 10px;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s;
position: relative;
}
.function-item:hover {
background-color: #f0f0f0;
}
.function-item.selected {
background-color: #e0f7fa;
}
.function-name {
flex: 1;
font-weight: bold;
text-decoration: none;
color: inherit;
}
.add-button {
margin-left: 5px;
}
.info-button {
background: none;
border: none;
cursor: pointer;
font-size: 16px;
margin-left: 5px;
position: relative;
}
.chat-container {
border: 1px solid #ccc;
padding: 10px;
border-radius: 5px;
flex: 1;
display: flex;
flex-direction: column;
max-height: calc(100vh - 400px);
margin-top: 20px; /* Added margin to separate from function list */
}
.chat-window {
flex: 1;
overflow-y: auto;
margin-bottom: 10px;
padding: 5px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
.chat-message {
margin-bottom: 10px;
}
.chat-message.user {
text-align: right;
}
.chat-message.assistant {
text-align: left;
}
.chat-input-container {
display: flex;
margin-top: 10px;
padding-bottom: 10px; /* Added padding at the bottom */
}
#userMessage {
flex: 1;
resize: none;
padding: 5px;
}
#sendMessage {
margin-left: 10px;
}
.available-functions-container {
margin-bottom: 20px;
}
.available-functions-list {
display: flex;
flex-wrap: wrap;
}
.available-function-item {
background-color: #e0f7fa;
padding: 5px 10px;
margin-right: 10px;
margin-bottom: 10px;
border-radius: 3px;
position: relative;
cursor: default;
}
.available-function-item .remove-function {
position: absolute;
top: 2px;
right: 5px;
cursor: pointer;
font-weight: bold;
}
/* Modal Styles */
.modal {
display: none; /* Hidden by default */
position: fixed;
z-index: 1000; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto;
background-color: rgba(0,0,0,0.5); /* Black w/ opacity */
}
.modal-content {
background-color: #fefefe;
margin: 5% auto; /* 15% from the top and centered */
padding: 20px;
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
max-height: 80%;
overflow-y: auto;
position: relative;
}
.close-button {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
position: absolute;
right: 10px;
top: 5px;
}
.close-button:hover,
.close-button:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
/* Responsive Layout */
@media (min-width: 768px) {
.main-content {
flex-direction: row;
}
.function-selection {
width: 25%;
margin-right: 20px;
}
.chat-container {
width: 75%;
margin-top: 0; /* Remove top margin in row layout */
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const functionListContainer = document.getElementById('functionList');
const chatWindow = document.getElementById('chatWindow');
const userMessageInput = document.getElementById('userMessage');
const sendMessageButton = document.getElementById('sendMessage');
const searchBar = document.getElementById('searchBar');
const modal = document.getElementById('functionModal');
const modalContent = document.getElementById('modalContent');
const closeButton = document.querySelector('.close-button');
let availableFunctions = [];
let allFunctions = [];
let chatHistory = [
// Optionally, you can initialize with system message if needed
// { role: 'system', message: 'You are a helpful assistant.' }
];
// Function to fetch available functions using /api/functions endpoint
function fetchFunctions() {
fetch("{{ url_for('api.get_functions') }}", {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => {
allFunctions = data;
displayFunctions(allFunctions);
})
.catch(error => console.error('Error fetching functions:', error));
}
// Function to display functions as selectable items
function displayFunctions(functions) {
functionListContainer.innerHTML = '';
functions.forEach(func => {
console.log(func);
const functionItem = document.createElement('div');
functionItem.className = 'function-item';
functionItem.dataset.name = func.name;
functionItem.dataset.description = func.description || '';
functionItem.dataset.details = JSON.stringify(func);
const functionName = document.createElement('a');
functionName.className = 'function-name';
functionName.textContent = func.name;
functionName.href = '/function/' + encodeURIComponent(func.name);
const addButton = document.createElement('button');
addButton.className = 'add-button';
addButton.textContent = 'Add';
// **New Code Starts Here**
// Check if the function is already in availableFunctions
if (availableFunctions.includes(func.name)) {
addButton.textContent = 'Remove';
addButton.classList.add('remove');
} else {
addButton.textContent = 'Add';
addButton.classList.remove('remove');
}
// **New Code Ends Here**
addButton.addEventListener('click', function(event) {
event.stopPropagation();
toggleFunctionSelection(func.name);
if (addButton.textContent === 'Add') {
addButton.textContent = 'Remove';
addButton.classList.add('remove');
} else {
addButton.textContent = 'Add';
addButton.classList.remove('remove');
}
});
const infoButton = document.createElement('button');
infoButton.className = 'info-button';
infoButton.innerHTML = 'ℹ️';
// Add click event listener to open modal with function details
infoButton.addEventListener('click', function(event) {
event.stopPropagation();
openModal(func);
});
functionItem.appendChild(functionName);
functionItem.appendChild(addButton);
functionItem.appendChild(infoButton);
functionListContainer.appendChild(functionItem);
// Add click event listener for selecting the function item
functionItem.addEventListener('click', function(event) {
// Avoid triggering when clicking on addButton or infoButton
if (event.target === addButton || event.target === infoButton || infoButton.contains(event.target)) {
return;
}
window.location.href = '/function/' + encodeURIComponent(func.name);
});
});
}
// Function to open modal with function details
function openModal(func) {
// Assemble the detailed information
let infoContent = `<h2>${func.name}</h2>`;
infoContent += `<strong>Version:</strong> ${func.version || 'N/A'}<br>`;
infoContent += `<strong>Created Date:</strong> ${func.created_date || 'N/A'}<br>`;
infoContent += `<strong>Metadata:</strong> <pre>${JSON.stringify(func.metadata, null, 2) || '{}'}<pre><br>`;
infoContent += `<strong>Dependencies:</strong> <pre>${JSON.stringify(func.dependencies, null, 2) || '[]'}<pre><br>`;
infoContent += `<strong>Triggers:</strong> <pre>${JSON.stringify(func.triggers, null, 2) || '[]'}<pre><br>`;
infoContent += `<strong>Input Parameters:</strong> <pre>${JSON.stringify(func.input_parameters, null, 2) || '[]'}<pre><br>`;
infoContent += `<strong>Output Parameters:</strong> <pre>${JSON.stringify(func.output_parameters, null, 2) || '[]'}<pre><br>`;
infoContent += `<strong>Code:</strong><pre>${func.code || 'N/A'}</pre>`;
modalContent.innerHTML = infoContent;
modal.style.display = 'block';
}
// Close the modal when the user clicks on the close button
closeButton.addEventListener('click', function() {
modal.style.display = 'none';
});
// Close the modal when the user clicks anywhere outside of the modal content
window.addEventListener('click', function(event) {
if (event.target === modal) {
modal.style.display = 'none';
}
});
// Filter functions based on search input
searchBar.addEventListener('input', function() {
const query = this.value.toLowerCase();
const filteredFunctions = allFunctions.filter(func =>
func.name.toLowerCase().includes(query) ||
(func.description && func.description.toLowerCase().includes(query))
);
displayFunctions(filteredFunctions);
});
// Toggle function selection
function toggleFunctionSelection(functionName) {
const index = availableFunctions.indexOf(functionName);
if (index === -1) {
availableFunctions.push(functionName);
addFunctionToAvailableList(functionName);
} else {
availableFunctions.splice(index, 1);
removeFunctionFromAvailableList(functionName);
}
}
// Add function to available functions list in UI
function addFunctionToAvailableList(functionName) {
const availableFunctionsList = document.getElementById('availableFunctionsList');
const functionItem = document.createElement('div');
functionItem.className = 'available-function-item';
functionItem.dataset.name = functionName;
functionItem.textContent = functionName;
const removeButton = document.createElement('span');
removeButton.className = 'remove-function';
removeButton.textContent = '×';
removeButton.addEventListener('click', function() {
toggleFunctionSelection(functionName);
// Update the add button in the function list
const functionItems = document.querySelectorAll('.function-item');
functionItems.forEach(item => {
if (item.dataset.name === functionName) {
const addButton = item.querySelector('.add-button');
if (addButton) {
addButton.textContent = 'Add';
addButton.classList.remove('remove');
}
}
});
});
functionItem.appendChild(removeButton);
availableFunctionsList.appendChild(functionItem);
}
// Remove function from available functions list in UI
function removeFunctionFromAvailableList(functionName) {
const availableFunctionsList = document.getElementById('availableFunctionsList');
const functionItems = availableFunctionsList.querySelectorAll('.available-function-item');
functionItems.forEach(item => {
if (item.dataset.name === functionName) {
availableFunctionsList.removeChild(item);
}
});
}
// Send user message
sendMessageButton.addEventListener('click', function() {
const userMessage = userMessageInput.value.trim();
if (userMessage === '') return;
if (availableFunctions.length === 0) {
alert('Please select at least one function before sending a message.');
return;
}
// Display user message
addMessageToChat('user', userMessage);
// Clear input
userMessageInput.value = '';
// Prepare parameters
const params = {
chat_history: chatHistory,
available_function_names: availableFunctions.join(', ')
};
fetch("{{ url_for('api.execute_function', function_name='chat_with_functions') }}", {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params)
})
.then(response => response.json())
.then(data => {
console.log('Data received from server:', data);
let assistantResponse;
if (typeof data === 'string') {
assistantResponse = data;
} else if (data.message) {
assistantResponse = data.message;
} else {
assistantResponse = JSON.stringify(data);
}
// Display the assistant's response
addMessageToChat('assistant', assistantResponse);
})
.catch(error => {
console.error('Error:', error);
addMessageToChat('assistant', 'Error processing your request.');
});
});
// Function to add message to chat window
function addMessageToChat(role, message) {
if (typeof message !== 'string') {
message = 'No message received.';
}
const messageElement = document.createElement('div');
messageElement.className = `chat-message ${role}`;
if (role === 'assistant') {
// Parse Markdown
messageElement.innerHTML = marked.parse(message);
} else {
messageElement.textContent = message;
}
chatWindow.appendChild(messageElement);
chatWindow.scrollTop = chatWindow.scrollHeight;
// Save message to chat history
chatHistory.push({ role: role, message: message });
}
// Initialize by fetching functions
fetchFunctions();
});
</script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/function_details.html
================================================
{% extends "base.html" %}
{% block title %}Function Details: {{ function_name }}{% endblock %}
{% block head %}
<link rel="stylesheet" href="{{ url_for('dashboard.static', filename='css/style.css') }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/theme/monokai.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/python/python.min.js"></script>
{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > <span id="functionName">{{ function_name }}</span>
</div>
{% endblock %}
{% block content %}
<h1>Function: <span id="functionName">{{ function_name }}</span></h1>
<div class="two-column">
<div class="column">
<div class="function-detail">
<div class="section-title">Execute Function</div>
<div id="executionForm"></div>
<button onclick="executeFunction()" class="button">Execute</button>
<div id="executionResult" class="execution-result"></div>
</div>
<div class="function-detail">
<div class="section-title">Details</div>
<div id="functionDetails"></div>
<div id="functionTriggers"></div>
</div>
</div>
<div class="column">
<div class="function-detail">
<div class="section-title">Code</div>
<textarea id="codeEditor"></textarea>
<button onclick="updateFunction()" class="button">Update Function</button>
</div>
<div class="function-detail">
<div class="section-title">Logs</div>
<div id="functionLogs"></div>
</div>
<div class="function-detail">
<div class="section-title">
Versions
</div>
<button class="button" id="toggleVersionHistory">Show Versions</button>
<div id="versionHistory" class="version-history" style="display: none;"></div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
window.functionName = "{{ function_name }}";
window.dashboardRoute = "{{ url_for('dashboard.dashboard_home') }}";
const apiRoutes = {
getFunction: "{{ url_for('api.get_function', function_name=function_name) }}",
executeFunction: "{{ url_for('api.execute_function', function_name=function_name) }}",
getLogs: "{{ url_for('api.get_logs', function_name=function_name) }}",
getTriggers: "{{ url_for('api.get_triggers', function_name=function_name) }}",
getFunctionVersions: "{{ url_for('api.get_function_versions', function_name=function_name) }}",
activateVersion: "{{ url_for('api.activate_function_version', function_name=function_name, version='VERSION_PLACEHOLDER') }}",
updateFunction: "{{ url_for('api.update_function', function_name=function_name) }}"
};
</script>
<script src="{{ url_for('dashboard.static', filename='js/function_details.js') }}"></script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/function_graph.html
================================================
{% extends "base.html" %}
{% block title %}Function Relationship Graph{% endblock %}
{% block head %}
{{ super() }}
<!-- Include Cytoscape.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script>
<!-- Include CodeMirror CSS and JS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/theme/monokai.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/python/python.min.js"></script>
<!-- Page-Specific Styles -->
<style>
#graph {
width: 100%;
height: calc(100vh - 160px); /* Adjust height considering nav and breadcrumb */
background-color: var(--background-color);
}
.overlay {
position: absolute;
background: var(--card-bg-color);
border: 1px solid var(--border-color);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 10;
top: 10px;
right: 10px;
width: 400px;
height: 90vh;
overflow-y: auto;
display: none;
}
.close-btn {
position: absolute;
top: 10px;
right: 10px;
font-size: 20px;
cursor: pointer;
color: var(--text-color);
}
.toggle-menu {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
}
.toggle-menu button {
padding: 10px;
cursor: pointer;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
}
.toggle-menu button:hover {
background-color: var(--hover-color);
}
.card {
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
background-color: var(--card-bg-color);
}
.card-title {
font-size: 1.2em;
margin-bottom: 10px;
color: var(--primary-color);
}
.card-content {
display: none;
}
.card-content.active {
display: block;
}
#tab-buttons {
display: flex;
margin-bottom: 10px;
}
.tab-button {
padding: 5px 10px;
margin-right: 5px;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 3px;
cursor: pointer;
}
.tab-button.active {
background-color: #007bff;
color: white;
border-color: #0056b3;
}
#tab-content {
border: 1px solid #ccc;
padding: 10px;
border-radius: 3px;
}
</style>
{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > Function Graph
</div>
{% endblock %}
{% block content %}
<h1>Function Relationship Graph</h1>
<div id="graph"></div>
<!-- Overlay for Function Details -->
<div id="overlay" class="overlay">
<span class="close-btn" onclick="closeOverlay()">×</span>
<div id="overlay-content">
<h2 id="functionName"></h2>
<!-- Tab buttons -->
<div id="tab-buttons">
<button class="tab-button active" onclick="showTab('description')">Description</button>
<button class="tab-button" onclick="showTab('code')">Code</button>
<button class="tab-button" onclick="showTab('logs')">Logs</button>
<button class="tab-button" onclick="showTab('execute')">Execute</button>
<button class="tab-button" onclick="showTab('versions')">Versions</button>
</div>
<!-- Tab content -->
<div id="tab-content">
<div id="description-tab">
<div id="functionDetails"></div>
<div id="functionTriggers"></div>
</div>
<div id="code-tab" style="display: none;">
<textarea id="codeEditor"></textarea>
<button onclick="updateFunction()" class="button">Update Function</button>
</div>
<div id="logs-tab" style="display: none;">
<div id="functionLogs"></div>
</div>
<div id="execute-tab" style="display: none;">
<div id="executionForm"></div>
<button onclick="executeFunction()" class="button">Execute</button>
<div id="executionResult" class="execution-result"></div>
</div>
<div id="versions-tab" style="display: none;">
<button class="button" id="toggleVersionHistory">Show Versions</button>
<div id="versionHistory" class="version-history" style="display: none;"></div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script>
// Define dashboardRoute correctly without _external and _scheme
window.dashboardRoute = "{{ url_for('dashboard.dashboard_home') }}";
// Initialize Cytoscape graph
let cy;
document.addEventListener('DOMContentLoaded', () => {
cy = cytoscape({
container: document.getElementById('graph'),
style: [
{
selector: 'node',
style: {
'shape': 'rectangle',
'background-color': '#E6F2FF',
'label': 'data(id)',
'text-valign': 'center',
'text-halign': 'center',
'text-wrap': 'wrap',
'text-max-width': '150px',
'width': 'label',
'height': 'label',
'padding': '12px',
'font-weight': 'bold',
'font-size': '16px',
'color': '#333333',
'text-outline-color': '#FFFFFF',
'text-outline-width': 1,
'cursor': 'pointer'
}
},
{
selector: 'node[type="import"]',
style: {
'background-color': '#E6FFF2' // Light green for imports
}
},
{
selector: 'node[type="trigger"]',
style: {
'background-color': '#FFE6E6' // Light red for triggers
}
},
{
selector: 'node:hover',
style: {
'background-opacity': 0.8
}
},
{
selector: 'edge',
style: {
'width': 1,
'line-color': '#999999',
'target-arrow-color': '#999999',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
'arrow-scale': 1.5
}
},
{
selector: 'edge[type="dependency"]',
style: {
'width': 2
}
},
{
selector: 'edge[type="trigger"]',
style: {
'line-style': 'dashed',
'label': 'trigger',
'font-size': '12px',
'text-rotation': 'autorotate',
'text-margin-y': -10
}
}
],
layout: {
name: 'cose',
idealEdgeLength: 50,
nodeOverlap: 20,
refresh: 20,
fit: true,
padding: 30,
randomize: false,
componentSpacing: 80,
nodeRepulsion: 450000,
edgeElasticity: 100,
nestingFactor: 5,
gravity: 80,
numIter: 1000,
initialTemp: 200,
coolingFactor: 0.95,
minTemp: 1.0
},
wheelSensitivity: 0.2
});
fetch('/api/functions')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch functions');
}
return response.json();
})
.then(data => {
const elements = [];
const imports = new Set();
data.forEach(func => {
elements.push({ data: { id: func.name, type: 'function' } });
func.dependencies.forEach(dep => {
elements.push({ data: { id: dep, type: 'function' } });
elements.push({ data: { source: func.name, target: dep, type: 'dependency' } });
});
func.triggers.forEach(trigger => {
elements.push({ data: { id: trigger, type: 'trigger' } });
elements.push({ data: { source: func.name, target: trigger, type: 'trigger' } });
});
func.imports.forEach(imp => imports.add(imp));
});
imports.forEach(imp => {
elements.push({ data: { id: imp, type: 'import' } });
});
data.forEach(func => {
func.imports.forEach(imp => {
elements.push({ data: { source: func.name, target: imp, type: 'import' } });
});
});
cy.add(elements);
cy.layout({ name: 'cose' }).run();
// Zoom to fit with a bit of padding
cy.fit(40);
// Add a slight zoom in
cy.zoom({
level: cy.zoom() * 1.3,
renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 }
});
})
.catch(error => {
console.error('Error fetching functions:', error);
});
cy.on('tap', 'node', function(evt) {
const node = evt.target;
if (node.data('type') === 'function') {
showFunctionOverlay(node.id());
}
});
cy.on('zoom pan', () => {
closeOverlay();
});
});
// Initialize global variables and API routes
window.functionName = null;
window.apiRoutes = {}; // Initialize apiRoutes as an empty object
window.functionDetailsJsLoaded = false; // Flag to check if function_details.js is loaded
// Update apiRoutes based on the selected function
window.updateApiRoutes = function(functionName) {
window.apiRoutes = {
getFunction: `/api/function/${functionName}`,
updateFunction: `/api/function/${functionName}`,
executeFunction: `/api/execute/${functionName}`,
getLogs: `/api/logs/${functionName}`,
getFunctionVersions: `/api/function/${functionName}/versions`,
activateVersion: (version) => `/api/function/${functionName}/versions/${version}/activate`
};
};
// Function to show the overlay with function details
function showFunctionOverlay(functionName) {
const overlay = document.getElementById('overlay');
document.getElementById('functionName').textContent = functionName;
// Set global functionName
window.functionName = functionName;
// Update apiRoutes with the current function name
window.updateApiRoutes(functionName);
overlay.style.display = 'block';
// Fetch the function data from the API
fetch(window.apiRoutes.getFunction)
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch function data');
}
return response.json();
})
.then(data => {
console.log("Fetched function data:", data);
if (data.error) {
console.error("Error fetching function data:", data.error);
return;
}
// Set the global functionData to the fetched data
window.functionData = data;
// Check if function_details.js is loaded
if (!window.functionDetailsJsLoaded) {
var script = document.createElement('script');
script.src = "{{ url_for('dashboard.static', filename='js/function_details.js') }}";
script.onload = function() {
window.functionDetailsJsLoaded = true;
// Call the required functions after loading the script
window.loadFunctionDetails();
window.loadFunctionLogs();
// Initialize CodeMirror editor
window.initCodeEditor();
// Set up version history toggle
const toggleVersionHistoryBtn = document.getElementById('toggleVersionHistory');
if (toggleVersionHistoryBtn) {
toggleVersionHistoryBtn.onclick = window.toggleVersionHistory;
}
// Default to showing the Description tab
showTab('description');
};
document.head.appendChild(script);
} else {
// If function_details.js is already loaded, just call the necessary functions
window.loadFunctionDetails();
window.loadFunctionLogs();
// Initialize CodeMirror editor
if (typeof codeEditor === 'undefined' || !codeEditor) {
window.initCodeEditor();
} else {
codeEditor.setValue('');
codeEditor.toTextArea();
window.initCodeEditor();
}
// Set up version history toggle
const toggleVersionHistoryBtn = document.getElementById('toggleVersionHistory');
if (toggleVersionHistoryBtn) {
toggleVersionHistoryBtn.onclick = window.toggleVersionHistory;
}
// Default to showing the Description tab
showTab('description');
}
})
.catch(error => {
console.error('Error fetching function data:', error);
});
}
// Function to handle tab switching
function showTab(tabName) {
const tabs = ['description', 'code', 'logs', 'execute', 'versions'];
tabs.forEach(tab => {
const tabElement = document.getElementById(`${tab}-tab`);
const tabButton = document.querySelector(`button.tab-button:nth-child(${tabs.indexOf(tab) + 1})`);
if (tab === tabName) {
tabElement.style.display = 'block';
tabButton.classList.add('active');
} else {
tabElement.style.display = 'none';
tabButton.classList.remove('active');
}
});
// Load content for specific tabs if needed
if (tabName === 'code' && document.getElementById('code-tab').innerHTML === '') {
window.initCodeEditor();
} else if (tabName === 'logs' && document.getElementById('logs-tab').innerHTML === '') {
window.loadFunctionLogs();
} else if (tabName === 'execute' && document.getElementById('executionForm').innerHTML === '') {
window.createExecutionForm();
} else if (tabName === 'versions' && document.getElementById('versionHistory').innerHTML === '') {
window.loadFunctionVersions();
}
}
// Function to close the overlay
function closeOverlay() {
document.getElementById('overlay').style.display = 'none';
}
// Function to toggle card visibility (if needed)
function toggleCard(cardId) {
const allCards = document.querySelectorAll('.card .card-content');
allCards.forEach(card => card.classList.remove('active'));
const selectedCard = document.getElementById(cardId).querySelector('.card-content');
selectedCard.classList.add('active');
}
// Override the executeFunction to ensure it uses POST method
window.executeFunction = function() {
let params = {};
const form = document.getElementById('executionForm');
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
params[input.name] = input.value;
});
fetch(apiRoutes.executeFunction, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
})
.then(response => {
if (!response.ok) {
return response.text().then(text => {
throw new Error(text || response.statusText);
});
}
return response.json();
})
.then(data => {
document.getElementById('executionResult').innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
})
.catch((error) => {
console.error('Error:', error);
document.getElementById('executionResult').innerHTML = `<pre class="error">Error executing function: ${error.message}</pre>`;
});
};
</script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/function_graph_3d.html
================================================
{% extends "base.html" %}
{% block title %}3D Function Relationship Graph{% endblock %}
{% block head %}
{{ super() }}
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Inter', Arial, sans-serif;
}
#3d-graph {
width: 100%;
height: 100vh;
}
.node-label {
background-color: white;
padding: 5px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
position: absolute;
display: none;
z-index: 1;
}
</style>
{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > 3D Graph
</div>
{% endblock %}
{% block content %}
<div id="3d-graph"></div>
<div id="node-label" class="node-label"></div>
{% endblock %}
{% block scripts %}
<!-- Include the 3D Force-Directed Graph and Three.js libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://unpkg.com/3d-force-graph"></script>
<script>
// Main script to generate 3D graph
// Fetch the data for functions and dependencies (same structure as before)
fetch('/api/functions')
.then(response => response.json())
.then(data => {
const imports = new Set();
// Prepare the nodes and links arrays for the 3D force-directed graph
const nodes = [];
const links = [];
// Map function data to nodes and edges
data.forEach(func => {
nodes.push({ id: func.name, group: 'function' });
func.dependencies.forEach(dep => {
links.push({ source: func.name, target: dep, type: 'dependency' });
});
func.triggers.forEach(trigger => {
links.push({ source: func.name, target: trigger, type: 'trigger' });
});
func.imports.forEach(imp => imports.add(imp));
});
imports.forEach(imp => {
nodes.push({ id: imp, group: 'import' });
});
data.forEach(func => {
func.imports.forEach(imp => {
links.push({ source: func.name, target: imp, type: 'import' });
});
});
// Create the 3D graph using 3D Force-Directed Graph
const Graph = ForceGraph3D()
(document.getElementById('3d-graph'))
.graphData({ nodes, links })
.nodeLabel('id')
.nodeAutoColorBy('group')
.linkWidth(link => link.type === 'dependency' ? 2 : 1)
.linkDirectionalParticles(2)
.linkDirectionalParticleWidth(2)
.nodeThreeObject(node => {
// Create a custom 3D object for nodes using the correct version of THREE
const sphereGeometry = new THREE.SphereGeometry(8, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: node.group === 'function' ? 0x44aa88 : 0xffcc00 });
return new THREE.Mesh(sphereGeometry, material);
})
.onNodeClick(node => {
// On node click, dynamically fetch and show more information
expandNode(node);
});
// Function to expand a node and add more details dynamically
function expandNode(node) {
// Fetch the details of the clicked node (function)
const url = '/api/functions/' + node.id;
fetch(url)
.then(response => response.json())
.then(funcData => {
// Handle function data here
// You can add more nodes or display details as needed
})
.catch(error => {
console.error('Error fetching function details:', error);
});
}
});
</script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/function_graph_mermaid.html
================================================
{% extends "base.html" %}
{% block title %}Function Relationship Mermaid Graph{% endblock %}
{% block head %}
{{ super() }}
<!-- Include Mermaid.js as an ESM module -->
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
// Initialize Mermaid with desired configuration
mermaid.initialize({
startOnLoad: false, // We will manually initialize after graph is inserted
theme: 'default',
flowchart: {
useMaxWidth: true,
htmlLabels: true,
fontFamily: 'Arial, sans-serif' // Use web-safe fonts to avoid CORS issues
}
});
</script>
<style>
/* Additional styles specific to the function_graph_mermaid.html */
#graph {
width: 100%;
height: calc(100vh - 160px); /* Adjust height considering nav and breadcrumb */
background-color: var(--background-color);
overflow: auto;
padding: 20px;
}
/* Download Button Styles */
#download-btn {
position: fixed;
top: 120px; /* Adjust based on your layout */
right: 30px;
padding: 10px 20px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
z-index: 1001; /* Ensure it's above the graph */
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
#download-btn:hover {
background-color: var(--hover-color);
}
/* Responsive Adjustments */
@media (max-width: 768px) {
#download-btn {
top: 100px;
right: 20px;
padding: 8px 16px;
}
}
/* Optional: Enhance node hover effects */
.mermaid .node rect {
cursor: pointer;
}
.mermaid .node:hover rect {
stroke: var(--primary-color);
stroke-width: 2px;
}
</style>
{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > Mermaid Graph
</div>
{% endblock %}
{% block content %}
<h1>Function Relationship Mermaid Graph</h1>
<div id="graph">
<div class="mermaid">
%% Mermaid graph will be injected here
</div>
</div>
<!-- Download Button -->
<!-- <button id="download-btn">Download Image</button>-->
{% endblock %}
{% block scripts %}
{{ super() }}
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
document.addEventListener('DOMContentLoaded', () => {
fetch('/api/functions')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch functions');
}
return response.json();
})
.then(data => {
let mermaidGraph = 'graph TD\n';
const imports = new Set();
const wrappers = []; // To store functions ending with _wrapper
// Process each function to build the graph
data.forEach(func => {
// Prepare input and output parameters
const inputs = Array.isArray(func.input_parameters) && func.input_parameters.length > 0
? func.input_parameters.map(param => `${param.name}: ${param.type}`).join(', ')
: 'None';
const outputs = Array.isArray(func.output_parameters) && func.output_parameters.length > 0
? func.output_parameters.map(param => `${param.name}: ${param.type}`).join(', ')
: 'None';
// Multiline label using <br/> for inputs and outputs, with function name in bold
const label = `<b>${func.name}</b><br/>Inputs: ${inputs}<br/>Outputs: ${outputs}`;
if (func.name.endsWith('_wrapper')) {
wrappers.push(func.name); // Add _wrapper functions to this array
}
// Define node with HTML-like labels for better formatting
mermaidGraph += ` ${func.name}["${label}"]\n`;
// Add dependencies as edges
func.dependencies.forEach(dep => {
mermaidGraph += ` ${func.name} -->|Depends on| ${dep}\n`;
});
// Add triggers as dashed edges
func.triggers.forEach(trigger => {
mermaidGraph += ` ${func.name} -.->|Triggered by| ${trigger}\n`;
});
// Collect imports
if (Array.isArray(func.imports)) {
func.imports.forEach(imp => imports.add(imp));
}
});
// Define Imports subgraph
if (imports.size > 0) {
mermaidGraph += ` subgraph Imports\n`;
imports.forEach(imp => {
mermaidGraph += ` ${imp}["Import: ${imp}"]\n`;
});
mermaidGraph += ` end\n`;
// Link functions to imports
data.forEach(func => {
if (Array.isArray(func.imports)) {
func.imports.forEach(imp => {
mermaidGraph += ` ${func.name} -->|Uses| ${imp}\n`;
});
}
});
}
// Define Wrapper_Functions subgraph if any
if (wrappers.length > 0) {
mermaidGraph += ` subgraph Wrapper_Functions\n`;
wrappers.forEach(wrapper => {
mermaidGraph += ` ${wrapper}["<b>${wrapper}</b>"]\n`;
});
mermaidGraph += ` end\n`;
// Link wrapper functions if necessary
// (Add any specific links related to wrapper functions here)
}
// Inject the graph into the Mermaid container
const graphContainer = document.querySelector('.mermaid');
graphContainer.innerHTML = mermaidGraph;
// Render the Mermaid graph
mermaid.init(undefined, graphContainer);
// Add click event listeners to nodes for interactivity
// Note: Mermaid does not provide built-in click events, so we need to add them manually
// This approach uses SVG elements generated by Mermaid
// Ensure that the graph has been rendered before adding event listeners
// Delay to ensure Mermaid has rendered the SVG
setTimeout(() => {
const nodes = graphContainer.querySelectorAll('.node');
nodes.forEach(node => {
node.style.cursor = 'pointer'; // Change cursor to pointer
node.addEventListener('click', () => {
// Extract function name from the label
const rawLabel = node.querySelector('text').innerHTML;
const functionName = rawLabel.split('<br/>')[0].replace('<b>', '').replace('</b>', '');
// Optional: Implement a click handler if needed
// For now, we'll just alert the function name
alert(`Function: ${functionName}`);
});
});
}, 500); // Adjust delay as necessary
})
.catch(error => {
console.error('Error fetching functions:', error);
document.getElementById('graph').innerHTML = `<p>Error loading graph: ${error.message}</p>`;
});
});
// Download Image Functionality
document.getElementById('download-btn').addEventListener('click', () => {
const graphContainer = document.querySelector('.mermaid');
const svg = graphContainer.querySelector('svg');
if (!svg) {
alert('Graph is not loaded yet.');
return;
}
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(svg);
// Create a canvas to draw the SVG
const canvas = document.createElement('canvas');
const bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
const ctx = canvas.getContext('2d');
const img = new Image();
const svgBlob = new Blob([svgString], {type: 'image/svg+xml;charset=utf-8'});
const url = URL.createObjectURL(svgBlob);
img.onload = () => {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
// Create a PNG data URL
const pngUrl = canvas.toDataURL('image/png');
// Create a temporary link to trigger download
const downloadLink = document.createElement('a');
downloadLink.href = pngUrl;
downloadLink.download = 'function_relationship_graph.png';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
img.onerror = (error) => {
console.error('Error loading SVG image for download:', error);
alert('Failed to convert graph to image.');
};
img.src = url;
});
</script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/index.html
================================================
{% extends "base.html" %}
{% block title %}Function Dashboard{% endblock %}
{% block content %}
<h1>Function Dashboard</h1>
<input type="text" id="searchInput" placeholder="Search functions..." onkeyup="filterTable()">
<table id="functionTable">
<thead>
<tr>
<th class="sortable" onclick="sortTable('name')">Name</th>
<th>Version</th>
<th>Description</th>
<th>Input Parameters</th>
<th>Output Parameters</th>
<th>Dependencies</th>
<th>Imports</th>
<th>Triggers</th>
<th class="sortable" onclick="sortTable('created_date')">Created Date</th>
<th class="sortable" onclick="sortTable('total_logs')">Total Logs</th>
<th class="sortable" onclick="sortTable('last_log_date')">Last Log Date</th>
</tr>
</thead>
<tbody>
<!-- Function rows will be dynamically inserted here -->
</tbody>
</table>
<div id="functionGrid" class="function-grid">
<!-- Function cards will be dynamically inserted here for mobile view -->
</div>
{% endblock %}
{% block scripts %}
<script>
const dashboardRoute = "{{ url_for('dashboard.dashboard_home') }}";
const apiFunctionsUrl = "{{ url_for('api.get_functions') }}";
const apiLogsUrl = "{{ url_for('api.get_logs', function_name='') }}"; // Base URL for logs
</script>
<!-- Include external JavaScript -->
<script src="{{ url_for('dashboard.static', filename='js/dashboard.js') }}"></script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/log_page.html
================================================
{% extends "base.html" %}
{% block title %}Log Details{% endblock %}
{% block content %}
<h1>Log Details for Log ID {{ log_id }}</h1>
<!-- Container to display the logs -->
<div id="logContainer"></div>
{% endblock %}
{% block scripts %}
<style>
/* CSS Styles */
.log-entry {
margin-left: 20px;
border-left: 1px solid #ccc;
padding-left: 10px;
position: relative;
}
.log-entry::before {
content: '';
position: absolute;
left: -1px;
top: 0;
bottom: 0;
width: 1px;
background: #ccc;
}
.log-header {
font-weight: bold;
display: flex;
align-items: center;
}
.log-status {
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 10px;
}
.log-status-success {
background-color: green;
}
.log-status-error {
background-color: red;
}
.log-status-other {
background-color: yellow;
}
.log-details {
margin-top: 5px;
display: none; /* Hidden by default */
background-color: #f9f9f9;
padding: 10px;
}
.toggle-button {
color: blue;
cursor: pointer;
text-decoration: underline;
margin-left: 10px;
}
pre {
background-color: #f4f4f4;
padding: 10px;
overflow: auto;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const logId = {{ log_id }};
const apiLogBundleUrl = '/api/log_bundle/' + logId;
fetch(apiLogBundleUrl)
.then(response => {
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
return response.json();
})
.then(data => {
const logs = data.logs;
if (!logs || logs.length === 0) {
document.getElementById('logContainer').innerHTML = '<p>No logs found.</p>';
return;
}
const logsById = {};
logs.forEach(log => {
log.children = [];
logsById[log.id] = log;
});
// Build the tree by assigning children to their respective parents
logs.forEach(log => {
if (log.parent_log_id !== null && logsById[log.parent_log_id]) {
logsById[log.parent_log_id].children.push(log);
}
});
// Identify all root logs (logs without a parent in the fetched bundle)
const rootLogs = logs.filter(log => log.parent_log_id === null || !logsById[log.parent_log_id]);
const logContainer = document.getElementById('logContainer');
rootLogs.forEach(log => {
renderLog(logContainer, log);
});
})
.catch(error => {
console.error('Error fetching log bundle:', error);
document.getElementById('logContainer').innerHTML = `<p>Error loading log bundle: ${error.message}</p>`;
});
function renderLog(container, log, depth = 0) {
const div = document.createElement('div');
div.className = 'log-entry';
div.style.marginLeft = (depth * 20) + 'px';
const header = document.createElement('div');
header.className = 'log-header';
const statusDiv = document.createElement('div');
statusDiv.className = 'log-status ' + getLogStatusClass(log.log_type);
header.appendChild(statusDiv);
const messageSpan = document.createElement('span');
messageSpan.textContent = `${log.function_name}: ${log.message}`;
const toggleButton = document.createElement('span');
toggleButton.className = 'toggle-button';
toggleButton.textContent = '[Show Details]';
toggleButton.addEventListener('click', function() {
if (detailsDiv.style.display === 'none') {
detailsDiv.style.display = 'block';
toggleButton.textContent = '[Hide Details]';
} else {
detailsDiv.style.display = 'none';
toggleButton.textContent = '[Show Details]';
}
});
header.appendChild(messageSpan);
header.appendChild(toggleButton);
div.appendChild(header);
const detailsDiv = document.createElement('div');
detailsDiv.className = 'log-details';
// Handle null or undefined params/output
const params = log.params ? JSON.stringify(log.params, null, 2) : 'N/A';
const output = log.output ? JSON.stringify(log.output, null, 2) : 'N/A';
detailsDiv.innerHTML = `
<p><strong>ID:</strong> ${log.id}</p>
<p><strong>Timestamp:</strong> ${new Date(log.timestamp).toLocaleString()}</p>
<p><strong>Log Type:</strong> ${log.log_type}</p>
<p><strong>Time Spent:</strong> ${log.time_spent ? log.time_spent.toFixed(3) + ' s' : 'N/A'}</p>
<p><strong>Parent Log ID:</strong> ${log.parent_log_id !== null ? log.parent_log_id : 'N/A'}</p>
<p><strong>Triggered By Log ID:</strong> ${log.triggered_by_log_id !== null ? log.triggered_by_log_id : 'N/A'}</p>
<p><strong>Params:</strong></p>
<pre>${params}</pre>
<p><strong>Output:</strong></p>
<pre>${output}</pre>
`;
div.appendChild(detailsDiv);
container.appendChild(div);
// Recursively render children
if (log.children && log.children.length > 0) {
log.children.forEach(childLog => {
renderLog(container, childLog, depth + 1);
});
}
}
function getLogStatusClass(logType) {
switch (logType.toLowerCase()) {
case 'success':
return 'log-status-success';
case 'error':
return 'log-status-error';
default:
return 'log-status-other';
}
}
});
</script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/log_relationship_graph.html
================================================
{% extends "base.html" %}
{% block title %}Log Relationship Graph{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > Log Graph
</div>
{% endblock %}
{% block content %}
<h1>Log Relationship Graph</h1>
<div class="controls">
<input type="text" id="functionName" placeholder="Function name (optional)">
<input type="datetime-local" id="startDate" placeholder="Start date">
<input type="datetime-local" id="endDate" placeholder="End date">
<button onclick="loadLogs()">Load Logs</button>
</div>
<div id="graph"></div>
<div class="legend">
<div class="legend-item">
<div class="legend-color" style="background-color: #3366cc;"></div>
<span>Parent Relationship</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #dc3912; border-top: 2px dashed #dc3912;"></div>
<span>Trigger Relationship</span>
</div>
</div>
<div id="overlay" class="overlay">
<div class="overlay-content">
<span class="close-btn" onclick="closeOverlay()">×</span>
<h2 id="logTitle"></h2>
<pre id="logContent"></pre>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.1/cytoscape.min.js"></script>
<script>
const apiLogsUrl = "{{ url_for('api.get_logs') }}";
</script>
<script src="{{ url_for('dashboard.static', filename='js/log_graph.js') }}"></script>
{% endblock %}
================================================
FILE: babyagi/dashboard/templates/logs_dashboard.html
================================================
{% extends "base.html" %}
{% block title %}Logs Dashboard{% endblock %}
{% block breadcrumb %}
<div class="breadcrumb">
<a href="{{ url_for('dashboard.dashboard_home') }}">Home</a> > Logs
</div>
{% endblock %}
{% block content %}
<h1>Logs Dashboard</h1>
<!-- Filter Section -->
<div class="filter-container">
<label class="filter-label" for="functionFilter">Function:</label>
<select id="functionFilter" class="filter-select">
<option value="">All</option>
<!-- Dynamic options will be populated here -->
</select>
<label class="filter-label" for="logTypeFilter">Log Type:</label>
<select id="logTypeFilter" class="filter-select">
<option value="">All</option>
<option value="info">Info</option>
<option value="success">Success</option>
<option value="error">Error</option>
<!-- Add more log types if applicable -->
</select>
<label class="filter-label" for="dateFilter">Date Range:</label>
<input type="date" id="startDate" class="filter-select">
<input type="date" id="endDate" class="filter-select">
<button class="btn btn-primary" onclick="applyFilters()">Apply Filters</button>
<button class="btn btn-secondary" onclick="resetFilters()">Reset Filters</button>
</div>
<!-- Logs Table (Desktop View) -->
<table id="logTable" class="table table-striped table-bordered desktop-only">
<thead>
<tr>
<th class="sortable" onclick="sortLogs('id')">ID</th>
<th class="sortable" onclick="sortLogs('function_name')">Function Name</th>
<th class="sortable" onclick="sortLogs('message')">Message</th>
<th class="sortable" onclick="sortLogs('timestamp')">Timestamp</th>
<th class="sortable" onclick="sortLogs('log_type')">Log Type</th>
<th class="sortable" onclick="sortLogs('time_spent')">Time Spent (s)</th>
<th class="sortable" onclick="sortLogs('parent_log_id')">Parent Log ID</th>
<th class="sortable" onclick="sortLogs('triggered_by_log_id')">Triggered By Log ID</th>
</tr>
</thead>
<tbody>
<!-- Log rows will be dynamically inserted here -->
</tbody>
</table>
<!-- Logs Grid (Mobile View) -->
<div id="logGrid" class="log-grid mobile-only">
<!-- Log cards will be dynamically inserted here -->
</div>
{% endblock %}
{% block scripts %}
<script>
const dashboardRoute = "{{ url_for('dashboard.dashboard_home') }}";
const apiLogsUrl = "{{ url_for('api.get_logs') }}";
</script>
<!-- Include external JavaScript -->
<script src="{{ url_for('dashboard.static', filename='js/log_dashboard.js') }}"></script>
{% endblock %}
================================================
FILE: babyagi/functionz/__init__.py
================================================
from .core.framework import func
__all__ = ['func']
================================================
FILE: babyagi/functionz/core/__init__.py
================================================
================================================
FILE: babyagi/functionz/core/execution.py
================================================
import subprocess
import sys
import importlib
import inspect
from typing import Any, Dict, List, Optional
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
class FunctionExecutor:
def __init__(self, python_func):
self.python_func = python_func
def _install_external_dependency(self, package_name: str, imp_name: str) -> Any:
try:
return importlib.import_module(imp_name)
except ImportError:
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
return importlib.import_module(package_name)
def _resolve_dependencies(self, function_version: Dict[str, Any], local_scope: Dict[str, Any],
parent_log_id: Optional[int], executed_functions: List[str],
visited: Optional[set] = None) -> None:
if visited is None:
visited = set()
function_name = function_version['name']
function_imports = self.python_func.db.get_function_imports(function_name)
for imp in function_imports:
lib_name = imp['lib'] if imp['lib'] else imp['name']
if lib_name not in local_scope:
module = self._install_external_dependency(lib_name, imp['name'])
local_scope[imp['name']] = module
for dep_name in function_version.get('dependencies', []):
if dep_name not in local_scope and dep_name not in visited:
visited.add(dep_name)
dep_data = self.python_func.db.get_function(dep_name)
if not dep_data:
raise ValueError(f"Dependency '{dep_name}' not found in the database.")
self._resolve_dependencies(dep_data, local_scope, parent_log_id, executed_functions, visited)
exec(dep_data['code'], local_scope)
if dep_name in local_scope:
dep_func = local_scope[dep_name]
# Wrap the dependent function
local_scope[dep_name] = self._create_function_wrapper(dep_func, dep_name, parent_log_id, executed_functions)
def _create_function_wrapper(self, func: callable, func_name: str, parent_log_id: int, executed_functions: List[str]):
def wrapper(*args, **kwargs):
return self.execute(func_name, *args, executed_functions=executed_functions, parent_log_id=parent_log_id, **kwargs)
return wrapper
def execute(
self,
function_name: str,
*args,
executed_functions: Optional[List[str]] = None,
parent_log_id: Optional[int] = None,
wrapper_log_id: Optional[int] = None,
triggered_by_log_id: Optional[int] = None,
**kwargs
) -> Any:
start_time = datetime.now()
executed_functions = executed_functions or []
log_id = None
bound_args = None
output = None
# Ensure wrapper_log_id is initialized
wrapper_log_id = wrapper_log_id if wrapper_log_id is not None else None
logger.info(f"Executing function: {function_name}")
try:
executed_functions.append(function_name)
function_version = self.python_func.db.get_function(function_name)
if not function_version:
raise ValueError(f"Function '{function_name}' not found in the database.")
# If the function being executed is the wrapper, create a special log entry and set wrapper_log_id
if function_name == 'execute_function_wrapper':
log_id = self._add_execution_log(
function_name,
start_time,
{},
None,
0,
parent_log_id,
triggered_by_log_id,
'started'
)
wrapper_log_id = log_id
else:
log_id = self._add_execution_log(
function_name,
start_time,
{},
None,
0,
wrapper_log_id if wrapper_log_id else parent_log_id,
triggered_by_log_id,
'started'
)
# Include 'datetime' in local_scope
local_scope = {
'func': self.python_func,
'parent_log_id': log_id,
'datetime': datetime # Added datetime here
}
self._resolve_dependencies(
function_version,
local_scope,
parent_log_id=log_id,
executed_functions=executed_functions
)
self._inject_secret_keys(local_scope)
exec(function_version['code'], local_scope)
if function_name not in local_scope:
raise ValueError(f"Failed to load function '{function_name}'.")
func = local_scope[function_name]
bound_args = self._bind_function_arguments(func, args, kwargs)
self._validate_input_parameters(function_version, bound_args)
params = bound_args.arguments
self._update_execution_log_params(log_id, params)
output = func(*bound_args.args, **bound_args.kwargs)
end_time = datetime.now()
time_spent = (end_time - start_time).total_seconds()
self._update_execution_log(log_id, output, time_spent, 'success')
# Check and execute triggers for the function
self._execute_triggered_functions(function_name, output, executed_functions, log_id)
return output
except Exception as e:
end_time = datetime.now()
time_spent = (end_time - start_time).total_seconds()
if log_id is not None:
self._update_execution_log(log_id, None, time_spent, 'error', str(e))
raise
def _check_key_dependencies(self, function_version: Dict[str, Any]) -> None:
if 'key_dependencies' in function_version.get('metadata', {}):
for key_name in function_version['metadata']['key_dependencies']:
if key_name not in self.python_func.db.get_all_secret_keys():
raise ValueError(f"Required secret key '{key_name}' not found for function '{function_version['name']}'")
def _inject_secret_keys(self, local_scope: Dict[str, Any]) -> None:
secret_keys = self.python_func.db.get_all_secret_keys()
if secret_keys:
logger.debug(f"Injecting secret keys: {list(secret_keys.keys())}")
local_scope.update(secret_keys)
def _bind_function_arguments(self, func: callable, args: tuple, kwargs: dict) -> inspect.BoundArguments:
sig = inspect.signature(func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
return bound_args
def _validate_input_parameters(self, function_version: Dict[str, Any], bound_args: inspect.BoundArguments) -> None:
input_params = function_version.get('input_parameters', [])
for param in input_params:
if param['name'] not in bound_args.arguments:
raise ValueError(f"Missing required input parameter '{param['name']}' for function '{function_version['name']}'")
def _add_execution_log(self, function_name: str, start_time: datetime, params: Dict[str, Any],
output: Any, time_spent: float, parent_log_id: Optional[int],
triggered_by_log_id: Optional[int], log_type: str, error_message: Optional[str] = None) -> int:
if log_type == 'started':
message = "Execution started."
elif log_type == 'success':
message = "Execution successful."
else:
message = f"Execution failed. Error: {error_message}"
return self.python_func.db.add_log(
function_name=function_name,
message=message,
timestamp=start_time,
params=params,
output=output,
time_spent=time_spent,
parent_log_id=parent_log_id,
triggered_by_log_id=triggered_by_log_id,
log_type=log_type
)
def _update_execution_log(self, log_id: int, output: Any, time_spent: float, log_type: str,
error_message: Optional[str] = None):
message = "Execution successful." if log_type == 'success' else f"Execution failed. Error: {error_message}"
update_data = {
'message': message,
'log_type': log_type
}
if output is not None:
update_data['output'] = output
if time_spent is not None:
update_data['time_spent'] = time_spent
self.python_func.db.update_log(log_id=log_id, **update_data)
def _update_execution_log_params(self, log_id: int, params: Dict[str, Any]) -> None:
self.python_func.db.update_log(log_id=log_id, params=params)
def _execute_triggered_functions(self, function_name: str, output: Any, executed_functions: List[str], log_id: int) -> None:
triggered_function_names = self.python_func.db.get_triggers_for_function(function_name)
logger.info(f"Functions triggered by {function_name}: {triggered_function_names}")
for triggered_function_name in triggered_function_names:
if triggered_function_name in executed_functions:
logger.warning(f"Triggered function '{triggered_function_name}' already executed in this chain. Skipping to prevent recursion.")
continue
try:
logger.info(f"Preparing to execute trigger: {triggered_function_name}")
triggered_function = self.python_func.db.get_function(triggered_function_name)
if triggered_function:
trigger_args, trigger_kwargs = self._prepare_trigger_arguments(triggered_function, output)
logger.info(f"Executing trigger {triggered_function_name} with args: {trigger_args} and kwargs: {trigger_kwargs}")
trigger_output = self.execute(
triggered_function_name,
*trigger_args,
executed_functions=executed_functions.copy(),
parent_log_id=log_id,
triggered_by_log_id=log_id,
**trigger_kwargs
)
logger.info(f"Trigger {triggered_function_name} execution completed. Output: {trigger_output}")
else:
logger.error(f"Triggered function '{triggered_function_name}' not found in the database.")
except Exception as e:
logger.error(f"Error executing triggered function '{triggered_function_name}': {str(e)}")
def _prepare_trigger_arguments(self, triggered_function: Dict[str, Any], output: Any) -> tuple:
triggered_params = triggered_function.get('input_parameters', [])
if triggered_params:
# If the triggered function expects parameters, pass the output as the first parameter
return (output,), {}
else:
# If the triggered function doesn't expect parameters, don't pass any
return (), {}
================================================
FILE: babyagi/functionz/core/framework.py
================================================
# core/framework.py
import os
import sys
import importlib.util
from typing import Optional, List, Dict, Any
from datetime import datetime
import logging
from ..db.db_router import DBRouter
from .execution import FunctionExecutor
from .registration import FunctionRegistrar
logger = logging.getLogger(__name__)
class Functionz:
def __init__(self, db_type='local', **db_kwargs):
self.db = DBRouter(db_type, **db_kwargs)
self.executor = FunctionExecutor(self)
self.registrar = FunctionRegistrar(self)
# Function execution
def execute_function(self, function_name: str, *args, **kwargs):
return self.executor.execute(function_name, *args, **kwargs)
def __getattr__(self, name):
if self.db.get_function(name):
return lambda *args, **kwargs: self.executor.execute(name, *args, **kwargs)
raise AttributeError(f"'PythonFunc' object has no attribute '{name}'")
# Function management
def get_function(self, name: str):
return self.db.get_function(name)
def get_function_versions(self, name: str):
return self.db.get_function_versions(name)
def get_all_functions(self) -> List[Dict[str, Any]]:
return self.db.get_all_functions()
def activate_function_version(self, name: str, version: int) -> None:
self.db.activate_function_version(name, version)
def get_function_imports(self, name: str):
return self.db.get_function_imports(name)
# Function registration (exposing registrar methods)
def register_function(self, *args, **kwargs):
return self.registrar.register_function(*args, **kwargs)
def update_function(self, *args, **kwargs):
return self.registrar.update_function(*args, **kwargs)
def add_function(self, *args, **kwargs):
return self.registrar.add_function(*args, **kwargs)
# Key management
def add_key(self, key_name: str, key_value: str) -> None:
self.db.add_secret_key(key_name, key_value)
def get_all_secret_keys(self, *args, **kwargs):
return self.db.get_all_secret_keys(*args, **kwargs)
# Import management
def get_all_imports(self, *args, **kwargs):
return self.db.get_all_imports(*args, **kwargs)
# Function pack and file loading
def load_function_pack(self, pack_name: str):
packs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'packs')
pack_path = os.path.join(packs_dir, pack_name + '.py')
if not os.path.exists(pack_path):
logger.error(f"Function pack '{pack_name}' not found.")
return
self._load_module_from_path(pack_path, pack_name)
def load_functions_from_file(self, file_path: str):
if not os.path.exists(file_path):
logger.error(f"File '{file_path}' not found.")
return
module_name = os.path.splitext(os.path.basename(file_path))[0]
self._load_module_from_path(file_path, module_name)
def _load_module_from_path(self, path: str, module_name: str):
spec = importlib.util.spec_from_file_location(module_name, path)
module = importlib.util.module_from_spec(spec)
module.func = self
original_sys_path = sys.path[:]
try:
babyagi_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
if babyagi_root not in sys.path:
sys.path.insert(0, babyagi_root)
spec.loader.exec_module(module)
logger.info(f"Loaded module '{module_name}' from '{path}'")
except Exception as e:
logger.error(f"Error loading module '{module_name}' from '{path}': {str(e)}")
import traceback
logger.error(traceback.format_exc())
finally:
sys.path = original_sys_path
# Trigger management
def add_trigger(self, triggered_function_name, triggering_function_name=None):
self.db.add_trigger(triggered_function_name, triggering_function_name)
def get_triggers_for_function(self, function_name: str) -> List[str]:
function_data = self.get_function(function_name)
return function_data.get('triggers', []) if function_data else []
# Logging and display
def get_logs(self, function_name: Optional[str] = None,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None) -> List[Dict[str, Any]]:
return self.db.get_logs(function_name, start_date, end_date)
def display(self):
functions = self.db.get_all_functions()
result = []
for function in functions:
function_info = [
f"Function: {function['name']}",
f" Version: {function['version']}",
f" Created Date: {function['created_date']}",
f" Metadata: {function['metadata']}",
f" Dependencies: {function['dependencies']}",
f" Triggers: {function.get('triggers', [])}",
" Input Parameters:",
]
for param in function['input_parameters']:
function_info.append(f" - {param['name']} ({param['type']})")
function_info.append(" Output Parameters:")
for param in function['output_parameters']:
function_info.append(f" - {param['name']} ({param['type']})")
function_info.append(f" Code:\n{function['code']}")
function_info.append("---")
result.append("\n".join(function_info))
return "\n\n".join(result)
# Create the global 'func' instance
func = Functionz()
================================================
FILE: babyagi/functionz/core/registration.py
================================================
# core/registration.py
from .execution import FunctionExecutor
import inspect
import ast
from typing import Optional, List, Dict, Any, Union
import logging
import json
logger = logging.getLogger(__name__)
class FunctionRegistrar:
def __init__(self, python_func):
self.python_func = python_func
def register_function(self, metadata: Optional[Dict[str, Any]] = None,
imports: Optional[List[str]] = None,
dependencies: Optional[List[str]] = None,
triggers: Optional[List[str]] = None,
key_dependencies: Optional[List[str]] = None):
"""Decorator to register a function."""
def decorator(func):
function_name = func.__name__
source_lines = inspect.getsourcelines(func)[0]
func_start = next(i for i, line in enumerate(source_lines) if line.strip().startswith('def '))
function_code = ''.join(source_lines[func_start:]).strip()
# Store metadata on the function object
func.__pythonfunc_metadata__ = {
'metadata': metadata or {},
'imports': imports or [],
'dependencies': dependencies or [],
'triggers': triggers or [],
'key_dependencies': key_dependencies or []
}
self.add_function(function_name, metadata=metadata, code=function_code,
imports=imports, dependencies=dependencies,
key_dependencies=key_dependencies, triggers=triggers)
def wrapper(*args, **kwargs):
return self.python_func.executor.execute(function_name, *args, **kwargs)
return wrapper
return decorator
def parse_function_parameters(self, code: str):
"""
Parse the input and output parameters of a given function code.
"""
try:
# Parse the source code into an AST
tree = ast.parse(code)
# Find the function definition node
function_def = next(node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef))
# Parse input parameters
input_params = []
for arg in function_def.args.args:
param_type = 'Any'
if arg.annotation:
param_type = ast.unparse(arg.annotation)
input_params.append({'name': arg.arg, 'type': param_type})
# Parse return statement to identify output parameters
output_params = []
returns = [node for node in ast.walk(function_def) if isinstance(node, ast.Return)]
if returns:
return_node = returns[0].value
if isinstance(return_node, ast.Dict):
# If returning a dictionary, treat each key as an output parameter
for key in return_node.keys:
if isinstance(key, ast.Str):
output_params.append({'name': key.s, 'type': 'Any'})
elif isinstance(return_node, (ast.Name, ast.Attribute)):
# If returning a single variable
output_params.append({'name': 'output', 'type': 'Any'})
elif isinstance(return_node, ast.Str):
# If returning a string literal
output_params.append({'name': 'output', 'type': 'str'})
else:
# For other types of returns, use a generic 'output' parameter
output_params.append({'name': 'output', 'type': 'Any'})
return input_params, output_params
except Exception as e:
# print(f"Error parsing function parameters: {str(e)}")
return [], []
def parse_import(self, imp):
try:
#print("Attempt to parse the string as JSON")
#print(imp.type())
parsed = json.loads(imp)
#print("Parsed string as JSON")
return parsed
except json.JSONDecodeError:
print("Failed to parse the string as JSON")
# If it fails, return the original string (it's a simple import)
return imp
# Register imports helper function
def register_imports(self, imports):
print(f"Registering imports: {imports}")
if isinstance(imports, list):
for imp in imports:
self.process_single_import(imp)
elif isinstance(imports, dict):
self.process_single_import(imports)
def process_single_import(self, imp):
if isinstance(imp, str):
self.python_func.db.add_import(imp, 'external', lib=None)
elif isinstance(imp, dict):
name = imp.get('name')
lib = imp.get('lib')
if name:
self.python_func.db.add_import(name, 'external', lib=lib)
else:
print(f"Unsupported import format: {imp}")
def process_imports(self, imports):
import_names = []
if imports:
if isinstance(imports, list):
for imp in imports:
if isinstance(imp, str):
import_names.append(imp)
elif isinstance(imp, dict):
import_name = imp.get('name')
if import_name:
import_names.append(import_name)
elif isinstance(imports, dict):
import_name = imports.get('name')
if import_name:
import_names.append(import_name)
return import_names
def function_has_no_changes(self, name, code, metadata, import_names, dependencies, triggers):
existing_function = self.python_func.db.get_function(name)
if not existing_function:
return False # Function does not exist, so changes are needed
existing_code = existing_function.get('code')
existing_metadata = existing_function.get('metadata', {})
existing_description = existing_metadata.get('description')
existing_imports = existing_function.get('imports') or []
existing_dependencies = existing_function.get('dependencies') or []
existing_triggers = existing_function.get('triggers') or []
new_description = metadata.get('description') if metadata else None
if (existing_code == code and
existing_description == new_description and
set(existing_imports) == set(import_names) and
set(existing_dependencies) == set(dependencies) and
set(existing_triggers) == set(triggers)):
return True # No changes
else:
return False # Changes detected
def add_function(self, name: str, metadata: Optional[Dict[str, Any]] = None,
code: Optional[str] = None, imports: Optional[List[Union[str, Dict[str, str]]]] = None,
dependencies: Optional[List[str]] = None, triggers: Optional[List[str]] = None,
key_dependencies: Optional[List[str]] = None,
input_parameters: Optional[List[Dict[str, str]]] = None,
output_parameters: Optional[List[Dict[str, str]]] = None) -> None:
if code:
# Parse input and output parameters if not provided
if input_parameters is None or output_parameters is None:
parsed_input, parsed_output = self.parse_function_parameters(code)
input_parameters = input_parameters or parsed_input
output_parameters = output_parameters or parsed_output
# Process imports
import_names = self.process_imports(imports)
# Ensure lists are not None
dependencies = dependencies or []
triggers = triggers or []
# Check for changes
if self.function_has_no_changes(name, code, metadata, import_names, dependencies, triggers):
#print(f"Function {name} has no changes.")
return
# Register imports
if imports:
self.register_imports(imports)
# Add or update the function in the database
existing_function = self.python_func.db.get_function(name)
if existing_function:
# Function exists, update it
self.python_func.db.update_function(
name, code=code, metadata=metadata, dependencies=dependencies,
input_parameters=input_parameters, output_parameters=output_parameters,
imports=import_names, triggers=triggers
)
else:
# Function does not exist, add it
self.python_func.db.add_function(
name, code=code, metadata=metadata, dependencies=dependencies,
input_parameters=input_parameters, output_parameters=output_parameters,
imports=import_names, triggers=triggers
)
if key_dependencies:
print(f"Function {name} requires keys: {key_dependencies}")
metadata = metadata or {}
metadata['key_dependencies'] = key_dependencies
if self.python_func.db.get_function('function_added_or_updated'):
try:
action = 'updated' if existing_function else 'added'
self.python_func.executor.execute(function_name='function_added_or_updated', action=action, triggered_function_name=name)
except Exception as e:
logger.error(f"Error executing trigger function 'function_added_or_updated': {str(e)}")
def update_function(self, name: str, code: Optional[str] = None, imports: Optional[List[Union[str, Dict[str, str]]]] = None,
metadata: Optional[Dict[str, Any]] = None,
dependencies: Optional[List[str]] = None,
triggers: Optional[List[str]] = None,
key_dependencies: Optional[List[str]] = None,
input_parameters: Optional[List[Dict[str, str]]] = None,
output_parameters: Optional[List[Dict[str, str]]] = None) -> None:
if code:
# Parse input and output parameters if not provided
if input_parameters is None or output_parameters is None:
parsed_input, parsed_output = self.parse_function_parameters(code)
input_parameters = input_parameters or parsed_input
output_parameters = output_parameters or parsed_output
# Process imports
import_names = self.process_imports(imports)
# Ensure lists are not None
dependencies = dependencies or []
triggers = triggers or []
# Check for changes
if self.function_has_no_changes(name, code, metadata, import_names, dependencies, triggers):
# print(f"Function {name} has no changes.")
return
# Update the function in the database
self.python_func.db.update_function(
name, code=code, metadata=metadata, dependencies=dependencies,
input_parameters=input_parameters, output_parameters=output_parameters,
imports=import_names, triggers=triggers
)
if key_dependencies:
metadata = metadata or {}
metadata['key_dependencies'] = key_dependencies
# Register imports
if imports:
self.register_imports(imports)
if self.python_func.db.get_function('function_added_or_updated'):
try:
self.python_func.executor.execute(function_name='function_added_or_updated', action='updated', triggered_function_name=name)
except Exception as e:
logger.error(f"Error executing trigger function 'function_added_or_updated': {s
gitextract_nxx39r7_/ ├── .gitignore ├── .replit ├── CNAME ├── CODE_READINESS_ANALYSIS.md ├── MANIFEST.in ├── README.md ├── babyagi/ │ ├── __init__.py │ ├── api/ │ │ └── __init__.py │ ├── dashboard/ │ │ ├── __init__.py │ │ ├── static/ │ │ │ ├── css/ │ │ │ │ └── style.css │ │ │ └── js/ │ │ │ ├── dashboard.js │ │ │ ├── function_details.js │ │ │ ├── function_graph.js │ │ │ ├── log_dashboard.js │ │ │ └── log_graph.js │ │ └── templates/ │ │ ├── base.html │ │ ├── chat.html │ │ ├── function_details.html │ │ ├── function_graph.html │ │ ├── function_graph_3d.html │ │ ├── function_graph_mermaid.html │ │ ├── index.html │ │ ├── log_page.html │ │ ├── log_relationship_graph.html │ │ └── logs_dashboard.html │ └── functionz/ │ ├── __init__.py │ ├── core/ │ │ ├── __init__.py │ │ ├── execution.py │ │ ├── framework.py │ │ └── registration.py │ ├── db/ │ │ ├── __init__.py │ │ ├── base_db.py │ │ ├── db_router.py │ │ ├── local_db.py │ │ └── models.py │ └── packs/ │ ├── default/ │ │ ├── ai_functions.py │ │ ├── default_functions.py │ │ ├── function_calling_chat.py │ │ └── os.py │ ├── drafts/ │ │ ├── choose_or_create_function.py │ │ ├── code_writing_functions.py │ │ ├── generate_function.py │ │ ├── react_agent.py │ │ ├── self_build.py │ │ ├── self_build2.py │ │ └── user_db.py │ └── plugins/ │ ├── airtable.py │ ├── augie.py │ ├── e2b.py │ ├── firecrawl.py │ ├── harmonic.py │ ├── payman.py │ ├── serpapi.py │ ├── voilanorbert.py │ └── wokelo.py ├── examples/ │ ├── custom_flask_example.py │ ├── custom_route_example.py │ ├── quickstart_example.py │ ├── self_build_example.py │ ├── simple_example.py │ └── trigger_example.py ├── main.py ├── pyproject.toml ├── requirements.txt └── setup.py
SYMBOL INDEX (273 symbols across 43 files)
FILE: babyagi/__init__.py
function get_func_instance (line 16) | def get_func_instance():
function create_app (line 19) | def create_app(dashboard_route='/dashboard'):
function register_function (line 48) | def register_function(*args, **kwargs):
function load_functions (line 61) | def load_functions(pack_name_or_path):
function use_blueprints (line 82) | def use_blueprints(app, dashboard_route='/dashboard'):
function __getattr__ (line 112) | def __getattr__(name):
FILE: babyagi/api/__init__.py
function create_api_blueprint (line 13) | def create_api_blueprint():
FILE: babyagi/dashboard/__init__.py
function create_dashboard (line 9) | def create_dashboard(func_instance, dashboard_route):
FILE: babyagi/dashboard/static/js/dashboard.js
function filterTable (line 8) | function filterTable() {
function sortTable (line 36) | function sortTable(key) {
function fetchLogs (line 63) | async function fetchLogs(functionName) {
function updateLogsAsync (line 80) | function updateLogsAsync(tasks) {
function populateDashboard (line 99) | async function populateDashboard(functions) {
function formatParams (line 158) | function formatParams(params) {
function formatList (line 167) | function formatList(items, className, dashboardRoute) {
function fetchFunctionsAndPopulate (line 182) | async function fetchFunctionsAndPopulate() {
FILE: babyagi/dashboard/static/js/function_details.js
function getApiRoute (line 8) | function getApiRoute(routeName, ...args) {
function loadFunctionDetails (line 33) | function loadFunctionDetails() {
function loadFunctionLogs (line 54) | function loadFunctionLogs() {
function initCodeEditor (line 80) | function initCodeEditor() {
function displayFunctionDetails (line 124) | function displayFunctionDetails() {
function createExecutionForm (line 176) | function createExecutionForm() {
function updateFunction (line 215) | function updateFunction() {
function executeFunction (line 241) | function executeFunction() {
function toggleVersionHistory (line 333) | function toggleVersionHistory() {
function loadFunctionVersions (line 346) | function loadFunctionVersions() {
function activateVersion (line 363) | function activateVersion(version) {
FILE: babyagi/dashboard/static/js/function_graph.js
function showFunctionOverlay (line 151) | function showFunctionOverlay(functionName) {
function showTab (line 184) | function showTab(tabName, functionName) {
function showCode (line 205) | function showCode(functionName) {
function showLogs (line 217) | function showLogs(functionName) {
function closeOverlay (line 229) | function closeOverlay() {
FILE: babyagi/dashboard/static/js/log_dashboard.js
function populateFilters (line 7) | async function populateFilters() {
function buildLogTree (line 51) | function buildLogTree(logs) {
function renderLogs (line 80) | function renderLogs() {
function renderTable (line 86) | function renderTable() {
function renderLogRow (line 96) | function renderLogRow(tableBody, log, depth, parentRowId) {
function toggleChildRows (line 151) | function toggleChildRows(parentLogId) {
function renderGrid (line 172) | function renderGrid() {
function renderLogCard (line 182) | function renderLogCard(container, log, depth) {
function capitalizeFirstLetter (line 207) | function capitalizeFirstLetter(string) {
function sortLogs (line 212) | function sortLogs(key) {
function applyFilters (line 250) | function applyFilters() {
function resetFilters (line 292) | function resetFilters() {
FILE: babyagi/dashboard/static/js/log_graph.js
function loadLogs (line 5) | async function loadLogs() {
function renderGraph (line 19) | function renderGraph(logs) {
FILE: babyagi/functionz/core/execution.py
class FunctionExecutor (line 11) | class FunctionExecutor:
method __init__ (line 12) | def __init__(self, python_func):
method _install_external_dependency (line 15) | def _install_external_dependency(self, package_name: str, imp_name: st...
method _resolve_dependencies (line 22) | def _resolve_dependencies(self, function_version: Dict[str, Any], loca...
method _create_function_wrapper (line 50) | def _create_function_wrapper(self, func: callable, func_name: str, par...
method execute (line 55) | def execute(
method _check_key_dependencies (line 152) | def _check_key_dependencies(self, function_version: Dict[str, Any]) ->...
method _inject_secret_keys (line 158) | def _inject_secret_keys(self, local_scope: Dict[str, Any]) -> None:
method _bind_function_arguments (line 164) | def _bind_function_arguments(self, func: callable, args: tuple, kwargs...
method _validate_input_parameters (line 170) | def _validate_input_parameters(self, function_version: Dict[str, Any],...
method _add_execution_log (line 176) | def _add_execution_log(self, function_name: str, start_time: datetime,...
method _update_execution_log (line 197) | def _update_execution_log(self, log_id: int, output: Any, time_spent: ...
method _update_execution_log_params (line 212) | def _update_execution_log_params(self, log_id: int, params: Dict[str, ...
method _execute_triggered_functions (line 217) | def _execute_triggered_functions(self, function_name: str, output: Any...
method _prepare_trigger_arguments (line 247) | def _prepare_trigger_arguments(self, triggered_function: Dict[str, Any...
FILE: babyagi/functionz/core/framework.py
class Functionz (line 16) | class Functionz:
method __init__ (line 17) | def __init__(self, db_type='local', **db_kwargs):
method execute_function (line 23) | def execute_function(self, function_name: str, *args, **kwargs):
method __getattr__ (line 26) | def __getattr__(self, name):
method get_function (line 32) | def get_function(self, name: str):
method get_function_versions (line 35) | def get_function_versions(self, name: str):
method get_all_functions (line 38) | def get_all_functions(self) -> List[Dict[str, Any]]:
method activate_function_version (line 41) | def activate_function_version(self, name: str, version: int) -> None:
method get_function_imports (line 44) | def get_function_imports(self, name: str):
method register_function (line 48) | def register_function(self, *args, **kwargs):
method update_function (line 51) | def update_function(self, *args, **kwargs):
method add_function (line 54) | def add_function(self, *args, **kwargs):
method add_key (line 58) | def add_key(self, key_name: str, key_value: str) -> None:
method get_all_secret_keys (line 61) | def get_all_secret_keys(self, *args, **kwargs):
method get_all_imports (line 65) | def get_all_imports(self, *args, **kwargs):
method load_function_pack (line 69) | def load_function_pack(self, pack_name: str):
method load_functions_from_file (line 79) | def load_functions_from_file(self, file_path: str):
method _load_module_from_path (line 87) | def _load_module_from_path(self, path: str, module_name: str):
method add_trigger (line 108) | def add_trigger(self, triggered_function_name, triggering_function_nam...
method get_triggers_for_function (line 111) | def get_triggers_for_function(self, function_name: str) -> List[str]:
method get_logs (line 116) | def get_logs(self, function_name: Optional[str] = None,
method display (line 121) | def display(self):
FILE: babyagi/functionz/core/registration.py
class FunctionRegistrar (line 12) | class FunctionRegistrar:
method __init__ (line 13) | def __init__(self, python_func):
method register_function (line 16) | def register_function(self, metadata: Optional[Dict[str, Any]] = None,
method parse_function_parameters (line 45) | def parse_function_parameters(self, code: str):
method parse_import (line 89) | def parse_import(self, imp):
method register_imports (line 102) | def register_imports(self, imports):
method process_single_import (line 110) | def process_single_import(self, imp):
method process_imports (line 121) | def process_imports(self, imports):
method function_has_no_changes (line 138) | def function_has_no_changes(self, name, code, metadata, import_names, ...
method add_function (line 161) | def add_function(self, name: str, metadata: Optional[Dict[str, Any]] =...
method update_function (line 220) | def update_function(self, name: str, code: Optional[str] = None, impor...
FILE: babyagi/functionz/db/base_db.py
class BaseDB (line 5) | class BaseDB(ABC):
method add_function (line 8) | def add_function(self, name: str, code: str, metadata: Optional[Dict[s...
method get_function (line 15) | def get_function(self, name: str) -> Optional[Dict[str, Any]]:
method get_all_functions (line 19) | def get_all_functions(self) -> List[Dict[str, Any]]:
method update_function (line 23) | def update_function(self, name: str, code: Optional[str] = None,
method remove_function (line 31) | def remove_function(self, name: str) -> None:
method get_function_versions (line 35) | def get_function_versions(self, name: str) -> List[Dict[str, Any]]:
method activate_function_version (line 39) | def activate_function_version(self, name: str, version: int) -> None:
method add_import (line 44) | def add_import(self, name: str, source: str, lib: Optional[str] = None...
method get_all_imports (line 48) | def get_all_imports(self) -> List[Dict[str, Any]]:
method add_log (line 53) | def add_log(self, function_name: str, message: str, timestamp: datetime,
method get_logs (line 60) | def get_logs(self, function_name: Optional[str] = None,
FILE: babyagi/functionz/db/db_router.py
class ImportResult (line 9) | class ImportResult:
method __init__ (line 10) | def __init__(self, name: str, source: str):
class DBRouter (line 14) | class DBRouter(BaseDB):
method __init__ (line 15) | def __init__(self, db_type: str = 'local', **kwargs):
method session_scope (line 22) | def session_scope(self):
method add_function (line 27) | def add_function(self, name: str, code: str, metadata: Optional[Dict[s...
method update_function (line 36) | def update_function(self, name: str, code: Optional[str] = None,
method get_function (line 58) | def get_function(self, name: str) -> Optional[Dict[str, Any]]:
method get_all_functions (line 78) | def get_all_functions(self) -> List[Dict[str, Any]]:
method remove_function (line 98) | def remove_function(self, name: str) -> None:
method get_function_versions (line 104) | def get_function_versions(self, name: str) -> List[Dict[str, Any]]:
method activate_function_version (line 124) | def activate_function_version(self, name: str, version: int) -> None:
method add_import (line 132) | def add_import(self, name: str, source: str, lib: Optional[str] = None...
method get_all_imports (line 136) | def get_all_imports(self) -> List[Dict[str, Any]]:
method get_function_imports (line 141) | def get_function_imports(self, function_name: str) -> List[Dict[str, A...
method add_log (line 153) | def add_log(self, function_name: str, message: str, timestamp: datetime,
method update_log (line 174) | def update_log(self, log_id: int, **kwargs) -> None:
method update_log_params (line 183) | def update_log_params(self, log_id: int, params: Dict[str, Any]) -> None:
method get_logs (line 193) | def get_logs(self, function_name: Optional[str] = None,
method get_log_bundle (line 215) | def get_log_bundle(self, log_id: int) -> List[Dict[str, Any]]:
method add_secret_key (line 270) | def add_secret_key(self, key_name: str, key_value: str) -> None:
method get_secret_key (line 278) | def get_secret_key(self, key_name: str) -> Optional[str]:
method get_all_secret_keys (line 283) | def get_all_secret_keys(self) -> Dict[str, str]:
method add_trigger (line 289) | def add_trigger(self, triggered_function_name: str, triggering_functio...
method get_triggers_for_function (line 293) | def get_triggers_for_function(self, function_name: str) -> List[str]:
FILE: babyagi/functionz/db/local_db.py
class LocalDB (line 12) | class LocalDB:
method __init__ (line 13) | def __init__(self, db_path='sqlite:///funztionz.db'):
method session_scope (line 19) | def session_scope(self):
method serialize_for_json (line 30) | def serialize_for_json(self, obj):
method get_function (line 45) | def get_function(self, session, name):
method get_active_version (line 48) | def get_active_version(self, session, function):
method get_all_functions (line 53) | def get_all_functions(self, session):
method add_or_update_function (line 58) | def add_or_update_function(self, session, name, code, metadata, depend...
method add_import (line 103) | def add_import(self, session, name, source, lib=None):
method add_log (line 111) | def add_log(self, session, function_name, message, timestamp, params, ...
method update_log (line 135) | def update_log(self, session, log_id: int, **kwargs) -> None:
method update_log_params (line 151) | def update_log_params(self, session, log_id: int, params) -> None:
method get_log (line 161) | def get_log(self, session, log_id: int):
method get_child_logs (line 171) | def get_child_logs(self, session, parent_id: int):
method get_logs (line 181) | def get_logs(self, session, function_name=None, start_date=None, end_d...
method get_log_bundle (line 199) | def get_log_bundle(self, session, log_id):
method add_secret_key (line 235) | def add_secret_key(self, session, function_id, key_name, key_value):
method add_secret_key (line 248) | def add_secret_key(self, session, key_name, key_value):
method get_secret_key (line 253) | def get_secret_key(self, session, key_name):
method get_all_secret_keys (line 257) | def get_all_secret_keys(self, session):
FILE: babyagi/functionz/db/models.py
function get_or_create_key (line 17) | def get_or_create_key():
class Function (line 44) | class Function(Base):
class FunctionVersion (line 50) | class FunctionVersion(Base):
class Import (line 70) | class Import(Base):
class Log (line 79) | class Log(Base):
class SecretKey (line 110) | class SecretKey(Base):
method value (line 117) | def value(self):
method value (line 127) | def value(self, plaintext_value):
FILE: babyagi/functionz/packs/default/ai_functions.py
function gpt_call (line 10) | def gpt_call(prompt: str) -> str:
function description_writer (line 20) | def description_writer(function_code: str) -> str:
function ai_description_generator (line 34) | def ai_description_generator(function_name: str) -> None:
function generate_missing_descriptions (line 64) | def generate_missing_descriptions() -> None:
function embed_input (line 84) | def embed_input(input_text: str, model: str = "text-embedding-ada-002",
function embed_function_description (line 116) | def embed_function_description(function: str) -> None:
function find_similar_function (line 172) | def find_similar_function(description: str, top_n: int = 3):
function generate_missing_embeddings (line 216) | def generate_missing_embeddings() -> None:
function choose_function (line 254) | def choose_function(prompt: str) -> str:
FILE: babyagi/functionz/packs/default/default_functions.py
function execute_function_wrapper (line 8) | def execute_function_wrapper(function_name: str, *args, **kwargs):
function add_new_function (line 35) | def add_new_function(
function function_added_or_updated (line 63) | def function_added_or_updated(action=None, triggered_function_name=None):
function add_key_wrapper (line 79) | def add_key_wrapper(key_name: str, key_value: str):
function get_function_versions_wrapper (line 83) | def get_function_versions_wrapper(name: str):
function get_function_wrapper (line 87) | def get_function_wrapper(name: str):
function get_all_functions_wrapper (line 92) | def get_all_functions_wrapper():
function activate_function_version_wrapper (line 96) | def activate_function_version_wrapper(name: str, version: int):
function display_functions_wrapper (line 100) | def display_functions_wrapper():
function get_logs_wrapper (line 107) | def get_logs_wrapper(function_name: str = None, start_date: datetime = N...
function add_trigger_wrapper (line 112) | def add_trigger_wrapper(triggered_function_name: str, triggering_functio...
function get_all_secret_keys (line 117) | def get_all_secret_keys():
function get_all_imports_wrapper (line 122) | def get_all_imports_wrapper():
FILE: babyagi/functionz/packs/default/function_calling_chat.py
function chat_with_functions (line 16) | def chat_with_functions(chat_history, available_function_names) -> str:
FILE: babyagi/functionz/packs/default/os.py
function get_full_directory_contents_cleaned (line 5) | def get_full_directory_contents_cleaned():
FILE: babyagi/functionz/packs/drafts/choose_or_create_function.py
function choose_or_create_function (line 18) | def choose_or_create_function(user_input: str) -> dict:
FILE: babyagi/functionz/packs/drafts/code_writing_functions.py
function check_existing_functions (line 7) | def check_existing_functions(user_input):
function break_down_task (line 77) | def break_down_task(user_input):
function decide_imports_and_apis (line 154) | def decide_imports_and_apis(context):
function get_functions_that_depend_on (line 224) | def get_functions_that_depend_on(function_name):
function generate_function_code (line 237) | def generate_function_code(function, context):
function create_function (line 345) | def create_function(function, context):
function generate_functions (line 389) | def generate_functions(function_breakdown, context):
function run_final_function (line 410) | def run_final_function(function_name, *args, **kwargs):
function extract_function_parameters (line 418) | def extract_function_parameters(user_input, function_name):
function process_user_input (line 514) | def process_user_input(user_input):
FILE: babyagi/functionz/packs/drafts/generate_function.py
function fetch_existing_functions (line 12) | def fetch_existing_functions(description: str) -> dict:
function analyze_internal_functions (line 44) | def analyze_internal_functions(description: str, existing_functions: str...
function fetch_function_codes (line 147) | def fetch_function_codes(function_names, intermediate_steps):
function determine_required_external_apis (line 186) | def determine_required_external_apis(description: str, intermediate_step...
function handle_api_documentation (line 286) | def handle_api_documentation(api_name: str, description: str, intermedia...
function generate_final_function_code (line 519) | def generate_final_function_code(description: str, reusable_function_cod...
function add_function_to_database (line 690) | def add_function_to_database(combined_final: dict, intermediate_steps: l...
function generate_function_from_description (line 739) | def generate_function_from_description(description: str) -> dict:
FILE: babyagi/functionz/packs/drafts/react_agent.py
function react_agent (line 11) | def react_agent(input_text) -> str:
FILE: babyagi/functionz/packs/drafts/self_build.py
function generate_queries (line 11) | def generate_queries(user_description, X=3, max_retries=3):
function self_build (line 80) | def self_build(user_description, X=3):
FILE: babyagi/functionz/packs/drafts/self_build2.py
function generate_and_process_queries (line 24) | def generate_and_process_queries(user_description: str, num_queries: int...
FILE: babyagi/functionz/packs/drafts/user_db.py
function get_user_db_class (line 9) | def get_user_db_class():
function create_database (line 42) | def create_database(db_name: str, db_type: str = 'sqlite', **kwargs):
function list_databases (line 73) | def list_databases():
function delete_database (line 89) | def delete_database(db_name: str, db_type: str = 'sqlite', **kwargs):
function create_table (line 109) | def create_table(db_name: str, table_name: str, columns: str):
function list_tables (line 159) | def list_tables(db_name: str):
function get_table (line 175) | def get_table(db_name: str, table_name: str):
function update_table (line 207) | def update_table(db_name: str, table_name: str, new_columns: str):
function delete_table (line 269) | def delete_table(db_name: str, table_name: str):
function create_record (line 301) | def create_record(db_name: str, table_name: str, data: list):
function read_records (line 334) | def read_records(db_name: str, table_name: str, filters: dict = None):
function update_record (line 357) | def update_record(db_name: str, table_name: str, record_id: int, data: j...
function delete_record (line 397) | def delete_record(db_name: str, table_name: str, record_id: int):
function convert_value (line 422) | def convert_value(value, target_type):
FILE: babyagi/functionz/packs/plugins/airtable.py
function init_airtable (line 9) | def init_airtable(base_id, table_name):
function create_record (line 26) | def create_record(base_id, table_name, record_data):
function get_record_by_id (line 43) | def get_record_by_id(base_id, table_name, record_id):
function update_record (line 60) | def update_record(base_id, table_name, record_id, updated_fields):
function delete_record (line 78) | def delete_record(base_id, table_name, record_id):
function get_all_records (line 95) | def get_all_records(base_id, table_name, max_records=100, sort_by=None):
function batch_upsert_records (line 113) | def batch_upsert_records(base_id, table_name, records, key_fields):
function batch_create_records (line 131) | def batch_create_records(base_id, table_name, records):
function batch_delete_records (line 148) | def batch_delete_records(base_id, table_name, record_ids):
function get_dynamic_records (line 168) | def get_dynamic_records(base_id, table_name, max_records=100, search_que...
FILE: babyagi/functionz/packs/plugins/augie.py
function generate_augie_params (line 7) | def generate_augie_params(user_input, voice_id="29vD33N1CtxCmqQRPOHJ"):
function create_augie (line 44) | def create_augie(params):
function get_augie_status (line 68) | def get_augie_status(augie_id):
function create_and_wait_for_video (line 95) | def create_and_wait_for_video(user_input, timeout=300, interval=10):
FILE: babyagi/functionz/packs/plugins/e2b.py
function execute_code_in_sandbox (line 8) | def execute_code_in_sandbox(code: str):
function chat_with_llm_and_execute (line 40) | def chat_with_llm_and_execute(user_message: str):
function save_chart (line 70) | def save_chart(base64_png: str, filename: str = "chart.png"):
function e2b_llm_to_chart (line 93) | def e2b_llm_to_chart(user_message: str):
FILE: babyagi/functionz/packs/plugins/firecrawl.py
function crawl_website (line 7) | def crawl_website(url: str, limit: int = 100, formats: list = ["markdown...
function check_crawl_status (line 32) | def check_crawl_status(crawl_id: str):
function scrape_website (line 53) | def scrape_website(url: str, formats: list = ["markdown", "html"]):
FILE: babyagi/functionz/packs/plugins/harmonic.py
function harmonic_enrich_company (line 9) | def harmonic_enrich_company(identifier):
function harmonic_search_companies (line 38) | def harmonic_search_companies(keywords, include_ids_only=False):
function harmonic_enrich_person_by_id (line 58) | def harmonic_enrich_person_by_id(person_id):
FILE: babyagi/functionz/packs/plugins/payman.py
function store_payman_api_keys (line 8) | def store_payman_api_keys():
function create_task (line 21) | def create_task(title: str, description: str, payout: int, currency: str...
function get_task_by_id (line 56) | def get_task_by_id(task_id: str, real_money: bool = False):
function get_all_tasks (line 82) | def get_all_tasks(page: int = 0, limit: int = 20, real_money: bool = Fal...
function get_task_submissions (line 111) | def get_task_submissions(task_id: str, statuses: list = None, page: int ...
function approve_task_submission (line 145) | def approve_task_submission(submission_id: str, real_money: bool = False):
function reject_task_submission (line 171) | def reject_task_submission(submission_id: str, rejection_reason: str, re...
FILE: babyagi/functionz/packs/plugins/serpapi.py
function serpapi_search_v2 (line 8) | def serpapi_search_v2(query: str, engine: str = "google", location: str ...
FILE: babyagi/functionz/packs/plugins/voilanorbert.py
function search_contact_by_name_domain (line 6) | def search_contact_by_name_domain(name, domain):
function search_contact_by_domain (line 61) | def search_contact_by_domain(domain):
FILE: babyagi/functionz/packs/plugins/wokelo.py
function get_auth_token (line 6) | def get_auth_token():
function create_industry_snapshot (line 36) | def create_industry_snapshot(access_token: str, industry_name: str):
function check_report_status (line 56) | def check_report_status(access_token: str, report_id: str):
function download_report (line 75) | def download_report(access_token: str, report_id: str, file_type: str = ...
function generate_industry_report (line 95) | def generate_industry_report(industry_name: str, file_type: str = 'pdf'):
FILE: examples/custom_flask_example.py
function integrated_function (line 13) | def integrated_function():
function home (line 19) | def home():
FILE: examples/custom_route_example.py
function another_custom_function (line 8) | def another_custom_function():
function home (line 12) | def home():
FILE: examples/quickstart_example.py
function home (line 14) | def home():
FILE: examples/self_build_example.py
function home (line 15) | def home():
FILE: examples/simple_example.py
function world (line 10) | def world():
function hello_world (line 14) | def hello_world():
function home (line 21) | def home():
FILE: examples/trigger_example.py
function function_a (line 4) | def function_a():
function function_b (line 9) | def function_b(input_data):
function home (line 16) | def home():
FILE: main.py
function home (line 12) | def home():
FILE: setup.py
function parse_requirements (line 9) | def parse_requirements(filename):
Condensed preview — 65 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (416K chars).
[
{
"path": ".gitignore",
"chars": 3129,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": ".replit",
"chars": 387,
"preview": "entrypoint = \"main.py\"\nmodules = [\"python-3.11\"]\n\n[nix]\nchannel = \"stable-24_05\"\n\n[unitTest]\nlanguage = \"python3\"\n\n[gitH"
},
{
"path": "CNAME",
"chars": 11,
"preview": "babyagi.org"
},
{
"path": "CODE_READINESS_ANALYSIS.md",
"chars": 10895,
"preview": "# BabyAGI Code Readiness Analysis\n\n**Analysis Date:** January 2026\n**Repository:** yoheinakajima/babyagi\n**Verdict:** **"
},
{
"path": "MANIFEST.in",
"chars": 551,
"preview": "# Include the README and LICENSE\ninclude README.md\ninclude LICENSE\n\n# Include all package directories and their contents"
},
{
"path": "README.md",
"chars": 10814,
"preview": "# BabyAGI\n\n> [!NOTE]\n> The original BabyAGI from March 2023 introduced task planning as a method for developing autonomo"
},
{
"path": "babyagi/__init__.py",
"chars": 5109,
"preview": "# babyagi/__init__.py\n\nfrom flask import Flask, g\nfrom .functionz.core.framework import Functionz\nfrom .dashboard import"
},
{
"path": "babyagi/api/__init__.py",
"chars": 7635,
"preview": "# babyagi/api/__init__.py\n\nfrom flask import Blueprint, jsonify, request, g\nfrom datetime import datetime\nfrom io import"
},
{
"path": "babyagi/dashboard/__init__.py",
"chars": 5719,
"preview": "# babyagi/dashboard/__init__.py\n\nfrom flask import Blueprint, render_template, g, send_from_directory\nimport logging\nimp"
},
{
"path": "babyagi/dashboard/static/css/style.css",
"chars": 7529,
"preview": "/* static/css/style.css */\n\n/* Global Variables */\n:root {\n /* Color variables */\n --primary-color: #3498db;\n -"
},
{
"path": "babyagi/dashboard/static/js/dashboard.js",
"chars": 8144,
"preview": "/* static/js/dashboard.js */\n\n// Assume that dashboardRoute, apiFunctionsUrl, and apiLogsUrl are defined in the HTML tem"
},
{
"path": "babyagi/dashboard/static/js/function_details.js",
"chars": 14000,
"preview": "// At the top of function_details.js\n\n\n// Ensure apiRoutes is defined\nwindow.apiRoutes = window.apiRoutes || {};\n\n// Hel"
},
{
"path": "babyagi/dashboard/static/js/function_graph.js",
"chars": 7774,
"preview": "// function_graph.js\nlet cy;\n\ndocument.addEventListener('DOMContentLoaded', () => {\n cy = cytoscape({\n contain"
},
{
"path": "babyagi/dashboard/static/js/log_dashboard.js",
"chars": 10553,
"preview": "let currentSort = { key: 'timestamp', direction: 'desc' }; // Set default sort to 'timestamp' descending\nlet allLogs = ["
},
{
"path": "babyagi/dashboard/static/js/log_graph.js",
"chars": 2396,
"preview": "document.addEventListener('DOMContentLoaded', () => {\n loadLogs();\n});\n\nasync function loadLogs() {\n try {\n "
},
{
"path": "babyagi/dashboard/templates/base.html",
"chars": 1493,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n {% block head %}\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" conten"
},
{
"path": "babyagi/dashboard/templates/chat.html",
"chars": 19874,
"preview": "{% extends \"base.html\" %}\n{% block title %}Chat Application{% endblock %}\n{% block breadcrumb %}\n <div class=\"breadcr"
},
{
"path": "babyagi/dashboard/templates/function_details.html",
"chars": 3450,
"preview": "{% extends \"base.html\" %}\n{% block title %}Function Details: {{ function_name }}{% endblock %}\n{% block head %}\n <lin"
},
{
"path": "babyagi/dashboard/templates/function_graph.html",
"chars": 19891,
"preview": "{% extends \"base.html\" %}\n{% block title %}Function Relationship Graph{% endblock %}\n\n{% block head %}\n {{ super() }}"
},
{
"path": "babyagi/dashboard/templates/function_graph_3d.html",
"chars": 4466,
"preview": "{% extends \"base.html\" %}\n{% block title %}3D Function Relationship Graph{% endblock %}\n{% block head %}\n {{ super() "
},
{
"path": "babyagi/dashboard/templates/function_graph_mermaid.html",
"chars": 10469,
"preview": "{% extends \"base.html\" %}\n{% block title %}Function Relationship Mermaid Graph{% endblock %}\n\n{% block head %}\n {{ su"
},
{
"path": "babyagi/dashboard/templates/index.html",
"chars": 1643,
"preview": "{% extends \"base.html\" %}\n{% block title %}Function Dashboard{% endblock %}\n{% block content %}\n <h1>Function Dashboa"
},
{
"path": "babyagi/dashboard/templates/log_page.html",
"chars": 6352,
"preview": "{% extends \"base.html\" %}\n{% block title %}Log Details{% endblock %}\n{% block content %}\n<h1>Log Details for Log ID {{ l"
},
{
"path": "babyagi/dashboard/templates/log_relationship_graph.html",
"chars": 1656,
"preview": "{% extends \"base.html\" %}\n{% block title %}Log Relationship Graph{% endblock %}\n{% block breadcrumb %}\n <div class=\"b"
},
{
"path": "babyagi/dashboard/templates/logs_dashboard.html",
"chars": 2887,
"preview": "{% extends \"base.html\" %}\n{% block title %}Logs Dashboard{% endblock %}\n{% block breadcrumb %}\n <div class=\"breadcrum"
},
{
"path": "babyagi/functionz/__init__.py",
"chars": 53,
"preview": "from .core.framework import func\n\n__all__ = ['func']\n"
},
{
"path": "babyagi/functionz/core/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "babyagi/functionz/core/execution.py",
"chars": 11373,
"preview": "import subprocess\nimport sys\nimport importlib\nimport inspect\nfrom typing import Any, Dict, List, Optional\nfrom datetime "
},
{
"path": "babyagi/functionz/core/framework.py",
"chars": 5632,
"preview": "# core/framework.py\n\nimport os\nimport sys\nimport importlib.util\nfrom typing import Optional, List, Dict, Any\nfrom dateti"
},
{
"path": "babyagi/functionz/core/registration.py",
"chars": 11896,
"preview": "# core/registration.py\n\nfrom .execution import FunctionExecutor\nimport inspect\nimport ast\nfrom typing import Optional, L"
},
{
"path": "babyagi/functionz/db/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "babyagi/functionz/db/base_db.py",
"chars": 2158,
"preview": "from abc import ABC, abstractmethod\nfrom typing import Optional, List, Dict, Any\nfrom datetime import datetime\n\nclass Ba"
},
{
"path": "babyagi/functionz/db/db_router.py",
"chars": 13272,
"preview": "from typing import List, Optional, Dict, Any\nfrom contextlib import contextmanager\nfrom datetime import datetime\n\nfrom ."
},
{
"path": "babyagi/functionz/db/local_db.py",
"chars": 9489,
"preview": "# local_db.py\n\nfrom sqlalchemy import create_engine, or_\nfrom sqlalchemy.orm import sessionmaker, scoped_session, joined"
},
{
"path": "babyagi/functionz/db/models.py",
"chars": 4754,
"preview": "# models.py\n\nfrom sqlalchemy import Column, Integer, String, DateTime, JSON, ForeignKey, Boolean, Table, Float, LargeBin"
},
{
"path": "babyagi/functionz/packs/default/ai_functions.py",
"chars": 9998,
"preview": "# packs/ai_generator.py\n\nfrom functionz.core.framework import func\n\n@func.register_function(\n metadata={\"description\""
},
{
"path": "babyagi/functionz/packs/default/default_functions.py",
"chars": 4391,
"preview": "# packs/default_functions.py\n\nfrom babyagi.functionz.core.framework import func\nfrom datetime import datetime\nfrom typin"
},
{
"path": "babyagi/functionz/packs/default/function_calling_chat.py",
"chars": 6875,
"preview": "from functionz.core.framework import func\nimport json\nimport litellm\n\n# Assuming `func` is the registry from your framew"
},
{
"path": "babyagi/functionz/packs/default/os.py",
"chars": 1140,
"preview": "@func.register_function(\n metadata={\"description\": \"Returns the current directory and recursively lists all files and"
},
{
"path": "babyagi/functionz/packs/drafts/choose_or_create_function.py",
"chars": 10198,
"preview": "from functionz.core.framework import func\n\n@func.register_function(\n metadata={\"description\": \"Choose or create a fun"
},
{
"path": "babyagi/functionz/packs/drafts/code_writing_functions.py",
"chars": 18154,
"preview": "from functionz.core.framework import func\n\n@func.register_function(\n metadata={\"description\": \"Checks if an existing fu"
},
{
"path": "babyagi/functionz/packs/drafts/generate_function.py",
"chars": 37797,
"preview": "\nfrom functionz.core.framework import func\n\n# Function 1: Fetch existing functions\n@func.register_function(\n metadata"
},
{
"path": "babyagi/functionz/packs/drafts/react_agent.py",
"chars": 9032,
"preview": "from functionz.core.framework import func\n\n@func.register_function(\n metadata={\n \"description\": \"An agent that"
},
{
"path": "babyagi/functionz/packs/drafts/self_build.py",
"chars": 4285,
"preview": "# self_build.py\n\nfrom functionz.core.framework import func\nimport json\n\n@func.register_function(\n metadata={\"descript"
},
{
"path": "babyagi/functionz/packs/drafts/self_build2.py",
"chars": 5943,
"preview": "from functionz.core.framework import func\n\n@func.register_function(\n metadata={\n \"name\": \"generate_and_process"
},
{
"path": "babyagi/functionz/packs/drafts/user_db.py",
"chars": 17353,
"preview": "# packs/user_db.py\n\nfrom PythonFunc.core.framework import func\n\n@func.register_function(\n metadata={\"description\": \"B"
},
{
"path": "babyagi/functionz/packs/plugins/airtable.py",
"chars": 7568,
"preview": "from functionz import func\n\n# Initialize the Airtable Table instance\n@func.register_function(\n metadata={\"description"
},
{
"path": "babyagi/functionz/packs/plugins/augie.py",
"chars": 4346,
"preview": "from babyagi.functionz.core.framework import func\n\n@func.register_function(\n metadata={\"description\": \"Generate paramet"
},
{
"path": "babyagi/functionz/packs/plugins/e2b.py",
"chars": 4093,
"preview": "import babyagi\n\n@babyagi.register_function(\n metadata={\"description\": \"Executes AI-generated Python code in a secure "
},
{
"path": "babyagi/functionz/packs/plugins/firecrawl.py",
"chars": 2320,
"preview": "# 1. Crawl a website and initiate a crawl job.\n@func.register_function(\n metadata={\"description\": \"Submits a crawl jo"
},
{
"path": "babyagi/functionz/packs/plugins/harmonic.py",
"chars": 2642,
"preview": "# Harmonic API Functions Pack for Functionz Framework\n\n\n@func.register_function(\n metadata={\"description\": \"Fetch a c"
},
{
"path": "babyagi/functionz/packs/plugins/payman.py",
"chars": 6851,
"preview": "from babyagi.functionz.core.framework import func\n\n# Store API keys (both test and real) from Replit secrets into the fu"
},
{
"path": "babyagi/functionz/packs/plugins/serpapi.py",
"chars": 2633,
"preview": "@func.register_function(\n metadata={\n \"description\": \"Perform a search using the latest SerpApi Python client libr"
},
{
"path": "babyagi/functionz/packs/plugins/voilanorbert.py",
"chars": 3192,
"preview": "@func.register_function(\n metadata={\"description\": \"Search for a contact by name and domain using VoilaNorbert's API.\"}"
},
{
"path": "babyagi/functionz/packs/plugins/wokelo.py",
"chars": 5657,
"preview": "@func.register_function(\n metadata={\"description\": \"Get an authentication token using the Wokelo API credentials.\"},\n "
},
{
"path": "examples/custom_flask_example.py",
"chars": 588,
"preview": "#this is an example of how to use the babyagi framework in a custom flask app\n\nfrom flask import Flask\nimport babyagi\nfr"
},
{
"path": "examples/custom_route_example.py",
"chars": 449,
"preview": "# this is an example of how to use babyagi with custom routes\n\nfrom babyagi import create_app, register_function\n\napp = "
},
{
"path": "examples/quickstart_example.py",
"chars": 421,
"preview": "\n\nimport babyagi\nimport os\n\n\napp = babyagi.create_app('/dashboard')\n\n# Add OpenAI key to enable automated descriptions a"
},
{
"path": "examples/self_build_example.py",
"chars": 672,
"preview": "import babyagi\nimport os\n\n# Add OpenAI key to enable automated descriptions and embedding of functions.\nbabyagi.add_key_"
},
{
"path": "examples/simple_example.py",
"chars": 760,
"preview": "# this is a simple example of registering two functions into babyagi, executing the function stored in the database, and"
},
{
"path": "examples/trigger_example.py",
"chars": 594,
"preview": "import babyagi\n\n@babyagi.register_function()\ndef function_a():\n print(\"Result from function A\")\n return \"Result fr"
},
{
"path": "main.py",
"chars": 419,
"preview": "import babyagi\nimport os\n\n\napp = babyagi.create_app('/dashboard')\n\n# Add OpenAI key to enable automated descriptions and"
},
{
"path": "pyproject.toml",
"chars": 610,
"preview": "[tool.poetry]\nname = \"babyagi\"\nversion = \"0.0.8\"\ndescription = \"\"\nauthors = [\"Your Name <you@example.com>\"]\n\n[tool.poetr"
},
{
"path": "requirements.txt",
"chars": 74,
"preview": "Flask>=2.0.0\nsqlalchemy>=1.4,<2.0\ncryptography\nscikit-learn\nlitellm\nopenai"
},
{
"path": "setup.py",
"chars": 1554,
"preview": "from setuptools import setup, find_packages\nimport os\n\n# Read the long description from README.md\nwith open(\"README.md\","
}
]
About this extraction
This page contains the full source code of the yoheinakajima/babyagi GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 65 files (386.8 KB), approximately 85.5k tokens, and a symbol index with 273 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.