From 527db3ce43a46e0e31b7bf34e3f05f25bd40a4ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Dec 2024 08:42:49 -0800 Subject: [PATCH 1/3] Update cryptography requirement from <44.0.0 to <45.0.0 (#380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates the requirements on [cryptography](https://github.com/pyca/cryptography) to permit the latest version. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/0.1...44.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Dean QuiƱanola --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4c7681fe..b770f89a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ backoff >= 2.2.1 boto3 >= 1.26.165 click >= 8.1.7 colorama >= 0.2.5, < 0.4.7 -cryptography < 44.0.0 +cryptography < 45.0.0 fastapi[all] >= 0.94.0 paramiko >= 3.3.1 prettytable >= 3.9.0 From 0a57890b04d2faf7286c27c0a3401bbea6368772 Mon Sep 17 00:00:00 2001 From: Gabriel Willen <91307534+gabewillen@users.noreply.github.com> Date: Sat, 7 Dec 2024 10:43:48 -0600 Subject: [PATCH 2/3] Feature/E-2131 Utility function for resolving model-cache paths from Huggingface repositories (#377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added a utility for resolving model cache paths from a huggingface repository * Added a TODO for the `path_template` key word argument * added unit tests for model cache resolver * fixed module documentation * resolve to None when a repository is improperly formatted * fixed comment wording --------- Co-authored-by: Dean QuiƱanola --- runpod/serverless/utils/rp_model_cache.py | 47 ++++++++++++++++ .../test_utils/test_model_cache.py | 53 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 runpod/serverless/utils/rp_model_cache.py create mode 100644 tests/test_serverless/test_utils/test_model_cache.py diff --git a/runpod/serverless/utils/rp_model_cache.py b/runpod/serverless/utils/rp_model_cache.py new file mode 100644 index 00000000..b16694ca --- /dev/null +++ b/runpod/serverless/utils/rp_model_cache.py @@ -0,0 +1,47 @@ +"""Utility function for transforming HuggingFace repositories into model-cache paths""" + +import typing +from runpod.serverless.modules.rp_logger import RunPodLogger + +log = RunPodLogger() + + +def resolve_model_cache_path_from_hugginface_repository( + huggingface_repository: str, + /, + path_template: str = "/runpod/cache/{model}/{revision}", # TODO: Should we just hardcode this? +) -> typing.Union[str, None]: + """ + Resolves the model-cache path for a HuggingFace model based on its repository string. + + Args: + huggingface_repository (str): Repository string in format "model_name:revision" or + "org/model_name:revision". If no revision is specified, + "main" is used. For example: + - "runwayml/stable-diffusion-v1-5:experimental" + - "runwayml/stable-diffusion-v1-5" (uses "main" revision) + - "stable-diffusion-v1-5:main" + path_template (str, optional): Template string for the cache path. Must contain {model} + and {revision} placeholders. Defaults to "/runpod/cache/{model}/{revision}". + + Returns: + str | None: Absolute path where the model is cached, following the template provided in path_template. Returns None if no model name could be extracted. + + Examples: + >>> resolve_model_cache_path_from_hugginface_repository("runwayml/stable-diffusion-v1-5:experimental") + "/runpod/cache/runwayml/stable-diffusion-v1-5/experimental" + >>> resolve_model_cache_path_from_hugginface_repository("runwayml/stable-diffusion-v1-5") + "/runpod/cache/runwayml/stable-diffusion-v1-5/main" + >>> resolve_model_cache_path_from_hugginface_repository(":experimental") + None + """ + model, *revision = huggingface_repository.rsplit(":", 1) + if not model: + # We could throw an exception here but returning None allows us to filter a list of repositories without needing a try/except block + log.warn( # type: ignore in strict mode the typechecker complains about this method being partially unknown + f'Unable to resolve the model-cache path for "{huggingface_repository}"' + ) + return None + return path_template.format( + model=model, revision=revision[0] if revision else "main" + ) diff --git a/tests/test_serverless/test_utils/test_model_cache.py b/tests/test_serverless/test_utils/test_model_cache.py new file mode 100644 index 00000000..28cea0ab --- /dev/null +++ b/tests/test_serverless/test_utils/test_model_cache.py @@ -0,0 +1,53 @@ +import unittest + +from runpod.serverless.utils.rp_model_cache import ( + resolve_model_cache_path_from_hugginface_repository, +) + + +class TestModelCache(unittest.TestCase): + """Tests for rp_model_cache""" + + def test_with_revision(self): + """Test with a revision""" + path = resolve_model_cache_path_from_hugginface_repository( + "runwayml/stable-diffusion-v1-5:experimental" + ) + self.assertEqual( + path, "/runpod/cache/runwayml/stable-diffusion-v1-5/experimental" + ) + + def test_without_revision(self): + """Test without a revision""" + path = resolve_model_cache_path_from_hugginface_repository( + "runwayml/stable-diffusion-v1-5" + ) + self.assertEqual(path, "/runpod/cache/runwayml/stable-diffusion-v1-5/main") + + def test_with_multiple_colons(self): + """Test with multiple colons""" + path = resolve_model_cache_path_from_hugginface_repository( + "runwayml/stable-diffusion:v1-5:experimental" + ) + self.assertEqual( + path, "/runpod/cache/runwayml/stable-diffusion:v1-5/experimental" + ) + + def test_with_custom_path_template(self): + """Test with a custom path template""" + path = resolve_model_cache_path_from_hugginface_repository( + "runwayml/stable-diffusion-v1-5:experimental", + "/my-custom-model-cache/{model}/{revision}", + ) + self.assertEqual( + path, "/my-custom-model-cache/runwayml/stable-diffusion-v1-5/experimental" + ) + + def test_with_missing_model_name(self): + """Test with a missing model name""" + path = resolve_model_cache_path_from_hugginface_repository(":experimental") + self.assertIsNone(path) + + +if __name__ == "__main__": + unittest.main() From d7a2131b34e567669135f41934b25ac10938553f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dean=20Qui=C3=B1anola?= Date: Tue, 10 Dec 2024 13:48:17 -0800 Subject: [PATCH 3/3] fix: streamed errors were previously swallowed (#384) This created false-positive completed tasks --- runpod/serverless/modules/rp_job.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runpod/serverless/modules/rp_job.py b/runpod/serverless/modules/rp_job.py index 22f377c2..f7f8feba 100644 --- a/runpod/serverless/modules/rp_job.py +++ b/runpod/serverless/modules/rp_job.py @@ -126,9 +126,15 @@ async def handle_job(session: ClientSession, config: Dict[str, Any], job) -> dic job_result = {"output": []} async for stream_output in generator_output: log.debug(f"Stream output: {stream_output}", job["id"]) - if "error" in stream_output: + + if type(stream_output.get("output")) == dict: + if stream_output["output"].get("error"): + stream_output = {"error": str(stream_output["output"]["error"])} + + if stream_output.get("error"): job_result = stream_output break + if config.get("return_aggregate_stream", False): job_result["output"].append(stream_output["output"])