Skip to content
Merged
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
13 changes: 11 additions & 2 deletions nemoguardrails/actions/llm/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@
from nemoguardrails.rails.llm.config import EmbeddingSearchProvider, RailsConfig
from nemoguardrails.rails.llm.options import GenerationOptions
from nemoguardrails.streaming import StreamingHandler
from nemoguardrails.utils import get_or_create_event_loop, new_event_dict, new_uuid
from nemoguardrails.utils import (
get_or_create_event_loop,
new_event_dict,
new_uuid,
safe_eval,
)

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -1039,7 +1044,11 @@ async def generate_value(

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

return literal_eval(value)
try:
return safe_eval(value)
except Exception as e:
log.error(f"Error evaluating value: {value}. Error: {str(e)}")
raise ValueError(f"Invalid LLM response: `{value}`")

@action(is_system_action=True)
async def generate_intent_steps_message(
Expand Down
24 changes: 24 additions & 0 deletions nemoguardrails/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import random
import re
import uuid
from ast import literal_eval
from collections import namedtuple
from datetime import datetime, timezone
from enum import Enum
Expand Down Expand Up @@ -384,3 +385,26 @@ def is_ignored_by_railsignore(filename: str, ignore_patterns: str) -> bool:
break

return ignore


def safe_eval(input_value: str) -> str:
"""
Safely evaluate a string to handle unescaped quotes or invalid syntax from the async generate_value action.

Args:
input_value (str): The input string to evaluate.

Returns:
str: The evaluated and properly formatted string.

Raises:
ValueError: If the input cannot be safely evaluated.
"""
if input_value.startswith(("'", '"')) and input_value.endswith(("'", '"')):
try:
return literal_eval(input_value)
except (ValueError, SyntaxError):
pass
escaped_value = input_value.replace("'", "\\'").replace('"', '\\"')
input_value = f"'{escaped_value}'"
return literal_eval(input_value)
27 changes: 26 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import pytest

from nemoguardrails.utils import new_event_dict
from nemoguardrails.utils import new_event_dict, safe_eval


def test_event_generation():
Expand Down Expand Up @@ -119,3 +119,28 @@ def test_wrong_property_type():
event_type,
script=script,
)


@pytest.mark.parametrize(
"input_value, expected_result",
[
('"It\'s a sunny day"', "It's a sunny day"), # double quotes with single quote
(
"\"He said, 'Hello'\"",
"He said, 'Hello'",
), # double quotes with nested single quote
(
"It's a sunny day",
"It's a sunny day",
), # unquoted string containing single quote
(
"It is a sunny day",
"It is a sunny day",
), # plain string not wrapped in quotes
("", ""), # empty string
],
)
def test_safe_eval(input_value, expected_result):
"""Test safe_eval with various input values."""
result = safe_eval(input_value)
assert result == expected_result
Loading