Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/google/adk/utils/instructions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@ async def _async_sub(pattern, repl_async_fn, string) -> str:
return ''.join(result)

async def _replace_match(match) -> str:
var_name = match.group().lstrip('{').rstrip('}').strip()
raw = match.group()
# Double (or more) braces are escape sequences: {{x}} → {x}
if raw.startswith('{{') and raw.endswith('}}'):
return raw[1:-1]
# Use slicing instead of lstrip/rstrip to avoid stripping extra braces
# from malformed inputs like '{var}}}'.
var_name = raw[1:-1].strip()
optional = False
if var_name.endswith('?'):
optional = True
Expand Down
42 changes: 42 additions & 0 deletions tests/unittests/utils/test_instructions_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,45 @@ async def test_inject_session_state_with_optional_missing_state_returns_empty():
instruction_template, invocation_context
)
assert populated_instruction == "Optional value: "


@pytest.mark.asyncio
async def test_double_braces_escape_to_literal():
"""Double braces {{x}} should produce literal {x}, not a state lookup."""
instruction_template = 'Generate a keyword like "roofing cost in {{city}}".'
invocation_context = await _create_test_readonly_context()

populated_instruction = await instructions_utils.inject_session_state(
instruction_template, invocation_context
)
assert populated_instruction == (
'Generate a keyword like "roofing cost in {city}".'
)


@pytest.mark.asyncio
async def test_double_braces_mixed_with_state_variable():
"""Double braces should escape while single braces still resolve state."""
instruction_template = "Hello {user_name}, use {{placeholder}} in prompts."
invocation_context = await _create_test_readonly_context(
state={"user_name": "Alice"}
)

populated_instruction = await instructions_utils.inject_session_state(
instruction_template, invocation_context
)
assert populated_instruction == (
"Hello Alice, use {placeholder} in prompts."
)


@pytest.mark.asyncio
async def test_triple_braces_peel_one_layer():
"""Triple braces {{{x}}} should peel one layer to {{x}}."""
instruction_template = "Escaped: {{{example}}}"
invocation_context = await _create_test_readonly_context()

populated_instruction = await instructions_utils.inject_session_state(
instruction_template, invocation_context
)
assert populated_instruction == "Escaped: {{example}}"