Skip to content

Commit 7d90d0a

Browse files
krithika369Krithika Sundararajan
and
Krithika Sundararajan
authored
Bugfix: Clear default route Id from custom ensembler configs (#200)
* Miscellaneous bug fixes * Add unit tests for the SDK changes, address PR comments * Bugfix: Regression in display of container configs for Pyfunc * Add type annotation to class methods Co-authored-by: Krithika Sundararajan <[email protected]>
1 parent 5f146f2 commit 7d90d0a

File tree

7 files changed

+323
-74
lines changed

7 files changed

+323
-74
lines changed

sdk/tests/conftest.py

+60-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
from turing.router.config.resource_request import ResourceRequest
1717
from turing.router.config.log_config import LogConfig, ResultLoggerType
1818
from turing.router.config.enricher import Enricher
19-
from turing.router.config.router_ensembler_config import DockerRouterEnsemblerConfig
19+
from turing.router.config.router_ensembler_config import (
20+
EnsemblerNopConfig,
21+
EnsemblerStandardConfig,
22+
DockerRouterEnsemblerConfig,
23+
PyfuncRouterEnsemblerConfig
24+
)
2025
from turing.router.config.common.env_var import EnvVar
2126
from turing.router.config.experiment_config import ExperimentConfig
2227
from tests.fixtures.mlflow import mock_mlflow
@@ -80,6 +85,58 @@ def generic_ensemblers(project, num_ensemblers):
8085
updated_at=datetime.now() + timedelta(seconds=i + 10)
8186
) for i in range(1, num_ensemblers + 1)]
8287

88+
@pytest.fixture
89+
def nop_router_ensembler_config():
90+
return EnsemblerNopConfig(final_response_route_id="test")
91+
92+
@pytest.fixture
93+
def standard_router_ensembler_config():
94+
return EnsemblerStandardConfig(
95+
experiment_mappings=[
96+
turing.generated.models.EnsemblerStandardConfigExperimentMappings(
97+
experiment="experiment-1",
98+
treatment="treatment-1",
99+
route="route-1"
100+
),
101+
turing.generated.models.EnsemblerStandardConfigExperimentMappings(
102+
experiment="experiment-2",
103+
treatment="treatment-2",
104+
route="route-2"
105+
)
106+
],
107+
fallback_response_route_id="route-1"
108+
)
109+
110+
@pytest.fixture
111+
def docker_router_ensembler_config():
112+
return DockerRouterEnsemblerConfig(
113+
image="test.io/just-a-test/turing-ensembler:0.0.0-build.0",
114+
resource_request=ResourceRequest(
115+
min_replica=1,
116+
max_replica=3,
117+
cpu_request="500m",
118+
memory_request="512Mi"
119+
),
120+
endpoint=f"http://localhost:5000/ensembler_endpoint",
121+
timeout="500ms",
122+
port=5120,
123+
env=[],
124+
)
125+
126+
@pytest.fixture
127+
def pyfunc_router_ensembler_config():
128+
return PyfuncRouterEnsemblerConfig(
129+
project_id=1,
130+
ensembler_id=1,
131+
resource_request=ResourceRequest(
132+
min_replica=1,
133+
max_replica=3,
134+
cpu_request="500m",
135+
memory_request="512Mi"
136+
),
137+
timeout="500ms",
138+
env=[],
139+
)
83140

84141
@pytest.fixture
85142
def bucket_name():
@@ -469,7 +526,7 @@ def generic_router_version(
469526

470527

471528
@pytest.fixture
472-
def generic_router_config():
529+
def generic_router_config(docker_router_ensembler_config):
473530
return RouterConfig(
474531
environment_name="id-dev",
475532
name="router-1",
@@ -530,19 +587,7 @@ def generic_router_config():
530587
)
531588
]
532589
),
533-
ensembler=DockerRouterEnsemblerConfig(
534-
image="test.io/just-a-test/turing-ensembler:0.0.0-build.0",
535-
resource_request=ResourceRequest(
536-
min_replica=1,
537-
max_replica=3,
538-
cpu_request="500m",
539-
memory_request="512Mi"
540-
),
541-
endpoint=f"http://localhost:5000/ensembler_endpoint",
542-
timeout="500ms",
543-
port=5120,
544-
env=[],
545-
)
590+
ensembler=docker_router_ensembler_config
546591
)
547592

548593

sdk/tests/router/config/router_config_test.py

+82-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import pytest
22
from turing.router.config.route import Route, DuplicateRouteException, InvalidRouteException
3-
3+
from turing.router.config.resource_request import ResourceRequest
4+
from turing.router.config.router_ensembler_config import (
5+
DockerRouterEnsemblerConfig,
6+
NopRouterEnsemblerConfig,
7+
StandardRouterEnsemblerConfig,
8+
PyfuncRouterEnsemblerConfig,
9+
RouterEnsemblerConfig
10+
)
411

