Skip to content

Commit 49161bb

Browse files
Introduce Langchain::Assistant#parallel_tool_calls options whether to allow the LLM to make multiple parallel tool calls (#827)
* Implement assistant.parallel_tool_calls: true/false option * fix linter * Specs * specs * fix linter * CHANGELOG entry
1 parent fe33fa2 commit 49161bb

25 files changed

+237
-75
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
- [BREAKING] Remove `Langchain::Assistant#clear_thread!` method
33
- [BREAKING] `Langchain::Messages::*` namespace had migrated to `Langchain::Assistant::Messages::*`
44
- [BREAKING] Modify `Langchain::LLM::AwsBedrock` constructor to pass model options via default_options: {...}
5+
- Introduce `Langchain::Assistant#parallel_tool_calls` options whether to allow the LLM to make multiple parallel tool calls. Default: true
56
- Minor improvements to the Langchain::Assistant class
67
- Added support for streaming with Anthropic
78
- Bump anthropic gem

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ Note that streaming is not currently supported for all LLMs.
534534
* `tools`: An array of tool instances (optional)
535535
* `instructions`: System instructions for the assistant (optional)
536536
* `tool_choice`: Specifies how tools should be selected. Default: "auto". A specific tool function name can be passed. This will force the Assistant to **always** use this function.
537+
* `parallel_tool_calls`: Whether to make multiple parallel tool calls. Default: true
537538
* `add_message_callback`: A callback function (proc, lambda) that is called when any message is added to the conversation (optional)
538539

539540
### Key Methods

lib/langchain/assistant.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,35 @@ module Langchain
1212
# tools: [Langchain::Tool::NewsRetriever.new(api_key: ENV["NEWS_API_KEY"])]
1313
# )
1414
class Assistant
15-
attr_reader :llm, :instructions, :state, :llm_adapter, :tool_choice
16-
attr_reader :total_prompt_tokens, :total_completion_tokens, :total_tokens, :messages
17-
attr_accessor :tools, :add_message_callback
15+
attr_reader :llm,
16+
:instructions,
17+
:state,
18+
:llm_adapter,
19+
:messages,
20+
:tool_choice,
21+
:total_prompt_tokens,
22+
:total_completion_tokens,
23+
:total_tokens
24+
25+
attr_accessor :tools,
26+
:add_message_callback,
27+
:parallel_tool_calls
1828

