From 992d4b79d479fbc658f31b77eb1a1b5e5eff1d9c Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Fri, 24 Jul 2026 10:42:08 +0200 Subject: [PATCH] =?UTF-8?q?fix(automation):=20reorder=20routes=20=E2=80=94?= =?UTF-8?q?=20static=20paths=20before=20dynamic=20{id}=20to=20prevent=2040?= =?UTF-8?q?0=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builtins/automation/agent_routes.py | 116 ++++---- app/plugins/builtins/automation/routes.py | 256 +++++++++--------- 2 files changed, 186 insertions(+), 186 deletions(-) diff --git a/app/plugins/builtins/automation/agent_routes.py b/app/plugins/builtins/automation/agent_routes.py index 7d59b17..60413b2 100644 --- a/app/plugins/builtins/automation/agent_routes.py +++ b/app/plugins/builtins/automation/agent_routes.py @@ -139,6 +139,64 @@ async def create_agent( return _agent_to_response(agent) + +@router.get( + "/tools", + dependencies=[Depends(require_permission("agents:read"))], +) +async def list_tools( + current_user: dict[str, Any] = Depends(get_current_user), +): + """List available tools from the tool registry.""" + try: + from app.plugins.builtins.ai_assistant.tool_registry import ( + get_tool_registry, + ) + + registry = get_tool_registry() + tools = registry.list_tools() + return { + "items": [ + { + "id": t.get("id", t.get("name", "")), + "name": t.get("name", ""), + "description": t.get("description", ""), + "plugin": t.get("plugin", ""), + } + for t in tools + ], + "total": len(tools), + } + except ImportError: + return {"items": [], "total": 0, "note": "Tool registry not available"} + except Exception as e: + logger.exception("Failed to list tools") + return {"items": [], "total": 0, "error": str(e)} + + +# ─── Recent Runs ─── + + +@router.get( + "/runs/recent", + dependencies=[Depends(require_permission("agents:read"))], + response_model=AgentRunListResponse, +) +async def list_recent_agent_runs( + limit: int = Query(20, ge=1, le=100), + status: str | None = Query(None), + current_user: dict[str, Any] = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +): + """List recent agent runs across all agents (for dashboard).""" + tenant_id = uuid.UUID(current_user["tenant_id"]) + items, total = await RunLogService.list_agent_runs( + db, tenant_id, agent_id=None, status=status, limit=limit, offset=0 + ) + return AgentRunListResponse( + items=[_agent_run_to_response(r) for r in items], + total=total, + ) @router.get( "/{agent_id}", dependencies=[Depends(require_permission("agents:read"))], @@ -419,61 +477,3 @@ async def send_agent_message_endpoint( ) return result - -@router.get( - "/tools", - dependencies=[Depends(require_permission("agents:read"))], -) -async def list_tools( - current_user: dict[str, Any] = Depends(get_current_user), -): - """List available tools from the tool registry.""" - try: - from app.plugins.builtins.ai_assistant.tool_registry import ( - get_tool_registry, - ) - - registry = get_tool_registry() - tools = registry.list_tools() - return { - "items": [ - { - "id": t.get("id", t.get("name", "")), - "name": t.get("name", ""), - "description": t.get("description", ""), - "plugin": t.get("plugin", ""), - } - for t in tools - ], - "total": len(tools), - } - except ImportError: - return {"items": [], "total": 0, "note": "Tool registry not available"} - except Exception as e: - logger.exception("Failed to list tools") - return {"items": [], "total": 0, "error": str(e)} - - -# ─── Recent Runs ─── - - -@router.get( - "/runs/recent", - dependencies=[Depends(require_permission("agents:read"))], - response_model=AgentRunListResponse, -) -async def list_recent_agent_runs( - limit: int = Query(20, ge=1, le=100), - status: str | None = Query(None), - current_user: dict[str, Any] = Depends(get_current_user), - db: AsyncSession = Depends(get_db), -): - """List recent agent runs across all agents (for dashboard).""" - tenant_id = uuid.UUID(current_user["tenant_id"]) - items, total = await RunLogService.list_agent_runs( - db, tenant_id, agent_id=None, status=status, limit=limit, offset=0 - ) - return AgentRunListResponse( - items=[_agent_run_to_response(r) for r in items], - total=total, - ) diff --git a/app/plugins/builtins/automation/routes.py b/app/plugins/builtins/automation/routes.py index b9873bf..f800d92 100644 --- a/app/plugins/builtins/automation/routes.py +++ b/app/plugins/builtins/automation/routes.py @@ -142,6 +142,134 @@ async def create_automation( return _automation_to_response(automation) +# ─── Recent Runs ─── + + +@router.get( + "/runs/recent", + dependencies=[Depends(require_permission("automation:read"))], + response_model=AutomationRunListResponse, +) +async def list_recent_automation_runs( + limit: int = Query(20, ge=1, le=100), + status: str | None = Query(None), + current_user: dict[str, Any] = Depends(get_current_user), + db: AsyncSession = Depends(get_db), +): + """List recent automation runs across all automations (for dashboard).""" + tenant_id = uuid.UUID(current_user["tenant_id"]) + items, total = await RunLogService.list_automation_runs( + db, tenant_id, automation_id=None, status=status, limit=limit, offset=0 + ) + return AutomationRunListResponse( + items=[_run_to_response(r) for r in items], + total=total, + ) + + +# ─── MiniApps ─── + + +@router.get( + "/miniapps", + dependencies=[Depends(require_permission("automation:read"))], + response_model=list[MiniAppResponse], +) +async def list_miniapps( + current_user: dict[str, Any] = Depends(get_current_user), +): + """List custom MiniApps from plugin config.""" + from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry + registry = MiniAppRegistry() + return registry.list_apps() + + +@router.post( + "/miniapps", + dependencies=[Depends(require_permission("automation:write"))], + response_model=MiniAppResponse, + status_code=201, +) +async def create_miniapp( + data: MiniAppCreate, + current_user: dict[str, Any] = Depends(get_current_user), +): + """Create a custom MiniApp definition.""" + from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry + registry = MiniAppRegistry() + registry.register( + app_id=data.app_id, + name=data.name, + icon=data.icon, + description=data.description, + plugin_name="automation", + render_schema=data.render_schema, + ) + return MiniAppResponse( + app_id=data.app_id, + name=data.name, + icon=data.icon, + description=data.description, + plugin_name="automation", + render_schema=data.render_schema, + ) + + +@router.delete( + "/miniapps/{app_id}", + dependencies=[Depends(require_permission("automation:delete"))], +) +async def delete_miniapp( + app_id: str, + current_user: dict[str, Any] = Depends(get_current_user), +): + """Delete a custom MiniApp definition.""" + from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry + registry = MiniAppRegistry() + registry.unregister(app_id) + return {"status": "ok"} + + +# ─── Settings ─── + + +@router.get( + "/settings", + dependencies=[Depends(require_permission("automation:configure"))], + response_model=AutomationSettingsResponse, +) +async def get_automation_settings( + current_user: dict[str, Any] = Depends(get_current_user), +): + """Get automation default settings.""" + # Return default settings (could be stored in plugin config in the future) + return AutomationSettingsResponse( + default_llm_model="ollama/deepseek-v4-flash", + heartbeat_default_interval=300, + max_concurrent_agents=5, + log_level="INFO", + ) + + +@router.patch( + "/settings", + dependencies=[Depends(require_permission("automation:configure"))], + response_model=AutomationSettingsResponse, +) +async def update_automation_settings( + data: AutomationSettingsUpdate, + current_user: dict[str, Any] = Depends(get_current_user), +): + """Update automation settings (stored in plugin config).""" + # TODO: Persist settings to plugin config table + # For now, return the updated values as a preview + settings = AutomationSettingsResponse( + default_llm_model=data.default_llm_model or "ollama/deepseek-v4-flash", + heartbeat_default_interval=data.heartbeat_default_interval or 300, + max_concurrent_agents=data.max_concurrent_agents or 5, + log_level=data.log_level or "INFO", + ) + return settings @router.get( "/{automation_id}", dependencies=[Depends(require_permission("automation:read"))], @@ -415,131 +543,3 @@ async def restore_automation_version( return _automation_to_response(automation) -# ─── Recent Runs ─── - - -@router.get( - "/runs/recent", - dependencies=[Depends(require_permission("automation:read"))], - response_model=AutomationRunListResponse, -) -async def list_recent_automation_runs( - limit: int = Query(20, ge=1, le=100), - status: str | None = Query(None), - current_user: dict[str, Any] = Depends(get_current_user), - db: AsyncSession = Depends(get_db), -): - """List recent automation runs across all automations (for dashboard).""" - tenant_id = uuid.UUID(current_user["tenant_id"]) - items, total = await RunLogService.list_automation_runs( - db, tenant_id, automation_id=None, status=status, limit=limit, offset=0 - ) - return AutomationRunListResponse( - items=[_run_to_response(r) for r in items], - total=total, - ) - - -# ─── MiniApps ─── - - -@router.get( - "/miniapps", - dependencies=[Depends(require_permission("automation:read"))], - response_model=list[MiniAppResponse], -) -async def list_miniapps( - current_user: dict[str, Any] = Depends(get_current_user), -): - """List custom MiniApps from plugin config.""" - from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry - registry = MiniAppRegistry() - return registry.list_apps() - - -@router.post( - "/miniapps", - dependencies=[Depends(require_permission("automation:write"))], - response_model=MiniAppResponse, - status_code=201, -) -async def create_miniapp( - data: MiniAppCreate, - current_user: dict[str, Any] = Depends(get_current_user), -): - """Create a custom MiniApp definition.""" - from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry - registry = MiniAppRegistry() - registry.register( - app_id=data.app_id, - name=data.name, - icon=data.icon, - description=data.description, - plugin_name="automation", - render_schema=data.render_schema, - ) - return MiniAppResponse( - app_id=data.app_id, - name=data.name, - icon=data.icon, - description=data.description, - plugin_name="automation", - render_schema=data.render_schema, - ) - - -@router.delete( - "/miniapps/{app_id}", - dependencies=[Depends(require_permission("automation:delete"))], -) -async def delete_miniapp( - app_id: str, - current_user: dict[str, Any] = Depends(get_current_user), -): - """Delete a custom MiniApp definition.""" - from app.plugins.builtins.kommunikation.miniapp_registry import MiniAppRegistry - registry = MiniAppRegistry() - registry.unregister(app_id) - return {"status": "ok"} - - -# ─── Settings ─── - - -@router.get( - "/settings", - dependencies=[Depends(require_permission("automation:configure"))], - response_model=AutomationSettingsResponse, -) -async def get_automation_settings( - current_user: dict[str, Any] = Depends(get_current_user), -): - """Get automation default settings.""" - # Return default settings (could be stored in plugin config in the future) - return AutomationSettingsResponse( - default_llm_model="ollama/deepseek-v4-flash", - heartbeat_default_interval=300, - max_concurrent_agents=5, - log_level="INFO", - ) - - -@router.patch( - "/settings", - dependencies=[Depends(require_permission("automation:configure"))], - response_model=AutomationSettingsResponse, -) -async def update_automation_settings( - data: AutomationSettingsUpdate, - current_user: dict[str, Any] = Depends(get_current_user), -): - """Update automation settings (stored in plugin config).""" - # TODO: Persist settings to plugin config table - # For now, return the updated values as a preview - settings = AutomationSettingsResponse( - default_llm_model=data.default_llm_model or "ollama/deepseek-v4-flash", - heartbeat_default_interval=data.heartbeat_default_interval or 300, - max_concurrent_agents=data.max_concurrent_agents or 5, - log_level=data.log_level or "INFO", - ) - return settings