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
@@ -5,17 +5,24 @@ module RubyLLM
5
5
  # OpenAI API integration. Handles chat completion, function calling,
6
6
  # and OpenAI's unique streaming format. Supports GPT-4, GPT-3.5,
7
7
  # and other OpenAI models.
8
- class OpenAI # rubocop:disable Metrics/ClassLength
9
- include Provider
8
+ module OpenAI
9
+ extend Provider
10
+ extend OpenAI::Chat
11
+ extend OpenAI::Embeddings
12
+ extend OpenAI::Models
13
+ extend OpenAI::Streaming
14
+ extend OpenAI::Tools
10
15
 
11
- def parse_error(response)
12
- return if response.body.empty?
13
-
14
- body = try_parse_json(response.body)
15
- body.is_a?(Hash) ? body.dig('error', 'message') : body
16
+ def self.extended(base)
17
+ base.extend(Provider)
18
+ base.extend(OpenAI::Chat)
19
+ base.extend(OpenAI::Embeddings)
20
+ base.extend(OpenAI::Models)
21
+ base.extend(OpenAI::Streaming)
22
+ base.extend(OpenAI::Tools)
16
23
  end
17
24
 
18
- private
25
+ module_function
19
26
 
20
27
  def api_base
21
28
  'https://api.openai.com/v1'
@@ -26,195 +33,6 @@ module RubyLLM
26
33
  'Authorization' => "Bearer #{RubyLLM.config.openai_api_key}"
27
34
  }
28
35
  end
