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

Release/v1.0 #1454

Closed
wants to merge 8 commits into from
Closed
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
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## [1.0.2] - 2024-12-16

### Fixed

- Exception when calling `Structure.to_json()` after it has run.
- `Agent` unintentionally modifying `stream` for all Prompt Drivers.
- `StructureVisualizer.base_url` for setting the base URL on the url generated by `StructureVisualizer.to_url()`.
- `StructureVisualizer.query_params` for setting query parameters on the url generated by `StructureVisualizer.to_url()`.
- Parsing `ActionCallDeltaMessageContent`s with empty string `partial_input`s.

## [1.0.1] - 2024-12-11

### Fixed
- Prompt Driver image input doc example not rendering

## [1.0.0] - 2024-12-09

Expand Down
2 changes: 1 addition & 1 deletion docs/griptape-framework/drivers/prompt-drivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Or use them independently:
You can pass images to the Driver if the model supports it:

```python
--8<-- "docs/griptape-framework/drivers/src/prompt_driver_images.py"
--8<-- "docs/griptape-framework/drivers/src/prompt_drivers_images.py"
```

## Prompt Drivers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
filename = "sample1.txt"
content = "This is the content of sample1.txt"

Path(filename).write_text(filename)
Path(filename).write_text(content)

# Now, read content from the file 'sample1.txt' using the agent's command
agent.run("Can you get me the sample1.txt file?")
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def from_deltas(cls, deltas: Sequence[BaseDeltaMessageContent]) -> ActionCallMes

if tag is not None and name is not None and path is not None:
try:
parsed_input = json.loads(json_input)
parsed_input = json.loads(json_input or "{}")
except json.JSONDecodeError as exc:
raise ValueError("Invalid JSON input for ToolAction") from exc
action = ToolAction(tag=tag, name=name, path=path, input=parsed_input)
Expand Down
7 changes: 5 additions & 2 deletions griptape/structures/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import TYPE_CHECKING, Callable, Optional, Union

from attrs import Attribute, Factory, define, field
from attrs import Attribute, Factory, define, evolve, field

from griptape.artifacts.text_artifact import TextArtifact
from griptape.common import observable
Expand All @@ -24,7 +24,10 @@ class Agent(Structure):
)
stream: bool = field(default=Factory(lambda: Defaults.drivers_config.prompt_driver.stream), kw_only=True)
prompt_driver: BasePromptDriver = field(
default=Factory(lambda: Defaults.drivers_config.prompt_driver), kw_only=True
default=Factory(
lambda self: evolve(Defaults.drivers_config.prompt_driver, stream=self.stream), takes_self=True
),
kw_only=True,
)
tools: list[BaseTool] = field(factory=list, kw_only=True)
max_meta_memory_entries: Optional[int] = field(default=20, kw_only=True)
Expand Down
4 changes: 3 additions & 1 deletion griptape/tasks/base_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import uuid
from abc import ABC, abstractmethod
from copy import deepcopy
from enum import Enum
from typing import TYPE_CHECKING, Any, Optional

Expand Down Expand Up @@ -194,7 +195,8 @@ def try_run(self) -> BaseArtifact: ...

@property
def full_context(self) -> dict[str, Any]:
context = self.context
# Need to deep copy so that the serialized context doesn't contain non-serializable data
context = deepcopy(self.context)
if self.structure is not None:
context.update(self.structure.context(self))

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "griptape"
version = "1.0.0"
version = "1.0.2"
description = "Modular Python framework for LLM workflows, tools, memory, and data."
authors = ["Griptape <[email protected]>"]
license = "Apache 2.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def test_from_deltas(self):
ActionCallDeltaMessageContent(tag="testtag"),
ActionCallDeltaMessageContent(name="TestName"),
ActionCallDeltaMessageContent(path="test_tag"),
ActionCallDeltaMessageContent(partial_input=""),
ActionCallDeltaMessageContent(partial_input='{"foo":'),
ActionCallDeltaMessageContent(partial_input='"bar"}'),
]
Expand Down
52 changes: 8 additions & 44 deletions tests/unit/structures/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,50 +255,6 @@ def test_task_outputs(self):
assert len(agent.task_outputs) == 1
assert agent.task_outputs[task.id] == task.output

