ruby_llm 0.1.0.pre35 → 0.1.0.pre36

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 586cc8473d06aebb4eb69ee646c8ad90250a522eac80d3471a22ef05e601b9de
4
- data.tar.gz: 4f060d755a37373230d1364dda3fc8b74fabdf4128206eb4faf2b11fd02090ac
3
+ metadata.gz: ed17bc0b342342484bd1e92b57f3b88a74d77cd43daeb1a569b132188025bafd
4
+ data.tar.gz: '0970e337a393e85cff88a449e723aa7c3e3e189b1923e16cf619f95d1bf65b03'
5
5
  SHA512:
6
- metadata.gz: 8e10930fd06b2dbdbaca81aa93f45cde41e8d1199173597e29d94af7ea073db470a91be9869f89b622aebc28d915510baf070ac6655ddbbe1591a94fbef7b2f8
7
- data.tar.gz: de99a1e6173f25b3c3bc7222910f6d1c8771b4c4f1062e8970f9472c7f551543c345cdffb1b3733ebd510e2351c27b5b274d483ba7047a32d90884b62577cb18
6
+ metadata.gz: 1e8b35980c57cd61e10c50b3eb97dbedbe138e2720b11940646c74807ada4c8b260aff3cbee8ed84abfa4b998d3d2747ff5f2421731fcf73ea038306774a2dc9
7
+ data.tar.gz: f0211b49713b10e00f1070fa7abb00a151ee7084e3f5e14b047be2c567afe4fcd5ade1ce662697a776470ab4e6c21e98ab1c453526e36b4ec29d6f7f6b9f6c0c
@@ -11,8 +11,8 @@
11
11
  "supports_vision": false,
12
12
  "supports_functions": false,
13
13
  "supports_json_mode": false,