29
-
30
- def completion_url
31
- 'chat/completions'
32
- end
33
-
34
- def stream_url
35
- completion_url
36
- end
37
-
38
- def models_url
39
- 'models'
40
- end
41
-
42
- def embedding_url
43
- 'embeddings'
44
- end
45
-
46
- def build_payload(messages, tools:, temperature:, model:, stream: false) # rubocop:disable Metrics/MethodLength
47
- {
48
- model: model,
49
- messages: format_messages(messages),
50
- temperature: temperature,
51
- stream: stream
52
- }.tap do |payload|
53
- if tools.any?
54
- payload[:tools] = tools.map { |_, tool| tool_for(tool) }
55
- payload[:tool_choice] = 'auto'
56
- end
57
- payload[:stream_options] = { include_usage: true } if stream
58
- end
59
- end
60
-
61
- def format_messages(messages)
62
- messages.map do |msg|
63
- {
64
- role: format_role(msg.role),
65
- content: msg.content,
66
- tool_calls: format_tool_calls(msg.tool_calls),
67
- tool_call_id: msg.tool_call_id
68
- }.compact
69
- end
70
- end
71
-
72
- def format_role(role)
73
- case role
74
- when :system
75
- 'developer'
76
- else
77
- role.to_s
78
- end
79
- end
80
-
81
- def build_embedding_payload(text, model:)
82
- {
83
- model: model,
84
- input: text
85
- }
86
- end
87
-
88
- def parse_embedding_response(response)
89
- embeddings = response.body['data'].map { |d| d['embedding'] }
90
- embeddings.size == 1 ? embeddings.first : embeddings
91
- end
92
-
93
- def format_tool_calls(tool_calls) # rubocop:disable Metrics/MethodLength
94
- return nil unless tool_calls&.any?
95
-
96
- tool_calls.map do |_, tc|
97
- {
98
- id: tc.id,
99
- type: 'function',
100
- function: {
101
- name: tc.name,
102
- arguments: JSON.generate(tc.arguments)
103
- }
104
- }
105
- end
106
- end
107
-
108
- def tool_for(tool) # rubocop:disable Metrics/MethodLength
109
- {
110
- type: 'function',
111
- function: {
112
- name: tool.name,
113
- description: tool.description,
114
- parameters: {
115
- type: 'object',
116
- properties: tool.parameters.transform_values { |param| param_schema(param) },
117
- required: tool.parameters.select { |_, p| p.required }.keys
118
- }
119
- }
120
- }
121
- end
122
-
123
- def param_schema(param)
124
- {
125
- type: param.type,
126
- description: param.description
127
- }.compact
128
- end
129
-
130
- def parse_completion_response(response) # rubocop:disable Metrics/MethodLength
131
- data = response.body
132
- return if data.empty?
133
-
134
- message_data = data.dig('choices', 0, 'message')
135
- return unless message_data
136
-
137
- Message.new(
138
- role: :assistant,
139
- content: message_data['content'],
140
- tool_calls: parse_tool_calls(message_data['tool_calls']),
141
- input_tokens: data['usage']['prompt_tokens'],
142
- output_tokens: data['usage']['completion_tokens'],
143
- model_id: data['model']
144
- )
145
- end
146
-
147
- def parse_tool_calls(tool_calls, parse_arguments: true) # rubocop:disable Metrics/MethodLength
148
- return nil unless tool_calls&.any?
149
-
150
- tool_calls.to_h do |tc|
151
- [
152
- tc['id'],
153
- ToolCall.new(
154
- id: tc['id'],
155
- name: tc.dig('function', 'name'),
156
- arguments: parse_arguments ? JSON.parse(tc.dig('function', 'arguments')) : tc.dig('function', 'arguments')
157
- )
158
- ]
159
- end
160
- end
161
-
162
- def parse_models_response(response) # rubocop:disable Metrics/MethodLength
163
- (response.body['data'] || []).map do |model|
164
- model_info = begin
165
- Models.find(model['id'])
166
- rescue StandardError
167
- nil
168
- end
169
- next unless model_info
170
-
171
- model_info.tap do |info|
172
- info.metadata.merge!(
173
- object: model['object'],
174
- owned_by: model['owned_by']
175
- )
176
- end
177
- end.compact
178
- end
179
-
180
- def handle_stream(&block) # rubocop:disable Metrics/MethodLength
181
- to_json_stream do |data|
182
- block.call(
183
- Chunk.new(
184
- role: :assistant,
185
- model_id: data['model'],
186
- content: data.dig('choices', 0, 'delta', 'content'),
187
- tool_calls: parse_tool_calls(data.dig('choices', 0, 'delta', 'tool_calls'), parse_arguments: false),
188
- input_tokens: data.dig('usage', 'prompt_tokens'),
189
- output_tokens: data.dig('usage', 'completion_tokens')
190
- )
191
- )
192
- end
193
- end
194
-
195
- def parse_list_models_response(response) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
196
- (response.body['data'] || []).map do |model|
197
- ModelInfo.new(
198
- id: model['id'],
199
- created_at: model['created'] ? Time.at(model['created']) : nil,
200
- display_name: capabilities.format_display_name(model['id']),
201
- provider: slug,
202
- type: capabilities.model_type(model['id']),
203
- family: capabilities.model_family(model['id']),
204
- metadata: {
205
- object: model['object'],
206
- owned_by: model['owned_by']
207
- },
208
- context_window: capabilities.context_window_for(model['id']),
209
- max_tokens: capabilities.max_tokens_for(model['id']),
210
- supports_vision: capabilities.supports_vision?(model['id']),
211
- supports_functions: capabilities.supports_functions?(model['id']),
212
- supports_json_mode: capabilities.supports_json_mode?(model['id']),
213
- input_price_per_million: capabilities.input_price_for(model['id']),
214
- output_price_per_million: capabilities.output_price_for(model['id'])
215
- )
216
- end
217
- end
218
36
  end
219
37
  end
220
38
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- VERSION = '0.1.0.pre30'
4
+ VERSION = '0.1.0.pre31'
5
5
  end
data/lib/ruby_llm.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'zeitwerk'
3
+ require 'base64'
4
+ require 'event_stream_parser'
4
5
  require 'faraday'
6
+ require 'faraday/retry'
5
7
  require 'json'
6
8
  require 'logger'
7
- require 'event_stream_parser'
8
9
  require 'securerandom'
10
+ require 'zeitwerk'
9
11
 
10
12
  loader = Zeitwerk::Loader.for_gem