512
@pytest.mark.parametrize(
613
"actual,new_routes,expected", [
@@ -29,6 +36,7 @@ def test_set_router_config_with_invalid_routes(actual, new_routes, expected, req
2936
])
3037
def test_set_router_config_with_invalid_default_route(actual, invalid_route_id, expected, request):
3138
actual = request.getfixturevalue(actual)
39+
actual.ensembler = None
3240
actual.default_route_id = invalid_route_id
3341
with pytest.raises(expected):
3442
actual.to_open_api()
@@ -43,3 +51,76 @@ def test_set_router_config_with_invalid_default_route(actual, invalid_route_id,
4351
def test_remove_router_config_default_route(actual, expected, request):
4452
actual = request.getfixturevalue(actual)
4553
assert "default_route_id" not in actual.to_open_api()
54+
55+
@pytest.mark.parametrize(
56+
"router,type,nop_config,standard_config,docker_config,pyfunc_config,expected_class", [
57+
pytest.param(
58+
"generic_router_config",
59+
"nop", "nop_router_ensembler_config", None, None, None,
60+
NopRouterEnsemblerConfig
61+
),
62+
pytest.param(
63+
"generic_router_config",
64+
"standard", None, "standard_router_ensembler_config", None, None,
65+
StandardRouterEnsemblerConfig
66+
),
67+
pytest.param(
68+
"generic_router_config",
69+
"docker", None, None, "generic_ensembler_docker_config", None,
70+
DockerRouterEnsemblerConfig
71+
),
72+
pytest.param(
73+
"generic_router_config",
74+
"pyfunc", None, None, None, "generic_ensembler_pyfunc_config",
75+
PyfuncRouterEnsemblerConfig
76+
)
77+
])
78+
def test_set_router_config_base_ensembler(
79+
router, type,
80+
nop_config, standard_config, docker_config, pyfunc_config,
81+
expected_class, request):
82+
actual = request.getfixturevalue(router)
83+
ensembler = RouterEnsemblerConfig(type=type,
84+
nop_config=None if nop_config is None else request.getfixturevalue(nop_config),
85+
standard_config=None if standard_config is None else request.getfixturevalue(standard_config),
86+
docker_config=None if docker_config is None else request.getfixturevalue(docker_config),
87+
pyfunc_config=None if pyfunc_config is None else request.getfixturevalue(pyfunc_config)
88+
)
89+
actual.ensembler = ensembler
90+
assert isinstance(actual.ensembler, expected_class)
91+
92+
@pytest.mark.parametrize(
93+
"router_config,default_route_id,ensembler,expected", [
94+
pytest.param(
95+
"generic_router_config",
96+
"model-a",
97+
StandardRouterEnsemblerConfig(experiment_mappings=[], fallback_response_route_id="model-b"),
98+
"model-b",
99+
),
100+
pytest.param(
101+
"generic_router_config",
102+
"model-a",
103+
NopRouterEnsemblerConfig(final_response_route_id="model-b"),
104+
"model-b",
105+
),
106+
pytest.param(
107+
"generic_router_config",
108+
"model-a",
109+
"docker_router_ensembler_config",
110+
None,
111+
),
112+
pytest.param(
113+
"generic_router_config",
114+
"model-a",
115+
"pyfunc_router_ensembler_config",
116+
None,
117+
)
118+
])
119+
def test_default_route_id_by_ensembler_config(router_config, default_route_id, ensembler, expected, request):
120+
router = request.getfixturevalue(router_config)
121+
router.default_route_id = default_route_id
122+
router.ensembler = request.getfixturevalue(ensembler) if isinstance(ensembler, str) else ensembler
123+
if expected:
124+
assert router.to_open_api().to_dict()["config"]["default_route_id"] == expected
125+
else:
126+
assert "default_route_id" not in router.to_open_api().to_dict()["config"]

sdk/tests/router/config/router_ensembler_config_test.py

+91-36
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from turing.router.config.route import InvalidRouteException
77
from turing.router.config.router_ensembler_config import (RouterEnsemblerConfig,
88
EnsemblerNopConfig,
9-
EnsemblerStandardConfig,
109
NopRouterEnsemblerConfig,
1110
PyfuncRouterEnsemblerConfig,
1211
DockerRouterEnsemblerConfig,
@@ -18,55 +17,26 @@
1817
pytest.param(
1918
1,
2019
"standard",
21-
EnsemblerStandardConfig(
22-
experiment_mappings=[
23-
turing.generated.models.EnsemblerStandardConfigExperimentMappings(
24-
experiment="experiment-1",
25-
treatment="treatment-1",
26-
route="route-1"
27-
),
28-
turing.generated.models.EnsemblerStandardConfigExperimentMappings(
29-
experiment="experiment-2",
30-
treatment="treatment-2",
31-
route="route-2"
32-
)
33-
],
34-
fallback_response_route_id="route-1"
35-
),
20+
"standard_router_ensembler_config",
3621
None,
3722
"generic_standard_router_ensembler_config"
3823
),
3924
pytest.param(
4025
1,
4126
"docker",
4227
None,
43-
turing.generated.models.EnsemblerDockerConfig(
44-
image="test.io/just-a-test/turing-ensembler:0.0.0-build.0",
45-
resource_request=turing.generated.models.ResourceRequest(
46-
min_replica=1,
47-
max_replica=3,
48-
cpu_request='100m',
49-
memory_request='512Mi'
50-
),
51-
endpoint=f"http://localhost:5000/ensembler_endpoint",
52-
timeout="500ms",
53-
port=5120,
54-
env=[
55-
turing.generated.models.EnvVar(
56-
name="env_name",
57-
value="env_val")
58-
],
59-
service_account="secret-name-for-google-service-account"
60-
),
28+
"generic_ensembler_docker_config",
6129
"generic_docker_router_ensembler_config"
6230
)
6331
])
6432
def test_create_router_ensembler_config(id, type, standard_config, docker_config, expected, request):
33+
docker_config_data = None if docker_config is None else request.getfixturevalue(docker_config)
34+
standard_config_data = None if standard_config is None else request.getfixturevalue(standard_config)
6535
actual = RouterEnsemblerConfig(
6636
id=id,
6737
type=type,
68-
standard_config=standard_config,
69-
docker_config=docker_config
38+
standard_config=standard_config_data,
39+
docker_config=docker_config_data
7040
).to_open_api()
7141
assert actual == request.getfixturevalue(expected)
7242

@@ -532,3 +502,88 @@ def test_create_nop_router_ensembler_config_with_invalid_route(
532502
router.ensembler = ensembler_config
533503
with pytest.raises(expected):
534504
router.to_open_api()
505+
506+
@pytest.mark.parametrize(
507+
"cls,config,expected", [
508+
pytest.param(
509+
NopRouterEnsemblerConfig,
510+
"nop_router_ensembler_config",
511+
{"type":"nop", "final_response_route_id": "test"}
512+
),
513+
pytest.param(
514+
StandardRouterEnsemblerConfig,
515+
"standard_router_ensembler_config",
516+
{
517+
"type":"standard",
518+
"experiment_mappings": [
519+
{
520+
"experiment": "experiment-1",
521+
"treatment": "treatment-1",
522+
"route": "route-1"
523+
},
524+
{
525+
"experiment": "experiment-2",
526+
"treatment": "treatment-2",
527+
"route": "route-2"
528+
}
529+
],
530+
"fallback_response_route_id": "route-1"
531+
}
532+
),
533+
pytest.param(
534+
DockerRouterEnsemblerConfig,
535+
"generic_ensembler_docker_config",
536+
{
537+
"type": "docker",
538+
"image": "test.io/just-a-test/turing-ensembler:0.0.0-build.0",
539+
"resource_request": ResourceRequest(
540+
min_replica=1,
541+
max_replica=3,
542+
cpu_request='100m',
543+
memory_request='512Mi'
544+
),
545+
"endpoint": "http://localhost:5000/ensembler_endpoint",
546+
"timeout": "500ms",
547+
"port": 5120,
548+
"env": [EnvVar(name="env_name", value="env_val")],
549+
"service_account": "secret-name-for-google-service-account"
550+
}
551+
),
552+
pytest.param(
553+
PyfuncRouterEnsemblerConfig,
554+
"generic_ensembler_pyfunc_config",
555+
{
556+
"type":"pyfunc",
557+
"project_id": 77,
558+
"ensembler_id": 11,
559+
"resource_request": ResourceRequest(
560+
min_replica=1,
561+
max_replica=3,
562+
cpu_request='100m',
563+
memory_request='512Mi'
564+
),
565+
"timeout": "500ms",
566+
"env": [EnvVar(name="env_name", value="env_val")]
567+
}
568+
)
569+
])
570+
def test_create_ensembler_config_from_config(
571+
cls, config, expected, request
572+
):
573+
config_data = request.getfixturevalue(config)
574+
assert cls.from_config(config_data).to_dict() == expected
575+
576+
def test_set_nop_ensembler_config_with_default_route(request):
577+
router = request.getfixturevalue("generic_router_config")
578+
router.ensembler = None
579+
router.default_route_id = "model-b"
580+
assert router.ensembler.final_response_route_id == "model-b"
581+
582+
def test_set_standard_ensembler_config_with_default_route(request):
583+
router = request.getfixturevalue("generic_router_config")
584+
router.ensembler = StandardRouterEnsemblerConfig(
585+
experiment_mappings=[],
586+
fallback_response_route_id=""
587+
)
588+
router.default_route_id = "model-b"
589+
assert router.ensembler.fallback_response_route_id == "model-b"

0 commit comments

Comments
 (0)