Skip to content

Commit

Permalink
core[minor]: rename ToolMessage.raw_output -> artifact (#24185)
Browse files Browse the repository at this point in the history
  • Loading branch information
baskaryan committed Jul 12, 2024
1 parent d77d9bf commit 6166ea6
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 106 deletions.
21 changes: 13 additions & 8 deletions libs/core/langchain_core/messages/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ class ToolMessage(BaseMessage):
ToolMessage(content='42', tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL')
Example: A ToolMessage where only part of the tool output is sent to the model
and the full output is passed in to raw_output.
and the full output is passed in to artifact.
.. versionadded:: 0.2.17
.. code-block:: python
Expand All @@ -36,7 +39,7 @@ class ToolMessage(BaseMessage):
ToolMessage(
content=tool_output["stdout"],
raw_output=tool_output,
artifact=tool_output,
tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL',
)
Expand All @@ -54,12 +57,14 @@ class ToolMessage(BaseMessage):
type: Literal["tool"] = "tool"
"""The type of the message (used for serialization). Defaults to "tool"."""

raw_output: Any = None
"""The raw output of the tool.
artifact: Any = None
"""Artifact of the Tool execution which is not meant to be sent to the model.
Should only be specified if it is different from the message content, e.g. if only
a subset of the full tool output is being passed as message content but the full
output is needed in other parts of the code.
**Not part of the payload sent to the model.** Should only be specified if it is
different from the message content, i.e. if only a subset of the full tool output
is being passed as message content.
.. versionadded:: 0.2.17
"""

@classmethod
Expand Down Expand Up @@ -106,7 +111,7 @@ def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore
return self.__class__(
tool_call_id=self.tool_call_id,
content=merge_content(self.content, other.content),
raw_output=merge_obj(self.raw_output, other.raw_output),
artifact=merge_obj(self.artifact, other.artifact),
additional_kwargs=merge_dicts(
self.additional_kwargs, other.additional_kwargs
),
Expand Down
4 changes: 2 additions & 2 deletions libs/core/langchain_core/messages/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,8 @@ def _create_message_from_message_type(
elif message_type == "function":
message = FunctionMessage(content=content, **kwargs)
elif message_type == "tool":
raw_output = kwargs.get("additional_kwargs", {}).pop("raw_output", None)
message = ToolMessage(content=content, raw_output=raw_output, **kwargs)
artifact = kwargs.get("additional_kwargs", {}).pop("artifact", None)
message = ToolMessage(content=content, artifact=artifact, **kwargs)
elif message_type == "remove":
message = RemoveMessage(**kwargs)
else:
Expand Down
44 changes: 22 additions & 22 deletions libs/core/langchain_core/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ class ChildTool(BaseTool):
] = False
"""Handle the content of the ValidationError thrown."""

response_format: Literal["content", "content_and_raw_output"] = "content"
response_format: Literal["content", "content_and_artifact"] = "content"
"""The tool response format.
If "content" then the output of the tool is interpreted as the contents of a
ToolMessage. If "content_and_raw_output" then the output is expected to be a
two-tuple corresponding to the (content, raw_output) of a ToolMessage.
ToolMessage. If "content_and_artifact" then the output is expected to be a
two-tuple corresponding to the (content, artifact) of a ToolMessage.
"""

class Config(Serializable.Config):
Expand Down Expand Up @@ -540,7 +540,7 @@ def run(
)

content = None
raw_output = None
artifact = None
error_to_raise: Union[Exception, KeyboardInterrupt, None] = None
try:
child_config = patch_config(config, callbacks=run_manager.get_child())
Expand All @@ -553,15 +553,15 @@ def run(
if config_param := _get_runnable_config_param(self._run):
tool_kwargs[config_param] = config
response = context.run(self._run, *tool_args, **tool_kwargs)
if self.response_format == "content_and_raw_output":
if self.response_format == "content_and_artifact":
if not isinstance(response, tuple) or len(response) != 2:
raise ValueError(
"Since response_format='content_and_raw_output' "
"Since response_format='content_and_artifact' "
"a two-tuple of the message content and raw tool output is "
f"expected. Instead generated response of type: "
f"{type(response)}."
)
content, raw_output = response
content, artifact = response
else:
content = response
except ValidationError as e:
Expand All @@ -580,7 +580,7 @@ def run(
if error_to_raise:
run_manager.on_tool_error(error_to_raise)
raise error_to_raise
output = _format_output(content, raw_output, tool_call_id)
output = _format_output(content, artifact, tool_call_id)
run_manager.on_tool_end(output, color=color, name=self.name, **kwargs)
return output

Expand Down Expand Up @@ -624,7 +624,7 @@ async def arun(
**kwargs,
)
content = None
raw_output = None
artifact = None
error_to_raise: Optional[Union[Exception, KeyboardInterrupt]] = None
try:
tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input)
Expand All @@ -644,15 +644,15 @@ async def arun(
response = await asyncio.create_task(coro, context=context) # type: ignore
else:
response = await coro
if self.response_format == "content_and_raw_output":
if self.response_format == "content_and_artifact":
if not isinstance(response, tuple) or len(response) != 2:
raise ValueError(
"Since response_format='content_and_raw_output' "
"Since response_format='content_and_artifact' "
"a two-tuple of the message content and raw tool output is "
f"expected. Instead generated response of type: "
f"{type(response)}."
)
content, raw_output = response
content, artifact = response
else:
content = response
except ValidationError as e:
Expand All @@ -672,7 +672,7 @@ async def arun(
await run_manager.on_tool_error(error_to_raise)
raise error_to_raise

output = _format_output(content, raw_output, tool_call_id)
output = _format_output(content, artifact, tool_call_id)
await run_manager.on_tool_end(output, color=color, name=self.name, **kwargs)
return output

Expand Down Expand Up @@ -883,7 +883,7 @@ def from_function(
args_schema: Optional[Type[BaseModel]] = None,
infer_schema: bool = True,
*,
response_format: Literal["content", "content_and_raw_output"] = "content",
response_format: Literal["content", "content_and_artifact"] = "content",
parse_docstring: bool = False,
error_on_invalid_docstring: bool = False,
**kwargs: Any,
Expand All @@ -902,8 +902,8 @@ def from_function(
infer_schema: Whether to infer the schema from the function's signature
response_format: The tool response format. If "content" then the output of
the tool is interpreted as the contents of a ToolMessage. If
"content_and_raw_output" then the output is expected to be a two-tuple
corresponding to the (content, raw_output) of a ToolMessage.
"content_and_artifact" then the output is expected to be a two-tuple
corresponding to the (content, artifact) of a ToolMessage.
parse_docstring: if ``infer_schema`` and ``parse_docstring``, will attempt
to parse parameter descriptions from Google Style function docstrings.
error_on_invalid_docstring: if ``parse_docstring`` is provided, configures
Expand Down Expand Up @@ -980,7 +980,7 @@ def tool(
return_direct: bool = False,
args_schema: Optional[Type[BaseModel]] = None,
infer_schema: bool = True,
response_format: Literal["content", "content_and_raw_output"] = "content",
response_format: Literal["content", "content_and_artifact"] = "content",
parse_docstring: bool = False,
error_on_invalid_docstring: bool = True,
) -> Callable:
Expand All @@ -996,8 +996,8 @@ def tool(
accept a dictionary input to its `run()` function.
response_format: The tool response format. If "content" then the output of
the tool is interpreted as the contents of a ToolMessage. If
"content_and_raw_output" then the output is expected to be a two-tuple
corresponding to the (content, raw_output) of a ToolMessage.
"content_and_artifact" then the output is expected to be a two-tuple
corresponding to the (content, artifact) of a ToolMessage.
parse_docstring: if ``infer_schema`` and ``parse_docstring``, will attempt to
parse parameter descriptions from Google Style function docstrings.
error_on_invalid_docstring: if ``parse_docstring`` is provided, configures
Expand All @@ -1020,7 +1020,7 @@ def search_api(query: str) -> str:
# Searches the API for the query.
return
@tool(response_format="content_and_raw_output")
@tool(response_format="content_and_artifact")
def search_api(query: str) -> Tuple[str, dict]:
return "partial json of results", {"full": "object of results"}
Expand Down Expand Up @@ -1385,7 +1385,7 @@ def _prep_run_args(


def _format_output(
content: Any, raw_output: Any, tool_call_id: Optional[str]
content: Any, artifact: Any, tool_call_id: Optional[str]
) -> Union[ToolMessage, Any]:
if tool_call_id:
# NOTE: This will fail to stringify lists which aren't actually content blocks
Expand All @@ -1397,7 +1397,7 @@ def _format_output(
and isinstance(content[0], (str, dict))
):
content = _stringify(content)
return ToolMessage(content, raw_output=raw_output, tool_call_id=tool_call_id)
return ToolMessage(content, artifact=artifact, tool_call_id=tool_call_id)
else:
return content

Expand Down
26 changes: 16 additions & 10 deletions libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,11 @@

ToolMessage(content='42', tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL')


Example: A ToolMessage where only part of the tool output is sent to the model
and the full output is passed in to raw_output.
and the full output is passed in to artifact.

.. versionadded:: 0.2.17

.. code-block:: python

Expand All @@ -472,7 +475,7 @@

ToolMessage(
content=tool_output["stdout"],
raw_output=tool_output,
artifact=tool_output,
tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL',
)

Expand All @@ -485,6 +488,9 @@
'title': 'Additional Kwargs',
'type': 'object',
}),
'artifact': dict({
'title': 'Artifact',
}),
'content': dict({
'anyOf': list([
dict({
Expand Down Expand Up @@ -514,9 +520,6 @@
'title': 'Name',
'type': 'string',
}),
'raw_output': dict({
'title': 'Raw Output',
}),
'response_metadata': dict({
'title': 'Response Metadata',
'type': 'object',
Expand Down Expand Up @@ -1062,8 +1065,11 @@

ToolMessage(content='42', tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL')


Example: A ToolMessage where only part of the tool output is sent to the model
and the full output is passed in to raw_output.
and the full output is passed in to artifact.

.. versionadded:: 0.2.17

.. code-block:: python

Expand All @@ -1077,7 +1083,7 @@

ToolMessage(
content=tool_output["stdout"],
raw_output=tool_output,
artifact=tool_output,
tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL',
)

Expand All @@ -1090,6 +1096,9 @@
'title': 'Additional Kwargs',
'type': 'object',
}),
'artifact': dict({
'title': 'Artifact',
}),
'content': dict({
'anyOf': list([
dict({
Expand Down Expand Up @@ -1119,9 +1128,6 @@
'title': 'Name',
'type': 'string',
}),
'raw_output': dict({
'title': 'Raw Output',
}),
'response_metadata': dict({
'title': 'Response Metadata',
'type': 'object',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,8 +813,11 @@

ToolMessage(content='42', tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL')


Example: A ToolMessage where only part of the tool output is sent to the model
and the full output is passed in to raw_output.
and the full output is passed in to artifact.

.. versionadded:: 0.2.17

.. code-block:: python

Expand All @@ -828,7 +831,7 @@

ToolMessage(
content=tool_output["stdout"],
raw_output=tool_output,
artifact=tool_output,
tool_call_id='call_Jja7J89XsjrOLA5r!MEOW!SL',
)

Expand All @@ -841,6 +844,9 @@
'title': 'Additional Kwargs',
'type': 'object',
}),
'artifact': dict({
'title': 'Artifact',
}),
'content': dict({
'anyOf': list([
dict({
Expand Down Expand Up @@ -870,9 +876,6 @@
'title': 'Name',
'type': 'string',
}),
'raw_output': dict({
'title': 'Raw Output',
}),
'response_metadata': dict({
'title': 'Response Metadata',
'type': 'object',
Expand Down
Loading

0 comments on commit 6166ea6

Please sign in to comment.