├── .gitignore ├── .replit ├── CNAME ├── 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 ├── poetry.lock ├── pyproject.toml ├── requirements.txt ├── self_build.png └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | # Custom ignores 163 | funztionz.db 164 | encryption_key.json 165 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | entrypoint = "main.py" 2 | modules = ["python-3.11"] 3 | 4 | [nix] 5 | channel = "stable-24_05" 6 | 7 | [unitTest] 8 | language = "python3" 9 | 10 | [gitHubImport] 11 | requiredFiles = [".replit", "replit.nix"] 12 | 13 | [deployment] 14 | run = ["python3", "main.py"] 15 | deploymentTarget = "cloudrun" 16 | 17 | [[ports]] 18 | localPort = 5000 19 | externalPort = 5000 20 | 21 | [[ports]] 22 | localPort = 8000 23 | externalPort = 8000 24 | 25 | [[ports]] 26 | localPort = 8080 27 | externalPort = 80 28 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | babyagi.org -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the README and LICENSE 2 | include README.md 3 | include LICENSE 4 | 5 | # Include all package directories and their contents 6 | recursive-include babyagi * 7 | 8 | # Include examples 9 | recursive-include examples * 10 | 11 | # Exclude specific sensitive or unwanted files 12 | exclude encryption_key.json 13 | exclude funztionz.db 14 | 15 | # Exclude other unnecessary files 16 | global-exclude *.pyc 17 | global-exclude __pycache__/ 18 | 19 | # Exclude .git and .gitignore 20 | global-exclude .git 21 | global-exclude .gitignore 22 | 23 | # Exclude .egg-info directory and files 24 | global-exclude *.egg-info 25 | global-exclude *.egg-info/* 26 | -------------------------------------------------------------------------------- /babyagi/__init__.py: -------------------------------------------------------------------------------- 1 | # babyagi/__init__.py 2 | 3 | from flask import Flask, g 4 | from .functionz.core.framework import Functionz 5 | from .dashboard import create_dashboard 6 | from .api import create_api_blueprint 7 | import os 8 | import importlib.util 9 | import traceback 10 | import sys 11 | 12 | # Singleton instance of the functionz framework 13 | _func_instance = Functionz() 14 | 15 | 16 | def get_func_instance(): 17 | return _func_instance 18 | 19 | def create_app(dashboard_route='/dashboard'): 20 | app = Flask(__name__) 21 | 22 | # Remove leading slash if present to avoid double slashes 23 | if dashboard_route.startswith('/'): 24 | dashboard_route = dashboard_route[1:] 25 | 26 | # Create and register the dashboard blueprint with dashboard_route 27 | dashboard_blueprint = create_dashboard(_func_instance, dashboard_route) 28 | 29 | # Create and register the API blueprint 30 | api_blueprint = create_api_blueprint() 31 | 32 | # Register the blueprints 33 | app.register_blueprint(dashboard_blueprint, url_prefix=f'/{dashboard_route}') 34 | app.register_blueprint(api_blueprint) # Mounted at '/api' as defined in the blueprint 35 | 36 | # Store the dashboard route for use in templates 37 | app.config['DASHBOARD_ROUTE'] = dashboard_route 38 | 39 | # Ensure the Functionz instance is accessible in the request context 40 | @app.before_request 41 | def set_functionz(): 42 | g.functionz = _func_instance 43 | g.dashboard_route = dashboard_route # Optional, if needed globally 44 | 45 | return app 46 | 47 | # Function to register functions using the babyagi framework 48 | def register_function(*args, **kwargs): 49 | def wrapper(func): 50 | try: 51 | _func_instance.register_function(*args, **kwargs)(func) 52 | setattr(sys.modules[__name__], func.__name__, func) 53 | #print(f"Function '{func.__name__}' registered successfully.") 54 | except Exception as e: 55 | print(f"Error registering function '{func.__name__}': {e}") 56 | traceback.print_exc() 57 | return func 58 | return wrapper 59 | 60 | # Function to load additional function packs 61 | def load_functions(pack_name_or_path): 62 | #print(f"Attempting to load function pack: {pack_name_or_path}") 63 | if os.path.exists(pack_name_or_path): 64 | try: 65 | spec = importlib.util.spec_from_file_location("custom_pack", pack_name_or_path) 66 | module = importlib.util.module_from_spec(spec) 67 | spec.loader.exec_module(module) 68 | #print(f"Custom pack loaded from {pack_name_or_path}") 69 | except Exception as e: 70 | #print(f"Failed to load custom pack from path '{pack_name_or_path}': {e}") 71 | traceback.print_exc() 72 | else: 73 | try: 74 | print(f"Assuming '{pack_name_or_path}' is an internal pack...") 75 | _func_instance.load_function_pack(pack_name_or_path) 76 | print(f"Internal pack '{pack_name_or_path}' loaded successfully.") 77 | except Exception as e: 78 | print(f"Failed to load internal pack '{pack_name_or_path}': {e}") 79 | traceback.print_exc() 80 | 81 | 82 | def use_blueprints(app, dashboard_route='/dashboard'): 83 | """ 84 | Registers the babyagi blueprints with the provided Flask app. 85 | 86 | Args: 87 | app (Flask): The Flask application instance. 88 | dashboard_route (str): The route prefix for the dashboard. 89 | """ 90 | # Remove leading slash if present 91 | if dashboard_route.startswith('/'): 92 | dashboard_route = dashboard_route[1:] 93 | 94 | # Create blueprints 95 | dashboard_blueprint = create_dashboard(_func_instance, dashboard_route) 96 | api_blueprint = create_api_blueprint() 97 | 98 | # Register blueprints 99 | app.register_blueprint(dashboard_blueprint, url_prefix=f'/{dashboard_route}') 100 | app.register_blueprint(api_blueprint) # Mounted at '/api' as defined in the blueprint 101 | 102 | # Store the dashboard route for use in templates 103 | app.config['DASHBOARD_ROUTE'] = dashboard_route 104 | 105 | # Ensure the Functionz instance is accessible in the request context 106 | @app.before_request 107 | def set_functionz(): 108 | g.functionz = _func_instance 109 | g.dashboard_route = dashboard_route # Optional, if needed globally 110 | 111 | 112 | def __getattr__(name): 113 | """ 114 | Dynamic attribute access for the babyagi module. 115 | If a function with the given name exists in the database, 116 | return a callable that executes the function via the executor. 117 | """ 118 | try: 119 | if _func_instance.get_function(name): 120 | # Return a callable that executes the function via the executor 121 | return lambda *args, **kwargs: _func_instance.executor.execute(name, *args, **kwargs) 122 | except Exception as e: 123 | pass 124 | raise AttributeError(f"module '{__name__}' has no attribute '{name}'") 125 | 126 | 127 | # Auto-load default function packs when babyagi is imported 128 | try: 129 | print("Attempting to load default function packs...") 130 | # Uncomment if needed 131 | _func_instance.load_function_pack('default/default_functions') 132 | _func_instance.load_function_pack('default/ai_functions') 133 | _func_instance.load_function_pack('default/os') 134 | _func_instance.load_function_pack('default/function_calling_chat') 135 | except Exception as e: 136 | print(f"Error loading default function packs: {e}") 137 | traceback.print_exc() 138 | 139 | print("babyagi/__init__.py loaded") 140 | -------------------------------------------------------------------------------- /babyagi/api/__init__.py: -------------------------------------------------------------------------------- 1 | # babyagi/api/__init__.py 2 | 3 | from flask import Blueprint, jsonify, request, g 4 | from datetime import datetime 5 | from io import StringIO 6 | import logging 7 | import os 8 | import sys 9 | import importlib.util 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | def create_api_blueprint(): 14 | api = Blueprint('api', __name__, url_prefix='/api') 15 | 16 | # Removed the before_request function since g.functionz is set in the main app 17 | 18 | @api.route('/functions') 19 | def get_functions(): 20 | logger.debug("Accessing /api/functions route.") 21 | try: 22 | functions = g.functionz.get_all_functions() 23 | logger.debug(f"Retrieved {len(functions)} functions.") 24 | return jsonify(functions) 25 | except Exception as e: 26 | logger.error(f"Error in get_functions: {str(e)}", exc_info=True) 27 | return jsonify({"error": str(e)}), 500 28 | 29 | @api.route('/function/') 30 | def get_function(function_name): 31 | logger.debug(f"Accessing /api/function/{function_name} route.") 32 | try: 33 | function = g.functionz.db.get_function(function_name) 34 | if not function: 35 | logger.warning(f"Function '{function_name}' not found.") 36 | return jsonify({"error": f"Function '{function_name}' not found."}), 404 37 | return jsonify(function) 38 | except Exception as e: 39 | logger.error(f"Error getting function {function_name}: {str(e)}", exc_info=True) 40 | return jsonify({"error": str(e)}), 500 41 | 42 | @api.route('/function/', methods=['PUT']) 43 | def update_function(function_name): 44 | logger.debug(f"Accessing /api/function/{function_name} [PUT] route.") 45 | try: 46 | data = request.get_json() 47 | if not data or 'code' not in data: 48 | logger.warning("No 'code' provided in request data.") 49 | return jsonify({"error": "No 'code' provided in request data."}), 400 50 | g.functionz.update_function(function_name, code=data['code']) 51 | logger.info(f"Function '{function_name}' updated successfully.") 52 | return jsonify({"status": "success"}) 53 | except Exception as e: 54 | logger.error(f"Error updating function {function_name}: {str(e)}", exc_info=True) 55 | return jsonify({"error": str(e)}), 500 56 | 57 | @api.route('/execute/', methods=['POST']) 58 | def execute_function(function_name): 59 | logger.debug(f"Accessing /api/execute/{function_name} [POST] route.") 60 | try: 61 | params = request.get_json() or {} 62 | 63 | if function_name == 'execute_function_wrapper': 64 | # Special handling for execute_function_wrapper 65 | inner_function_name = params.pop('function_name', None) 66 | args = params.pop('args', []) 67 | kwargs = params.pop('kwargs', {}) 68 | result = g.functionz.executor.execute(function_name, inner_function_name, *args, **kwargs) 69 | else: 70 | # Normal execution for other functions 71 | result = g.functionz.executor.execute(function_name, **params) 72 | 73 | logger.info(f"Function '{function_name}' executed successfully.") 74 | return jsonify(result) 75 | except Exception as e: 76 | logger.error(f"Error executing function {function_name}: {str(e)}", exc_info=True) 77 | return jsonify({"error": str(e)}), 500 78 | 79 | @api.route('/function//versions') 80 | def get_function_versions(function_name): 81 | logger.debug(f"Accessing /api/function/{function_name}/versions route.") 82 | try: 83 | versions = g.functionz.get_function_versions(function_name) 84 | logger.debug(f"Retrieved {len(versions)} versions for function '{function_name}'.") 85 | return jsonify(versions) 86 | except Exception as e: 87 | logger.error(f"Error getting versions for function {function_name}: {str(e)}", exc_info=True) 88 | return jsonify({"error": str(e)}), 500 89 | 90 | @api.route('/function//activate/', methods=['POST']) 91 | def activate_function_version(function_name, version): 92 | logger.debug(f"Accessing /api/function/{function_name}/activate/{version} [POST] route.") 93 | try: 94 | g.functionz.activate_function_version(function_name, int(version)) 95 | logger.info(f"Version {version} of function '{function_name}' activated successfully.") 96 | return jsonify({"status": "success"}) 97 | except ValueError: 98 | logger.warning(f"Invalid version number provided: {version}") 99 | return jsonify({"error": "Invalid version number."}), 400 100 | except Exception as e: 101 | logger.error(f"Error activating version {version} for function {function_name}: {str(e)}", exc_info=True) 102 | return jsonify({"error": str(e)}), 500 103 | 104 | 105 | @api.route('/logs/') 106 | @api.route('/logs', defaults={'function_name': None}) 107 | def get_logs(function_name): 108 | logger.debug(f"Accessing /api/logs/{function_name if function_name else 'all'} route.") 109 | try: 110 | start_date_str = request.args.get('start_date') 111 | end_date_str = request.args.get('end_date') 112 | triggered_by_log_id_str = request.args.get('triggered_by_log_id') # New filter 113 | start_date = datetime.fromisoformat(start_date_str) if start_date_str else None 114 | end_date = datetime.fromisoformat(end_date_str) if end_date_str else None 115 | triggered_by_log_id = int(triggered_by_log_id_str) if triggered_by_log_id_str else None # Convert to int if provided 116 | 117 | logs = g.functionz.db.get_logs(function_name, start_date, end_date, triggered_by_log_id) 118 | if function_name: 119 | logger.debug(f"Retrieved {len(logs)} logs for function '{function_name}'.") 120 | else: 121 | logger.debug(f"Retrieved {len(logs)} logs for all functions.") 122 | return jsonify(logs) 123 | except ValueError: 124 | logger.warning("Invalid date format or triggered_by_log_id provided.") 125 | return jsonify({"error": "Invalid date format or triggered_by_log_id. Use ISO format for dates and integer for triggered_by_log_id."}), 400 126 | except Exception as e: 127 | logger.error(f"Error getting logs for function '{function_name}': {str(e)}", exc_info=True) 128 | return jsonify({"error": str(e)}), 500 129 | 130 | @api.route('/log_bundle/') 131 | def get_log_bundle(log_id): 132 | logger.debug(f"Accessing /api/log_bundle/{log_id} route.") 133 | try: 134 | logs = g.functionz.db.get_log_bundle(log_id) 135 | return jsonify({'logs': logs}) 136 | except Exception as e: 137 | logger.error(f"Error getting log bundle for log_id '{log_id}': {str(e)}", exc_info=True) 138 | return jsonify({"error": str(e)}), 500 139 | 140 | 141 | @api.route('/triggers/', methods=['GET']) 142 | def get_triggers(function_name): 143 | logger.debug(f"Accessing /api/triggers/{function_name} [GET] route.") 144 | try: 145 | triggers = g.functionz.get_triggers_for_function(function_name) 146 | trigger_list = [ 147 | getattr(trigger.triggering_function, 'name', 'any function') 148 | for trigger in triggers 149 | ] 150 | logger.debug(f"Retrieved {len(trigger_list)} triggers for function '{function_name}'.") 151 | return jsonify(trigger_list) 152 | except Exception as e: 153 | logger.error(f"Error getting triggers for function {function_name}: {str(e)}", exc_info=True) 154 | return jsonify({"error": str(e)}), 500 155 | 156 | 157 | logger.info("API blueprint created successfully.") 158 | return api 159 | -------------------------------------------------------------------------------- /babyagi/dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | # babyagi/dashboard/__init__.py 2 | 3 | from flask import Blueprint, render_template, g, send_from_directory 4 | import logging 5 | import os 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | def create_dashboard(func_instance, dashboard_route): 10 | if func_instance is None: 11 | raise ValueError("func_instance cannot be None") 12 | if dashboard_route is None: 13 | raise ValueError("dashboard_route cannot be None") 14 | 15 | dashboard = Blueprint('dashboard', __name__, 16 | template_folder='templates', 17 | static_folder='static', 18 | static_url_path='/dashboard/static') 19 | 20 | logger.info("Creating dashboard blueprint...") 21 | 22 | @dashboard.before_request 23 | def before_request(): 24 | """Set up the necessary context before each request.""" 25 | g.functionz = func_instance 26 | g.dashboard_route = dashboard_route 27 | logger.debug("Set g.functionz and g.dashboard_route for the request context.") 28 | 29 | @dashboard.route('/') 30 | def dashboard_home(): 31 | logger.info("Accessing dashboard home page.") 32 | try: 33 | logger.debug(f"Dashboard Route: {g.dashboard_route}") 34 | return render_template('index.html', dashboard_route=g.dashboard_route) 35 | except Exception as e: 36 | logger.error(f"Error in dashboard_home: {str(e)}", exc_info=True) 37 | return f"Error loading dashboard: {str(e)}", 500 38 | 39 | @dashboard.route('/function/') 40 | def function_detail(function_name): 41 | logger.info(f"Accessing function detail for: {function_name}") 42 | try: 43 | function = g.functionz.db.get_function(function_name) 44 | if not function: 45 | logger.warning(f"Function '{function_name}' not found.") 46 | return f"Function '{function_name}' not found.", 404 47 | return render_template('function_details.html', function_name=function_name, dashboard_route=g.dashboard_route) 48 | except Exception as e: 49 | logger.error(f"Error in function_detail: {str(e)}", exc_info=True) 50 | return f"Error loading function detail: {str(e)}", 500 51 | 52 | @dashboard.route('/graph') 53 | def function_graph(): 54 | logger.info("Accessing function relationship graph page.") 55 | try: 56 | return render_template('function_graph.html', dashboard_route=g.dashboard_route) 57 | except Exception as e: 58 | logger.error(f"Error in function_graph: {str(e)}", exc_info=True) 59 | return f"Error loading function graph: {str(e)}", 500 60 | 61 | @dashboard.route('/mermaid') 62 | def function_graph_mermaid(): 63 | logger.info("Accessing mermaid function relationship graph page.") 64 | try: 65 | return render_template('function_graph_mermaid.html', dashboard_route=g.dashboard_route) 66 | except Exception as e: 67 | logger.error(f"Error in function_graph_mermaid: {str(e)}", exc_info=True) 68 | return f"Error loading mermaid function graph: {str(e)}", 500 69 | 70 | @dashboard.route('/3d') 71 | def function_graph_3d(): 72 | logger.info("Accessing 3D function relationship graph page.") 73 | try: 74 | return render_template('function_graph_3d.html', dashboard_route=g.dashboard_route) 75 | except Exception as e: 76 | logger.error(f"Error in function_graph_3d: {str(e)}", exc_info=True) 77 | return f"Error loading 3D function graph: {str(e)}", 500 78 | 79 | @dashboard.route('/logs') 80 | def logs_dashboard(): 81 | logger.info("Accessing logs dashboard.") 82 | try: 83 | return render_template('logs_dashboard.html', dashboard_route=g.dashboard_route) 84 | except Exception as e: 85 | logger.error(f"Error in logs_dashboard: {str(e)}", exc_info=True) 86 | return f"Error loading logs dashboard: {str(e)}", 500 87 | 88 | @dashboard.route('/log/') 89 | def log_page(log_id): 90 | logger.info(f"Accessing log page for Log ID {log_id}.") 91 | try: 92 | return render_template( 93 | 'log_page.html', 94 | log_id=log_id, 95 | dashboard_route=g.dashboard_route # Pass the dashboard route if needed 96 | ) 97 | except Exception as e: 98 | logger.error(f"Error in log_page for Log ID {log_id}: {str(e)}", exc_info=True) 99 | return f"Error loading log page for Log ID {log_id}: {str(e)}", 500 100 | 101 | @dashboard.route('/log_graph') 102 | def log_relationship_graph(): 103 | logger.info("Accessing log relationship graph.") 104 | try: 105 | return render_template('log_relationship_graph.html', dashboard_route=g.dashboard_route) 106 | except Exception as e: 107 | logger.error(f"Error in log_relationship_graph: {str(e)}", exc_info=True) 108 | return f"Error loading log relationship graph: {str(e)}", 500 109 | 110 | @dashboard.route('/chat') 111 | def chat_page(): 112 | logger.info("Accessing chat page.") 113 | try: 114 | return render_template('chat.html', dashboard_route=g.dashboard_route) 115 | except Exception as e: 116 | logger.error(f"Error in chat_page: {str(e)}", exc_info=True) 117 | return f"Error loading chat page: {str(e)}", 500 118 | 119 | @dashboard.route('/') 120 | def serve_static_files(filename): 121 | """Serve static files from the dashboard's static folder.""" 122 | logger.debug(f"Serving static file: {filename}") 123 | try: 124 | return send_from_directory(dashboard.static_folder, filename) 125 | except Exception as e: 126 | logger.error(f"Error serving static file '{filename}': {str(e)}", exc_info=True) 127 | return "File not found.", 404 128 | 129 | logger.info("Dashboard blueprint created successfully.") 130 | return dashboard 131 | 132 | logger.info("Dashboard __init__.py loaded successfully.") 133 | -------------------------------------------------------------------------------- /babyagi/dashboard/static/css/style.css: -------------------------------------------------------------------------------- 1 | /* static/css/style.css */ 2 | 3 | /* Global Variables */ 4 | :root { 5 | /* Color variables */ 6 | --primary-color: #3498db; 7 | --secondary-color: #2c3e50; 8 | --background-color: #f0f4f8; 9 | --text-color: #333333; 10 | --border-color: #e0e0e0; 11 | --hover-color: #f8f9fa; 12 | --card-bg-color: #ffffff; 13 | --bg-color: #f0f4f8; 14 | --text-color: #333; 15 | --accent-color: #3498db; 16 | --hover-color: #f7fbfc; 17 | --card-bg: #ffffff; 18 | } 19 | 20 | 21 | 22 | /* Global Styles */ 23 | body, html { 24 | margin: 0; 25 | padding: 0; 26 | font-family: 'Inter', Arial, sans-serif; 27 | background-color: var(--background-color); 28 | color: var(--text-color); 29 | font-size: 12px; 30 | line-height: 1.4; 31 | } 32 | 33 | .container { 34 | width: 100%; 35 | max-width: 100%; 36 | padding: 20px; 37 | margin: 0 auto; 38 | box-sizing: border-box; 39 | } 40 | 41 | 42 | .details-container { 43 | width: 100%; 44 | max-width: 1600px; 45 | padding: 20px; 46 | margin: 0 auto; 47 | box-sizing: border-box; 48 | } 49 | 50 | /* Hide elements with 'mobile-only' class on screens wider than 768px */ 51 | @media (min-width: 768px) { 52 | .mobile-only { 53 | display: none; 54 | } 55 | } 56 | 57 | /* Hide elements with 'desktop-only' class on screens narrower than 768px */ 58 | @media (max-width: 767px) { 59 | .desktop-only { 60 | display: none; 61 | } 62 | } 63 | 64 | 65 | /* Headers */ 66 | h1 { 67 | color: var(--secondary-color); 68 | font-size: 1.5em; 69 | font-weight: 600; 70 | margin-bottom: 20px; 71 | text-align: left; 72 | } 73 | 74 | h1 small { 75 | font-size: 0.7em; 76 | font-weight: normal; 77 | margin-left: 10px; 78 | } 79 | 80 | h1 small a { 81 | color: var(--primary-color); 82 | text-decoration: none; 83 | } 84 | 85 | /* Navigation Menu Styles */ 86 | nav { 87 | background-color: var(--secondary-color); 88 | padding: 10px 0; 89 | margin-bottom: 20px; 90 | } 91 | 92 | nav ul { 93 | list-style-type: none; 94 | margin: 0; 95 | padding: 0; 96 | display: flex; 97 | justify-content: left; 98 | flex-wrap: wrap; 99 | } 100 | 101 | nav li { 102 | margin: 0 15px; 103 | } 104 | 105 | nav a { 106 | color: #ffffff; 107 | text-decoration: none; 108 | font-weight: 500; 109 | font-size: 14px; 110 | } 111 | 112 | nav a:hover { 113 | text-decoration: underline; 114 | color: var(--primary-color); 115 | } 116 | 117 | /* Breadcrumb Styles */ 118 | .breadcrumb { 119 | margin-bottom: 20px; 120 | } 121 | 122 | .breadcrumb a { 123 | color: var(--primary-color); 124 | text-decoration: none; 125 | } 126 | 127 | .breadcrumb a:hover { 128 | text-decoration: underline; 129 | } 130 | 131 | 132 | 133 | /* Search Bar Styles */ 134 | #searchInput { 135 | width: 100%; 136 | max-width: 1200px; /* Match the container's max-width */ 137 | padding: 10px; 138 | margin-bottom: 20px; 139 | font-size: 14px; 140 | border: 1px solid var(--border-color); 141 | border-radius: 4px; 142 | box-sizing: border-box; 143 | } 144 | 145 | /* Table Styles */ 146 | table { 147 | width: 100%; 148 | border-collapse: separate; 149 | border-spacing: 0; 150 | background-color: var(--card-bg-color); 151 | box-shadow: 0 1px 3px rgba(0,0,0,0.1); 152 | table-layout: fixed; 153 | } 154 | 155 | th, td { 156 | padding: 12px; 157 | text-align: left; 158 | border-bottom: 1px solid var(--border-color); 159 | vertical-align: top; 160 | } 161 | 162 | td { 163 | word-wrap: break-word; 164 | overflow-wrap: break-word; 165 | } 166 | 167 | th { 168 | background-color: var(--secondary-color); 169 | color: white; 170 | font-weight: 500; 171 | white-space: nowrap; 172 | cursor: pointer; 173 | } 174 | 175 | th.sortable:hover { 176 | background-color: var(--primary-color); 177 | } 178 | 179 | tr:hover { 180 | background-color: var(--hover-color); 181 | } 182 | 183 | .function-name { 184 | color: var(--primary-color); 185 | font-weight: 500; 186 | text-decoration: none; 187 | } 188 | 189 | .function-name:hover { 190 | text-decoration: underline; 191 | } 192 | 193 | .function-link, .function-name { 194 | color: var(--primary-color); 195 | font-weight: 500; 196 | text-decoration: none; 197 | } 198 | 199 | .function-link:hover, .function-name:hover { 200 | text-decoration: underline; 201 | } 202 | 203 | .small-text { 204 | font-size: 0.92em; 205 | color: #666; 206 | } 207 | 208 | .params-list, .dependencies-list, .imports-list, .triggers-list { 209 | list-style-type: none; 210 | padding: 0; 211 | margin: 0; 212 | } 213 | 214 | .params-list li, .dependencies-list li, .imports-list, .triggers-list li { 215 | margin-bottom: 2px; 216 | } 217 | 218 | /* Card Styles (for mobile view) */ 219 | .function-grid { 220 | display: none; 221 | grid-template-columns: 1fr; 222 | gap: 15px; 223 | padding: 10px; 224 | } 225 | 226 | .function-card { 227 | background-color: var(--background-color); 228 | border: 1px solid var(--border-color); 229 | border-radius: 4px; 230 | padding: 15px; 231 | } 232 | 233 | .function-meta { 234 | color: #666; 235 | margin-bottom: 10px; 236 | } 237 | 238 | .function-description { 239 | margin-bottom: 10px; 240 | } 241 | 242 | .params-title { 243 | font-weight: 500; 244 | margin-top: 8px; 245 | margin-bottom: 3px; 246 | } 247 | 248 | .log-info { 249 | display: flex; 250 | justify-content: space-between; 251 | color: #666; 252 | border-top: 1px solid var(--border-color); 253 | padding-top: 8px; 254 | margin-top: 8px; 255 | } 256 | 257 | /* Function Detail Page Styles */ 258 | .function-detail { 259 | background-color: var(--card-bg-color); 260 | padding: 20px; 261 | border-radius: 8px; 262 | margin-bottom: 20px; 263 | } 264 | 265 | .section-title { 266 | font-size: 1.2em; 267 | font-weight: bold; 268 | margin-top: 20px; 269 | margin-bottom: 10px; 270 | color: var(--primary-color); 271 | } 272 | 273 | .button { 274 | background-color: var(--primary-color); 275 | color: white; 276 | padding: 10px 15px; 277 | border: none; 278 | border-radius: 4px; 279 | cursor: pointer; 280 | font-size: 14px; 281 | margin-right: 10px; 282 | } 283 | 284 | .button:hover { 285 | background-color: var(--hover-color); 286 | } 287 | 288 | #functionLogs { 289 | max-height:600px; 290 | overflow-y:scroll; 291 | } 292 | 293 | .log-entry, .execution-result { 294 | background-color: #f8f9fa; 295 | padding: 10px; 296 | margin-bottom: 10px; 297 | border-radius: 4px; 298 | font-family: monospace; 299 | font-size: 12px; 300 | line-height: 1.4; 301 | overflow-x: auto; 302 | } 303 | 304 | .param-input { 305 | margin-bottom: 10px; 306 | } 307 | 308 | .param-input label { 309 | display: block; 310 | margin-bottom: 5px; 311 | } 312 | 313 | .param-input input, .param-input textarea { 314 | width: 100%; 315 | padding: 5px; 316 | border: 1px solid var(--border-color); 317 | border-radius: 4px; 318 | } 319 | 320 | .breadcrumb { 321 | margin-bottom: 20px; 322 | } 323 | 324 | .breadcrumb a { 325 | color: var(--primary-color); 326 | text-decoration: none; 327 | } 328 | 329 | .breadcrumb a:hover { 330 | text-decoration: underline; 331 | } 332 | 333 | .two-column { 334 | display: flex; 335 | flex-wrap: wrap; 336 | gap: 20px; 337 | } 338 | 339 | .column { 340 | flex: 1; 341 | min-width: 300px; 342 | } 343 | 344 | .detail-item { 345 | background-color: #f8f9fa; 346 | padding: 10px; 347 | margin-bottom: 10px; 348 | border-radius: 4px; 349 | font-family: monospace; 350 | font-size: 12px; 351 | line-height: 1.4; 352 | overflow-x: auto; 353 | } 354 | 355 | .detail-label { 356 | display: inline-block; 357 | min-width: 120px; 358 | font-weight: bold; 359 | color: var(--primary-color); 360 | margin-right: 10px; 361 | } 362 | 363 | .detail-value { 364 | display: inline-block; 365 | } 366 | 367 | .detail-list { 368 | margin: 5px 0 5px 130px; 369 | padding-left: 20px; 370 | } 371 | 372 | .CodeMirror { 373 | font-size: 12px; 374 | border: 1px solid var(--border-color); 375 | border-radius: 4px; 376 | margin-bottom: 5px; 377 | } 378 | 379 | /* Version History */ 380 | .version-item { 381 | padding: 10px; 382 | border-bottom: 1px solid #ccc; 383 | background-color: #f8f9fa; 384 | margin-bottom: 5px; 385 | word-wrap: break-word; 386 | white-space: pre-wrap; 387 | max-height:600px; 388 | } 389 | 390 | .version-item.active { 391 | background-color: #e0f7fa; 392 | } 393 | 394 | .version-item pre { 395 | background-color: #f1f1f1; 396 | padding: 10px; 397 | border-radius: 4px; 398 | font-size: 12px; 399 | overflow-x: auto; 400 | white-space: pre-wrap; 401 | word-wrap: break-word; 402 | line-height: 1.4; 403 | } 404 | 405 | #toggleVersionHistory { 406 | margin-top: 10px; 407 | } 408 | 409 | #versionHistory { 410 | margin-top: 10px; 411 | } 412 | 413 | /* Media Queries */ 414 | @media (max-width: 768px) { 415 | /* Hide the table in mobile, show the card grid */ 416 | table { 417 | display: none; 418 | } 419 | .function-grid { 420 | display: grid; 421 | } 422 | .two-column { 423 | flex-direction: column; 424 | } 425 | .container, #searchInput, .function-detail, .column, input, body { 426 | padding: 10px; 427 | max-width: 100%; 428 | } 429 | .param-input{ 430 | margin:10px; 431 | } 432 | } -------------------------------------------------------------------------------- /babyagi/dashboard/static/js/dashboard.js: -------------------------------------------------------------------------------- 1 | /* static/js/dashboard.js */ 2 | 3 | // Assume that dashboardRoute, apiFunctionsUrl, and apiLogsUrl are defined in the HTML template 4 | 5 | let currentSort = { key: null, direction: 'asc' }; 6 | let allFunctions = []; // This will hold the functions data globally 7 | 8 | function filterTable() { 9 | const input = document.getElementById("searchInput").value.toLowerCase(); 10 | const table = document.getElementById("functionTable"); 11 | const rows = table.getElementsByTagName("tr"); 12 | const grid = document.getElementById("functionGrid"); 13 | const cards = grid.getElementsByClassName("function-card"); 14 | 15 | for (let i = 1; i < rows.length; i++) { // Start at 1 to skip header row 16 | let match = false; 17 | const cells = rows[i].getElementsByTagName("td"); 18 | for (let j = 0; j < cells.length; j++) { 19 | if (cells[j].innerText.toLowerCase().includes(input)) { 20 | match = true; 21 | break; 22 | } 23 | } 24 | rows[i].style.display = match ? "" : "none"; 25 | } 26 | 27 | // Filter cards for mobile view 28 | for (let i = 0; i < cards.length; i++) { 29 | if (cards[i].innerText.toLowerCase().includes(input)) { 30 | cards[i].style.display = ""; 31 | } else { 32 | cards[i].style.display = "none"; 33 | } 34 | } 35 | } 36 | function sortTable(key) { 37 | const direction = currentSort.key === key && currentSort.direction === 'asc' ? 'desc' : 'asc'; 38 | currentSort = { key, direction }; 39 | 40 | const sortedFunctions = [...allFunctions].sort((a, b) => { 41 | let valA, valB; 42 | if (key === 'name') { 43 | valA = a.name.toLowerCase(); 44 | valB = b.name.toLowerCase(); 45 | } else if (key === 'created_date') { 46 | valA = new Date(a.created_date); 47 | valB = new Date(b.created_date); 48 | } else if (key === 'total_logs') { 49 | valA = a.total_logs || 0; 50 | valB = b.total_logs || 0; 51 | } else if (key === 'last_log_date') { 52 | valA = new Date(a.last_log_date || 0); 53 | valB = new Date(b.last_log_date || 0); 54 | } 55 | 56 | if (direction === 'asc') return valA > valB ? 1 : -1; 57 | return valA < valB ? 1 : -1; 58 | }); 59 | 60 | populateDashboard(sortedFunctions); 61 | } 62 | 63 | async function fetchLogs(functionName) { 64 | try { 65 | const response = await fetch(`${apiLogsUrl}${encodeURIComponent(functionName)}`); 66 | if (!response.ok) { 67 | throw new Error('Failed to fetch logs'); 68 | } 69 | const logs = await response.json(); 70 | return { 71 | total_logs: logs.length, 72 | last_log_date: logs.length > 0 ? logs[logs.length - 1].timestamp : null 73 | }; 74 | } catch (error) { 75 | console.error(`Error fetching logs for ${functionName}:`, error); 76 | return { total_logs: 0, last_log_date: null }; 77 | } 78 | } 79 | 80 | function updateLogsAsync(tasks) { 81 | const promises = tasks.map(([functionName, row]) => { 82 | return fetchLogs(functionName).then(({ total_logs, last_log_date }) => { 83 | const totalLogsCell = row.querySelector('.total-logs'); 84 | const lastLogDateCell = row.querySelector('.last-log-date'); 85 | totalLogsCell.textContent = total_logs; 86 | lastLogDateCell.textContent = last_log_date ? new Date(last_log_date).toLocaleDateString() : 'N/A'; 87 | 88 | // Update function in allFunctions array 89 | const func = allFunctions.find(f => f.name === functionName); 90 | if (func) { 91 | func.total_logs = total_logs; 92 | func.last_log_date = last_log_date; 93 | } 94 | }); 95 | }); 96 | return Promise.all(promises); 97 | } 98 | 99 | async function populateDashboard(functions) { 100 | allFunctions = functions; // Store functions globally for sorting 101 | const tableBody = document.querySelector('#functionTable tbody'); 102 | const grid = document.getElementById('functionGrid'); 103 | const logTasks = []; 104 | 105 | tableBody.innerHTML = ''; 106 | grid.innerHTML = ''; 107 | 108 | for (const func of functions) { 109 | const row = document.createElement('tr'); 110 | const description = func.metadata && func.metadata.description ? func.metadata.description : 'No description available'; 111 | const createdDate = new Date(func.created_date).toLocaleDateString(); 112 | 113 | row.innerHTML = ` 114 | ${func.name} 115 | v${func.version} 116 | ${description} 117 | ${formatParams(func.input_parameters)} 118 | ${formatParams(func.output_parameters)} 119 | ${formatList(func.dependencies, 'dependencies-list', dashboardRoute)} 120 | ${formatList(func.imports, 'imports-list')} 121 | ${formatList(func.triggers, 'triggers-list', dashboardRoute)} 122 | ${createdDate} 123 | Loading... 124 | Loading... 125 | `; 126 | tableBody.appendChild(row); 127 | 128 | // Populate card view (for mobile) 129 | const card = document.createElement('div'); 130 | card.className = 'function-card'; 131 | card.innerHTML = ` 132 | ${func.name} 133 |
v${func.version} | Created: ${createdDate}
134 |
${description}
135 |
Input Parameters:
136 | ${formatParams(func.input_parameters)} 137 |
Output Parameters:
138 | ${formatParams(func.output_parameters)} 139 |
Dependencies:
140 | ${formatList(func.dependencies, 'dependencies-list', dashboardRoute)} 141 |
Imports:
142 | ${formatList(func.imports, 'imports-list')} 143 |
Triggers:
144 | ${formatList(func.triggers, 'triggers-list', dashboardRoute)} 145 |
146 | Total Logs: Loading... 147 | Last Log: Loading... 148 |
149 | `; 150 | grid.appendChild(card); 151 | 152 | logTasks.push([func.name, row]); 153 | } 154 | 155 | updateLogsAsync(logTasks); 156 | } 157 | 158 | function formatParams(params) { 159 | if (!params || params.length === 0) { 160 | return ''; 161 | } 162 | return '
    ' + 163 | params.map(param => `
  • ${param.name}: ${param.type}
  • `).join('') + 164 | '
'; 165 | } 166 | 167 | function formatList(items, className, dashboardRoute) { 168 | if (!items || items.length === 0) { 169 | return '-'; 170 | } 171 | return '
    ' + 172 | items.map(item => { 173 | if (className === 'dependencies-list' || className === 'triggers-list') { 174 | return `
  • ${item}
  • `; 175 | } 176 | return `
  • ${item}
  • `; 177 | }).join('') + 178 | '
'; 179 | } 180 | 181 | // Fetch functions data and populate the dashboard 182 | async function fetchFunctionsAndPopulate() { 183 | try { 184 | const response = await fetch(apiFunctionsUrl); 185 | if (!response.ok) { 186 | throw new Error('Failed to fetch functions'); 187 | } 188 | const data = await response.json(); 189 | populateDashboard(data); 190 | } catch (error) { 191 | console.error('Error fetching functions:', error); 192 | document.querySelector('.container').innerHTML += `

