Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JsonArtifact #937

Merged
merged 5 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

## Json

A [JsonArtifact](../../reference/griptape/artifacts/json_artifact.md) is used for passing JSON-serliazable data around the framework. Anything passed to `value` will be converted using `json.dumps(json.loads(value))`.
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
21 changes: 21 additions & 0 deletions griptape/artifacts/json_artifact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from __future__ import annotations

import json
from typing import 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):
value: Json = field(converter=lambda v: json.loads(json.dumps(v)), metadata={"serializable": True})

def to_text(self) -> str:
return json.dumps(self.value)

def __add__(self, other: BaseArtifact) -> JsonArtifact:
raise NotImplementedError
29 changes: 29 additions & 0 deletions tests/unit/artifacts/test_json_artifact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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"}))
assert JsonArtifact({"foo": 1}).value == json.loads(json.dumps({"foo": 1}))
assert JsonArtifact({"foo": 1.0}).value == json.loads(json.dumps({"foo": 1.0}))
assert JsonArtifact({"foo": True}).value == json.loads(json.dumps({"foo": True}))
assert JsonArtifact({"foo": None}).value == json.loads(json.dumps({"foo": None}))
assert JsonArtifact([{"foo": {"bar": "baz"}}]).value == json.loads(json.dumps([{"foo": {"bar": "baz"}}]))
assert JsonArtifact(None).value == json.loads(json.dumps(None))
assert JsonArtifact("foo").value == json.loads(json.dumps("foo"))

def test___add__(self):
with pytest.raises(NotImplementedError):
JsonArtifact({"foo": "bar"}) + TextArtifact("invalid json")

def test_to_text(self):
assert JsonArtifact({"foo": "bar"}).to_text() == json.dumps({"foo": "bar"})
assert JsonArtifact({"foo": 1}).to_text() == json.dumps({"foo": 1})
assert JsonArtifact({"foo": 1.0}).to_text() == json.dumps({"foo": 1.0})
assert JsonArtifact({"foo": True}).to_text() == json.dumps({"foo": True})
assert JsonArtifact({"foo": None}).to_text() == json.dumps({"foo": None})
assert JsonArtifact([{"foo": {"bar": "baz"}}]).to_text() == json.dumps([{"foo": {"bar": "baz"}}])
Loading