Skip to content

Commit 75cd064

Browse files
yeesiancopybara-github
authored andcommitted
fix: GenAI SDK client - handle os paths for .whl in requirements when deploying to agent engine
PiperOrigin-RevId: 788584767
1 parent 4fcfcb9 commit 75cd064

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

tests/unit/vertex_langchain/test_agent_engines.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,19 @@ def test_invalid_requirement_warning(self, caplog):
32513251
_utils.parse_constraints(["invalid requirement line"])
32523252
assert "Failed to parse constraint" in caplog.text
32533253

3254+
def test_requirements_with_whl_files(self):
3255+
whl_files = [
3256+
"wxPython-4.2.4-cp39-cp39-macosx_12_0_x86_64.whl",
3257+
"/content/wxPython-4.2.3-cp39-cp39-macosx_12_0_x86_64.whl",
3258+
"https://wxpython.org/Phoenix/snapshot-builds/wxPython-4.2.2-cp38-cp38-macosx_12_0_x86_64.whl",
3259+
]
3260+
result = _utils.parse_constraints(whl_files)
3261+
assert result == {
3262+
"wxPython-4.2.2-cp38-cp38-macosx_12_0_x86_64.whl": None,
3263+
"wxPython-4.2.3-cp39-cp39-macosx_12_0_x86_64.whl": None,
3264+
"wxPython-4.2.4-cp39-cp39-macosx_12_0_x86_64.whl": None,
3265+
}
3266+
32543267
def test_compare_requirements_with_required_packages(self):
32553268
requirements = {"requests": "2.0.0"}
32563269
constraints = ["requests==1.0.0"]

tests/unit/vertexai/genai/test_agent_engines.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,14 @@ def set_up(self):
730730
pass
731731

732732

733+
def _create_fake_object_with_module(module_name):
734+
class FakeObject:
735+
pass
736+
737+
FakeObject.__module__ = module_name
738+
return FakeObject()
739+
740+
733741
@pytest.mark.usefixtures("google_auth_mock")
734742
class TestAgentEngineHelpers:
735743
def setup_method(self):
@@ -919,6 +927,102 @@ def test_register_api_methods(self):
919927
"description"
920928
)
921929

