Module: RubyLLM::Providers::Bedrock::Chat
- Included in:
- RubyLLM::Providers::Bedrock
- Defined in:
- lib/ruby_llm/providers/bedrock/chat.rb
Overview
Chat methods for Bedrock Converse API.
Class Method Summary collapse
- .budget_reasoning_config(thinking) ⇒ Object
- .completion_url ⇒ Object
- .default_input_schema ⇒ Object
- .effort_reasoning_config(thinking) ⇒ Object
- .normalize_tool_result_block(block) ⇒ Object
- .parse_completion_response(response) ⇒ Object
- .parse_reasoning_content_block(block) ⇒ Object
- .parse_text_content(content_blocks) ⇒ Object
- .parse_thinking(content_blocks) ⇒ Object
- .parse_tool_calls(content_blocks) ⇒ Object
- .render_additional_model_request_fields(thinking) ⇒ Object
- .render_inference_config(_model, temperature) ⇒ Object
- .render_message_content(msg) ⇒ Object
- .render_messages(messages) ⇒ Object
- .render_non_tool_message(msg) ⇒ Object
-
.render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists,Lint/UnusedMethodArgument.
- .render_raw_content(content) ⇒ Object
- .render_raw_tool_result_content(raw_value) ⇒ Object
- .render_reasoning_fields(thinking) ⇒ Object
- .render_role(role) ⇒ Object
- .render_system(messages) ⇒ Object
- .render_thinking_block(thinking) ⇒ Object
- .render_tool(tool) ⇒ Object
- .render_tool_config(tools) ⇒ Object
- .render_tool_result_block(msg) ⇒ Object
- .render_tool_result_content(content) ⇒ Object
- .sanitize_non_assistant_raw_blocks(blocks) ⇒ Object
- .tool_result_content_block?(block) ⇒ Boolean
Class Method Details
.budget_reasoning_config(thinking) ⇒ Object
262 263 264 265 266 267 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 262 def budget_reasoning_config(thinking) budget = thinking.respond_to?(:budget) ? thinking.budget : thinking return nil unless budget.is_a?(Integer) { reasoning_config: { type: 'enabled', budget_tokens: budget } } end |
.completion_url ⇒ Object
10 11 12 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 10 def completion_url "/model/#{@model.id}/converse" end |
.default_input_schema ⇒ Object
337 338 339 340 341 342 343 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 337 def default_input_schema { 'type' => 'object', 'properties' => {}, 'required' => [] } end |
.effort_reasoning_config(thinking) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 250 def effort_reasoning_config(thinking) effort = thinking.respond_to?(:effort) ? thinking.effort : nil effort = effort.to_s if effort return nil if effort.nil? || effort.empty? || effort == 'none' if (@model) { reasoning_config: { type: 'enabled', reasoning_effort: effort } } else { reasoning_effort: effort } end end |
.normalize_tool_result_block(block) ⇒ Object
176 177 178 179 180 181 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 176 def normalize_tool_result_block(block) return nil unless block.is_a?(Hash) return block if tool_result_content_block?(block) nil end |
.parse_completion_response(response) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 40 def parse_completion_response(response) data = response.body return if data.nil? || data.empty? content_blocks = data.dig('output', 'message', 'content') || [] usage = data['usage'] || {} thinking_text, thinking_signature = parse_thinking(content_blocks) Message.new( role: :assistant, content: parse_text_content(content_blocks), thinking: Thinking.build(text: thinking_text, signature: thinking_signature), tool_calls: parse_tool_calls(content_blocks), input_tokens: usage['inputTokens'], output_tokens: usage['outputTokens'], cached_tokens: usage['cacheReadInputTokens'], cache_creation_tokens: usage['cacheWriteInputTokens'], thinking_tokens: usage['reasoningTokens'], model_id: data['modelId'], raw: response ) end |
.parse_reasoning_content_block(block) ⇒ Object
308 309 310 311 312 313 314 315 316 317 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 308 def parse_reasoning_content_block(block) reasoning_content = block['reasoningContent'] return [nil, nil] unless reasoning_content.is_a?(Hash) reasoning_text = reasoning_content['reasoningText'] || {} text = reasoning_text['text'].is_a?(String) ? reasoning_text['text'] : nil signature = reasoning_text['signature'] if reasoning_text['signature'].is_a?(String) signature ||= reasoning_content['redactedContent'] if reasoning_content['redactedContent'].is_a?(String) [text, signature] end |
.parse_text_content(content_blocks) ⇒ Object
290 291 292 293 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 290 def parse_text_content(content_blocks) text = content_blocks.filter_map { |block| block['text'] if block['text'].is_a?(String) }.join text.empty? ? nil : text end |
.parse_thinking(content_blocks) ⇒ Object
295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 295 def parse_thinking(content_blocks) text = +'' signature = nil content_blocks.each do |block| chunk_text, chunk_signature = parse_reasoning_content_block(block) text << chunk_text if chunk_text signature ||= chunk_signature end [text.empty? ? nil : text, signature] end |
.parse_tool_calls(content_blocks) ⇒ Object
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 319 def parse_tool_calls(content_blocks) tool_calls = {} content_blocks.each do |block| tool_use = block['toolUse'] next unless tool_use tool_call_id = tool_use['toolUseId'] tool_calls[tool_call_id] = ToolCall.new( id: tool_call_id, name: tool_use['name'], arguments: tool_use['input'] || {} ) end tool_calls.empty? ? nil : tool_calls end |
.render_additional_model_request_fields(thinking) ⇒ Object
232 233 234 235 236 237 238 239 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 232 def render_additional_model_request_fields(thinking) fields = {} reasoning_fields = render_reasoning_fields(thinking) fields = RubyLLM::Utils.deep_merge(fields, reasoning_fields) if reasoning_fields fields.empty? ? nil : fields end |
.render_inference_config(_model, temperature) ⇒ Object
200 201 202 203 204 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 200 def render_inference_config(_model, temperature) config = {} config[:temperature] = temperature unless temperature.nil? config end |
.render_message_content(msg) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 96 def (msg) if msg.content.is_a?(RubyLLM::Content::Raw) return render_raw_content(msg.content) if msg.role == :assistant return sanitize_non_assistant_raw_blocks(render_raw_content(msg.content)) end blocks = [] thinking_block = render_thinking_block(msg.thinking) blocks << thinking_block if msg.role == :assistant && thinking_block text_and_media_blocks = Media.render_content(msg.content, used_document_names: @used_document_names) blocks.concat(text_and_media_blocks) if text_and_media_blocks if msg.tool_call? msg.tool_calls.each_value do |tool_call| blocks << { toolUse: { toolUseId: tool_call.id, name: tool_call.name, input: tool_call.arguments } } end end blocks end |
.render_messages(messages) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 63 def () rendered = [] tool_result_blocks = [] .each do |msg| if msg.tool_result? tool_result_blocks << render_tool_result_block(msg) next end unless tool_result_blocks.empty? rendered << { role: 'user', content: tool_result_blocks } tool_result_blocks = [] end = (msg) rendered << if end rendered << { role: 'user', content: tool_result_blocks } unless tool_result_blocks.empty? rendered end |
.render_non_tool_message(msg) ⇒ Object
86 87 88 89 90 91 92 93 94 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 86 def (msg) content = (msg) return nil if content.empty? { role: render_role(msg.role), content: content } end |
.render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists,Lint/UnusedMethodArgument
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 14 def render_payload(, tools:, temperature:, model:, stream: false, schema: nil, thinking: nil) # rubocop:disable Metrics/ParameterLists,Lint/UnusedMethodArgument @model = model @used_document_names = {} , = .partition { |msg| msg.role == :system } payload = { messages: () } system_blocks = render_system() payload[:system] = system_blocks unless system_blocks.empty? payload[:inferenceConfig] = render_inference_config(model, temperature) tool_config = render_tool_config(tools) if tool_config payload[:toolConfig] = tool_config payload[:tools] = tool_config[:tools] # Internal mirror for shared payload inspections in specs. end additional_fields = render_additional_model_request_fields(thinking) payload[:additionalModelRequestFields] = additional_fields if additional_fields payload end |
.render_raw_content(content) ⇒ Object
126 127 128 129 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 126 def render_raw_content(content) value = content.value value.is_a?(Array) ? value : [value] end |
.render_raw_tool_result_content(raw_value) ⇒ Object
166 167 168 169 170 171 172 173 174 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 166 def render_raw_tool_result_content(raw_value) blocks = raw_value.is_a?(Array) ? raw_value : [raw_value] normalized = blocks.filter_map do |block| normalize_tool_result_block(block) end normalized.empty? ? [{ text: raw_value.to_s }] : normalized end |
.render_reasoning_fields(thinking) ⇒ Object
241 242 243 244 245 246 247 248 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 241 def render_reasoning_fields(thinking) return nil unless thinking&.enabled? effort_config = effort_reasoning_config(thinking) return effort_config if effort_config budget_reasoning_config(thinking) end |
.render_role(role) ⇒ Object
189 190 191 192 193 194 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 189 def render_role(role) case role when :assistant then 'assistant' else 'user' end end |
.render_system(messages) ⇒ Object
196 197 198 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 196 def render_system() .flat_map { |msg| Media.render_content(msg.content, used_document_names: @used_document_names) } end |
.render_thinking_block(thinking) ⇒ Object
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 269 def render_thinking_block(thinking) return nil unless thinking if thinking.text { reasoningContent: { reasoningText: { text: thinking.text, signature: thinking.signature }.compact } } elsif thinking.signature { reasoningContent: { redactedContent: thinking.signature } } end end |
.render_tool(tool) ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 214 def render_tool(tool) input_schema = tool.params_schema || RubyLLM::Tool::SchemaDefinition.from_parameters(tool.parameters)&.json_schema tool_spec = { toolSpec: { name: tool.name, description: tool.description, inputSchema: { json: input_schema || default_input_schema } } } return tool_spec if tool.provider_params.empty? RubyLLM::Utils.deep_merge(tool_spec, tool.provider_params) end |
.render_tool_config(tools) ⇒ Object
206 207 208 209 210 211 212 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 206 def render_tool_config(tools) return nil if tools.empty? { tools: tools.values.map { |tool| render_tool(tool) } } end |
.render_tool_result_block(msg) ⇒ Object
140 141 142 143 144 145 146 147 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 140 def render_tool_result_block(msg) { toolResult: { toolUseId: msg.tool_call_id, content: render_tool_result_content(msg.content) } } end |
.render_tool_result_content(content) ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 149 def render_tool_result_content(content) return render_raw_tool_result_content(content.value) if content.is_a?(RubyLLM::Content::Raw) if content.is_a?(Hash) || content.is_a?(Array) [{ json: content }] elsif content.is_a?(RubyLLM::Content) blocks = [] blocks << { text: content.text } if content.text content..each do || blocks << { text: .for_llm } end blocks else [{ text: content.to_s }] end end |
.sanitize_non_assistant_raw_blocks(blocks) ⇒ Object
131 132 133 134 135 136 137 138 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 131 def sanitize_non_assistant_raw_blocks(blocks) blocks.filter_map do |block| next unless block.is_a?(Hash) next if block.key?(:reasoningContent) || block.key?('reasoningContent') block end end |
.tool_result_content_block?(block) ⇒ Boolean
183 184 185 186 187 |
# File 'lib/ruby_llm/providers/bedrock/chat.rb', line 183 def tool_result_content_block?(block) %w[text json document image].any? do |key| block.key?(key) || block.key?(key.to_sym) end end |