fix: Replace automation stubs with real execution engine

- Replace automation execute stub with run_automation() from execution_engine.py
- Replace agent execute stub with run_agent() from agent_runner.py
- Persist automation settings in system_settings.automation_config JSONB
- Add migration 0034 for automation_config column
- Settings are now saved and loaded from database instead of being ignored
This commit is contained in:
Agent Zero
2026-07-25 02:52:07 +02:00
parent 46cadb5165
commit 6412eb03a8
4 changed files with 160 additions and 30 deletions
@@ -0,0 +1,27 @@
"""Add automation_config JSONB column to system_settings.
Revision ID: 0034
Revises: 0033_bank_accounts
Create Date: 2026-07-25
"""
from __future__ import annotations
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSONB
revision: str = "0034_automation_config"
down_revision: Union[str, None] = "0033_bank_accounts"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("system_settings", sa.Column("automation_config", JSONB, nullable=True))
def downgrade() -> None:
op.drop_column("system_settings", "automation_config")
+3
View File
@@ -50,3 +50,6 @@ class SystemSettings(Base, TenantMixin):
theme_accent_color: Mapped[str] = mapped_column(String(20), nullable=False, default="#d946ef")
theme_font_family: Mapped[str] = mapped_column(String(100), nullable=False, default="Inter")
theme_border_radius: Mapped[str] = mapped_column(String(20), nullable=False, default="0.5rem")
# Automation plugin settings (JSONB)
from sqlalchemy.dialects.postgresql import JSONB
automation_config: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
@@ -306,14 +306,38 @@ async def execute_agent(
db.add(run)
await db.flush()
# TODO: Execute agent asynchronously via Agent Runner
run.status = "completed"
run.completed_at = __import__("datetime").datetime.now(__import__("datetime").timezone.utc)
run.duration_seconds = 0.0
run.result = "Executed successfully (stub)"
await db.flush()
# Execute agent via agent runner
from app.plugins.builtins.automation.agent_runner import run_agent
return {"status": "ok", "run_id": str(run.id)}
run_id = str(run.id)
await db.commit()
# Run agent
result = await run_agent(
ctx={"tenant_id": str(tenant_id), "user_id": current_user["user_id"]},
agent_id=str(aid),
trigger_type="manual",
trigger_data={"triggered_by": current_user["user_id"]},
)
# Update run with results
from sqlalchemy import update as sa_update
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
async with db.begin():
await db.execute(
sa_update(AgentRun)
.where(AgentRun.id == run.id)
.values(
status=result.get("status", "completed"),
completed_at=now,
duration_seconds=0.0,
result=result,
output_data=result,
)
)
return {"status": result.get("status", "ok"), "run_id": run_id, "result": result}
@router.post(
+99 -23
View File
@@ -240,15 +240,28 @@ async def delete_miniapp(
)
async def get_automation_settings(
current_user: dict[str, Any] = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""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",
"""Get automation settings (persisted in system_settings metadata)."""
tenant_id = uuid.UUID(current_user["tenant_id"])
from app.models.system_settings import SystemSettings
from sqlalchemy import select
result = await db.execute(
select(SystemSettings).where(SystemSettings.tenant_id == tenant_id)
)
settings = result.scalar_one_or_none()
defaults = AutomationSettingsResponse()
if settings and hasattr(settings, "automation_config") and settings.automation_config:
cfg = settings.automation_config
return AutomationSettingsResponse(
default_llm_model=cfg.get("default_llm_model", defaults.default_llm_model),
heartbeat_default_interval=cfg.get("heartbeat_default_interval", defaults.heartbeat_default_interval),
max_concurrent_agents=cfg.get("max_concurrent_agents", defaults.max_concurrent_agents),
log_level=cfg.get("log_level", defaults.log_level),
)
return defaults
@router.patch(
@@ -259,17 +272,55 @@ async def get_automation_settings(
async def update_automation_settings(
data: AutomationSettingsUpdate,
current_user: dict[str, Any] = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""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",
"""Update automation settings (persisted in system_settings metadata)."""
tenant_id = uuid.UUID(current_user["tenant_id"])
from app.models.system_settings import SystemSettings
from sqlalchemy import select
from sqlalchemy.dialects.postgresql import JSONB
result = await db.execute(
select(SystemSettings).where(SystemSettings.tenant_id == tenant_id)
)
return settings
settings = result.scalar_one_or_none()
# Build new config
new_config = {
"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",
}
if settings:
# Update existing settings row
if hasattr(settings, "automation_config"):
settings.automation_config = new_config
else:
# Fallback: store in a generic metadata field if available
await db.execute(
"UPDATE system_settings SET automation_config = :cfg WHERE id = :sid",
{"cfg": new_config, "sid": settings.id},
)
else:
# Create new settings row with automation config
settings = SystemSettings(
tenant_id=tenant_id,
company_name="Default",
company_street="",
company_city="",
company_zip="",
company_country="DE",
)
if hasattr(settings, "automation_config"):
settings.automation_config = new_config
db.add(settings)
await db.flush()
await db.commit()
return AutomationSettingsResponse(**new_config)
@router.get(
"/{automation_id}",
dependencies=[Depends(require_permission("automation:read"))],
@@ -380,14 +431,39 @@ async def execute_automation(
db.add(run)
await db.flush()
# TODO: Execute automation actions asynchronously
run.status = "completed"
run.completed_at = __import__("datetime").datetime.now(__import__("datetime").timezone.utc)
run.duration_seconds = 0.0
run.result = "Executed successfully (stub)"
await db.flush()
# Execute automation via execution engine
import asyncio
from app.plugins.builtins.automation.execution_engine import run_automation
return {"status": "ok", "run_id": str(run.id)}
run_id = str(run.id)
await db.commit()
# Run automation asynchronously
result = await run_automation(
ctx={"tenant_id": str(tenant_id), "user_id": current_user["user_id"]},
automation_id=str(aid),
trigger_type="manual",
trigger_data={"triggered_by": current_user["user_id"]},
)
# Update run with results
from sqlalchemy import update as sa_update
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
async with db.begin():
await db.execute(
sa_update(AutomationRun)
.where(AutomationRun.id == run.id)
.values(
status=result.get("status", "completed"),
completed_at=now,
duration_seconds=0.0,
result=result,
output_data=result,
)
)
return {"status": result.get("status", "ok"), "run_id": run_id, "result": result}
@router.post(