Error loading functions. Please try refreshing the page.

`; 193 | } 194 | } 195 | 196 | // Call the function when the page loads 197 | document.addEventListener('DOMContentLoaded', () => { 198 | fetchFunctionsAndPopulate(); 199 | 200 | // Add event listener for search input 201 | const searchInput = document.getElementById("searchInput"); 202 | searchInput.addEventListener('input', filterTable); 203 | }); -------------------------------------------------------------------------------- /babyagi/dashboard/static/js/function_graph.js: -------------------------------------------------------------------------------- 1 | // function_graph.js 2 | let cy; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | cy = cytoscape({ 6 | container: document.getElementById('graph'), 7 | style: [ 8 | { 9 | selector: 'node', 10 | style: { 11 | 'shape': 'rectangle', 12 | 'background-color': '#E6F2FF', 13 | 'label': 'data(id)', 14 | 'text-valign': 'center', 15 | 'text-halign': 'center', 16 | 'text-wrap': 'wrap', 17 | 'text-max-width': '150px', 18 | 'width': 'label', 19 | 'height': 'label', 20 | 'padding': '12px', 21 | 'font-weight': 'bold', 22 | 'font-size': '16px', 23 | 'color': '#333333', 24 | 'text-outline-color': '#FFFFFF', 25 | 'text-outline-width': 1, 26 | 'cursor': 'pointer' 27 | } 28 | }, 29 | { 30 | selector: 'node[type="import"]', 31 | style: { 32 | 'background-color': '#E6FFF2' 33 | } 34 | }, 35 | { 36 | selector: 'node[type="trigger"]', 37 | style: { 38 | 'background-color': '#FFE6E6' 39 | } 40 | }, 41 | { 42 | selector: 'node:hover', 43 | style: { 44 | 'background-opacity': 0.8 45 | } 46 | }, 47 | { 48 | selector: 'edge', 49 | style: { 50 | 'width': 1, 51 | 'line-color': '#999999', 52 | 'target-arrow-color': '#999999', 53 | 'target-arrow-shape': 'triangle', 54 | 'curve-style': 'bezier', 55 | 'arrow-scale': 1.5 56 | } 57 | }, 58 | { 59 | selector: 'edge[type="dependency"]', 60 | style: { 61 | 'width': 2 62 | } 63 | }, 64 | { 65 | selector: 'edge[type="trigger"]', 66 | style: { 67 | 'line-style': 'dashed', 68 | 'label': 'trigger', 69 | 'font-size': '12px', 70 | 'text-rotation': 'autorotate', 71 | 'text-margin-y': -10 72 | } 73 | } 74 | ], 75 | layout: { 76 | name: 'cose', 77 | idealEdgeLength: 50, 78 | nodeOverlap: 20, 79 | refresh: 20, 80 | fit: true, 81 | padding: 30, 82 | randomize: false, 83 | componentSpacing: 80, 84 | nodeRepulsion: 450000, 85 | edgeElasticity: 100, 86 | nestingFactor: 5, 87 | gravity: 80, 88 | numIter: 1000, 89 | initialTemp: 200, 90 | coolingFactor: 0.95, 91 | minTemp: 1.0 92 | }, 93 | wheelSensitivity: 0.2 94 | }); 95 | 96 | fetch('/api/functions') 97 | .then(response => response.json()) 98 | .then(data => { 99 | const elements = []; 100 | const imports = new Set(); 101 | 102 | data.forEach(func => { 103 | elements.push({ data: { id: func.name, type: 'function' } }); 104 | 105 | func.dependencies.forEach(dep => { 106 | elements.push({ data: { id: dep, type: 'function' } }); 107 | elements.push({ data: { source: func.name, target: dep, type: 'dependency' } }); 108 | }); 109 | 110 | func.triggers.forEach(trigger => { 111 | elements.push({ data: { id: trigger, type: 'trigger' } }); 112 | elements.push({ data: { source: func.name, target: trigger, type: 'trigger' } }); 113 | }); 114 | 115 | func.imports.forEach(imp => imports.add(imp)); 116 | }); 117 | 118 | imports.forEach(imp => { 119 | elements.push({ data: { id: imp, type: 'import' } }); 120 | }); 121 | 122 | data.forEach(func => { 123 | func.imports.forEach(imp => { 124 | elements.push({ data: { source: func.name, target: imp, type: 'import' } }); 125 | }); 126 | }); 127 | 128 | cy.add(elements); 129 | cy.layout({ name: 'cose' }).run(); 130 | 131 | cy.fit(40); 132 | 133 | cy.zoom({ 134 | level: cy.zoom() * 1.3, 135 | renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } 136 | }); 137 | }); 138 | 139 | cy.on('tap', 'node', function(evt) { 140 | const node = evt.target; 141 | if (node.data('type') === 'function' || node.data('type') === 'trigger') { 142 | showFunctionOverlay(node.id()); 143 | } 144 | }); 145 | 146 | cy.on('zoom pan', () => { 147 | closeOverlay(); 148 | }); 149 | }); 150 | 151 | function showFunctionOverlay(functionName) { 152 | const overlay = document.getElementById('overlay'); 153 | const content = document.getElementById('overlay-content'); 154 | 155 | fetch(`/api/function/${functionName}`) 156 | .then(response => response.json()) 157 | .then(data => { 158 | content.innerHTML = ` 159 |

