Skip to content
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
4 changes: 2 additions & 2 deletions libs/core/langchain_core/messages/ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,9 @@ def content_blocks(self) -> list[types.ContentBlock]:
"args": tool_call["args"],
}
if "index" in tool_call:
tool_call_block["index"] = tool_call["index"]
tool_call_block["index"] = tool_call["index"] # type: ignore[typeddict-item]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ewwwwwwwwwwwww

if "extras" in tool_call:
tool_call_block["extras"] = tool_call["extras"]
tool_call_block["extras"] = tool_call["extras"] # type: ignore[typeddict-item]
blocks.append(tool_call_block)

return blocks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,12 @@ def _iter_blocks() -> Iterable[types.ContentBlock]:
not isinstance(message, AIMessageChunk)
and len(message.tool_calls) == 1
):
tool_call_block = message.tool_calls[0]
tool_call_block: types.ToolCall = {
"type": "tool_call",
"name": message.tool_calls[0]["name"],
"args": message.tool_calls[0]["args"],
"id": message.tool_calls[0].get("id"),
}
if "index" in block:
tool_call_block["index"] = block["index"]
yield tool_call_block
Expand Down
16 changes: 14 additions & 2 deletions libs/core/langchain_core/messages/block_translators/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ def _convert_to_v1_from_chat_completions(
content_blocks = []

for tool_call in message.tool_calls:
content_blocks.append(tool_call)
content_blocks.append(
{
"type": "tool_call",
"name": tool_call["name"],
"args": tool_call["args"],
"id": tool_call.get("id"),
}
)

return content_blocks

Expand Down Expand Up @@ -287,7 +294,12 @@ def _iter_blocks() -> Iterable[types.ContentBlock]:
elif call_id:
for tool_call in message.tool_calls or []:
if tool_call.get("id") == call_id:
tool_call_block = tool_call.copy()
tool_call_block = {
"type": "tool_call",
"name": tool_call["name"],
"args": tool_call["args"],
"id": tool_call.get("id"),
}
break
else:
for invalid_tool_call in message.invalid_tool_calls or []:
Expand Down
32 changes: 31 additions & 1 deletion libs/core/langchain_core/messages/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from langchain_core.messages import content as types
from langchain_core.messages.base import BaseMessage, BaseMessageChunk, merge_content
from langchain_core.messages.content import InvalidToolCall as InvalidToolCall
from langchain_core.messages.content import ToolCall as ToolCall
from langchain_core.utils._merge import merge_dicts, merge_obj


Expand Down Expand Up @@ -199,6 +198,37 @@ def __add__(self, other: Any) -> BaseMessageChunk: # type: ignore[override]
return super().__add__(other)


class ToolCall(TypedDict):
"""Represents a request to call a tool.

Example:

.. code-block:: python

{
"name": "foo",
"args": {"a": 1},
"id": "123"
}

This represents a request to call the tool named "foo" with arguments {"a": 1}
and an identifier of "123".

"""

name: str
"""The name of the tool to be called."""
args: dict[str, Any]
"""The arguments to the tool call."""
id: Optional[str]
"""An identifier associated with the tool call.

An identifier is needed to associate a tool call request with a tool
call result in events when multiple concurrent tool calls are made.
"""
type: NotRequired[Literal["tool_call"]]


def tool_call(
*,
name: str,
Expand Down
10 changes: 10 additions & 0 deletions libs/core/tests/unit_tests/messages/test_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ def test_add_ai_message_chunks_usage() -> None:
)


def test_init_tool_calls() -> None:
# Test we add "type" key on init
msg = AIMessage("", tool_calls=[{"name": "foo", "args": {"a": "b"}, "id": "abc"}])
assert len(msg.tool_calls) == 1
assert msg.tool_calls[0]["type"] == "tool_call"

# Test we can assign without adding type key
msg.tool_calls = [{"name": "bar", "args": {"c": "d"}, "id": "def"}]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assignment doesn't add a type key right?



def test_content_blocks() -> None:
message = AIMessage(
"",
Expand Down
50 changes: 2 additions & 48 deletions libs/core/tests/unit_tests/prompts/__snapshots__/test_chat.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -1014,23 +1014,12 @@

This represents a request to call the tool named "foo" with arguments {"a": 1}
and an identifier of "123".

.. note::
``create_tool_call`` may also be used as a factory to create a
``ToolCall``. Benefits include:

* Automatic ID generation (when not provided)
* Required arguments strictly validated at creation time
''',
'properties': dict({
'args': dict({
'title': 'Args',
'type': 'object',
}),
'extras': dict({
'title': 'Extras',
'type': 'object',
}),
'id': dict({
'anyOf': list([
dict({
Expand All @@ -1042,17 +1031,6 @@
]),
'title': 'Id',
}),
'index': dict({
'anyOf': list([
dict({
'type': 'integer',
}),
dict({
'type': 'string',
}),
]),
'title': 'Index',
}),
'name': dict({
'title': 'Name',
'type': 'string',
Expand All @@ -1064,10 +1042,9 @@
}),
}),
'required': list([
'type',
'id',
'name',
'args',
'id',
]),
'title': 'ToolCall',
'type': 'object',
Expand Down Expand Up @@ -2485,23 +2462,12 @@

This represents a request to call the tool named "foo" with arguments {"a": 1}
and an identifier of "123".

.. note::
``create_tool_call`` may also be used as a factory to create a
``ToolCall``. Benefits include:

* Automatic ID generation (when not provided)
* Required arguments strictly validated at creation time
''',
'properties': dict({
'args': dict({
'title': 'Args',
'type': 'object',
}),
'extras': dict({
'title': 'Extras',
'type': 'object',
}),
'id': dict({
'anyOf': list([
dict({
Expand All @@ -2513,17 +2479,6 @@
]),
'title': 'Id',
}),
'index': dict({
'anyOf': list([
dict({
'type': 'integer',
}),
dict({
'type': 'string',
}),
]),
'title': 'Index',
}),
'name': dict({
'title': 'Name',
'type': 'string',
Expand All @@ -2535,10 +2490,9 @@
}),
}),
'required': list([
'type',
'id',
'name',
'args',
'id',
]),
'title': 'ToolCall',
'type': 'object',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1417,23 +1417,12 @@

This represents a request to call the tool named "foo" with arguments {"a": 1}
and an identifier of "123".

.. note::
``create_tool_call`` may also be used as a factory to create a
``ToolCall``. Benefits include:

* Automatic ID generation (when not provided)
* Required arguments strictly validated at creation time
''',
'properties': dict({
'args': dict({
'title': 'Args',
'type': 'object',
}),
'extras': dict({
'title': 'Extras',
'type': 'object',
}),
'id': dict({
'anyOf': list([
dict({
Expand All @@ -1445,17 +1434,6 @@
]),
'title': 'Id',
}),
'index': dict({
'anyOf': list([
dict({
'type': 'integer',
}),
dict({
'type': 'string',
}),
]),
'title': 'Index',
}),
'name': dict({
'title': 'Name',
'type': 'string',
Expand All @@ -1467,10 +1445,9 @@
}),
}),
'required': list([
'type',
'id',
'name',
'args',
'id',
]),
'title': 'ToolCall',
'type': 'object',
Expand Down
Loading