jaf-py 2.4.5__tar.gz → 2.4.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jaf_py-2.4.5 → jaf_py-2.4.7}/PKG-INFO +2 -1
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/engine.py +169 -65
- jaf_py-2.4.7/jaf/core/guardrails.py +666 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/types.py +83 -1
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/providers/__init__.py +2 -1
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/providers/model.py +363 -8
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf_py.egg-info/PKG-INFO +2 -1
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf_py.egg-info/SOURCES.txt +2 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf_py.egg-info/requires.txt +1 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/pyproject.toml +3 -2
- jaf_py-2.4.7/tests/test_manual.py +329 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/LICENSE +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/README.md +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/agent.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/agent_card.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/client.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/examples/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/examples/client_example.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/examples/integration_example.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/examples/rag_demo/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/examples/server_demo/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/examples/server_example.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/cleanup.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/factory.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/providers/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/providers/composite.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/providers/in_memory.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/providers/postgres.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/providers/redis.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/serialization.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/tests/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/tests/run_comprehensive_tests.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/tests/test_cleanup.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/tests/test_serialization.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/tests/test_stress_concurrency.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/tests/test_task_lifecycle.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/memory/types.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/protocol.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/server.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/standalone_client.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/run_tests.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/test_agent.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/test_client.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/test_integration.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/test_protocol.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/tests/test_types.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/a2a/types.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/cli.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/agent_tool.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/analytics.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/composition.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/errors.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/parallel_agents.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/performance.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/proxy.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/proxy_helpers.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/state.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/streaming.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/tool_results.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/tools.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/tracing.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/core/workflows.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/exceptions.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/approval_storage.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/factory.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/providers/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/providers/in_memory.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/providers/postgres.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/providers/redis.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/types.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/memory/utils.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/plugins/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/plugins/base.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/policies/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/policies/handoff.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/policies/validation.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/providers/mcp.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/server/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/server/main.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/server/server.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/server/types.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/utils/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/utils/attachments.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/utils/document_processor.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/visualization/__init__.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/visualization/example.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/visualization/functional_core.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/visualization/graphviz.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/visualization/imperative_shell.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf/visualization/types.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf_py.egg-info/dependency_links.txt +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf_py.egg-info/entry_points.txt +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/jaf_py.egg-info/top_level.txt +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/setup.cfg +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/setup.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_a2a_deep.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_a2a_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_api_reference_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_attachments.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_callback_system_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_coffee_tool.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_conversation_id_fix.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_deployment_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_docs_code_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_engine.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_engine_manual.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_error_handling_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_getting_started_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_math_tool.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_mcp_comprehensive.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_mcp_docs.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_mcp_real_functionality.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_mcp_transports.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_memory_system_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_model_providers_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_property_based.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_proxy_simple.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_redis_fixes.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_redis_memory.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_server_api_examples.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_session_continuity.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_streamable_http_mcp_example.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_timeout_functionality.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_tool_integration.py +0 -0
- {jaf_py-2.4.5 → jaf_py-2.4.7}/tests/test_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jaf-py
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.7
|
|
4
4
|
Summary: A purely functional agent framework with immutable state and composable tools - Python implementation
|
|
5
5
|
Author: JAF Contributors
|
|
6
6
|
Maintainer: JAF Contributors
|
|
@@ -43,6 +43,7 @@ Requires-Dist: opentelemetry-api>=1.22.0
|
|
|
43
43
|
Requires-Dist: opentelemetry-sdk>=1.22.0
|
|
44
44
|
Requires-Dist: opentelemetry-exporter-otlp>=1.22.0
|
|
45
45
|
Requires-Dist: langfuse<3.0.0
|
|
46
|
+
Requires-Dist: litellm>=1.76.3
|
|
46
47
|
Provides-Extra: tracing
|
|
47
48
|
Requires-Dist: opentelemetry-api>=1.22.0; extra == "tracing"
|
|
48
49
|
Requires-Dist: opentelemetry-sdk>=1.22.0; extra == "tracing"
|
|
@@ -32,6 +32,8 @@ from .types import (
|
|
|
32
32
|
Interruption,
|
|
33
33
|
GuardrailEvent,
|
|
34
34
|
GuardrailEventData,
|
|
35
|
+
GuardrailViolationEvent,
|
|
36
|
+
GuardrailViolationEventData,
|
|
35
37
|
MemoryEvent,
|
|
36
38
|
MemoryEventData,
|
|
37
39
|
OutputParseEvent,
|
|
@@ -61,6 +63,15 @@ from .types import (
|
|
|
61
63
|
ToolCallFunction,
|
|
62
64
|
ToolCallStartEvent,
|
|
63
65
|
ToolCallStartEventData,
|
|
66
|
+
Guardrail,
|
|
67
|
+
ValidValidationResult,
|
|
68
|
+
InvalidValidationResult,
|
|
69
|
+
)
|
|
70
|
+
from .guardrails import (
|
|
71
|
+
build_effective_guardrails,
|
|
72
|
+
execute_input_guardrails_sequential,
|
|
73
|
+
execute_input_guardrails_parallel,
|
|
74
|
+
execute_output_guardrails,
|
|
64
75
|
)
|
|
65
76
|
|
|
66
77
|
|
|
@@ -399,36 +410,6 @@ async def _run_internal(
|
|
|
399
410
|
if resumed:
|
|
400
411
|
return resumed
|
|
401
412
|
|
|
402
|
-
# Check initial input guardrails on first turn
|
|
403
|
-
if state.turn_count == 0:
|
|
404
|
-
first_user_message = next((m for m in state.messages if m.role == ContentRole.USER or m.role == 'user'), None)
|
|
405
|
-
if first_user_message and config.initial_input_guardrails:
|
|
406
|
-
for guardrail in config.initial_input_guardrails:
|
|
407
|
-
if config.on_event:
|
|
408
|
-
config.on_event(GuardrailEvent(data=GuardrailEventData(
|
|
409
|
-
guardrail_name=getattr(guardrail, '__name__', 'unknown_guardrail'),
|
|
410
|
-
content=get_text_content(first_user_message.content)
|
|
411
|
-
)))
|
|
412
|
-
if asyncio.iscoroutinefunction(guardrail):
|
|
413
|
-
result = await guardrail(get_text_content(first_user_message.content))
|
|
414
|
-
else:
|
|
415
|
-
result = guardrail(get_text_content(first_user_message.content))
|
|
416
|
-
|
|
417
|
-
if not result.is_valid:
|
|
418
|
-
if config.on_event:
|
|
419
|
-
config.on_event(GuardrailEvent(data=GuardrailEventData(
|
|
420
|
-
guardrail_name=getattr(guardrail, '__name__', 'unknown_guardrail'),
|
|
421
|
-
content=get_text_content(first_user_message.content),
|
|
422
|
-
is_valid=False,
|
|
423
|
-
error_message=result.error_message
|
|
424
|
-
)))
|
|
425
|
-
return RunResult(
|
|
426
|
-
final_state=state,
|
|
427
|
-
outcome=ErrorOutcome(error=InputGuardrailTripwire(
|
|
428
|
-
reason=result.error_message or "Input guardrail failed"
|
|
429
|
-
))
|
|
430
|
-
)
|
|
431
|
-
|
|
432
413
|
# Check max turns
|
|
433
414
|
max_turns = config.max_turns or 50
|
|
434
415
|
if state.turn_count >= max_turns:
|
|
@@ -445,6 +426,105 @@ async def _run_internal(
|
|
|
445
426
|
outcome=ErrorOutcome(error=AgentNotFound(agent_name=state.current_agent_name))
|
|
446
427
|
)
|
|
447
428
|
|
|
429
|
+
# Determine if agent has advanced guardrails configuration
|
|
430
|
+
has_advanced_guardrails = bool(
|
|
431
|
+
current_agent.advanced_config and
|
|
432
|
+
current_agent.advanced_config.guardrails and
|
|
433
|
+
(current_agent.advanced_config.guardrails.input_prompt or
|
|
434
|
+
current_agent.advanced_config.guardrails.output_prompt or
|
|
435
|
+
current_agent.advanced_config.guardrails.require_citations)
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
print('[JAF:ENGINE] Debug guardrails setup:', {
|
|
439
|
+
'agent_name': current_agent.name,
|
|
440
|
+
'has_advanced_config': bool(current_agent.advanced_config),
|
|
441
|
+
'has_advanced_guardrails': has_advanced_guardrails,
|
|
442
|
+
'initial_input_guardrails': len(config.initial_input_guardrails or []),
|
|
443
|
+
'final_output_guardrails': len(config.final_output_guardrails or [])
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
# Build effective guardrails
|
|
447
|
+
effective_input_guardrails: List[Guardrail] = []
|
|
448
|
+
effective_output_guardrails: List[Guardrail] = []
|
|
449
|
+
|
|
450
|
+
if has_advanced_guardrails:
|
|
451
|
+
result = await build_effective_guardrails(current_agent, config)
|
|
452
|
+
effective_input_guardrails, effective_output_guardrails = result
|
|
453
|
+
else:
|
|
454
|
+
effective_input_guardrails = list(config.initial_input_guardrails or [])
|
|
455
|
+
effective_output_guardrails = list(config.final_output_guardrails or [])
|
|
456
|
+
|
|
457
|
+
# Execute input guardrails on first turn
|
|
458
|
+
input_guardrails_to_run = (effective_input_guardrails
|
|
459
|
+
if state.turn_count == 0 and effective_input_guardrails
|
|
460
|
+
else [])
|
|
461
|
+
|
|
462
|
+
print('[JAF:ENGINE] Input guardrails to run:', {
|
|
463
|
+
'turn_count': state.turn_count,
|
|
464
|
+
'effective_input_length': len(effective_input_guardrails),
|
|
465
|
+
'input_guardrails_to_run_length': len(input_guardrails_to_run),
|
|
466
|
+
'has_advanced_guardrails': has_advanced_guardrails
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
if input_guardrails_to_run and state.turn_count == 0:
|
|
470
|
+
first_user_message = next((m for m in state.messages if m.role == ContentRole.USER or m.role == 'user'), None)
|
|
471
|
+
if first_user_message:
|
|
472
|
+
if has_advanced_guardrails:
|
|
473
|
+
execution_mode = (current_agent.advanced_config.guardrails.execution_mode
|
|
474
|
+
if current_agent.advanced_config and current_agent.advanced_config.guardrails
|
|
475
|
+
else 'parallel')
|
|
476
|
+
|
|
477
|
+
if execution_mode == 'sequential':
|
|
478
|
+
guardrail_result = await execute_input_guardrails_sequential(
|
|
479
|
+
input_guardrails_to_run, first_user_message, config
|
|
480
|
+
)
|
|
481
|
+
if not guardrail_result.is_valid:
|
|
482
|
+
return RunResult(
|
|
483
|
+
final_state=state,
|
|
484
|
+
outcome=ErrorOutcome(error=InputGuardrailTripwire(
|
|
485
|
+
reason=getattr(guardrail_result, 'error_message', 'Input guardrail violation')
|
|
486
|
+
))
|
|
487
|
+
)
|
|
488
|
+
else:
|
|
489
|
+
# Parallel execution with LLM call overlap
|
|
490
|
+
guardrail_result = await execute_input_guardrails_parallel(
|
|
491
|
+
input_guardrails_to_run, first_user_message, config
|
|
492
|
+
)
|
|
493
|
+
if not guardrail_result.is_valid:
|
|
494
|
+
print(f"🚨 Input guardrail violation: {getattr(guardrail_result, 'error_message', 'Unknown violation')}")
|
|
495
|
+
return RunResult(
|
|
496
|
+
final_state=state,
|
|
497
|
+
outcome=ErrorOutcome(error=InputGuardrailTripwire(
|
|
498
|
+
reason=getattr(guardrail_result, 'error_message', 'Input guardrail violation')
|
|
499
|
+
))
|
|
500
|
+
)
|
|
501
|
+
else:
|
|
502
|
+
# Legacy guardrails path
|
|
503
|
+
print('[JAF:ENGINE] Using LEGACY guardrails path with', len(input_guardrails_to_run), 'guardrails')
|
|
504
|
+
for guardrail in input_guardrails_to_run:
|
|
505
|
+
if config.on_event:
|
|
506
|
+
config.on_event(GuardrailEvent(data=GuardrailEventData(
|
|
507
|
+
guardrail_name=getattr(guardrail, '__name__', 'unknown_guardrail'),
|
|
508
|
+
content=get_text_content(first_user_message.content)
|
|
509
|
+
)))
|
|
510
|
+
if asyncio.iscoroutinefunction(guardrail):
|
|
511
|
+
result = await guardrail(get_text_content(first_user_message.content))
|
|
512
|
+
else:
|
|
513
|
+
result = guardrail(get_text_content(first_user_message.content))
|
|
514
|
+
|
|
515
|
+
if not result.is_valid:
|
|
516
|
+
if config.on_event:
|
|
517
|
+
config.on_event(GuardrailViolationEvent(data=GuardrailViolationEventData(
|
|
518
|
+
stage='input',
|
|
519
|
+
reason=getattr(result, 'error_message', 'Input guardrail failed')
|
|
520
|
+
)))
|
|
521
|
+
return RunResult(
|
|
522
|
+
final_state=state,
|
|
523
|
+
outcome=ErrorOutcome(error=InputGuardrailTripwire(
|
|
524
|
+
reason=getattr(result, 'error_message', 'Input guardrail failed')
|
|
525
|
+
))
|
|
526
|
+
)
|
|
527
|
+
|
|
448
528
|
# Agent debugging logs removed for performance
|
|
449
529
|
|
|
450
530
|
# Get model name
|
|
@@ -752,13 +832,27 @@ async def _run_internal(
|
|
|
752
832
|
)))
|
|
753
833
|
|
|
754
834
|
# Check final output guardrails
|
|
755
|
-
if
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
835
|
+
if has_advanced_guardrails:
|
|
836
|
+
# Use new advanced system
|
|
837
|
+
output_guardrail_result = await execute_output_guardrails(
|
|
838
|
+
effective_output_guardrails, output_data, config
|
|
839
|
+
)
|
|
840
|
+
if not output_guardrail_result.is_valid:
|
|
841
|
+
return RunResult(
|
|
842
|
+
final_state=replace(state, messages=new_messages),
|
|
843
|
+
outcome=ErrorOutcome(error=OutputGuardrailTripwire(
|
|
844
|
+
reason=getattr(output_guardrail_result, 'error_message', 'Output guardrail violation')
|
|
845
|
+
))
|
|
846
|
+
)
|
|
847
|
+
else:
|
|
848
|
+
# Legacy system
|
|
849
|
+
if effective_output_guardrails:
|
|
850
|
+
for guardrail in effective_output_guardrails:
|
|
851
|
+
if config.on_event:
|
|
852
|
+
config.on_event(GuardrailEvent(data=GuardrailEventData(
|
|
853
|
+
guardrail_name=getattr(guardrail, '__name__', 'unknown_guardrail'),
|
|
854
|
+
content=output_data
|
|
855
|
+
)))
|
|
762
856
|
if asyncio.iscoroutinefunction(guardrail):
|
|
763
857
|
result = await guardrail(output_data)
|
|
764
858
|
else:
|
|
@@ -766,16 +860,14 @@ async def _run_internal(
|
|
|
766
860
|
|
|
767
861
|
if not result.is_valid:
|
|
768
862
|
if config.on_event:
|
|
769
|
-
config.on_event(
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
is_valid=False,
|
|
773
|
-
error_message=result.error_message
|
|
863
|
+
config.on_event(GuardrailViolationEvent(data=GuardrailViolationEventData(
|
|
864
|
+
stage='output',
|
|
865
|
+
reason=getattr(result, 'error_message', 'Output guardrail failed')
|
|
774
866
|
)))
|
|
775
867
|
return RunResult(
|
|
776
868
|
final_state=replace(state, messages=new_messages, approvals=state.approvals),
|
|
777
869
|
outcome=ErrorOutcome(error=OutputGuardrailTripwire(
|
|
778
|
-
reason=result
|
|
870
|
+
reason=getattr(result, 'error_message', 'Output guardrail failed')
|
|
779
871
|
))
|
|
780
872
|
)
|
|
781
873
|
|
|
@@ -799,32 +891,44 @@ async def _run_internal(
|
|
|
799
891
|
)
|
|
800
892
|
else:
|
|
801
893
|
# No output codec, return content as string
|
|
802
|
-
if
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
894
|
+
if has_advanced_guardrails:
|
|
895
|
+
# Use new advanced system
|
|
896
|
+
output_guardrail_result = await execute_output_guardrails(
|
|
897
|
+
effective_output_guardrails, get_text_content(assistant_message.content), config
|
|
898
|
+
)
|
|
899
|
+
if not output_guardrail_result.is_valid:
|
|
900
|
+
return RunResult(
|
|
901
|
+
final_state=replace(state, messages=new_messages),
|
|
902
|
+
outcome=ErrorOutcome(error=OutputGuardrailTripwire(
|
|
903
|
+
reason=getattr(output_guardrail_result, 'error_message', 'Output guardrail violation')
|
|
904
|
+
))
|
|
905
|
+
)
|
|
906
|
+
else:
|
|
907
|
+
# Legacy system
|
|
908
|
+
if effective_output_guardrails:
|
|
909
|
+
for guardrail in effective_output_guardrails:
|
|
815
910
|
if config.on_event:
|
|
816
911
|
config.on_event(GuardrailEvent(data=GuardrailEventData(
|
|
817
912
|
guardrail_name=getattr(guardrail, '__name__', 'unknown_guardrail'),
|
|
818
|
-
content=get_text_content(assistant_message.content)
|
|
819
|
-
is_valid=False,
|
|
820
|
-
error_message=result.error_message
|
|
913
|
+
content=get_text_content(assistant_message.content)
|
|
821
914
|
)))
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
915
|
+
if asyncio.iscoroutinefunction(guardrail):
|
|
916
|
+
result = await guardrail(get_text_content(assistant_message.content))
|
|
917
|
+
else:
|
|
918
|
+
result = guardrail(get_text_content(assistant_message.content))
|
|
919
|
+
|
|
920
|
+
if not result.is_valid:
|
|
921
|
+
if config.on_event:
|
|
922
|
+
config.on_event(GuardrailViolationEvent(data=GuardrailViolationEventData(
|
|
923
|
+
stage='output',
|
|
924
|
+
reason=getattr(result, 'error_message', 'Output guardrail failed')
|
|
925
|
+
)))
|
|
926
|
+
return RunResult(
|
|
927
|
+
final_state=replace(state, messages=new_messages, approvals=state.approvals),
|
|
928
|
+
outcome=ErrorOutcome(error=OutputGuardrailTripwire(
|
|
929
|
+
reason=getattr(result, 'error_message', 'Output guardrail failed')
|
|
930
|
+
))
|
|
931
|
+
)
|
|
828
932
|
|
|
829
933
|
return RunResult(
|
|
830
934
|
final_state=replace(state, messages=new_messages, turn_count=state.turn_count + 1, approvals=state.approvals),
|