14
- "input_price_per_million": 0.075,
15
- "output_price_per_million": 0.3,
14
+ "input_price_per_million": 0.0,
15
+ "output_price_per_million": 0.0,
16
16
  "metadata": {
17
17
  "object": "model",
18
18
  "owned_by": "google"
@@ -23,8 +23,8 @@
23
23
  "created_at": "2023-08-21T18:16:55+02:00",
24
24
  "display_name": "Babbage 002",
25
25
  "provider": "openai",
26
- "context_window": 4096,
27
- "max_tokens": 4096,
26
+ "context_window": 16384,
27
+ "max_tokens": 16384,
28
28
  "type": "chat",
29
29
  "family": "babbage",
30
30
  "supports_vision": false,
@@ -80,7 +80,7 @@
80
80
  "created_at": "2023-07-11T00:00:00Z",
81
81
  "display_name": "Claude 2.0",
82
82
  "provider": "anthropic",
83
- "context_window": 100000,
83
+ "context_window": 200000,
84
84
  "max_tokens": 4096,
85
85
  "type": "chat",
86
86
  "family": "claude2",
@@ -96,7 +96,7 @@
96
96
  "created_at": "2023-11-21T00:00:00Z",
97
97
  "display_name": "Claude 2.1",
98
98
  "provider": "anthropic",
99
- "context_window": 100000,
99
+ "context_window": 200000,
100
100
  "max_tokens": 4096,
101
101
  "type": "chat",
102
102
  "family": "claude2",
@@ -116,7 +116,7 @@
116
116
  "max_tokens": 8192,
117
117
  "type": "chat",
118
118
  "family": "claude35_haiku",
119
- "supports_vision": false,
119
+ "supports_vision": true,
120
120
  "supports_functions": true,
121
121
  "supports_json_mode": true,
122
122
  "input_price_per_million": 0.8,
@@ -161,9 +161,9 @@
161
161
  "display_name": "Claude 3.7 Sonnet",
162
162
  "provider": "anthropic",
163
163
  "context_window": 200000,
164
- "max_tokens": 4096,
164
+ "max_tokens": 8192,
165
165
  "type": "chat",
166
- "family": "claude2",
166
+ "family": "claude37_sonnet",
167
167
  "supports_vision": true,
168
168
  "supports_functions": true,
169
169
  "supports_json_mode": true,
@@ -262,8 +262,8 @@
262
262
  "created_at": "2023-08-21T18:11:41+02:00",
263
263
  "display_name": "Davinci 002",
264
264
  "provider": "openai",
265
- "context_window": 4096,
266
- "max_tokens": 4096,
265
+ "context_window": 16384,
266
+ "max_tokens": 16384,
267
267
  "type": "chat",
268
268
  "family": "davinci",
269
269
  "supports_vision": false,
@@ -857,7 +857,7 @@
857
857
  "family": "gemini20_flash_lite",
858
858
  "supports_vision": true,
859
859
  "supports_functions": false,
860
- "supports_json_mode": true,
860
+ "supports_json_mode": false,
861
861
  "input_price_per_million": 0.075,
862
862
  "output_price_per_million": 0.3,
863
863
  "metadata": {
@@ -876,7 +876,7 @@
876
876
  "family": "gemini20_flash_lite",
877
877
  "supports_vision": true,
878
878
  "supports_functions": false,
879
- "supports_json_mode": true,
879
+ "supports_json_mode": false,
880
880
  "input_price_per_million": 0.075,
881
881
  "output_price_per_million": 0.3,
882
882
  "metadata": {
@@ -895,7 +895,7 @@
895
895
  "family": "gemini20_flash_lite",
896
896
  "supports_vision": true,
897
897
  "supports_functions": false,
898
- "supports_json_mode": true,
898
+ "supports_json_mode": false,
899
899
  "input_price_per_million": 0.075,
900
900
  "output_price_per_million": 0.3,
901
901
  "metadata": {
@@ -914,7 +914,7 @@
914
914
  "family": "gemini20_flash_lite",
915
915
  "supports_vision": true,
916
916
  "supports_functions": false,
917
- "supports_json_mode": true,
917
+ "supports_json_mode": false,
918
918
  "input_price_per_million": 0.075,
919
919
  "output_price_per_million": 0.3,
920
920
  "metadata": {
@@ -1650,7 +1650,7 @@
1650
1650
  "display_name": "GPT-4o-Mini Realtime Preview",
1651
1651
  "provider": "openai",
1652
1652
  "context_window": 128000,
1653
- "max_tokens": 16384,
1653
+ "max_tokens": 4096,
1654
1654
  "type": "chat",
1655
1655
  "family": "gpt4o_mini_realtime",
1656
1656
  "supports_vision": true,
@@ -1669,7 +1669,7 @@
1669
1669
  "display_name": "GPT-4o-Mini Realtime Preview 20241217",
1670
1670
  "provider": "openai",
1671
1671
  "context_window": 128000,
1672
- "max_tokens": 16384,
1672
+ "max_tokens": 4096,
1673
1673
  "type": "chat",
1674
1674
  "family": "gpt4o_mini_realtime",
1675
1675
  "supports_vision": true,
@@ -1685,10 +1685,10 @@
1685
1685
  {
1686
1686
  "id": "gpt-4o-realtime-preview",
1687
1687
  "created_at": "2024-09-30T03:33:18+02:00",
1688
- "display_name": "GPT-4o Realtime Preview",
1688
+ "display_name": "GPT-4o-Realtime Preview",
1689
1689
  "provider": "openai",
1690
1690
  "context_window": 128000,
1691
- "max_tokens": 16384,
1691
+ "max_tokens": 4096,
1692
1692
  "type": "chat",
1693
1693
  "family": "gpt4o_realtime",
1694
1694
  "supports_vision": true,
@@ -1704,10 +1704,10 @@
1704
1704
  {
1705
1705
  "id": "gpt-4o-realtime-preview-2024-10-01",
1706
1706
  "created_at": "2024-09-24T00:49:26+02:00",
1707
- "display_name": "GPT-4o Realtime Preview 20241001",
1707
+ "display_name": "GPT-4o-Realtime Preview 20241001",
1708
1708
  "provider": "openai",
1709
1709
  "context_window": 128000,
1710
- "max_tokens": 16384,
1710
+ "max_tokens": 4096,
1711
1711
  "type": "chat",
1712
1712
  "family": "gpt4o_realtime",
1713
1713
  "supports_vision": true,
@@ -1723,10 +1723,10 @@
1723
1723
  {
1724
1724
  "id": "gpt-4o-realtime-preview-2024-12-17",
1725
1725
  "created_at": "2024-12-11T20:30:30+01:00",
1726
- "display_name": "GPT-4o Realtime Preview 20241217",
1726
+ "display_name": "GPT-4o-Realtime Preview 20241217",
1727
1727
  "provider": "openai",
1728
1728
  "context_window": 128000,
1729
- "max_tokens": 16384,
1729
+ "max_tokens": 4096,
1730
1730
  "type": "chat",
1731
1731
  "family": "gpt4o_realtime",
1732
1732
  "supports_vision": true,
@@ -1820,7 +1820,7 @@
1820
1820
  "created_at": "2024-09-06T20:56:48+02:00",
1821
1821
  "display_name": "O1-Mini",
1822
1822
  "provider": "openai",
1823
- "context_window": 200000,
1823
+ "context_window": 128000,
1824
1824
  "max_tokens": 4096,
1825
1825
  "type": "chat",
1826
1826
  "family": "o1_mini",
@@ -1839,7 +1839,7 @@
1839
1839
  "created_at": "2024-09-06T20:56:19+02:00",
1840
1840
  "display_name": "O1-Mini 20240912",
1841
1841
  "provider": "openai",
1842
- "context_window": 200000,
1842
+ "context_window": 128000,
1843
1843
  "max_tokens": 65536,
1844
1844
  "type": "chat",
1845
1845
  "family": "o1_mini",
@@ -1894,7 +1894,7 @@
1894
1894
  {
1895
1895
  "id": "omni-moderation-2024-09-26",
1896
1896
  "created_at": "2024-11-27T20:07:46+01:00",
1897
- "display_name": "Omni Moderation 20240926",
1897
+ "display_name": "Omni-Moderation 20240926",
1898
1898
  "provider": "openai",
1899
1899
  "context_window": 4096,
1900
1900
  "max_tokens": 4096,
@@ -1913,7 +1913,7 @@
1913
1913
  {
1914
1914
  "id": "omni-moderation-latest",
1915
1915
  "created_at": "2024-11-15T17:47:45+01:00",
1916
- "display_name": "Omni Moderation Latest",
1916
+ "display_name": "Omni-Moderation Latest",
1917
1917
  "provider": "openai",
1918
1918
  "context_window": 4096,
1919
1919
  "max_tokens": 4096,
@@ -7,45 +7,74 @@ module RubyLLM
7
7
  module Capabilities
8
8
  module_function
9
9
 
10
- def determine_context_window(model_id)
11
- case model_id
12
- when /claude-3/ then 200_000
13
- else 100_000
14
- end
10
+ # Determines the context window size for a given model
11
+ # @param model_id [String] the model identifier
12
+ # @return [Integer] the context window size in tokens
13
+ def determine_context_window(_model_id)
14
+ # All Claude 3 and 3.5 and 3.7 models have 200K token context windows
15
+ 200_000
15
16
  end
16
17
 
18
+ # Determines the maximum output tokens for a given model
19
+ # @param model_id [String] the model identifier
20
+ # @return [Integer] the maximum output tokens
17
21
  def determine_max_tokens(model_id)
18
22
  case model_id
23
+ when /claude-3-7-sonnet/ then 8_192 # Can be increased to 64K with extended thinking
19
24
  when /claude-3-5/ then 8_192
20
- else 4_096
25
+ else 4_096 # Claude 3 Opus and Haiku
21
26
  end
22
27
  end
23
28
 
29
+ # Gets the input price per million tokens for a given model
30
+ # @param model_id [String] the model identifier
31
+ # @return [Float] the price per million tokens for input
24
32
  def get_input_price(model_id)
25
33
  PRICES.dig(model_family(model_id), :input) || default_input_price
26
34
  end
27
35
 
36
+ # Gets the output price per million tokens for a given model
37
+ # @param model_id [String] the model identifier
38
+ # @return [Float] the price per million tokens for output
28
39
  def get_output_price(model_id)
29
40
  PRICES.dig(model_family(model_id), :output) || default_output_price
30
41
  end
31
42
 
43
+ # Determines if a model supports vision capabilities
44
+ # @param model_id [String] the model identifier
45
+ # @return [Boolean] true if the model supports vision
32
46
  def supports_vision?(model_id)
33
- return false if model_id.match?(/claude-3-5-haiku/)
34
- return false if model_id.match?(/claude-[12]/)
35
-
36
- true
47
+ # All Claude 3, 3.5, and 3.7 models support vision
48
+ !model_id.match?(/claude-[12]/)
37
49
  end
38
50
 
51
+ # Determines if a model supports function calling
52
+ # @param model_id [String] the model identifier
53
+ # @return [Boolean] true if the model supports functions
39
54
  def supports_functions?(model_id)
40
- model_id.include?('claude-3')
55
+ model_id.match?(/claude-3/)
41
56
  end
42
57
 
58
+ # Determines if a model supports JSON mode
59
+ # @param model_id [String] the model identifier
60
+ # @return [Boolean] true if the model supports JSON mode
43
61
  def supports_json_mode?(model_id)
44
- model_id.include?('claude-3')
62
+ model_id.match?(/claude-3/)
63
+ end
64
+
65
+ # Determines if a model supports extended thinking
66
+ # @param model_id [String] the model identifier
67
+ # @return [Boolean] true if the model supports extended thinking
68
+ def supports_extended_thinking?(model_id)
69
+ model_id.match?(/claude-3-7-sonnet/)
45
70
  end
46
71
 
72
+ # Determines the model family for a given model ID
73
+ # @param model_id [String] the model identifier
74
+ # @return [Symbol] the model family identifier
47
75
  def model_family(model_id)
48
76
  case model_id
77
+ when /claude-3-7-sonnet/ then :claude37_sonnet
49
78
  when /claude-3-5-sonnet/ then :claude35_sonnet
50
79
  when /claude-3-5-haiku/ then :claude35_haiku
51
80
  when /claude-3-opus/ then :claude3_opus
@@ -55,23 +84,32 @@ module RubyLLM
55
84
  end
56
85
  end
57
86
 
87
+ # Returns the model type
88
+ # @param model_id [String] the model identifier (unused but kept for API consistency)
89
+ # @return [String] the model type, always 'chat' for Anthropic models
58
90
  def model_type(_)
59
91
  'chat'
60
92
  end
61
93
 
94
+ # Pricing information for Anthropic models (per million tokens)
62
95
  PRICES = {
63
- claude35_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
64
- claude35_haiku: { input: 0.80, output: 4.0 }, # $0.80/$4.00 per million tokens
65
- claude3_opus: { input: 15.0, output: 75.0 }, # $15.00/$75.00 per million tokens
66
- claude3_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
67
- claude3_haiku: { input: 0.25, output: 1.25 }, # $0.25/$1.25 per million tokens
68
- claude2: { input: 3.0, output: 15.0 } # Default pricing for Claude 2.x models
96
+ claude37_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
97
+ claude35_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
98
+ claude35_haiku: { input: 0.80, output: 4.0 }, # $0.80/$4.00 per million tokens
99
+ claude3_opus: { input: 15.0, output: 75.0 }, # $15.00/$75.00 per million tokens
100
+ claude3_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
101
+ claude3_haiku: { input: 0.25, output: 1.25 }, # $0.25/$1.25 per million tokens
102
+ claude2: { input: 3.0, output: 15.0 } # Default pricing for Claude 2.x models
69
103
  }.freeze
70
104
 
105
+ # Default input price if model not found in PRICES
106
+ # @return [Float] default price per million tokens for input
71
107
  def default_input_price
72
108
  3.0
73
109
  end
74
110
 
111
+ # Default output price if model not found in PRICES
112
+ # @return [Float] default price per million tokens for output
75
113
  def default_output_price
76
114
  15.0
77
115
  end
@@ -7,6 +7,9 @@ module RubyLLM
7
7
  module Capabilities
8
8
  module_function
9
9
 
10
+ # Returns the context window size for the given model
11
+ # @param model_id [String] the model identifier
12
+ # @return [Integer] the context window size in tokens
10
13
  def context_window_for(model_id)
11
14
  case model_id
12
15
  when /deepseek-(?:chat|reasoner)/ then 64_000
@@ -14,6 +17,9 @@ module RubyLLM
14
17
  end
15
18
  end
16
19
 
20
+ # Returns the maximum number of tokens that can be generated
21
+ # @param model_id [String] the model identifier
22
+ # @return [Integer] the maximum number of tokens
17
23
  def max_tokens_for(model_id)
18
24
  case model_id
19
25
  when /deepseek-(?:chat|reasoner)/ then 8_192
@@ -21,30 +27,51 @@ module RubyLLM
21
27
  end
22
28
  end
23
29
 
30
+ # Returns the price per million tokens for input (cache miss)
31
+ # @param model_id [String] the model identifier
32
+ # @return [Float] the price per million tokens in USD
24
33
  def input_price_for(model_id)
25
34
  PRICES.dig(model_family(model_id), :input_miss) || default_input_price
26
35
  end
27
36
 
37
+ # Returns the price per million tokens for output
38
+ # @param model_id [String] the model identifier
39
+ # @return [Float] the price per million tokens in USD
28
40
  def output_price_for(model_id)
29
41
  PRICES.dig(model_family(model_id), :output) || default_output_price
30
42
  end
31
43
 
44
+ # Returns the price per million tokens for input with cache hit
45
+ # @param model_id [String] the model identifier
46
+ # @return [Float] the price per million tokens in USD
32
47
  def cache_hit_price_for(model_id)
33
48
  PRICES.dig(model_family(model_id), :input_hit) || default_cache_hit_price
34
49
  end
35
50
 
51
+ # Determines if the model supports vision capabilities
52
+ # @param model_id [String] the model identifier
53
+ # @return [Boolean] true if the model supports vision
36
54
  def supports_vision?(_model_id)
37
55
  false # DeepSeek models don't currently support vision
38
56
  end
39
57
 
58
+ # Determines if the model supports function calling
59
+ # @param model_id [String] the model identifier
60
+ # @return [Boolean] true if the model supports function calling
40
61
  def supports_functions?(model_id)
41
62
  model_id.match?(/deepseek-chat/) # Only deepseek-chat supports function calling
42
63
  end
43
64
 
65
+ # Determines if the model supports JSON mode
66
+ # @param model_id [String] the model identifier
67
+ # @return [Boolean] true if the model supports JSON mode
44
68
  def supports_json_mode?(model_id)
45
69
  model_id.match?(/deepseek-chat/) # Only deepseek-chat supports JSON mode
46
70
  end
47
71
 
72
+ # Returns a formatted display name for the model
73
+ # @param model_id [String] the model identifier
74
+ # @return [String] the formatted display name
48
75
  def format_display_name(model_id)
49
76
  case model_id
50
77
  when 'deepseek-chat' then 'DeepSeek V3'
@@ -56,10 +83,16 @@ module RubyLLM
56
83
  end
57
84
  end
58
85
 
86
+ # Returns the model type
87
+ # @param model_id [String] the model identifier
88
+ # @return [String] the model type (e.g., 'chat')
59
89
  def model_type(_model_id)
60
90
  'chat' # All DeepSeek models are chat models
61
91
  end
62
92
 
93
+ # Returns the model family
94
+ # @param model_id [String] the model identifier
95
+ # @return [Symbol] the model family
63
96
  def model_family(model_id)
64
97
  case model_id
65
98
  when /deepseek-chat/ then :chat
@@ -84,14 +117,20 @@ module RubyLLM
84
117
 
85
118
  private
86
119
 
120
+ # Default input price when model family can't be determined
121
+ # @return [Float] the default input price
87
122
  def default_input_price
88
123
  0.27 # Default to chat cache miss price
89
124
  end
90
125
 
126
+ # Default output price when model family can't be determined
127
+ # @return [Float] the default output price
91
128
  def default_output_price
92
129
  1.10 # Default to chat output price
93
130
  end
94
131
 
132
+ # Default cache hit price when model family can't be determined
133
+ # @return [Float] the default cache hit price
95
134
  def default_cache_hit_price
96
135
  0.07 # Default to chat cache hit price
97
136
  end
@@ -7,25 +7,34 @@ module RubyLLM
7
7
  module Capabilities # rubocop:disable Metrics/ModuleLength
8
8
  module_function
9
9
 
10
+ # Returns the context window size (input token limit) for the given model
11
+ # @param model_id [String] the model identifier
12
+ # @return [Integer] the context window size in tokens
10
13
  def context_window_for(model_id)
11
14
  case model_id
12
15
  when /gemini-2\.0-flash/, /gemini-1\.5-flash/ then 1_048_576
13
16
  when /gemini-1\.5-pro/ then 2_097_152
14
- when /text-embedding/, /embedding-001/ then 2_048
17
+ when /text-embedding-004/, /embedding-001/ then 2_048
15
18
  when /aqa/ then 7_168
16
19
  else 32_768 # Sensible default for unknown models
17
20
  end
18
21
  end
19
22
 
23
+ # Returns the maximum output tokens for the given model
24
+ # @param model_id [String] the model identifier
25
+ # @return [Integer] the maximum output tokens
20
26
  def max_tokens_for(model_id)
21
27
  case model_id
22
28
  when /gemini-2\.0-flash/, /gemini-1\.5/ then 8_192
23
- when /text-embedding/, /embedding-001/ then 768 # Output dimension size for embeddings
29
+ when /text-embedding-004/, /embedding-001/ then 768 # Output dimension size for embeddings
24
30
  when /aqa/ then 1_024
25
31
  else 4_096 # Sensible default
26
32
  end
27
33
  end
28
34
 
35
+ # Returns the input price per million tokens for the given model
36
+ # @param model_id [String] the model identifier
37
+ # @return [Float] the price per million tokens in USD
29
38
  def input_price_for(model_id)
30
39
  base_price = PRICES.dig(pricing_family(model_id), :input) || default_input_price
31
40
  return base_price unless long_context_model?(model_id)
@@ -34,6 +43,9 @@ module RubyLLM
34
43
  context_length(model_id) > 128_000 ? base_price * 2 : base_price
35
44
  end
36
45
 
46
+ # Returns the output price per million tokens for the given model
47
+ # @param model_id [String] the model identifier
48
+ # @return [Float] the price per million tokens in USD
37
49
  def output_price_for(model_id)
38
50
  base_price = PRICES.dig(pricing_family(model_id), :output) || default_output_price
39
51
  return base_price unless long_context_model?(model_id)
@@ -42,6 +54,9 @@ module RubyLLM
42
54
  context_length(model_id) > 128_000 ? base_price * 2 : base_price
43
55
  end
44
56
 
57
+ # Determines if the model supports vision (image/video) inputs
58
+ # @param model_id [String] the model identifier
59
+ # @return [Boolean] true if the model supports vision inputs
45
60
  def supports_vision?(model_id)
46
61
  return false if model_id.match?(/text-embedding|embedding-001|aqa/)
47
62
  return false if model_id.match?(/gemini-1\.0/)
@@ -49,6 +64,9 @@ module RubyLLM
49
64
  model_id.match?(/gemini-[12]\.[05]/)
50
65
  end
51
66
 
67
+ # Determines if the model supports function calling
68
+ # @param model_id [String] the model identifier
69
+ # @return [Boolean] true if the model supports function calling
52
70
  def supports_functions?(model_id)
53
71
  return false if model_id.match?(/text-embedding|embedding-001|aqa/)
54
72
  return false if model_id.match?(/flash-lite/)
@@ -57,13 +75,20 @@ module RubyLLM
57
75
  model_id.match?(/gemini-[12]\.[05]-(?:pro|flash)(?!-lite)/)
58
76
  end
59
77
 
78
+ # Determines if the model supports JSON mode
79
+ # @param model_id [String] the model identifier
80
+ # @return [Boolean] true if the model supports JSON mode
60
81
  def supports_json_mode?(model_id)
61
82
  return false if model_id.match?(/text-embedding|embedding-001|aqa/)
62
83
  return false if model_id.match?(/gemini-1\.0/)
84
+ return false if model_id.match?(/gemini-2\.0-flash-lite/)
63
85
 
64
86
  model_id.match?(/gemini-\d/)
65
87
  end
66
88
 
89
+ # Formats the model ID into a human-readable display name
90
+ # @param model_id [String] the model identifier
91
+ # @return [String] the formatted display name
67
92
  def format_display_name(model_id)
68
93
  model_id
69
94
  .delete_prefix('models/')
@@ -76,20 +101,32 @@ module RubyLLM
76
101
  .strip
77
102
  end
78
103
 
104
+ # Determines if the model supports context caching
105
+ # @param model_id [String] the model identifier
106
+ # @return [Boolean] true if the model supports caching
79
107
  def supports_caching?(model_id)
80
108
  return false if model_id.match?(/flash-lite|gemini-1\.0/)
81
109
 
82
110
  model_id.match?(/gemini-[12]\.[05]/)
83
111
  end
84
112
 
113
+ # Determines if the model supports tuning
114
+ # @param model_id [String] the model identifier
115
+ # @return [Boolean] true if the model supports tuning
85
116
  def supports_tuning?(model_id)
86
117
  model_id.match?(/gemini-1\.5-flash/)
87
118
  end
88
119
 
120
+ # Determines if the model supports audio inputs
121
+ # @param model_id [String] the model identifier
122
+ # @return [Boolean] true if the model supports audio inputs
89
123
  def supports_audio?(model_id)
90
124
  model_id.match?(/gemini-[12]\.[05]/)
91
125
  end
92
126
 
127
+ # Returns the type of model (chat, embedding, image)
128
+ # @param model_id [String] the model identifier
129
+ # @return [String] the model type
93
130
  def model_type(model_id)
94
131
  case model_id
95
132
  when /text-embedding|embedding/ then 'embedding'
@@ -98,6 +135,9 @@ module RubyLLM
98
135
  end
99
136
  end
100
137
 
138
+ # Returns the model family identifier
139
+ # @param model_id [String] the model identifier
140
+ # @return [String] the model family identifier
101
141
  def model_family(model_id) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
102
142
  case model_id
103
143
  when /gemini-2\.0-flash-lite/ then 'gemini20_flash_lite'
@@ -113,7 +153,10 @@ module RubyLLM
113
153
  end
114
154
  end
115
155
 
116
- def pricing_family(model_id) # rubocop:disable Metrics/CyclomaticComplexity
156
+ # Returns the pricing family identifier for the model
157
+ # @param model_id [String] the model identifier
158
+ # @return [Symbol] the pricing family identifier
159
+ def pricing_family(model_id) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
117
160
  case model_id
118
161
  when /gemini-2\.0-flash-lite/ then :flash_lite_2 # rubocop:disable Naming/VariableNumber
119
162
  when /gemini-2\.0-flash/ then :flash_2 # rubocop:disable Naming/VariableNumber
@@ -122,18 +165,26 @@ module RubyLLM
122
165
  when /gemini-1\.5-pro/ then :pro
123
166
  when /gemini-1\.0-pro/ then :pro_1_0 # rubocop:disable Naming/VariableNumber
124
167
  when /text-embedding|embedding/ then :embedding
168
+ when /aqa/ then :aqa
125
169
  else :base
126
170
  end
127
171
  end
128
172
 
173
+ # Determines if the model supports long context
174
+ # @param model_id [String] the model identifier
175
+ # @return [Boolean] true if the model supports long context
129
176
  def long_context_model?(model_id)
130
177
  model_id.match?(/gemini-1\.5-(?:pro|flash)/)
131
178
  end
132
179
 
180
+ # Returns the context length for the model
181
+ # @param model_id [String] the model identifier
182
+ # @return [Integer] the context length in tokens
133
183
  def context_length(model_id)
134
184
  context_window_for(model_id)
135
185
  end
136
186
 
187
+ # Pricing information for Gemini models (per 1M tokens in USD)
137
188
  PRICES = {
138
189
  flash_2: { # Gemini 2.0 Flash # rubocop:disable Naming/VariableNumber
139
190
  input: 0.10,
@@ -152,19 +203,22 @@ module RubyLLM
152
203
  input: 0.075,
153
204
  output: 0.30,
154
205
  cache: 0.01875,
155
- cache_storage: 1.00
206
+ cache_storage: 1.00,
207
+ grounding_search: 35.00 # per 1K requests
156
208
  },
157
209
  flash_8b: { # Gemini 1.5 Flash 8B
158
210
  input: 0.0375,
159
211
  output: 0.15,
160
212
  cache: 0.01,
161
- cache_storage: 0.25
213
+ cache_storage: 0.25,
214
+ grounding_search: 35.00 # per 1K requests
162
215
  },
163
216
  pro: { # Gemini 1.5 Pro
164
217
  input: 1.25,
165
218
  output: 5.0,
166
219
  cache: 0.3125,
167
- cache_storage: 4.50
220
+ cache_storage: 4.50,
221
+ grounding_search: 35.00 # per 1K requests
168
222
  },
169
223
  pro_1_0: { # Gemini 1.0 Pro # rubocop:disable Naming/VariableNumber
170
224
  input: 0.50,
@@ -173,15 +227,23 @@ module RubyLLM
173
227
  embedding: { # Text Embedding models
174
228
  input: 0.00,
175
229
  output: 0.00
230
+ },
231
+ aqa: { # AQA model
232
+ input: 0.00,
233
+ output: 0.00
176
234
  }
177
235
  }.freeze
178
236
 
237
+ # Default input price for unknown models
238
+ # @return [Float] the default input price per million tokens
179
239
  def default_input_price
180
240
  0.075 # Default to Flash pricing
181
241
  end
182
242
 
243
+ # Default output price for unknown models
244
+ # @return [Float] the default output price per million tokens
183
245
  def default_output_price
184
- 0.30 # Default to Flash pricing
246
+ 0.30 # Default to Flash pricing
185
247
  end
186
248
  end
187
249
  end
@@ -7,76 +7,113 @@ module RubyLLM
7
7
  module Capabilities # rubocop:disable Metrics/ModuleLength
8
8
  module_function
9
9
 
10
+ # Returns the context window size for the given model ID
11
+ # @param model_id [String] the model identifier
12
+ # @return [Integer] the context window size in tokens
10
13
  def context_window_for(model_id)
11
14
  case model_id
12
- when /o[13]-mini/, /o3-mini-2025/ then 200_000
13
- when /o1-2024/ then 200_000
14
- when /gpt-4o/, /gpt-4-turbo/ then 128_000
15
- when /gpt-4-0[0-9]{3}/ then 8_192
16
- when /gpt-3.5-turbo-instruct/ then 4_096
17
- when /gpt-3.5/ then 16_385
15
+ when /o1-2024/, /o3-mini/, /o3-mini-2025/ then 200_000
16
+ when /gpt-4o/, /gpt-4o-mini/, /gpt-4-turbo/, /o1-mini/ then 128_000
17
+ when /gpt-4-0[0-9]{3}/ then 8_192
18
+ when /gpt-3.5-turbo-instruct/ then 4_096
19
+ when /gpt-3.5/ then 16_385
20
+ when /babbage-002/, /davinci-002/ then 16_384
18
21
  else 4_096
19
22
  end
20
23
  end
21
24
 
22
- def max_tokens_for(model_id) # rubocop:disable Metrics/CyclomaticComplexity
25
+ # Returns the maximum output tokens for the given model ID
26
+ # @param model_id [String] the model identifier
27
+ # @return [Integer] the maximum output tokens
28
+ def max_tokens_for(model_id) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
23
29
  case model_id
24
- when /o1-2024/, /o3-mini/ then 100_000
25
- when /o1-mini-2024/ then 65_536
26
- when /gpt-4o-2024-05-13/ then 4_096
27
- when /gpt-4o/, /gpt-4o-mini/ then 16_384
28
- when /gpt-4o-realtime/ then 4_096
29
- when /gpt-4-0[0-9]{3}/ then 8_192
30
- when /gpt-3.5-turbo/ then 4_096
30
+ when /o1-2024/, /o3-mini/, /o3-mini-2025/ then 100_000
31
+ when /o1-mini-2024/ then 65_536
32
+ when /gpt-4o-2024-05-13/ then 4_096
33
+ when /gpt-4o-realtime/, /gpt-4o-mini-realtime/ then 4_096
34
+ when /gpt-4o/, /gpt-4o-mini/, /gpt-4o-audio/, /gpt-4o-mini-audio/ then 16_384
35
+ when /gpt-4-0[0-9]{3}/ then 8_192
36
+ when /gpt-4-turbo/, /gpt-3.5-turbo/ then 4_096
37
+ when /babbage-002/, /davinci-002/ then 16_384
31
38
  else 4_096
32
39
  end
33
40
  end
34
41
 
42
+ # Returns the input price per million tokens for the given model ID
43
+ # @param model_id [String] the model identifier
44
+ # @return [Float] the price per million tokens for input
35
45
  def input_price_for(model_id)
36
46
  PRICES.dig(model_family(model_id), :input) || default_input_price
37
47
  end
38
48
 
49
+ # Returns the output price per million tokens for the given model ID
50
+ # @param model_id [String] the model identifier
51
+ # @return [Float] the price per million tokens for output
39
52
  def output_price_for(model_id)
40
53
  PRICES.dig(model_family(model_id), :output) || default_output_price
41
54
  end
42
55
 
56
+ # Determines if the model supports vision capabilities
57
+ # @param model_id [String] the model identifier
58
+ # @return [Boolean] true if the model supports vision
43
59
  def supports_vision?(model_id)
44
60
  model_id.match?(/gpt-4o|o1/) || model_id.match?(/gpt-4-(?!0314|0613)/)
45
61
  end
46
62
 
63
+ # Determines if the model supports function calling
64
+ # @param model_id [String] the model identifier
65
+ # @return [Boolean] true if the model supports functions
47
66
  def supports_functions?(model_id)
48
67
  !model_id.include?('instruct')
49
68
  end
50
69
 
70
+ # Determines if the model supports audio input/output
71
+ # @param model_id [String] the model identifier
72
+ # @return [Boolean] true if the model supports audio
51
73
  def supports_audio?(model_id)
52
74
  model_id.match?(/audio-preview|realtime-preview|whisper|tts/)
53
75
  end
54
76
 
77
+ # Determines if the model supports JSON mode
78
+ # @param model_id [String] the model identifier
79
+ # @return [Boolean] true if the model supports JSON mode
55
80
  def supports_json_mode?(model_id)
56
81
  model_id.match?(/gpt-4-\d{4}-preview/) ||
57
82
  model_id.include?('turbo') ||
58
83
  model_id.match?(/gpt-3.5-turbo-(?!0301|0613)/)
59
84
  end
60
85
 
86
+ # Formats the model ID into a human-readable display name
87
+ # @param model_id [String] the model identifier
88
+ # @return [String] the formatted display name
61
89
  def format_display_name(model_id)
62
90
  model_id.then { |id| humanize(id) }
63
91
  .then { |name| apply_special_formatting(name) }
64
92
  end
65
93
 
94
+ # Determines the type of model
95
+ # @param model_id [String] the model identifier
96
+ # @return [String] the model type (chat, embedding, image, audio, moderation)
66
97
  def model_type(model_id)
67
98
  case model_id
68
99
  when /text-embedding|embedding/ then 'embedding'
69
100
  when /dall-e/ then 'image'
70
101
  when /tts|whisper/ then 'audio'
71
- when /omni-moderation/ then 'moderation'
102
+ when /omni-moderation|text-moderation/ then 'moderation'
72
103
  else 'chat'
73
104
  end
74
105
  end
75
106
 
107
+ # Determines if the model supports structured output
108
+ # @param model_id [String] the model identifier
109
+ # @return [Boolean] true if the model supports structured output
76
110
  def supports_structured_output?(model_id)
77
- model_id.match?(/gpt-4o|o[13]-mini|o1/)
111
+ model_id.match?(/gpt-4o|o[13]-mini|o1|o3-mini/)
78
112
  end
79
113
 
114
+ # Determines the model family for pricing and capability lookup
115
+ # @param model_id [String] the model identifier
116
+ # @return [Symbol] the model family identifier
80
117
  def model_family(model_id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
81
118
  case model_id
82
119
  when /o3-mini/ then 'o3_mini'
@@ -100,13 +137,14 @@ module RubyLLM
100
137
  when /tts-1-hd/ then 'tts1_hd'
101
138
  when /tts-1/ then 'tts1'
102
139
  when /whisper/ then 'whisper1'
103
- when /omni-moderation/ then 'moderation'
140
+ when /omni-moderation|text-moderation/ then 'moderation'
104
141
  when /babbage/ then 'babbage'
105
142
  when /davinci/ then 'davinci'
106
143
  else 'other'
107
144
  end
108
145
  end
109
146
 
147
+ # Pricing information for OpenAI models (per million tokens unless otherwise specified)
110
148
  PRICES = {
111
149
  o1: { input: 15.0, cached_input: 7.5, output: 60.0 },
112
150
  o1_mini: { input: 1.10, cached_input: 0.55, output: 4.40 },
@@ -150,19 +188,27 @@ module RubyLLM
150
188
  embedding2: { price: 0.10 },
151
189
  davinci: { input: 2.0, output: 2.0 },
152
190
  babbage: { input: 0.40, output: 0.40 },
153
- tts1: { price: 15.0 },
154
- tts1_hd: { price: 30.0 },
155
- whisper1: { price: 0.006 }
191
+ tts1: { price: 15.0 }, # per million characters
192
+ tts1_hd: { price: 30.0 }, # per million characters
193
+ whisper1: { price: 0.006 }, # per minute
194
+ moderation: { price: 0.0 } # free
156
195
  }.freeze
157
196
 
197
+ # Default input price when model-specific pricing is not available
198
+ # @return [Float] the default price per million tokens
158
199
  def default_input_price
159
200
  0.50
160
201
  end
161
202
 
203
+ # Default output price when model-specific pricing is not available
204
+ # @return [Float] the default price per million tokens
162
205
  def default_output_price
163
206
  1.50
164
207
  end
165
208
 
209
+ # Converts a model ID to a human-readable format
210
+ # @param id [String] the model identifier
211
+ # @return [String] the humanized model name
166
212
  def humanize(id)
167
213
  id.tr('-', ' ')
168
214
  .split(' ')
@@ -170,18 +216,25 @@ module RubyLLM
170
216
  .join(' ')
171
217
  end
172
218
 
219
+ # Applies special formatting rules to model names
220
+ # @param name [String] the humanized model name
221
+ # @return [String] the specially formatted model name
173
222
  def apply_special_formatting(name) # rubocop:disable Metrics/MethodLength
174
223
  name
175
224
  .gsub(/(\d{4}) (\d{2}) (\d{2})/, '\1\2\3')
176
225
  .gsub(/^Gpt /, 'GPT-')
177
226
  .gsub(/^O([13]) /, 'O\1-')
227
+ .gsub(/^O3 Mini/, 'O3-Mini')
228
+ .gsub(/^O1 Mini/, 'O1-Mini')
178
229
  .gsub(/^Chatgpt /, 'ChatGPT-')
179
230
  .gsub(/^Tts /, 'TTS-')
180
231
  .gsub(/^Dall E /, 'DALL-E-')
181
232
  .gsub(/3\.5 /, '3.5-')
182
233
  .gsub(/4 /, '4-')
183
- .gsub(/4o (?=Mini|Preview|Turbo|Audio)/, '4o-')
234
+ .gsub(/4o (?=Mini|Preview|Turbo|Audio|Realtime)/, '4o-')
184
235
  .gsub(/\bHd\b/, 'HD')
236
+ .gsub(/Omni Moderation/, 'Omni-Moderation')
237
+ .gsub(/Text Moderation/, 'Text-Moderation')
185
238
  end
186
239
  end
187
240
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- VERSION = '0.1.0.pre35'
4
+ VERSION = '0.1.0.pre36'
5
5
  end
@@ -16,6 +16,9 @@ PROVIDER_DOCS = {
16
16
  },
17
17
  deepseek: {
18
18
  models: 'https://api-docs.deepseek.com/quick_start/pricing/'
19
+ },
20
+ anthropic: {
21
+ models: 'https://docs.anthropic.com/en/docs/about-claude/models/all-models'
19
22
  }
20
23
  }.freeze
21
24
 
@@ -85,8 +88,10 @@ namespace :models do # rubocop:disable Metrics/BlockLength
85
88
  end
86
89
  end
87
90
 
88
- desc 'Update model capabilities modules by scraping provider documentation'
91
+ desc 'Update model capabilities modules by scraping provider documentation (use PROVIDER=name to update only one)'
89
92
  task :update_capabilities do # rubocop:disable Metrics/BlockLength
93
+ # Check if a specific provider was requested
94
+ target_provider = ENV['PROVIDER']&.to_sym
90
95
  require 'ruby_llm'
91
96
  require 'fileutils'
92
97
 
@@ -97,8 +102,15 @@ namespace :models do # rubocop:disable Metrics/BlockLength
97
102
  config.gemini_api_key = ENV.fetch('GEMINI_API_KEY')
98
103
  end
99
104
 
105
+ # Filter providers if a specific one was requested
106
+ providers_to_process = if target_provider && PROVIDER_DOCS.key?(target_provider)
107
+ { target_provider => PROVIDER_DOCS[target_provider] }
108
+ else
109
+ PROVIDER_DOCS
110
+ end
111
+
100
112
  # Process each provider
101
- PROVIDER_DOCS.each do |provider, urls| # rubocop:disable Metrics/BlockLength
113
+ providers_to_process.each do |provider, urls| # rubocop:disable Metrics/BlockLength
102
114
  puts "Processing #{provider}..."
103
115
 
104
116
  # Initialize our AI assistants
@@ -175,12 +187,22 @@ namespace :models do # rubocop:disable Metrics/BlockLength
175
187
 
176
188
  response = claude.ask(code_prompt)
177
189
 
190
+ # Extract Ruby code from Claude's response
191
+ puts " Extracting Ruby code from Claude's response..."
192
+ ruby_code = nil
193
+
194
+ # Look for Ruby code block
195
+ ruby_code = Regexp.last_match(1).strip if response.content =~ /```ruby\s*(.*?)```/m
196
+
197
+ # Verify we found Ruby code
198
+ raise "No Ruby code block found in Claude's response" if ruby_code.nil? || ruby_code.empty?
199
+
178
200
  # Save the file
179
201
  file_path = "lib/ruby_llm/providers/#{provider}/capabilities.rb"
180
202
  puts " Writing #{file_path}..."
181
203
 
182
204
  FileUtils.mkdir_p(File.dirname(file_path))
183
- File.write(file_path, response.content)
205
+ File.write(file_path, ruby_code)
184
206
  rescue StandardError => e
185
207
  raise "Failed to process #{provider}: #{e.message}"
186
208
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre35
4
+ version: 0.1.0.pre36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino