Skip to content

Commit

Permalink
Merge branch 'main' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
collindutter committed Aug 2, 2024
2 parents d577ac5 + fb5e21c commit fd7e2eb
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 79 deletions.
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Parameter `count` for `QdrantVectorStoreDriver.query` now optional as per documentation.
- Path issues on Windows with `LocalFileManagerDriver` and `AmazonS3FileManagerDriver`.

## [0.29.0] - 2024-07-30

### Added
- Native function calling support to `OpenAiChatPromptDriver`, `AzureOpenAiChatPromptDriver`, `AnthropicPromptDriver`, `AmazonBedrockPromptDriver`, `GooglePromptDriver`, `OllamaPromptDriver`, and `CoherePromptDriver`.
- `OllamaEmbeddingDriver` for generating embeddings with Ollama.
- `GriptapeCloudKnowledgeBaseVectorStoreDriver` to query Griptape Cloud Knowledge Bases.
- `GriptapeCloudEventListenerDriver.api_key` defaults to the value in the `GT_CLOUD_API_KEY` environment variable.
- `BaseObservabilityDriver` as the base class for all Observability Drivers.
- `DummyObservabilityDriver` as a no-op Observability Driver.
- `OpenTelemetryObservabilityDriver` for sending observability data to an open telemetry collector or vendor.
- `GriptapeCloudObservabilityDriver` for sending observability data to Griptape Cloud.
- `DatadogObservabilityDriver` for sending observability data to a Datadog Agent.
- `Observability` context manager for enabling observability and configuring which Observability Driver to use.
- `@observable` decorator for selecting which functions/methods to provide observability for.
- `GenericArtifact` for storing any data.
- `BaseTextArtifact` for text-based Artifacts to subclass.

### Changed
- **BREAKING**: `BaseVectorStoreDriver.upsert_text_artifacts` optional arguments are now keyword-only arguments.
- **BREAKING**: `BaseVectorStoreDriver.upsert_text_artifact` optional arguments are now keyword-only arguments.
- **BREAKING**: `BaseVectorStoreDriver.upsert_text` optional arguments are now keyword-only arguments.
- **BREAKING**: `BaseVectorStoreDriver.does_entry_exist` optional arguments are now keyword-only arguments.
- **BREAKING**: `BaseVectorStoreDriver.load_artifacts` optional arguments are now keyword-only arguments.
- **BREAKING**: `BaseVectorStoreDriver.upsert_vector` optional arguments are now keyword-only arguments.
- **BREAKING**: `BaseVectorStoreDriver.query` optional arguments are now keyword-only arguments.
- **BREAKING**: `EventListener.publish_event`'s `flush` argument is now a keyword-only argument.
- **BREAKING**: `BaseEventListenerDriver.publish_event`'s `flush` argument is now a keyword-only argument.
- **BREAKING**: Renamed `DummyException` to `DummyError` for pep8 naming compliance.
- **BREAKING**: Migrate to `sqlalchemy` 2.0.
- **BREAKING**: Make `sqlalchemy` an optional dependency.
- **BREAKING**: Renamed `drivers-sql-redshift` to `drivers-sql-amazon-redshift`
- **BREAKING**: Renamed `drivers-prompt-huggingface` extra to `drivers-prompt-huggingface-hub`.
- **BREAKING**: Renamed `drivers-vector-postgresql` extra to `drivers-vector-pgvector`.
- **BREAKING**: Update `marqo` dependency to `^3.7.0`.
- **BREAKING**: Removed `drivers-sql-postgresql` extra. Use `drivers-sql` extra and install necessary drivers (i.e. `psycopg2`) separately.
- Removed unnecessary `sqlalchemy-redshift` dependency in `drivers-sql-amazon-redshift` extra.
- Removed unnecessary `transformers` dependency in `drivers-prompt-huggingface` extra.
- Removed unnecessary `huggingface-hub` dependency in `drivers-prompt-huggingface-pipeline` extra.
- `CsvRowArtifact` now inherits from `BaseTextArtifact`.
- `TextArtifact` now inherits from `BaseTextArtifact`.

### Fixed
- Parameter `count` for `QdrantVectorStoreDriver.query` now optional as per documentation.
- Path issues on Windows with `LocalFileManagerDriver` and `AmazonS3FileManagerDriver`.

