ruby_llm 1.14.0 → 1.14.1
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 +6 -2
- data/lib/ruby_llm/active_record/chat_methods.rb +12 -5
- data/lib/ruby_llm/active_record/message_methods.rb +2 -13
- data/lib/ruby_llm/active_record/payload_helpers.rb +26 -0
- data/lib/ruby_llm/active_record/tool_call_methods.rb +2 -15
- data/lib/ruby_llm/aliases.json +19 -10
- data/lib/ruby_llm/models.json +4939 -8152
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -133
- data/lib/ruby_llm/providers/anthropic/models.rb +2 -8
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -119
- data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -215
- data/lib/ruby_llm/providers/gemini/models.rb +2 -4
- data/lib/ruby_llm/providers/openai/capabilities.rb +95 -203
- data/lib/ruby_llm/providers/openai/models.rb +2 -4
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
- data/lib/ruby_llm/providers/perplexity/models.rb +12 -14
- data/lib/ruby_llm/version.rb +1 -1
- metadata +3 -2
|
@@ -3,37 +3,10 @@
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Providers
|
|
5
5
|
class Anthropic
|
|
6
|
-
#
|
|
6
|
+
# Provider-level capability checks used outside the model registry.
|
|
7
7
|
module Capabilities
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
|
-
def determine_context_window(_model_id)
|
|
11
|
-
200_000
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def determine_max_tokens(model_id)
|
|
15
|
-
case model_id
|
|
16
|
-
when /claude-3-7-sonnet/, /claude-3-5/ then 8_192
|
|
17
|
-
else 4_096
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def get_input_price(model_id)
|
|
22
|
-
PRICES.dig(model_family(model_id), :input) || default_input_price
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def get_output_price(model_id)
|
|
26
|
-
PRICES.dig(model_family(model_id), :output) || default_output_price
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def supports_vision?(model_id)
|
|
30
|
-
!model_id.match?(/claude-[12]/)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def supports_functions?(model_id)
|
|
34
|
-
!model_id.match?(/claude-[12]/)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
10
|
def supports_tool_choice?(_model_id)
|
|
38
11
|
true
|
|
39
12
|
end
|
|
@@ -41,111 +14,6 @@ module RubyLLM
|
|
|
41
14
|
def supports_tool_parallel_control?(_model_id)
|
|
42
15
|
true
|
|
43
16
|
end
|
|
44
|
-
|
|
45
|
-
def supports_json_mode?(model_id)
|
|
46
|
-
!model_id.match?(/claude-[12]/)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def supports_structured_output?(model_id)
|
|
50
|
-
match = model_id.match(/claude-(?:sonnet|opus|haiku)-(\d+)-(\d+)/)
|
|
51
|
-
return false unless match
|
|
52
|
-
|
|
53
|
-
major = match[1].to_i
|
|
54
|
-
minor = match[2].to_i
|
|
55
|
-
major > 4 || (major == 4 && minor >= 5)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def supports_extended_thinking?(model_id)
|
|
59
|
-
model_id.match?(/claude-3-7-sonnet/)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def model_family(model_id)
|
|
63
|
-
case model_id
|
|
64
|
-
when /claude-3-7-sonnet/ then 'claude-3-7-sonnet'
|
|
65
|
-
when /claude-3-5-sonnet/ then 'claude-3-5-sonnet'
|
|
66
|
-
when /claude-3-5-haiku/ then 'claude-3-5-haiku'
|
|
67
|
-
when /claude-3-opus/ then 'claude-3-opus'
|
|
68
|
-
when /claude-3-sonnet/ then 'claude-3-sonnet'
|
|
69
|
-
when /claude-3-haiku/ then 'claude-3-haiku'
|
|
70
|
-
else 'claude-2'
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def model_type(_)
|
|
75
|
-
'chat'
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
PRICES = {
|
|
79
|
-
'claude-3-7-sonnet': { input: 3.0, output: 15.0 },
|
|
80
|
-
'claude-3-5-sonnet': { input: 3.0, output: 15.0 },
|
|
81
|
-
'claude-3-5-haiku': { input: 0.80, output: 4.0 },
|
|
82
|
-
'claude-3-opus': { input: 15.0, output: 75.0 },
|
|
83
|
-
'claude-3-haiku': { input: 0.25, output: 1.25 },
|
|
84
|
-
'claude-2': { input: 3.0, output: 15.0 }
|
|
85
|
-
}.freeze
|
|
86
|
-
|
|
87
|
-
def default_input_price
|
|
88
|
-
3.0
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def default_output_price
|
|
92
|
-
15.0
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def modalities_for(model_id)
|
|
96
|
-
modalities = {
|
|
97
|
-
input: ['text'],
|
|
98
|
-
output: ['text']
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
unless model_id.match?(/claude-[12]/)
|
|
102
|
-
modalities[:input] << 'image'
|
|
103
|
-
modalities[:input] << 'pdf'
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
modalities
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def capabilities_for(model_id)
|
|
110
|
-
capabilities = ['streaming']
|
|
111
|
-
|
|
112
|
-
unless model_id.match?(/claude-[12]/)
|
|
113
|
-
capabilities << 'function_calling'
|
|
114
|
-
capabilities << 'batch'
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
capabilities << 'structured_output' if supports_structured_output?(model_id)
|
|
118
|
-
capabilities << 'reasoning' if model_id.match?(/claude-3-7-sonnet|claude-(?:sonnet|opus|haiku)-4/)
|
|
119
|
-
capabilities << 'citations' if model_id.match?(/claude-3\.5|claude-3-7/)
|
|
120
|
-
capabilities
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def pricing_for(model_id)
|
|
124
|
-
family = model_family(model_id)
|
|
125
|
-
prices = PRICES.fetch(family.to_sym, { input: default_input_price, output: default_output_price })
|
|
126
|
-
|
|
127
|
-
standard_pricing = {
|
|
128
|
-
input_per_million: prices[:input],
|
|
129
|
-
output_per_million: prices[:output]
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
batch_pricing = {
|
|
133
|
-
input_per_million: prices[:input] * 0.5,
|
|
134
|
-
output_per_million: prices[:output] * 0.5
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if model_id.match?(/claude-3-7/)
|
|
138
|
-
standard_pricing[:reasoning_output_per_million] = prices[:output] * 2.5
|
|
139
|
-
batch_pricing[:reasoning_output_per_million] = prices[:output] * 1.25
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
{
|
|
143
|
-
text_tokens: {
|
|
144
|
-
standard: standard_pricing,
|
|
145
|
-
batch: batch_pricing
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
end
|
|
149
17
|
end
|
|
150
18
|
end
|
|
151
19
|
end
|
|
@@ -11,21 +11,15 @@ module RubyLLM
|
|
|
11
11
|
'v1/models'
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def parse_list_models_response(response, slug,
|
|
14
|
+
def parse_list_models_response(response, slug, _capabilities)
|
|
15
15
|
Array(response.body['data']).map do |model_data|
|
|
16
16
|
model_id = model_data['id']
|
|
17
17
|
|
|
18
18
|
Model::Info.new(
|
|
19
19
|
id: model_id,
|
|
20
|
-
name: model_data['display_name'],
|
|
20
|
+
name: model_data['display_name'] || model_id,
|
|
21
21
|
provider: slug,
|
|
22
|
-
family: capabilities.model_family(model_id),
|
|
23
22
|
created_at: Time.parse(model_data['created_at']),
|
|
24
|
-
context_window: capabilities.determine_context_window(model_id),
|
|
25
|
-
max_output_tokens: capabilities.determine_max_tokens(model_id),
|
|
26
|
-
modalities: capabilities.modalities_for(model_id),
|
|
27
|
-
capabilities: capabilities.capabilities_for(model_id),
|
|
28
|
-
pricing: capabilities.pricing_for(model_id),
|
|
29
23
|
metadata: {}
|
|
30
24
|
)
|
|
31
25
|
end
|
|
@@ -3,44 +3,10 @@
|
|
|
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
|
-
def context_window_for(model_id)
|
|
11
|
-
case model_id
|
|
12
|
-
when /deepseek-(?:chat|reasoner)/ then 64_000
|
|
13
|
-
else 32_768
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def max_tokens_for(model_id)
|
|
18
|
-
case model_id
|
|
19
|
-
when /deepseek-(?:chat|reasoner)/ then 8_192
|
|
20
|
-
else 4_096
|
|
21
|
-
end
|
|
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
|
|
43
|
-
|
|
44
10
|
def supports_tool_choice?(_model_id)
|
|
45
11
|
true
|
|
46
12
|
end
|
|
@@ -48,90 +14,6 @@ module RubyLLM
|
|
|
48
14
|
def supports_tool_parallel_control?(_model_id)
|
|
49
15
|
false
|
|
50
16
|
end
|
|
51
|
-
|
|
52
|
-
def supports_json_mode?(_model_id)
|
|
53
|
-
false
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def format_display_name(model_id)
|
|
57
|
-
case model_id
|
|
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
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def model_type(_model_id)
|
|
68
|
-
'chat'
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def model_family(model_id)
|
|
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
|
-
|
|
115
|
-
capabilities
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def pricing_for(model_id)
|
|
119
|
-
family = model_family(model_id)
|
|
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]
|
|
128
|
-
|
|
129
|
-
{
|
|
130
|
-
text_tokens: {
|
|
131
|
-
standard: standard_pricing
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
end
|
|
135
17
|
end
|
|
136
18
|
end
|
|
137
19
|
end
|
|
@@ -3,13 +3,35 @@
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Providers
|
|
5
5
|
class Gemini
|
|
6
|
-
#
|
|
6
|
+
# Provider-level capability checks and narrow registry fallbacks.
|
|
7
7
|
module Capabilities
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
|
+
PRICES = {
|
|
11
|
+
flash_2: { input: 0.10, output: 0.40 }, # rubocop:disable Naming/VariableNumber
|
|
12
|
+
flash_lite_2: { input: 0.075, output: 0.30 }, # rubocop:disable Naming/VariableNumber
|
|
13
|
+
flash: { input: 0.075, output: 0.30 },
|
|
14
|
+
flash_8b: { input: 0.0375, output: 0.15 },
|
|
15
|
+
pro: { input: 1.25, output: 5.0 },
|
|
16
|
+
pro_2_5: { input: 0.12, output: 0.50 }, # rubocop:disable Naming/VariableNumber
|
|
17
|
+
gemini_embedding: { input: 0.002, output: 0.004 },
|
|
18
|
+
embedding: { input: 0.00, output: 0.00 },
|
|
19
|
+
imagen: { price: 0.03 },
|
|
20
|
+
aqa: { input: 0.00, output: 0.00 }
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
def supports_tool_choice?(_model_id)
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def supports_tool_parallel_control?(_model_id)
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
|
|
10
31
|
def context_window_for(model_id)
|
|
11
32
|
case model_id
|
|
12
|
-
when /gemini-2\.5-pro-exp-03-25/, /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/,
|
|
33
|
+
when /gemini-2\.5-pro-exp-03-25/, /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/,
|
|
34
|
+
/gemini-1\.5-flash-8b/
|
|
13
35
|
1_048_576
|
|
14
36
|
when /gemini-1\.5-pro/ then 2_097_152
|
|
15
37
|
when /gemini-embedding-exp/ then 8_192
|
|
@@ -23,7 +45,8 @@ module RubyLLM
|
|
|
23
45
|
def max_tokens_for(model_id)
|
|
24
46
|
case model_id
|
|
25
47
|
when /gemini-2\.5-pro-exp-03-25/ then 64_000
|
|
26
|
-
when /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/, /gemini-1\.5-flash-8b/,
|
|
48
|
+
when /gemini-2\.0-flash/, /gemini-2\.0-flash-lite/, /gemini-1\.5-flash/, /gemini-1\.5-flash-8b/,
|
|
49
|
+
/gemini-1\.5-pro/
|
|
27
50
|
8_192
|
|
28
51
|
when /gemini-embedding-exp/ then nil
|
|
29
52
|
when /text-embedding-004/, /embedding-001/ then 768
|
|
@@ -32,18 +55,24 @@ module RubyLLM
|
|
|
32
55
|
end
|
|
33
56
|
end
|
|
34
57
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
def critical_capabilities_for(model_id)
|
|
59
|
+
capabilities = []
|
|
60
|
+
capabilities << 'function_calling' if supports_functions?(model_id)
|
|
61
|
+
capabilities << 'structured_output' if supports_structured_output?(model_id)
|
|
62
|
+
capabilities << 'vision' if supports_vision?(model_id)
|
|
63
|
+
capabilities
|
|
40
64
|
end
|
|
41
65
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
def pricing_for(model_id)
|
|
67
|
+
prices = PRICES.fetch(pricing_family(model_id), { input: 0.075, output: 0.30 })
|
|
68
|
+
{
|
|
69
|
+
text_tokens: {
|
|
70
|
+
standard: {
|
|
71
|
+
input_per_million: prices[:input] || prices[:price] || 0.075,
|
|
72
|
+
output_per_million: prices[:output] || prices[:price] || 0.30
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
47
76
|
end
|
|
48
77
|
|
|
49
78
|
def supports_vision?(model_id)
|
|
@@ -52,25 +81,13 @@ module RubyLLM
|
|
|
52
81
|
model_id.match?(/gemini|flash|pro|imagen/)
|
|
53
82
|
end
|
|
54
83
|
|
|
55
|
-
def supports_video?(model_id)
|
|
56
|
-
model_id.match?(/gemini/)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
84
|
def supports_functions?(model_id)
|
|
60
85
|
return false if model_id.match?(/text-embedding|embedding-001|aqa|flash-lite|imagen|gemini-2\.0-flash-lite/)
|
|
61
86
|
|
|
62
87
|
model_id.match?(/gemini|pro|flash/)
|
|
63
88
|
end
|
|
64
89
|
|
|
65
|
-
def
|
|
66
|
-
true
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def supports_tool_parallel_control?(_model_id)
|
|
70
|
-
false
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def supports_json_mode?(model_id)
|
|
90
|
+
def supports_structured_output?(model_id)
|
|
74
91
|
if model_id.match?(/text-embedding|embedding-001|aqa|imagen|gemini-2\.0-flash-lite|gemini-2\.5-pro-exp-03-25/)
|
|
75
92
|
return false
|
|
76
93
|
end
|
|
@@ -78,59 +95,6 @@ module RubyLLM
|
|
|
78
95
|
model_id.match?(/gemini|pro|flash/)
|
|
79
96
|
end
|
|
80
97
|
|
|
81
|
-
def format_display_name(model_id)
|
|
82
|
-
model_id
|
|
83
|
-
.delete_prefix('models/')
|
|
84
|
-
.split('-')
|
|
85
|
-
.map(&:capitalize)
|
|
86
|
-
.join(' ')
|
|
87
|
-
.gsub(/(\d+\.\d+)/, ' \1')
|
|
88
|
-
.gsub(/\s+/, ' ')
|
|
89
|
-
.gsub('Aqa', 'AQA')
|
|
90
|
-
.strip
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def supports_caching?(model_id)
|
|
94
|
-
if model_id.match?(/flash-lite|gemini-2\.5-pro-exp-03-25|aqa|imagen|text-embedding|embedding-001/)
|
|
95
|
-
return false
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
model_id.match?(/gemini|pro|flash/)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def supports_tuning?(model_id)
|
|
102
|
-
model_id.match?(/gemini-1\.5-flash|gemini-1\.5-flash-8b/)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def supports_audio?(model_id)
|
|
106
|
-
model_id.match?(/gemini|pro|flash/)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def model_type(model_id)
|
|
110
|
-
case model_id
|
|
111
|
-
when /text-embedding|embedding|gemini-embedding/ then 'embedding'
|
|
112
|
-
when /imagen/ then 'image'
|
|
113
|
-
else 'chat'
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def model_family(model_id)
|
|
118
|
-
case model_id
|
|
119
|
-
when /gemini-2\.5-pro-exp-03-25/ then 'gemini25_pro_exp'
|
|
120
|
-
when /gemini-2\.0-flash-lite/ then 'gemini20_flash_lite'
|
|
121
|
-
when /gemini-2\.0-flash/ then 'gemini20_flash'
|
|
122
|
-
when /gemini-1\.5-flash-8b/ then 'gemini15_flash_8b'
|
|
123
|
-
when /gemini-1\.5-flash/ then 'gemini15_flash'
|
|
124
|
-
when /gemini-1\.5-pro/ then 'gemini15_pro'
|
|
125
|
-
when /gemini-embedding-exp/ then 'gemini_embedding_exp'
|
|
126
|
-
when /text-embedding-004/ then 'embedding4'
|
|
127
|
-
when /embedding-001/ then 'embedding1'
|
|
128
|
-
when /aqa/ then 'aqa'
|
|
129
|
-
when /imagen-3/ then 'imagen3'
|
|
130
|
-
else 'other'
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
98
|
def pricing_family(model_id)
|
|
135
99
|
case model_id
|
|
136
100
|
when /gemini-2\.5-pro-exp-03-25/ then :pro_2_5 # rubocop:disable Naming/VariableNumber
|
|
@@ -147,142 +111,8 @@ module RubyLLM
|
|
|
147
111
|
end
|
|
148
112
|
end
|
|
149
113
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def context_length(model_id)
|
|
155
|
-
context_window_for(model_id)
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
PRICES = {
|
|
159
|
-
flash_2: { # rubocop:disable Naming/VariableNumber
|
|
160
|
-
input: 0.10,
|
|
161
|
-
output: 0.40,
|
|
162
|
-
audio_input: 0.70,
|
|
163
|
-
cache: 0.025,
|
|
164
|
-
cache_storage: 1.00,
|
|
165
|
-
grounding_search: 35.00
|
|
166
|
-
},
|
|
167
|
-
flash_lite_2: { # rubocop:disable Naming/VariableNumber
|
|
168
|
-
input: 0.075,
|
|
169
|
-
output: 0.30
|
|
170
|
-
},
|
|
171
|
-
flash: {
|
|
172
|
-
input: 0.075,
|
|
173
|
-
output: 0.30,
|
|
174
|
-
cache: 0.01875,
|
|
175
|
-
cache_storage: 1.00,
|
|
176
|
-
grounding_search: 35.00
|
|
177
|
-
},
|
|
178
|
-
flash_8b: {
|
|
179
|
-
input: 0.0375,
|
|
180
|
-
output: 0.15,
|
|
181
|
-
cache: 0.01,
|
|
182
|
-
cache_storage: 0.25,
|
|
183
|
-
grounding_search: 35.00
|
|
184
|
-
},
|
|
185
|
-
pro: {
|
|
186
|
-
input: 1.25,
|
|
187
|
-
output: 5.0,
|
|
188
|
-
cache: 0.3125,
|
|
189
|
-
cache_storage: 4.50,
|
|
190
|
-
grounding_search: 35.00
|
|
191
|
-
},
|
|
192
|
-
pro_2_5: { # rubocop:disable Naming/VariableNumber
|
|
193
|
-
input: 0.12,
|
|
194
|
-
output: 0.50
|
|
195
|
-
},
|
|
196
|
-
gemini_embedding: {
|
|
197
|
-
input: 0.002,
|
|
198
|
-
output: 0.004
|
|
199
|
-
},
|
|
200
|
-
embedding: {
|
|
201
|
-
input: 0.00,
|
|
202
|
-
output: 0.00
|
|
203
|
-
},
|
|
204
|
-
imagen: {
|
|
205
|
-
price: 0.03
|
|
206
|
-
},
|
|
207
|
-
aqa: {
|
|
208
|
-
input: 0.00,
|
|
209
|
-
output: 0.00
|
|
210
|
-
}
|
|
211
|
-
}.freeze
|
|
212
|
-
|
|
213
|
-
def default_input_price
|
|
214
|
-
0.075
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def default_output_price
|
|
218
|
-
0.30
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
def modalities_for(model_id)
|
|
222
|
-
modalities = {
|
|
223
|
-
input: ['text'],
|
|
224
|
-
output: ['text']
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if supports_vision?(model_id)
|
|
228
|
-
modalities[:input] << 'image'
|
|
229
|
-
modalities[:input] << 'pdf'
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
modalities[:input] << 'video' if supports_video?(model_id)
|
|
233
|
-
modalities[:input] << 'audio' if model_id.match?(/audio/)
|
|
234
|
-
modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)
|
|
235
|
-
modalities[:output] = ['image'] if model_id.match?(/imagen/)
|
|
236
|
-
|
|
237
|
-
modalities
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def capabilities_for(model_id)
|
|
241
|
-
capabilities = ['streaming']
|
|
242
|
-
|
|
243
|
-
capabilities << 'function_calling' if supports_functions?(model_id)
|
|
244
|
-
capabilities << 'structured_output' if supports_json_mode?(model_id)
|
|
245
|
-
capabilities << 'batch' if model_id.match?(/embedding|flash/)
|
|
246
|
-
capabilities << 'caching' if supports_caching?(model_id)
|
|
247
|
-
capabilities << 'fine_tuning' if supports_tuning?(model_id)
|
|
248
|
-
capabilities
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
def pricing_for(model_id)
|
|
252
|
-
family = pricing_family(model_id)
|
|
253
|
-
prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })
|
|
254
|
-
|
|
255
|
-
standard_pricing = {
|
|
256
|
-
input_per_million: prices[:input],
|
|
257
|
-
output_per_million: prices[:output]
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
standard_pricing[:cached_input_per_million] = prices[:input_hit] if prices[:input_hit]
|
|
261
|
-
|
|
262
|
-
batch_pricing = {
|
|
263
|
-
input_per_million: (standard_pricing[:input_per_million] || 0) * 0.5,
|
|
264
|
-
output_per_million: (standard_pricing[:output_per_million] || 0) * 0.5
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if standard_pricing[:cached_input_per_million]
|
|
268
|
-
batch_pricing[:cached_input_per_million] = standard_pricing[:cached_input_per_million] * 0.5
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
pricing = {
|
|
272
|
-
text_tokens: {
|
|
273
|
-
standard: standard_pricing,
|
|
274
|
-
batch: batch_pricing
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if model_id.match?(/embedding|gemini-embedding/)
|
|
279
|
-
pricing[:embeddings] = {
|
|
280
|
-
standard: { input_per_million: prices[:price] || 0.002 }
|
|
281
|
-
}
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
pricing
|
|
285
|
-
end
|
|
114
|
+
module_function :context_window_for, :max_tokens_for, :critical_capabilities_for, :pricing_for,
|
|
115
|
+
:supports_vision?, :supports_functions?, :supports_structured_output?, :pricing_family
|
|
286
116
|
end
|
|
287
117
|
end
|
|
288
118
|
end
|
|
@@ -17,14 +17,12 @@ module RubyLLM
|
|
|
17
17
|
|
|
18
18
|
Model::Info.new(
|
|
19
19
|
id: model_id,
|
|
20
|
-
name: model_data['displayName'],
|
|
20
|
+
name: model_data['displayName'] || model_id,
|
|
21
21
|
provider: slug,
|
|
22
|
-
family: capabilities.model_family(model_id),
|
|
23
22
|
created_at: nil,
|
|
24
23
|
context_window: model_data['inputTokenLimit'] || capabilities.context_window_for(model_id),
|
|
25
24
|
max_output_tokens: model_data['outputTokenLimit'] || capabilities.max_tokens_for(model_id),
|
|
26
|
-
|
|
27
|
-
capabilities: capabilities.capabilities_for(model_id),
|
|
25
|
+
capabilities: capabilities.critical_capabilities_for(model_id),
|
|
28
26
|
pricing: capabilities.pricing_for(model_id),
|
|
29
27
|
metadata: {
|
|
30
28
|
version: model_data['version'],
|