ruby_llm 1.13.2 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +16 -13
- data/lib/generators/ruby_llm/agent/agent_generator.rb +36 -0
- data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -0
- data/lib/generators/ruby_llm/agent/templates/instructions.txt.erb.tt +0 -0
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +110 -41
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +14 -15
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -11
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +25 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +31 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +31 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/new.html.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +27 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_content.html.erb.tt +1 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +2 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +4 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +17 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +40 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +27 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +19 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +5 -3
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +6 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +2 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +4 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +11 -12
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +27 -17
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -4
- data/lib/generators/ruby_llm/generator_helpers.rb +41 -17
- data/lib/generators/ruby_llm/install/install_generator.rb +21 -18
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +3 -4
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +1 -1
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +3 -3
- data/lib/generators/ruby_llm/schema/schema_generator.rb +26 -0
- data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +2 -0
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +9 -0
- data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/tool/tool_generator.rb +96 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +1 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +7 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +49 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +5 -7
- data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +1 -1
- data/lib/ruby_llm/active_record/acts_as.rb +6 -26
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +125 -29
- data/lib/ruby_llm/active_record/chat_methods.rb +50 -26
- data/lib/ruby_llm/active_record/message_methods.rb +104 -4
- data/lib/ruby_llm/active_record/model_methods.rb +8 -10
- data/lib/ruby_llm/active_record/payload_helpers.rb +29 -0
- data/lib/ruby_llm/active_record/tool_call_methods.rb +18 -0
- data/lib/ruby_llm/agent.rb +15 -2
- data/lib/ruby_llm/aliases.json +141 -89
- data/lib/ruby_llm/aliases.rb +3 -0
- data/lib/ruby_llm/attachment.rb +44 -40
- data/lib/ruby_llm/chat.rb +229 -59
- data/lib/ruby_llm/configuration.rb +69 -75
- data/lib/ruby_llm/connection.rb +37 -10
- data/lib/ruby_llm/content.rb +15 -1
- data/lib/ruby_llm/cost.rb +224 -0
- data/lib/ruby_llm/deprecator.rb +24 -0
- data/lib/ruby_llm/embedding.rb +31 -1
- data/lib/ruby_llm/error.rb +16 -75
- data/lib/ruby_llm/error_middleware.rb +81 -0
- data/lib/ruby_llm/image.rb +39 -4
- data/lib/ruby_llm/instrumentation.rb +36 -0
- data/lib/ruby_llm/message.rb +20 -0
- data/lib/ruby_llm/mime_type.rb +25 -0
- data/lib/ruby_llm/model/info.rb +67 -14
- data/lib/ruby_llm/model/pricing.rb +19 -9
- data/lib/ruby_llm/model/pricing_category.rb +13 -2
- data/lib/ruby_llm/model/pricing_tier.rb +20 -9
- data/lib/ruby_llm/model_registry.rb +39 -0
- data/lib/ruby_llm/models.json +21796 -20601
- data/lib/ruby_llm/models.rb +107 -34
- data/lib/ruby_llm/models_schema.json +3 -0
- data/lib/ruby_llm/provider.rb +25 -4
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -133
- data/lib/ruby_llm/providers/anthropic/chat.rb +49 -15
- data/lib/ruby_llm/providers/anthropic/models.rb +4 -8
- data/lib/ruby_llm/providers/anthropic/streaming.rb +2 -0
- data/lib/ruby_llm/providers/anthropic/tools.rb +32 -3
- data/lib/ruby_llm/providers/anthropic.rb +4 -0
- data/lib/ruby_llm/providers/azure/media.rb +1 -1
- data/lib/ruby_llm/providers/azure.rb +4 -0
- data/lib/ruby_llm/providers/bedrock/auth.rb +1 -0
- data/lib/ruby_llm/providers/bedrock/chat.rb +26 -13
- data/lib/ruby_llm/providers/bedrock/media.rb +21 -3
- data/lib/ruby_llm/providers/bedrock/models.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming.rb +10 -1
- data/lib/ruby_llm/providers/bedrock.rb +5 -1
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +28 -103
- data/lib/ruby_llm/providers/deepseek/chat.rb +9 -0
- data/lib/ruby_llm/providers/deepseek.rb +4 -0
- data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -215
- data/lib/ruby_llm/providers/gemini/chat.rb +10 -4
- data/lib/ruby_llm/providers/gemini/images.rb +2 -2
- data/lib/ruby_llm/providers/gemini/media.rb +16 -9
- data/lib/ruby_llm/providers/gemini/models.rb +2 -4
- data/lib/ruby_llm/providers/gemini/streaming.rb +6 -1
- data/lib/ruby_llm/providers/gemini/tools.rb +5 -1
- data/lib/ruby_llm/providers/gemini.rb +4 -0
- data/lib/ruby_llm/providers/gpustack/chat.rb +8 -1
- data/lib/ruby_llm/providers/gpustack/models.rb +2 -0
- data/lib/ruby_llm/providers/gpustack.rb +4 -0
- data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -2
- data/lib/ruby_llm/providers/mistral/chat.rb +56 -5
- data/lib/ruby_llm/providers/mistral/media.rb +55 -0
- data/lib/ruby_llm/providers/mistral/models.rb +2 -0
- data/lib/ruby_llm/providers/mistral.rb +5 -1
- data/lib/ruby_llm/providers/ollama/chat.rb +8 -1
- data/lib/ruby_llm/providers/ollama.rb +4 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +157 -195
- data/lib/ruby_llm/providers/openai/chat.rb +61 -7
- data/lib/ruby_llm/providers/openai/images.rb +58 -6
- data/lib/ruby_llm/providers/openai/media.rb +40 -16
- data/lib/ruby_llm/providers/openai/models.rb +2 -4
- data/lib/ruby_llm/providers/openai/streaming.rb +7 -6
- data/lib/ruby_llm/providers/openai/tools.rb +2 -0
- data/lib/ruby_llm/providers/openai/transcription.rb +1 -0
- data/lib/ruby_llm/providers/openai.rb +10 -0
- data/lib/ruby_llm/providers/openrouter/chat.rb +36 -8
- data/lib/ruby_llm/providers/openrouter/images.rb +3 -3
- data/lib/ruby_llm/providers/openrouter/models.rb +1 -1
- data/lib/ruby_llm/providers/openrouter/streaming.rb +5 -6
- data/lib/ruby_llm/providers/openrouter.rb +4 -0
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
- data/lib/ruby_llm/providers/perplexity/chat.rb +11 -0
- data/lib/ruby_llm/providers/perplexity/media.rb +62 -0
- data/lib/ruby_llm/providers/perplexity/models.rb +12 -14
- data/lib/ruby_llm/providers/perplexity.rb +5 -1
- data/lib/ruby_llm/providers/vertexai.rb +8 -0
- data/lib/ruby_llm/providers/xai/chat.rb +9 -0
- data/lib/ruby_llm/providers/xai/models.rb +15 -27
- data/lib/ruby_llm/providers/xai.rb +5 -1
- data/lib/ruby_llm/railtie.rb +11 -1
- data/lib/ruby_llm/stream_accumulator.rb +45 -30
- data/lib/ruby_llm/streaming.rb +4 -0
- data/lib/ruby_llm/tokens.rb +8 -0
- data/lib/ruby_llm/tool.rb +24 -7
- data/lib/ruby_llm/tool_concurrency.rb +105 -0
- data/lib/ruby_llm/transcription.rb +2 -1
- data/lib/ruby_llm/utils.rb +39 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +11 -6
- data/lib/tasks/models.rake +45 -16
- data/lib/tasks/release.rake +51 -24
- data/lib/tasks/ruby_llm.rake +6 -5
- data/lib/tasks/vcr.rake +1 -1
- metadata +81 -21
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +0 -13
|
@@ -16,7 +16,7 @@ module RubyLLM
|
|
|
16
16
|
|
|
17
17
|
content = []
|
|
18
18
|
|
|
19
|
-
content
|
|
19
|
+
append_formatted_content(content, msg.content) unless msg.content.nil? || msg.content.empty?
|
|
20
20
|
|
|
21
21
|
msg.tool_calls.each_value do |tool_call|
|
|
22
22
|
content << format_tool_use_block(tool_call)
|
|
@@ -44,11 +44,23 @@ module RubyLLM
|
|
|
44
44
|
}
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def append_formatted_content(content_blocks, content)
|
|
48
|
+
formatted_content = Media.format_content(content)
|
|
49
|
+
if formatted_content.is_a?(Array)
|
|
50
|
+
content_blocks.concat(formatted_content)
|
|
51
|
+
else
|
|
52
|
+
content_blocks << formatted_content
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
47
56
|
def format_tool_result_block(msg)
|
|
57
|
+
content = msg.content
|
|
58
|
+
content = '(no output)' if content.nil? || (content.respond_to?(:empty?) && content.empty?)
|
|
59
|
+
|
|
48
60
|
{
|
|
49
61
|
type: 'tool_result',
|
|
50
62
|
tool_use_id: msg.tool_call_id,
|
|
51
|
-
content: Media.format_content(
|
|
63
|
+
content: Media.format_content(content)
|
|
52
64
|
}
|
|
53
65
|
end
|
|
54
66
|
|
|
@@ -69,12 +81,29 @@ module RubyLLM
|
|
|
69
81
|
|
|
70
82
|
def extract_tool_calls(data)
|
|
71
83
|
if json_delta?(data)
|
|
72
|
-
|
|
84
|
+
extract_tool_call_delta(data)
|
|
85
|
+
elsif content_block_start?(data)
|
|
86
|
+
extract_tool_call_start(data)
|
|
73
87
|
else
|
|
74
88
|
parse_tool_calls(data['content_block'])
|
|
75
89
|
end
|
|
76
90
|
end
|
|
77
91
|
|
|
92
|
+
def extract_tool_call_delta(data)
|
|
93
|
+
{ data['index'] => ToolCall.new(id: nil, name: nil, arguments: data.dig('delta', 'partial_json')) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def extract_tool_call_start(data)
|
|
97
|
+
tool_calls = parse_tool_calls(data['content_block'])
|
|
98
|
+
return tool_calls if tool_calls.nil? || data['index'].nil?
|
|
99
|
+
|
|
100
|
+
{ data['index'] => tool_calls.values.first }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def content_block_start?(data)
|
|
104
|
+
data['type'] == 'content_block_start'
|
|
105
|
+
end
|
|
106
|
+
|
|
78
107
|
def parse_tool_calls(content_blocks)
|
|
79
108
|
return nil if content_blocks.nil?
|
|
80
109
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
3
5
|
module RubyLLM
|
|
4
6
|
module Providers
|
|
5
7
|
class Bedrock
|
|
@@ -56,7 +58,7 @@ module RubyLLM
|
|
|
56
58
|
content: parse_text_content(content_blocks),
|
|
57
59
|
thinking: Thinking.build(text: thinking_text, signature: thinking_signature),
|
|
58
60
|
tool_calls: parse_tool_calls(content_blocks),
|
|
59
|
-
input_tokens: usage
|
|
61
|
+
input_tokens: input_tokens(usage),
|
|
60
62
|
output_tokens: usage['outputTokens'],
|
|
61
63
|
cached_tokens: usage['cacheReadInputTokens'],
|
|
62
64
|
cache_creation_tokens: usage['cacheWriteInputTokens'],
|
|
@@ -66,6 +68,13 @@ module RubyLLM
|
|
|
66
68
|
)
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
def input_tokens(usage)
|
|
72
|
+
input_tokens = usage['inputTokens']
|
|
73
|
+
return unless input_tokens
|
|
74
|
+
|
|
75
|
+
[input_tokens.to_i - usage['cacheReadInputTokens'].to_i - usage['cacheWriteInputTokens'].to_i, 0].max
|
|
76
|
+
end
|
|
77
|
+
|
|
69
78
|
def render_messages(messages)
|
|
70
79
|
rendered = []
|
|
71
80
|
tool_result_blocks = []
|
|
@@ -154,19 +163,23 @@ module RubyLLM
|
|
|
154
163
|
|
|
155
164
|
def render_tool_result_content(content)
|
|
156
165
|
return render_raw_tool_result_content(content.value) if content.is_a?(RubyLLM::Content::Raw)
|
|
166
|
+
return [{ json: content }] if content.is_a?(Hash) || content.is_a?(Array)
|
|
167
|
+
return render_content_tool_result_content(content) if content.is_a?(RubyLLM::Content)
|
|
157
168
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
[text_tool_result_block(content)]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def render_content_tool_result_content(content)
|
|
173
|
+
blocks = []
|
|
174
|
+
blocks << text_tool_result_block(content.text) unless content.text.to_s.empty?
|
|
175
|
+
content.attachments.each { |attachment| blocks << text_tool_result_block(attachment.for_llm) }
|
|
176
|
+
blocks.empty? ? [text_tool_result_block(nil)] : blocks
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def text_tool_result_block(text)
|
|
180
|
+
text = text.to_s
|
|
181
|
+
text = '(no output)' if text.empty?
|
|
182
|
+
{ text: text }
|
|
170
183
|
end
|
|
171
184
|
|
|
172
185
|
def render_raw_tool_result_content(raw_value)
|
|
@@ -38,15 +38,17 @@ module RubyLLM
|
|
|
38
38
|
case attachment.type
|
|
39
39
|
when :image
|
|
40
40
|
render_image_attachment(attachment)
|
|
41
|
-
when :pdf
|
|
41
|
+
when :pdf, :document
|
|
42
42
|
render_document_attachment(attachment, used_document_names:)
|
|
43
43
|
when :text
|
|
44
|
-
|
|
44
|
+
render_text_attachment(attachment)
|
|
45
45
|
else
|
|
46
46
|
raise UnsupportedAttachmentError, attachment.mime_type
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
SUPPORTED_DOCUMENT_FORMATS = %w[pdf csv doc docx xls xlsx html txt md].freeze
|
|
51
|
+
|
|
50
52
|
def render_image_attachment(attachment)
|
|
51
53
|
{
|
|
52
54
|
image: {
|
|
@@ -58,11 +60,19 @@ module RubyLLM
|
|
|
58
60
|
}
|
|
59
61
|
end
|
|
60
62
|
|
|
63
|
+
def render_text_attachment(attachment)
|
|
64
|
+
{ text: attachment.for_llm }
|
|
65
|
+
end
|
|
66
|
+
|
|
61
67
|
def render_document_attachment(attachment, used_document_names:)
|
|
68
|
+
format = document_format(attachment)
|
|
69
|
+
|
|
70
|
+
raise UnsupportedAttachmentError, attachment.mime_type unless supported_document_format?(attachment)
|
|
71
|
+
|
|
62
72
|
document_name = unique_document_name(sanitize_document_name(attachment.filename), used_document_names)
|
|
63
73
|
{
|
|
64
74
|
document: {
|
|
65
|
-
format:
|
|
75
|
+
format: format,
|
|
66
76
|
name: document_name,
|
|
67
77
|
source: {
|
|
68
78
|
bytes: attachment.encoded
|
|
@@ -71,6 +81,14 @@ module RubyLLM
|
|
|
71
81
|
}
|
|
72
82
|
end
|
|
73
83
|
|
|
84
|
+
def supported_document_format?(attachment)
|
|
85
|
+
SUPPORTED_DOCUMENT_FORMATS.include?(document_format(attachment))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def document_format(attachment)
|
|
89
|
+
attachment.extension || attachment.format
|
|
90
|
+
end
|
|
91
|
+
|
|
74
92
|
def sanitize_document_name(filename)
|
|
75
93
|
base = File.basename(filename.to_s, '.*')
|
|
76
94
|
safe = base.gsub(/[^a-zA-Z0-9_-]/, '_')
|
|
@@ -10,7 +10,7 @@ module RubyLLM
|
|
|
10
10
|
REGION_PREFIXES = %w[global us eu ap sa ca me af il].freeze
|
|
11
11
|
|
|
12
12
|
def models_api_base
|
|
13
|
-
"https://bedrock.#{bedrock_region}.amazonaws.com"
|
|
13
|
+
@config.bedrock_api_base || "https://bedrock.#{bedrock_region}.amazonaws.com"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def models_url
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'faraday'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
3
7
|
module RubyLLM
|
|
4
8
|
module Providers
|
|
5
9
|
class Bedrock
|
|
@@ -158,7 +162,10 @@ module RubyLLM
|
|
|
158
162
|
end
|
|
159
163
|
|
|
160
164
|
def extract_input_tokens(metadata_usage, usage, message_usage)
|
|
161
|
-
metadata_usage['inputTokens']
|
|
165
|
+
bedrock_usage = metadata_usage['inputTokens'] ? metadata_usage : usage
|
|
166
|
+
return Bedrock::Chat.input_tokens(bedrock_usage) if bedrock_usage['inputTokens']
|
|
167
|
+
|
|
168
|
+
message_usage['input_tokens']
|
|
162
169
|
end
|
|
163
170
|
|
|
164
171
|
def extract_output_tokens(metadata_usage, usage)
|
|
@@ -221,6 +228,7 @@ module RubyLLM
|
|
|
221
228
|
|
|
222
229
|
reasoning_text = reasoning_content['reasoningText'] || {}
|
|
223
230
|
return reasoning_text['text'] if reasoning_text['text']
|
|
231
|
+
return reasoning_content['text'] if reasoning_content['text']
|
|
224
232
|
return event.dig('delta', 'thinking') if event.dig('delta', 'type') == 'thinking_delta'
|
|
225
233
|
|
|
226
234
|
nil
|
|
@@ -241,6 +249,7 @@ module RubyLLM
|
|
|
241
249
|
reasoning_content = delta['reasoningContent'] || {}
|
|
242
250
|
reasoning_text = reasoning_content['reasoningText'] || {}
|
|
243
251
|
return reasoning_text['signature'] if reasoning_text['signature']
|
|
252
|
+
return reasoning_content['signature'] if reasoning_content['signature']
|
|
244
253
|
return event.dig('delta', 'signature') if event.dig('delta', 'type') == 'signature_delta'
|
|
245
254
|
|
|
246
255
|
nil
|
|
@@ -11,7 +11,7 @@ module RubyLLM
|
|
|
11
11
|
include Bedrock::Streaming
|
|
12
12
|
|
|
13
13
|
def api_base
|
|
14
|
-
"https://bedrock-runtime.#{bedrock_region}.amazonaws.com"
|
|
14
|
+
@config.bedrock_api_base || "https://bedrock-runtime.#{bedrock_region}.amazonaws.com"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def headers
|
|
@@ -53,6 +53,10 @@ module RubyLLM
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
class << self
|
|
56
|
+
def configuration_options
|
|
57
|
+
%i[bedrock_api_key bedrock_secret_key bedrock_region bedrock_session_token bedrock_api_base]
|
|
58
|
+
end
|
|
59
|
+
|
|
56
60
|
def configuration_requirements
|
|
57
61
|
%i[bedrock_api_key bedrock_secret_key bedrock_region]
|
|
58
62
|
end
|
|
@@ -3,43 +3,22 @@
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Providers
|
|
5
5
|
class DeepSeek
|
|
6
|
-
#
|
|
6
|
+
# Provider-level capability checks used outside the model registry.
|
|
7
7
|
module Capabilities
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def input_price_for(model_id)
|
|
25
|
-
PRICES.dig(model_family(model_id), :input_miss) || default_input_price
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def output_price_for(model_id)
|
|
29
|
-
PRICES.dig(model_family(model_id), :output) || default_output_price
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def cache_hit_price_for(model_id)
|
|
33
|
-
PRICES.dig(model_family(model_id), :input_hit) || default_cache_hit_price
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def supports_vision?(_model_id)
|
|
37
|
-
false
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def supports_functions?(model_id)
|
|
41
|
-
model_id.match?(/deepseek-chat/)
|
|
42
|
-
end
|
|
10
|
+
DEFAULT_CONTEXT_WINDOW = 1_000_000
|
|
11
|
+
DEFAULT_MAX_OUTPUT_TOKENS = 384_000
|
|
12
|
+
DEFAULT_PRICES = {
|
|
13
|
+
input: 0.14,
|
|
14
|
+
output: 0.28,
|
|
15
|
+
cache_read: 0.0028
|
|
16
|
+
}.freeze
|
|
17
|
+
PRO_PRICES = {
|
|
18
|
+
input: 0.435,
|
|
19
|
+
output: 0.87,
|
|
20
|
+
cache_read: 0.003625
|
|
21
|
+
}.freeze
|
|
43
22
|
|
|
44
23
|
def supports_tool_choice?(_model_id)
|
|
45
24
|
true
|
|
@@ -49,86 +28,32 @@ module RubyLLM
|
|
|
49
28
|
false
|
|
50
29
|
end
|
|
51
30
|
|
|
52
|
-
def
|
|
53
|
-
|
|
31
|
+
def context_window_for(_model_id)
|
|
32
|
+
DEFAULT_CONTEXT_WINDOW
|
|
54
33
|
end
|
|
55
34
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
when 'deepseek-chat' then 'DeepSeek V3'
|
|
59
|
-
when 'deepseek-reasoner' then 'DeepSeek R1'
|
|
60
|
-
else
|
|
61
|
-
model_id.split('-')
|
|
62
|
-
.map(&:capitalize)
|
|
63
|
-
.join(' ')
|
|
64
|
-
end
|
|
35
|
+
def max_tokens_for(_model_id)
|
|
36
|
+
DEFAULT_MAX_OUTPUT_TOKENS
|
|
65
37
|
end
|
|
66
38
|
|
|
67
|
-
def
|
|
68
|
-
'
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
case model_id
|
|
73
|
-
when /deepseek-reasoner/ then :reasoner
|
|
74
|
-
else :chat
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
PRICES = {
|
|
79
|
-
chat: {
|
|
80
|
-
input_hit: 0.07,
|
|
81
|
-
input_miss: 0.27,
|
|
82
|
-
output: 1.10
|
|
83
|
-
},
|
|
84
|
-
reasoner: {
|
|
85
|
-
input_hit: 0.14,
|
|
86
|
-
input_miss: 0.55,
|
|
87
|
-
output: 2.19
|
|
88
|
-
}
|
|
89
|
-
}.freeze
|
|
90
|
-
|
|
91
|
-
def default_input_price
|
|
92
|
-
0.27
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def default_output_price
|
|
96
|
-
1.10
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def default_cache_hit_price
|
|
100
|
-
0.07
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def modalities_for(_model_id)
|
|
104
|
-
{
|
|
105
|
-
input: ['text'],
|
|
106
|
-
output: ['text']
|
|
107
|
-
}
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def capabilities_for(model_id)
|
|
111
|
-
capabilities = ['streaming']
|
|
112
|
-
|
|
113
|
-
capabilities << 'function_calling' if model_id.match?(/deepseek-chat/)
|
|
114
|
-
|
|
39
|
+
def critical_capabilities_for(model_id)
|
|
40
|
+
v4_model = model_id.start_with?('deepseek-v4-')
|
|
41
|
+
capabilities = ['function_calling']
|
|
42
|
+
capabilities << 'structured_output' if v4_model
|
|
43
|
+
capabilities << 'reasoning' if model_id == 'deepseek-reasoner' || v4_model
|
|
115
44
|
capabilities
|
|
116
45
|
end
|
|
117
46
|
|
|
118
47
|
def pricing_for(model_id)
|
|
119
|
-
|
|
120
|
-
prices = PRICES.fetch(family, { input_miss: default_input_price, output: default_output_price })
|
|
121
|
-
|
|
122
|
-
standard_pricing = {
|
|
123
|
-
input_per_million: prices[:input_miss],
|
|
124
|
-
output_per_million: prices[:output]
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
standard_pricing[:cached_input_per_million] = prices[:input_hit] if prices[:input_hit]
|
|
48
|
+
prices = model_id == 'deepseek-v4-pro' ? PRO_PRICES : DEFAULT_PRICES
|
|
128
49
|
|
|
129
50
|
{
|
|
130
51
|
text_tokens: {
|
|
131
|
-
standard:
|
|
52
|
+
standard: {
|
|
53
|
+
input_per_million: prices[:input],
|
|
54
|
+
output_per_million: prices[:output],
|
|
55
|
+
cache_read_input_per_million: prices[:cache_read]
|
|
56
|
+
}
|
|
132
57
|
}
|
|
133
58
|
}
|
|
134
59
|
end
|
|
@@ -10,6 +10,15 @@ module RubyLLM
|
|
|
10
10
|
def format_role(role)
|
|
11
11
|
role.to_s
|
|
12
12
|
end
|
|
13
|
+
|
|
14
|
+
def format_content(content)
|
|
15
|
+
OpenAI::Media.format_content(
|
|
16
|
+
content,
|
|
17
|
+
document_attachments: :none,
|
|
18
|
+
image_attachments: false,
|
|
19
|
+
audio_attachments: false
|
|
20
|
+
)
|
|
21
|
+
end
|
|
13
22
|
end
|
|
14
23
|
end
|
|
15
24
|
end
|