참고소스 수정본
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
import asyncio
|
||||
import pytest
|
||||
from time import sleep
|
||||
from guardrails.validator_base import Validator, register_validator
|
||||
from guardrails_ai.types import PassResult
|
||||
|
||||
|
||||
@register_validator(name="test/validator1", data_type="string")
|
||||
class Validator1(Validator):
|
||||
def validate(self, value, metadata):
|
||||
# This seems more realistic but is unreliable
|
||||
# counter = 0
|
||||
# for i in range(100000000):
|
||||
# counter += 1
|
||||
# This seems suspicious, but is consistent
|
||||
sleep(0.3)
|
||||
metadata["order"].append("test/validator1")
|
||||
return PassResult()
|
||||
|
||||
|
||||
@register_validator(name="test/validator2", data_type="string")
|
||||
class Validator2(Validator):
|
||||
def validate(self, value, metadata):
|
||||
# counter = 0
|
||||
# for i in range(1):
|
||||
# counter += 1
|
||||
sleep(0.1)
|
||||
metadata["order"].append("test/validator2")
|
||||
return PassResult()
|
||||
|
||||
|
||||
@register_validator(name="test/validator3", data_type="string")
|
||||
class Validator3(Validator):
|
||||
def validate(self, value, metadata):
|
||||
# counter = 0
|
||||
# for i in range(100000):
|
||||
# counter += 1
|
||||
sleep(0.2)
|
||||
metadata["order"].append("test/validator3")
|
||||
return PassResult()
|
||||
|
||||
|
||||
@register_validator(name="test/async_validator1", data_type="string")
|
||||
class AsyncValidator1(Validator):
|
||||
async def async_validate(self, value, metadata):
|
||||
await asyncio.sleep(0.3)
|
||||
metadata["order"].append("test/async_validator1")
|
||||
return PassResult()
|
||||
|
||||
|
||||
@register_validator(name="test/async_validator2", data_type="string")
|
||||
class AsyncValidator2(Validator):
|
||||
async def async_validate(self, value, metadata):
|
||||
await asyncio.sleep(0.1)
|
||||
metadata["order"].append("test/async_validator2")
|
||||
return PassResult()
|
||||
|
||||
|
||||
@register_validator(name="test/async_validator3", data_type="string")
|
||||
class AsyncValidator3(Validator):
|
||||
async def async_validate(self, value, metadata):
|
||||
await asyncio.sleep(0.2)
|
||||
metadata["order"].append("test/async_validator3")
|
||||
return PassResult()
|
||||
|
||||
|
||||
class TestValidatorConcurrency:
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_validate_with_sync_validators(self):
|
||||
from guardrails.validator_service import AsyncValidatorService
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
async_validator_service = AsyncValidatorService()
|
||||
|
||||
value, metadata = await async_validator_service.async_validate(
|
||||
value="value",
|
||||
metadata={"order": []},
|
||||
validator_map={
|
||||
"$": [
|
||||
# Note the order
|
||||
Validator1(),
|
||||
Validator2(),
|
||||
Validator3(),
|
||||
]
|
||||
},
|
||||
iteration=iteration,
|
||||
absolute_path="$",
|
||||
reference_path="$",
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {
|
||||
"order": ["test/validator2", "test/validator3", "test/validator1"]
|
||||
}
|
||||
|
||||
def test_validate_with_sync_validators(self):
|
||||
from guardrails.validator_service import AsyncValidatorService
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
async_validator_service = AsyncValidatorService()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
value, metadata = async_validator_service.validate(
|
||||
value="value",
|
||||
metadata={"order": []},
|
||||
validator_map={
|
||||
"$": [
|
||||
# Note the order
|
||||
Validator1(),
|
||||
Validator2(),
|
||||
Validator3(),
|
||||
]
|
||||
},
|
||||
iteration=iteration,
|
||||
absolute_path="$",
|
||||
reference_path="$",
|
||||
loop=loop,
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {
|
||||
"order": ["test/validator2", "test/validator3", "test/validator1"]
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_validate_with_async_validators(self):
|
||||
from guardrails.validator_service import AsyncValidatorService
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
async_validator_service = AsyncValidatorService()
|
||||
|
||||
value, metadata = await async_validator_service.async_validate(
|
||||
value="value",
|
||||
metadata={"order": []},
|
||||
validator_map={
|
||||
"$": [
|
||||
# Note the order
|
||||
AsyncValidator1(),
|
||||
AsyncValidator2(),
|
||||
AsyncValidator3(),
|
||||
]
|
||||
},
|
||||
iteration=iteration,
|
||||
absolute_path="$",
|
||||
reference_path="$",
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {
|
||||
"order": [
|
||||
"test/async_validator2",
|
||||
"test/async_validator3",
|
||||
"test/async_validator1",
|
||||
]
|
||||
}
|
||||
|
||||
def test_validate_with_async_validators(self):
|
||||
from guardrails.validator_service import AsyncValidatorService
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
async_validator_service = AsyncValidatorService()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
value, metadata = async_validator_service.validate(
|
||||
value="value",
|
||||
metadata={"order": []},
|
||||
validator_map={
|
||||
"$": [
|
||||
# Note the order
|
||||
AsyncValidator1(),
|
||||
AsyncValidator2(),
|
||||
AsyncValidator3(),
|
||||
]
|
||||
},
|
||||
iteration=iteration,
|
||||
absolute_path="$",
|
||||
reference_path="$",
|
||||
loop=loop,
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {
|
||||
"order": [
|
||||
"test/async_validator2",
|
||||
"test/async_validator3",
|
||||
"test/async_validator1",
|
||||
]
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_validate_with_mixed_validators(self):
|
||||
from guardrails.validator_service import AsyncValidatorService
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
async_validator_service = AsyncValidatorService()
|
||||
|
||||
value, metadata = await async_validator_service.async_validate(
|
||||
value="value",
|
||||
metadata={"order": []},
|
||||
validator_map={
|
||||
"$": [
|
||||
# Note the order
|
||||
Validator1(),
|
||||
Validator2(),
|
||||
AsyncValidator3(),
|
||||
]
|
||||
},
|
||||
iteration=iteration,
|
||||
absolute_path="$",
|
||||
reference_path="$",
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {
|
||||
"order": ["test/validator2", "test/async_validator3", "test/validator1"]
|
||||
}
|
||||
|
||||
def test_validate_with_mixed_validators(self):
|
||||
from guardrails.validator_service import AsyncValidatorService
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
async_validator_service = AsyncValidatorService()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
value, metadata = async_validator_service.validate(
|
||||
value="value",
|
||||
metadata={"order": []},
|
||||
validator_map={
|
||||
"$": [
|
||||
# Note the order
|
||||
Validator1(),
|
||||
Validator2(),
|
||||
AsyncValidator3(),
|
||||
]
|
||||
},
|
||||
iteration=iteration,
|
||||
absolute_path="$",
|
||||
reference_path="$",
|
||||
loop=loop,
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {
|
||||
"order": ["test/validator2", "test/async_validator3", "test/validator1"]
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
from asyncio import get_event_loop
|
||||
from asyncio.unix_events import _UnixSelectorEventLoop
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from guardrails.validator_service import should_run_sync, get_loop
|
||||
from guardrails.classes.history import Iteration
|
||||
|
||||
|
||||
try:
|
||||
import uvloop
|
||||
except ImportError:
|
||||
uvloop = None
|
||||
|
||||
|
||||
class TestShouldRunSync:
|
||||
def test_guardrails_run_sync_is_true(self):
|
||||
GUARDRAILS_RUN_SYNC_bak = os.environ.get("GUARDRAILS_RUN_SYNC")
|
||||
os.environ["GUARDRAILS_RUN_SYNC"] = "true"
|
||||
|
||||
result = should_run_sync()
|
||||
assert result is True
|
||||
|
||||
if GUARDRAILS_RUN_SYNC_bak is not None:
|
||||
os.environ["GUARDRAILS_RUN_SYNC"] = GUARDRAILS_RUN_SYNC_bak
|
||||
else:
|
||||
del os.environ["GUARDRAILS_RUN_SYNC"]
|
||||
|
||||
def test_guardrails_run_sync_is_false(self):
|
||||
GUARDRAILS_RUN_SYNC_bak = os.environ.get("GUARDRAILS_RUN_SYNC")
|
||||
os.environ["GUARDRAILS_RUN_SYNC"] = "false"
|
||||
|
||||
result = should_run_sync()
|
||||
assert result is False
|
||||
|
||||
if GUARDRAILS_RUN_SYNC_bak is not None:
|
||||
os.environ["GUARDRAILS_RUN_SYNC"] = GUARDRAILS_RUN_SYNC_bak
|
||||
else:
|
||||
del os.environ["GUARDRAILS_RUN_SYNC"]
|
||||
|
||||
|
||||
class TestGetLoop:
|
||||
def test_raises_if_loop_is_running(self):
|
||||
loop = get_event_loop()
|
||||
|
||||
async def callback():
|
||||
# NOTE: This means only AsyncGuard will parallelize validators
|
||||
# if it's called within an async function.
|
||||
with pytest.raises(RuntimeError, match="An event loop is already running."):
|
||||
get_loop()
|
||||
|
||||
loop.run_until_complete(callback())
|
||||
|
||||
@pytest.mark.skipif(uvloop is None, reason="uvloop is not installed")
|
||||
def test_uvloop_is_used_when_installed(self):
|
||||
loop = get_loop()
|
||||
assert isinstance(loop, uvloop.Loop)
|
||||
|
||||
@pytest.mark.skipif(uvloop is not None, reason="uvloop is installed")
|
||||
def test_asyncio_default_is_used_otherwise(self):
|
||||
loop = get_loop()
|
||||
assert isinstance(loop, _UnixSelectorEventLoop)
|
||||
|
||||
|
||||
class TestValidate:
|
||||
def test_forced_sync(self, mocker):
|
||||
GUARDRAILS_RUN_SYNC_bak = os.environ.get("GUARDRAILS_RUN_SYNC")
|
||||
os.environ["GUARDRAILS_RUN_SYNC"] = "true"
|
||||
|
||||
from guardrails.validator_service import validate, SequentialValidatorService
|
||||
|
||||
mocker.spy(SequentialValidatorService, "__init__")
|
||||
mocker.spy(SequentialValidatorService, "validate")
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
value, metadata = validate(
|
||||
value="value",
|
||||
metadata={},
|
||||
validator_map={},
|
||||
iteration=iteration,
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {}
|
||||
SequentialValidatorService.__init__.assert_called_once()
|
||||
SequentialValidatorService.validate.assert_called_once()
|
||||
|
||||
if GUARDRAILS_RUN_SYNC_bak is not None:
|
||||
os.environ["GUARDRAILS_RUN_SYNC"] = GUARDRAILS_RUN_SYNC_bak
|
||||
else:
|
||||
del os.environ["GUARDRAILS_RUN_SYNC"]
|
||||
|
||||
def test_async(self, mocker):
|
||||
from guardrails.validator_service import validate, AsyncValidatorService
|
||||
|
||||
mocker.spy(AsyncValidatorService, "__init__")
|
||||
mocker.spy(AsyncValidatorService, "validate")
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
value, metadata = validate(
|
||||
value="value",
|
||||
metadata={},
|
||||
validator_map={},
|
||||
iteration=iteration,
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {}
|
||||
AsyncValidatorService.__init__.assert_called_once()
|
||||
AsyncValidatorService.validate.assert_called_once()
|
||||
|
||||
def test_sync_busy_loop(self, mocker):
|
||||
from guardrails.validator_service import validate, SequentialValidatorService
|
||||
|
||||
mocker.spy(SequentialValidatorService, "__init__")
|
||||
mocker.spy(SequentialValidatorService, "validate")
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
loop = get_event_loop()
|
||||
|
||||
async def callback():
|
||||
with pytest.warns(
|
||||
Warning,
|
||||
match=(
|
||||
"Could not obtain an event loop."
|
||||
" Falling back to synchronous validation."
|
||||
),
|
||||
):
|
||||
value, metadata = validate(
|
||||
value="value",
|
||||
metadata={},
|
||||
validator_map={},
|
||||
iteration=iteration,
|
||||
)
|
||||
assert value == "value"
|
||||
assert metadata == {}
|
||||
|
||||
loop.run_until_complete(callback())
|
||||
|
||||
SequentialValidatorService.__init__.assert_called_once()
|
||||
SequentialValidatorService.validate.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_async_validate(mocker):
|
||||
from guardrails.validator_service import async_validate, AsyncValidatorService
|
||||
|
||||
mocker.spy(AsyncValidatorService, "__init__")
|
||||
mocker.spy(AsyncValidatorService, "async_validate")
|
||||
|
||||
iteration = Iteration(
|
||||
call_id="mock_call_id",
|
||||
index=0,
|
||||
)
|
||||
|
||||
value, metadata = await async_validate(
|
||||
value="value",
|
||||
metadata={},
|
||||
validator_map={},
|
||||
iteration=iteration,
|
||||
)
|
||||
|
||||
assert value == "value"
|
||||
assert metadata == {}
|
||||
AsyncValidatorService.__init__.assert_called_once()
|
||||
AsyncValidatorService.async_validate.assert_called_once()
|
||||
Reference in New Issue
Block a user