new feature: External guardrails/judges

This commit is contained in:
2026-08-24 11:14:10 -03:00
parent 05373deff2
commit fd37138c4f
384 changed files with 40593 additions and 3621 deletions

View File

@@ -0,0 +1,97 @@
from pathlib import Path
from types import SimpleNamespace
import pytest
from agent_framework.guardrails.base import RailDecision
from agent_framework.guardrails.output_supervisor import OutputSupervisor
from agent_framework.guardrails.rail_action import RailAction
from agent_framework.observability.code_mapper import create_observability_code_mapper
def _settings(**kwargs):
defaults = dict(
OBSERVABILITY_DEFAULT_MAPPING_ENABLED=True,
OBSERVABILITY_DEFAULT_MAPPING_PATH=None,
OBSERVABILITY_CODE_MAPPING_ENABLED=False,
OBSERVABILITY_CODE_MAPPING_PATH=None,
)
defaults.update(kwargs)
return SimpleNamespace(**defaults)
def test_old_agent_without_mapping_gets_framework_default_registry():
mapper = create_observability_code_mapper(_settings())
assert mapper.map("guardrail.output_supervisor.started") == "GRL.001"
assert mapper.map("guardrail.result.block") == "GRL.004"
assert mapper.map("guardrail.output_supervisor.completed") == "GRL.009"
assert mapper.action_for("REVPREC") == "retry"
assert mapper.action_for("CMP") == "retry"
assert mapper.action_for("SCO") == "retry"
assert mapper.action_for("GND") == "retry"
assert mapper.action_for("ATH") == "handover"
assert mapper.map("GRL.004") == "GRL.004"
def test_agent_overlay_overrides_only_declared_entries(tmp_path: Path):
overlay = tmp_path / "observability_mapping.yaml"
overlay.write_text(
"""version: \"2\"\nmappings:\n guardrail.dlex_in:\n label: GRL.004\n aliases: [DLEX_IN]\n guardrail.tox:\n label: GRL.005\n aliases: [TOX]\n""",
encoding="utf-8",
)
mapper = create_observability_code_mapper(_settings(
OBSERVABILITY_CODE_MAPPING_ENABLED=True,
OBSERVABILITY_CODE_MAPPING_PATH=str(overlay),
))
# Agent-specific contract overrides framework named GRL.DLEX_IN / GRL.TOX.
assert mapper.map("guardrail.dlex_in") == "GRL.004"
assert mapper.map("DLEX_IN") == "GRL.004"
assert mapper.map("TOX") == "GRL.005"
# Unrelated framework defaults remain intact.
assert mapper.map("guardrail.result.retry") == "GRL.005"
assert mapper.action_for("REVPREC") == "retry"
assert mapper.map("guardrail.pinj") == "GRL.PINJ"
def test_agent_overlay_can_override_default_action(tmp_path: Path):
overlay = tmp_path / "observability_mapping.yaml"
overlay.write_text(
"""mappings:\n guardrail.revprec:\n action: handover\n aliases: [REVPREC]\n""",
encoding="utf-8",
)
mapper = create_observability_code_mapper(_settings(
OBSERVABILITY_CODE_MAPPING_ENABLED=True,
OBSERVABILITY_CODE_MAPPING_PATH=str(overlay),
))
assert mapper.action_for("REVPREC") == "handover"
# Other historical defaults are still inherited.
assert mapper.action_for("CMP") == "retry"
def test_framework_default_carries_legacy_phraseology_rewrite_policy():
mapper = create_observability_code_mapper(_settings())
remediation = mapper.remediation_for("FRASEOLOGIA")
assert remediation is not None
assert remediation["type"] == "rewrite"
assert remediation["max_attempts"] == 1
assert remediation["generation_name"] == "guardrail.fraseologia.rewrite"
class _DeniedLegacyRail:
code = "REVPREC"
async def evaluate(self, text, context):
return RailDecision(code=self.code, allowed=False, reason="premature")
@pytest.mark.asyncio
async def test_old_legacy_rail_keeps_retry_with_new_framework_and_no_agent_mapping():
mapper = create_observability_code_mapper(_settings())
supervisor = OutputSupervisor(
rails=[_DeniedLegacyRail()],
enable_parallel=False,
observability_mapper=mapper,
)
decision = await supervisor.evaluate("candidate", {})
assert decision.action == RailAction.RETRY
assert decision.results[0].metadata.get("action_source") == "observability_mapping"