core.condition_evaluator

Safe condition expression evaluation for conditional prompt execution.

Uses AST parsing to safely evaluate conditions without eval()/exec(), supporting comparisons, boolean logic, function calls, and method access.

Safe condition expression evaluation for conditional prompt execution.

Uses AST parsing to safely evaluate conditions without eval()/exec(), supporting comparisons, boolean logic, function calls, and method access.

class ConditionEvaluator(results_by_name)[source]

Bases: object

Safely evaluates condition expressions for conditional prompt execution.

Security model: Never uses eval() or exec() on user input. Conditions are parsed using AST and evaluated using a restricted set of operators, functions, and method calls.

Syntax:

{{prompt_name.property}} == “value” {{prompt_name.property}} != “value” {{prompt_name.property}} contains “substring” {{prompt_name.property}} not contains “substring” {{prompt_name.property}} matches “regex” len({{prompt_name.response}}) > 100 {{a.status}} == “success” and {{b.status}} == “success” {{a.status}} == “success” or {{b.status}} == “success” not {{prompt_name.has_response}}

String methods:

{{prompt_name.response}}.startswith(“prefix”) {{prompt_name.response}}.endswith(“suffix”) {{prompt_name.response}}.lower() == “value” {{prompt_name.response}}.split(“,”)[0]

JSON functions:

json_get({{prompt_name.response}}, “key.nested[0]”) json_get_default({{prompt_name.response}}, “key”, “default”) json_has({{prompt_name.response}}, “key”) json_keys({{prompt_name.response}}) “key” in json_keys({{prompt_name.response}})

Available properties:
  • status: “success”, “failed”, “skipped”

  • response: The AI response text

  • attempts: Number of retry attempts (int)

  • error: Error message if failed (str)

  • has_response: True if response exists and non-empty (bool)

Note

For JSON responses, use json_has(), json_get(), or "key" in json_keys(...) to query structure. The contains and in operators always perform substring matching on the stringified response text, which can produce false matches against dict/list repr strings (e.g. "True" in {{s.response}} matches "{'pass': True}" because the four characters appear in the repr, not because of a value).

Parameters:

results_by_name (dict[str, dict[str, Any]])

ALLOWED_OPERATORS = {<class 'ast.And'>: <function ConditionEvaluator.<lambda>>, <class 'ast.Eq'>: <built-in function eq>, <class 'ast.Gt'>: <built-in function gt>, <class 'ast.GtE'>: <built-in function ge>, <class 'ast.Lt'>: <built-in function lt>, <class 'ast.LtE'>: <built-in function le>, <class 'ast.NotEq'>: <built-in function ne>, <class 'ast.Or'>: <function ConditionEvaluator.<lambda>>}
ALLOWED_UNARY_OPERATORS = {<class 'ast.Not'>: <built-in function not_>}
ALLOWED_FUNCTIONS = {'abs': <built-in function abs>, 'bool': <function ConditionEvaluator.<lambda>>, 'count': <function ConditionEvaluator.<lambda>>, 'find': <function ConditionEvaluator.<lambda>>, 'float': <function ConditionEvaluator.<lambda>>, 'int': <function ConditionEvaluator.<lambda>>, 'is_empty': <function ConditionEvaluator.<lambda>>, 'is_null': <function ConditionEvaluator.<lambda>>, 'json_get': <function ConditionEvaluator.<lambda>>, 'json_get_default': <function ConditionEvaluator.<lambda>>, 'json_has': <function ConditionEvaluator.<lambda>>, 'json_keys': <function ConditionEvaluator.<lambda>>, 'json_parse': <function ConditionEvaluator.<lambda>>, 'json_type': <function ConditionEvaluator.<lambda>>, 'json_values': <function ConditionEvaluator.<lambda>>, 'len': <built-in function len>, 'lower': <function ConditionEvaluator.<lambda>>, 'lstrip': <function ConditionEvaluator.<lambda>>, 'max': <built-in function max>, 'min': <built-in function min>, 'replace': <function ConditionEvaluator.<lambda>>, 'rfind': <function ConditionEvaluator.<lambda>>, 'round': <built-in function round>, 'rsplit': <function ConditionEvaluator.<lambda>>, 'rstrip': <function ConditionEvaluator.<lambda>>, 'slice': <function ConditionEvaluator.<lambda>>, 'split': <function ConditionEvaluator.<lambda>>, 'str': <function ConditionEvaluator.<lambda>>, 'strip': <function ConditionEvaluator.<lambda>>, 'trim': <function ConditionEvaluator.<lambda>>, 'upper': <function ConditionEvaluator.<lambda>>}
ALLOWED_STRING_METHODS = frozenset({'capitalize', 'center', 'count', 'endswith', 'find', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 'split', 'startswith', 'strip', 'title', 'upper', 'zfill'})
ALLOWED_LIST_METHODS = frozenset({'count', 'index'})
ALLOWED_DICT_METHODS = frozenset({'get', 'keys', 'values'})
VARIABLE_PATTERN = re.compile('\\{\\{(\\w+)\\.(\\w+)\\}\\}')
__init__(results_by_name)[source]

