mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-13 08:15:21 +02:00
feat: campaign create error on missing template variables
This commit is contained in:
parent
d8942dffb1
commit
e513e563ee
6 changed files with 165 additions and 7 deletions
|
|
@ -1,10 +1,40 @@
|
|||
import re
|
||||
from collections import Counter
|
||||
from typing import Dict, List
|
||||
from typing import Dict, List, Set
|
||||
|
||||
from api.services.workflow.dto import EdgeDataDTO, NodeDataDTO, NodeType, ReactFlowDTO
|
||||
from api.services.workflow.errors import ItemKind, WorkflowError
|
||||
|
||||
# Regex for matching {{ variable }} template placeholders.
|
||||
# Captures: group(1) = variable path, group(2) = filter name, group(3) = filter value.
|
||||
# Shared with api.utils.template_renderer via import.
|
||||
TEMPLATE_VAR_PATTERN = r"\{\{\s*([^|\s}]+)(?:\s*\|\s*([^:}]+)(?::([^}]+))?)?\s*\}\}"
|
||||
|
||||
# Variables injected by the system at runtime, not from source data.
|
||||
_SYSTEM_VARIABLES = {"campaign_id", "provider", "source_uuid"}
|
||||
|
||||
|
||||
def extract_template_variables(text: str) -> Set[str]:
|
||||
"""Extract template variable names from a string, excluding nested paths,
|
||||
variables with a fallback filter, and system-injected variables."""
|
||||
variables: Set[str] = set()
|
||||
for match in re.finditer(TEMPLATE_VAR_PATTERN, text):
|
||||
var_name = match.group(1).strip()
|
||||
filter_name = match.group(2).strip() if match.group(2) else None
|
||||
|
||||
# Skip nested paths (runtime-resolved, e.g. gathered_context.city)
|
||||
if "." in var_name:
|
||||
continue
|
||||
# Skip variables with a fallback (they have a default value)
|
||||
if filter_name == "fallback":
|
||||
continue
|
||||
# Skip system-injected variables
|
||||
if var_name in _SYSTEM_VARIABLES:
|
||||
continue
|
||||
|
||||
variables.add(var_name)
|
||||
return variables
|
||||
|
||||
|
||||
class Edge:
|
||||
def __init__(self, source: str, target: str, data: EdgeDataDTO):
|
||||
|
|
@ -99,6 +129,44 @@ class WorkflowGraph:
|
|||
except IndexError:
|
||||
self.global_node_id = None
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# template variable extraction
|
||||
# -----------------------------------------------------------
|
||||
def get_required_template_variables(self) -> Set[str]:
|
||||
"""Extract all template variables referenced in node prompts/greetings
|
||||
and edge transition speeches.
|
||||
|
||||
Scans:
|
||||
- Start node: prompt, greeting
|
||||
- Agent / End / Global nodes: prompt
|
||||
- All edges: transition_speech
|
||||
|
||||
Returns a set of top-level variable names that the workflow expects
|
||||
from the source data (excluding nested paths, fallback vars, and
|
||||
system-injected vars).
|
||||
"""
|
||||
variables: Set[str] = set()
|
||||
|
||||
for node in self.nodes.values():
|
||||
if node.node_type in (
|
||||
NodeType.startNode,
|
||||
NodeType.agentNode,
|
||||
NodeType.endNode,
|
||||
NodeType.globalNode,
|
||||
):
|
||||
if node.prompt:
|
||||
variables |= extract_template_variables(node.prompt)
|
||||
|
||||
# greeting is only relevant on the start node
|
||||
if node.node_type == NodeType.startNode and node.greeting:
|
||||
variables |= extract_template_variables(node.greeting)
|
||||
|
||||
for edge in self.edges:
|
||||
if edge.transition_speech:
|
||||
variables |= extract_template_variables(edge.transition_speech)
|
||||
|
||||
return variables
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# validators
|
||||
# -----------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue