ruby_llm 1.2.0 → 1.3.0rc1
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 +80 -133
- data/lib/ruby_llm/active_record/acts_as.rb +212 -33
- data/lib/ruby_llm/aliases.json +48 -6
- data/lib/ruby_llm/attachments/audio.rb +12 -0
- data/lib/ruby_llm/attachments/image.rb +9 -0
- data/lib/ruby_llm/attachments/pdf.rb +9 -0
- data/lib/ruby_llm/attachments.rb +78 -0
- data/lib/ruby_llm/chat.rb +22 -19
- data/lib/ruby_llm/configuration.rb +30 -1
- data/lib/ruby_llm/connection.rb +95 -0
- data/lib/ruby_llm/content.rb +51 -72
- data/lib/ruby_llm/context.rb +30 -0
- data/lib/ruby_llm/embedding.rb +13 -5
- data/lib/ruby_llm/error.rb +1 -1
- data/lib/ruby_llm/image.rb +13 -5
- data/lib/ruby_llm/message.rb +12 -4
- data/lib/ruby_llm/mime_types.rb +713 -0
- data/lib/ruby_llm/model_info.rb +208 -27
- data/lib/ruby_llm/models.json +25766 -2154
- data/lib/ruby_llm/models.rb +95 -14
- data/lib/ruby_llm/provider.rb +48 -90
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +76 -13
- data/lib/ruby_llm/providers/anthropic/chat.rb +7 -14
- data/lib/ruby_llm/providers/anthropic/media.rb +44 -34
- data/lib/ruby_llm/providers/anthropic/models.rb +15 -15
- data/lib/ruby_llm/providers/anthropic/tools.rb +2 -2
- data/lib/ruby_llm/providers/anthropic.rb +3 -3
- data/lib/ruby_llm/providers/bedrock/capabilities.rb +61 -2
- data/lib/ruby_llm/providers/bedrock/chat.rb +30 -73
- data/lib/ruby_llm/providers/bedrock/media.rb +56 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +50 -58
- data/lib/ruby_llm/providers/bedrock/streaming/base.rb +16 -0
- data/lib/ruby_llm/providers/bedrock.rb +14 -25
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +35 -2
- data/lib/ruby_llm/providers/deepseek.rb +3 -3
- data/lib/ruby_llm/providers/gemini/capabilities.rb +84 -3
- data/lib/ruby_llm/providers/gemini/chat.rb +8 -37
- data/lib/ruby_llm/providers/gemini/embeddings.rb +18 -34
- data/lib/ruby_llm/providers/gemini/images.rb +2 -2
- data/lib/ruby_llm/providers/gemini/media.rb +39 -110
- data/lib/ruby_llm/providers/gemini/models.rb +16 -22
- data/lib/ruby_llm/providers/gemini/tools.rb +1 -1
- data/lib/ruby_llm/providers/gemini.rb +3 -3
- data/lib/ruby_llm/providers/ollama/chat.rb +28 -0
- data/lib/ruby_llm/providers/ollama/media.rb +44 -0
- data/lib/ruby_llm/providers/ollama.rb +34 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +78 -3
- data/lib/ruby_llm/providers/openai/chat.rb +6 -4
- data/lib/ruby_llm/providers/openai/embeddings.rb +8 -12
- data/lib/ruby_llm/providers/openai/media.rb +38 -21
- data/lib/ruby_llm/providers/openai/models.rb +16 -17
- data/lib/ruby_llm/providers/openai/tools.rb +9 -5
- data/lib/ruby_llm/providers/openai.rb +7 -5
- data/lib/ruby_llm/providers/openrouter/models.rb +88 -0
- data/lib/ruby_llm/providers/openrouter.rb +31 -0
- data/lib/ruby_llm/stream_accumulator.rb +4 -4
- data/lib/ruby_llm/streaming.rb +3 -3
- data/lib/ruby_llm/utils.rb +22 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +15 -5
- data/lib/tasks/models.rake +69 -33
- data/lib/tasks/models_docs.rake +164 -121
- data/lib/tasks/vcr.rake +4 -2
- metadata +23 -14
- data/lib/tasks/browser_helper.rb +0 -97
- data/lib/tasks/capability_generator.rb +0 -123
- data/lib/tasks/capability_scraper.rb +0 -224
- data/lib/tasks/cli_helper.rb +0 -22
- data/lib/tasks/code_validator.rb +0 -29
- data/lib/tasks/model_updater.rb +0 -66
@@ -15,13 +15,13 @@ module RubyLLM
|
|
15
15
|
|
16
16
|
module_function
|
17
17
|
|
18
|
-
def api_base
|
18
|
+
def api_base(_config)
|
19
19
|
'https://api.anthropic.com'
|
20
20
|
end
|
21
21
|
|
22
|
-
def headers
|
22
|
+
def headers(config)
|
23
23
|
{
|
24
|
-
'x-api-key' =>
|
24
|
+
'x-api-key' => config.anthropic_api_key,
|
25
25
|
'anthropic-version' => '2023-06-01'
|
26
26
|
}
|
27
27
|
end
|
@@ -149,8 +149,6 @@ module RubyLLM
|
|
149
149
|
0.2
|
150
150
|
end
|
151
151
|
|
152
|
-
private
|
153
|
-
|
154
152
|
# Converts a model ID to a human-readable format
|
155
153
|
# @param id [String] the model identifier
|
156
154
|
# @return [String] the humanized model name
|
@@ -162,6 +160,67 @@ module RubyLLM
|
|
162
160
|
.map(&:capitalize)
|
163
161
|
.join(' ')
|
164
162
|
end
|
163
|
+
|
164
|
+
def modalities_for(model_id)
|
165
|
+
modalities = {
|
166
|
+
input: ['text'],
|
167
|
+
output: ['text']
|
168
|
+
}
|
169
|
+
|
170
|
+
# Vision support for Claude models
|
171
|
+
if model_id.match?(/anthropic\.claude/) && supports_vision?(model_id)
|
172
|
+
modalities[:input] << 'image'
|
173
|
+
modalities[:input] << 'pdf'
|
174
|
+
end
|
175
|
+
|
176
|
+
modalities
|
177
|
+
end
|
178
|
+
|
179
|
+
def capabilities_for(model_id)
|
180
|
+
capabilities = []
|
181
|
+
|
182
|
+
# Streaming
|
183
|
+
capabilities << 'streaming' if model_id.match?(/anthropic\.claude/)
|
184
|
+
|
185
|
+
# Function calling & structured output
|
186
|
+
capabilities << 'function_calling' if supports_functions?(model_id)
|
187
|
+
|
188
|
+
capabilities << 'structured_output' if supports_json_mode?(model_id)
|
189
|
+
|
190
|
+
# Extended thinking for 3.7 models
|
191
|
+
capabilities << 'reasoning' if model_id.match?(/claude-3-7/)
|
192
|
+
|
193
|
+
# Batch capabilities for newer Claude models
|
194
|
+
if model_id.match?(/claude-3\.5|claude-3-7/)
|
195
|
+
capabilities << 'batch'
|
196
|
+
capabilities << 'citations'
|
197
|
+
end
|
198
|
+
|
199
|
+
capabilities
|
200
|
+
end
|
201
|
+
|
202
|
+
def pricing_for(model_id)
|
203
|
+
family = model_family(model_id)
|
204
|
+
prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })
|
205
|
+
|
206
|
+
standard_pricing = {
|
207
|
+
input_per_million: prices[:input],
|
208
|
+
output_per_million: prices[:output]
|
209
|
+
}
|
210
|
+
|
211
|
+
# Batch pricing - typically 50% of standard
|
212
|
+
batch_pricing = {
|
213
|
+
input_per_million: prices[:input] * 0.5,
|
214
|
+
output_per_million: prices[:output] * 0.5
|
215
|
+
}
|
216
|
+
|
217
|
+
{
|
218
|
+
text_tokens: {
|
219
|
+
standard: standard_pricing,
|
220
|
+
batch: batch_pricing
|
221
|
+
}
|
222
|
+
}
|
223
|
+
end
|
165
224
|
end
|
166
225
|
end
|
167
226
|
end
|
@@ -5,58 +5,22 @@ module RubyLLM
|
|
5
5
|
module Bedrock
|
6
6
|
# Chat methods for the AWS Bedrock API implementation
|
7
7
|
module Chat
|
8
|
-
|
9
|
-
|
10
|
-
def completion_url
|
11
|
-
"model/#{@model_id}/invoke"
|
12
|
-
end
|
13
|
-
|
14
|
-
def render_payload(messages, tools:, temperature:, model:, stream: false) # rubocop:disable Lint/UnusedMethodArgument
|
15
|
-
# Hold model_id in instance variable for use in completion_url and stream_url
|
16
|
-
@model_id = model
|
8
|
+
module_function
|
17
9
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
def sync_response(connection, payload)
|
11
|
+
signature = sign_request("#{connection.connection.url_prefix}#{completion_url}", config: connection.config,
|
12
|
+
payload:)
|
13
|
+
response = connection.post completion_url, payload do |req|
|
14
|
+
req.headers.merge! build_headers(signature.headers, streaming: block_given?)
|
23
15
|
end
|
24
|
-
|
25
|
-
|
26
|
-
def separate_messages(messages)
|
27
|
-
messages.partition { |msg| msg.role == :system }
|
28
|
-
end
|
29
|
-
|
30
|
-
def build_system_content(system_messages)
|
31
|
-
if system_messages.length > 1
|
32
|
-
RubyLLM.logger.warn(
|
33
|
-
"Amazon Bedrock's Claude implementation only supports a single system message. " \
|
34
|
-
'Multiple system messages will be combined into one.'
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
|
-
system_messages.map { |msg| format_message(msg)[:content] }.join("\n\n")
|
39
|
-
end
|
40
|
-
|
41
|
-
def build_base_payload(chat_messages, temperature, model)
|
42
|
-
{
|
43
|
-
anthropic_version: 'bedrock-2023-05-31',
|
44
|
-
messages: chat_messages.map { |msg| format_message(msg) },
|
45
|
-
temperature: temperature,
|
46
|
-
max_tokens: RubyLLM.models.find(model).max_tokens
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
def add_optional_fields(payload, system_content:, tools:)
|
51
|
-
payload[:tools] = tools.values.map { |t| function_for(t) } if tools.any?
|
52
|
-
payload[:system] = system_content unless system_content.empty?
|
16
|
+
Anthropic::Chat.parse_completion_response response
|
53
17
|
end
|
54
18
|
|
55
19
|
def format_message(msg)
|
56
20
|
if msg.tool_call?
|
57
|
-
format_tool_call(msg)
|
21
|
+
Anthropic::Tools.format_tool_call(msg)
|
58
22
|
elsif msg.tool_result?
|
59
|
-
format_tool_result(msg)
|
23
|
+
Anthropic::Tools.format_tool_result(msg)
|
60
24
|
else
|
61
25
|
format_basic_message(msg)
|
62
26
|
end
|
@@ -64,43 +28,36 @@ module RubyLLM
|
|
64
28
|
|
65
29
|
def format_basic_message(msg)
|
66
30
|
{
|
67
|
-
role: convert_role(msg.role),
|
68
|
-
content:
|
31
|
+
role: Anthropic::Chat.convert_role(msg.role),
|
32
|
+
content: Media.format_content(msg.content)
|
69
33
|
}
|
70
34
|
end
|
71
35
|
|
72
|
-
|
73
|
-
case role
|
74
|
-
when :tool, :user then 'user'
|
75
|
-
when :system then 'system'
|
76
|
-
else 'assistant'
|
77
|
-
end
|
78
|
-
end
|
36
|
+
private
|
79
37
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
38
|
+
def completion_url
|
39
|
+
"model/#{@model_id}/invoke"
|
40
|
+
end
|
83
41
|
|
84
|
-
|
85
|
-
|
42
|
+
def render_payload(messages, tools:, temperature:, model:, stream: false) # rubocop:disable Lint/UnusedMethodArgument
|
43
|
+
# Hold model_id in instance variable for use in completion_url and stream_url
|
44
|
+
@model_id = model
|
86
45
|
|
87
|
-
|
88
|
-
|
46
|
+
system_messages, chat_messages = Anthropic::Chat.separate_messages(messages)
|
47
|
+
system_content = Anthropic::Chat.build_system_content(system_messages)
|
89
48
|
|
90
|
-
|
91
|
-
|
92
|
-
|
49
|
+
build_base_payload(chat_messages, temperature, model).tap do |payload|
|
50
|
+
Anthropic::Chat.add_optional_fields(payload, system_content:, tools:)
|
51
|
+
end
|
93
52
|
end
|
94
53
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
model_id: data['model']
|
103
|
-
)
|
54
|
+
def build_base_payload(chat_messages, temperature, model)
|
55
|
+
{
|
56
|
+
anthropic_version: 'bedrock-2023-05-31',
|
57
|
+
messages: chat_messages.map { |msg| format_message(msg) },
|
58
|
+
temperature: temperature,
|
59
|
+
max_tokens: RubyLLM.models.find(model)&.max_tokens || 4096
|
60
|
+
}
|
104
61
|
end
|
105
62
|
end
|
106
63
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyLLM
|
4
|
+
module Providers
|
5
|
+
module Bedrock
|
6
|
+
# Media handling methods for the Bedrock API integration
|
7
|
+
module Media
|
8
|
+
extend Anthropic::Media
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def format_content(content)
|
13
|
+
return [Anthropic::Media.format_text(content)] unless content.is_a?(Content)
|
14
|
+
|
15
|
+
parts = []
|
16
|
+
parts << Anthropic::Media.format_text(content.text) if content.text
|
17
|
+
|
18
|
+
content.attachments.each do |attachment|
|
19
|
+
case attachment
|
20
|
+
when Attachments::Image
|
21
|
+
parts << format_image(attachment)
|
22
|
+
when Attachments::PDF
|
23
|
+
parts << format_pdf(attachment)
|
24
|
+
else
|
25
|
+
raise "Unsupported attachment type: #{attachment.class}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
parts
|
30
|
+
end
|
31
|
+
|
32
|
+
def format_image(image)
|
33
|
+
{
|
34
|
+
type: 'image',
|
35
|
+
source: {
|
36
|
+
type: 'base64',
|
37
|
+
media_type: image.mime_type,
|
38
|
+
data: image.encoded
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def format_pdf(pdf)
|
44
|
+
{
|
45
|
+
type: 'document',
|
46
|
+
source: {
|
47
|
+
type: 'base64',
|
48
|
+
media_type: pdf.mime_type,
|
49
|
+
data: pdf.encoded
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -5,15 +5,14 @@ module RubyLLM
|
|
5
5
|
module Bedrock
|
6
6
|
# Models methods for the AWS Bedrock API implementation
|
7
7
|
module Models
|
8
|
-
def list_models
|
9
|
-
|
10
|
-
|
11
|
-
full_models_url = "#{
|
12
|
-
signature = sign_request(full_models_url, method: :get)
|
13
|
-
response = connection.get(
|
8
|
+
def list_models(connection:)
|
9
|
+
config = connection.config
|
10
|
+
mgmt_api_base = "https://bedrock.#{config.bedrock_region}.amazonaws.com"
|
11
|
+
full_models_url = "#{mgmt_api_base}/#{models_url}"
|
12
|
+
signature = sign_request(full_models_url, config: config, method: :get)
|
13
|
+
response = connection.get(full_models_url) do |req|
|
14
14
|
req.headers.merge! signature.headers
|
15
15
|
end
|
16
|
-
@connection = nil # reset connection since base url is different
|
17
16
|
|
18
17
|
parse_list_models_response(response, slug, capabilities)
|
19
18
|
end
|
@@ -25,65 +24,58 @@ module RubyLLM
|
|
25
24
|
end
|
26
25
|
|
27
26
|
def parse_list_models_response(response, slug, capabilities)
|
28
|
-
|
29
|
-
data.filter { |model| model['modelId'].include?('claude') }
|
30
|
-
.map { |model| create_model_info(model, slug, capabilities) }
|
31
|
-
end
|
27
|
+
models = Array(response.body['modelSummaries'])
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
**base_model_attributes(model_id, model, slug),
|
37
|
-
**capability_attributes(model_id, capabilities),
|
38
|
-
**pricing_attributes(model_id, capabilities),
|
39
|
-
metadata: build_metadata(model)
|
40
|
-
)
|
41
|
-
end
|
29
|
+
# Filter to include only models we care about
|
30
|
+
models.select { |m| m['modelId'].include?('claude') }.map do |model_data|
|
31
|
+
model_id = model_data['modelId']
|
42
32
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
33
|
+
ModelInfo.new(
|
34
|
+
id: model_id_with_region(model_id, model_data),
|
35
|
+
name: model_data['modelName'] || capabilities.format_display_name(model_id),
|
36
|
+
provider: slug,
|
37
|
+
family: capabilities.model_family(model_id),
|
38
|
+
created_at: nil,
|
39
|
+
context_window: capabilities.context_window_for(model_id),
|
40
|
+
max_output_tokens: capabilities.max_tokens_for(model_id),
|
41
|
+
modalities: capabilities.modalities_for(model_id),
|
42
|
+
capabilities: capabilities.capabilities_for(model_id),
|
43
|
+
pricing: capabilities.pricing_for(model_id),
|
44
|
+
metadata: {
|
45
|
+
provider_name: model_data['providerName'],
|
46
|
+
inference_types: model_data['inferenceTypesSupported'] || [],
|
47
|
+
streaming_supported: model_data['responseStreamingSupported'] || false,
|
48
|
+
input_modalities: model_data['inputModalities'] || [],
|
49
|
+
output_modalities: model_data['outputModalities'] || []
|
50
|
+
}
|
51
|
+
)
|
52
|
+
end
|
50
53
|
end
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
"us.#{model_id}"
|
57
|
-
end
|
55
|
+
# Simple test-friendly method that only sets the ID
|
56
|
+
def create_model_info(model_data, slug, _capabilities)
|
57
|
+
model_id = model_data['modelId']
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
59
|
+
ModelInfo.new(
|
60
|
+
id: model_id_with_region(model_id, model_data),
|
61
|
+
name: model_data['modelName'] || model_id,
|
62
|
+
provider: slug,
|
63
|
+
family: 'claude',
|
64
|
+
created_at: nil,
|
65
|
+
context_window: 200_000,
|
66
|
+
max_output_tokens: 4096,
|
67
|
+
modalities: { input: ['text'], output: ['text'] },
|
68
|
+
capabilities: [],
|
69
|
+
pricing: {},
|
70
|
+
metadata: {}
|
71
|
+
)
|
69
72
|
end
|
70
73
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
output_price_per_million: capabilities.output_price_for(model_id)
|
75
|
-
}
|
76
|
-
end
|
74
|
+
def model_id_with_region(model_id, model_data)
|
75
|
+
return model_id unless model_data['inferenceTypesSupported']&.include?('INFERENCE_PROFILE')
|
76
|
+
return model_id if model_data['inferenceTypesSupported']&.include?('ON_DEMAND')
|
77
77
|
|
78
|
-
|
79
|
-
{
|
80
|
-
provider_name: model['providerName'],
|
81
|
-
customizations_supported: model['customizationsSupported'] || [],
|
82
|
-
inference_configurations: model['inferenceTypesSupported'] || [],
|
83
|
-
response_streaming_supported: model['responseStreamingSupported'] || false,
|
84
|
-
input_modalities: model['inputModalities'] || [],
|
85
|
-
output_modalities: model['outputModalities'] || []
|
86
|
-
}
|
78
|
+
"us.#{model_id}"
|
87
79
|
end
|
88
80
|
end
|
89
81
|
end
|
@@ -29,6 +29,22 @@ module RubyLLM
|
|
29
29
|
"model/#{@model_id}/invoke-with-response-stream"
|
30
30
|
end
|
31
31
|
|
32
|
+
def stream_response(connection, payload, &block)
|
33
|
+
signature = sign_request("#{connection.connection.url_prefix}#{stream_url}", config: connection.config,
|
34
|
+
payload:)
|
35
|
+
accumulator = StreamAccumulator.new
|
36
|
+
|
37
|
+
connection.post stream_url, payload do |req|
|
38
|
+
req.headers.merge! build_headers(signature.headers, streaming: block_given?)
|
39
|
+
req.options.on_data = handle_stream do |chunk|
|
40
|
+
accumulator.add chunk
|
41
|
+
block.call chunk
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
accumulator.to_message
|
46
|
+
end
|
47
|
+
|
32
48
|
def handle_stream(&block)
|
33
49
|
buffer = String.new
|
34
50
|
proc do |chunk, _bytes, env|
|
@@ -13,27 +13,16 @@ module RubyLLM
|
|
13
13
|
extend Bedrock::Streaming
|
14
14
|
extend Bedrock::Models
|
15
15
|
extend Bedrock::Signing
|
16
|
-
|
17
|
-
# This provider currently only supports Anthropic models, so the tools/media implementation is shared
|
18
|
-
extend Anthropic::Media
|
16
|
+
extend Bedrock::Media
|
19
17
|
extend Anthropic::Tools
|
20
18
|
|
21
19
|
module_function
|
22
20
|
|
23
|
-
def api_base
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def post(url, payload)
|
28
|
-
signature = sign_request("#{connection.url_prefix}#{url}", payload:)
|
29
|
-
connection.post url, payload do |req|
|
30
|
-
req.headers.merge! build_headers(signature.headers, streaming: block_given?)
|
31
|
-
|
32
|
-
yield req if block_given?
|
33
|
-
end
|
21
|
+
def api_base(config)
|
22
|
+
"https://bedrock-runtime.#{config.bedrock_region}.amazonaws.com"
|
34
23
|
end
|
35
24
|
|
36
|
-
def parse_error(response)
|
25
|
+
def parse_error(response)
|
37
26
|
return if response.body.empty?
|
38
27
|
|
39
28
|
body = try_parse_json(response.body)
|
@@ -49,25 +38,25 @@ module RubyLLM
|
|
49
38
|
end
|
50
39
|
end
|
51
40
|
|
52
|
-
def sign_request(url, method: :post, payload: nil)
|
53
|
-
signer = create_signer
|
54
|
-
request = build_request(url, method:, payload:)
|
41
|
+
def sign_request(url, config:, method: :post, payload: nil)
|
42
|
+
signer = create_signer(config)
|
43
|
+
request = build_request(url, config:, method:, payload:)
|
55
44
|
signer.sign_request(request)
|
56
45
|
end
|
57
46
|
|
58
|
-
def create_signer
|
47
|
+
def create_signer(config)
|
59
48
|
Signing::Signer.new({
|
60
|
-
access_key_id:
|
61
|
-
secret_access_key:
|
62
|
-
session_token:
|
63
|
-
region:
|
49
|
+
access_key_id: config.bedrock_api_key,
|
50
|
+
secret_access_key: config.bedrock_secret_key,
|
51
|
+
session_token: config.bedrock_session_token,
|
52
|
+
region: config.bedrock_region,
|
64
53
|
service: 'bedrock'
|
65
54
|
})
|
66
55
|
end
|
67
56
|
|
68
|
-
def build_request(url, method: :post, payload: nil)
|
57
|
+
def build_request(url, config:, method: :post, payload: nil)
|
69
58
|
{
|
70
|
-
connection: connection,
|
59
|
+
connection: connection(config),
|
71
60
|
http_method: method,
|
72
61
|
url: url || completion_url,
|
73
62
|
body: payload ? JSON.generate(payload, ascii_only: false) : nil
|
@@ -114,8 +114,6 @@ module RubyLLM
|
|
114
114
|
}
|
115
115
|
}.freeze
|
116
116
|
|
117
|
-
private
|
118
|
-
|
119
117
|
# Default input price when model family can't be determined
|
120
118
|
# @return [Float] the default input price
|
121
119
|
def default_input_price
|
@@ -133,6 +131,41 @@ module RubyLLM
|
|
133
131
|
def default_cache_hit_price
|
134
132
|
0.07 # Default to chat cache hit price
|
135
133
|
end
|
134
|
+
|
135
|
+
def modalities_for(_model_id)
|
136
|
+
{
|
137
|
+
input: ['text'],
|
138
|
+
output: ['text']
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
def capabilities_for(model_id)
|
143
|
+
capabilities = ['streaming']
|
144
|
+
|
145
|
+
# Function calling for chat models
|
146
|
+
capabilities << 'function_calling' if model_id.match?(/deepseek-chat/)
|
147
|
+
|
148
|
+
capabilities
|
149
|
+
end
|
150
|
+
|
151
|
+
def pricing_for(model_id)
|
152
|
+
family = model_family(model_id)
|
153
|
+
prices = PRICES.fetch(family, { input_miss: default_input_price, output: default_output_price })
|
154
|
+
|
155
|
+
standard_pricing = {
|
156
|
+
input_per_million: prices[:input_miss],
|
157
|
+
output_per_million: prices[:output]
|
158
|
+
}
|
159
|
+
|
160
|
+
# Add cached pricing if available
|
161
|
+
standard_pricing[:cached_input_per_million] = prices[:input_hit] if prices[:input_hit]
|
162
|
+
|
163
|
+
{
|
164
|
+
text_tokens: {
|
165
|
+
standard: standard_pricing
|
166
|
+
}
|
167
|
+
}
|
168
|
+
end
|
136
169
|
end
|
137
170
|
end
|
138
171
|
end
|
@@ -9,13 +9,13 @@ module RubyLLM
|
|
9
9
|
|
10
10
|
module_function
|
11
11
|
|
12
|
-
def api_base
|
12
|
+
def api_base(_config)
|
13
13
|
'https://api.deepseek.com'
|
14
14
|
end
|
15
15
|
|
16
|
-
def headers
|
16
|
+
def headers(config)
|
17
17
|
{
|
18
|
-
'Authorization' => "Bearer #{
|
18
|
+
'Authorization' => "Bearer #{config.deepseek_api_key}"
|
19
19
|
}
|
20
20
|
end
|
21
21
|
|