Module: RubyLLM::BedrockInvoke::ToolSearch
- Defined in:
- lib/ruby_llm/bedrock_invoke/tool_search.rb
Overview
Payload shaping for Anthropic's tool search tool on Bedrock InvokeModel.
Bedrock exposes tool search only through InvokeModel (not Converse), as
a beta: the request body needs anthropic_beta: ["tool-search-tool-2025-10-19"]
and the unversioned tool_search_tool_regex tool type (the first-party
API uses versioned types and no beta flag; no BM25 variant exists on
Bedrock). Tools marked deferred still send their full definition every
request with defer_loading: true — they are excluded from the cached
prompt prefix until the model discovers them via the search tool.
Constant Summary collapse
- BETA_FLAG =
'tool-search-tool-2025-10-19'- SEARCH_TOOL_TYPE =
'tool_search_tool_regex'- SEARCH_TOOL_NAME =
'tool_search_tool_regex'- SERVER_BLOCK_TYPES =
%w[server_tool_use tool_search_tool_result].freeze
Class Method Summary collapse
-
.apply!(payload, tools) ⇒ Object
Mutates a rendered Anthropic Messages payload: flags deferred tool definitions, prepends the search tool, and opts into the beta.
- .block_type(block) ⇒ Object
- .contains_server_blocks?(blocks) ⇒ Boolean
- .deferred?(tool) ⇒ Boolean
- .deferred_definition?(definition) ⇒ Boolean
-
.deferred_names(tools) ⇒ Object
toolsis RubyLLM's name => tool hash; returns name strings. - .definition_value(definition, key) ⇒ Object
-
.ensure_beta!(payload) ⇒ Object
Both generations deep-merge user params (with_params / with_provider_options) over the rendered payload AFTER apply! runs, and deep_merge REPLACES arrays — silently dropping the beta flag.
- .inject_search_tool!(definitions) ⇒ Object
- .search_tool_definition?(definition) ⇒ Boolean
- .server_block?(block) ⇒ Boolean
Class Method Details
.apply!(payload, tools) ⇒ Object
Mutates a rendered Anthropic Messages payload: flags deferred tool definitions, prepends the search tool, and opts into the beta. Tools may be marked via the Deferred module or by carrying defer_loading in their own provider params. No-op when nothing is deferred.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 36 def apply!(payload, tools) names = deferred_names(tools) definitions = payload[:tools] if definitions.nil? return payload if names.empty? raise RubyLLM::BedrockInvoke::Error, 'Deferred tools present but payload has no tools' end definitions.each do |definition| name = definition_value(definition, :name).to_s definition[:defer_loading] = true if names.include?(name) end return payload unless definitions.any? { |d| deferred_definition?(d) } inject_search_tool!(definitions) payload[:anthropic_beta] = Array(payload[:anthropic_beta]) | [BETA_FLAG] payload end |
.block_type(block) ⇒ Object
80 81 82 83 84 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 80 def block_type(block) return nil unless block.is_a?(Hash) (block[:type] || block['type']).to_s end |
.contains_server_blocks?(blocks) ⇒ Boolean
76 77 78 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 76 def contains_server_blocks?(blocks) Array(blocks).any? { |block| server_block?(block) } end |
.deferred?(tool) ⇒ Boolean
22 23 24 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 22 def deferred?(tool) tool.respond_to?(:defer_loading?) && tool.defer_loading? end |
.deferred_definition?(definition) ⇒ Boolean
86 87 88 89 90 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 86 def deferred_definition?(definition) return false unless definition.is_a?(Hash) !!definition_value(definition, :defer_loading) end |
.deferred_names(tools) ⇒ Object
tools is RubyLLM's name => tool hash; returns name strings.
27 28 29 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 27 def deferred_names(tools) tools.values.select { |tool| deferred?(tool) }.map { |tool| tool.name.to_s } end |
.definition_value(definition, key) ⇒ Object
98 99 100 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 98 def definition_value(definition, key) definition[key] || definition[key.to_s] end |
.ensure_beta!(payload) ⇒ Object
Both generations deep-merge user params (with_params / with_provider_options) over the rendered payload AFTER apply! runs, and deep_merge REPLACES arrays — silently dropping the beta flag. Called on the final merged payload right before the request goes out.
62 63 64 65 66 67 68 69 70 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 62 def ensure_beta!(payload) definitions = payload[:tools] || payload['tools'] return payload unless definitions.is_a?(Array) return payload unless definitions.any? { |d| deferred_definition?(d) || search_tool_definition?(d) } betas = Array(payload.delete('anthropic_beta')) | Array(payload[:anthropic_beta]) | [BETA_FLAG] payload[:anthropic_beta] = betas payload end |
.inject_search_tool!(definitions) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 102 def inject_search_tool!(definitions) return if definitions.any? { |d| search_tool_definition?(d) } collision = definitions.find { |d| definition_value(d, :name).to_s == SEARCH_TOOL_NAME } if collision raise RubyLLM::BedrockInvoke::Error, "A tool named '#{SEARCH_TOOL_NAME}' collides with the injected tool search tool. Rename it." end definitions.unshift(type: SEARCH_TOOL_TYPE, name: SEARCH_TOOL_NAME) end |
.search_tool_definition?(definition) ⇒ Boolean
92 93 94 95 96 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 92 def search_tool_definition?(definition) return false unless definition.is_a?(Hash) definition_value(definition, :type) == SEARCH_TOOL_TYPE end |
.server_block?(block) ⇒ Boolean
72 73 74 |
# File 'lib/ruby_llm/bedrock_invoke/tool_search.rb', line 72 def server_block?(block) SERVER_BLOCK_TYPES.include?(block_type(block)) end |