Initialize evaluator with completed prompt results.

Parameters:

results_by_name (dict[str, dict[str, Any]]) – Dict mapping prompt_name to result dict with keys: - status: str - response: str - attempts: int - error: str - has_response: bool

Return type:

None

evaluate(condition)[source]

Evaluate a condition expression.

Parameters:

condition (str) – The condition string to evaluate

Returns:

Tuple of (result, error_message) - result: True if condition passes, False otherwise - error_message: None if successful, error string if failed

Return type:

tuple[bool, str | None]

evaluate_with_trace(condition)[source]

Evaluate a condition expression and return the resolved trace.

Like evaluate(), but also returns the resolved expression showing what each {{name.property}} was substituted with.

Parameters:

condition (str) – The condition string to evaluate.

Returns:

Tuple of (result, error_message, resolved_trace). - result: True if condition passes, False otherwise. - error_message: None if successful, error string if failed. - resolved_trace: The condition after variable substitution,

e.g. ‘“failed” == “success”’, or None if no condition.

Return type:

tuple[bool, str | None, str | None]

classmethod extract_referenced_names(condition)[source]

Extract all prompt names referenced in a condition.

Parameters:

condition (str) – The condition string

Returns:

List of prompt names referenced via {{name.property}} syntax

Return type:

list

classmethod validate_syntax(condition)[source]

Validate condition syntax without evaluating.

Parameters:

condition (str) – The condition string to validate

Returns:

Tuple of (is_valid, error_message)

Return type:

tuple[bool, str | None]

Classes

class ConditionEvaluator(results_by_name)[source]

Bases: object

Safely evaluates condition expressions for conditional prompt execution.

Security model: Never uses eval() or exec() on user input. Conditions are parsed using AST and evaluated using a restricted set of operators, functions, and method calls.

Syntax:

{{prompt_name.property}} == “value” {{prompt_name.property}} != “value” {{prompt_name.property}} contains “substring” {{prompt_name.property}} not contains “substring” {{prompt_name.property}} matches “regex” len({{prompt_name.response}}) > 100 {{a.status}} == “success” and {{b.status}} == “success” {{a.status}} == “success” or {{b.status}} == “success” not {{prompt_name.has_response}}

String methods:

{{prompt_name.response}}.startswith(“prefix”) {{prompt_name.response}}.endswith(“suffix”) {{prompt_name.response}}.lower() == “value” {{prompt_name.response}}.split(“,”)[0]

JSON functions:

json_get({{prompt_name.response}}, “key.nested[0]”) json_get_default({{prompt_name.response}}, “key”, “default”) json_has({{prompt_name.response}}, “key”) json_keys({{prompt_name.response}}) “key” in json_keys({{prompt_name.response}})

Available properties:
  • status: “success”, “failed”, “skipped”

  • response: The AI response text

  • attempts: Number of retry attempts (int)

  • error: Error message if failed (str)

  • has_response: True if response exists and non-empty (bool)

Note

For JSON responses, use json_has(), json_get(), or "key" in json_keys(...) to query structure. The contains and in operators always perform substring matching on the stringified response text, which can produce false matches against dict/list repr strings (e.g. "True" in {{s.response}} matches "{'pass': True}" because the four characters appear in the repr, not because of a value).

Parameters:

results_by_name (dict[str, dict[str, Any]])