930+
@pytest.mark.usefixtures("caplog")
931+
def test_invalid_requirement_warning(self, caplog):
932+
_agent_engines_utils._parse_constraints(["invalid requirement line"])
933+
assert "Failed to parse constraint" in caplog.text
934+
935+
def test_requirements_with_whl_files(self):
936+
whl_files = [
937+
"wxPython-4.2.4-cp39-cp39-macosx_12_0_x86_64.whl",
938+
"/content/wxPython-4.2.3-cp39-cp39-macosx_12_0_x86_64.whl",
939+
"https://wxpython.org/Phoenix/snapshot-builds/wxPython-4.2.2-cp38-cp38-macosx_12_0_x86_64.whl",
940+
]
941+
result = _agent_engines_utils._parse_constraints(whl_files)
942+
assert result == {
943+
"wxPython-4.2.2-cp38-cp38-macosx_12_0_x86_64.whl": None,
944+
"wxPython-4.2.3-cp39-cp39-macosx_12_0_x86_64.whl": None,
945+
"wxPython-4.2.4-cp39-cp39-macosx_12_0_x86_64.whl": None,
946+
}
947+
948+
def test_compare_requirements_with_required_packages(self):
949+
requirements = {"requests": "2.0.0"}
950+
constraints = ["requests==1.0.0"]
951+
result = _agent_engines_utils._compare_requirements(requirements, constraints)
952+
assert result == {
953+
"actions": {"append": set()},
954+
"warnings": {
955+
"incompatible": {"requests==2.0.0 (required: ==1.0.0)"},
956+
"missing": set(),
957+
},
958+
}
959+
960+
@pytest.mark.usefixtures("importlib_metadata_version_mock")
961+
def test_scan_simple_object(self):
962+
"""Test scanning an object importing a known third-party package."""
963+
fake_obj = _create_fake_object_with_module("requests")
964+
requirements = _agent_engines_utils._scan_requirements(
965+
fake_obj,
966+
package_distributions=_TEST_PACKAGE_DISTRIBUTIONS,
967+
)
968+
assert requirements == {
969+
"cloudpickle": "3.0.0",
970+
"pydantic": "1.11.1",
971+
"requests": "2.0.0",
972+
}
973+
974+
@pytest.mark.usefixtures("importlib_metadata_version_mock")
975+
def test_scan_object_with_stdlib_module(self):
976+
"""Test that stdlib modules are ignored by default."""
977+
fake_obj_stdlib = _create_fake_object_with_module("json")
978+
requirements = _agent_engines_utils._scan_requirements(
979+
fake_obj_stdlib,
980+
package_distributions=_TEST_PACKAGE_DISTRIBUTIONS,
981+
)
982+
# Requirements should not contain 'json',
983+
# because 'json' is a stdlib module.
984+
assert requirements == {
985+
"cloudpickle": "3.0.0",
986+
"pydantic": "1.11.1",
987+
}
988+
989+
@pytest.mark.usefixtures("importlib_metadata_version_mock")
990+
def test_scan_with_default_ignore_modules(self, monkeypatch):
991+
"""Test implicitly ignoring a module."""
992+
fake_obj = _create_fake_object_with_module("requests")
993+
original_base = _agent_engines_utils._BASE_MODULES
994+
monkeypatch.setattr(
995+
_agent_engines_utils,
996+
"_BASE_MODULES",
997+
set(original_base) | {"requests"},
998+
)
999+
requirements = _agent_engines_utils._scan_requirements(
1000+
fake_obj,
1001+
package_distributions=_TEST_PACKAGE_DISTRIBUTIONS,
1002+
)
1003+
# Requirements should not contain 'requests',
1004+
# because 'requests' is implicitly ignored in `_BASE_MODULES`.
1005+
assert requirements == {
1006+
"cloudpickle": "3.0.0",
1007+
"pydantic": "1.11.1",
1008+
}
1009+
1010+
@pytest.mark.usefixtures("importlib_metadata_version_mock")
1011+
def test_scan_with_explicit_ignore_modules(self):
1012+
"""Test explicitly ignoring a module."""
1013+
fake_obj = _create_fake_object_with_module("requests")
1014+
requirements = _agent_engines_utils._scan_requirements(
1015+
fake_obj,
1016+
ignore_modules=["requests"],
1017+
package_distributions=_TEST_PACKAGE_DISTRIBUTIONS,
1018+
)
1019+
# Requirements should not contain 'requests',
1020+
# because 'requests' is explicitly ignored in `ignore_modules`.
1021+
assert requirements == {
1022+
"cloudpickle": "3.0.0",
1023+
"pydantic": "1.11.1",
1024+
}
1025+
9221026

9231027
@pytest.mark.usefixtures("google_auth_mock")
9241028
class TestAgentEngine:

vertexai/_genai/_agent_engines_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,8 @@ def _parse_constraints(
686686
result: Dict[str, Optional[_SpecifierSet]] = {}
687687
for constraint in constraints:
688688
try:
689+
if constraint.endswith(".whl"):
690+
constraint = os.path.basename(constraint)
689691
requirement = requirements.Requirement(constraint)
690692
except Exception as e:
691693
logger.warning(f"Failed to parse constraint: {constraint}. Exception: {e}")

vertexai/agent_engines/_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import dataclasses
1717
import inspect
1818
import json
19+
import os
1920
import sys
2021
import types
2122
import typing
@@ -359,6 +360,8 @@ def parse_constraints(
359360
result = {}
360361
for constraint in constraints:
361362
try:
363+
if constraint.endswith(".whl"):
364+
constraint = os.path.basename(constraint)
362365
requirement = requirements.Requirement(constraint)
363366
except Exception as e:
364367
LOGGER.warning(f"Failed to parse constraint: {constraint}. Exception: {e}")

0 commit comments

Comments
 (0)