Skip to content

Commit 86cc8e6

Browse files
authored
feat(AI Assistant): scaffold automations (baserow#4172)
* kuma scaffolds automations * Address copilot feedback * Address feedback
1 parent ecc358a commit 86cc8e6

File tree

26 files changed

+1279
-126
lines changed

26 files changed

+1279
-126
lines changed

backend/.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ per-file-ignores =
1111
exclude =
1212
.git,
1313
__pycache__,
14+
src/baserow/config/settings/local.py,
1415
src/baserow/core/formula/parser/generated
1516

1617

backend/src/baserow/contrib/automation/nodes/node_types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ def after_create(self, node: CoreRouterActionNode):
245245
:param node: The router node instance that was just created.
246246
"""
247247

248-
node.service.edges.create(label=_("Branch"))
248+
if not len(node.service.edges.all()):
249+
node.service.edges.create(label=_("Branch"))
249250

250251
def prepare_values(
251252
self,

backend/src/baserow/contrib/automation/workflows/service.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,30 @@ def get_workflow(self, user: AbstractUser, workflow_id: int) -> AutomationWorkfl
5454

5555
return workflow
5656

57+
def list_workflows(
58+
self, user: AbstractUser, automation_id: int
59+
) -> List[AutomationWorkflow]:
60+
"""
61+
Lists all the workflows that belong to the given automation.
62+
63+
:param user: The user requesting the workflows.
64+
:param automation_id: The automation to which the workflows belong.
65+
:return: A list of AutomationWorkflow instances.
66+
"""
67+
68+
automation = AutomationHandler().get_automation(automation_id)
69+
70+
all_workflows = self.handler.get_workflows(
71+
automation, base_queryset=AutomationWorkflow.objects
72+
)
73+
74+
return CoreHandler().filter_queryset(
75+
user,
76+
ReadAutomationWorkflowOperationType.type,
77+
all_workflows,
78+
workspace=automation.workspace,
79+
)
80+
5781
def create_workflow(
5882
self,
5983
user: AbstractUser,

enterprise/backend/src/baserow_enterprise/apps.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,18 +303,20 @@ def ready(self):
303303
notification_type_registry.register(TwoWaySyncDeactivatedNotificationType())
304304

305305
from baserow_enterprise.assistant.tools import (
306-
CreateDatabaseToolType,
306+
CreateBuildersToolType,
307307
CreateFieldsToolType,
308308
CreateTablesToolType,
309309
CreateViewFiltersToolType,
310310
CreateViewsToolType,
311+
CreateWorkflowsToolType,
311312
GenerateDatabaseFormulaToolType,
312313
GetRowsToolsToolType,
313314
GetTablesSchemaToolType,
314-
ListDatabasesToolType,
315+
ListBuildersToolType,
315316
ListRowsToolType,
316317
ListTablesToolType,
317318
ListViewsToolType,
319+
ListWorkflowsToolType,
318320
NavigationToolType,
319321
SearchDocsToolType,
320322
)
@@ -325,8 +327,8 @@ def ready(self):
325327
assistant_tool_registry.register(SearchDocsToolType())
326328
assistant_tool_registry.register(NavigationToolType())
327329

328-
assistant_tool_registry.register(ListDatabasesToolType())
329-
assistant_tool_registry.register(CreateDatabaseToolType())
330+
assistant_tool_registry.register(ListBuildersToolType())
331+
assistant_tool_registry.register(CreateBuildersToolType())
330332
assistant_tool_registry.register(ListTablesToolType())
331333
assistant_tool_registry.register(CreateTablesToolType())
332334
assistant_tool_registry.register(GetTablesSchemaToolType())
@@ -338,6 +340,9 @@ def ready(self):
338340
assistant_tool_registry.register(CreateViewsToolType())
339341
assistant_tool_registry.register(CreateViewFiltersToolType())
340342

343+
assistant_tool_registry.register(ListWorkflowsToolType())
344+
assistant_tool_registry.register(CreateWorkflowsToolType())
345+
341346
# The signals must always be imported last because they use the registries
342347
# which need to be filled first.
343348
import baserow_enterprise.audit_log.signals # noqa: F

enterprise/backend/src/baserow_enterprise/assistant/assistant.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ def _init_lm_client(self):
145145
model=lm_model,
146146
cache=not settings.DEBUG,
147147
max_retries=5,
148+
max_tokens=32000,
148149
)
149150

150151
def _init_assistant(self):

enterprise/backend/src/baserow_enterprise/assistant/prompts.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@
3838
AUTOMATION_BUILDER_CONCEPTS = """
3939
### AUTOMATIONS (no-code automation builder)
4040
41-
**Structure**: Automation → Workflows → Triggers + Actions + Routers (Nodes)
41+
**Structure**: Automation → Workflows → Trigger + Actions + Routers (Nodes)
4242
4343
**Key concepts**:
44-
• **Triggers**: Events that start automations (e.g., row created/updated, view accessed)
44+
• **Trigger**: The single event that starts the workflow (e.g., row created/updated/deleted)
4545
• **Actions**: Tasks performed (e.g., create/update rows, send emails, call webhooks)
4646
• **Routers**: Conditional logic (if/else, switch) to control flow
4747
• **Execution**: Runs in the background; monitor via logs
4848
• **History**: Track runs, successes, failures
49-
• **Publishing**: Requires domain configuration
49+
• **Publishing**: Requires at least one configured action
5050
"""
5151

5252
ASSISTANT_SYSTEM_PROMPT = (
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from .automation.tools import * # noqa: F401, F403
2+
from .core.tools import * # noqa: F401, F403
13
from .database.tools import * # noqa: F401, F403
24
from .navigation.tools import * # noqa: F401, F403
35
from .search_docs.tools import * # noqa: F401, F403
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .tools import CreateWorkflowsToolType, ListWorkflowsToolType
2+
3+
__all__ = [
4+
"ListWorkflowsToolType",
5+
"CreateWorkflowsToolType",
6+
]
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from typing import TYPE_CHECKING, Any, Callable
2+
3+
from django.contrib.auth.models import AbstractUser
4+
from django.db import transaction
5+
from django.utils.translation import gettext as _
6+
7+
from baserow.contrib.automation.workflows.service import AutomationWorkflowService
8+
from baserow.core.models import Workspace
9+
from baserow_enterprise.assistant.tools.registries import AssistantToolType
10+
from baserow_enterprise.assistant.types import WorkflowNavigationType
11+
12+
from . import utils
13+
from .types import WorkflowCreate
14+
15+
if TYPE_CHECKING:
16+
from baserow_enterprise.assistant.assistant import ToolHelpers
17+
18+
19+
def get_list_workflows_tool(
20+
user: AbstractUser, workspace: Workspace, tool_helpers: "ToolHelpers"
21+
) -> Callable[[int], dict[str, list[dict]]]:
22+
"""
23+
List all workflows in an automation.
24+
"""
25+
26+
def list_workflows(automation_id: int) -> dict[str, Any]:
27+
"""
28+
List all workflows in an automation application.
29+
30+
:param automation_id: The ID of the automation application
31+
:return: Dictionary with workflows list
32+
"""
33+
34+
nonlocal user, workspace, tool_helpers
35+
36+
tool_helpers.update_status(_("Listing workflows..."))
37+
38+
automation = utils.get_automation(automation_id, user, workspace)
39+
workflows = AutomationWorkflowService().list_workflows(user, automation.id)
40+
41+
return {
42+
"workflows": [
43+
{"id": w.id, "name": w.name, "state": w.state} for w in workflows
44+
]
45+
}
46+
47+
return list_workflows
48+
49+
50+
def get_create_workflows_tool(
51+
user: AbstractUser, workspace: Workspace, tool_helpers: "ToolHelpers"
52+
) -> Callable[[int, list[WorkflowCreate]], dict[str, list[dict]]]:
53+
"""
54+
Create new workflows.
55+
"""
56+
57+
def create_workflows(
58+
automation_id: int, workflows: list[WorkflowCreate]
59+
) -> dict[str, Any]:
60+
"""
61+
Create one or more workflows in an automation.
62+
63+
:param automation_id: The automation application ID
64+
:param workflows: List of workflows to create
65+
:return: Dictionary with created workflows
66+
"""
67+
68+
nonlocal user, workspace, tool_helpers
69+
70+
tool_helpers.update_status(_("Creating workflows..."))
71+
72+
created = []
73+
74+
automation = utils.get_automation(automation_id, user, workspace)
75+
for wf in workflows:
76+
with transaction.atomic():
77+
orm_workflow = utils.create_workflow(user, automation, wf)
78+
created.append(
79+
{
80+
"id": orm_workflow.id,
81+
"name": orm_workflow.name,
82+
"state": orm_workflow.state,
83+
}
84+
)
85+
# Navigate to the last created workflow
86+
tool_helpers.navigate_to(
87+
WorkflowNavigationType(
88+
type="automation-workflow",
89+
automation_id=automation.id,
90+
workflow_id=orm_workflow.id,
91+
workflow_name=orm_workflow.name,
92+
)
93+
)
94+
95+
return {"created_workflows": created}
96+
97+
return create_workflows
98+
99+
100+
# ============================================================================
101+
# TOOL TYPE REGISTRY
102+
# ============================================================================
103+
104+
105+
class ListWorkflowsToolType(AssistantToolType):
106+
type = "list_workflows"
107+
108+
@classmethod
109+
def get_tool(cls, user, workspace, tool_helpers):
110+
return get_list_workflows_tool(user, workspace, tool_helpers)
111+
112+
113+
class CreateWorkflowsToolType(AssistantToolType):
114+
type = "create_workflows"
115+
116+
@classmethod
117+
def get_tool(cls, user, workspace, tool_helpers):
118+
return get_create_workflows_tool(user, workspace, tool_helpers)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from .node import (
2+
AiAgentNodeCreate,
3+
CreateRowActionCreate,
4+
DeleteRowActionCreate,
5+
NodeBase,
6+
RouterNodeCreate,
7+
SendEmailActionCreate,
8+
TriggerNodeCreate,
9+
UpdateRowActionCreate,
10+
)
11+
from .workflow import WorkflowCreate, WorkflowItem
12+
13+
__all__ = [
14+
"WorkflowCreate",
15+
"WorkflowItem",
16+
"NodeBase",
17+
"RouterNodeCreate",
18+
"CreateRowActionCreate",
19+
"UpdateRowActionCreate",
20+
"DeleteRowActionCreate",
21+
"SendEmailActionCreate",
22+
"AiAgentNodeCreate",
23+
"TriggerNodeCreate",
24+
]

0 commit comments

Comments
 (0)