Skip to content

Commit

Permalink
Add JsonArtifact
Browse files Browse the repository at this point in the history
  • Loading branch information
vachillo committed Aug 15, 2024
1 parent babc56a commit 5a1c7b6
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Global config, `griptape.config.config`, for setting global configuration defaults.
- Unique name generation for all `RagEngine` modules.
- Support for bitshift composition in `BaseTask` for adding parent/child tasks.
- `JsonArtifact` for handling de/seralization of values.

### Changed
- **BREAKING**: Removed all uses of `EventPublisherMixin` in favor of `event_bus`.
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ install/core: ## Install core dependencies.
.PHONY: install/all
install/all: ## Install all dependencies.
@poetry install --with dev --with test --with docs --all-extras
@poetry run pre-commit install

.PHONY: install/dev
install/dev: ## Install dev dependencies.
Expand Down
4 changes: 4 additions & 0 deletions docs/griptape-framework/data/artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ A [BooleanArtifact](../../reference/griptape/artifacts/boolean_artifact.md) is u
A [GenericArtifact](../../reference/griptape/artifacts/generic_artifact.md) can be used as an escape hatch for passing any type of data around the framework.
It is generally not recommended to use this Artifact type, but it can be used in a handful of situations where no other Artifact type fits the data being passed.
See [talking to a video](../../examples/talk-to-a-video.md) for an example of using a `GenericArtifact` to pass a Gemini-specific video file.

## JsonArtifact

A [JsonArtifact](../../reference/griptape/artifacts/json_artifact.md) is used for passing JSON-serliazable data around the framework. Any object passed to `obj` will be converted using `json.dumps`, and `JsonArtifact.value` will be the value of `json.loads(json.dumps(obj))`.
2 changes: 2 additions & 0 deletions griptape/artifacts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .error_artifact import ErrorArtifact
from .info_artifact import InfoArtifact
from .text_artifact import TextArtifact
from .json_artifact import JsonArtifact
from .blob_artifact import BlobArtifact
from .boolean_artifact import BooleanArtifact
from .csv_row_artifact import CsvRowArtifact
Expand All @@ -18,6 +19,7 @@
"ErrorArtifact",
"InfoArtifact",
"TextArtifact",
"JsonArtifact",
"BlobArtifact",
"BooleanArtifact",
"CsvRowArtifact",
Expand Down
35 changes: 35 additions & 0 deletions griptape/artifacts/json_artifact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

import json
from typing import Any, Union

from attrs import define, field

from griptape.artifacts import BaseArtifact

Json = Union[dict[str, "Json"], list["Json"], str, int, float, bool, None]


@define
class JsonArtifact(BaseArtifact):
_obj: Any = field(metadata={"serializable": True}, alias="obj")
value: dict = field(init=False, metadata={"serializable": True})
_json_str: str = field(init=False, metadata={"serializable": True})

def __attrs_post_init__(self) -> None:
if self._obj is None:
self._obj = {}

Check warning on line 21 in griptape/artifacts/json_artifact.py

View check run for this annotation

Codecov / codecov/patch

griptape/artifacts/json_artifact.py#L21

Added line #L21 was not covered by tests
self._json_str = json.dumps(self._obj)
self.value = json.loads(self._json_str)

def to_text(self) -> str:
return self._json_str

def __add__(self, other: BaseArtifact) -> JsonArtifact:
if not isinstance(other, JsonArtifact):
raise ValueError(f"Cannot add {type(self)} and {type(other)}")

from griptape.utils import dict_merge

self.value = dict_merge(self.value, other.value)
return self
25 changes: 25 additions & 0 deletions tests/unit/artifacts/test_json_artifact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json

import pytest

from griptape.artifacts import JsonArtifact, TextArtifact


class TestJsonArtifact:
def test_value_type_conversion(self):
assert JsonArtifact({"foo": "bar"}).value == json.loads(json.dumps({"foo": "bar"}))

def test___add__(self):
assert (JsonArtifact({"foo": "bar"}) + JsonArtifact({"value": "baz"})).value == JsonArtifact(
{"foo": "bar", "value": "baz"}
).value

new = JsonArtifact({"foo": "bar"}) + JsonArtifact({"value": "baz"})
assert new.value == {"foo": "bar", "value": "baz"}

assert (JsonArtifact({"foo": "bar"}) + JsonArtifact({"foo": "baz"})).value == JsonArtifact({"foo": "baz"}).value
with pytest.raises(ValueError):
JsonArtifact({"foo": "bar"}) + TextArtifact("invalid json")

def test_to_text(self):
assert JsonArtifact({"foo": "bar"}).to_text() == json.dumps({"foo": "bar"})

0 comments on commit 5a1c7b6

Please sign in to comment.