diff --git a/CHANGELOG.md b/CHANGELOG.md index 921950955..00745b4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## \[0.34.2\] - 2024-11-07 + +### Fixed + +- Restore human-friendly default `ImageArtifact` and `AudioArtifact` names with file type extension. + ## \[0.34.1\] - 2024-11-05 ### Added diff --git a/griptape/artifacts/audio_artifact.py b/griptape/artifacts/audio_artifact.py index e9e38858a..4796e99de 100644 --- a/griptape/artifacts/audio_artifact.py +++ b/griptape/artifacts/audio_artifact.py @@ -1,5 +1,9 @@ from __future__ import annotations +import random +import string +import time + from attrs import define, field from griptape.artifacts import BlobArtifact @@ -15,6 +19,12 @@ class AudioArtifact(BlobArtifact): format: str = field(kw_only=True, metadata={"serializable": True}) + def __attrs_post_init__(self) -> None: + # Generating the name string requires attributes set by child classes. + # This waits until all attributes are available before generating a name. + if self.name == self.id: + self.name = self.make_name() + @property def mime_type(self) -> str: return f"audio/{self.format}" @@ -24,3 +34,9 @@ def to_bytes(self) -> bytes: def to_text(self) -> str: return f"Audio, format: {self.format}, size: {len(self.value)} bytes" + + def make_name(self) -> str: + entropy = "".join(random.choices(string.ascii_lowercase + string.digits, k=4)) + fmt_time = time.strftime("%y%m%d%H%M%S", time.localtime()) + + return f"audio_artifact_{fmt_time}_{entropy}.{self.format}" diff --git a/griptape/artifacts/image_artifact.py b/griptape/artifacts/image_artifact.py index 36170ee0d..cf81d9cb5 100644 --- a/griptape/artifacts/image_artifact.py +++ b/griptape/artifacts/image_artifact.py @@ -1,5 +1,9 @@ from __future__ import annotations +import random +import string +import time + from attrs import define, field from griptape.artifacts import BlobArtifact @@ -19,6 +23,12 @@ class ImageArtifact(BlobArtifact): width: int = field(kw_only=True, metadata={"serializable": True}) height: int = field(kw_only=True, metadata={"serializable": True}) + def __attrs_post_init__(self) -> None: + # Generating the name string requires attributes set by child classes. + # This waits until all attributes are available before generating a name. + if self.name == self.id: + self.name = self.make_name() + @property def mime_type(self) -> str: return f"image/{self.format}" @@ -28,3 +38,9 @@ def to_bytes(self) -> bytes: def to_text(self) -> str: return f"Image, format: {self.format}, size: {len(self.value)} bytes" + + def make_name(self) -> str: + entropy = "".join(random.choices(string.ascii_lowercase + string.digits, k=4)) + fmt_time = time.strftime("%y%m%d%H%M%S", time.localtime()) + + return f"image_artifact_{fmt_time}_{entropy}.{self.format}" diff --git a/pyproject.toml b/pyproject.toml index ef87db34c..ca7dd6a12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "griptape" -version = "0.34.1" +version = "0.34.2" description = "Modular Python framework for LLM workflows, tools, memory, and data." authors = ["Griptape "] license = "Apache 2.0" diff --git a/tests/unit/artifacts/test_audio_artifact.py b/tests/unit/artifacts/test_audio_artifact.py index aab6af630..d7a11ad35 100644 --- a/tests/unit/artifacts/test_audio_artifact.py +++ b/tests/unit/artifacts/test_audio_artifact.py @@ -35,3 +35,7 @@ def test_deserialization(self, audio_artifact): assert deserialized_artifact.format == "pcm" assert deserialized_artifact.meta["model"] == "provider/model" assert deserialized_artifact.meta["prompt"] == "two words" + + def test_name(self, audio_artifact): + assert audio_artifact.name.startswith("audio_artifact_") + assert audio_artifact.name.endswith(audio_artifact.format) diff --git a/tests/unit/artifacts/test_image_artifact.py b/tests/unit/artifacts/test_image_artifact.py index a632953ae..559697e9a 100644 --- a/tests/unit/artifacts/test_image_artifact.py +++ b/tests/unit/artifacts/test_image_artifact.py @@ -40,3 +40,7 @@ def test_deserialization(self, image_artifact): assert deserialized_artifact.height == 512 assert deserialized_artifact.meta["model"] == "openai/dalle2" assert deserialized_artifact.meta["prompt"] == "a cute cat" + + def test_name(self, image_artifact): + assert image_artifact.name.startswith("image_artifact_") + assert image_artifact.name.endswith(image_artifact.format)