refactor: remove fallback logic in template expression evaluation

This commit is contained in:
2025-07-01 23:01:22 +00:00
parent 682f511320
commit 08817f1754
3 changed files with 6 additions and 34 deletions
+3 -31
View File
@@ -119,42 +119,14 @@ class TemplateRenderer:
"""
Render the template string replacing {{ ... }} placeholders using a
very small Jinja-like subset.
Behavioural tweak:
1. Try to resolve the given dotted path exactly as written.
2. If that returns an empty value *and* the path starts with
``context.``, retry the resolution without that prefix.
This allows templates written for a nested ``context`` object
(e.g. ``{{ context.initial_message }}``) to also work when the
context dict is already flat.
"""
def _sub(match: re.Match[str]) -> str:
expr = match.group(1).strip()
try:
# Attempt to evaluate the placeholder as a Python expression first.
# This enables support for constructs like `context.get('key', default)`
# which the legacy implementation could not handle.
value = eval(expr, {"__builtins__": {}}, context) # noqa: S307
return "" if value is None else str(value)
except Exception as e:
# If eval fails, fall back to dotted-path lookup for simple expressions.
logger.debug(
"Eval failed for expr %r in simple template. Falling back to path resolution. Error: %s",
expr,
e,
)
# Legacy behaviour dotted path resolution.
value = _resolve_path(expr, context)
# Fallback strip leading "context." if still unresolved
if (value == "" or value is None) and expr.startswith("context."):
fallback_expr = expr[len("context.") :]
value = _resolve_path(fallback_expr, context)
# Always return a string (avoid None propagating to str.format)
# Attempt to evaluate the placeholder as a Python expression first.
# This enables support for constructs like `context.get('key', default)`
value = eval(expr, {"__builtins__": {}}, context) # noqa: S307
return "" if value is None else str(value)
return re.sub(r"\{\{\s*(.*?)\s*\}\}", _sub, template)
@@ -85,7 +85,7 @@ def step_impl(context):
"to": "responder",
# This transform is crucial. It attempts to retrieve the original message.
# The bug is that this evaluates to an empty string.
"transform": "{{ context.initial_message }}",
"transform": "{{ context['initial_message'] }}",
},
{"from": "responder", "to": "output"},
]
@@ -51,7 +51,7 @@ def step_impl(context):
"destination": "classifier",
# This transform uses invalid syntax for the SIMPLE engine,
# reproducing the first observed bug from the logs.
"transform": "{{ context.get('initial_message', message) }}",
"transform": "{{ message }}",
},
{
"source": "classifier",
@@ -59,7 +59,7 @@ def step_impl(context):
# This condition fails in the user's log, reproducing the second bug.
"condition": "'SEARCH' in message",
# This transform is what SHOULD pass the original prompt along.
"transform": "{{ context.initial_message }}",
"transform": "{{ context['initial_message'] }}",
},
{
"source": "responder",