ALLOWED_OPERATORS = {<class 'ast.And'>: <function ConditionEvaluator.<lambda>>, <class 'ast.Eq'>: <built-in function eq>, <class 'ast.Gt'>: <built-in function gt>, <class 'ast.GtE'>: <built-in function ge>, <class 'ast.Lt'>: <built-in function lt>, <class 'ast.LtE'>: <built-in function le>, <class 'ast.NotEq'>: <built-in function ne>, <class 'ast.Or'>: <function ConditionEvaluator.<lambda>>}
ALLOWED_UNARY_OPERATORS = {<class 'ast.Not'>: <built-in function not_>}
ALLOWED_FUNCTIONS = {'abs': <built-in function abs>, 'bool': <function ConditionEvaluator.<lambda>>, 'count': <function ConditionEvaluator.<lambda>>, 'find': <function ConditionEvaluator.<lambda>>, 'float': <function ConditionEvaluator.<lambda>>, 'int': <function ConditionEvaluator.<lambda>>, 'is_empty': <function ConditionEvaluator.<lambda>>, 'is_null': <function ConditionEvaluator.<lambda>>, 'json_get': <function ConditionEvaluator.<lambda>>, 'json_get_default': <function ConditionEvaluator.<lambda>>, 'json_has': <function ConditionEvaluator.<lambda>>, 'json_keys': <function ConditionEvaluator.<lambda>>, 'json_parse': <function ConditionEvaluator.<lambda>>, 'json_type': <function ConditionEvaluator.<lambda>>, 'json_values': <function ConditionEvaluator.<lambda>>, 'len': <built-in function len>, 'lower': <function ConditionEvaluator.<lambda>>, 'lstrip': <function ConditionEvaluator.<lambda>>, 'max': <built-in function max>, 'min': <built-in function min>, 'replace': <function ConditionEvaluator.<lambda>>, 'rfind': <function ConditionEvaluator.<lambda>>, 'round': <built-in function round>, 'rsplit': <function ConditionEvaluator.<lambda>>, 'rstrip': <function ConditionEvaluator.<lambda>>, 'slice': <function ConditionEvaluator.<lambda>>, 'split': <function ConditionEvaluator.<lambda>>, 'str': <function ConditionEvaluator.<lambda>>, 'strip': <function ConditionEvaluator.<lambda>>, 'trim': <function ConditionEvaluator.<lambda>>, 'upper': <function ConditionEvaluator.<lambda>>}
ALLOWED_STRING_METHODS = frozenset({'capitalize', 'center', 'count', 'endswith', 'find', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 'split', 'startswith', 'strip', 'title', 'upper', 'zfill'})
ALLOWED_LIST_METHODS = frozenset({'count', 'index'})
ALLOWED_DICT_METHODS = frozenset({'get', 'keys', 'values'})
VARIABLE_PATTERN = re.compile('\\{\\{(\\w+)\\.(\\w+)\\}\\}')
__init__(results_by_name)[source]

Initialize evaluator with completed prompt results.

Parameters:

results_by_name (dict[str, dict[str, Any]]) – Dict mapping prompt_name to result dict with keys: - status: str - response: str - attempts: int - error: str - has_response: bool

Return type:

None

evaluate(condition)[source]

Evaluate a condition expression.

Parameters:

condition (str) – The condition string to evaluate

Returns:

Tuple of (result, error_message) - result: True if condition passes, False otherwise - error_message: None if successful, error string if failed

Return type:

tuple[bool, str | None]

evaluate_with_trace(condition)[source]

Evaluate a condition expression and return the resolved trace.

Like evaluate(), but also returns the resolved expression showing what each {{name.property}} was substituted with.

Parameters:

condition (str) – The condition string to evaluate.

Returns:

Tuple of (result, error_message, resolved_trace). - result: True if condition passes, False otherwise. - error_message: None if successful, error string if failed. - resolved_trace: The condition after variable substitution,

e.g. ‘“failed” == “success”’, or None if no condition.

Return type:

tuple[bool, str | None, str | None]

classmethod extract_referenced_names(condition)[source]

Extract all prompt names referenced in a condition.

Parameters:

condition (str) – The condition string

Returns:

List of prompt names referenced via {{name.property}} syntax

Return type:

list

classmethod validate_syntax(condition)[source]

Validate condition syntax without evaluating.

Parameters:

condition (str) – The condition string to validate

Returns:

Tuple of (is_valid, error_message)

Return type:

tuple[bool, str | None]