Skip to content

Commit 8c9ed82

Browse files
committed
fix(topic_safety): handle InternalEvent objects in topic safety actions for Colang 2.0 (#1335)
Convert InternalEvent objects to dictionary format before passing to to_chat_messages() to prevent 'InternalEvent' object is not subscriptable TypeError when using topic safety with Colang 2.0 runtime.
1 parent 559605e commit 8c9ed82

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

nemoguardrails/library/topic_safety/actions.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,17 @@ async def topic_safety_check_input(
4646
model_name = model_name or context.get("model", None)
4747

4848
if events is not None:
49-
conversation_history = to_chat_messages(events)
49+
# convert InternalEvent objects to dictionary format for compatibility with to_chat_messages
50+
dict_events = []
51+
for event in events:
52+
if hasattr(event, "name") and hasattr(event, "arguments"):
53+
dict_event = {"type": event.name}
54+
dict_event.update(event.arguments)
55+
dict_events.append(dict_event)
56+
else:
57+
dict_events.append(event)
58+
59+
conversation_history = to_chat_messages(dict_events)
5060

5161
if model_name is None:
5262
error_msg = (
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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+
"""Test for InternalEvent handling in topic_safety_check_input action."""
17+
18+
from unittest.mock import AsyncMock, patch
19+
20+
import pytest
21+
22+
from nemoguardrails.colang.v2_x.runtime.flows import InternalEvent
23+
from nemoguardrails.library.topic_safety.actions import topic_safety_check_input
24+
25+
26+
@pytest.mark.asyncio
27+
async def test_topic_safety_check_input_with_internal_events():
28+
"""Test that topic_safety_check_input can handle InternalEvent objects without failing.
29+
30+
This test would fail before the fix with:
31+
TypeError: 'InternalEvent' object is not subscriptable
32+
"""
33+
internal_events = [
34+
InternalEvent(
35+
name="UtteranceUserActionFinished",
36+
arguments={"final_transcript": "Hello, how are you?"},
37+
),
38+
InternalEvent(
39+
name="StartUtteranceBotAction",
40+
arguments={"script": "I'm doing well, thank you!"},
41+
),
42+
]
43+
44+
class MockTaskManager:
45+
def render_task_prompt(self, task):
46+
return "Check if the conversation is on topic."
47+
48+
def get_stop_tokens(self, task):
49+
return []
50+
51+
def get_max_tokens(self, task):
52+
return 10
53+
54+
llms = {"topic_control": "mock_llm"}
55+
llm_task_manager = MockTaskManager()
56+
57+
with patch(
58+
"nemoguardrails.library.topic_safety.actions.llm_call", new_callable=AsyncMock
59+
) as mock_llm_call:
60+
mock_llm_call.return_value = "on-topic"
61+
62+
# should not raise TypeError: 'InternalEvent' object is not subscriptable
63+
result = await topic_safety_check_input(
64+
llms=llms,
65+
llm_task_manager=llm_task_manager,
66+
model_name="topic_control",
67+
context={"user_message": "Hello"},
68+
events=internal_events,
69+
)
70+
71+
assert isinstance(result, dict)
72+
assert "on_topic" in result
73+
assert isinstance(result["on_topic"], bool)
74+
assert result["on_topic"] is True

0 commit comments

Comments
 (0)