1929
# Create a new assistant
2030
#
2131
# @param llm [Langchain::LLM::Base] LLM instance that the assistant will use
2232
# @param tools [Array<Langchain::Tool::Base>] Tools that the assistant has access to
2333
# @param instructions [String] The system instructions
2434
# @param tool_choice [String] Specify how tools should be selected. Options: "auto", "any", "none", or <specific function name>
25-
# @params add_message_callback [Proc] A callback function (Proc or lambda) that is called when any message is added to the conversation
35+
# @param parallel_tool_calls [Boolean] Whether or not to run tools in parallel
36+
# @param messages [Array<Langchain::Assistant::Messages::Base>] The messages
37+
# @param add_message_callback [Proc] A callback function (Proc or lambda) that is called when any message is added to the conversation
2638
def initialize(
2739
llm:,
2840
tools: [],
2941
instructions: nil,
3042
tool_choice: "auto",
43+
parallel_tool_calls: true,
3144
messages: [],
3245
add_message_callback: nil,
3346
&block
@@ -47,6 +60,7 @@ def initialize(
4760

4861
self.messages = messages
4962
@tools = tools
63+
@parallel_tool_calls = parallel_tool_calls
5064
self.tool_choice = tool_choice
5165
self.instructions = instructions
5266
@block = block
@@ -326,7 +340,8 @@ def chat_with_llm
326340
instructions: @instructions,
327341
messages: array_of_message_hashes,
328342
tools: @tools,
329-
tool_choice: tool_choice
343+
tool_choice: tool_choice,
344+
parallel_tool_calls: parallel_tool_calls
330345
)
331346
@llm.chat(**params, &@block)
332347
end

lib/langchain/assistant/llm/adapters/anthropic.rb

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@ module Adapters
77
class Anthropic < Base
88
# Build the chat parameters for the Anthropic API
99
#
10-
# @param tools [Array<Hash>] The tools to use
11-
# @param instructions [String] The system instructions
1210
# @param messages [Array<Hash>] The messages
11+
# @param instructions [String] The system instructions
12+
# @param tools [Array<Hash>] The tools to use
1313
# @param tool_choice [String] The tool choice
14+
# @param parallel_tool_calls [Boolean] Whether to make parallel tool calls
1415
# @return [Hash] The chat parameters
15-
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
16+
def build_chat_params(
17+
messages:,
18+
instructions:,
19+
tools:,
20+
tool_choice:,
21+
parallel_tool_calls:
22+
)
1623
params = {messages: messages}
1724
if tools.any?
1825
params[:tools] = build_tools(tools)
19-
params[:tool_choice] = build_tool_choice(tool_choice)
26+
params[:tool_choice] = build_tool_choice(tool_choice, parallel_tool_calls)
2027
end
2128
params[:system] = instructions if instructions
2229
params
@@ -31,7 +38,7 @@ def build_chat_params(tools:, instructions:, messages:, tool_choice:)
3138
# @param tool_call_id [String] The tool call ID
3239
# @return [Messages::AnthropicMessage] The Anthropic message
3340
def build_message(role:, content: nil, image_url: nil, tool_calls: [], tool_call_id: nil)
34-
warn "Image URL is not supported by Anthropic currently" if image_url
41+
Langchain.logger.warn "WARNING: Image URL is not supported by Anthropic currently" if image_url
3542

3643
Messages::AnthropicMessage.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
3744
end
@@ -76,15 +83,20 @@ def support_system_message?
7683

7784
private
7885

79-
def build_tool_choice(choice)
86+
def build_tool_choice(choice, parallel_tool_calls)
87+
tool_choice_object = {disable_parallel_tool_use: !parallel_tool_calls}
88+
8089
case choice
8190
when "auto"
82-
{type: "auto"}
91+
tool_choice_object[:type] = "auto"
8392
when "any"
84-
{type: "any"}
93+
tool_choice_object[:type] = "any"
8594
else
86-
{type: "tool", name: choice}
95+
tool_choice_object[:type] = "tool"
96+
tool_choice_object[:name] = choice
8797
end
98+
99+
tool_choice_object
88100
end
89101
end
90102
end

lib/langchain/assistant/llm/adapters/base.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ module Adapters
77
class Base
88
# Build the chat parameters for the LLM
99
#
10-
# @param tools [Array] The tools to use
11-
# @param instructions [String] The system instructions
1210
# @param messages [Array] The messages
11+
# @param instructions [String] The system instructions
12+
# @param tools [Array] The tools to use
1313
# @param tool_choice [String] The tool choice
14+
# @param parallel_tool_calls [Boolean] Whether to make parallel tool calls
1415
# @return [Hash] The chat parameters
15-
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
16+
def build_chat_params(
17+
messages:,
18+
instructions:,
19+
tools:,
20+
tool_choice:,
21+
parallel_tool_calls:
22+
)
1623
raise NotImplementedError, "Subclasses must implement build_chat_params"
1724
end
1825

lib/langchain/assistant/llm/adapters/google_gemini.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ module Adapters
77
class GoogleGemini < Base
88
# Build the chat parameters for the Google Gemini LLM
99
#
10-
# @param tools [Array] The tools to use
11-
# @param instructions [String] The system instructions
1210
# @param messages [Array] The messages
11+
# @param instructions [String] The system instructions
12+
# @param tools [Array] The tools to use
1313
# @param tool_choice [String] The tool choice
14+
# @param parallel_tool_calls [Boolean] Whether to make parallel tool calls
1415
# @return [Hash] The chat parameters
15-
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
16+
def build_chat_params(
17+
messages:,
18+
instructions:,
19+
tools:,
20+
tool_choice:,
21+
parallel_tool_calls:
22+
)
23+
Langchain.logger.warn "WARNING: `parallel_tool_calls:` is not supported by Google Gemini currently"
24+
1625
params = {messages: messages}
1726
if tools.any?
1827
params[:tools] = build_tools(tools)
@@ -31,7 +40,7 @@ def build_chat_params(tools:, instructions:, messages:, tool_choice:)
3140
# @param tool_call_id [String] The tool call ID
3241
# @return [Messages::GoogleGeminiMessage] The Google Gemini message
3342
def build_message(role:, content: nil, image_url: nil, tool_calls: [], tool_call_id: nil)
34-
warn "Image URL is not supported by Google Gemini" if image_url
43+
Langchain.logger.warn "Image URL is not supported by Google Gemini" if image_url
3544

3645
Messages::GoogleGeminiMessage.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
3746
end

lib/langchain/assistant/llm/adapters/mistral_ai.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@ module Adapters
77
class MistralAI < Base
88
# Build the chat parameters for the Mistral AI LLM
99
#
10-
# @param tools [Array] The tools to use
11-
# @param instructions [String] The system instructions
1210
# @param messages [Array] The messages
11+
# @param instructions [String] The system instructions
12+
# @param tools [Array] The tools to use
1313
# @param tool_choice [String] The tool choice
14+
# @param parallel_tool_calls [Boolean] Whether to make parallel tool calls
1415
# @return [Hash] The chat parameters
15-
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
16+
def build_chat_params(
17+
messages:,
18+
instructions:,
19+
tools:,
20+
tool_choice:,
21+
parallel_tool_calls:
22+
)
23+
Langchain.logger.warn "WARNING: `parallel_tool_calls:` is not supported by Mistral AI currently"
24+
1625
params = {messages: messages}
1726
if tools.any?
1827
params[:tools] = build_tools(tools)

lib/langchain/assistant/llm/adapters/ollama.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,22 @@ module Adapters
77
class Ollama < Base
88
# Build the chat parameters for the Ollama LLM
99
#
10-
# @param tools [Array] The tools to use
11-
# @param instructions [String] The system instructions
1210
# @param messages [Array] The messages
11+
# @param instructions [String] The system instructions
12+
# @param tools [Array] The tools to use
1313
# @param tool_choice [String] The tool choice
14+
# @param parallel_tool_calls [Boolean] Whether to make parallel tool calls
1415
# @return [Hash] The chat parameters
15-
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
16+
def build_chat_params(
17+
messages:,
18+
instructions:,
19+
tools:,
20+
tool_choice:,
21+
parallel_tool_calls:
22+
)
23+
Langchain.logger.warn "WARNING: `parallel_tool_calls:` is not supported by Ollama currently"
24+
Langchain.logger.warn "WARNING: `tool_choice:` is not supported by Ollama currently"
25+
1626
params = {messages: messages}
1727
if tools.any?
1828
params[:tools] = build_tools(tools)
@@ -29,7 +39,7 @@ def build_chat_params(tools:, instructions:, messages:, tool_choice:)
2939
# @param tool_call_id [String] The tool call ID
3040
# @return [Messages::OllamaMessage] The Ollama message
3141
def build_message(role:, content: nil, image_url: nil, tool_calls: [], tool_call_id: nil)
32-
warn "Image URL is not supported by Ollama currently" if image_url
42+
Langchain.logger.warn "WARNING: Image URL is not supported by Ollama currently" if image_url
3343

3444
Messages::OllamaMessage.new(role: role, content: content, tool_calls: tool_calls, tool_call_id: tool_call_id)
3545
end

lib/langchain/assistant/llm/adapters/openai.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,24 @@ module Adapters
77
class OpenAI < Base
88
# Build the chat parameters for the OpenAI LLM
99
#
10-
# @param tools [Array] The tools to use
11-
# @param instructions [String] The system instructions
1210
# @param messages [Array] The messages
11+
# @param instructions [String] The system instructions
12+
# @param tools [Array] The tools to use
1313
# @param tool_choice [String] The tool choice
14+
# @param parallel_tool_calls [Boolean] Whether to make parallel tool calls
1415
# @return [Hash] The chat parameters
15-
def build_chat_params(tools:, instructions:, messages:, tool_choice:)
16+
def build_chat_params(
17+
messages:,
18+
instructions:,
19+
tools:,
20+
tool_choice:,
21+
parallel_tool_calls:
22+
)
1623
params = {messages: messages}
1724
if tools.any?
1825
params[:tools] = build_tools(tools)
1926
params[:tool_choice] = build_tool_choice(tool_choice)
27+
params[:parallel_tool_calls] = parallel_tool_calls
2028
end
2129
params
2230
end

lib/langchain/llm/ai21.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module Langchain::LLM
88
# gem "ai21", "~> 0.2.1"
99
#
1010
# Usage:
11-
# ai21 = Langchain::LLM::AI21.new(api_key: ENV["AI21_API_KEY"])
11+
# llm = Langchain::LLM::AI21.new(api_key: ENV["AI21_API_KEY"])
1212
#
1313
class AI21 < Base
1414
DEFAULTS = {

0 commit comments

Comments
 (0)