11
13
  loader.inflector.inflect(
data/ruby_llm.gemspec CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency 'event_stream_parser', '>= 0.3.0', '< 2.0.0'
37
37
  spec.add_dependency 'faraday', '>= 2.0'
38
38
  spec.add_dependency 'faraday-multipart', '>= 1.0'
39
+ spec.add_dependency 'faraday-retry', '>= 2.0'
39
40
  spec.add_dependency 'zeitwerk', '>= 2.6'
40
41
 
41
42
  # Rails integration dependencies
@@ -56,6 +57,7 @@ Gem::Specification.new do |spec|
56
57
  spec.add_development_dependency 'rubocop', '>= 1.0'
57
58
  spec.add_development_dependency 'rubocop-rake', '>= 0.6'
58
59
  spec.add_development_dependency 'simplecov', '>= 0.21'
60
+ spec.add_development_dependency 'sqlite3'
59
61
  spec.add_development_dependency 'webmock', '~> 3.18'
60
62
  spec.add_development_dependency 'yard', '>= 0.9'
61
63
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre30
4
+ version: 0.1.0.pre31
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-17 00:00:00.000000000 Z
11
+ date: 2025-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: event_stream_parser
@@ -58,6 +58,20 @@ dependencies:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
60
  version: '1.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: faraday-retry
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
61
75
  - !ruby/object:Gem::Dependency
62
76
  name: zeitwerk
63
77
  requirement: !ruby/object:Gem::Requirement
@@ -294,6 +308,20 @@ dependencies:
294
308
  - - ">="
295
309
  - !ruby/object:Gem::Version
296
310
  version: '0.21'
311
+ - !ruby/object:Gem::Dependency
312
+ name: sqlite3
313
+ requirement: !ruby/object:Gem::Requirement
314
+ requirements:
315
+ - - ">="
316
+ - !ruby/object:Gem::Version
317
+ version: '0'
318
+ type: :development
319
+ prerelease: false
320
+ version_requirements: !ruby/object:Gem::Requirement
321
+ requirements:
322
+ - - ">="
323
+ - !ruby/object:Gem::Version
324
+ version: '0'
297
325
  - !ruby/object:Gem::Dependency
298
326
  name: webmock
299
327
  requirement: !ruby/object:Gem::Requirement
@@ -332,11 +360,11 @@ executables: []
332
360
  extensions: []
333
361
  extra_rdoc_files: []
334
362
  files:
335
- - ".github/workflows/gem-push.yml"
336
- - ".github/workflows/test.yml"
363
+ - ".github/workflows/cicd.yml"
337
364
  - ".gitignore"
338
365
  - ".overcommit.yml"
339
366
  - ".rspec"
367
+ - ".rspec_status"
340
368
  - ".rubocop.yml"
341
369
  - Gemfile
342
370
  - LICENSE
@@ -349,21 +377,33 @@ files:
349
377
  - lib/ruby_llm/chat.rb
350
378
  - lib/ruby_llm/chunk.rb
351
379
  - lib/ruby_llm/configuration.rb
380
+ - lib/ruby_llm/content.rb
352
381
  - lib/ruby_llm/embedding.rb
353
382
  - lib/ruby_llm/error.rb
354
383
  - lib/ruby_llm/message.rb
355
- - lib/ruby_llm/model_capabilities/anthropic.rb
356
- - lib/ruby_llm/model_capabilities/deepseek.rb
357
- - lib/ruby_llm/model_capabilities/gemini.rb
358
- - lib/ruby_llm/model_capabilities/openai.rb
359
384
  - lib/ruby_llm/model_info.rb
360
385
  - lib/ruby_llm/models.json
361
386
  - lib/ruby_llm/models.rb
362
387
  - lib/ruby_llm/provider.rb
363
388
  - lib/ruby_llm/providers/anthropic.rb
389
+ - lib/ruby_llm/providers/anthropic/capabilities.rb
390
+ - lib/ruby_llm/providers/anthropic/chat.rb
391
+ - lib/ruby_llm/providers/anthropic/embeddings.rb
392
+ - lib/ruby_llm/providers/anthropic/models.rb
393
+ - lib/ruby_llm/providers/anthropic/streaming.rb
394
+ - lib/ruby_llm/providers/anthropic/tools.rb
364
395
  - lib/ruby_llm/providers/deepseek.rb
396
+ - lib/ruby_llm/providers/deepseek/capabilites.rb
365
397
  - lib/ruby_llm/providers/gemini.rb
398
+ - lib/ruby_llm/providers/gemini/capabilities.rb
399
+ - lib/ruby_llm/providers/gemini/models.rb
366
400
  - lib/ruby_llm/providers/openai.rb
401
+ - lib/ruby_llm/providers/openai/capabilities.rb
402
+ - lib/ruby_llm/providers/openai/chat.rb
403
+ - lib/ruby_llm/providers/openai/embeddings.rb
404
+ - lib/ruby_llm/providers/openai/models.rb
405
+ - lib/ruby_llm/providers/openai/streaming.rb
406
+ - lib/ruby_llm/providers/openai/tools.rb
367
407
  - lib/ruby_llm/railtie.rb
368
408
  - lib/ruby_llm/stream_accumulator.rb
369
409
  - lib/ruby_llm/tool.rb
@@ -1,35 +0,0 @@
1
- name: Test
2
-
3
-
4
-
5
- on:
6
- push:
7
- branches: [ "main" ]
8
- pull_request:
9
- branches: [ "main" ]
10
- workflow_call:
11
-
12
- jobs:
13
- test:
14
- runs-on: ubuntu-latest
15
- strategy:
16
- matrix:
17
- ruby-version: ['3.1', '3.2', '3.3']
18
-
19
- steps:
20
- - uses: actions/checkout@v4
21
-
22
- - name: Set up Ruby
23
- uses: ruby/setup-ruby@v1
24
- with:
25
- ruby-version: ${{ matrix.ruby-version }}
26
- bundler-cache: true
27
-
28
- - name: Install dependencies
29
- run: bundle install
30
-
31
- - name: Check code format
32
- run: bundle exec rubocop
33
-
34
- # - name: Run tests
35
- # run: bundle exec rspec
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module ModelCapabilities
5
- # Determines capabilities and pricing for Anthropic models
6
- module Anthropic
7
- module_function
8
-
9
- def determine_context_window(model_id)
10
- case model_id
11
- when /claude-3/ then 200_000
12
- else 100_000
13
- end
14
- end
15
-
16
- def determine_max_tokens(model_id)
17
- case model_id
18
- when /claude-3-5/ then 8_192
19
- else 4_096
20
- end
21
- end
22
-
23
- def get_input_price(model_id)
24
- PRICES.dig(model_family(model_id), :input) || default_input_price
25
- end
26
-
27
- def get_output_price(model_id)
28
- PRICES.dig(model_family(model_id), :output) || default_output_price
29
- end
30
-
31
- def supports_vision?(model_id)
32
- return false if model_id.match?(/claude-3-5-haiku/)
33
- return false if model_id.match?(/claude-[12]/)
34
-
35
- true
36
- end
37
-
38
- def supports_functions?(model_id)
39
- model_id.include?('claude-3')
40
- end
41
-
42
- def supports_json_mode?(model_id)
43
- model_id.include?('claude-3')
44
- end
45
-
46
- def model_family(model_id)
47
- case model_id
48
- when /claude-3-5-sonnet/ then :claude35_sonnet
49
- when /claude-3-5-haiku/ then :claude35_haiku
50
- when /claude-3-opus/ then :claude3_opus
51
- when /claude-3-sonnet/ then :claude3_sonnet
52
- when /claude-3-haiku/ then :claude3_haiku
53
- else :claude2
54
- end
55
- end
56
-
57
- def model_type(_)
58
- 'chat'
59
- end
60
-
61
- PRICES = {
62
- claude35_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
63
- claude35_haiku: { input: 0.80, output: 4.0 }, # $0.80/$4.00 per million tokens
64
- claude3_opus: { input: 15.0, output: 75.0 }, # $15.00/$75.00 per million tokens
65
- claude3_sonnet: { input: 3.0, output: 15.0 }, # $3.00/$15.00 per million tokens
66
- claude3_haiku: { input: 0.25, output: 1.25 }, # $0.25/$1.25 per million tokens
67
- claude2: { input: 3.0, output: 15.0 } # Default pricing for Claude 2.x models
68
- }.freeze
69
-
70
- def default_input_price
71
- 3.0
72
- end
73
-
74
- def default_output_price
75
- 15.0
76
- end
77
- end
78
- end
79
- end
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module ModelCapabilities
5
- # Determines capabilities and pricing for DeepSeek models
6
- module DeepSeek
7
- module_function
8
-
9
- # Returns the context window size for the given model
10
- # @param model_id [String] the model identifier
11
- # @return [Integer] the context window size in tokens
12
- def context_window_for(model_id)
13
- case model_id
14
- when /deepseek-(?:chat|reasoner)/ then 64_000
15
- else 32_768 # Sensible default
16
- end
17
- end
18
-
19
- # Returns the maximum output tokens for the given model
20
- # @param model_id [String] the model identifier
21
- # @return [Integer] the maximum output tokens
22
- def max_tokens_for(model_id)
23
- case model_id
24
- when /deepseek-(?:chat|reasoner)/ then 8_192
25
- else 4_096 # Default if max_tokens not specified
26
- end
27
- end
28
-
29
- # Returns the input price per million tokens for the given model
30
- # @param model_id [String] the model identifier
31
- # @return [Float] the price per million tokens
32
- def input_price_for(model_id)
33
- PRICES.dig(model_family(model_id), :input_miss) || default_input_price
34
- end
35
-
36
- # Returns the output price per million tokens for the given model
37
- # @param model_id [String] the model identifier
38
- # @return [Float] the price per million tokens
39
- def output_price_for(model_id)
40
- PRICES.dig(model_family(model_id), :output) || default_output_price
41
- end
42
-
43
- # Returns the cache hit price per million tokens for the given model
44
- # @param model_id [String] the model identifier
45
- # @return [Float] the price per million tokens
46
- def cache_hit_price_for(model_id)
47
- PRICES.dig(model_family(model_id), :input_hit) || default_cache_hit_price
48
- end
49
-
50
- # Determines if the model supports vision capabilities
51
- # @param model_id [String] the model identifier
52
- # @return [Boolean] true if the model supports vision
53
- def supports_vision?(_model_id)
54
- false # DeepSeek models don't currently support vision
55
- end
56
-
57
- # Determines if the model supports function calling
58
- # @param model_id [String] the model identifier
59
- # @return [Boolean] true if the model supports function calling
60
- def supports_functions?(model_id)
61
- model_id.match?(/deepseek-chat/) # Only deepseek-chat supports function calling
62
- end
63
-
64
- # Determines if the model supports JSON mode
65
- # @param model_id [String] the model identifier
66
- # @return [Boolean] true if the model supports JSON mode
67
- def supports_json_mode?(model_id)
68
- model_id.match?(/deepseek-chat/) # Only deepseek-chat supports JSON mode
69
- end
70
-
71
- # Formats the model ID into a display name
72
- # @param model_id [String] the model identifier
73
- # @return [String] the formatted display name
74
- def format_display_name(model_id)
75
- case model_id
76
- when 'deepseek-chat' then 'DeepSeek V3'
77
- when 'deepseek-reasoner' then 'DeepSeek R1'
78
- else
79
- model_id.split('-')
80
- .map(&:capitalize)
81
- .join(' ')
82
- end
83
- end
84
-
85
- # Returns the model type
86
- # @param model_id [String] the model identifier
87
- # @return [String] the model type
88
- def model_type(_model_id)
89
- 'chat' # All DeepSeek models are chat models
90
- end
91
-
92
- # Returns the model family for pricing purposes
93
- # @param model_id [String] the model identifier
94
- # @return [String] the model family identifier
95
- def model_family(model_id)
96
- case model_id
97
- when /deepseek-chat/ then :chat
98
- when /deepseek-reasoner/ then :reasoner
99
- else :chat # Default to chat family
100
- end
101
- end
102
-
103
- # Pricing information for DeepSeek models (USD per 1M tokens)
104
- PRICES = {
105
- chat: {
106
- input_hit: 0.07, # $0.07 per million tokens on cache hit
107
- input_miss: 0.27, # $0.27 per million tokens on cache miss
108
- output: 1.10 # $1.10 per million tokens output
109
- },
110
- reasoner: {
111
- input_hit: 0.14, # $0.14 per million tokens on cache hit
112
- input_miss: 0.55, # $0.55 per million tokens on cache miss
113
- output: 2.19 # $2.19 per million tokens output
114
- }
115
- }.freeze
116
-
117
- private
118
-
119
- def default_input_price
120
- 0.27 # Default to chat cache miss price
121
- end
122
-
123
- def default_output_price
124
- 1.10 # Default to chat output price
125
- end
126
-
127
- def default_cache_hit_price
128
- 0.07 # Default to chat cache hit price
129
- end
130
- end
131
- end
132
- end