${data.name}

160 |
161 | 162 | 163 | 164 |
165 |
166 |
167 |

Description: ${data.metadata.description || 'No description available.'}

168 |

Version: ${data.version}

169 |
170 | 171 | 172 |
173 | `; 174 | 175 | const node = cy.$id(functionName); 176 | const renderedPosition = node.renderedPosition(); 177 | 178 | overlay.style.left = `${renderedPosition.x + 10}px`; 179 | overlay.style.top = `${renderedPosition.y + 10}px`; 180 | overlay.style.display = 'block'; 181 | }); 182 | } 183 | 184 | function showTab(tabName, functionName) { 185 | const tabs = ['description', 'code', 'logs']; 186 | tabs.forEach(tab => { 187 | const tabElement = document.getElementById(`${tab}-tab`); 188 | const tabButton = document.querySelector(`button.tab-button:nth-child(${tabs.indexOf(tab) + 1})`); 189 | if (tab === tabName) { 190 | tabElement.style.display = 'block'; 191 | tabButton.classList.add('active'); 192 | } else { 193 | tabElement.style.display = 'none'; 194 | tabButton.classList.remove('active'); 195 | } 196 | }); 197 | 198 | if (tabName === 'code' && document.getElementById('code-tab').innerHTML === '') { 199 | showCode(functionName); 200 | } else if (tabName === 'logs' && document.getElementById('logs-tab').innerHTML === '') { 201 | showLogs(functionName); 202 | } 203 | } 204 | 205 | function showCode(functionName) { 206 | fetch(`/api/function/${functionName}`) 207 | .then(response => response.json()) 208 | .then(data => { 209 | const codeTab = document.getElementById('code-tab'); 210 | codeTab.innerHTML = ` 211 |

Code:

212 |
${data.code}
213 | `; 214 | }); 215 | } 216 | 217 | function showLogs(functionName) { 218 | fetch(`/api/logs/${functionName}`) 219 | .then(response => response.json()) 220 | .then(data => { 221 | const logsTab = document.getElementById('logs-tab'); 222 | logsTab.innerHTML = ` 223 |

Logs:

