Compare commits

...

2 Commits

Author SHA1 Message Date
Reinier van der Leer
a65189f6e6 Merge branch 'dev' into pwuts/open-2547-support-input-blocks-alongside-trigger-blocks 2025-12-15 15:38:10 +01:00
Reinier van der Leer
09369d2c3c WIP: backend MVP, frontend models and UI need updating 2025-10-21 07:22:27 +02:00
5 changed files with 121 additions and 9 deletions

View File

@@ -500,14 +500,33 @@ async def _execute_webhook_preset_trigger(
return
logger.debug(f"Executing preset #{preset.id} for webhook #{webhook.id}")
# Separate trigger inputs from regular graph inputs
trigger_node_id = trigger_node.id.split("-")[0]
trigger_params_key = f"_trigger_params_{trigger_node_id}"
# Extract trigger parameters and regular inputs
graph_inputs = preset.inputs.copy()
trigger_inputs = graph_inputs.pop(trigger_params_key, None)
if trigger_inputs is None:
# We can't run this, so log a warning and skip
logger.warning(
f"Preset #{preset.id} is missing trigger parameters for node "
f"#{trigger_node.id}"
)
return
# Add webhook payload to trigger inputs
trigger_inputs["payload"] = payload
try:
await add_graph_execution(
user_id=webhook.user_id,
graph_id=preset.graph_id,
preset_id=preset.id,
inputs=graph_inputs,
graph_version=preset.graph_version,
graph_credentials_inputs=preset.credentials,
nodes_input_masks={trigger_node.id: {**preset.inputs, "payload": payload}},
nodes_input_masks={trigger_node.id: trigger_inputs},
)
except GraphNotInLibraryError as e:
logger.warning(

View File

@@ -299,6 +299,9 @@ class TriggeredPresetSetupRequest(pydantic.BaseModel):
graph_version: int
trigger_config: dict[str, Any]
constant_inputs: dict[str, Any] = pydantic.Field(
default_factory=dict, description="Regular graph input values"
)
agent_credentials: dict[str, CredentialsMetaInput] = pydantic.Field(
default_factory=dict
)

View File

@@ -188,6 +188,13 @@ async def setup_trigger(
detail=f"Could not set up webhook: {feedback}",
)
# Combine trigger config with graph inputs using the new convention
trigger_node_id = trigger_node.id.split("-")[0]
trigger_params_key = f"_trigger_params_{trigger_node_id}"
preset_inputs = params.constant_inputs.copy()
preset_inputs[trigger_params_key] = trigger_config_with_credentials
new_preset = await db.create_preset(
user_id=user_id,
preset=models.LibraryAgentPresetCreatable(
@@ -195,7 +202,7 @@ async def setup_trigger(
graph_version=params.graph_version,
name=params.name,
description=params.description,
inputs=trigger_config_with_credentials,
inputs=preset_inputs,
credentials=params.agent_credentials,
webhook_id=new_webhook.id,
is_active=True,
@@ -247,8 +254,19 @@ async def update_preset(
if (trigger_node := graph.webhook_input_node) and (
preset.inputs is not None and preset.credentials is not None
):
# Extract trigger config from the special key if it exists
trigger_node_id = trigger_node.id.split("-")[0]
trigger_params_key = f"_trigger_params_{trigger_node_id}"
trigger_config = preset.inputs.get(trigger_params_key, None)
if trigger_config is None:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Missing trigger configuration for node {trigger_node.id}",
)
trigger_config_with_credentials = {
**preset.inputs,
**trigger_config,
**(
make_node_credentials_input_map(graph, preset.credentials).get(
trigger_node.id

View File

@@ -0,0 +1,78 @@
/*
Migration to support input blocks alongside webhook trigger blocks.
This migration converts existing triggered presets to use the new format where:
- Regular graph input values are stored as-is in AgentNodeExecutionInputOutput
- Trigger-specific parameters are stored under a special key: _trigger_params_{node_prefix}
This allows graphs to have both trigger blocks and input blocks simultaneously.
*/
BEGIN;
SET LOCAL statement_timeout = '10min';
-- Find all graphs with webhook trigger nodes (triggered graphs)
-- NOTE: Must check graph structure, not just preset.webhookId, because
-- presets can be auto-disabled (webhookId = NULL) while still being triggered presets
WITH triggered_graphs AS (
SELECT DISTINCT
an."agentGraphId" as graph_id,
an."agentGraphVersion" as graph_version,
an."id" as webhook_node_id,
SPLIT_PART(an."id", '-', 1) as node_prefix
FROM "AgentNode" an
WHERE an."agentBlockId" IN (
'd0180ce6-ccb9-48c7-8256-b39e93e62801', -- Airtable Webhook Trigger block
'9464a020-ed1d-49e1-990f-7f2ac924a2b7', -- Compass AI Trigger block
'd0204ed8-8b81-408d-8b8d-ed087a546228', -- Exa Webset Webhook block
'8fa8c167-2002-47ce-aba8-97572fc5d387', -- Generic Webhook Trigger block
'6c60ec01-8128-419e-988f-96a063ee2fea', -- GitHub Pull Request Trigger block
'8a74c2ad-0104-4640-962f-26c6b69e58cd' -- Slant3D Order Webhook block
)
),
-- Find all presets using triggered graphs (both active and auto-disabled)
triggered_presets AS (
SELECT
ap."id" as preset_id,
tg.graph_id,
tg.graph_version,
tg.webhook_node_id,
tg.node_prefix,
ap."webhookId" -- May be NULL if auto-disabled
FROM "AgentPreset" ap
JOIN triggered_graphs tg ON tg.graph_id = ap."agentGraphId"
AND tg.graph_version = ap."agentGraphVersion"
WHERE ap."isDeleted" = false
),
-- Get all current input data for triggered presets
current_inputs AS (
SELECT
tp.preset_id,
tp.node_prefix,
aneio."name" as input_name,
aneio."data" as input_data
FROM triggered_presets tp
JOIN "AgentNodeExecutionInputOutput" aneio ON aneio."id" = ANY(
SELECT unnest(ap."InputPresets")
FROM "AgentPreset" ap
WHERE ap."id" = tp.preset_id
)
)
-- Create new trigger parameter entries and link them to presets
INSERT INTO "AgentNodeExecutionInputOutput" ("id", "name", "data", "agentPresetId")
SELECT
gen_random_uuid()::text,
'_trigger_params_' || ci.node_prefix,
jsonb_object_agg(ci.input_name, ci.input_data),
ci.preset_id
FROM current_inputs ci
GROUP BY ci.preset_id, ci.node_prefix;
-- Note: This migration converts ALL existing inputs in triggered presets to trigger parameters
-- In the new system, regular graph inputs would be stored alongside these trigger parameters
-- but without the special _trigger_params_ prefix
COMMIT;

View File

@@ -151,12 +151,6 @@ export function BlocksControl({
(block.uiType == BlockUIType.WEBHOOK &&
graphHasWebhookNodes &&
"Agents can only have one webhook-triggered block") ||
(block.uiType == BlockUIType.WEBHOOK &&
graphHasInputNodes &&
"Webhook-triggered blocks can't be used together with input blocks") ||
(block.uiType == BlockUIType.INPUT &&
graphHasWebhookNodes &&
"Input blocks can't be used together with a webhook-triggered block") ||
null,
}));
}, [