## [0.28.2] - 2024-07-12
### Fixed
- Conversation Memory being incorrectly inserted into the `PromptTask.prompt_stack` when no system content is present.
Expand Down
2 changes: 2 additions & 0 deletions griptape/artifacts/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .base_artifact import BaseArtifact
from .base_text_artifact import BaseTextArtifact
from .error_artifact import ErrorArtifact
from .info_artifact import InfoArtifact
from .text_artifact import TextArtifact
Expand All @@ -15,6 +16,7 @@

__all__ = [
"BaseArtifact",
"BaseTextArtifact",
"ErrorArtifact",
"InfoArtifact",
"TextArtifact",
Expand Down
34 changes: 34 additions & 0 deletions griptape/artifacts/base_text_artifact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Optional

from attrs import define, field

from griptape.artifacts import BaseArtifact

if TYPE_CHECKING:
from griptape.drivers import BaseEmbeddingDriver
from griptape.tokenizers import BaseTokenizer


@define
class BaseTextArtifact(BaseArtifact):
encoding: str = field(default="utf-8", kw_only=True)
encoding_error_handler: str = field(default="strict", kw_only=True)
_embedding: list[float] = field(factory=list, kw_only=True)

@property
def embedding(self) -> Optional[list[float]]:
return None if len(self._embedding) == 0 else self._embedding

def generate_embedding(self, driver: BaseEmbeddingDriver) -> Optional[list[float]]:
self._embedding.clear()
self._embedding.extend(driver.embed_string(str(self.value)))

return self.embedding

def token_count(self, tokenizer: BaseTokenizer) -> int:
return tokenizer.count_tokens(str(self.value))

def to_bytes(self) -> bytes:
return str(self.value).encode(encoding=self.encoding, errors=self.encoding_error_handler)
8 changes: 4 additions & 4 deletions griptape/artifacts/csv_row_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@


@define
class CsvRowArtifact(TextArtifact):
class CsvRowArtifact(BaseTextArtifact):
value: dict[str, str] = field(converter=BaseArtifact.value_to_dict, metadata={"serializable": True})
delimiter: str = field(default=",", kw_only=True, metadata={"serializable": True})

def __add__(self, other: BaseArtifact) -> CsvRowArtifact:
return CsvRowArtifact(self.value | other.value)

def __bool__(self) -> bool:
return len(self) > 0

def to_text(self) -> str:
with io.StringIO() as csvfile:
writer = csv.DictWriter(
Expand All @@ -28,6 +31,3 @@ def to_text(self) -> str:
writer.writerow(self.value)

return csvfile.getvalue().strip()

def __bool__(self) -> bool:
return len(self) > 0
24 changes: 2 additions & 22 deletions griptape/artifacts/text_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,15 @@
from griptape.artifacts import BaseArtifact

if TYPE_CHECKING:
from griptape.drivers import BaseEmbeddingDriver
from griptape.tokenizers import BaseTokenizer
from griptape.artifacts import BaseArtifact


@define
class TextArtifact(BaseArtifact):
class TextArtifact(BaseTextArtifact):
value: str = field(converter=str, metadata={"serializable": True})
encoding: str = field(default="utf-8", kw_only=True)
encoding_error_handler: str = field(default="strict", kw_only=True)
_embedding: list[float] = field(factory=list, kw_only=True)

@property
def embedding(self) -> Optional[list[float]]:
return None if len(self._embedding) == 0 else self._embedding

def __add__(self, other: BaseArtifact) -> TextArtifact:
return TextArtifact(self.value + other.value)

def __bool__(self) -> bool:
return bool(self.value.strip())

def generate_embedding(self, driver: BaseEmbeddingDriver) -> Optional[list[float]]:
self._embedding.clear()
self._embedding.extend(driver.embed_string(str(self.value)))

return self.embedding

def token_count(self, tokenizer: BaseTokenizer) -> int:
return tokenizer.count_tokens(str(self.value))

def to_bytes(self) -> bytes:
return self.value.encode(encoding=self.encoding, errors=self.encoding_error_handler)
26 changes: 11 additions & 15 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "griptape"
version = "0.28.2"
version = "0.29.0"
description = "Modular Python framework for LLM workflows, tools, memory, and data."
authors = ["Griptape <[email protected]>"]
license = "Apache 2.0"
Expand Down Expand Up @@ -59,7 +59,7 @@ opentelemetry-api = {version = "^1.25.0", optional = true}
opentelemetry-instrumentation = {version = "^0.46b0", optional = true}
opentelemetry-instrumentation-threading = {version = "^0.46b0", optional = true}
opentelemetry-exporter-otlp-proto-http = {version = "^1.25.0", optional = true}
diffusers = {git = "https://github.com/griptape-ai/diffusers.git", branch = "main", optional = true}
diffusers = {version = "^0.29.1", optional = true}
accelerate = {version = "^0.32.1", optional = true}
sentencepiece = {version = "^0.2.0", optional = true}
torch = {version = "^2.3.1", optional = true}
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/artifacts/test_base_text_artifact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import pytest

from griptape.artifacts import TextArtifact
from griptape.tokenizers import OpenAiTokenizer
from tests.mocks.mock_embedding_driver import MockEmbeddingDriver


class TestBaseTextArtifact:
def test_generate_embedding(self):
assert TextArtifact("foobar").generate_embedding(MockEmbeddingDriver()) == [0, 1]

def test_embedding(self):
artifact = TextArtifact("foobar")

assert artifact.embedding is None
assert artifact.generate_embedding(MockEmbeddingDriver()) == [0, 1]
assert artifact.embedding == [0, 1]

def test_to_bytes_encoding(self):
assert (
TextArtifact("ß", name="foobar.txt", encoding="ascii", encoding_error_handler="backslashreplace").to_bytes()
== b"\\xdf"
)

def test_to_bytes_encoding_error(self):
with pytest.raises(ValueError):
assert TextArtifact("ß", encoding="ascii").to_bytes()

def test_token_count(self):
assert (
TextArtifact("foobarbaz").token_count(
OpenAiTokenizer(model=OpenAiTokenizer.DEFAULT_OPENAI_GPT_3_CHAT_MODEL)
)
== 2
)
4 changes: 0 additions & 4 deletions tests/unit/artifacts/test_csv_row_artifact.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from griptape.artifacts import CsvRowArtifact
from tests.mocks.mock_embedding_driver import MockEmbeddingDriver


class TestCsvRowArtifact:
Expand All @@ -14,9 +13,6 @@ def test___add__(self):
"test2": "bar",
}

def test_generate_embedding(self):
assert CsvRowArtifact({"test1": "foo"}).generate_embedding(MockEmbeddingDriver()) == [0, 1]

def test_to_text(self):
assert CsvRowArtifact({"test1": "foo|bar", "test2": 1}, delimiter="|").to_text() == '"foo|bar"|1'

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/artifacts/test_list_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_child_type(self):

def test_is_type(self):
assert ListArtifact([TextArtifact("foo")]).is_type(TextArtifact)
assert ListArtifact([CsvRowArtifact({"foo": "bar"})]).is_type(TextArtifact)
assert ListArtifact([CsvRowArtifact({"foo": "bar"})]).is_type(BaseTextArtifact)
assert ListArtifact([CsvRowArtifact({"foo": "bar"})]).is_type(CsvRowArtifact)

def test_has_items(self):
Expand Down
31 changes: 0 additions & 31 deletions tests/unit/artifacts/test_text_artifact.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,6 @@ def test_value_type_conversion(self):
def test___add__(self):
assert (TextArtifact("foo") + TextArtifact("bar")).value == "foobar"

def test_generate_embedding(self):
assert TextArtifact("foobar").generate_embedding(MockEmbeddingDriver()) == [0, 1]

def test_embedding(self):
artifact = TextArtifact("foobar")

assert artifact.embedding is None
assert artifact.generate_embedding(MockEmbeddingDriver()) == [0, 1]
assert artifact.embedding == [0, 1]

def test_to_text(self):
assert TextArtifact("foobar").to_text() == "foobar"

def test_to_bytes_encoding(self):
assert (
TextArtifact("ß", name="foobar.txt", encoding="ascii", encoding_error_handler="backslashreplace").to_bytes()
== b"\\xdf"
)

def test_to_bytes_encoding_error(self):
with pytest.raises(ValueError):
assert TextArtifact("ß", encoding="ascii").to_bytes()

def test_token_count(self):
assert (
TextArtifact("foobarbaz").token_count(
OpenAiTokenizer(model=OpenAiTokenizer.DEFAULT_OPENAI_GPT_3_CHAT_MODEL)
)
== 2
)

def test_to_dict(self):
assert TextArtifact("foobar").to_dict()["value"] == "foobar"

Expand Down

0 comments on commit fd7e2eb

Please sign in to comment.