224 |
${JSON.stringify(data, null, 2)}
225 | `; 226 | }); 227 | } 228 | 229 | function closeOverlay() { 230 | document.getElementById('overlay').style.display = 'none'; 231 | } -------------------------------------------------------------------------------- /babyagi/dashboard/static/js/log_graph.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | loadLogs(); 3 | }); 4 | 5 | async function loadLogs() { 6 | try { 7 | const response = await fetch(apiLogsUrl); 8 | if (!response.ok) { 9 | throw new Error('Failed to fetch logs'); 10 | } 11 | const logs = await response.json(); 12 | renderGraph(logs); 13 | } catch (error) { 14 | console.error('Error loading logs:', error); 15 | alert('Failed to load logs. Please try again later.'); 16 | } 17 | } 18 | 19 | function renderGraph(logs) { 20 | // Process logs and build graph data 21 | const elements = logs.map(log => ({ 22 | data: { id: log.id, label: log.function_name } 23 | })); 24 | 25 | // Add edges for relationships 26 | logs.forEach(log => { 27 | if (log.parent_log_id) { 28 | elements.push({ 29 | data: { 30 | id: `parent-${log.id}`, 31 | source: log.parent_log_id, 32 | target: log.id, 33 | label: 'Parent' 34 | } 35 | }); 36 | } 37 | if (log.triggered_by_log_id) { 38 | elements.push({ 39 | data: { 40 | id: `trigger-${log.id}`, 41 | source: log.triggered_by_log_id, 42 | target: log.id, 43 | label: 'Triggered By' 44 | } 45 | }); 46 | } 47 | }); 48 | 49 | // Initialize Cytoscape graph 50 | var cy = cytoscape({ 51 | container: document.getElementById('graph'), 52 | elements: elements, 53 | style: [ 54 | { 55 | selector: 'node', 56 | style: { 57 | 'label': 'data(label)', 58 | 'background-color': '#666', 59 | 'text-valign': 'center', 60 | 'text-halign': 'center', 61 | 'color': '#fff', 62 | 'text-outline-width': 2, 63 | 'text-outline-color': '#666' 64 | } 65 | }, 66 | { 67 | selector: 'edge', 68 | style: { 69 | 'width': 2, 70 | 'line-color': '#ccc', 71 | 'target-arrow-color': '#ccc', 72 | 'target-arrow-shape': 'triangle', 73 | 'curve-style': 'bezier' 74 | } 75 | } 76 | ], 77 | layout: { 78 | name: 'breadthfirst', 79 | directed: true, 80 | padding: 10 81 | } 82 | }); 83 | 84 | // Add event listeners if needed 85 | } 86 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block head %} 5 | 6 | 7 | {% block title %}PythonFunc Dashboard{% endblock %} 8 | 9 | 10 | 11 | {% endblock %} 12 | 13 | 14 | 15 | 26 |
27 | 28 | {% block breadcrumb %}{% endblock %} 29 | 30 | {% block content %}{% endblock %} 31 |
32 | {% block scripts %}{% endblock %} 33 | 34 | 35 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/function_details.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Function Details: {{ function_name }}{% endblock %} 3 | {% block head %} 4 | 5 | 6 | 7 | 8 | 9 | {% endblock %} 10 | {% block breadcrumb %} 11 | 14 | {% endblock %} 15 | {% block content %} 16 |

Function: {{ function_name }}

17 | 18 |
19 |
20 |
21 |
Execute Function
22 |
23 | 24 |
25 |
26 |
27 |
Details
28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 |
Code
36 | 37 | 38 |
39 | 40 |
41 |
Logs
42 |
43 |
44 | 45 |
46 |
47 | Versions 48 |
49 | 50 | 51 |
52 |
53 |
54 | {% endblock %} 55 | {% block scripts %} 56 | 69 | 70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/function_graph_3d.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}3D Function Relationship Graph{% endblock %} 3 | {% block head %} 4 | {{ super() }} 5 | 25 | {% endblock %} 26 | {% block breadcrumb %} 27 | 30 | {% endblock %} 31 | {% block content %} 32 |
33 |
34 | {% endblock %} 35 | {% block scripts %} 36 | 37 | 38 | 39 | 40 | 115 | {% endblock %} 116 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/function_graph_mermaid.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Function Relationship Mermaid Graph{% endblock %} 3 | 4 | {% block head %} 5 | {{ super() }} 6 | 7 | 21 | 69 | {% endblock %} 70 | 71 | {% block breadcrumb %} 72 | 75 | {% endblock %} 76 | 77 | {% block content %} 78 |

Function Relationship Mermaid Graph

79 |
80 |
81 | %% Mermaid graph will be injected here 82 |
83 |
84 | 85 | 86 | {% endblock %} 87 | 88 | {% block scripts %} 89 | {{ super() }} 90 | 255 | {% endblock %} 256 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Function Dashboard{% endblock %} 3 | {% block content %} 4 |

Function Dashboard

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
NameVersionDescriptionInput ParametersOutput ParametersDependenciesImportsTriggersCreated DateTotal LogsLast Log Date
26 |
27 | 28 |
29 | {% endblock %} 30 | {% block scripts %} 31 | 36 | 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/log_page.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Log Details{% endblock %} 3 | {% block content %} 4 |

Log Details for Log ID {{ log_id }}

5 | 6 | 7 |
8 | {% endblock %} 9 | 10 | {% block scripts %} 11 | 75 | 76 | 197 | {% endblock %} -------------------------------------------------------------------------------- /babyagi/dashboard/templates/log_relationship_graph.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Log Relationship Graph{% endblock %} 3 | {% block breadcrumb %} 4 | 7 | {% endblock %} 8 | {% block content %} 9 |

Log Relationship Graph

10 |
11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 | Parent Relationship 21 |
22 |
23 |
24 | Trigger Relationship 25 |
26 |
27 |
28 |
29 | × 30 |

31 |

32 |         
33 |
34 | {% endblock %} 35 | {% block scripts %} 36 | 37 | 40 | 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /babyagi/dashboard/templates/logs_dashboard.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}Logs Dashboard{% endblock %} 3 | {% block breadcrumb %} 4 | 7 | {% endblock %} 8 | {% block content %} 9 |

Logs Dashboard

10 | 11 | 12 |
13 | 14 | 18 | 19 | 20 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
IDFunction NameMessageTimestampLog TypeTime Spent (s)Parent Log IDTriggered By Log ID
54 | 55 | 56 |
57 | 58 |
59 | {% endblock %} 60 | {% block scripts %} 61 | 65 | 66 | 67 | {% endblock %} 68 | -------------------------------------------------------------------------------- /babyagi/functionz/__init__.py: -------------------------------------------------------------------------------- 1 | from .core.framework import func 2 | 3 | __all__ = ['func'] 4 | -------------------------------------------------------------------------------- /babyagi/functionz/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoheinakajima/babyagi/4f4fd1f62c2b0726479d6bc7bbf87463369a663e/babyagi/functionz/core/__init__.py -------------------------------------------------------------------------------- /babyagi/functionz/core/framework.py: -------------------------------------------------------------------------------- 1 | # core/framework.py 2 | 3 | import os 4 | import sys 5 | import importlib.util 6 | from typing import Optional, List, Dict, Any 7 | from datetime import datetime 8 | import logging 9 | 10 | from ..db.db_router import DBRouter 11 | from .execution import FunctionExecutor 12 | from .registration import FunctionRegistrar 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | class Functionz: 17 | def __init__(self, db_type='local', **db_kwargs): 18 | self.db = DBRouter(db_type, **db_kwargs) 19 | self.executor = FunctionExecutor(self) 20 | self.registrar = FunctionRegistrar(self) 21 | 22 | # Function execution 23 | def execute_function(self, function_name: str, *args, **kwargs): 24 | return self.executor.execute(function_name, *args, **kwargs) 25 | 26 | def __getattr__(self, name): 27 | if self.db.get_function(name): 28 | return lambda *args, **kwargs: self.executor.execute(name, *args, **kwargs) 29 | raise AttributeError(f"'PythonFunc' object has no attribute '{name}'") 30 | 31 | # Function management 32 | def get_function(self, name: str): 33 | return self.db.get_function(name) 34 | 35 | def get_function_versions(self, name: str): 36 | return self.db.get_function_versions(name) 37 | 38 | def get_all_functions(self) -> List[Dict[str, Any]]: 39 | return self.db.get_all_functions() 40 | 41 | def activate_function_version(self, name: str, version: int) -> None: 42 | self.db.activate_function_version(name, version) 43 | 44 | def get_function_imports(self, name: str): 45 | return self.db.get_function_imports(name) 46 | 47 | # Function registration (exposing registrar methods) 48 | def register_function(self, *args, **kwargs): 49 | return self.registrar.register_function(*args, **kwargs) 50 | 51 | def update_function(self, *args, **kwargs): 52 | return self.registrar.update_function(*args, **kwargs) 53 | 54 | def add_function(self, *args, **kwargs): 55 | return self.registrar.add_function(*args, **kwargs) 56 | 57 | # Key management 58 | def add_key(self, key_name: str, key_value: str) -> None: 59 | self.db.add_secret_key(key_name, key_value) 60 | 61 | def get_all_secret_keys(self, *args, **kwargs): 62 | return self.db.get_all_secret_keys(*args, **kwargs) 63 | 64 | # Import management 65 | def get_all_imports(self, *args, **kwargs): 66 | return self.db.get_all_imports(*args, **kwargs) 67 | 68 | # Function pack and file loading 69 | def load_function_pack(self, pack_name: str): 70 | packs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'packs') 71 | pack_path = os.path.join(packs_dir, pack_name + '.py') 72 | 73 | if not os.path.exists(pack_path): 74 | logger.error(f"Function pack '{pack_name}' not found.") 75 | return 76 | 77 | self._load_module_from_path(pack_path, pack_name) 78 | 79 | def load_functions_from_file(self, file_path: str): 80 | if not os.path.exists(file_path): 81 | logger.error(f"File '{file_path}' not found.") 82 | return 83 | 84 | module_name = os.path.splitext(os.path.basename(file_path))[0] 85 | self._load_module_from_path(file_path, module_name) 86 | 87 | def _load_module_from_path(self, path: str, module_name: str): 88 | spec = importlib.util.spec_from_file_location(module_name, path) 89 | module = importlib.util.module_from_spec(spec) 90 | module.func = self 91 | 92 | original_sys_path = sys.path[:] 93 | try: 94 | babyagi_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) 95 | if babyagi_root not in sys.path: 96 | sys.path.insert(0, babyagi_root) 97 | 98 | spec.loader.exec_module(module) 99 | logger.info(f"Loaded module '{module_name}' from '{path}'") 100 | except Exception as e: 101 | logger.error(f"Error loading module '{module_name}' from '{path}': {str(e)}") 102 | import traceback 103 | logger.error(traceback.format_exc()) 104 | finally: 105 | sys.path = original_sys_path 106 | 107 | # Trigger management 108 | def add_trigger(self, triggered_function_name, triggering_function_name=None): 109 | self.db.add_trigger(triggered_function_name, triggering_function_name) 110 | 111 | def get_triggers_for_function(self, function_name: str) -> List[str]: 112 | function_data = self.get_function(function_name) 113 | return function_data.get('triggers', []) if function_data else [] 114 | 115 | # Logging and display 116 | def get_logs(self, function_name: Optional[str] = None, 117 | start_date: Optional[datetime] = None, 118 | end_date: Optional[datetime] = None) -> List[Dict[str, Any]]: 119 | return self.db.get_logs(function_name, start_date, end_date) 120 | 121 | def display(self): 122 | functions = self.db.get_all_functions() 123 | result = [] 124 | 125 | for function in functions: 126 | function_info = [ 127 | f"Function: {function['name']}", 128 | f" Version: {function['version']}", 129 | f" Created Date: {function['created_date']}", 130 | f" Metadata: {function['metadata']}", 131 | f" Dependencies: {function['dependencies']}", 132 | f" Triggers: {function.get('triggers', [])}", 133 | " Input Parameters:", 134 | ] 135 | for param in function['input_parameters']: 136 | function_info.append(f" - {param['name']} ({param['type']})") 137 | 138 | function_info.append(" Output Parameters:") 139 | for param in function['output_parameters']: 140 | function_info.append(f" - {param['name']} ({param['type']})") 141 | 142 | function_info.append(f" Code:\n{function['code']}") 143 | function_info.append("---") 144 | 145 | result.append("\n".join(function_info)) 146 | 147 | return "\n\n".join(result) 148 | 149 | # Create the global 'func' instance 150 | func = Functionz() -------------------------------------------------------------------------------- /babyagi/functionz/db/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoheinakajima/babyagi/4f4fd1f62c2b0726479d6bc7bbf87463369a663e/babyagi/functionz/db/__init__.py -------------------------------------------------------------------------------- /babyagi/functionz/db/base_db.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Optional, List, Dict, Any 3 | from datetime import datetime 4 | 5 | class BaseDB(ABC): 6 | # Function management 7 | @abstractmethod 8 | def add_function(self, name: str, code: str, metadata: Optional[Dict[str, Any]] = None, 9 | dependencies: Optional[List[str]] = None, 10 | input_parameters: Optional[List[Dict[str, Any]]] = None, 11 | output_parameters: Optional[List[Dict[str, Any]]] = None) -> None: 12 | pass 13 | 14 | @abstractmethod 15 | def get_function(self, name: str) -> Optional[Dict[str, Any]]: 16 | pass 17 | 18 | @abstractmethod 19 | def get_all_functions(self) -> List[Dict[str, Any]]: 20 | pass 21 | 22 | @abstractmethod 23 | def update_function(self, name: str, code: Optional[str] = None, 24 | metadata: Optional[Dict[str, Any]] = None, 25 | dependencies: Optional[List[str]] = None, 26 | input_parameters: Optional[List[Dict[str, Any]]] = None, 27 | output_parameters: Optional[List[Dict[str, Any]]] = None) -> None: 28 | pass 29 | 30 | @abstractmethod 31 | def remove_function(self, name: str) -> None: 32 | pass 33 | 34 | @abstractmethod 35 | def get_function_versions(self, name: str) -> List[Dict[str, Any]]: 36 | pass 37 | 38 | @abstractmethod 39 | def activate_function_version(self, name: str, version: int) -> None: 40 | pass 41 | 42 | # Import management 43 | @abstractmethod 44 | def add_import(self, name: str, source: str, lib: Optional[str] = None) -> None: 45 | pass 46 | 47 | @abstractmethod 48 | def get_all_imports(self) -> List[Dict[str, Any]]: 49 | pass 50 | 51 | # Logging 52 | @abstractmethod 53 | def add_log(self, function_name: str, message: str, timestamp: datetime, 54 | params: Optional[Dict[str, Any]] = None, 55 | output: Optional[Any] = None, 56 | time_spent: Optional[float] = None) -> None: 57 | pass 58 | 59 | @abstractmethod 60 | def get_logs(self, function_name: Optional[str] = None, 61 | start_date: Optional[datetime] = None, 62 | end_date: Optional[datetime] = None) -> List[Dict[str, Any]]: 63 | pass -------------------------------------------------------------------------------- /babyagi/functionz/db/local_db.py: -------------------------------------------------------------------------------- 1 | # local_db.py 2 | 3 | from sqlalchemy import create_engine, or_ 4 | from sqlalchemy.orm import sessionmaker, scoped_session, joinedload 5 | from sqlalchemy.exc import SQLAlchemyError 6 | from contextlib import contextmanager 7 | from .models import Base, Function, FunctionVersion, Import, Log, SecretKey, fernet 8 | import datetime 9 | 10 | 11 | 12 | class LocalDB: 13 | def __init__(self, db_path='sqlite:///funztionz.db'): 14 | self.engine = create_engine(db_path) 15 | Base.metadata.create_all(self.engine) 16 | self.Session = scoped_session(sessionmaker(bind=self.engine)) 17 | 18 | @contextmanager 19 | def session_scope(self): 20 | session = self.Session() 21 | try: 22 | yield session 23 | session.commit() 24 | except SQLAlchemyError as e: 25 | session.rollback() 26 | raise e 27 | finally: 28 | self.Session.remove() 29 | 30 | def serialize_for_json(self, obj): 31 | """ 32 | Recursively convert datetime objects to ISO format strings within the given object. 33 | Handles dictionaries, lists, and individual datetime objects. 34 | """ 35 | if isinstance(obj, dict): 36 | return {k: self.serialize_for_json(v) for k, v in obj.items()} 37 | elif isinstance(obj, list): 38 | return [self.serialize_for_json(element) for element in obj] 39 | elif isinstance(obj, datetime.datetime): 40 | return obj.isoformat() 41 | else: 42 | return obj 43 | 44 | 45 | def get_function(self, session, name): 46 | return session.query(Function).filter_by(name=name).first() 47 | 48 | def get_active_version(self, session, function): 49 | return session.query(FunctionVersion).filter_by(function_id=function.id, is_active=True).options( 50 | joinedload(FunctionVersion.dependencies) 51 | ).first() 52 | 53 | def get_all_functions(self, session): 54 | return session.query(Function).options( 55 | joinedload(Function.versions).joinedload(FunctionVersion.dependencies) 56 | ).all() 57 | 58 | def add_or_update_function(self, session, name, code, metadata, dependencies, triggers, input_parameters, output_parameters, imports=None): 59 | function = self.get_function(session, name) 60 | if not function: 61 | function = Function(name=name) 62 | session.add(function) 63 | session.flush() 64 | 65 | # Handle imports before creating the FunctionVersion 66 | import_objects = [] 67 | if imports: 68 | for import_name in imports: 69 | imp = session.query(Import).filter_by(name=import_name).first() 70 | if not imp: 71 | imp = Import(name=import_name, source='external') 72 | session.add(imp) 73 | session.flush() 74 | import_objects.append(imp) 75 | 76 | # Create the FunctionVersion instance, now including triggers as JSON 77 | version = FunctionVersion( 78 | function=function, 79 | version=len(function.versions) + 1, 80 | code=code, 81 | function_metadata=metadata or {}, 82 | is_active=True, 83 | input_parameters=input_parameters or [], 84 | output_parameters=output_parameters or [], 85 | imports=import_objects, # Pass the list of Import objects here 86 | triggers=triggers or [] # Store the triggers as JSON 87 | ) 88 | session.add(version) 89 | 90 | # Handle dependencies 91 | if dependencies: 92 | for dep in dependencies: 93 | dep_func = self.get_function(session, dep) 94 | if dep_func: 95 | version.dependencies.append(dep_func) 96 | 97 | # Deactivate previous versions 98 | for v in function.versions: 99 | if v != version: 100 | v.is_active = False 101 | 102 | 103 | def add_import(self, session, name, source, lib=None): 104 | existing_import = session.query(Import).filter_by(name=name).first() 105 | if not existing_import: 106 | new_import = Import(name=name, source=source, lib=lib) 107 | session.add(new_import) 108 | 109 | 110 | 111 | def add_log(self, session, function_name, message, timestamp, params, output, time_spent, parent_log_id=None, triggered_by_log_id=None, log_type='info'): 112 | if isinstance(timestamp, str): 113 | # Convert the string timestamp back to a datetime object 114 | timestamp = datetime.datetime.fromisoformat(timestamp) 115 | 116 | # Serialize params and output to ensure JSON serializability 117 | serialized_params = self.serialize_for_json(params) if params else None 118 | serialized_output = self.serialize_for_json(output) if output else None 119 | 120 | new_log = Log( 121 | function_name=function_name, 122 | message=message, 123 | timestamp=timestamp, 124 | params=serialized_params, 125 | output=serialized_output, 126 | time_spent=time_spent, 127 | parent_log_id=parent_log_id, 128 | triggered_by_log_id=triggered_by_log_id, 129 | log_type=log_type 130 | ) 131 | session.add(new_log) 132 | session.flush() # This ensures new_log.id is populated 133 | return new_log.id 134 | 135 | def update_log(self, session, log_id: int, **kwargs) -> None: 136 | # Fetch the log entry by id 137 | log_entry = session.query(Log).filter(Log.id == log_id).first() 138 | if log_entry: 139 | # Update only the fields provided in kwargs 140 | for key, value in kwargs.items(): 141 | if hasattr(log_entry, key): 142 | setattr(log_entry, key, value) 143 | else: 144 | raise ValueError(f"Log has no attribute '{key}'") 145 | # No need to call session.commit(); it will be committed in the session scope 146 | else: 147 | raise ValueError(f"Log with id {log_id} not found.") 148 | 149 | 150 | 151 | def update_log_params(self, session, log_id: int, params) -> None: 152 | log_entry = session.query(Log).filter_by(id=log_id).one_or_none() 153 | if log_entry is None: 154 | raise ValueError(f"Log entry with id {log_id} not found.") 155 | 156 | log_entry.params = params 157 | session.commit() 158 | 159 | 160 | 161 | def get_log(self, session, log_id: int): 162 | """ 163 | Fetches a single log entry by its ID. 164 | 165 | :param session: SQLAlchemy session object. 166 | :param log_id: The ID of the log to retrieve. 167 | :return: Log object if found, else None. 168 | """ 169 | return session.query(Log).filter_by(id=log_id).first() 170 | 171 | def get_child_logs(self, session, parent_id: int): 172 | """ 173 | Retrieves all child logs that have the given parent_log_id. 174 | 175 | :param session: SQLAlchemy session object. 176 | :param parent_id: The ID of the parent log. 177 | :return: List of Log objects. 178 | """ 179 | return session.query(Log).filter_by(parent_log_id=parent_id).all() 180 | 181 | def get_logs(self, session, function_name=None, start_date=None, end_date=None, triggered_by_log_id=None): 182 | query = session.query(Log) 183 | 184 | if function_name: 185 | query = query.filter(Log.function_name == function_name) 186 | 187 | if start_date: 188 | query = query.filter(Log.timestamp >= start_date) 189 | 190 | if end_date: 191 | query = query.filter(Log.timestamp <= end_date) 192 | 193 | if triggered_by_log_id: 194 | query = query.filter(Log.triggered_by_log_id == triggered_by_log_id) 195 | 196 | return query.all() 197 | 198 | 199 | def get_log_bundle(self, session, log_id): 200 | logs_collected = {} 201 | 202 | def fetch_related_logs(current_log_id): 203 | if current_log_id in logs_collected: 204 | return 205 | log = session.query(Log).filter_by(id=current_log_id).one_or_none() 206 | if not log: 207 | return 208 | logs_collected[current_log_id] = log 209 | 210 | # Fetch parent log 211 | if log.parent_log_id: 212 | fetch_related_logs(log.parent_log_id) 213 | 214 | # Fetch sibling logs 215 | sibling_logs = session.query(Log).filter( 216 | Log.parent_log_id == log.parent_log_id, 217 | Log.id != current_log_id 218 | ).all() 219 | for sibling in sibling_logs: 220 | if sibling.id not in logs_collected: 221 | fetch_related_logs(sibling.id) 222 | 223 | # Fetch child logs 224 | child_logs = session.query(Log).filter_by(parent_log_id=current_log_id).all() 225 | for child in child_logs: 226 | if child.id not in logs_collected: 227 | fetch_related_logs(child.id) 228 | 229 | fetch_related_logs(log_id) 230 | return list(logs_collected.values()) 231 | 232 | 233 | 234 | 235 | def add_secret_key(self, session, function_id, key_name, key_value): 236 | print(f"Encrypting value for key '{key_name}'") 237 | try: 238 | encrypted_value = fernet.encrypt(key_value.encode()) 239 | print(f"Value encrypted successfully for key '{key_name}'") 240 | secret_key = SecretKey(function_id=function_id, name=key_name, _encrypted_value=encrypted_value) 241 | session.add(secret_key) 242 | print(f"Secret key '{key_name}' added to session") 243 | except Exception as e: 244 | print(f"Error in add_secret_key: {str(e)}") 245 | raise 246 | 247 | 248 | def add_secret_key(self, session, key_name, key_value): 249 | encrypted_value = fernet.encrypt(key_value.encode()) 250 | secret_key = SecretKey(name=key_name, _encrypted_value=encrypted_value) 251 | session.add(secret_key) 252 | 253 | def get_secret_key(self, session, key_name): 254 | return session.query(SecretKey).filter_by(name=key_name).first() 255 | 256 | 257 | def get_all_secret_keys(self, session): 258 | return session.query(SecretKey).all() 259 | 260 | -------------------------------------------------------------------------------- /babyagi/functionz/db/models.py: -------------------------------------------------------------------------------- 1 | # models.py 2 | 3 | from sqlalchemy import Column, Integer, String, DateTime, JSON, ForeignKey, Boolean, Table, Float, LargeBinary 4 | from sqlalchemy.ext.declarative import declarative_base 5 | from sqlalchemy.orm import relationship 6 | from cryptography.fernet import Fernet 7 | from cryptography.fernet import InvalidToken 8 | from sqlalchemy.ext.hybrid import hybrid_property 9 | import os 10 | import json 11 | from datetime import datetime 12 | 13 | Base = declarative_base() 14 | 15 | KEY_FILE = 'encryption_key.json' 16 | 17 | def get_or_create_key(): 18 | if os.path.exists(KEY_FILE): 19 | with open(KEY_FILE, 'r') as f: 20 | return json.load(f)['key'] 21 | else: 22 | key = Fernet.generate_key().decode() 23 | with open(KEY_FILE, 'w') as f: 24 | json.dump({'key': key}, f) 25 | return key 26 | 27 | ENCRYPTION_KEY = get_or_create_key() 28 | print(f"Using encryption key: {ENCRYPTION_KEY}") 29 | fernet = Fernet(ENCRYPTION_KEY.encode()) 30 | 31 | # Association table for function dependencies (many-to-many between FunctionVersion and Function) 32 | function_dependency = Table('function_dependency', Base.metadata, 33 | Column('function_version_id', Integer, ForeignKey('function_versions.id')), 34 | Column('dependency_id', Integer, ForeignKey('functions.id')) 35 | ) 36 | 37 | # **Define function_version_imports association table here** 38 | function_version_imports = Table('function_version_imports', Base.metadata, 39 | Column('function_version_id', Integer, ForeignKey('function_versions.id')), 40 | Column('import_id', Integer, ForeignKey('imports.id')) 41 | ) 42 | 43 | 44 | class Function(Base): 45 | __tablename__ = 'functions' 46 | id = Column(Integer, primary_key=True) 47 | name = Column(String, unique=True) 48 | versions = relationship("FunctionVersion", back_populates="function", cascade="all, delete-orphan") 49 | 50 | class FunctionVersion(Base): 51 | __tablename__ = 'function_versions' 52 | id = Column(Integer, primary_key=True) 53 | function_id = Column(Integer, ForeignKey('functions.id')) 54 | version = Column(Integer) 55 | code = Column(String) 56 | function_metadata = Column(JSON) 57 | is_active = Column(Boolean, default=False) 58 | created_date = Column(DateTime, default=datetime.utcnow) 59 | input_parameters = Column(JSON) 60 | output_parameters = Column(JSON) 61 | function = relationship("Function", back_populates="versions") 62 | dependencies = relationship('Function', secondary=function_dependency, 63 | primaryjoin=(function_dependency.c.function_version_id == id), 64 | secondaryjoin=(function_dependency.c.dependency_id == Function.id)) 65 | imports = relationship('Import', secondary=function_version_imports, back_populates='function_versions') 66 | triggers = Column(JSON, nullable=True) # Store triggers as a JSON field 67 | 68 | 69 | 70 | class Import(Base): 71 | __tablename__ = 'imports' 72 | id = Column(Integer, primary_key=True) 73 | name = Column(String, unique=True) 74 | lib = Column(String, nullable=True) 75 | source = Column(String) 76 | function_versions = relationship('FunctionVersion', secondary=function_version_imports, back_populates='imports') 77 | 78 | 79 | class Log(Base): 80 | __tablename__ = 'logs' 81 | 82 | id = Column(Integer, primary_key=True) 83 | function_name = Column(String, nullable=False) 84 | message = Column(String, nullable=False) 85 | timestamp = Column(DateTime, nullable=False) 86 | params = Column(JSON, nullable=True) 87 | output = Column(JSON, nullable=True) 88 | time_spent = Column(Float, nullable=True) 89 | log_type = Column(String, nullable=False) 90 | 91 | # Parent Log Relationship 92 | parent_log_id = Column(Integer, ForeignKey('logs.id'), nullable=True) 93 | parent_log = relationship( 94 | 'Log', 95 | remote_side=[id], 96 | backref='child_logs', 97 | foreign_keys=[parent_log_id] 98 | ) 99 | 100 | # Triggered By Log Relationship 101 | triggered_by_log_id = Column(Integer, ForeignKey('logs.id'), nullable=True) 102 | triggered_by_log = relationship( 103 | 'Log', 104 | remote_side=[id], 105 | backref='triggered_logs', 106 | foreign_keys=[triggered_by_log_id] 107 | ) 108 | 109 | 110 | class SecretKey(Base): 111 | __tablename__ = 'secret_keys' 112 | id = Column(Integer, primary_key=True) 113 | name = Column(String, nullable=False, unique=True) # Make name unique 114 | _encrypted_value = Column(LargeBinary, nullable=False) 115 | 116 | @hybrid_property 117 | def value(self): 118 | if self._encrypted_value: 119 | try: 120 | return fernet.decrypt(self._encrypted_value).decode() 121 | except InvalidToken: 122 | print(f"Error decrypting value for key: {self.name}. The encryption key may have changed.") 123 | return None 124 | return None 125 | 126 | @value.setter 127 | def value(self, plaintext_value): 128 | if plaintext_value: 129 | self._encrypted_value = fernet.encrypt(plaintext_value.encode()) 130 | else: 131 | self._encrypted_value = None 132 | 133 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/default/ai_functions.py: -------------------------------------------------------------------------------- 1 | # packs/ai_generator.py 2 | 3 | from functionz.core.framework import func 4 | 5 | @func.register_function( 6 | metadata={"description": "GPT Call function using LiteLLm"}, 7 | imports=["litellm"], 8 | key_dependencies=["openai_api_key"] 9 | ) 10 | def gpt_call(prompt: str) -> str: 11 | from litellm import completion 12 | messages = [{"role": "user", "content": prompt}] 13 | response = completion(model="gpt-4o", messages=messages) 14 | return response['choices'][0]['message']['content'] 15 | 16 | @func.register_function( 17 | metadata={"description": "Generates a description for a function using LiteLLm"}, 18 | dependencies=["gpt_call"] 19 | ) 20 | def description_writer(function_code: str) -> str: 21 | prompt = ( 22 | f"Provide a concise and clear description for the following Python function:\n\n" 23 | f"{function_code}\n\n" 24 | f"Description:" 25 | ) 26 | description = func.gpt_call(prompt) 27 | return description 28 | 29 | @func.register_function( 30 | metadata={"description": "Generates and updates descriptions for functions lacking one or having an empty description"}, 31 | dependencies=["description_writer"], 32 | triggers=["function_added_or_updated"] 33 | ) 34 | def ai_description_generator(function_name: str) -> None: 35 | print(f"Generating AI description for function: {function_name}") 36 | function = func.db.get_function(function_name) 37 | if not function: 38 | print(f"Function '{function_name}' not found in the database.") 39 | return 40 | 41 | description = function.get('metadata', {}).get('description', '').strip() 42 | function_code = function.get('code', '') 43 | 44 | if not description and function_code.strip(): 45 | #print(f"Generating description for function '{function_name}'.") 46 | generated_description = func.description_writer(function_code) 47 | func.update_function( 48 | name=function_name, 49 | metadata={"description": generated_description} 50 | ) 51 | print(f"Description for function '{function_name}' has been generated and updated.") 52 | return f"Description for function '{function_name}' has been generated and updated." 53 | elif not function_code.strip(): 54 | print(f"Function '{function_name}' has no code to generate a description.") 55 | return f"Function '{function_name}' has no code to generate a description." 56 | else: 57 | print(f"Function '{function_name}' already has a non-empty description.") 58 | return f"Function '{function_name}' already has a non-empty description." 59 | 60 | @func.register_function( 61 | metadata={"description": "Scans all functions and generates descriptions for those lacking one"}, 62 | dependencies=["ai_description_generator"] 63 | ) 64 | def generate_missing_descriptions() -> None: 65 | all_functions = func.db.get_all_functions() 66 | missing_description_functions = [ 67 | func_info['name'] for func_info in all_functions 68 | if not func_info.get('metadata', {}).get('description') 69 | ] 70 | if not missing_description_functions: 71 | print("All functions already have descriptions.") 72 | return 73 | print(f"Found {len(missing_description_functions)} function(s) without descriptions. Generating descriptions...") 74 | for function_name in missing_description_functions: 75 | func.ai_description_generator(function_name) 76 | print("Description generation process completed.") 77 | 78 | 79 | @func.register_function( 80 | metadata={"description": "Embeds an input using LiteLLM"}, 81 | imports=["litellm"], 82 | key_dependencies=["openai_api_key"] 83 | ) 84 | def embed_input(input_text: str, model: str = "text-embedding-ada-002", 85 | encoding_format: str = "float", dimensions: int = None, 86 | timeout: int = 600) -> list: 87 | from litellm import embedding 88 | import os 89 | 90 | # Set OpenAI API Key from environment variables 91 | os.environ['OPENAI_API_KEY'] = os.getenv('OPENAI_API_KEY') 92 | 93 | # Prepare the embedding request with optional parameters 94 | embedding_params = { 95 | "model": model, 96 | "input": [input_text], 97 | "encoding_format": encoding_format, 98 | "timeout": timeout 99 | } 100 | 101 | if dimensions: 102 | embedding_params["dimensions"] = dimensions 103 | 104 | # Call the LiteLLM embedding function 105 | response = embedding(**embedding_params) 106 | 107 | # Return the embedding from the response 108 | return response['data'][0]['embedding'] 109 | 110 | 111 | @func.register_function( 112 | metadata={"description": "Embeds and updates a function's description if it exists"}, 113 | dependencies=["embed_input"], 114 | imports=["os","csv"] 115 | ) 116 | def embed_function_description(function: str) -> None: 117 | print(f"Embedding description for function: {function}") 118 | # Retrieve the function details from the database 119 | function_data = func.db.get_function(function) 120 | if not function_data: 121 | print(f"Function '{function}' not found in the database.") 122 | return 123 | 124 | description = function_data.get('metadata', {}).get('description', '').strip() 125 | if description: 126 | print(f"Embedding description for function '{function}'.") 127 | embedding = func.embed_input(description) 128 | 129 | # Check if 'function_embeddings.csv' exists, create it if not 130 | file_path = 'function_embeddings.csv' 131 | file_exists = os.path.isfile(file_path) 132 | 133 | # Create a list to store CSV data 134 | rows = [] 135 | 136 | if file_exists: 137 | with open(file_path, mode='r') as file: 138 | reader = csv.reader(file) 139 | rows = list(reader) 140 | 141 | # Look for the function in the existing rows 142 | function_found = False 143 | for i, row in enumerate(rows): 144 | if row[0] == function: # function column is the first column 145 | rows[i][1] = str(embedding) # Update the embedding 146 | function_found = True 147 | print(f"Updated embedding for function '{function}'.") 148 | 149 | if not function_found: 150 | # Add a new row if the function is not found 151 | rows.append([function, str(embedding)]) 152 | print(f"Added new function '{function}' with its embedding.") 153 | 154 | # Write back the data to the CSV file (create or update) 155 | with open(file_path, mode='w', newline='') as file: 156 | writer = csv.writer(file) 157 | writer.writerows(rows) 158 | 159 | print(f"Embedding for function '{function}' has been saved.") 160 | return embedding 161 | else: 162 | print(f"Function '{function}' has no description to embed.") 163 | return f"Function '{function}' has no description to embed." 164 | 165 | 166 | 167 | @func.register_function( 168 | metadata={"description": "Finds similar functions based on the provided description"}, 169 | dependencies=["embed_input"], 170 | imports=["numpy", "csv", "sklearn","os"] 171 | ) 172 | def find_similar_function(description: str, top_n: int = 3): 173 | import numpy as np 174 | from sklearn.metrics.pairwise import cosine_similarity 175 | # Step 1: Embed the input description 176 | #print(f"Embedding input description: {description}") 177 | input_embedding = func.embed_input(description) 178 | 179 | # Step 2: Load stored embeddings and descriptions from CSV 180 | file_path = 'function_embeddings.csv' 181 | stored_embeddings = [] 182 | stored_functions = [] 183 | 184 | if not os.path.isfile(file_path): 185 | print(f"No embeddings found in {file_path}.") 186 | return [] 187 | 188 | with open(file_path, mode='r') as file: 189 | reader = csv.reader(file) 190 | for row in reader: 191 | stored_functions.append(row[0]) 192 | stored_embeddings.append(np.fromstring(row[1].strip("[]"), sep=',')) 193 | 194 | if not stored_embeddings: 195 | print("No embeddings stored.") 196 | return [] 197 | 198 | # Step 3: Calculate cosine similarity between the input embedding and stored embeddings 199 | similarities = cosine_similarity([input_embedding], stored_embeddings) 200 | 201 | # Step 4: Sort stored functions by similarity 202 | sorted_indices = np.argsort(similarities[0])[::-1] # Sort in descending order 203 | 204 | # Step 5: Return the top N most similar functions 205 | similar_functions = [stored_functions[i] for i in sorted_indices[:top_n]] 206 | 207 | print(f"Top {top_n} similar functions: {similar_functions}") 208 | return similar_functions 209 | 210 | 211 | @func.register_function( 212 | metadata={"description": "Generates embeddings for functions missing from the CSV"}, 213 | dependencies=["embed_function_description"], 214 | imports=["os", "csv"] 215 | ) 216 | def generate_missing_embeddings() -> None: 217 | # Step 1: Retrieve all functions from the database 218 | all_functions = func.db.get_all_functions() 219 | all_function_names = [func_info['name'] for func_info in all_functions] 220 | 221 | # Step 2: Check if 'function_embeddings.csv' exists 222 | file_path = 'function_embeddings.csv' 223 | file_exists = os.path.isfile(file_path) 224 | 225 | # Read existing embeddings from CSV if the file exists 226 | embedded_functions = [] 227 | if file_exists: 228 | with open(file_path, mode='r') as file: 229 | reader = csv.reader(file) 230 | embedded_functions = [row[0] for row in reader] # First column is the function name 231 | 232 | # Step 3: Find functions without embeddings 233 | missing_embeddings = [func_name for func_name in all_function_names if func_name not in embedded_functions] 234 | 235 | if not missing_embeddings: 236 | print("All functions already have embeddings.") 237 | return 238 | 239 | print(f"Found {len(missing_embeddings)} function(s) without embeddings. Generating embeddings...") 240 | 241 | # Step 4: Embed the functions that are missing 242 | for function_name in missing_embeddings: 243 | print(f"Embedding function: {function_name}") 244 | func.embed_function_description(function_name) 245 | 246 | print("Embedding generation process completed.") 247 | 248 | 249 | 250 | @func.register_function( 251 | metadata={"description": "Chooses a function to use"}, 252 | dependencies=["get_all_functions","gpt_call"] 253 | ) 254 | def choose_function(prompt: str) -> str: 255 | functions = func.get_all_functions() 256 | prompt = ( 257 | f"Which functions are most relevant to the following input? It could be ones to use or look at as reference to build a new one:\n\n" 258 | f"{prompt}\n\n" 259 | f"Functions:{functions}" 260 | ) 261 | choice = func.gpt_call(prompt) 262 | return {"functions":functions,"choice":choice} -------------------------------------------------------------------------------- /babyagi/functionz/packs/default/default_functions.py: -------------------------------------------------------------------------------- 1 | # packs/default_functions.py 2 | 3 | from babyagi.functionz.core.framework import func 4 | from datetime import datetime 5 | from typing import Optional, Dict, Any, List 6 | 7 | @func.register_function() 8 | def execute_function_wrapper(function_name: str, *args, **kwargs): 9 | # Create an initial log for the wrapper 10 | wrapper_log_id = func.db.add_log( 11 | function_name="execute_function_wrapper", 12 | message="Wrapper execution started.", 13 | timestamp=datetime.now(), 14 | params={"function_name": function_name, "args": args, "kwargs": kwargs}, 15 | output=None, 16 | time_spent=0, 17 | parent_log_id=None, 18 | triggered_by_log_id=None, 19 | log_type='started' 20 | ) 21 | # Execute the function with the wrapper's log ID as the parent ID 22 | result = func.execute_function(function_name, *args, parent_log_id=wrapper_log_id, **kwargs) 23 | 24 | # Update the wrapper log after execution 25 | func.db.update_log(wrapper_log_id, output=result, log_type='success', message="Wrapper execution completed.") 26 | return result 27 | 28 | 29 | @func.register_function( 30 | metadata={ 31 | "description": "Dynamically adds a new function to the system with the provided code and metadata." 32 | }, 33 | imports=["typing"] 34 | ) 35 | def add_new_function( 36 | name: str, 37 | code: str, 38 | metadata: dict = None, 39 | imports: list = None, 40 | dependencies: list = None, 41 | key_dependencies: list = None, 42 | triggers: list = None 43 | ) -> bool: 44 | 45 | try: 46 | func.registrar.add_function( 47 | name=name, 48 | code=code, 49 | metadata=metadata, 50 | imports=imports, 51 | dependencies=dependencies, 52 | key_dependencies=key_dependencies, 53 | triggers=triggers 54 | ) 55 | #print(f"Function '{name}' added successfully.") 56 | return True 57 | except Exception as e: 58 | print(f"Error adding function '{name}': {str(e)}") 59 | return False 60 | 61 | 62 | @func.register_function() 63 | def function_added_or_updated(action=None, triggered_function_name=None): 64 | """ 65 | Triggered whenever a function is added or updated. 66 | """ 67 | print(f"Function '{triggered_function_name}' has been {action}.") 68 | function_data = func.get_function(triggered_function_name) if triggered_function_name else None 69 | if function_data: 70 | return triggered_function_name 71 | else: 72 | print(f"Function '{triggered_function_name}' not found in the database.") 73 | return None 74 | 75 | @func.register_function( 76 | metadata={"description": "Add a secret key to the database."}, 77 | imports=["os"] 78 | ) 79 | def add_key_wrapper(key_name: str, key_value: str): 80 | return func.add_key(key_name, key_value) 81 | 82 | @func.register_function(metadata={"description": "Get all versions of a given function."}) 83 | def get_function_versions_wrapper(name: str): 84 | return func.get_function_versions(name) 85 | 86 | @func.register_function() 87 | def get_function_wrapper(name: str): 88 | return func.get_function(name) 89 | 90 | 91 | @func.register_function(metadata={"description": "Get all versions of a given function."}) 92 | def get_all_functions_wrapper(): 93 | return func.get_all_functions() 94 | 95 | @func.register_function(metadata={"description": "Activate a specific version of a function."}) 96 | def activate_function_version_wrapper(name: str, version: int): 97 | return func.activate_function_version(name, version) 98 | 99 | @func.register_function(metadata={"description": "Display all registered functions and their metadata."}) 100 | def display_functions_wrapper(): 101 | return func.display() 102 | 103 | @func.register_function( 104 | metadata={"description": "Get logs for a specific function, optionally filtered by date."}, 105 | imports=['datetime'] 106 | ) 107 | def get_logs_wrapper(function_name: str = None, start_date: datetime = None, 108 | end_date: datetime = None): 109 | return func.get_logs(function_name, start_date, end_date) 110 | 111 | @func.register_function(metadata={"description": "Add a trigger that executes a specific function when another function is executed."}) 112 | def add_trigger_wrapper(triggered_function_name: str, triggering_function_name: Optional[str] = None): 113 | return func.add_trigger(triggered_function_name, triggering_function_name) 114 | 115 | 116 | @func.register_function(metadata={"description": "Get all secret keys stored."}) 117 | def get_all_secret_keys(): 118 | return func.get_all_secret_keys() 119 | 120 | 121 | @func.register_function(metadata={"description": "Get all imports."}) 122 | def get_all_imports_wrapper(): 123 | return func.get_all_imports() -------------------------------------------------------------------------------- /babyagi/functionz/packs/default/function_calling_chat.py: -------------------------------------------------------------------------------- 1 | from functionz.core.framework import func 2 | import json 3 | import litellm 4 | 5 | # Assuming `func` is the registry from your framework 6 | # and `execute_function_wrapper` is already registered in the database. 7 | 8 | @func.register_function( 9 | metadata={ 10 | "description": "A chat application that interacts with LiteLLM and executes selected functions from the database." 11 | }, 12 | imports=["litellm", "json"], 13 | dependencies=["get_function_wrapper", "execute_function_wrapper"], 14 | key_dependencies=["OPENAI_API_KEY"] # Ensure this key is set in your environment 15 | ) 16 | def chat_with_functions(chat_history, available_function_names) -> str: 17 | def map_python_type_to_json(python_type: str) -> dict: 18 | """ 19 | Maps Python type annotations to JSON Schema types. 20 | 21 | Args: 22 | python_type (str): The Python type as a string. 23 | 24 | Returns: 25 | dict: The corresponding JSON Schema type with additional details if necessary. 26 | """ 27 | type_mapping = { 28 | "str": {"type": "string"}, 29 | "int": {"type": "integer"}, 30 | "float": {"type": "number"}, 31 | "bool": {"type": "boolean"}, 32 | "list": {"type": "array", "items": {"type": "string"}}, # Assuming list of strings 33 | "dict": {"type": "object"}, 34 | "Any": {"type": "string"} # Default to string for unsupported types 35 | } 36 | return type_mapping.get(python_type, {"type": "string"}) 37 | 38 | # Enable verbose logging for LiteLLM 39 | litellm.set_verbose = True 40 | 41 | # Initialize chat context with system message 42 | chat_context = [ 43 | {"role": "system", "content": "You are a helpful assistant."} 44 | ] 45 | 46 | # Validate and append chat history 47 | if not isinstance(chat_history, list): 48 | raise ValueError("chat_history must be a list of messages.") 49 | 50 | for message in chat_history: 51 | if not isinstance(message, dict): 52 | raise ValueError("Each message in chat_history must be a dictionary.") 53 | role = message.get('role') 54 | content = message.get('message') 55 | if role not in ['user', 'assistant', 'system']: 56 | raise ValueError("Message role must be 'user', 'assistant', or 'system'.") 57 | if not isinstance(content, str): 58 | raise ValueError("Message content must be a string.") 59 | chat_context.append({"role": role, "content": content}) 60 | 61 | # Handle available_function_names input 62 | if isinstance(available_function_names, str): 63 | # Split the string by commas and strip whitespace 64 | available_function_names = [name.strip() for name in available_function_names.split(',') if name.strip()] 65 | elif isinstance(available_function_names, list): 66 | # Ensure all elements are strings and strip whitespace 67 | available_function_names = [name.strip() for name in available_function_names if isinstance(name, str) and name.strip()] 68 | else: 69 | raise ValueError("available_function_names must be a string or a list of strings.") 70 | 71 | if not available_function_names: 72 | raise ValueError("No valid function names provided in available_function_names.") 73 | 74 | # Fetch available functions from the database 75 | tools = [] 76 | for func_name in available_function_names: 77 | # Retrieve function details using the get_function_wrapper 78 | function_data = get_function_wrapper(func_name) 79 | if function_data: 80 | # Construct the tool definition for LiteLLM 81 | tool = { 82 | "type": "function", 83 | "function": { 84 | "name": function_data['name'], 85 | "description": function_data['metadata']['description'], 86 | "parameters": { 87 | "type": "object", 88 | "properties": {}, 89 | "required": [] 90 | }, 91 | }, 92 | } 93 | 94 | # Map input_parameters to the tool's parameters 95 | for param in function_data.get('input_parameters', []): 96 | # Convert Python types to JSON Schema types 97 | json_schema = map_python_type_to_json(param['type']) 98 | tool['function']['parameters']['properties'][param['name']] = { 99 | **json_schema, 100 | "description": param.get('description', '') 101 | } 102 | if param.get('required', False): 103 | tool['function']['parameters']['required'].append(param['name']) 104 | 105 | tools.append(tool) 106 | else: 107 | # Handle the case where the function is not found 108 | raise ValueError(f"Function '{func_name}' not found in the database.") 109 | 110 | 111 | # Call LiteLLM's completion API with the user message and available tools 112 | response = litellm.completion( 113 | model="gpt-4-turbo", 114 | messages=chat_context, 115 | tools=tools, 116 | tool_choice="auto" 117 | ) 118 | 119 | # Extract the message from the response 120 | response_message = response['choices'][0]['message'] 121 | 122 | # Check if the model wants to call any functions 123 | tool_calls = response_message.get('tool_calls', []) 124 | 125 | # If there are function calls, execute them 126 | if tool_calls: 127 | # Append the assistant's message to the chat context 128 | chat_context.append(response_message) 129 | 130 | for tool_call in tool_calls: 131 | function_name = tool_call['function']['name'] 132 | function_args = json.loads(tool_call['function']['arguments']) 133 | tool_call_id = tool_call['id'] # Extract the tool_call_id 134 | 135 | # Execute the function using execute_function_wrapper 136 | try: 137 | function_response = execute_function_wrapper(function_name, **function_args) 138 | except Exception as e: 139 | function_response = f"Error executing function '{function_name}': {str(e)}" 140 | 141 | # Ensure function_response is a string 142 | if not isinstance(function_response, str): 143 | function_response = json.dumps(function_response) 144 | 145 | # Append the function response to the chat context 146 | chat_context.append({ 147 | "tool_call_id": tool_call_id, # Include the tool_call_id 148 | "role": "tool", # Use 'tool' as per LiteLLM's protocol 149 | "name": function_name, 150 | "content": function_response 151 | }) 152 | 153 | # Call LiteLLM again with the updated context including function responses 154 | second_response = litellm.completion( 155 | model="gpt-4-turbo", 156 | messages=chat_context 157 | ) 158 | 159 | # Extract and return the assistant's final response 160 | assistant_response = second_response['choices'][0]['message']['content'] 161 | return assistant_response 162 | else: 163 | # If no functions are called, return the assistant's message directly 164 | assistant_response = response_message.get('content', '') 165 | return assistant_response 166 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/default/os.py: -------------------------------------------------------------------------------- 1 | @func.register_function( 2 | metadata={"description": "Returns the current directory and recursively lists all files and folders, excluding hidden files, folders (those starting with a '.'), and the '__pycache__' folder. The output omits the current directory prefix from file paths for readability."}, 3 | imports=["os"] 4 | ) 5 | def get_full_directory_contents_cleaned(): 6 | current_directory = os.getcwd() # Get current working directory 7 | directory_structure = {} 8 | 9 | # Walk through the directory and its subdirectories 10 | for root, dirs, files in os.walk(current_directory): 11 | # Filter out hidden directories, '__pycache__', and hidden files 12 | dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] 13 | files = [f for f in files if not f.startswith('.')] 14 | 15 | # Remove the current directory path from the root 16 | relative_root = root.replace(current_directory, "").lstrip(os.sep) 17 | directory_structure[relative_root] = { 18 | "folders": dirs, 19 | "files": files 20 | } 21 | 22 | return {"current_directory": current_directory, "directory_structure": directory_structure} 23 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/drafts/choose_or_create_function.py: -------------------------------------------------------------------------------- 1 | from functionz.core.framework import func 2 | 3 | @func.register_function( 4 | metadata={"description": "Choose or create a function based on user input and execute it."}, 5 | dependencies=[ 6 | "display_functions_wrapper", 7 | "get_function_wrapper", 8 | "execute_function_wrapper", 9 | "generate_function_from_description" 10 | ], 11 | imports=[ 12 | {"name": "litellm", "lib": "litellm"}, 13 | {"name": "pydantic", "lib": "pydantic"}, 14 | {"name": "typing", "lib": "typing"}, 15 | {"name": "json", "lib": "json"}, 16 | ] 17 | ) 18 | def choose_or_create_function(user_input: str) -> dict: 19 | """ 20 | Takes user input, compares against existing functions, decides whether to use an existing function or generate a new one, then executes the function with generated parameters. 21 | 22 | Args: 23 | user_input (str): The user's input or request. 24 | 25 | Returns: 26 | dict: A dictionary containing the result of the function execution, intermediate steps, and any relevant information. 27 | """ 28 | from litellm import completion 29 | from pydantic import BaseModel, Field, ValidationError 30 | from typing import List, Optional, Dict, Any 31 | import json 32 | 33 | intermediate_steps = [] 34 | 35 | # Step 1: Fetch existing functions 36 | try: 37 | existing_functions = display_functions_wrapper() 38 | print(f"[DEBUG] Existing Functions: {existing_functions}") 39 | intermediate_steps.append({"step": "Fetch Existing Functions", "content": existing_functions}) 40 | except Exception as e: 41 | print(f"[ERROR] Failed to fetch existing functions: {e}") 42 | intermediate_steps.append({"step": "Error Fetching Existing Functions", "content": str(e)}) 43 | return {"intermediate_steps": intermediate_steps, "error": "# Error fetching existing functions."} 44 | 45 | # Step 2: Use LLM to decide whether to use an existing function or generate a new one 46 | system_prompt = """ 47 | You are an assistant that helps decide whether an existing function can fulfill a user's request or if a new function needs to be created. 48 | 49 | Please analyze the user's input and the list of available functions. 50 | 51 | Return your decision in the following JSON format: 52 | 53 | { 54 | "use_existing_function": true or false, 55 | "function_name": "name of the existing function" (if applicable), 56 | "function_description": "description of the function to generate" (if applicable) 57 | } 58 | 59 | Provide only the JSON response, without any additional text. 60 | """ 61 | 62 | class FunctionDecision(BaseModel): 63 | use_existing_function: bool = Field(..., description="True if an existing function can be used; False if a new function needs to be generated.") 64 | function_name: Optional[str] = Field(None, description="Name of the existing function to use.") 65 | function_description: Optional[str] = Field(None, description="Description of the new function to generate.") 66 | 67 | decision_prompt = f""" 68 | The user has provided the following input: 69 | \"{user_input}\" 70 | 71 | Available Functions: 72 | {existing_functions} 73 | """ 74 | 75 | try: 76 | decision_response = completion( 77 | model="gpt-4o-mini", 78 | messages=[ 79 | {"role": "system", "content": system_prompt}, 80 | {"role": "user", "content": decision_prompt} 81 | ], 82 | response_format=FunctionDecision 83 | ) 84 | print(f"[DEBUG] Decision Response: {decision_response}") 85 | except Exception as e: 86 | print(f"[ERROR] LLM call for FunctionDecision failed: {e}") 87 | intermediate_steps.append({"step": "Error in FunctionDecision LLM Call", "content": str(e)}) 88 | return {"intermediate_steps": intermediate_steps, "error": "# Error during function decision analysis."} 89 | 90 | # Parse the response 91 | try: 92 | content = decision_response.choices[0].message.content 93 | print(f"[DEBUG] Raw Decision Content: {content}") 94 | decision_parsed = FunctionDecision.parse_raw(content) 95 | print(f"[DEBUG] Parsed FunctionDecision: {decision_parsed}") 96 | intermediate_steps.append({"step": "Function Decision", "content": decision_parsed.dict()}) 97 | except (ValidationError, IndexError, AttributeError, json.JSONDecodeError) as e: 98 | print(f"[ERROR] Parsing FunctionDecision response failed: {e}") 99 | intermediate_steps.append({"step": "Error Parsing FunctionDecision Response", "content": str(e)}) 100 | return {"intermediate_steps": intermediate_steps, "error": "# Error parsing FunctionDecision response."} 101 | 102 | if decision_parsed.use_existing_function and decision_parsed.function_name: 103 | function_name = decision_parsed.function_name 104 | print(f"[INFO] Using existing function: {function_name}") 105 | elif not decision_parsed.use_existing_function and decision_parsed.function_description: 106 | # Generate the new function 107 | print(f"[INFO] Generating new function based on description.") 108 | gen_result = generate_function_from_description(decision_parsed.function_description) 109 | intermediate_steps.extend(gen_result.get("intermediate_steps", [])) 110 | if not gen_result.get("added_to_database"): 111 | print(f"[ERROR] Failed to generate and add new function.") 112 | return {"intermediate_steps": intermediate_steps, "error": "# Error generating new function."} 113 | # Get the function name from the generated function code 114 | function_name = gen_result.get("function_name") 115 | if not function_name: 116 | # Extract function name from the code 117 | import re 118 | match = re.search(r'def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(', gen_result.get("final_code", "")) 119 | if match: 120 | function_name = match.group(1) 121 | print(f"[INFO] Extracted function name: {function_name}") 122 | else: 123 | print(f"[ERROR] Function name not found in generated code.") 124 | return {"intermediate_steps": intermediate_steps, "error": "# Function name not found in generated code."} 125 | else: 126 | print(f"[ERROR] Invalid decision or missing information.") 127 | return {"intermediate_steps": intermediate_steps, "error": "# Invalid function decision."} 128 | 129 | # Step 3: Get the function code using get_function_wrapper 130 | try: 131 | function_info = get_function_wrapper(function_name) 132 | if not function_info: 133 | print(f"[ERROR] Function {function_name} not found.") 134 | intermediate_steps.append({"step": "Error Fetching Function", "content": f"Function {function_name} not found."}) 135 | return {"intermediate_steps": intermediate_steps, "error": f"# Function {function_name} not found."} 136 | print(f"[DEBUG] Function Info: {function_info}") 137 | intermediate_steps.append({"step": "Fetch Function Info", "content": function_info}) 138 | except Exception as e: 139 | print(f"[ERROR] Fetching function info failed: {e}") 140 | intermediate_steps.append({"step": "Error Fetching Function Info", "content": str(e)}) 141 | return {"intermediate_steps": intermediate_steps, "error": "# Error fetching function info."} 142 | 143 | # Step 4: Use LLM to generate parameters for the function based on user input 144 | param_prompt = f""" 145 | The user has provided the following input: 146 | \"{user_input}\" 147 | 148 | The function to execute is: 149 | {function_info.get('code', '')} 150 | 151 | Generate a JSON object with a single key "parameters" that contains the parameters required by the function, filled in appropriately based on the user's input. 152 | 153 | Return only the JSON object, with no additional text. 154 | """ 155 | 156 | try: 157 | # Define a Pydantic model with a fixed field "parameters" 158 | class FunctionParameters(BaseModel): 159 | parameters: Dict[str, Any] 160 | 161 | class Config: 162 | extra = 'forbid' # This sets 'additionalProperties' to False 163 | 164 | param_response = completion( 165 | model="gpt-4o-mini", 166 | messages=[ 167 | {"role": "system", "content": "You are an assistant that provides only JSON-formatted data, with no additional text."}, 168 | {"role": "user", "content": param_prompt} 169 | ], 170 | response_format=FunctionParameters # Keep the same parsing format 171 | ) 172 | print(f"[DEBUG] Parameter Response: {param_response}") 173 | except Exception as e: 174 | print(f"[ERROR] LLM call for parameter generation failed: {e}") 175 | intermediate_steps.append({"step": "Error in Parameter Generation LLM Call", "content": str(e)}) 176 | return {"intermediate_steps": intermediate_steps, "error": "# Error generating parameters."} 177 | 178 | # Parse the response using the Pydantic model 179 | try: 180 | content = param_response.choices[0].message.content 181 | print(f"[DEBUG] Raw Parameter Content: {content}") 182 | function_params_model = FunctionParameters.parse_raw(content) 183 | function_params = function_params_model.parameters # Extract the parameters dictionary 184 | print(f"[DEBUG] Parsed Parameters: {function_params}") 185 | intermediate_steps.append({"step": "Generate Function Parameters", "content": function_params}) 186 | except (ValidationError, IndexError, AttributeError, json.JSONDecodeError) as e: 187 | print(f"[ERROR] Parsing parameters failed: {e}") 188 | intermediate_steps.append({"step": "Error Parsing Parameters", "content": str(e)}) 189 | return {"intermediate_steps": intermediate_steps, "error": "# Error parsing function parameters."} 190 | 191 | # Step 5: Execute the function using execute_function_wrapper 192 | try: 193 | # Ensure that function_params is a dictionary 194 | if not isinstance(function_params, dict): 195 | raise TypeError("function_params must be a dictionary") 196 | execution_result = execute_function_wrapper(function_name, **function_params) 197 | print(f"[DEBUG] Execution Result: {execution_result}") 198 | intermediate_steps.append({"step": "Execute Function", "content": execution_result}) 199 | except Exception as e: 200 | print(f"[ERROR] Function execution failed: {e}") 201 | intermediate_steps.append({"step": "Error Executing Function", "content": str(e)}) 202 | return {"intermediate_steps": intermediate_steps, "error": "# Error executing function."} 203 | 204 | return { 205 | "intermediate_steps": intermediate_steps, 206 | "execution_result": execution_result 207 | } -------------------------------------------------------------------------------- /babyagi/functionz/packs/drafts/react_agent.py: -------------------------------------------------------------------------------- 1 | from functionz.core.framework import func 2 | 3 | @func.register_function( 4 | metadata={ 5 | "description": "An agent that takes an input, plans using LLM, executes actions using functions from display_functions_wrapper(), and continues until the task is complete using chain-of-thought techniques while providing detailed reasoning and function execution steps." 6 | }, 7 | imports=["litellm", "json", "copy"], 8 | dependencies=["get_function_wrapper", "execute_function_wrapper", "get_all_functions_wrapper"], 9 | key_dependencies=["OPENAI_API_KEY"] 10 | ) 11 | def react_agent(input_text) -> str: 12 | def map_python_type_to_json(python_type: str) -> dict: 13 | type_mapping = { 14 | "str": {"type": "string"}, 15 | "int": {"type": "integer"}, 16 | "float": {"type": "number"}, 17 | "bool": {"type": "boolean"}, 18 | "list": {"type": "array", "items": {"type": "string"}}, 19 | "dict": {"type": "object"}, 20 | "Any": {"type": "string"} 21 | } 22 | return type_mapping.get(python_type, {"type": "string"}) 23 | 24 | try: 25 | # Enable verbose logging for LiteLLM 26 | litellm.set_verbose = True 27 | 28 | # Get available functions using get_all_functions_wrapper 29 | all_functions = get_all_functions_wrapper() 30 | 31 | # Extract function names from the structured data 32 | available_function_names = [func_info['name'] for func_info in all_functions] 33 | 34 | # Fetch available functions from the database 35 | tools = [] 36 | for func_name in available_function_names: 37 | # Retrieve function details using get_function_wrapper 38 | function_data = get_function_wrapper(func_name) 39 | if function_data: 40 | # Construct the tool definition for LiteLLM 41 | tool = { 42 | "type": "function", 43 | "function": { 44 | "name": function_data['name'], 45 | "description": function_data['metadata'].get('description', ''), 46 | "parameters": { 47 | "type": "object", 48 | "properties": {}, 49 | "required": [] 50 | }, 51 | }, 52 | } 53 | 54 | # Map input_parameters to the tool's parameters 55 | for param in function_data.get('input_parameters', []): 56 | json_schema = map_python_type_to_json(param['type']) 57 | tool['function']['parameters']['properties'][param['name']] = { 58 | **json_schema, 59 | "description": param.get('description', '') 60 | } 61 | if param.get('required', False): 62 | tool['function']['parameters']['required'].append(param['name']) 63 | 64 | tools.append(tool) 65 | else: 66 | raise ValueError(f"Function '{func_name}' not found in the database.") 67 | 68 | # Initialize function call history 69 | function_call_history = [] 70 | 71 | # Initialize chat context with system message 72 | system_prompt = ( 73 | "You are an AI assistant that uses a chain-of-thought reasoning process to solve tasks. " 74 | "Let's think step by step to solve the following problem. " 75 | "You have access to the following functions which you can use to complete the task. " 76 | "Explain your reasoning in detail, including any functions you use and their outputs. " 77 | "At the end of your reasoning, provide the final answer after 'Answer:'. " 78 | "Before finalizing, review your reasoning for any errors or inconsistencies. " 79 | "Avoid repeating function calls with the same arguments you've already tried. " 80 | "Here is the history of function calls you have made so far: {{function_call_history}}" 81 | ) 82 | 83 | chat_context = [ 84 | {"role": "system", "content": system_prompt.replace("{{function_call_history}}", "None")}, 85 | {"role": "user", "content": input_text}, 86 | ] 87 | 88 | # Initialize loop parameters 89 | max_iterations = 5 90 | iteration = 0 91 | 92 | full_reasoning_path = "" 93 | 94 | while iteration < max_iterations: 95 | iteration += 1 96 | 97 | # Update the system prompt with the current function call history 98 | if function_call_history: 99 | history_str = "\n".join([ 100 | f"- {call['function_name']} with arguments {call['arguments']} produced output: {call['output']}" 101 | for call in function_call_history 102 | ]) 103 | else: 104 | history_str = "None" 105 | 106 | chat_context[0]['content'] = system_prompt.replace("{{function_call_history}}", history_str) 107 | 108 | # Call LiteLLM's completion API with the chat context and tools 109 | response = litellm.completion( 110 | model="gpt-4-turbo", 111 | messages=chat_context, 112 | tools=tools, 113 | tool_choice="auto", 114 | max_tokens=1500, 115 | temperature=0.7 116 | ) 117 | 118 | # Extract the message from the response 119 | response_message = response['choices'][0]['message'] 120 | 121 | # Append the assistant's message to the chat context and full reasoning path 122 | chat_context.append(response_message) 123 | full_reasoning_path += f"\nIteration {iteration}:\n{response_message['content']}\n" 124 | 125 | # Check if the assistant wants to call any functions 126 | tool_calls = response_message.get('tool_calls', []) 127 | 128 | if tool_calls: 129 | for tool_call in tool_calls: 130 | function_name = tool_call['function']['name'] 131 | function_args = json.loads(tool_call['function']['arguments']) 132 | tool_call_id = tool_call['id'] 133 | 134 | # Check if this function call with these arguments has already been made 135 | if any( 136 | call['function_name'] == function_name and call['arguments'] == function_args 137 | for call in function_call_history 138 | ): 139 | function_response = f"Function '{function_name}' with arguments {function_args} has already been called. Please try a different approach." 140 | else: 141 | # Execute the function using execute_function_wrapper 142 | try: 143 | function_output = execute_function_wrapper(function_name, **function_args) 144 | function_call_history.append({ 145 | 'function_name': function_name, 146 | 'arguments': function_args, 147 | 'output': function_output 148 | }) 149 | function_response = f"Function '{function_name}' executed successfully with output: {function_output}" 150 | except Exception as e: 151 | function_response = f"Error executing function '{function_name}': {str(e)}" 152 | 153 | # Ensure function_response is a string 154 | if not isinstance(function_response, str): 155 | function_response = json.dumps(function_response) 156 | 157 | # Append the function response to the chat context and full reasoning path 158 | chat_context.append({ 159 | "tool_call_id": tool_call_id, 160 | "role": "tool", 161 | "name": function_name, 162 | "content": function_response 163 | }) 164 | full_reasoning_path += f"Function Call: {function_name}\nArguments: {function_args}\nOutput: {function_response}\n" 165 | 166 | # Continue the loop to allow the assistant to process the function outputs 167 | continue 168 | else: 169 | # No function calls, assume task is complete 170 | break 171 | 172 | # Extract the final answer from the last assistant message 173 | final_answer = response_message['content'].split('Answer:')[-1].strip() if 'Answer:' in response_message['content'] else response_message['content'] 174 | 175 | # Compile the full response including reasoning steps and function call history 176 | if function_call_history: 177 | function_calls_str = "\n".join([ 178 | f"Function '{call['function_name']}' called with arguments {call['arguments']}, produced output: {call['output']}" 179 | for call in function_call_history 180 | ]) 181 | else: 182 | function_calls_str = "No functions were called." 183 | 184 | full_response = ( 185 | f"Full Reasoning Path:\n{full_reasoning_path}\n\n" 186 | f"Functions Used:\n{function_calls_str}\n\n" 187 | f"Final Answer:\n{final_answer}" 188 | ) 189 | 190 | return full_response 191 | 192 | except Exception as e: 193 | return f"An error occurred: {str(e)}\n\nFull reasoning path so far:\n{full_reasoning_path}" -------------------------------------------------------------------------------- /babyagi/functionz/packs/drafts/self_build.py: -------------------------------------------------------------------------------- 1 | # self_build.py 2 | 3 | from functionz.core.framework import func 4 | import json 5 | 6 | @func.register_function( 7 | metadata={"description": "Generates queries based on user description"}, 8 | dependencies=["gpt_call"], 9 | imports=["json"] 10 | ) 11 | def generate_queries(user_description, X=3, max_retries=3): 12 | """ 13 | Generates X distinct queries that require action based on the user description using gpt_call. 14 | 15 | Args: 16 | user_description (str): Description of the user or their needs. 17 | X (int, optional): Number of queries to generate. Defaults to 3. 18 | max_retries (int, optional): Maximum number of retries for generating valid queries. Defaults to 3. 19 | 20 | Returns: 21 | list: A list of generated queries. 22 | 23 | Raises: 24 | ValueError: If unable to generate valid queries within the retry limit. 25 | """ 26 | prompt = f""" 27 | You are an AI assistant. Based on the following user description, generate {X} distinct queries that such a user might ask: 28 | 29 | User Description: 30 | "{user_description}" 31 | 32 | Provide the queries in JSON format as a list: 33 | 34 | [ 35 | "Query 1", 36 | "Query 2", 37 | ... 38 | ] 39 | 40 | Ensure the queries are diverse, relevant to the user description, and represent realistic questions that this user might ask. Make requests that require action, such as using tools and APIs, which you should specify in the request. Based on the user description, guess what types of tools they use and specify them in the query. 41 | """ 42 | 43 | errors = [] # To collect error messages from each attempt 44 | 45 | for attempt in range(1, max_retries + 1): 46 | response = gpt_call(prompt) 47 | try: 48 | queries = json.loads(response) 49 | if isinstance(queries, list) and len(queries) == X and all(isinstance(q, str) for q in queries): 50 | return queries 51 | else: 52 | error_message = ( 53 | f"Attempt {attempt}: Invalid JSON structure or incorrect number of queries. " 54 | f"Expected {X} string queries, but received: {response}" 55 | ) 56 | errors.append(error_message) 57 | except json.JSONDecodeError as e: 58 | error_message = ( 59 | f"Attempt {attempt}: JSON decoding failed with error: {str(e)}. " 60 | f"Response received: {response}" 61 | ) 62 | errors.append(error_message) 63 | except Exception as e: 64 | error_message = ( 65 | f"Attempt {attempt}: An unexpected error occurred: {str(e)}. " 66 | f"Response received: {response}" 67 | ) 68 | errors.append(error_message) 69 | 70 | # After all attempts, raise an error with all collected messages 71 | full_error_message = " | ".join(errors) 72 | raise ValueError(f"Failed to generate {X} valid queries after {max_retries} attempts. Errors: {full_error_message}") 73 | 74 | 75 | @func.register_function( 76 | metadata={"description": "Processes generated queries based on user description"}, 77 | dependencies=["generate_queries", "process_user_input"], 78 | imports=["json"] 79 | ) 80 | def self_build(user_description, X=3): 81 | """ 82 | Generates queries based on the user description and processes each query. 83 | 84 | Args: 85 | user_description (str): Description of the user or their needs. 86 | X (int, optional): Number of queries to generate and process. Defaults to 3. 87 | 88 | Returns: 89 | list: A list of dictionaries containing each query and its corresponding output. 90 | 91 | Raises: 92 | ValueError: If query generation fails. 93 | """ 94 | try: 95 | 96 | print("\033[1;33mUser Description: ", user_description, "\033[0m") 97 | # Generate queries 98 | queries = generate_queries(user_description, X) 99 | except ValueError as e: 100 | # Log the error message for debugging 101 | print(f"Error in generate_queries: {str(e)}") 102 | return [] 103 | 104 | print("\033[1;34mQueries generated by self_build: ", queries, "\033[0m") 105 | results = [] 106 | for idx, query in enumerate(queries, start=1): 107 | try: 108 | output = process_user_input(query) 109 | results.append({'query': query, 'output': output}) 110 | except Exception as e: 111 | # Log the error message for debugging 112 | print(f"Error processing query {idx} ('{query}'): {str(e)}") 113 | results.append({'query': query, 'output': None, 'error': str(e)}) 114 | 115 | return results 116 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/drafts/self_build2.py: -------------------------------------------------------------------------------- 1 | from functionz.core.framework import func 2 | 3 | @func.register_function( 4 | metadata={ 5 | "name": "generate_and_process_queries", 6 | "description": "Generates a specified number of synthetic queries based on a user description and processes each query using the choose_or_create_function.", 7 | "input_parameters": { 8 | "user_description": "A detailed description provided by the user.", 9 | "num_queries": "An integer specifying the number of synthetic queries to generate." 10 | }, 11 | "output": "A dictionary containing the results of each processed query along with intermediate steps and any errors." 12 | }, 13 | dependencies=[ 14 | "choose_or_create_function", 15 | "litellm.completion", 16 | ], 17 | imports=[ 18 | {"name": "litellm", "lib": "litellm"}, 19 | {"name": "pydantic", "lib": "pydantic"}, 20 | {"name": "typing", "lib": "typing"}, 21 | {"name": "json", "lib": "json"}, 22 | ] 23 | ) 24 | def generate_and_process_queries(user_description: str, num_queries: int) -> dict: 25 | """ 26 | Generates X synthetic queries based on the user description and processes each query 27 | using the choose_or_create_function. 28 | 29 | Args: 30 | user_description (str): The user's description to base synthetic queries on. 31 | num_queries (int): The number of synthetic queries to generate. 32 | 33 | Returns: 34 | dict: A dictionary containing the results of each query execution, intermediate steps, and any relevant information. 35 | """ 36 | from litellm import completion 37 | from pydantic import BaseModel, Field, ValidationError 38 | from typing import List, Dict, Any 39 | import json 40 | 41 | intermediate_steps = [] 42 | results = [] 43 | 44 | # Step 1: Generate synthetic queries based on the user description 45 | system_prompt = """ 46 | You are an AI assistant specialized in generating relevant and distinct queries based on a given description. 47 | 48 | Given a user description, generate a specified number of unique and diverse queries that a user might ask an AI assistant. 49 | Ensure that the queries are varied and cover different aspects of the description. 50 | 51 | Return your response in the following JSON format: 52 | 53 | { 54 | "queries": [ 55 | "First synthetic query.", 56 | "Second synthetic query.", 57 | ... 58 | ] 59 | } 60 | 61 | Provide only the JSON response, without any additional text. 62 | """ 63 | 64 | class QueryGenerationResponse(BaseModel): 65 | queries: List[str] = Field(..., description="A list of generated synthetic queries.") 66 | 67 | generation_prompt = f""" 68 | User Description: 69 | \"\"\"{user_description}\"\"\" 70 | 71 | Number of Queries to Generate: 72 | {num_queries} 73 | """ 74 | 75 | try: 76 | generation_response = completion( 77 | model="gpt-4o-mini", 78 | messages=[ 79 | {"role": "system", "content": system_prompt}, 80 | {"role": "user", "content": generation_prompt} 81 | ], 82 | response_format=QueryGenerationResponse 83 | ) 84 | print(f"[DEBUG] Generation Response: {generation_response}") 85 | except Exception as e: 86 | print(f"[ERROR] LLM call for query generation failed: {e}") 87 | intermediate_steps.append({"step": "Error in Query Generation LLM Call", "content": str(e)}) 88 | return {"intermediate_steps": intermediate_steps, "error": "# Error generating synthetic queries."} 89 | 90 | # Parse the response 91 | try: 92 | content = generation_response.choices[0].message.content 93 | print(f"[DEBUG] Raw Generation Content: {content}") 94 | generation_parsed = QueryGenerationResponse.parse_raw(content) 95 | print(f"[DEBUG] Parsed Query Generation: {generation_parsed}") 96 | intermediate_steps.append({"step": "Generate Synthetic Queries", "content": generation_parsed.dict()}) 97 | except (ValidationError, IndexError, AttributeError, json.JSONDecodeError) as e: 98 | print(f"[ERROR] Parsing query generation response failed: {e}") 99 | intermediate_steps.append({"step": "Error Parsing Query Generation Response", "content": str(e)}) 100 | return {"intermediate_steps": intermediate_steps, "error": "# Error parsing synthetic queries."} 101 | 102 | synthetic_queries = generation_parsed.queries 103 | 104 | if not synthetic_queries or len(synthetic_queries) != num_queries: 105 | print(f"[ERROR] Number of generated queries does not match the requested number.") 106 | intermediate_steps.append({ 107 | "step": "Query Count Mismatch", 108 | "content": f"Requested: {num_queries}, Generated: {len(synthetic_queries)}" 109 | }) 110 | return { 111 | "intermediate_steps": intermediate_steps, 112 | "error": "# The number of generated queries does not match the requested number." 113 | } 114 | 115 | # Step 2: Process each synthetic query using choose_or_create_function 116 | for idx, query in enumerate(synthetic_queries, start=1): 117 | intermediate_steps.append({"step": f"Processing Query {idx}", "content": query}) 118 | try: 119 | # Assuming choose_or_create_function is accessible within the scope 120 | # If it's in a different module, you might need to import it accordingly 121 | query_result = choose_or_create_function(query) 122 | results.append({ 123 | "query": query, 124 | "result": query_result.get("execution_result"), 125 | "intermediate_steps": query_result.get("intermediate_steps", []) 126 | }) 127 | intermediate_steps.append({ 128 | "step": f"Executed Query {idx}", 129 | "content": query_result.get("execution_result") 130 | }) 131 | except Exception as e: 132 | print(f"[ERROR] Processing query {idx} failed: {e}") 133 | intermediate_steps.append({ 134 | "step": f"Error Processing Query {idx}", 135 | "content": str(e) 136 | }) 137 | results.append({ 138 | "query": query, 139 | "error": f"# Error processing query: {e}" 140 | }) 141 | 142 | return { 143 | "intermediate_steps": intermediate_steps, 144 | "results": results 145 | } 146 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/airtable.py: -------------------------------------------------------------------------------- 1 | from functionz import func 2 | 3 | # Initialize the Airtable Table instance 4 | @func.register_function( 5 | metadata={"description": "Initialize Airtable table instance with access token, base ID, and table name."}, 6 | key_dependencies=["airtable_access_token"], 7 | imports=["pyairtable"] 8 | ) 9 | def init_airtable(base_id, table_name): 10 | """ 11 | Initialize the Airtable Table instance. 12 | :param base_id: ID of the Airtable base 13 | :param table_name: Name of the table within the base 14 | :return: Airtable Table instance 15 | """ 16 | api_token = globals()["airtable_access_token"] 17 | from pyairtable import Table 18 | return Table(api_token, base_id, table_name) 19 | 20 | 21 | # Create a single record in a given Airtable table 22 | @func.register_function( 23 | metadata={"description": "Create a new record in a specific Airtable table."}, 24 | dependencies=["init_airtable"] 25 | ) 26 | def create_record(base_id, table_name, record_data): 27 | """ 28 | Create a new record in the specified Airtable table. 29 | :param base_id: ID of the Airtable base 30 | :param table_name: Name of the table within the base 31 | :param record_data: Dictionary containing record fields and values 32 | :return: The newly created record 33 | """ 34 | table = init_airtable(base_id, table_name) 35 | return table.create(record_data) 36 | 37 | 38 | # Retrieve a single record by its ID 39 | @func.register_function( 40 | metadata={"description": "Retrieve a specific record from Airtable using its record ID."}, 41 | dependencies=["init_airtable"] 42 | ) 43 | def get_record_by_id(base_id, table_name, record_id): 44 | """ 45 | Retrieve a record by its unique ID from the specified Airtable table. 46 | :param base_id: ID of the Airtable base 47 | :param table_name: Name of the table within the base 48 | :param record_id: Unique record ID in Airtable 49 | :return: The record data as a dictionary 50 | """ 51 | table = init_airtable(base_id, table_name) 52 | return table.get(record_id) 53 | 54 | 55 | # Update an existing record by its ID 56 | @func.register_function( 57 | metadata={"description": "Update a record in Airtable using its ID and new field values."}, 58 | dependencies=["init_airtable"] 59 | ) 60 | def update_record(base_id, table_name, record_id, updated_fields): 61 | """ 62 | Update a record in Airtable with new values for specified fields. 63 | :param base_id: ID of the Airtable base 64 | :param table_name: Name of the table within the base 65 | :param record_id: Unique record ID in Airtable 66 | :param updated_fields: Dictionary with updated field values 67 | :return: The updated record data as a dictionary 68 | """ 69 | table = init_airtable(base_id, table_name) 70 | return table.update(record_id, updated_fields) 71 | 72 | 73 | # Delete a record by its ID 74 | @func.register_function( 75 | metadata={"description": "Delete a specific record from an Airtable table."}, 76 | dependencies=["init_airtable"] 77 | ) 78 | def delete_record(base_id, table_name, record_id): 79 | """ 80 | Delete a record from Airtable using its unique ID. 81 | :param base_id: ID of the Airtable base 82 | :param table_name: Name of the table within the base 83 | :param record_id: Unique record ID in Airtable 84 | :return: Deletion confirmation message 85 | """ 86 | table = init_airtable(base_id, table_name) 87 | return table.delete(record_id) 88 | 89 | 90 | # Retrieve all records from a table 91 | @func.register_function( 92 | metadata={"description": "Retrieve all records from a specific Airtable table."}, 93 | dependencies=["init_airtable"] 94 | ) 95 | def get_all_records(base_id, table_name, max_records=100, sort_by=None): 96 | """ 97 | Get all records from the specified table, with optional sorting. 98 | :param base_id: ID of the Airtable base 99 | :param table_name: Name of the table within the base 100 | :param max_records: Maximum number of records to retrieve 101 | :param sort_by: Optional list of fields to sort the records by 102 | :return: List of all records 103 | """ 104 | table = init_airtable(base_id, table_name) 105 | return table.all(max_records=max_records, sort=sort_by) 106 | 107 | 108 | # Upsert multiple records into a table based on unique fields 109 | @func.register_function( 110 | metadata={"description": "Upsert (create or update) multiple records into a table using unique field identifiers."}, 111 | dependencies=["init_airtable"] 112 | ) 113 | def batch_upsert_records(base_id, table_name, records, key_fields): 114 | """ 115 | Upsert multiple records into the specified table. 116 | :param base_id: ID of the Airtable base 117 | :param table_name: Name of the table within the base 118 | :param records: List of records to be upserted 119 | :param key_fields: List of fields to use as unique keys 120 | :return: List of created or updated records 121 | """ 122 | table = init_airtable(base_id, table_name) 123 | return table.batch_upsert(records, key_fields=key_fields) 124 | 125 | 126 | # Batch create multiple records in a table 127 | @func.register_function( 128 | metadata={"description": "Create multiple records in a table using batch operations."}, 129 | dependencies=["init_airtable"] 130 | ) 131 | def batch_create_records(base_id, table_name, records): 132 | """ 133 | Create multiple records in the specified table. 134 | :param base_id: ID of the Airtable base 135 | :param table_name: Name of the table within the base 136 | :param records: List of records to be created 137 | :return: List of created records 138 | """ 139 | table = init_airtable(base_id, table_name) 140 | return table.batch_create(records) 141 | 142 | 143 | # Batch delete multiple records from a table 144 | @func.register_function( 145 | metadata={"description": "Delete multiple records in a table using batch operations."}, 146 | dependencies=["init_airtable"] 147 | ) 148 | def batch_delete_records(base_id, table_name, record_ids): 149 | """ 150 | Batch delete records using their unique IDs. 151 | :param base_id: ID of the Airtable base 152 | :param table_name: Name of the table within the base 153 | :param record_ids: List of record IDs to be deleted 154 | :return: Confirmation messages for deleted records 155 | """ 156 | table = init_airtable(base_id, table_name) 157 | return table.batch_delete(record_ids) 158 | 159 | 160 | 161 | from functionz import func 162 | 163 | @func.register_function( 164 | metadata={"description": "Fetch a dynamic number of rows from Airtable based on a flexible search query."}, 165 | dependencies=["init_airtable"], 166 | imports=["pyairtable"] 167 | ) 168 | def get_dynamic_records(base_id, table_name, max_records=100, search_query=None, sort_by=None, fields=None, view=None, page_size=100): 169 | """ 170 | Fetch a dynamic number of records from an Airtable table based on a custom query. 171 | 172 | :param base_id: ID of the Airtable base 173 | :param table_name: Name of the table within the base 174 | :param max_records: Maximum number of records to retrieve 175 | :param search_query: Dictionary of field-value pairs to match (e.g., {"Name": "Alice", "Age": 30}) 176 | :param sort_by: List of fields to sort the records by (e.g., ["Name", "-Age"]) 177 | :param fields: List of specific fields to retrieve (e.g., ["Name", "Age"]) 178 | :param view: View ID or name to filter the records by 179 | :param page_size: Number of records per page request 180 | :return: List of matching records 181 | """ 182 | from pyairtable.formulas import match 183 | table = init_airtable(base_id, table_name) 184 | 185 | # Construct a formula using the match function if search_query is provided 186 | formula = None 187 | if search_query: 188 | from pyairtable.formulas import match 189 | formula = match(search_query) 190 | 191 | # Use iterate to handle large datasets if max_records is set higher than page_size 192 | records = table.iterate( 193 | formula=formula, 194 | sort=sort_by, 195 | fields=fields, 196 | view=view, 197 | page_size=page_size, 198 | max_records=max_records 199 | ) 200 | 201 | # Collect results from the generator into a list 202 | return list(records) 203 | 204 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/augie.py: -------------------------------------------------------------------------------- 1 | from babyagi.functionz.core.framework import func 2 | 3 | @func.register_function( 4 | metadata={"description": "Generate parameters for Augie creation using GPT."}, 5 | dependencies=["gpt_call"] 6 | ) 7 | def generate_augie_params(user_input, voice_id="29vD33N1CtxCmqQRPOHJ"): 8 | """ 9 | This function generates JSON parameters for creating an Augie video. 10 | It uses GPT to structure the user input into the required format, keeping the default voice_id. 11 | 12 | Parameters: 13 | - user_input: The basic user input text. 14 | - voice_id: Default voice ID (not generated by GPT). 15 | 16 | Returns: A dictionary with the necessary parameters for creating the Augie video. 17 | """ 18 | prompt = ( 19 | "You are creating parameters for a video generation API request. " 20 | "The user has provided input text for the video content. Structure the input into the following JSON format:\n" 21 | "{\n" 22 | " 'name': '',\n" 23 | " 'text': '',\n" 24 | " 'orientation': 'landscape' or 'portrait',\n" 25 | " 'make_video': true or false\n" 26 | "}\n" 27 | "Do not generate a voice ID, use the one provided by the API system." 28 | ) 29 | 30 | gpt_output = gpt_call({"prompt": prompt, "user_input": user_input}) 31 | 32 | # Parse GPT output and construct parameters 33 | params = gpt_output['text'] # Assuming gpt_call returns a structured response. 34 | params['voice_id'] = voice_id # Set the default voice ID. 35 | 36 | return params 37 | 38 | 39 | @func.register_function( 40 | metadata={"description": "Creates a video on Augie platform."}, 41 | key_dependencies=["augie_api_key"], 42 | imports={"name": "requests", "lib": "requests"} 43 | ) 44 | def create_augie(params): 45 | """Function to create a video on Augie platform with parameters.""" 46 | API_KEY = globals()['augie_api_key'] 47 | BASE_URL = 'https://beta.api.augie.studio/v1' 48 | 49 | headers = { 50 | 'x-api-key': API_KEY, 51 | 'Content-Type': 'application/json' 52 | } 53 | 54 | import requests 55 | response = requests.post(f'{BASE_URL}/augies', json=params, headers=headers) 56 | 57 | if response.status_code == 201: 58 | return response.json() # Returns the creation response 59 | else: 60 | raise Exception(f"Failed to create Augie: {response.text}") 61 | 62 | 63 | @func.register_function( 64 | metadata={"description": "Checks the status of the created video."}, 65 | key_dependencies=["augie_api_key"], 66 | imports={"name": "requests", "lib": "requests"} 67 | ) 68 | def get_augie_status(augie_id): 69 | """Function to check the status of an Augie video creation.""" 70 | API_KEY = globals()['augie_api_key'] 71 | BASE_URL = 'https://beta.api.augie.studio/v1' 72 | 73 | headers = { 74 | 'x-api-key': API_KEY 75 | } 76 | 77 | import requests 78 | response = requests.get(f'{BASE_URL}/augies/{augie_id}/status', headers=headers) 79 | 80 | if response.status_code == 200: 81 | status_data = response.json() 82 | if status_data.get('status') == 'succeeded' and 'output' in status_data and 'video' in status_data['output']: 83 | return {"status": "completed", "video_url": status_data['output']['video']} 84 | else: 85 | return {"status": "processing"} 86 | else: 87 | raise Exception(f"Failed to get Augie status: {response.text}") 88 | 89 | 90 | @func.register_function( 91 | metadata={"description": "Wrapper to create a video and keep checking its status until available."}, 92 | dependencies=["generate_augie_params", "create_augie", "get_augie_status"], 93 | imports={"name": "time", "lib": "time"} 94 | ) 95 | def create_and_wait_for_video(user_input, timeout=300, interval=10): 96 | """ 97 | Wrapper function to create a video from user input and wait for it to be available. 98 | - user_input: Basic input from the user, processed by GPT. 99 | - timeout: The max time to wait (in seconds). 100 | - interval: Time between status checks (in seconds). 101 | """ 102 | import time 103 | 104 | # Generate parameters using GPT 105 | params = generate_augie_params(user_input) 106 | 107 | # Create the video 108 | creation_response = create_augie(params) 109 | augie_id = creation_response.get('id') 110 | 111 | if not augie_id: 112 | raise Exception("Failed to retrieve Augie ID after creation.") 113 | 114 | # Wait and check the status periodically 115 | start_time = time.time() 116 | while time.time() - start_time < timeout: 117 | status_response = get_augie_status(augie_id) 118 | if status_response['status'] == 'completed': 119 | return status_response # Return video URL if available 120 | time.sleep(interval) 121 | 122 | # Timeout reached, return failure 123 | raise TimeoutError(f"Video creation timed out after {timeout} seconds.") 124 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/e2b.py: -------------------------------------------------------------------------------- 1 | import babyagi 2 | 3 | @babyagi.register_function( 4 | metadata={"description": "Executes AI-generated Python code in a secure E2B sandbox."}, 5 | imports=["e2b_code_interpreter"], 6 | key_dependencies=["e2b_api_key"] 7 | ) 8 | def execute_code_in_sandbox(code: str): 9 | """ 10 | This function initializes an E2B sandbox and executes AI-generated Python code within it. 11 | 12 | :param code: Python code to be executed. 13 | :return: Results and logs from the code execution. 14 | """ 15 | from e2b_code_interpreter import CodeInterpreter 16 | 17 | with CodeInterpreter() as sandbox: 18 | # Execute the code in the sandbox 19 | execution = sandbox.notebook.exec_cell(code) 20 | 21 | # Handle execution errors 22 | if execution.error: 23 | return {"error": execution.error.name, "message": execution.error.value, "traceback": execution.error.traceback} 24 | 25 | # Gather results 26 | results = [{"text": result.text, "formats": result.formats()} for result in execution.results] 27 | logs = {"stdout": execution.logs.stdout, "stderr": execution.logs.stderr} 28 | 29 | return {"results": results, "logs": logs} 30 | 31 | 32 | # Function 2: Chat with LLM (OpenAI) and parse response to execute in sandbox 33 | 34 | @babyagi.register_function( 35 | metadata={"description": "Calls the OpenAI API (gpt-3.5-turbo) to generate code and execute it in an E2B sandbox."}, 36 | imports=["litellm", "os"], 37 | key_dependencies=["openai_api_key"], 38 | dependencies=["execute_code_in_sandbox"] 39 | ) 40 | def chat_with_llm_and_execute(user_message: str): 41 | """ 42 | This function calls the OpenAI API (via litellm) to generate Python code based on the user's message, 43 | then executes that code in an E2B sandbox. 44 | 45 | :param user_message: The message to prompt the LLM with. 46 | :return: Results from the executed code and logs. 47 | """ 48 | from litellm import completion 49 | 50 | # Load OpenAI API key from environment 51 | api_key = globals()['openai_api_key'] 52 | 53 | # Define the message for the LLM 54 | messages = [{"role": "user", "content": user_message}] 55 | 56 | # Call the LLM using litellm completion method 57 | response = completion(model="gpt-3.5-turbo", messages=messages) 58 | llm_generated_code = response['choices'][0]['message']['content'] 59 | 60 | # Execute the generated code in the E2B sandbox 61 | return execute_code_in_sandbox(llm_generated_code) 62 | 63 | 64 | # Function 3: Save generated charts 65 | 66 | @babyagi.register_function( 67 | metadata={"description": "Saves a base64-encoded PNG chart to a file."}, 68 | imports=["base64"], 69 | ) 70 | def save_chart(base64_png: str, filename: str = "chart.png"): 71 | """ 72 | Saves a base64-encoded PNG chart to a file. 73 | 74 | :param base64_png: Base64-encoded PNG data. 75 | :param filename: The name of the file to save the chart. 76 | :return: The path to the saved chart. 77 | """ 78 | png_data = base64.b64decode(base64_png) 79 | 80 | # Save the decoded PNG to a file 81 | with open(filename, "wb") as file: 82 | file.write(png_data) 83 | 84 | return f"Chart saved to {filename}" 85 | 86 | 87 | # Function 4: Execute main flow (chat with LLM, run code, save chart if exists) 88 | 89 | @babyagi.register_function( 90 | metadata={"description": "Main function to prompt LLM, execute code in E2B, and save any generated charts."}, 91 | dependencies=["chat_with_llm_and_execute", "save_chart"] 92 | ) 93 | def e2b_llm_to_chart(user_message: str): 94 | """ 95 | The main workflow function: sends a message to the LLM, executes the generated code, and saves any charts. 96 | 97 | :param user_message: The user's input prompt for the LLM. 98 | :return: Final results and path to saved chart if applicable. 99 | """ 100 | # Get code execution results and logs 101 | execution_results = chat_with_llm_and_execute(user_message) 102 | 103 | # Check if any chart (PNG) was generated 104 | if execution_results["results"]: 105 | for result in execution_results["results"]: 106 | if "png" in result["formats"]: 107 | # Save the chart if PNG format is present 108 | chart_filename = save_chart(result["formats"]["png"]) 109 | return {"execution_results": execution_results, "chart_saved_to": chart_filename} 110 | 111 | return {"execution_results": execution_results} 112 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/firecrawl.py: -------------------------------------------------------------------------------- 1 | # 1. Crawl a website and initiate a crawl job. 2 | @func.register_function( 3 | metadata={"description": "Submits a crawl job for a website and returns a job ID."}, 4 | key_dependencies=["firecrawl_api_key"], 5 | imports={"name":"firecrawl","lib":"firecrawl-py"} 6 | ) 7 | def crawl_website(url: str, limit: int = 100, formats: list = ["markdown", "html"], poll_interval: int = 30): 8 | """ 9 | Submits a crawl job for the given URL and returns the crawl job status and job ID. 10 | """ 11 | from firecrawl import FirecrawlApp 12 | api_key = globals()['firecrawl_api_key'] 13 | app = FirecrawlApp(api_key=api_key) 14 | 15 | try: 16 | crawl_status = app.crawl_url( 17 | url, 18 | params={'limit': limit, 'scrapeOptions': {'formats': formats}}, 19 | poll_interval=poll_interval 20 | ) 21 | return crawl_status 22 | except Exception as e: 23 | return {"error": str(e)} 24 | 25 | 26 | # 2. Check the status of a crawl job. 27 | @func.register_function( 28 | metadata={"description": "Checks the status of an ongoing or completed crawl job by its job ID."}, 29 | key_dependencies=["firecrawl_api_key"], 30 | imports={"name":"firecrawl","lib":"firecrawl-py"} 31 | ) 32 | def check_crawl_status(crawl_id: str): 33 | """ 34 | Checks the status of the crawl job and returns the job details including markdown and HTML data. 35 | """ 36 | from firecrawl import FirecrawlApp 37 | api_key = globals()['firecrawl_api_key'] 38 | app = FirecrawlApp(api_key=api_key) 39 | 40 | try: 41 | crawl_status = app.check_crawl_status(crawl_id) 42 | return crawl_status 43 | except Exception as e: 44 | return {"error": str(e)} 45 | 46 | 47 | # 3. Scrape a single website URL for markdown and HTML data. 48 | @func.register_function( 49 | metadata={"description": "Scrapes a single URL and returns markdown and HTML content."}, 50 | key_dependencies=["firecrawl_api_key"], 51 | imports={"name":"firecrawl","lib":"firecrawl-py"} 52 | ) 53 | def scrape_website(url: str, formats: list = ["markdown", "html"]): 54 | """ 55 | Scrapes the given URL and returns the data (markdown, HTML, and metadata). 56 | """ 57 | from firecrawl import FirecrawlApp 58 | api_key = globals()['firecrawl_api_key'] 59 | app = FirecrawlApp(api_key=api_key) 60 | 61 | try: 62 | scrape_result = app.scrape_url(url, params={'formats': formats}) 63 | return scrape_result 64 | except Exception as e: 65 | return {"error": str(e)} 66 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/harmonic.py: -------------------------------------------------------------------------------- 1 | # Harmonic API Functions Pack for Functionz Framework 2 | 3 | 4 | @func.register_function( 5 | metadata={"description": "Fetch a company's enrichment data using its identifier (URL or domain)."}, 6 | key_dependencies=["harmonic_api_key"], 7 | imports=[{"name": "requests", "lib": "requests"}] 8 | ) 9 | def harmonic_enrich_company(identifier): 10 | """ 11 | Enrich a company using its URL, domain, or identifier. 12 | Returns the full response from Harmonic API. 13 | """ 14 | api_key = globals()['harmonic_api_key'] 15 | url = "https://api.harmonic.ai/companies" 16 | headers = {"accept": "application/json", "apikey": api_key} 17 | 18 | # Determine the appropriate parameter based on identifier type 19 | if identifier.startswith('http'): 20 | params = {"crunchbase_url": identifier} if 'crunchbase.com' in identifier else {"website_url": identifier} 21 | elif '.' in identifier and not identifier.startswith('http'): 22 | params = {"website_domain": identifier} 23 | else: 24 | url += f"/{identifier}" 25 | params = {} 26 | 27 | # Use POST if parameters are present, otherwise GET 28 | response = requests.post(url, headers=headers, params=params) if params else requests.get(url, headers=headers) 29 | response.raise_for_status() 30 | return response.json() 31 | 32 | 33 | @func.register_function( 34 | metadata={"description": "Search for companies using a set of keywords."}, 35 | key_dependencies=["harmonic_api_key"], 36 | imports=[{"name": "requests", "lib": "requests"}] 37 | ) 38 | def harmonic_search_companies(keywords, include_ids_only=False): 39 | """ 40 | Search for companies using keywords. 41 | Returns a list of companies and their metadata. 42 | """ 43 | api_key = globals()['harmonic_api_key'] 44 | url = "https://api.harmonic.ai/search/companies_by_keywords" 45 | headers = {"accept": "application/json", "apikey": api_key, "Content-Type": "application/json"} 46 | 47 | data = {"keywords": keywords, "include_ids_only": include_ids_only} 48 | response = requests.post(url, headers=headers, json=data) 49 | response.raise_for_status() 50 | return response.json() 51 | 52 | 53 | @func.register_function( 54 | metadata={"description": "Fetch detailed information about a person using their ID."}, 55 | key_dependencies=["harmonic_api_key"], 56 | imports=[{"name": "requests", "lib": "requests"}] 57 | ) 58 | def harmonic_enrich_person_by_id(person_id): 59 | """ 60 | Retrieve detailed information about a person using their Harmonic ID. 61 | """ 62 | api_key = globals()['harmonic_api_key'] 63 | url = f"https://api.harmonic.ai/persons/{person_id}" 64 | headers = {"accept": "application/json", "apikey": api_key} 65 | 66 | response = requests.get(url, headers=headers) 67 | response.raise_for_status() 68 | return response.json() 69 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/payman.py: -------------------------------------------------------------------------------- 1 | from babyagi.functionz.core.framework import func 2 | 3 | # Store API keys (both test and real) from Replit secrets into the function database 4 | @func.register_function( 5 | metadata={"description": "Store PayMan API keys (test and real) from Replit secrets into the function database"}, 6 | imports=["os"] 7 | ) 8 | def store_payman_api_keys(): 9 | # Store test API key 10 | func.add_key('payman_test_api_key', os.environ['PAYMAN_TEST_API_KEY']) 11 | # Store real API key 12 | func.add_key('payman_api_key', os.environ['PAYMAN_API_KEY']) 13 | 14 | 15 | # Create Task Function 16 | @func.register_function( 17 | metadata={"description": "Create a task on PayMan platform (test by default, real if 'real_money' is True)"}, 18 | key_dependencies=["payman_test_api_key", "payman_api_key"], 19 | imports=["requests"] 20 | ) 21 | def create_task(title: str, description: str, payout: int, currency: str = "USD", category: str = "MARKETING", real_money: bool = False): 22 | if real_money: 23 | api_key = globals()['payman_api_key'] 24 | base_url = "https://agent.payman.ai/api" 25 | else: 26 | api_key = globals()['payman_test_api_key'] 27 | base_url = "https://agent-sandbox.payman.ai/api" 28 | 29 | headers = { 30 | "x-payman-api-secret": api_key, 31 | "Content-Type": "application/json", 32 | "Accept": "application/vnd.payman.v1+json" 33 | } 34 | payload = { 35 | "title": title, 36 | "description": description, 37 | "payout": payout, # Payout in cents (e.g. 5000 for $50) 38 | "currency": currency, 39 | "category": category, 40 | "requiredSubmissions": 1, 41 | "submissionPolicy": "OPEN_SUBMISSIONS_ONE_PER_USER" 42 | } 43 | try: 44 | response = requests.post(f"{base_url}/tasks", headers=headers, json=payload) 45 | return response.json() 46 | except requests.exceptions.HTTPError as e: 47 | return {"error": str(e)} 48 | 49 | 50 | # Get Task by ID Function 51 | @func.register_function( 52 | metadata={"description": "Get a task by its ID on PayMan platform (test by default, real if 'real_money' is True)"}, 53 | key_dependencies=["payman_test_api_key", "payman_api_key"], 54 | imports=["requests"] 55 | ) 56 | def get_task_by_id(task_id: str, real_money: bool = False): 57 | if real_money: 58 | api_key = globals()['payman_api_key'] 59 | base_url = "https://agent.payman.ai/api" 60 | else: 61 | api_key = globals()['payman_test_api_key'] 62 | base_url = "https://agent-sandbox.payman.ai/api" 63 | 64 | headers = { 65 | "x-payman-api-secret": api_key, 66 | "Content-Type": "application/json", 67 | "Accept": "application/vnd.payman.v1+json" 68 | } 69 | try: 70 | response = requests.get(f"{base_url}/tasks/{task_id}", headers=headers) 71 | return response.json() 72 | except requests.exceptions.HTTPError as e: 73 | return {"error": str(e)} 74 | 75 | 76 | # Get All Tasks Function 77 | @func.register_function( 78 | metadata={"description": "Get all tasks for the current organization on PayMan platform (test by default, real if 'real_money' is True)"}, 79 | key_dependencies=["payman_test_api_key", "payman_api_key"], 80 | imports=["requests"] 81 | ) 82 | def get_all_tasks(page: int = 0, limit: int = 20, real_money: bool = False): 83 | if real_money: 84 | api_key = globals()['payman_api_key'] 85 | base_url = "https://agent.payman.ai/api" 86 | else: 87 | api_key = globals()['payman_test_api_key'] 88 | base_url = "https://agent-sandbox.payman.ai/api" 89 | 90 | headers = { 91 | "x-payman-api-secret": api_key, 92 | "Content-Type": "application/json", 93 | "Accept": "application/vnd.payman.v1+json" 94 | } 95 | params = { 96 | "page": page, 97 | "limit": limit 98 | } 99 | try: 100 | response = requests.get(f"{base_url}/tasks", headers=headers, params=params) 101 | return response.json() 102 | except requests.exceptions.HTTPError as e: 103 | return {"error": str(e)} 104 | 105 | 106 | @func.register_function( 107 | metadata={"description": "Get all submissions for a task on PayMan platform (test by default, real if 'real_money' is True)"}, 108 | key_dependencies=["payman_test_api_key", "payman_api_key"], 109 | imports=["requests"] 110 | ) 111 | def get_task_submissions(task_id: str, statuses: list = None, page: int = 0, limit: int = 20, real_money: bool = False): 112 | if real_money: 113 | api_key = globals()['payman_api_key'] 114 | base_url = "https://agent.payman.ai/api" 115 | else: 116 | api_key = globals()['payman_test_api_key'] 117 | base_url = "https://agent-sandbox.payman.ai/api" 118 | 119 | headers = { 120 | "x-payman-api-secret": api_key, 121 | "Content-Type": "application/json", 122 | "Accept": "application/vnd.payman.v1+json" 123 | } 124 | 125 | params = { 126 | "page": page, 127 | "limit": limit 128 | } 129 | 130 | if statuses: 131 | params["statuses"] = ",".join(statuses) 132 | 133 | try: 134 | response = requests.get(f"{base_url}/tasks/{task_id}/submissions", headers=headers, params=params) 135 | return response.json() 136 | except requests.exceptions.HTTPError as e: 137 | return {"error": str(e)} 138 | 139 | # Approve Task Submission Function 140 | @func.register_function( 141 | metadata={"description": "Approve a task submission on PayMan platform (test by default, real if 'real_money' is True)"}, 142 | key_dependencies=["payman_test_api_key", "payman_api_key"], 143 | imports=["requests"] 144 | ) 145 | def approve_task_submission(submission_id: str, real_money: bool = False): 146 | if real_money: 147 | api_key = globals()['payman_api_key'] 148 | base_url = "https://agent.payman.ai/api" 149 | else: 150 | api_key = globals()['payman_test_api_key'] 151 | base_url = "https://agent-sandbox.payman.ai/api" 152 | 153 | headers = { 154 | "x-payman-api-secret": api_key, 155 | "Content-Type": "application/json", 156 | "Accept": "application/vnd.payman.v1+json" 157 | } 158 | try: 159 | response = requests.post(f"{base_url}/tasks/submissions/{submission_id}/approve", headers=headers) 160 | return response.json() 161 | except requests.exceptions.HTTPError as e: 162 | return {"error": str(e)} 163 | 164 | 165 | # Reject Task Submission Function 166 | @func.register_function( 167 | metadata={"description": "Reject a task submission on PayMan platform (test by default, real if 'real_money' is True)"}, 168 | key_dependencies=["payman_test_api_key", "payman_api_key"], 169 | imports=["requests"] 170 | ) 171 | def reject_task_submission(submission_id: str, rejection_reason: str, real_money: bool = False): 172 | if real_money: 173 | api_key = globals()['payman_api_key'] 174 | base_url = "https://agent.payman.ai/api" 175 | else: 176 | api_key = globals()['payman_test_api_key'] 177 | base_url = "https://agent-sandbox.payman.ai/api" 178 | 179 | headers = { 180 | "x-payman-api-secret": api_key, 181 | "Content-Type": "application/json", 182 | "Accept": "application/vnd.payman.v1+json" 183 | } 184 | try: 185 | response = requests.post(f"{base_url}/tasks/submissions/{submission_id}/reject", headers=headers, json=rejection_reason) 186 | return response.json() 187 | except requests.exceptions.HTTPError as e: 188 | return {"error": str(e)} 189 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/serpapi.py: -------------------------------------------------------------------------------- 1 | @func.register_function( 2 | metadata={ 3 | "description": "Perform a search using the latest SerpApi Python client library with customizable parameters." 4 | }, 5 | key_dependencies=["serpapi_api_key"], 6 | imports=["serpapi"] 7 | ) 8 | def serpapi_search_v2(query: str, engine: str = "google", location: str = "United States", language: str = "en", country: str = "us", safe_search: bool = False, num_results: int = 10, start: int = 0, async_request: bool = False, output_format: str = "json"): 9 | """ 10 | Perform a search using the SerpApi service with a flexible set of parameters. 11 | 12 | Args: 13 | query (str): The search query. 14 | engine (str): The search engine to use (e.g., 'google', 'bing'). Default is 'google'. 15 | location (str): The location to target the search. Default is 'United States'. 16 | language (str): UI language for the search. Default is 'en'. 17 | country (str): Country code for the search. Default is 'us'. 18 | safe_search (bool): Flag for SafeSearch filtering. Default is False. 19 | num_results (int): Number of search results to retrieve. Default is 10. 20 | start (int): Pagination offset. Default is 0. 21 | async_request (bool): Whether to make an asynchronous request. Default is False. 22 | output_format (str): Format of the output ('json' or 'html'). Default is 'json'. 23 | 24 | Returns: 25 | dict or str: The search results in the specified format. 26 | """ 27 | # Import necessary modules and classes within function scope. 28 | 29 | # Get the API key from the global variables. 30 | api_key = globals().get("serpapi_api_key", "") 31 | if not api_key: 32 | raise ValueError("API key is missing. Please provide a valid SerpApi key.") 33 | 34 | # Initialize the SerpApi client. 35 | client = serpapi.Client(api_key=api_key) 36 | 37 | # Define the search parameters. 38 | params = { 39 | "q": query, 40 | "engine": engine, 41 | "location": location, 42 | "hl": language, 43 | "gl": country, 44 | "safe": "active" if safe_search else "off", 45 | "num": num_results, 46 | "start": start, 47 | "async": async_request, 48 | "output": output_format, 49 | } 50 | 51 | try: 52 | # Perform the search and get the results. 53 | search_results = client.search(**params) 54 | 55 | # Return the results in the specified format. 56 | if output_format == "json": 57 | return search_results.as_dict() 58 | elif output_format == "html": 59 | return search_results.get("raw_html", "No HTML content found.") 60 | else: 61 | raise ValueError("Invalid output format specified. Choose either 'json' or 'html'.") 62 | 63 | except requests.exceptions.HTTPError as e: 64 | # Handle potential SerpApi errors and HTTP errors. 65 | return {"error": str(e)} 66 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/voilanorbert.py: -------------------------------------------------------------------------------- 1 | @func.register_function( 2 | metadata={"description": "Search for a contact by name and domain using VoilaNorbert's API."}, 3 | key_dependencies=["voilanorbert_api_key"], 4 | imports=["requests", "time"] 5 | ) 6 | def search_contact_by_name_domain(name, domain): 7 | """ 8 | Searches for a contact by name and domain using the VoilaNorbert API. 9 | 10 | Args: 11 | name (str): Full name of the person to search. 12 | domain (str): Domain of the company the person works for. 13 | 14 | Returns: 15 | dict: The contact information if found, otherwise an appropriate message. 16 | """ 17 | api_key = globals().get('voilanorbert_api_key') 18 | if not api_key: 19 | return {"error": "API key not found"} 20 | 21 | # Prepare the API request 22 | search_url = 'https://api.voilanorbert.com/2018-01-08/search/name' 23 | auth = ('any_string', api_key) 24 | data = {'name': name, 'domain': domain} 25 | 26 | try: 27 | # POST request to initiate search 28 | response = requests.post(search_url, auth=auth, data=data) 29 | if response.status_code == 402: 30 | return {"error": "No credits available for this search"} 31 | elif response.status_code != 200: 32 | return {"error": f"Failed to search contact: {response.json()}"} 33 | 34 | result = response.json() 35 | contact_id = result.get('id') 36 | 37 | # Polling to check if the email is found 38 | contact_url = f'https://api.voilanorbert.com/2018-01-08/contacts/{contact_id}' 39 | while True: 40 | contact_response = requests.get(contact_url, auth=auth) 41 | if contact_response.status_code == 200: 42 | contact_data = contact_response.json() 43 | if not contact_data['searching']: 44 | if contact_data['email']: 45 | return { 46 | "email": contact_data['email']['email'], 47 | "score": contact_data['email']['score'] 48 | } 49 | return {"message": "Email not found!"} 50 | time.sleep(10) 51 | 52 | except requests.RequestException as e: 53 | return {"error": str(e)} 54 | 55 | 56 | @func.register_function( 57 | metadata={"description": "Search for contacts by domain using VoilaNorbert's API."}, 58 | key_dependencies=["voilanorbert_api_key"], 59 | imports=["requests"] 60 | ) 61 | def search_contact_by_domain(domain): 62 | """ 63 | Searches for contacts by domain using the VoilaNorbert API. 64 | 65 | Args: 66 | domain (str): The domain of the company to search for contacts. 67 | 68 | Returns: 69 | list: A list of found contacts with emails if available. 70 | """ 71 | api_key = globals().get('voilanorbert_api_key') 72 | if not api_key: 73 | return {"error": "API key not found"} 74 | 75 | # Prepare the API request 76 | search_url = 'https://api.voilanorbert.com/2018-01-08/search/domain' 77 | auth = ('any_string', api_key) 78 | data = {'domain': domain} 79 | 80 | try: 81 | # POST request to initiate search 82 | response = requests.post(search_url, auth=auth, data=data) 83 | if response.status_code == 402: 84 | return {"error": "No credits available for this search"} 85 | elif response.status_code != 200: 86 | return {"error": f"Failed to search contacts: {response.json()}"} 87 | 88 | result = response.json() 89 | return result.get('result', []) 90 | 91 | except requests.RequestException as e: 92 | return {"error": str(e)} 93 | -------------------------------------------------------------------------------- /babyagi/functionz/packs/plugins/wokelo.py: -------------------------------------------------------------------------------- 1 | @func.register_function( 2 | metadata={"description": "Get an authentication token using the Wokelo API credentials."}, 3 | key_dependencies=["wokelo_username", "wokelo_password"], 4 | imports=[{"name": "requests", "lib": "requests"}] 5 | ) 6 | def get_auth_token(): 7 | """Obtain an authentication token using Wokelo API credentials stored as secrets.""" 8 | import requests 9 | BASE_URL = 'https://api.wokelo.ai' 10 | url = BASE_URL + '/auth/token' 11 | 12 | headers = {"Content-Type": "application/x-www-form-urlencoded"} 13 | data = { 14 | "client_id": "B5I07FeItrqH5V8ytQKNwHPDHeMQnGBheg7A6FAg", 15 | "client_secret": "JkVEP6FZhTkolz9vwkFSFAMVKLO0r9CnYU2RlGcRSzxZGZSkdSbSCed30VHg55IWU94F3sh0fTGUy8dTGslQZmcpCGvPhEUs9w3uobWa4ftXvsahriFCReRIxEUdd2f8", 16 | "grant_type": "password", 17 | "username": globals()['wokelo_username'], 18 | "password": globals()['wokelo_password'], 19 | } 20 | 21 | response = requests.post(url, headers=headers, data=data) 22 | 23 | if response.status_code == 200: 24 | # Successfully obtained token 25 | token_info = response.json() 26 | return token_info.get("access_token") 27 | else: 28 | return {"error": f"Failed to obtain token. Status code: {response.status_code}", "details": response.text} 29 | 30 | 31 | @func.register_function( 32 | metadata={"description": "Create an industry snapshot report in Wokelo."}, 33 | dependencies=["get_auth_token"], 34 | imports=[{"name": "requests", "lib": "requests"}] 35 | ) 36 | def create_industry_snapshot(access_token: str, industry_name: str): 37 | """Initiate a new industry snapshot report.""" 38 | import requests 39 | BASE_URL = 'https://api.wokelo.ai' 40 | url = f'{BASE_URL}/api/industry_primer/v3/start/' 41 | headers = {'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'} 42 | payload = {"industry": industry_name} 43 | 44 | response = requests.post(url, json=payload, headers=headers) 45 | if response.status_code == 200: 46 | return response.json().get('report_id') 47 | else: 48 | return {"error": "Failed to create industry snapshot.", "details": response.text} 49 | 50 | 51 | @func.register_function( 52 | metadata={"description": "Check the status of an industry report using its report ID."}, 53 | dependencies=["get_auth_token"], 54 | imports=[{"name": "requests", "lib": "requests"}] 55 | ) 56 | def check_report_status(access_token: str, report_id: str): 57 | """Check the status of a generated report in Wokelo.""" 58 | import requests 59 | BASE_URL = 'https://api.wokelo.ai' 60 | url = f'{BASE_URL}/api/assets/get_report_status/?report_id={report_id}' 61 | headers = {'Authorization': f'Bearer {access_token}'} 62 | 63 | response = requests.get(url, headers=headers) 64 | if response.status_code == 200: 65 | return response.json() 66 | else: 67 | return {"error": "Failed to check report status.", "details": response.text} 68 | 69 | 70 | @func.register_function( 71 | metadata={"description": "Download the generated report from Wokelo."}, 72 | dependencies=["get_auth_token"], 73 | imports=[{"name": "requests", "lib": "requests"}] 74 | ) 75 | def download_report(access_token: str, report_id: str, file_type: str = 'pdf'): 76 | """Download a specific report in a given file format.""" 77 | import requests 78 | BASE_URL = 'https://api.wokelo.ai' 79 | url = f'{BASE_URL}/api/assets/download_report/' 80 | headers = {'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'} 81 | payload = {"file_type": file_type, "report_id": report_id} 82 | 83 | response = requests.post(url, json=payload, headers=headers) 84 | if response.status_code == 200: 85 | return response.content 86 | else: 87 | return {"error": "Failed to download report.", "details": response.text} 88 | 89 | 90 | @func.register_function( 91 | metadata={"description": "Generate an industry report in Wokelo by tying all steps together."}, 92 | dependencies=["get_auth_token", "create_industry_snapshot", "check_report_status", "download_report"], 93 | imports=[{"name": "time", "lib": "time"}] 94 | ) 95 | def generate_industry_report(industry_name: str, file_type: str = 'pdf'): 96 | """ 97 | Complete workflow for generating and downloading an industry report. 98 | 99 | Args: 100 | industry_name (str): The name of the industry to generate the report for. 101 | file_type (str): The format of the report file to be downloaded. Default is 'pdf'. 102 | 103 | Returns: 104 | str: File path of the downloaded report. 105 | """ 106 | # Step 1: Get an authentication token. 107 | access_token = get_auth_token() 108 | if not isinstance(access_token, str): 109 | return access_token # If there's an error, return the error message. 110 | 111 | # Step 2: Create an industry snapshot. 112 | report_id = create_industry_snapshot(access_token, industry_name) 113 | if not isinstance(report_id, str): 114 | return report_id # If there's an error, return the error message. 115 | 116 | # Step 3: Check report status until it's ready. 117 | print(f"Initiated report creation. Waiting for the report (ID: {report_id}) to be exported.") 118 | while True: 119 | status_info = check_report_status(access_token, report_id) 120 | if 'status' in status_info and status_info['status'] == 'exported': 121 | print(f"Report is ready for download: {report_id}") 122 | break 123 | elif 'status' in status_info: 124 | print(f"Current report status: {status_info['status']}. Checking again in 30 seconds...") 125 | else: 126 | return status_info # Error occurred. 127 | time.sleep(30) 128 | 129 | # Step 4: Download the report. 130 | report_content = download_report(access_token, report_id, file_type) 131 | if isinstance(report_content, dict) and 'error' in report_content: 132 | return report_content # Return the error if download failed. 133 | 134 | # Step 5: Save the report locally. 135 | report_filename = f'report_{report_id}.{file_type}' 136 | with open(report_filename, 'wb') as report_file: 137 | report_file.write(report_content) 138 | 139 | return f"Report downloaded successfully: {report_filename}" 140 | -------------------------------------------------------------------------------- /examples/custom_flask_example.py: -------------------------------------------------------------------------------- 1 | #this is an example of how to use the babyagi framework in a custom flask app 2 | 3 | from flask import Flask 4 | import babyagi 5 | from babyagi import register_function, load_functions 6 | 7 | app = Flask(__name__) 8 | 9 | # Use the babyagi blueprints 10 | babyagi.use_blueprints(app, dashboard_route='/dashboard') 11 | 12 | @register_function() 13 | def integrated_function(): 14 | return "Hello from integrated function!" 15 | 16 | load_functions('plugins/firecrawl') 17 | 18 | @app.route('/') 19 | def home(): 20 | return "Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 21 | 22 | if __name__ == "__main__": 23 | app.run(host='0.0.0.0', port=8080) 24 | -------------------------------------------------------------------------------- /examples/custom_route_example.py: -------------------------------------------------------------------------------- 1 | # this is an example of how to use babyagi with custom routes 2 | 3 | from babyagi import create_app, register_function 4 | 5 | app = create_app('/dashboard') 6 | 7 | @register_function() 8 | def another_custom_function(): 9 | return "Hello from another custom function!" 10 | 11 | @app.route('/') 12 | def home(): 13 | return f"Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 14 | 15 | if __name__ == "__main__": 16 | app.run(host='0.0.0.0', port=8080) 17 | -------------------------------------------------------------------------------- /examples/quickstart_example.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import babyagi 4 | import os 5 | 6 | 7 | app = babyagi.create_app('/dashboard') 8 | 9 | # Add OpenAI key to enable automated descriptions and embedding of functions. 10 | babyagi.add_key_wrapper('openai_api_key',os.environ['OPENAI_API_KEY']) 11 | 12 | 13 | @app.route('/') 14 | def home(): 15 | return f"Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 16 | 17 | if __name__ == "__main__": 18 | app.run(host='0.0.0.0', port=8080) 19 | -------------------------------------------------------------------------------- /examples/self_build_example.py: -------------------------------------------------------------------------------- 1 | import babyagi 2 | import os 3 | 4 | # Add OpenAI key to enable automated descriptions and embedding of functions. 5 | babyagi.add_key_wrapper('openai_api_key',os.environ['OPENAI_API_KEY']) 6 | 7 | # Load below function packs to play with experimental self-building functions 8 | babyagi.load_functions("drafts/code_writing_functions") 9 | babyagi.load_functions("drafts/self_build") 10 | 11 | 12 | babyagi.self_build("A growth marketer at an enterprise SaaS company.") 13 | 14 | @app.route('/') 15 | def home(): 16 | return f"Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 17 | 18 | if __name__ == "__main__": 19 | app = babyagi.create_app('/dashboard') 20 | app.run(host='0.0.0.0', port=8080) 21 | -------------------------------------------------------------------------------- /examples/simple_example.py: -------------------------------------------------------------------------------- 1 | # this is a simple example of registering two functions into babyagi, executing the function stored in the database, and loading the dashboard 2 | 3 | import babyagi 4 | import os 5 | 6 | # Add OpenAI key to enable automated descriptions and embedding of functions. 7 | babyagi.add_key_wrapper('openai_api_key',os.environ['OPENAI_API_KEY']) 8 | 9 | @babyagi.register_function() 10 | def world(): 11 | return "world" 12 | 13 | @babyagi.register_function(dependencies=["world"]) 14 | def hello_world(): 15 | x = world() 16 | return f"Hello {x}!" 17 | 18 | print(hello_world()) 19 | 20 | @app.route('/') 21 | def home(): 22 | return f"Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 23 | 24 | if __name__ == "__main__": 25 | app = babyagi.create_app('/dashboard') 26 | app.run(host='0.0.0.0', port=8080) 27 | -------------------------------------------------------------------------------- /examples/trigger_example.py: -------------------------------------------------------------------------------- 1 | import babyagi 2 | 3 | @babyagi.register_function() 4 | def function_a(): 5 | print("Result from function A") 6 | return "Result from function A" 7 | 8 | @babyagi.register_function(triggers=['function_a']) 9 | def function_b(input_data): 10 | print(f"Function B triggered with input: {input_data}") 11 | return f"Function B triggered with input: {input_data}" 12 | 13 | function_a() 14 | 15 | @app.route('/') 16 | def home(): 17 | return f"Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 18 | 19 | if __name__ == "__main__": 20 | app = babyagi.create_app('/dashboard') 21 | app.run(host='0.0.0.0', port=8080) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import babyagi 2 | import os 3 | 4 | 5 | app = babyagi.create_app('/dashboard') 6 | 7 | # Add OpenAI key to enable automated descriptions and embedding of functions. 8 | babyagi.add_key_wrapper('openai_api_key',os.environ['OPENAI_API_KEY']) 9 | 10 | 11 | @app.route('/') 12 | def home(): 13 | return f"Welcome to the main app. Visit /dashboard for BabyAGI dashboard." 14 | 15 | if __name__ == "__main__": 16 | app.run(host='0.0.0.0', port=8080) 17 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "babyagi" 3 | version = "0.0.8" 4 | description = "" 5 | authors = ["Your Name "] 6 | 7 | [tool.poetry.dependencies] 8 | python = ">=3.10.0,<3.12" 9 | db = "^0.1.1" 10 | flask = "^3.0.3" 11 | werkzeug = "^3.0.4" 12 | setuptools = "^75.1.0" 13 | 14 | [tool.pyright] 15 | # https://github.com/microsoft/pyright/blob/main/docs/configuration.md 16 | useLibraryCodeForTypes = true 17 | exclude = [".cache"] 18 | 19 | [tool.ruff] 20 | # https://beta.ruff.rs/docs/configuration/ 21 | select = ['E', 'W', 'F', 'I', 'B', 'C4', 'ARG', 'SIM'] 22 | ignore = ['W291', 'W292', 'W293'] 23 | 24 | [build-system] 25 | requires = ["poetry-core>=1.0.0"] 26 | build-backend = "poetry.core.masonry.api" -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=2.0.0 2 | sqlalchemy>=1.4,<2.0 3 | cryptography 4 | scikit-learn 5 | litellm 6 | openai -------------------------------------------------------------------------------- /self_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yoheinakajima/babyagi/4f4fd1f62c2b0726479d6bc7bbf87463369a663e/self_build.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | # Read the long description from README.md 5 | with open("README.md", "r", encoding="utf-8") as fh: 6 | long_description = fh.read() 7 | 8 | # Read requirements from requirements.txt 9 | def parse_requirements(filename): 10 | with open(filename, "r") as f: 11 | lines = f.readlines() 12 | # Remove comments and empty lines 13 | return [line.strip() for line in lines if line.strip() and not line.startswith("#")] 14 | 15 | setup( 16 | name="babyagi", # Ensure this is the desired package name 17 | version="0.1.2", # Update this version appropriately 18 | author="Yohei Nakajima", 19 | author_email="babyagi@untapped.vc", 20 | description="An experimental prototype framework for building self building autonomous agents.", 21 | long_description= long_description, 22 | long_description_content_type="text/markdown", 23 | url="https://github.com/yoheinakajima/babyagi", # Update if necessary 24 | packages=find_packages(), 25 | include_package_data=True, # Include package data as specified in MANIFEST.in 26 | classifiers=[ 27 | "Programming Language :: Python :: 3", 28 | "License :: OSI Approved :: MIT License", 29 | "Operating System :: OS Independent", 30 | ], 31 | python_requires='>=3.6', 32 | install_requires=parse_requirements("requirements.txt"), 33 | entry_points={ 34 | 'console_scripts': [ 35 | 'babyagi=babyagi.main:main', # Example entry point 36 | ], 37 | }, 38 | keywords="AGI, AI, Framework, Baby AGI", 39 | project_urls={ # Optional 40 | "Author": "https://x.com/yoheinakajima", 41 | }, 42 | ) 43 | --------------------------------------------------------------------------------