ruby_llm 0.1.0.pre30 → 0.1.0.pre31

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{gem-push.yml → cicd.yml} +32 -4
  3. data/.rspec_status +27 -0
  4. data/lib/ruby_llm/active_record/acts_as.rb +5 -5
  5. data/lib/ruby_llm/chat.rb +2 -2
  6. data/lib/ruby_llm/configuration.rb +3 -1
  7. data/lib/ruby_llm/content.rb +79 -0
  8. data/lib/ruby_llm/embedding.rb +9 -3
  9. data/lib/ruby_llm/message.rb +9 -1
  10. data/lib/ruby_llm/models.json +14 -14
  11. data/lib/ruby_llm/provider.rb +39 -14
  12. data/lib/ruby_llm/providers/anthropic/capabilities.rb +81 -0
  13. data/lib/ruby_llm/providers/anthropic/chat.rb +86 -0
  14. data/lib/ruby_llm/providers/anthropic/embeddings.rb +20 -0
  15. data/lib/ruby_llm/providers/anthropic/models.rb +48 -0
  16. data/lib/ruby_llm/providers/anthropic/streaming.rb +37 -0
  17. data/lib/ruby_llm/providers/anthropic/tools.rb +97 -0
  18. data/lib/ruby_llm/providers/anthropic.rb +8 -234
  19. data/lib/ruby_llm/providers/deepseek/capabilites.rb +101 -0
  20. data/lib/ruby_llm/providers/deepseek.rb +4 -2
  21. data/lib/ruby_llm/providers/gemini/capabilities.rb +191 -0
  22. data/lib/ruby_llm/providers/gemini/models.rb +20 -0
  23. data/lib/ruby_llm/providers/gemini.rb +5 -10
  24. data/lib/ruby_llm/providers/openai/capabilities.rb +191 -0
  25. data/lib/ruby_llm/providers/openai/chat.rb +68 -0
  26. data/lib/ruby_llm/providers/openai/embeddings.rb +39 -0
  27. data/lib/ruby_llm/providers/openai/models.rb +40 -0
  28. data/lib/ruby_llm/providers/openai/streaming.rb +31 -0
  29. data/lib/ruby_llm/providers/openai/tools.rb +69 -0
  30. data/lib/ruby_llm/providers/openai.rb +15 -197
  31. data/lib/ruby_llm/version.rb +1 -1
  32. data/lib/ruby_llm.rb +4 -2
  33. data/ruby_llm.gemspec +2 -0
  34. metadata +48 -8
  35. data/.github/workflows/test.yml +0 -35
  36. data/lib/ruby_llm/model_capabilities/anthropic.rb +0 -79
  37. data/lib/ruby_llm/model_capabilities/deepseek.rb +0 -132
  38. data/lib/ruby_llm/model_capabilities/gemini.rb +0 -190
  39. data/lib/ruby_llm/model_capabilities/openai.rb +0 -189
