From 2d302067b24da6705dcf9018770a339004467696 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 23:04:18 +0000 Subject: [PATCH] Add JSON-based route registration system with API handler plugins Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- .../web_server_json_routes/package.json | 13 ++ .../web_server_json_routes/workflow.json | 135 ++++++++++++++++++ .../autometabuilder/workflow/plugin_map.json | 8 +- .../web/web_api_navigation/package.json | 8 ++ .../web_api_navigation/web_api_navigation.py | 7 + .../web_api_translation_options/package.json | 8 ++ .../web_api_translation_options.py | 8 ++ .../web/web_api_workflow_graph/package.json | 8 ++ .../web_api_workflow_graph.py | 8 ++ .../web_api_workflow_packages/package.json | 8 ++ .../web_api_workflow_packages.py | 8 ++ .../web/web_api_workflow_plugins/package.json | 8 ++ .../web_api_workflow_plugins.py | 9 ++ .../web/web_register_routes/package.json | 8 ++ .../web_register_routes.py | 104 ++++++++++++++ 15 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 backend/autometabuilder/packages/web_server_json_routes/package.json create mode 100644 backend/autometabuilder/packages/web_server_json_routes/workflow.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_navigation/package.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_navigation/web_api_navigation.py create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_translation_options/package.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_translation_options/web_api_translation_options.py create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/package.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/web_api_workflow_graph.py create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/package.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/web_api_workflow_packages.py create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/package.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/web_api_workflow_plugins.py create mode 100644 backend/autometabuilder/workflow/plugins/web/web_register_routes/package.json create mode 100644 backend/autometabuilder/workflow/plugins/web/web_register_routes/web_register_routes.py diff --git a/backend/autometabuilder/packages/web_server_json_routes/package.json b/backend/autometabuilder/packages/web_server_json_routes/package.json new file mode 100644 index 0000000..7f9def7 --- /dev/null +++ b/backend/autometabuilder/packages/web_server_json_routes/package.json @@ -0,0 +1,13 @@ +{ + "name": "web_server_json_routes", + "version": "1.0.0", + "description": "Web server with routes defined in JSON workflow", + "main": "workflow.json", + "author": "AutoMetabuilder", + "metadata": { + "label": "Web Server (JSON Routes)", + "tags": ["web", "server", "json-routes"], + "icon": "web", + "category": "templates" + } +} diff --git a/backend/autometabuilder/packages/web_server_json_routes/workflow.json b/backend/autometabuilder/packages/web_server_json_routes/workflow.json new file mode 100644 index 0000000..aca7fcd --- /dev/null +++ b/backend/autometabuilder/packages/web_server_json_routes/workflow.json @@ -0,0 +1,135 @@ +{ + "name": "Web Server with JSON Routes", + "active": true, + "nodes": [ + { + "id": "configure_logging", + "name": "Configure Logging", + "type": "backend.configure_logging", + "typeVersion": 1, + "position": [0, 0], + "parameters": {} + }, + { + "id": "load_env", + "name": "Load Environment", + "type": "backend.load_env", + "typeVersion": 1, + "position": [300, 0], + "parameters": {} + }, + { + "id": "create_app", + "name": "Create Flask App", + "type": "web.create_flask_app", + "typeVersion": 1, + "position": [600, 0], + "parameters": { + "name": "autometabuilder", + "config": { + "JSON_SORT_KEYS": false + } + } + }, + { + "id": "register_api_routes", + "name": "Register API Routes", + "type": "web.register_routes", + "typeVersion": 1, + "position": [900, 0], + "parameters": { + "blueprint_name": "api", + "routes": [ + { + "path": "/api/navigation", + "methods": ["GET"], + "handler": "web.api_navigation", + "handler_type": "plugin" + }, + { + "path": "/api/workflow/packages", + "methods": ["GET"], + "handler": "web.api_workflow_packages", + "handler_type": "plugin" + }, + { + "path": "/api/workflow/plugins", + "methods": ["GET"], + "handler": "web.api_workflow_plugins", + "handler_type": "plugin" + }, + { + "path": "/api/workflow/graph", + "methods": ["GET"], + "handler": "web.api_workflow_graph", + "handler_type": "plugin" + }, + { + "path": "/api/translation-options", + "methods": ["GET"], + "handler": "web.api_translation_options", + "handler_type": "plugin" + } + ] + } + }, + { + "id": "start_server", + "name": "Start Web Server", + "type": "web.start_server", + "typeVersion": 1, + "position": [1200, 0], + "parameters": { + "host": "0.0.0.0", + "port": 8000, + "debug": false + } + } + ], + "connections": { + "Configure Logging": { + "main": { + "0": [ + { + "node": "Load Environment", + "type": "main", + "index": 0 + } + ] + } + }, + "Load Environment": { + "main": { + "0": [ + { + "node": "Create Flask App", + "type": "main", + "index": 0 + } + ] + } + }, + "Create Flask App": { + "main": { + "0": [ + { + "node": "Register API Routes", + "type": "main", + "index": 0 + } + ] + } + }, + "Register API Routes": { + "main": { + "0": [ + { + "node": "Start Web Server", + "type": "main", + "index": 0 + } + ] + } + } + } +} diff --git a/backend/autometabuilder/workflow/plugin_map.json b/backend/autometabuilder/workflow/plugin_map.json index cfe77b1..8072c12 100644 --- a/backend/autometabuilder/workflow/plugin_map.json +++ b/backend/autometabuilder/workflow/plugin_map.json @@ -122,5 +122,11 @@ "web.update_translation": "autometabuilder.workflow.plugins.web.web_update_translation.web_update_translation.run", "web.write_messages_dir": "autometabuilder.workflow.plugins.web.web_write_messages_dir.web_write_messages_dir.run", "web.write_prompt": "autometabuilder.workflow.plugins.web.web_write_prompt.web_write_prompt.run", - "web.write_workflow": "autometabuilder.workflow.plugins.web.web_write_workflow.web_write_workflow.run" + "web.write_workflow": "autometabuilder.workflow.plugins.web.web_write_workflow.web_write_workflow.run", + "web.register_routes": "autometabuilder.workflow.plugins.web.web_register_routes.web_register_routes.run", + "web.api_navigation": "autometabuilder.workflow.plugins.web.web_api_navigation.web_api_navigation.run", + "web.api_workflow_packages": "autometabuilder.workflow.plugins.web.web_api_workflow_packages.web_api_workflow_packages.run", + "web.api_workflow_plugins": "autometabuilder.workflow.plugins.web.web_api_workflow_plugins.web_api_workflow_plugins.run", + "web.api_workflow_graph": "autometabuilder.workflow.plugins.web.web_api_workflow_graph.web_api_workflow_graph.run", + "web.api_translation_options": "autometabuilder.workflow.plugins.web.web_api_translation_options.web_api_translation_options.run" } \ No newline at end of file diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_navigation/package.json b/backend/autometabuilder/workflow/plugins/web/web_api_navigation/package.json new file mode 100644 index 0000000..28567dd --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_navigation/package.json @@ -0,0 +1,8 @@ +{ + "name": "web.api_navigation", + "version": "1.0.0", + "description": "Handle /api/navigation endpoint", + "main": "web_api_navigation.py", + "author": "AutoMetabuilder", + "category": "web" +} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_navigation/web_api_navigation.py b/backend/autometabuilder/workflow/plugins/web/web_api_navigation/web_api_navigation.py new file mode 100644 index 0000000..6ea7eba --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_navigation/web_api_navigation.py @@ -0,0 +1,7 @@ +"""Workflow plugin: handle /api/navigation endpoint.""" + + +def run(_runtime, _inputs): + """Return navigation items.""" + from autometabuilder.data import get_navigation_items + return {"result": {"navigation": get_navigation_items()}} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_translation_options/package.json b/backend/autometabuilder/workflow/plugins/web/web_api_translation_options/package.json new file mode 100644 index 0000000..1b32134 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_translation_options/package.json @@ -0,0 +1,8 @@ +{ + "name": "web.api_translation_options", + "version": "1.0.0", + "description": "Handle /api/translation-options endpoint", + "main": "web_api_translation_options.py", + "author": "AutoMetabuilder", + "category": "web" +} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_translation_options/web_api_translation_options.py b/backend/autometabuilder/workflow/plugins/web/web_api_translation_options/web_api_translation_options.py new file mode 100644 index 0000000..d239084 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_translation_options/web_api_translation_options.py @@ -0,0 +1,8 @@ +"""Workflow plugin: handle /api/translation-options endpoint.""" + + +def run(_runtime, _inputs): + """Return available translations.""" + from autometabuilder.data import list_translations + translations = list_translations() + return {"result": {"translations": translations}} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/package.json b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/package.json new file mode 100644 index 0000000..3599a78 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/package.json @@ -0,0 +1,8 @@ +{ + "name": "web.api_workflow_graph", + "version": "1.0.0", + "description": "Handle /api/workflow/graph endpoint", + "main": "web_api_workflow_graph.py", + "author": "AutoMetabuilder", + "category": "web" +} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/web_api_workflow_graph.py b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/web_api_workflow_graph.py new file mode 100644 index 0000000..d3a6017 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_graph/web_api_workflow_graph.py @@ -0,0 +1,8 @@ +"""Workflow plugin: handle /api/workflow/graph endpoint.""" + + +def run(_runtime, _inputs): + """Return workflow graph.""" + from autometabuilder.workflow.workflow_graph import build_workflow_graph + graph = build_workflow_graph() + return {"result": graph} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/package.json b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/package.json new file mode 100644 index 0000000..9efe296 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/package.json @@ -0,0 +1,8 @@ +{ + "name": "web.api_workflow_packages", + "version": "1.0.0", + "description": "Handle /api/workflow/packages endpoint", + "main": "web_api_workflow_packages.py", + "author": "AutoMetabuilder", + "category": "web" +} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/web_api_workflow_packages.py b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/web_api_workflow_packages.py new file mode 100644 index 0000000..c8e1647 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_packages/web_api_workflow_packages.py @@ -0,0 +1,8 @@ +"""Workflow plugin: handle /api/workflow/packages endpoint.""" + + +def run(_runtime, _inputs): + """Return workflow packages.""" + from autometabuilder.data import load_workflow_packages, summarize_workflow_packages + packages = load_workflow_packages() + return {"result": {"packages": summarize_workflow_packages(packages)}} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/package.json b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/package.json new file mode 100644 index 0000000..a2d5568 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/package.json @@ -0,0 +1,8 @@ +{ + "name": "web.api_workflow_plugins", + "version": "1.0.0", + "description": "Handle /api/workflow/plugins endpoint", + "main": "web_api_workflow_plugins.py", + "author": "AutoMetabuilder", + "category": "web" +} diff --git a/backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/web_api_workflow_plugins.py b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/web_api_workflow_plugins.py new file mode 100644 index 0000000..1f6217c --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_api_workflow_plugins/web_api_workflow_plugins.py @@ -0,0 +1,9 @@ +"""Workflow plugin: handle /api/workflow/plugins endpoint.""" + + +def run(_runtime, _inputs): + """Return workflow plugins metadata.""" + from autometabuilder.utils import load_metadata + metadata = load_metadata() + plugins = metadata.get("workflow_plugins", {}) + return {"result": {"plugins": plugins}} diff --git a/backend/autometabuilder/workflow/plugins/web/web_register_routes/package.json b/backend/autometabuilder/workflow/plugins/web/web_register_routes/package.json new file mode 100644 index 0000000..8ebf76c --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_register_routes/package.json @@ -0,0 +1,8 @@ +{ + "name": "web.register_routes", + "version": "1.0.0", + "description": "Register Flask routes from JSON configuration", + "main": "web_register_routes.py", + "author": "AutoMetabuilder", + "category": "web" +} diff --git a/backend/autometabuilder/workflow/plugins/web/web_register_routes/web_register_routes.py b/backend/autometabuilder/workflow/plugins/web/web_register_routes/web_register_routes.py new file mode 100644 index 0000000..6ead199 --- /dev/null +++ b/backend/autometabuilder/workflow/plugins/web/web_register_routes/web_register_routes.py @@ -0,0 +1,104 @@ +"""Workflow plugin: register routes from JSON configuration.""" +from flask import Blueprint, jsonify, request + + +def run(runtime, inputs): + """ + Register routes from JSON configuration. + + This allows routes to be defined declaratively in the workflow JSON + rather than in Python code. + + Inputs: + blueprint_name: Name for the blueprint (required) + routes: List of route configurations (required) + Each route should have: + - path: The URL path (e.g., "/api/navigation") + - methods: List of HTTP methods (default: ["GET"]) + - handler: Name of the handler function or plugin to call + - handler_type: "plugin" or "function" (default: "plugin") + - handler_inputs: Inputs to pass to the plugin/function (optional) + + Returns: + dict: Contains the blueprint in result + """ + app = runtime.context.get("flask_app") + if not app: + return {"error": "Flask app not found in context. Run web.create_flask_app first."} + + blueprint_name = inputs.get("blueprint_name") + if not blueprint_name: + return {"error": "blueprint_name is required"} + + routes = inputs.get("routes", []) + if not routes: + return {"error": "routes list is required"} + + # Create blueprint + blueprint = Blueprint(blueprint_name, __name__) + + # Register each route + for route_config in routes: + path = route_config.get("path") + if not path: + runtime.logger.error(f"Route missing 'path' in {blueprint_name}") + continue + + methods = route_config.get("methods", ["GET"]) + handler = route_config.get("handler") + handler_type = route_config.get("handler_type", "plugin") + handler_inputs = route_config.get("handler_inputs", {}) + + if not handler: + runtime.logger.error(f"Route {path} missing 'handler' in {blueprint_name}") + continue + + # Create route handler function + def make_handler(handler_name, h_type, h_inputs): + """Create a handler function with captured variables.""" + def route_handler(): + try: + if h_type == "plugin": + # Execute plugin and return result + from autometabuilder.workflow.plugin_registry import load_plugin_map, PluginRegistry + plugin_map = load_plugin_map() + registry = PluginRegistry(plugin_map) + plugin = registry.get(handler_name) + + if not plugin: + return jsonify({"error": f"Plugin {handler_name} not found"}), 500 + + # Merge handler inputs with any request data + inputs_copy = dict(h_inputs) + if request.method == "POST" and request.is_json: + inputs_copy.update(request.get_json()) + + result = plugin(runtime, inputs_copy) + + # If result has a "result" key, return that + if isinstance(result, dict) and "result" in result: + return jsonify(result["result"]), 200 + + return jsonify(result), 200 + else: + # For function type, could load and call a function + return jsonify({"error": "Function handler type not yet implemented"}), 500 + + except Exception as e: + runtime.logger.error(f"Error in route handler {path}: {e}") + return jsonify({"error": str(e)}), 500 + + return route_handler + + # Add route to blueprint + handler_func = make_handler(handler, handler_type, handler_inputs) + handler_func.__name__ = f"{blueprint_name}_{path.replace('/', '_')}" + blueprint.add_url_rule(path, view_func=handler_func, methods=methods) + + # Register blueprint with app + app.register_blueprint(blueprint) + + return { + "result": blueprint, + "message": f"Registered blueprint '{blueprint_name}' with {len(routes)} routes" + }