Class: Legion::Extensions::Llm::Provider
- Inherits:
-
Object
- Object
- Legion::Extensions::Llm::Provider
- Includes:
- Streaming
- Defined in:
- lib/legion/extensions/llm/provider.rb,
lib/legion/extensions/llm/provider/open_ai_compatible.rb
Overview
Base class for LLM providers.
Defined Under Namespace
Modules: OpenAICompatible
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
Class Method Summary collapse
- .assume_models_exist? ⇒ Boolean
- .capabilities ⇒ Object
- .configuration_options ⇒ Object
- .configuration_requirements ⇒ Object
- .configured?(config) ⇒ Boolean
- .configured_providers(config) ⇒ Object
- .configured_remote_providers(config) ⇒ Object
- .for(model) ⇒ Object
- .local? ⇒ Boolean
- .local_providers ⇒ Object
- .name ⇒ Object
- .providers ⇒ Object
- .register(name, provider_class) ⇒ Object
- .remote? ⇒ Boolean
- .remote_providers ⇒ Object
- .resolve(name) ⇒ Object
-
.resolve_model_id(model_id, config: nil) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument.
- .slug ⇒ Object
Instance Method Summary collapse
- #api_base ⇒ Object
- #assume_models_exist? ⇒ Boolean
- #capabilities ⇒ Object
-
#complete(messages, tools:, temperature:, model:, params: {}, headers: {}, schema: nil, thinking: nil, tool_prefs: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists.
- #configuration_requirements ⇒ Object
- #configured? ⇒ Boolean
- #embed(text, model:, dimensions:) ⇒ Object
- #endpoint_manifest ⇒ Object
- #format_messages(messages) ⇒ Object
- #format_tool_calls(_tool_calls) ⇒ Object
- #headers ⇒ Object
-
#initialize(config) ⇒ Provider
constructor
A new instance of Provider.
-
#list_models ⇒ Object
rubocop:enable Metrics/ParameterLists.
- #local? ⇒ Boolean
- #moderate(input, model:) ⇒ Object
- #name ⇒ Object
-
#paint(prompt, model:, size:, with: nil, mask: nil, params: {}) ⇒ Object
rubocop:disable Metrics/ParameterLists.
- #parse_error(response) ⇒ Object
- #parse_tool_calls(_tool_calls) ⇒ Object
- #readiness(live: false) ⇒ Object
- #remote? ⇒ Boolean
- #slug ⇒ Object
- #transcribe(audio_file, model:, language:) ⇒ Object
Methods included from Streaming
build_on_data_handler, build_stream_error_response, error_chunk?, faraday_1?, handle_data, handle_error_chunk, handle_error_event, handle_failed_response, handle_json_error_chunk, handle_parsed_error, handle_sse, handle_stream, json_error_payload?, parse_error_from_json, parse_streaming_error, process_stream_chunk, stream_response
Constructor Details
#initialize(config) ⇒ Provider
Returns a new instance of Provider.
12 13 14 15 16 |
# File 'lib/legion/extensions/llm/provider.rb', line 12 def initialize(config) @config = config ensure_configured! @connection = Connection.new(self, @config) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
10 11 12 |
# File 'lib/legion/extensions/llm/provider.rb', line 10 def config @config end |
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
10 11 12 |
# File 'lib/legion/extensions/llm/provider.rb', line 10 def connection @connection end |
Class Method Details
.assume_models_exist? ⇒ Boolean
214 215 216 |
# File 'lib/legion/extensions/llm/provider.rb', line 214 def assume_models_exist? false end |
.capabilities ⇒ Object
194 195 196 |
# File 'lib/legion/extensions/llm/provider.rb', line 194 def capabilities nil end |
.configuration_options ⇒ Object
202 203 204 |
# File 'lib/legion/extensions/llm/provider.rb', line 202 def [] end |
.configuration_requirements ⇒ Object
198 199 200 |
# File 'lib/legion/extensions/llm/provider.rb', line 198 def configuration_requirements [] end |
.configured?(config) ⇒ Boolean
222 223 224 |
# File 'lib/legion/extensions/llm/provider.rb', line 222 def configured?(config) configuration_requirements.all? { |req| config.send(req) } end |
.configured_providers(config) ⇒ Object
254 255 256 257 258 |
# File 'lib/legion/extensions/llm/provider.rb', line 254 def configured_providers(config) providers.select do |_slug, provider_class| provider_class.configured?(config) end.values end |
.configured_remote_providers(config) ⇒ Object
260 261 262 263 264 |
# File 'lib/legion/extensions/llm/provider.rb', line 260 def configured_remote_providers(config) providers.select do |_slug, provider_class| provider_class.remote? && provider_class.configured?(config) end.values end |
.for(model) ⇒ Object
237 238 239 240 |
# File 'lib/legion/extensions/llm/provider.rb', line 237 def for(model) model_info = Models.find(model) resolve model_info.provider end |
.local? ⇒ Boolean
206 207 208 |
# File 'lib/legion/extensions/llm/provider.rb', line 206 def local? false end |
.local_providers ⇒ Object
246 247 248 |
# File 'lib/legion/extensions/llm/provider.rb', line 246 def local_providers providers.select { |_slug, provider_class| provider_class.local? } end |
.name ⇒ Object
186 187 188 |
# File 'lib/legion/extensions/llm/provider.rb', line 186 def name to_s.split('::').last end |
.providers ⇒ Object
242 243 244 |
# File 'lib/legion/extensions/llm/provider.rb', line 242 def providers @providers ||= {} end |
.register(name, provider_class) ⇒ Object
226 227 228 229 |
# File 'lib/legion/extensions/llm/provider.rb', line 226 def register(name, provider_class) providers[name.to_sym] = provider_class Legion::Extensions::Llm::Configuration.(provider_class.) end |
.remote? ⇒ Boolean
210 211 212 |
# File 'lib/legion/extensions/llm/provider.rb', line 210 def remote? !local? end |
.remote_providers ⇒ Object
250 251 252 |
# File 'lib/legion/extensions/llm/provider.rb', line 250 def remote_providers providers.select { |_slug, provider_class| provider_class.remote? } end |
.resolve(name) ⇒ Object
231 232 233 234 235 |
# File 'lib/legion/extensions/llm/provider.rb', line 231 def resolve(name) return nil if name.nil? providers[name.to_sym] end |
.resolve_model_id(model_id, config: nil) ⇒ Object
rubocop:disable Lint/UnusedMethodArgument
218 219 220 |
# File 'lib/legion/extensions/llm/provider.rb', line 218 def resolve_model_id(model_id, config: nil) # rubocop:disable Lint/UnusedMethodArgument model_id end |
.slug ⇒ Object
190 191 192 |
# File 'lib/legion/extensions/llm/provider.rb', line 190 def slug name.downcase end |
Instance Method Details
#api_base ⇒ Object
18 19 20 |
# File 'lib/legion/extensions/llm/provider.rb', line 18 def api_base raise NotImplementedError end |
#assume_models_exist? ⇒ Boolean
112 113 114 |
# File 'lib/legion/extensions/llm/provider.rb', line 112 def assume_models_exist? self.class.assume_models_exist? end |
#capabilities ⇒ Object
34 35 36 |
# File 'lib/legion/extensions/llm/provider.rb', line 34 def capabilities self.class.capabilities end |
#complete(messages, tools:, temperature:, model:, params: {}, headers: {}, schema: nil, thinking: nil, tool_prefs: nil) ⇒ Object
rubocop:disable Metrics/ParameterLists
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/legion/extensions/llm/provider.rb', line 43 def complete(, tools:, temperature:, model:, params: {}, headers: {}, schema: nil, thinking: nil, tool_prefs: nil, &) normalized_temperature = maybe_normalize_temperature(temperature, model) payload = Utils.deep_merge( render_payload( , tools: tools, tool_prefs: tool_prefs, temperature: normalized_temperature, model: model, stream: block_given?, schema: schema, thinking: thinking ), params ) if block_given? stream_response @connection, payload, headers, & else sync_response @connection, payload, headers end end |
#configuration_requirements ⇒ Object
38 39 40 |
# File 'lib/legion/extensions/llm/provider.rb', line 38 def configuration_requirements self.class.configuration_requirements end |
#configured? ⇒ Boolean
100 101 102 |
# File 'lib/legion/extensions/llm/provider.rb', line 100 def configured? configuration_requirements.all? { |req| @config.send(req) } end |
#embed(text, model:, dimensions:) ⇒ Object
74 75 76 77 78 |
# File 'lib/legion/extensions/llm/provider.rb', line 74 def (text, model:, dimensions:) payload = (text, model:, dimensions:) response = @connection.post((model:), payload) (response, model:, text:) end |
#endpoint_manifest ⇒ Object
137 138 139 140 141 142 143 144 145 146 |
# File 'lib/legion/extensions/llm/provider.rb', line 137 def endpoint_manifest endpoint_methods.each_with_object({}) do |(key, method_name), result| next unless respond_to?(method_name) value = public_send(method_name) result[key] = value unless value.nil? rescue ArgumentError, NotImplementedError next end end |
#format_messages(messages) ⇒ Object
168 169 170 171 172 173 174 175 |
# File 'lib/legion/extensions/llm/provider.rb', line 168 def () .map do |msg| { role: msg.role.to_s, content: msg.content } end end |
#format_tool_calls(_tool_calls) ⇒ Object
177 178 179 |
# File 'lib/legion/extensions/llm/provider.rb', line 177 def format_tool_calls(_tool_calls) nil end |
#headers ⇒ Object
22 23 24 |
# File 'lib/legion/extensions/llm/provider.rb', line 22 def headers {} end |
#list_models ⇒ Object
rubocop:enable Metrics/ParameterLists
69 70 71 72 |
# File 'lib/legion/extensions/llm/provider.rb', line 69 def list_models response = @connection.get models_url parse_list_models_response response, slug, capabilities end |
#local? ⇒ Boolean
104 105 106 |
# File 'lib/legion/extensions/llm/provider.rb', line 104 def local? self.class.local? end |
#moderate(input, model:) ⇒ Object
80 81 82 83 84 |
# File 'lib/legion/extensions/llm/provider.rb', line 80 def moderate(input, model:) payload = render_moderation_payload(input, model:) response = @connection.post moderation_url, payload parse_moderation_response(response, model:) end |
#name ⇒ Object
30 31 32 |
# File 'lib/legion/extensions/llm/provider.rb', line 30 def name self.class.name end |
#paint(prompt, model:, size:, with: nil, mask: nil, params: {}) ⇒ Object
rubocop:disable Metrics/ParameterLists
86 87 88 89 90 91 |
# File 'lib/legion/extensions/llm/provider.rb', line 86 def paint(prompt, model:, size:, with: nil, mask: nil, params: {}) # rubocop:disable Metrics/ParameterLists validate_paint_inputs!(with:, mask:) payload = render_image_payload(prompt, model:, size:, with:, mask:, params:) response = @connection.post images_url(with:, mask:), payload parse_image_response(response, model:) end |
#parse_error(response) ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/legion/extensions/llm/provider.rb', line 148 def parse_error(response) return if response.body.empty? body = try_parse_json(response.body) case body when Hash error = body['error'] return error if error.is_a?(String) body.dig('error', 'message') when Array body.map do |part| error = part['error'] error.is_a?(String) ? error : part.dig('error', 'message') end.join('. ') else body end end |
#parse_tool_calls(_tool_calls) ⇒ Object
181 182 183 |
# File 'lib/legion/extensions/llm/provider.rb', line 181 def parse_tool_calls(_tool_calls) nil end |
#readiness(live: false) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/legion/extensions/llm/provider.rb', line 116 def readiness(live: false) = { provider: slug.to_sym, name: name, configured: configured?, ready: configured?, local: local?, remote: remote?, api_base: api_base, endpoints: endpoint_manifest, live: live } return .merge(health: { checked: false }) unless live && [:endpoints][:health] response = @connection.get([:endpoints][:health]) .merge(ready: configured? && health_ready?(response.body), health: response.body) rescue StandardError => e .merge(ready: false, health: { error: e.class.name, message: e. }) end |
#remote? ⇒ Boolean
108 109 110 |
# File 'lib/legion/extensions/llm/provider.rb', line 108 def remote? self.class.remote? end |
#slug ⇒ Object
26 27 28 |
# File 'lib/legion/extensions/llm/provider.rb', line 26 def slug self.class.slug end |
#transcribe(audio_file, model:, language:) ⇒ Object
93 94 95 96 97 98 |
# File 'lib/legion/extensions/llm/provider.rb', line 93 def transcribe(audio_file, model:, language:, **) file_part = build_audio_file_part(audio_file) payload = render_transcription_payload(file_part, model:, language:, **) response = @connection.post transcription_url, payload parse_transcription_response(response, model:) end |