Skip to content

Commit e81049d

Browse files
feat(AI Assistant): generates formulas for workflow nodes (baserow#4199)
* Kuma generates formulas for workflow nodes * address feedback * Set mode=BASEROW_FORMULA_MODE_ADVANCED, not type. --------- Co-authored-by: peter_baserow <peter@baserow.io>
1 parent 6813015 commit e81049d

File tree

9 files changed

+962
-39
lines changed

9 files changed

+962
-39
lines changed

backend/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ lint: .check-dev
152152
$(VISORT) --check --skip generated $(BACKEND_SOURCE_DIRS) $(BACKEND_TESTS_DIRS)
153153
# TODO: make baserow command reading dotenv files
154154
DJANGO_SETTINGS_MODULE=$(DJANGO_SETTINGS_MODULE) $(VBASEROW) makemigrations --dry-run --check
155-
$(VBANDIT) -r --exclude src/baserow/test_utils $(BACKEND_SOURCE_DIRS)
155+
$(VBANDIT) -r --exclude src/baserow/test_utils,src/baserow/config/settings/local.py $(BACKEND_SOURCE_DIRS)
156156

157157
lint-python: lint
158158

backend/src/baserow/config/settings/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ def __setitem__(self, key, value):
10491049
# The minimum amount of minutes the periodic task's "minute" interval
10501050
# supports. Self-hosters can run every minute, if they choose to.
10511051
INTEGRATIONS_PERIODIC_MINUTE_MIN = int(
1052-
os.getenv("BASEROW_INTEGRATIONS_PERIODIC_MINUTE_MIN", 1)
1052+
os.getenv("BASEROW_INTEGRATIONS_PERIODIC_MINUTE_MIN") or 1
10531053
)
10541054

10551055
TOTP_ISSUER_NAME = os.getenv("BASEROW_TOTP_ISSUER_NAME", "Baserow")

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from django.conf import settings
2+
13
CORE_CONCEPTS = """
24
### BASEROW STRUCTURE
35
@@ -50,21 +52,22 @@
5052
"""
5153

5254
ASSISTANT_SYSTEM_PROMPT = (
53-
"""
55+
f"""
5456
You are Kuma, an AI expert for Baserow (open-source no-code platform).
5557
5658
## YOUR KNOWLEDGE
5759
1. **Core concepts** (below)
5860
2. **Detailed docs** - use search_docs tool to search when needed
59-
3. **API specs** - guide users to https://api.baserow.io/api/schema.json
61+
3. **API specs** - guide users to "{settings.PUBLIC_BACKEND_URL}/api/schema.json"
62+
4. **Official website** - "https://baserow.io"
6063
6164
## HOW TO HELP
6265
• Use American English spelling and grammar
6366
• Be clear, concise, and actionable
6467
• For troubleshooting: ask for error messages or describe expected vs actual results
6568
• **NEVER** fabricate answers or URLs. Acknowledge when you can't be sure.
6669
• When you have the tools to help, **ALWAYS** use them instead of answering with instructions.
67-
* At the end, **always** ask follow-up questions to understand user needs and continue the conversation.
70+
• When finished, briefly suggest one or more logical next steps only if they use tools you have access to and directly builds on what was just done.
6871
6972
## FORMATTING (CRITICAL)
7073
• **No HTML**: Only Markdown (bold, italics, lists, code, tables)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
GENERATE_FORMULA_PROMPT = """
2+
You are a formula builder. Generate formulas using these functions:
3+
4+
**Comparison operators** (for router conditions only):
5+
equal, not_equal, greater_than, less_than, greater_than_equal, less_than_equal
6+
- Arguments: numbers, 'strings', or get() functions
7+
- Returns: boolean
8+
- Example: greater_than(get('age'), 18)
9+
10+
**concat(...args)** - Joins arguments into a string
11+
- Arguments: 'string literals' or get() functions
12+
- Example: concat('Hello ', get('name'), '!')
13+
14+
**get(path)** - Retrieves values from context using path notation
15+
- Objects: get('user.name')
16+
- Arrays: get('items.0'), get('orders.2.total')
17+
- Nested: get('users.0.address.city')
18+
- All: get('users.*.email') returns a list of emails from all users
19+
20+
**if(condition, true_value, false_value)** - Conditional expression
21+
- Arguments: a boolean condition, value if true, value if false
22+
- Example: if(greater_than(get('score'), 50), 'pass', 'fail')
23+
24+
**today()** - Returns the current date
25+
**now()** - Returns the current date and time
26+
27+
**constants**:
28+
- A string literal enclosed in single quotes (e.g., 'hello world', '123')
29+
30+
**Example 1 - String Fields:**
31+
Input:
32+
fields_to_resolve: {
33+
"ai_prompt": "Determine the priority level based on {{ trigger.title }} and {{ trigger.due_date }}. Choices are: High, Medium, Low.",
34+
}
35+
context: {"previous_node": {"1": [{"title": "Finish report", "due_date": "2025-11-08"}]}}
36+
context_metadata: {
37+
"1": {"id": 1, "ref": "trigger", "field_1": {"name": "title", "type": "string"}, "field_2": {"name": "due_date", "type": "date"}},
38+
"today": "2025-11-07"
39+
}
40+
feedback: ""
41+
Output:
42+
generated_formula: {
43+
"ai_prompt": "concat(
44+
'Determine the priority level based on ',
45+
get('previous_node.1.0.title'),
46+
' and ',
47+
get('previous_node.1.0.due_date'),
48+
'. Choices are: High, Medium, Low.'
49+
)"
50+
}
51+
**Example 2 - Router Conditions:**
52+
Input:
53+
fields_to_resolve: {
54+
"condition_1": "Check if {{ trigger.amount }} is greater than 1000",
55+
}
56+
context: {"previous_node": {"1": [{"amount": 1500}]}},
57+
context_metadata: {
58+
"1": {"id": 1, "ref": "trigger", "field_1": {"name": "amount", "type": "number"}},
59+
}
60+
feedback: ""
61+
Output:
62+
generated_formula: {
63+
"condition_1": "greater_than(get('previous_node.1.0.amount'), 1000)"
64+
}
65+
66+
**Task:**
67+
68+
You are given:
69+
* **fields_to_resolve** — a dictionary where each key is a field name and each value contains instructions to generate a formula.
70+
* **context** — a dictionary containing the available data.
71+
* **context_metadata** — a dictionary describing the structure and types within the context.
72+
* **feedback** — optional information with reported formula errors from previous runs.
73+
74+
**Goal:**
75+
Generate a dictionary called **generated_formula**, where:
76+
77+
* Keys are the field names from **fields_to_resolve**.
78+
* Values are valid formulas that can be used in the automation node.
79+
80+
**Rules:**
81+
82+
1. Feel free to skip fields whose description starts with `[optional]`.
83+
2. Exclude any field if you cannot generate a valid formula for it.
84+
3. If **feedback** is provided, use it to refine or correct the generated formulas.
85+
4. Strive to produce the most accurate and useful formulas possible based on the provided context and metadata.
86+
"""

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ def create_workflows(
5858
automation_id: int, workflows: list[WorkflowCreate]
5959
) -> dict[str, Any]:
6060
"""
61-
Create one or more workflows in an automation.
61+
Create one or more workflows in an automation. Always use {{ node.ref }} to
62+
reference previous nodes values inside the workflow.
6263
6364
:param automation_id: The automation application ID
6465
:param workflows: List of workflows to create
@@ -67,21 +68,26 @@ def create_workflows(
6768

6869
nonlocal user, workspace, tool_helpers
6970

70-
tool_helpers.update_status(_("Creating workflows..."))
71-
7271
created = []
7372

7473
automation = utils.get_automation(automation_id, user, workspace)
7574
for wf in workflows:
7675
with transaction.atomic():
77-
orm_workflow = utils.create_workflow(user, automation, wf)
76+
orm_workflow, node_mapping = utils.create_workflow(
77+
user, automation, wf, tool_helpers
78+
)
7879
created.append(
7980
{
8081
"id": orm_workflow.id,
8182
"name": orm_workflow.name,
8283
"state": orm_workflow.state,
8384
}
8485
)
86+
87+
# In separate transactions, try to update the formulas inside the workflow,
88+
# so we don't block the main creation if something goes wrong here.
89+
utils.update_workflow_formulas(wf, node_mapping, tool_helpers)
90+
8591
# Navigate to the last created workflow
8692
tool_helpers.navigate_to(
8793
WorkflowNavigationType(

enterprise/backend/src/baserow_enterprise/assistant/tools/automation/types/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
AiAgentNodeCreate,
33
CreateRowActionCreate,
44
DeleteRowActionCreate,
5+
HasFormulasToCreateMixin,
56
NodeBase,
67
RouterNodeCreate,
78
SendEmailActionCreate,
@@ -21,4 +22,5 @@
2122
"SendEmailActionCreate",
2223
"AiAgentNodeCreate",
2324
"TriggerNodeCreate",
25+
"HasFormulasToCreateMixin",
2426
]

0 commit comments

Comments
 (0)