Skip to content

Commit 31dfe9c

Browse files
committed
Respect the provided log options regardless of tracing configuration
1 parent bee719b commit 31dfe9c

2 files changed

Lines changed: 111 additions & 16 deletions

File tree

nemoguardrails/rails/llm/llmrails.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ async def generate_async(
911911
await streaming_handler.push_chunk(END_OF_STREAM)
912912

913913
# IF tracing is enabled we need to set GenerationLog attrs
914+
original_log_options = None
914915
if self.config.tracing.enabled:
915916
if options is None:
916917
options = GenerationOptions()
@@ -921,6 +922,7 @@ async def generate_async(
921922
else:
922923
# If options is a dict, convert it to GenerationOptions
923924
options = GenerationOptions(**options)
925+
original_log_options = options.log.model_copy(deep=True)
924926

925927
# enable log options
926928
# it is aggressive, but these are required for tracing
@@ -1038,6 +1040,26 @@ async def generate_async(
10381040
)
10391041
await tracer.export_async()
10401042

1043+
# respect original log specification, if tracing added information to the output
1044+
if original_log_options:
1045+
if not any((
1046+
original_log_options.internal_events,
1047+
original_log_options.activated_rails,
1048+
original_log_options.llm_calls,
1049+
original_log_options.colang_history
1050+
)):
1051+
del res.log
1052+
else:
1053+
if not original_log_options.internal_events:
1054+
res.log.internal_events = []
1055+
if not original_log_options.activated_rails:
1056+
res.log.activated_rails = []
1057+
if not original_log_options.llm_calls:
1058+
res.log.llm_calls = []
1059+
1060+
1061+
1062+
10411063
return res
10421064
else:
10431065
# If a prompt is used, we only return the content of the message.

tests/test_tracing.py

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,7 @@ async def test_tracing_does_not_mutate_user_options():
312312
), "User's original options were modified! This causes instability."
313313

314314
# verify that tracing still works
315-
assert response.log is not None, "Tracing should still work correctly"
316-
assert (
317-
response.log.activated_rails is not None
318-
), "Should have activated rails data"
315+
assert "log" not in response, "Tracing should still work correctly, without affecting returned log"
319316

320317

321318
@pytest.mark.asyncio
@@ -358,17 +355,16 @@ async def test_tracing_with_none_options():
358355
messages=[{"role": "user", "content": "hello"}], options=None
359356
)
360357

361-
assert response.log is not None
362-
assert response.log.activated_rails is not None
363-
assert response.log.stats is not None
358+
assert "log" not in response
364359

365360

366361
@pytest.mark.asyncio
367362
async def test_tracing_aggressive_override_when_all_disabled():
368363
"""Test that tracing aggressively enables all logging when user disables all options.
369364
370365
When user disables all three tracing related options, tracing still enables
371-
ALL of them to ensure comprehensive logging data.
366+
ALL of them to ensure comprehensive logging data. However, this should not contaminate the
367+
returned response object
372368
"""
373369

374370
config = RailsConfig.from_content(
@@ -425,11 +421,10 @@ async def test_tracing_aggressive_override_when_all_disabled():
425421

426422
assert response.log is not None
427423
assert (
428-
response.log.activated_rails is not None
429-
and len(response.log.activated_rails) > 0
424+
response.log.activated_rails == []
430425
)
431-
assert response.log.llm_calls is not None
432-
assert response.log.internal_events is not None
426+
assert response.log.llm_calls == []
427+
assert response.log.internal_events == []
433428

434429
assert user_options.log.activated_rails == original_activated_rails
435430
assert user_options.log.llm_calls == original_llm_calls
@@ -439,6 +434,84 @@ async def test_tracing_aggressive_override_when_all_disabled():
439434
assert user_options.log.internal_events == False
440435

441436

437+
@pytest.mark.asyncio
438+
async def test_tracing_preserves_specific_log_fields():
439+
"""Test that tracing does not modify the returned log fields
440+
"""
441+
442+
config = RailsConfig.from_content(
443+
colang_content="""
444+
define user express greeting
445+
"hello"
446+
447+
define flow
448+
user express greeting
449+
bot express greeting
450+
451+
define bot express greeting
452+
"Hello! How can I assist you today?"
453+
""",
454+
config={
455+
"models": [],
456+
"tracing": {"enabled": True, "adapters": [{"name": "FileSystem"}]},
457+
},
458+
)
459+
460+
chat = TestChat(
461+
config,
462+
llm_completions=[
463+
"user express greeting",
464+
"bot express greeting",
465+
"Hello! How can I assist you today?",
466+
],
467+
)
468+
469+
# user explicitly disables ALL tracing related options
470+
user_options = GenerationOptions(
471+
log=GenerationLogOptions(
472+
activated_rails=False,
473+
llm_calls=True,
474+
internal_events=True,
475+
colang_history=True,
476+
)
477+
)
478+
479+
original_activated_rails = user_options.log.activated_rails
480+
original_llm_calls = user_options.log.llm_calls
481+
original_internal_events = user_options.log.internal_events
482+
original_colang_history = user_options.log.colang_history
483+
484+
with patch.object(Tracer, "export_async", return_value=None):
485+
response = await chat.app.generate_async(
486+
messages=[{"role": "user", "content": "hello"}], options=user_options
487+
)
488+
489+
assert user_options.log.activated_rails == original_activated_rails
490+
assert user_options.log.llm_calls == original_llm_calls
491+
assert user_options.log.internal_events == original_internal_events
492+
assert user_options.log.colang_history == original_colang_history
493+
494+
assert response.log is not None
495+
assert (
496+
response.log.activated_rails == []
497+
)
498+
assert (
499+
response.log.llm_calls is not None
500+
and len(response.log.llm_calls) > 0
501+
)
502+
assert (
503+
response.log.internal_events is not None
504+
and len(response.log.internal_events) > 0
505+
)
506+
507+
assert user_options.log.activated_rails == original_activated_rails
508+
assert user_options.log.llm_calls == original_llm_calls
509+
assert user_options.log.internal_events == original_internal_events
510+
assert user_options.log.activated_rails == False
511+
assert user_options.log.llm_calls == True
512+
assert user_options.log.internal_events == True
513+
514+
442515
@pytest.mark.asyncio
443516
async def test_tracing_aggressive_override_with_dict_options():
444517
"""Test that tracing works correctly when options are passed as a dict.
@@ -502,11 +575,11 @@ async def test_tracing_aggressive_override_with_dict_options():
502575

503576
assert response.log is not None
504577
assert (
505-
response.log.activated_rails is not None
506-
and len(response.log.activated_rails) > 0
578+
response.log.activated_rails == []
579+
and len(response.log.activated_rails) == 0
507580
)
508-
assert response.log.llm_calls is not None
509-
assert response.log.internal_events is not None
581+
assert response.log.llm_calls == []
582+
assert response.log.internal_events == []
510583

511584

512585
if __name__ == "__main__":

0 commit comments

Comments
 (0)