-
Notifications
You must be signed in to change notification settings - Fork 176
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
Feature/native tool calling #791
Feature/native tool calling #791
Conversation
5205b8f
to
32cd862
Compare
Codecov ReportAttention: Patch coverage is 📢 Thoughts on this report? Let us know! |
e758ebe
to
fc63600
Compare
|
||
|
||
@define | ||
class ActionChunkArtifact(TextArtifact, SerializableMixin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really don't like this class, but it's required for when we're streaming tool usage. Should we change ActionsArtifact.Action
into ActionArtifact
? Then we can yield ActionArtifact
s much like we yield TextArtifact
s during streaming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying that you would yield an ActionArtifact
after getting all the ActionChunkArtifact
for that action, instead of yielding the ActionChunkArtifact
s themselves? (Or would you yield both?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regardless, I think moving ActionsArtifact.Action
into a non-nested ActionArtifact
is a good idea.
72e890c
to
99d2253
Compare
8fb4345
to
4766361
Compare
text = response.content[0].text if response.content and response.content[0].type == "text" else "" | ||
tool_uses = [content for content in response.content if content.type == "tool_use"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it guaranteed that the text always appears as the first element?
|
||
|
||
@define | ||
class ActionChunkArtifact(TextArtifact, SerializableMixin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you saying that you would yield an ActionArtifact
after getting all the ActionChunkArtifact
for that action, instead of yielding the ActionChunkArtifact
s themselves? (Or would you yield both?)
278044a
to
d0e0bf9
Compare
d0e0bf9
to
e634b86
Compare
|
||
If you choose to execute an action, your response should be a plain JSON object that successfully validates against the schema. The schema is provided below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if the LLM cares, but the correct syntax would be JSON string
not JSON object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost got my head around everything. I think I'll need one more review though
def test___add__(self): | ||
artifact_1 = ActionChunkArtifact("foo", tag="bar", name="baz", path="qux", partial_input="quux", index=1) | ||
artifact_2 = ActionChunkArtifact("bar", tag="baz", name="qux", path="quux", partial_input="quuz", index=2) | ||
added_artifact = artifact_1 + artifact_2 | ||
assert added_artifact.value == "foobar" | ||
assert added_artifact.tag == "barbaz" | ||
assert added_artifact.name == "bazqux" | ||
assert added_artifact.path == "quxquux" | ||
assert added_artifact.partial_input == "quuxquuz" | ||
assert added_artifact.index == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a hard time understanding why this behavior is desirable... is the add operator even used anywhere?
@@ -42,6 +42,7 @@ def test_to_dict(self, config): | |||
"stream": False, | |||
"temperature": 0.1, | |||
"type": "AmazonBedrockPromptDriver", | |||
"use_native_tools": False, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we (or should we) throw an error if True
is passed to a prompt driver that doesn't support native tools? (Seems like it'd be an easy change, just have that be the default behavior in base prompt driver)
response = s3_client.list_objects_v2(Bucket=bucket) | ||
for obj in response.get("Contents", []): | ||
print(obj.get("Key")) | ||
s3_client.list_objects_v2(Bucket=bucket) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
else: | ||
return ActionChunkArtifact( | ||
value=self.value + other.value, | ||
tag=self.tag, | ||
name=self.name, | ||
path=self.path, | ||
partial_input=self.partial_input, | ||
index=self.index, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo action_chunk_artifact.value += "some text"
is more readable than action_chunk_artifact += "some text"
|
||
|
||
@define | ||
class ActionChunkArtifact(TextArtifact, SerializableMixin): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regardless, I think moving ActionsArtifact.Action
into a non-nested ActionArtifact
is a good idea.
for tool_call in tool_calls | ||
] | ||
|
||
return ActionsArtifact(actions=actions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be passing the message like in the anthropic prompt driver (value=message.content.strip()
)?
if tool_call_delta.id is not None: | ||
name, path = tool_call_delta.function.name.split("-") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to check tool_call_delta.function.name
(rather than tool_call_delta.id
)?
else: | ||
content_delta = delta.content or "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it ever possible for both delta.content
and delta.tool_calls
to be present / non-None?
elif content_block_type == "tool_use": | ||
name, path = content_block.name.split("-") | ||
yield ActionChunkArtifact( | ||
value=f"{name}-{path}", index=chunk.index, tag=content_block.id, name=name, path=path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its a little confusing to sometimes set value
to the "function name" (e.g. name-path
) and sometimes set it to partial_input
. It feels like we want two different types of chunks.
Or maybe just add some note in the doc string about how to interpret value
in the context of ActionChunkArtifact
.
tool_choice: dict = field(default=Factory(lambda: {"type": "auto"}), kw_only=True, metadata={"serializable": False}) | ||
use_native_tools: bool = field(default=True, kw_only=True, metadata={"serializable": True}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doc strings for these would be nice
Closing in favor of #867 |
This PR adds the ability for prompt drivers to use native function calling over the Griptape provided approach (regex).
To do this, the following things have been changed:
PrompStack.TOOL_CALL_ROLE
, andPromptStack.TOOL_RESULT_ROLE
. Depending on the the LLM provider, these may get translated intouser
orassistant
roles.ActionsArtifact
for tracking function calls made by the LLM.ActionChunkArtifact
for tracking partial function calls made by the LLM when streaming.ActionsSubtask.Action
toActionsArtifact.Action
so that Prompt Drivers could more easily interact with Actions.PromptStack.content: str
toPromptStack.content: str | BaseArtifact
. Right now the type of content depends on the role (Tool roles useActionsArtifact
), but this will be changed in the next PR that breaks up PromptStack.Input into distinct classes (PromptStackTextInput
,PromptStackActionsInput
,PromptStackImageInput
etc.)ActionsSubtask.input: str
toActionsSubtask.input: str | ActionsArtifact.Action
. If a string is provided, the Task will parse it into anActionsSubtask.Action
using regex (current behavior). If anActionsSubtask.Action
is provided, it will use this (new behavior).ActionsSubtask.single_action
flag for use withToolTask
to denote that the incoming action string is not a list. The parsing logic changes slightly when this flag is set to true.BasePromptDriver.use_native_tools
to toggle between old regex tool usage and native tool usage.OpenAiChatPromptDriver
to use new Prompt Stack tool inputsAnthropicChatPromptDriver
to use new Prompt Stack tool inputs.toolkit_task/system.j2
prompt to conditionally render CoT wording based onuse_native_tools
.tool_task/system.j2
prompt to conditionally render action schema instructions based onuse_native_tools
.