@@ -1,190 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module ModelCapabilities
5
- # Determines capabilities and pricing for Google Gemini models
6
- module Gemini # rubocop:disable Metrics/ModuleLength
7
- extend self
8
-
9
- def context_window_for(model_id)
10
- case model_id
11
- when /gemini-2\.0-flash/, /gemini-1\.5-flash/ then 1_048_576
12
- when /gemini-1\.5-pro/ then 2_097_152
13
- when /text-embedding/, /embedding-001/ then 2_048
14
- when /aqa/ then 7_168
15
- else 32_768 # Sensible default for unknown models
16
- end
17
- end
18
-
19
- def max_tokens_for(model_id)
20
- case model_id
21
- when /gemini-2\.0-flash/, /gemini-1\.5/ then 8_192
22
- when /text-embedding/, /embedding-001/ then 768 # Output dimension size for embeddings
23
- when /aqa/ then 1_024
24
- else 4_096 # Sensible default
25
- end
26
- end
27
-
28
- def input_price_for(model_id)
29
- base_price = PRICES.dig(pricing_family(model_id), :input) || default_input_price
30
- return base_price unless long_context_model?(model_id)
31
-
32
- # Double the price for prompts longer than 128k tokens
33
- context_length(model_id) > 128_000 ? base_price * 2 : base_price
34
- end
35
-
36
- def output_price_for(model_id)
37
- base_price = PRICES.dig(pricing_family(model_id), :output) || default_output_price
38
- return base_price unless long_context_model?(model_id)
39
-
40
- # Double the price for prompts longer than 128k tokens
41
- context_length(model_id) > 128_000 ? base_price * 2 : base_price
42
- end
43
-
44
- def supports_vision?(model_id)
45
- return false if model_id.match?(/text-embedding|embedding-001|aqa/)
46
- return false if model_id.match?(/gemini-1\.0/)
47
-
48
- model_id.match?(/gemini-[12]\.[05]/)
49
- end
50
-
51
- def supports_functions?(model_id)
52
- return false if model_id.match?(/text-embedding|embedding-001|aqa/)
53
- return false if model_id.match?(/flash-lite/)
54
- return false if model_id.match?(/gemini-1\.0/)
55
-
56
- model_id.match?(/gemini-[12]\.[05]-(?:pro|flash)(?!-lite)/)
57
- end
58
-
59
- def supports_json_mode?(model_id)
60
- return false if model_id.match?(/text-embedding|embedding-001|aqa/)
61
- return false if model_id.match?(/flash-lite/)
62
- return false if model_id.match?(/gemini-1\.0/)
63
-
64
- model_id.match?(/gemini-1\.5/)
65
- end
66
-
67
- def format_display_name(model_id)
68
- model_id
69
- .delete_prefix('models/')
70
- .split('-')
71
- .map(&:capitalize)
72
- .join(' ')
73
- .gsub(/(\d+\.\d+)/, ' \1') # Add space before version numbers
74
- .gsub(/\s+/, ' ') # Clean up multiple spaces
75
- .gsub(/Aqa/, 'AQA') # Special case for AQA
76
- .strip
77
- end
78
-
79
- def supports_caching?(model_id)
80
- return false if model_id.match?(/flash-lite|gemini-1\.0/)
81
-
82
- model_id.match?(/gemini-[12]\.[05]/)
83
- end
84
-
85
- def supports_tuning?(model_id)
86
- model_id.match?(/gemini-1\.5-flash/)
87
- end
88
-
89
- def supports_audio?(model_id)
90
- model_id.match?(/gemini-[12]\.[05]/)
91
- end
92
-
93
- def model_type(model_id)
94
- case model_id
95
- when /text-embedding|embedding/ then 'embedding'
96
- when /imagen/ then 'image'
97
- else 'chat'
98
- end
99
- end
100
-
101
- def model_family(model_id) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength
102
- case model_id
103
- when /gemini-2\.0-flash-lite/ then 'gemini20_flash_lite'
104
- when /gemini-2\.0-flash/ then 'gemini20_flash'
105
- when /gemini-1\.5-flash-8b/ then 'gemini15_flash_8b'
106
- when /gemini-1\.5-flash/ then 'gemini15_flash'
107
- when /gemini-1\.5-pro/ then 'gemini15_pro'
108
- when /gemini-1\.0-pro/ then 'gemini10_pro'
109
- when /text-embedding-004/ then 'embedding4'
110
- when /embedding-001/ then 'embedding1'
111
- when /aqa/ then 'aqa'
112
- else 'other'
113
- end
114
- end
115
-
116
- def pricing_family(model_id) # rubocop:disable Metrics/CyclomaticComplexity
117
- case model_id
118
- when /gemini-2\.0-flash-lite/ then :flash_lite_2 # rubocop:disable Naming/VariableNumber
119
- when /gemini-2\.0-flash/ then :flash_2 # rubocop:disable Naming/VariableNumber
120
- when /gemini-1\.5-flash-8b/ then :flash_8b
121
- when /gemini-1\.5-flash/ then :flash
122
- when /gemini-1\.5-pro/ then :pro
123
- when /gemini-1\.0-pro/ then :pro_1_0 # rubocop:disable Naming/VariableNumber
124
- when /text-embedding|embedding/ then :embedding
125
- else :base
126
- end
127
- end
128
-
129
- private
130
-
131
- def long_context_model?(model_id)
132
- model_id.match?(/gemini-1\.5-(?:pro|flash)/)
133
- end
134
-
135
- def context_length(model_id)
136
- context_window_for(model_id)
137
- end
138
-
139
- PRICES = {
140
- flash_2: { # Gemini 2.0 Flash # rubocop:disable Naming/VariableNumber
141
- input: 0.10,
142
- output: 0.40,
143
- audio_input: 0.70,
144
- cache: 0.025,
145
- cache_storage: 1.00
146
- },
147
- flash_lite_2: { # Gemini 2.0 Flash Lite # rubocop:disable Naming/VariableNumber
148
- input: 0.075,
149
- output: 0.30,
150
- cache: 0.01875,
151
- cache_storage: 1.00
152
- },
153
- flash: { # Gemini 1.5 Flash
154
- input: 0.075,
155
- output: 0.30,
156
- cache: 0.01875,
157
- cache_storage: 1.00
158
- },
159
- flash_8b: { # Gemini 1.5 Flash 8B
160
- input: 0.0375,
161
- output: 0.15,
162
- cache: 0.01,
163
- cache_storage: 0.25
164
- },
165
- pro: { # Gemini 1.5 Pro
166
- input: 1.25,
167
- output: 5.0,
168
- cache: 0.3125,
169
- cache_storage: 4.50
170
- },
171
- pro_1_0: { # Gemini 1.0 Pro # rubocop:disable Naming/VariableNumber
172
- input: 0.50,
173
- output: 1.50
174
- },
175
- embedding: { # Text Embedding models
176
- input: 0.00,
177
- output: 0.00
178
- }
179
- }.freeze
180
-
181
- def default_input_price
182
- 0.075 # Default to Flash pricing
183
- end
184
-
185
- def default_output_price
186
- 0.30 # Default to Flash pricing
187
- end
188
- end
189
- end
190
- end
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module ModelCapabilities
5
- # Determines capabilities and pricing for OpenAI models
6
- module OpenAI # rubocop:disable Metrics/ModuleLength
7
- extend self
8
-
9
- def context_window_for(model_id)
10
- case model_id
11
- when /o[13]-mini/, /o3-mini-2025/ then 200_000
12
- when /o1-2024/ then 200_000
13
- when /gpt-4o/, /gpt-4-turbo/ then 128_000
14
- when /gpt-4-0[0-9]{3}/ then 8_192
15
- when /gpt-3.5-turbo-instruct/ then 4_096
16
- when /gpt-3.5/ then 16_385
17
- else 4_096
18
- end
19
- end
20
-
21
- def max_tokens_for(model_id) # rubocop:disable Metrics/CyclomaticComplexity
22
- case model_id
23
- when /o1-2024/, /o3-mini/ then 100_000
24
- when /o1-mini-2024/ then 65_536
25
- when /gpt-4o-2024-05-13/ then 4_096
26
- when /gpt-4o/, /gpt-4o-mini/ then 16_384
27
- when /gpt-4o-realtime/ then 4_096
28
- when /gpt-4-0[0-9]{3}/ then 8_192
29
- when /gpt-3.5-turbo/ then 4_096
30
- else 4_096
31
- end
32
- end
33
-
34
- def input_price_for(model_id)
35
- PRICES.dig(model_family(model_id), :input) || default_input_price
36
- end
37
-
38
- def output_price_for(model_id)
39
- PRICES.dig(model_family(model_id), :output) || default_output_price
40
- end
41
-
42
- def supports_vision?(model_id)
43
- model_id.match?(/gpt-4o|o1/) || model_id.match?(/gpt-4-(?!0314|0613)/)
44
- end
45
-
46
- def supports_functions?(model_id)
47
- !model_id.include?('instruct')
48
- end
49
-
50
- def supports_audio?(model_id)
51
- model_id.match?(/audio-preview|realtime-preview|whisper|tts/)
52
- end
53
-
54
- def supports_json_mode?(model_id)
55
- model_id.match?(/gpt-4-\d{4}-preview/) ||
56
- model_id.include?('turbo') ||
57
- model_id.match?(/gpt-3.5-turbo-(?!0301|0613)/)
58
- end
59
-
60
- def format_display_name(model_id)
61
- model_id.then { |id| humanize(id) }
62
- .then { |name| apply_special_formatting(name) }
63
- end
64
-
65
- def model_type(model_id)
66
- case model_id
67
- when /text-embedding|embedding/ then 'embedding'
68
- when /dall-e/ then 'image'
69
- when /tts|whisper/ then 'audio'
70
- when /omni-moderation/ then 'moderation'
71
- else 'chat'
72
- end
73
- end
74
-
75
- def supports_structured_output?(model_id)
76
- model_id.match?(/gpt-4o|o[13]-mini|o1/)
77
- end
78
-
79
- def model_family(model_id) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
80
- case model_id
81
- when /o3-mini/ then 'o3_mini'
82
- when /o1-mini/ then 'o1_mini'
83
- when /o1/ then 'o1'
84
- when /gpt-4o-audio/ then 'gpt4o_audio'
85
- when /gpt-4o-realtime/ then 'gpt4o_realtime'
86
- when /gpt-4o-mini-audio/ then 'gpt4o_mini_audio'
87
- when /gpt-4o-mini-realtime/ then 'gpt4o_mini_realtime'
88
- when /gpt-4o-mini/ then 'gpt4o_mini'
89
- when /gpt-4o/ then 'gpt4o'
90
- when /gpt-4-turbo/ then 'gpt4_turbo'
91
- when /gpt-4/ then 'gpt4'
92
- when /gpt-3.5-turbo-instruct/ then 'gpt35_instruct'
93
- when /gpt-3.5/ then 'gpt35'
94
- when /dall-e-3/ then 'dalle3'
95
- when /dall-e-2/ then 'dalle2'
96
- when /text-embedding-3-large/ then 'embedding3_large'
97
- when /text-embedding-3-small/ then 'embedding3_small'
98
- when /text-embedding-ada/ then 'embedding2'
99
- when /tts-1-hd/ then 'tts1_hd'
100
- when /tts-1/ then 'tts1'
101
- when /whisper/ then 'whisper1'
102
- when /omni-moderation/ then 'moderation'
103
- when /babbage/ then 'babbage'
104
- when /davinci/ then 'davinci'
105
- else 'other'
106
- end
107
- end
108
-
109
- private
110
-
111
- PRICES = {
112
- o1: { input: 15.0, cached_input: 7.5, output: 60.0 },
113
- o1_mini: { input: 1.10, cached_input: 0.55, output: 4.40 },
114
- o3_mini: { input: 1.10, cached_input: 0.55, output: 4.40 },
115
- gpt4o: { input: 2.50, cached_input: 1.25, output: 10.0 },
116
- gpt4o_audio: {
117
- text_input: 2.50,
118
- audio_input: 40.0,
119
- text_output: 10.0,
120
- audio_output: 80.0
121
- },
122
- gpt4o_realtime: {
123
- text_input: 5.0,
124
- cached_text_input: 2.50,
125
- audio_input: 40.0,
126
- cached_audio_input: 2.50,
127
- text_output: 20.0,
128
- audio_output: 80.0
129
- },
130
- gpt4o_mini: { input: 0.15, cached_input: 0.075, output: 0.60 },
131
- gpt4o_mini_audio: {
132
- text_input: 0.15,
133
- audio_input: 10.0,
134
- text_output: 0.60,
135
- audio_output: 20.0
136
- },
137
- gpt4o_mini_realtime: {
138
- text_input: 0.60,
139
- cached_text_input: 0.30,
140
- audio_input: 10.0,
141
- cached_audio_input: 0.30,
142
- text_output: 2.40,
143
- audio_output: 20.0
144
- },
145
- gpt4_turbo: { input: 10.0, output: 30.0 },
146
- gpt4: { input: 30.0, output: 60.0 },
147
- gpt35: { input: 0.50, output: 1.50 },
148
- gpt35_instruct: { input: 1.50, output: 2.0 },
149
- embedding3_large: { price: 0.13 },
150
- embedding3_small: { price: 0.02 },
151
- embedding2: { price: 0.10 },
152
- davinci: { input: 2.0, output: 2.0 },
153
- babbage: { input: 0.40, output: 0.40 },
154
- tts1: { price: 15.0 },
155
- tts1_hd: { price: 30.0 },
156
- whisper1: { price: 0.006 }
157
- }.freeze
158
-
159
- def default_input_price
160
- 0.50
161
- end
162
-
163
- def default_output_price
164
- 1.50
165
- end
166
-
167
- def humanize(id)
168
- id.tr('-', ' ')
169
- .split(' ')
170
- .map(&:capitalize)
171
- .join(' ')
172
- end
173
-
174
- def apply_special_formatting(name) # rubocop:disable Metrics/MethodLength
175
- name
176
- .gsub(/(\d{4}) (\d{2}) (\d{2})/, '\1\2\3')
177
- .gsub(/^Gpt /, 'GPT-')
178
- .gsub(/^O([13]) /, 'O\1-')
179
- .gsub(/^Chatgpt /, 'ChatGPT-')
180
- .gsub(/^Tts /, 'TTS-')
181
- .gsub(/^Dall E /, 'DALL-E-')
182
- .gsub(/3\.5 /, '3.5-')
183
- .gsub(/4 /, '4-')
184
- .gsub(/4o (?=Mini|Preview|Turbo|Audio)/, '4o-')
185
- .gsub(/\bHd\b/, 'HD')
186
- end
187
- end
188
- end
189
- end