Skip to content

Commit 48d8baf

Browse files
committed
Fix: Handle unescaped quotes in generate_value using safe_eval
1 parent b02904c commit 48d8baf

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

nemoguardrails/actions/llm/generation.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,10 @@ async def generate_value(
10391039

10401040
log.info(f"Generated value for ${var_name}: {value}")
10411041

1042-
return literal_eval(value)
1042+
try:
1043+
return safe_eval(value)
1044+
except Exception:
1045+
raise Exception(f"Invalid LLM response: `{value}`")
10431046

10441047
@action(is_system_action=True)
10451048
async def generate_intent_steps_message(
@@ -1342,3 +1345,26 @@ def clean_utterance_content(utterance: str) -> str:
13421345
# It should be translated to an actual \n character.
13431346
utterance = utterance.replace("\\n", "\n")
13441347
return utterance
1348+
1349+
1350+
def safe_eval(input_value: str) -> str:
1351+
"""
1352+
Safely evaluate a string to handle unescaped quotes or invalid syntax from the async generate_value action.
1353+
1354+
Args:
1355+
input_value (str): The input string to evaluate.
1356+
1357+
Returns:
1358+
str: The evaluated and properly formatted string.
1359+
1360+
Raises:
1361+
ValueError: If the input cannot be safely evaluated.
1362+
"""
1363+
if input_value.startswith(("'", '"')) and input_value.endswith(("'", '"')):
1364+
try:
1365+
return literal_eval(input_value)
1366+
except (ValueError, SyntaxError):
1367+
pass
1368+
escaped_value = input_value.replace("'", "\\'").replace('"', '\\"')
1369+
input_value = f"'{escaped_value}'"
1370+
return literal_eval(input_value)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from nemoguardrails.actions.llm.generation import safe_eval
17+
18+
19+
def test_safe_eval_double_quotes_with_single_quote():
20+
"""Test safe_eval with a string wrapped in double quotes that contains a single quote."""
21+
input_value = '"It\'s a sunny day"'
22+
result = safe_eval(input_value)
23+
assert result == "It's a sunny day"
24+
25+
26+
def test_safe_eval_double_quotes_with_nested_single_quote():
27+
"""Test safe_eval with a string wrapped in double quotes that contains a nested single quote."""
28+
input_value = "\"He said, 'Hello'\""
29+
result = safe_eval(input_value)
30+
assert result == "He said, 'Hello'"
31+
32+
33+
def test_safe_eval_unquoted_string_with_single_quote():
34+
"""Test safe_eval with a string not wrapped in quotes, containing a single quote."""
35+
input_value = "It's a sunny day"
36+
result = safe_eval(input_value)
37+
assert result == "It's a sunny day"
38+
39+
40+
def test_safe_eval_unquoted_plain_string():
41+
"""Test safe_eval with a plain string not wrapped in quotes."""
42+
input_value = "It is a sunny day"
43+
result = safe_eval(input_value)
44+
assert result == "It is a sunny day"
45+
46+
47+
def test_safe_eval_empty_string():
48+
"""Test safe_eval with an empty string as input."""
49+
input_value = ""
50+
result = safe_eval(input_value)
51+
assert result == ""

0 commit comments

Comments
 (0)