lex-llm 0.7.6 → 0.8.4

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 (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +139 -0
  3. data/RULES.md +97 -0
  4. data/lib/legion/extensions/llm/auto_registration.rb +4 -13
  5. data/lib/legion/extensions/llm/canonical/chunk.rb +107 -94
  6. data/lib/legion/extensions/llm/canonical/content_block.rb +65 -75
  7. data/lib/legion/extensions/llm/canonical/message.rb +53 -69
  8. data/lib/legion/extensions/llm/canonical/params.rb +58 -34
  9. data/lib/legion/extensions/llm/canonical/request.rb +68 -56
  10. data/lib/legion/extensions/llm/canonical/response.rb +62 -78
  11. data/lib/legion/extensions/llm/canonical/strict.rb +105 -0
  12. data/lib/legion/extensions/llm/canonical/thinking.rb +36 -84
  13. data/lib/legion/extensions/llm/canonical/thinking_config.rb +149 -0
  14. data/lib/legion/extensions/llm/canonical/tool_call.rb +43 -53
  15. data/lib/legion/extensions/llm/canonical/tool_definition.rb +56 -46
  16. data/lib/legion/extensions/llm/canonical/tool_schema.rb +15 -22
  17. data/lib/legion/extensions/llm/canonical/usage.rb +47 -40
  18. data/lib/legion/extensions/llm/canonical.rb +7 -5
  19. data/lib/legion/extensions/llm/configuration.rb +40 -10
  20. data/lib/legion/extensions/llm/connection.rb +8 -30
  21. data/lib/legion/extensions/llm/credential_sources.rb +32 -49
  22. data/lib/legion/extensions/llm/discovery/actor.rb +92 -0
  23. data/lib/legion/extensions/llm/discovery/pipeline.rb +604 -0
  24. data/lib/legion/extensions/llm/error.rb +0 -14
  25. data/lib/legion/extensions/llm/fleet/contract_error.rb +15 -0
  26. data/lib/legion/extensions/llm/fleet/envelope_validation.rb +7 -6
  27. data/lib/legion/extensions/llm/fleet/fleet_envelope.rb +66 -0
  28. data/lib/legion/extensions/llm/fleet/protocol.rb +18 -5
  29. data/lib/legion/extensions/llm/fleet/provider_responder.rb +58 -156
  30. data/lib/legion/extensions/llm/fleet/token_validator.rb +15 -21
  31. data/lib/legion/extensions/llm/fleet/worker_execution.rb +107 -154
  32. data/lib/legion/extensions/llm/inventory/errors.rb +0 -1
  33. data/lib/legion/extensions/llm/inventory/evidence.rb +1 -1
  34. data/lib/legion/extensions/llm/inventory/identity.rb +55 -39
  35. data/lib/legion/extensions/llm/inventory/probe_token.rb +9 -12
  36. data/lib/legion/extensions/llm/inventory/publisher.rb +15 -53
  37. data/lib/legion/extensions/llm/inventory/records.rb +36 -100
  38. data/lib/legion/extensions/llm/inventory/registry.rb +54 -48
  39. data/lib/legion/extensions/llm/inventory/snapshot.rb +7 -21
  40. data/lib/legion/extensions/llm/inventory/weight_reconciler.rb +13 -6
  41. data/lib/legion/extensions/llm/inventory/weight_schema.rb +47 -8
  42. data/lib/legion/extensions/llm/provider/open_ai_compatible.rb +178 -182
  43. data/lib/legion/extensions/llm/provider.rb +192 -315
  44. data/lib/legion/extensions/llm/provider_contract.rb +25 -8
  45. data/lib/legion/extensions/llm/provider_settings.rb +5 -26
  46. data/lib/legion/extensions/llm/responses/thinking_extractor.rb +8 -1
  47. data/lib/legion/extensions/llm/responses/tool_arguments.rb +48 -0
  48. data/lib/legion/extensions/llm/routing/provider_outcome.rb +25 -0
  49. data/lib/legion/extensions/llm/routing/records.rb +34 -17
  50. data/lib/legion/extensions/llm/stream_accumulator.rb +186 -270
  51. data/lib/legion/extensions/llm/streaming.rb +50 -35
  52. data/lib/legion/extensions/llm/taxonomies.rb +14 -26
  53. data/lib/legion/extensions/llm/transport/fleet_lane.rb +8 -10
  54. data/lib/legion/extensions/llm/transport/messages/fleet_error.rb +3 -2
  55. data/lib/legion/extensions/llm/transport/messages/fleet_request.rb +6 -8
  56. data/lib/legion/extensions/llm/transport/messages/fleet_response.rb +10 -9
  57. data/lib/legion/extensions/llm/utils.rb +23 -5
  58. data/lib/legion/extensions/llm/version.rb +1 -1
  59. data/lib/legion/extensions/llm.rb +8 -98
  60. data/spec/legion/extensions/llm/auto_registration_spec.rb +4 -9
  61. data/spec/legion/extensions/llm/canonical/chunk_spec.rb +66 -252
  62. data/spec/legion/extensions/llm/canonical/content_block_spec.rb +52 -197
  63. data/spec/legion/extensions/llm/canonical/message_spec.rb +89 -204
  64. data/spec/legion/extensions/llm/canonical/params_spec.rb +55 -136
  65. data/spec/legion/extensions/llm/canonical/request_spec.rb +81 -143
  66. data/spec/legion/extensions/llm/canonical/response_spec.rb +68 -204
  67. data/spec/legion/extensions/llm/canonical/thinking/config_spec.rb +222 -0
  68. data/spec/legion/extensions/llm/canonical/thinking_spec.rb +23 -171
  69. data/spec/legion/extensions/llm/canonical/tool_call_spec.rb +59 -162
  70. data/spec/legion/extensions/llm/canonical/tool_definition_spec.rb +55 -191
  71. data/spec/legion/extensions/llm/canonical/tool_schema_spec.rb +26 -67
  72. data/spec/legion/extensions/llm/canonical/usage_spec.rb +46 -155
  73. data/spec/legion/extensions/llm/configuration_spec.rb +31 -5
  74. data/spec/legion/extensions/llm/conformance/canonical_type_examples.rb +106 -0
  75. data/spec/legion/extensions/llm/conformance/client_translator_examples.rb +1 -2
  76. data/spec/legion/extensions/llm/conformance/conformance.rb +10 -2
  77. data/spec/legion/extensions/llm/conformance/fixtures/canonical_fleet_round_trip.json +1 -1
  78. data/spec/legion/extensions/llm/conformance/fixtures/canonical_thinking_request.json +2 -2
  79. data/spec/legion/extensions/llm/conformance/provider_translator_examples.rb +1 -1
  80. data/spec/legion/extensions/llm/conformance/ssot_contract_conformance_spec.rb +128 -0
  81. data/spec/legion/extensions/llm/conformance/ssot_contract_examples.rb +505 -0
  82. data/spec/legion/extensions/llm/conformance/ssot_provider_examples.rb +11 -10
  83. data/spec/legion/extensions/llm/credential_sources_spec.rb +12 -13
  84. data/spec/legion/extensions/llm/error_spec.rb +2 -12
  85. data/spec/legion/extensions/llm/fleet/exact_offering_spec.rb +59 -45
  86. data/spec/legion/extensions/llm/fleet/provider_responder_spec.rb +173 -95
  87. data/spec/legion/extensions/llm/fleet/token_validator_spec.rb +7 -2
  88. data/spec/legion/extensions/llm/fleet/worker_execution_spec.rb +93 -74
  89. data/spec/legion/extensions/llm/fleet_messages_spec.rb +119 -125
  90. data/spec/legion/extensions/llm/gemspec_spec.rb +1 -2
  91. data/spec/legion/extensions/llm/inventory/boot_spec.rb +4 -4
  92. data/spec/legion/extensions/llm/inventory/identity_spec.rb +127 -110
  93. data/spec/legion/extensions/llm/inventory/probe_token_spec.rb +4 -4
  94. data/spec/legion/extensions/llm/inventory/publisher_spec.rb +8 -61
  95. data/spec/legion/extensions/llm/inventory/records_spec.rb +82 -47
  96. data/spec/legion/extensions/llm/inventory/registry_activation_spec.rb +49 -16
  97. data/spec/legion/extensions/llm/inventory/registry_replacement_spec.rb +5 -4
  98. data/spec/legion/extensions/llm/inventory/snapshot_spec.rb +11 -5
  99. data/spec/legion/extensions/llm/inventory/weight_reconciler_spec.rb +7 -3
  100. data/spec/legion/extensions/llm/inventory/weight_schema_spec.rb +60 -10
  101. data/spec/legion/extensions/llm/provider/open_ai_compatible_spec.rb +128 -68
  102. data/spec/legion/extensions/llm/provider/open_ai_compatible_tool_calls_array_spec.rb +7 -31
  103. data/spec/legion/extensions/llm/provider_contract_spec.rb +10 -15
  104. data/spec/legion/extensions/llm/provider_spec.rb +97 -78
  105. data/spec/legion/extensions/llm/routing/records_spec.rb +38 -5
  106. data/spec/legion/extensions/llm/stream_accumulator_spec.rb +174 -144
  107. data/spec/legion/extensions/llm/streaming_spec.rb +27 -0
  108. data/spec/legion/extensions/llm/taxonomies_spec.rb +43 -43
  109. data/spec/legion/extensions/llm/transport/fleet_lane_spec.rb +1 -1
  110. data/spec/legion/extensions/llm/utils_spec.rb +26 -7
  111. data/spec/legion/extensions/llm_base_contract_spec.rb +55 -90
  112. data/spec/legion/extensions/llm_extension_spec.rb +5 -5
  113. data/spec/support/fake_llm_provider.rb +45 -39
  114. data/spec/support/fake_ssot_harness.rb +7 -2
  115. metadata +13 -54
  116. data/lib/legion/extensions/llm/agent.rb +0 -366
  117. data/lib/legion/extensions/llm/aliases.json +0 -436
  118. data/lib/legion/extensions/llm/aliases.rb +0 -67
  119. data/lib/legion/extensions/llm/attachment.rb +0 -229
  120. data/lib/legion/extensions/llm/chat.rb +0 -354
  121. data/lib/legion/extensions/llm/chunk.rb +0 -10
  122. data/lib/legion/extensions/llm/content.rb +0 -81
  123. data/lib/legion/extensions/llm/context.rb +0 -33
  124. data/lib/legion/extensions/llm/embedding.rb +0 -33
  125. data/lib/legion/extensions/llm/image.rb +0 -109
  126. data/lib/legion/extensions/llm/inventory/capabilities.rb +0 -40
  127. data/lib/legion/extensions/llm/inventory/scoped_refresher.rb +0 -310
  128. data/lib/legion/extensions/llm/message.rb +0 -118
  129. data/lib/legion/extensions/llm/mime_type.rb +0 -75
  130. data/lib/legion/extensions/llm/model/info.rb +0 -286
  131. data/lib/legion/extensions/llm/model/modalities.rb +0 -26
  132. data/lib/legion/extensions/llm/model/pricing.rb +0 -52
  133. data/lib/legion/extensions/llm/model/pricing_category.rb +0 -50
  134. data/lib/legion/extensions/llm/model/pricing_tier.rb +0 -37
  135. data/lib/legion/extensions/llm/model.rb +0 -11
  136. data/lib/legion/extensions/llm/models.json +0 -57313
  137. data/lib/legion/extensions/llm/models.rb +0 -530
  138. data/lib/legion/extensions/llm/models_schema.json +0 -168
  139. data/lib/legion/extensions/llm/moderation.rb +0 -60
  140. data/lib/legion/extensions/llm/registry_event_builder.rb +0 -141
  141. data/lib/legion/extensions/llm/registry_publisher.rb +0 -107
  142. data/lib/legion/extensions/llm/responses/chat_response.rb +0 -43
  143. data/lib/legion/extensions/llm/responses/embedding_response.rb +0 -38
  144. data/lib/legion/extensions/llm/responses/stream_chunk.rb +0 -43
  145. data/lib/legion/extensions/llm/routing/lane_key.rb +0 -66
  146. data/lib/legion/extensions/llm/routing/model_offering.rb +0 -241
  147. data/lib/legion/extensions/llm/routing/offering_registry.rb +0 -101
  148. data/lib/legion/extensions/llm/routing/registry_event.rb +0 -167
  149. data/lib/legion/extensions/llm/thinking.rb +0 -53
  150. data/lib/legion/extensions/llm/tokens.rb +0 -51
  151. data/lib/legion/extensions/llm/tool_call.rb +0 -34
  152. data/lib/legion/extensions/llm/transcription.rb +0 -39
  153. data/lib/legion/extensions/llm/transport/messages/registry_event.rb +0 -44
  154. data/spec/legion/extensions/llm/agent_spec.rb +0 -179
  155. data/spec/legion/extensions/llm/attachment_spec.rb +0 -25
  156. data/spec/legion/extensions/llm/conformance/fixtures/ssot_identity_vectors.json +0 -84
  157. data/spec/legion/extensions/llm/context_spec.rb +0 -127
  158. data/spec/legion/extensions/llm/inventory/capabilities_spec.rb +0 -43
  159. data/spec/legion/extensions/llm/inventory/scoped_refresher_spec.rb +0 -340
  160. data/spec/legion/extensions/llm/message_spec.rb +0 -64
  161. data/spec/legion/extensions/llm/model/info_spec.rb +0 -222
  162. data/spec/legion/extensions/llm/models_spec.rb +0 -104
  163. data/spec/legion/extensions/llm/registry_event_builder_spec.rb +0 -68
  164. data/spec/legion/extensions/llm/registry_publisher_spec.rb +0 -22
  165. data/spec/legion/extensions/llm/responses/response_objects_spec.rb +0 -75
  166. data/spec/legion/extensions/llm/routing/model_offering_spec.rb +0 -281
  167. data/spec/legion/extensions/llm/routing/offering_registry_spec.rb +0 -50
  168. data/spec/legion/extensions/llm/routing/registry_event_spec.rb +0 -120
@@ -1,286 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Model
7
- CAPABILITY_ALIASES = {
8
- function_calling: :tools,
9
- functions: :tools,
10
- tool: :tools,
11
- tool_use: :tools,
12
- stream: :streaming,
13
- stream_chat: :streaming
14
- }.freeze
15
-
16
- Info = ::Data.define(
17
- :id, :name, :provider, :instance, :family,
18
- :capabilities, :context_length, :parameter_count,
19
- :parameter_size, :quantization, :size_bytes,
20
- :modalities_input, :modalities_output, :metadata
21
- ) do
22
- # rubocop:disable Metrics/ParameterLists
23
- def initialize(
24
- id:, name: nil, provider: nil, instance: :default,
25
- family: nil, capabilities: [], context_length: nil,
26
- parameter_count: nil, parameter_size: nil, quantization: nil,
27
- size_bytes: nil, modalities_input: [], modalities_output: [],
28
- metadata: {}
29
- )
30
- normalized_family = family.nil? ? nil : family.to_s.downcase.strip
31
-
32
- super(
33
- id: id.to_s.strip,
34
- name: (name || id).to_s.strip,
35
- provider: provider.to_s.downcase.to_sym,
36
- instance: (instance || :default).to_s.downcase.to_sym,
37
- family: normalized_family,
38
- capabilities: normalize_capabilities(capabilities),
39
- context_length: to_int(context_length),
40
- parameter_count: to_int(parameter_count),
41
- parameter_size: parameter_size&.to_s&.strip,
42
- quantization: quantization&.to_s&.strip,
43
- size_bytes: to_int(size_bytes),
44
- modalities_input: normalize_symbols(modalities_input),
45
- modalities_output: normalize_symbols(modalities_output),
46
- metadata: metadata.is_a?(Hash) ? metadata : {}
47
- )
48
- end
49
- # rubocop:enable Metrics/ParameterLists
50
-
51
- # ── Capability predicates ─────────────────────────────────────
52
-
53
- def completion? = capabilities.include?(:completion)
54
- def embedding? = capabilities.include?(:embedding)
55
- def vision? = capabilities.include?(:vision)
56
- def tools? = capabilities.include?(:tools)
57
- def thinking? = capabilities.include?(:thinking)
58
-
59
- # Returns true if the model supports prompt caching (Anthropic Claude 4.x, 3.5 Sonnet+).
60
- # Checks the explicit `prompt_caching` capability flag in the capabilities array.
61
- def supports_prompt_caching?
62
- capabilities.include?(:prompt_caching)
63
- end
64
-
65
- def supports?(capability)
66
- capabilities.include?(capability.to_s.downcase.to_sym)
67
- end
68
-
69
- # ── Backward-compatible accessors ─────────────────────────────
70
- # These bridge the legacy Model::Info class API used by Models,
71
- # OpenAICompatible, and provider gems. They read from metadata
72
- # where the old fields were stored.
73
-
74
- def context_window
75
- context_length || metadata[:context_window]
76
- end
77
-
78
- def max_output_tokens
79
- metadata[:max_output_tokens]
80
- end
81
-
82
- def max_tokens
83
- max_output_tokens
84
- end
85
-
86
- def created_at
87
- metadata[:created_at]
88
- end
89
-
90
- def knowledge_cutoff
91
- metadata[:knowledge_cutoff]
92
- end
93
-
94
- def modalities
95
- Modalities.new(input: modalities_input.map(&:to_s), output: modalities_output.map(&:to_s))
96
- end
97
-
98
- def pricing
99
- Pricing.new(metadata[:pricing] || {})
100
- end
101
-
102
- def display_name
103
- name
104
- end
105
-
106
- def label
107
- "#{provider} - #{display_name}"
108
- end
109
-
110
- def input_price_per_million
111
- pricing.text_tokens.input
112
- end
113
-
114
- def output_price_per_million
115
- pricing.text_tokens.output
116
- end
117
-
118
- def supports_vision?
119
- vision? || modalities_input.include?(:image)
120
- end
121
-
122
- def supports_video?
123
- modalities_input.include?(:video)
124
- end
125
-
126
- def supports_functions?
127
- tools? || capabilities.include?(:function_calling)
128
- end
129
-
130
- # Legacy capability predicates (string-based)
131
- %w[function_calling structured_output batch reasoning citations streaming prompt_caching].each do |cap|
132
- define_method "#{cap}?" do
133
- supports?(cap)
134
- end
135
- end
136
-
137
- def type
138
- output = modalities_output.map(&:to_s)
139
- return 'embedding' if output.include?('embeddings') || embedding?
140
- return 'moderation' if output.include?('moderation')
141
- return 'image' if output.include?('image')
142
- return 'audio' if output.include?('audio')
143
- return 'video' if output.include?('video')
144
-
145
- 'chat'
146
- end
147
-
148
- # Factory for assumed-to-exist models without full metadata.
149
- def self.default(model_id, provider)
150
- new(
151
- id: model_id,
152
- name: model_id.tr('-', ' ').capitalize,
153
- provider: provider,
154
- capabilities: %w[function_calling streaming vision structured_output],
155
- modalities_input: %w[text image],
156
- modalities_output: %w[text],
157
- metadata: { warning: 'Assuming model exists, capabilities may not be accurate' }
158
- )
159
- end
160
-
161
- # Factory that accepts both legacy and new-style hashes and maps
162
- # them to the new struct fields. Handles round-tripping through to_h.
163
- def self.from_hash(data)
164
- data = data.transform_keys(&:to_sym) if data.is_a?(Hash)
165
-
166
- input_mods, output_mods = extract_modalities(data)
167
-
168
- new(
169
- id: data[:id],
170
- name: data[:name],
171
- provider: data[:provider],
172
- instance: data[:instance],
173
- family: data[:family],
174
- capabilities: data[:capabilities] || [],
175
- context_length: data[:context_length] || data[:context_window],
176
- parameter_count: data[:parameter_count],
177
- parameter_size: data[:parameter_size],
178
- quantization: data[:quantization],
179
- size_bytes: data[:size_bytes],
180
- modalities_input: input_mods,
181
- modalities_output: output_mods,
182
- metadata: build_metadata(data)
183
- )
184
- end
185
-
186
- private
187
-
188
- def normalize_capabilities(value)
189
- raw = Array(value).compact.filter_map do |cap|
190
- next unless cap.respond_to?(:to_s)
191
-
192
- sym = cap.to_s.downcase.strip.tr('-', '_').to_sym
193
- sym unless sym.to_s.empty?
194
- end
195
-
196
- (raw + Legion::Extensions::Llm::Capabilities.normalize(raw)).uniq.freeze
197
- end
198
-
199
- def normalize_symbols(value)
200
- Array(value).compact.each_with_object([]) do |item, normalized|
201
- symbol = item.to_s.downcase.strip.to_sym
202
- next if symbol.to_s.empty?
203
-
204
- normalized << symbol
205
- alias_symbol = CAPABILITY_ALIASES[symbol]
206
- normalized << alias_symbol if alias_symbol
207
- end.uniq
208
- end
209
-
210
- def to_int(value)
211
- return nil if value.nil?
212
-
213
- value.to_i
214
- end
215
-
216
- # Class-level helpers for from_hash normalization
217
- class << self
218
- private
219
-
220
- def extract_modalities(data)
221
- # New-style keys take priority (round-trip from to_h)
222
- return [Array(data[:modalities_input]), Array(data[:modalities_output])] if data.key?(:modalities_input) || data.key?(:modalities_output)
223
-
224
- # Legacy: modalities is a hash or Modalities object
225
- modalities_data = data[:modalities]
226
- input_mods = if modalities_data.respond_to?(:input)
227
- modalities_data.input
228
- elsif modalities_data.is_a?(Hash)
229
- Array(modalities_data[:input])
230
- else
231
- []
232
- end
233
- output_mods = if modalities_data.respond_to?(:output)
234
- modalities_data.output
235
- elsif modalities_data.is_a?(Hash)
236
- Array(modalities_data[:output])
237
- else
238
- []
239
- end
240
- [input_mods, output_mods]
241
- end
242
-
243
- def build_metadata(data)
244
- extra = {}
245
- extra[:created_at] = normalize_created_at(data[:created_at]) if data.key?(:created_at)
246
- if data.key?(:knowledge_cutoff)
247
- extra[:knowledge_cutoff] =
248
- normalize_knowledge_cutoff(data[:knowledge_cutoff])
249
- end
250
- extra[:max_output_tokens] = data[:max_output_tokens] if data.key?(:max_output_tokens)
251
- extra[:pricing] = normalize_pricing(data[:pricing]) if data.key?(:pricing)
252
-
253
- base = data[:metadata] || {}
254
- base.merge(extra).compact
255
- end
256
-
257
- def normalize_created_at(value)
258
- return nil if value.nil?
259
- return value if value.is_a?(Time)
260
-
261
- Utils.to_time(value)&.utc
262
- rescue StandardError
263
- nil
264
- end
265
-
266
- def normalize_knowledge_cutoff(value)
267
- return nil if value.nil?
268
- return value if value.is_a?(Date)
269
-
270
- Utils.to_date(value)
271
- rescue StandardError
272
- nil
273
- end
274
-
275
- def normalize_pricing(value)
276
- return nil if value.nil?
277
- return value.to_h if value.respond_to?(:to_h)
278
-
279
- value
280
- end
281
- end
282
- end
283
- end
284
- end
285
- end
286
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Model
7
- # Holds and manages input and output modalities for a language model
8
- class Modalities
9
- attr_reader :input, :output
10
-
11
- def initialize(data)
12
- @input = Array(data[:input]).map(&:to_s)
13
- @output = Array(data[:output]).map(&:to_s)
14
- end
15
-
16
- def to_h
17
- {
18
- input: input,
19
- output: output
20
- }
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Model
7
- # A collection that manages and provides access to different categories of pricing information
8
- class Pricing
9
- def initialize(data)
10
- @data = {}
11
-
12
- %i[text_tokens images audio_tokens embeddings].each do |category|
13
- @data[category] = PricingCategory.new(data[category]) if data[category] && !empty_pricing?(data[category])
14
- end
15
- end
16
-
17
- def method_missing(method, *args)
18
- if respond_to_missing?(method)
19
- @data[method.to_sym] || PricingCategory.new
20
- else
21
- super
22
- end
23
- end
24
-
25
- def respond_to_missing?(method, include_private = false)
26
- %i[text_tokens images audio_tokens embeddings].include?(method.to_sym) || super
27
- end
28
-
29
- def to_h
30
- @data.transform_values(&:to_h)
31
- end
32
-
33
- private
34
-
35
- def empty_pricing?(data)
36
- return true unless data
37
-
38
- %i[standard batch].each do |tier|
39
- next unless data[tier]
40
-
41
- data[tier].each_value do |value|
42
- return false if value && value != 0.0
43
- end
44
- end
45
-
46
- true
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Model
7
- # Represents pricing tiers for different usage categories (standard and batch)
8
- class PricingCategory
9
- attr_reader :standard, :batch
10
-
11
- def initialize(data = {})
12
- @standard = PricingTier.new(data[:standard] || {}) unless empty_tier?(data[:standard])
13
- @batch = PricingTier.new(data[:batch] || {}) unless empty_tier?(data[:batch])
14
- end
15
-
16
- def input
17
- standard&.input_per_million
18
- end
19
-
20
- def output
21
- standard&.output_per_million
22
- end
23
-
24
- def cached_input
25
- standard&.cached_input_per_million
26
- end
27
-
28
- def [](key)
29
- key == :batch ? batch : standard
30
- end
31
-
32
- def to_h
33
- result = {}
34
- result[:standard] = standard.to_h if standard
35
- result[:batch] = batch.to_h if batch
36
- result
37
- end
38
-
39
- private
40
-
41
- def empty_tier?(tier_data)
42
- return true unless tier_data
43
-
44
- tier_data.values.all? { |v| v.nil? || v == 0.0 }
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Model
7
- # A dynamic class for storing non-zero pricing values with flexible attribute access
8
- class PricingTier
9
- def initialize(data = {})
10
- @values = {}
11
-
12
- data.each do |key, value|
13
- @values[key.to_sym] = value if value && value != 0.0
14
- end
15
- end
16
-
17
- def method_missing(method, *args)
18
- if method.to_s.end_with?('=')
19
- key = method.to_s.chomp('=').to_sym
20
- @values[key] = args.first if args.first && args.first != 0.0
21
- elsif @values.key?(method)
22
- @values[method]
23
- end
24
- end
25
-
26
- def respond_to_missing?(method, include_private = false)
27
- method.to_s.end_with?('=') || @values.key?(method.to_sym) || super
28
- end
29
-
30
- def to_h
31
- @values
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- # Model-related classes for working with LLM models
7
- module Model
8
- end
9
- end
10
- end
11
- end