def test_to_dict(self):
task = PromptTask("test prompt")
agent = Agent(prompt_driver=MockPromptDriver())
agent.add_task(task)
expected_agent_dict = {
"type": "Agent",
"id": agent.id,
"tasks": [
{
"type": agent.tasks[0].type,
"id": agent.tasks[0].id,
"state": str(agent.tasks[0].state),
"parent_ids": agent.tasks[0].parent_ids,
"child_ids": agent.tasks[0].child_ids,
"max_meta_memory_entries": agent.tasks[0].max_meta_memory_entries,
"context": agent.tasks[0].context,
}
],
"conversation_memory": {
"type": agent.conversation_memory.type,
"runs": agent.conversation_memory.runs,
"meta": agent.conversation_memory.meta,
"max_runs": agent.conversation_memory.max_runs,
},
}
assert agent.to_dict() == expected_agent_dict

def test_from_dict(self):
task = PromptTask("test prompt")
agent = Agent(prompt_driver=MockPromptDriver())
agent.add_task(task)

serialized_agent = agent.to_dict()
assert isinstance(serialized_agent, dict)

deserialized_agent = Agent.from_dict(serialized_agent)
assert isinstance(deserialized_agent, Agent)

assert deserialized_agent.task_outputs[task.id] is None
deserialized_agent.run()

assert len(deserialized_agent.task_outputs) == 1
assert deserialized_agent.task_outputs[task.id].value == "mock output"

def test_runnable_mixin(self):
mock_on_before_run = Mock()
mock_after_run = Mock()
Expand All @@ -320,3 +276,11 @@ def test_is_running(self):
task.state = BaseTask.State.RUNNING

assert agent.is_running()

def test_stream_mutation(self):
prompt_driver = MockPromptDriver()
agent = Agent(prompt_driver=MockPromptDriver(), stream=True)

assert isinstance(agent.tasks[0], PromptTask)
assert agent.tasks[0].prompt_driver.stream is True
assert agent.tasks[0].prompt_driver is not prompt_driver
64 changes: 64 additions & 0 deletions tests/unit/structures/test_structure.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest

from griptape.structures import Agent, Pipeline
from griptape.tasks import PromptTask
from tests.mocks.mock_prompt_driver import MockPromptDriver


class TestStructure:
Expand All @@ -16,3 +18,65 @@ def test_output(self):
ValueError, match="Structure's output Task has no output. Run the Structure to generate output."
):
assert agent.output

def test_to_dict(self):
task = PromptTask("test prompt")
agent = Agent(prompt_driver=MockPromptDriver())
agent.add_task(task)
expected_agent_dict = {
"type": "Agent",
"id": agent.id,
"tasks": [
{
"type": agent.tasks[0].type,
"id": agent.tasks[0].id,
"state": str(agent.tasks[0].state),
"parent_ids": agent.tasks[0].parent_ids,
"child_ids": agent.tasks[0].child_ids,
"max_meta_memory_entries": agent.tasks[0].max_meta_memory_entries,
"context": agent.tasks[0].context,
}
],
"conversation_memory": {
"type": agent.conversation_memory.type,
"runs": agent.conversation_memory.runs,
"meta": agent.conversation_memory.meta,
"max_runs": agent.conversation_memory.max_runs,
},
"conversation_memory_strategy": agent.conversation_memory_strategy,
}
assert agent.to_dict() == expected_agent_dict

agent.run()

expected_agent_dict = {
**expected_agent_dict,
"tasks": [
{
**expected_agent_dict["tasks"][0],
"state": str(agent.tasks[0].state),
}
],
"conversation_memory": {
**expected_agent_dict["conversation_memory"],
"runs": agent.conversation_memory.to_dict()["runs"],
},
}
assert agent.to_dict() == expected_agent_dict

def test_from_dict(self):
task = PromptTask("test prompt")
agent = Agent(prompt_driver=MockPromptDriver())
agent.add_task(task)

serialized_agent = agent.to_dict()
assert isinstance(serialized_agent, dict)

deserialized_agent = Agent.from_dict(serialized_agent)
assert isinstance(deserialized_agent, Agent)

assert deserialized_agent.task_outputs[task.id] is None
deserialized_agent.run()

assert len(deserialized_agent.task_outputs) == 1
assert deserialized_agent.task_outputs[task.id].value == "mock output"
Loading