llm.rb 11.3.1 → 12.1.0

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +354 -2
  3. data/LICENSE +93 -17
  4. data/README.md +226 -616
  5. data/data/anthropic.json +322 -426
  6. data/data/bedrock.json +2634 -1144
  7. data/data/deepinfra.json +1513 -0
  8. data/data/deepseek.json +57 -28
  9. data/data/google.json +411 -771
  10. data/data/openai.json +1104 -771
  11. data/data/xai.json +141 -292
  12. data/data/zai.json +263 -141
  13. data/lib/llm/active_record/acts_as_agent.rb +3 -41
  14. data/lib/llm/active_record/acts_as_llm.rb +18 -0
  15. data/lib/llm/active_record.rb +3 -3
  16. data/lib/llm/agent.rb +25 -1
  17. data/lib/llm/context.rb +17 -5
  18. data/lib/llm/contract/completion.rb +2 -2
  19. data/lib/llm/json_adapter.rb +29 -3
  20. data/lib/llm/provider.rb +3 -3
  21. data/lib/llm/providers/deepinfra/audio.rb +66 -0
  22. data/lib/llm/providers/deepinfra/images.rb +90 -0
  23. data/lib/llm/providers/deepinfra/response_adapter.rb +36 -0
  24. data/lib/llm/providers/deepinfra.rb +100 -0
  25. data/lib/llm/providers/deepseek/images.rb +109 -0
  26. data/lib/llm/providers/deepseek/request_adapter.rb +32 -0
  27. data/lib/llm/providers/deepseek/response_adapter/image.rb +9 -0
  28. data/lib/llm/providers/deepseek/response_adapter.rb +29 -0
  29. data/lib/llm/providers/deepseek.rb +4 -2
  30. data/lib/llm/providers/google/request_adapter.rb +22 -5
  31. data/lib/llm/providers/google.rb +4 -4
  32. data/lib/llm/providers/llamacpp.rb +5 -5
  33. data/lib/llm/providers/openai/audio.rb +6 -2
  34. data/lib/llm/providers/openai/images.rb +9 -50
  35. data/lib/llm/providers/openai/request_adapter/respond.rb +38 -4
  36. data/lib/llm/providers/openai/response_adapter/audio.rb +5 -1
  37. data/lib/llm/providers/openai/response_adapter/completion.rb +1 -1
  38. data/lib/llm/providers/openai/response_adapter/image.rb +0 -4
  39. data/lib/llm/providers/openai/responses.rb +1 -0
  40. data/lib/llm/providers/openai/stream_parser.rb +5 -6
  41. data/lib/llm/providers/openai.rb +2 -2
  42. data/lib/llm/providers/xai/images.rb +49 -26
  43. data/lib/llm/providers/xai.rb +2 -2
  44. data/lib/llm/repl/input.rb +64 -0
  45. data/lib/llm/repl/status.rb +30 -0
  46. data/lib/llm/repl/stream.rb +46 -0
  47. data/lib/llm/repl/transcript.rb +61 -0
  48. data/lib/llm/repl/window.rb +107 -0
  49. data/lib/llm/repl.rb +78 -0
  50. data/lib/llm/response.rb +10 -0
  51. data/lib/llm/schema/leaf.rb +7 -1
  52. data/lib/llm/schema/renderer.rb +121 -0
  53. data/lib/llm/schema.rb +30 -0
  54. data/lib/llm/sequel/agent.rb +2 -43
  55. data/lib/llm/sequel/plugin.rb +25 -7
  56. data/lib/llm/tools/chdir.rb +23 -0
  57. data/lib/llm/tools/git.rb +41 -0
  58. data/lib/llm/tools/mkdir.rb +32 -0
  59. data/lib/llm/tools/pwd.rb +20 -0
  60. data/lib/llm/tools/read_file.rb +40 -0
  61. data/lib/llm/tools/rg.rb +46 -0
  62. data/lib/llm/tools/shell.rb +48 -0
  63. data/lib/llm/tools/swap_text.rb +25 -0
  64. data/lib/llm/tools/write_file.rb +24 -0
  65. data/lib/llm/tools.rb +5 -0
  66. data/lib/llm/tracer/telemetry.rb +4 -6
  67. data/lib/llm/tracer.rb +9 -21
  68. data/lib/llm/transport/execution.rb +16 -1
  69. data/lib/llm/transport/net_http_adapter.rb +1 -1
  70. data/lib/llm/uridata.rb +16 -0
  71. data/lib/llm/version.rb +1 -1
  72. data/lib/llm.rb +9 -0
  73. data/llm.gemspec +5 -18
  74. data/resources/deepdive.md +829 -263
  75. metadata +31 -18
  76. data/lib/llm/tracer/langsmith.rb +0 -144
@@ -17,46 +17,6 @@ module LLM::ActiveRecord
17
17
  # class and forwarded to an internal agent subclass.
18
18
  module ActsAsAgent
19
19
  module ClassMethods
20
- def model(model = nil, &block)
21
- return agent.model if model.nil? && !block
22
- agent.model(model, &block)
23
- end
24
-
25
- def tools(*tools, &block)
26
- return agent.tools if tools.empty? && !block
27
- agent.tools(*tools, &block)
28
- end
29
-
30
- def skills(*skills, &block)
31
- return agent.skills if skills.empty? && !block
32
- agent.skills(*skills, &block)
33
- end
34
-
35
- def schema(schema = nil, &block)
36
- return agent.schema if schema.nil? && !block
37
- agent.schema(schema, &block)
38
- end
39
-
40
- def instructions(instructions = nil)
41
- return agent.instructions if instructions.nil?
42
- agent.instructions(instructions)
43
- end
44
-
45
- def concurrency(concurrency = nil)
46
- return agent.concurrency if concurrency.nil?
47
- agent.concurrency(concurrency)
48
- end
49
-
50
- def confirm(*tool_names, &block)
51
- return agent.confirm if tool_names.empty? && !block
52
- agent.confirm(*tool_names, &block)
53
- end
54
-
55
- def tracer(tracer = nil, &block)
56
- return agent.tracer if tracer.nil? && !block
57
- agent.tracer(tracer, &block)
58
- end
59
-
60
20
  def agent
61
21
  @agent ||= Class.new(LLM::Agent)
62
22
  end
@@ -92,13 +52,15 @@ module LLM::ActiveRecord
92
52
  # Evaluated in the model class after the wrapper is installed, so agent
93
53
  # DSL methods such as `model`, `tools`, `schema`, `instructions`, and
94
54
  # `concurrency` can be configured inline.
55
+ # @yieldparam [LLM::Agent] agent
56
+ # Yields an instance of {LLM::Agent LLM::Agent}.
95
57
  # @return [void]
96
58
  def acts_as_agent(options = EMPTY_HASH, &block)
97
59
  options = DEFAULTS.merge(options)
98
60
  class_attribute :llm_plugin_options, instance_accessor: false, default: DEFAULTS unless respond_to?(:llm_plugin_options)
99
61
  self.llm_plugin_options = options.freeze
100
62
  extend Hooks
101
- class_exec(&block) if block
63
+ block_given? ? class_exec(agent, &block) : nil
102
64
  end
103
65
 
104
66
  module InstanceMethods
@@ -204,6 +204,24 @@ module LLM::ActiveRecord
204
204
 
205
205
  private
206
206
 
207
+ ##
208
+ # @return [LLM::Provider]
209
+ def set_provider
210
+ raise NotImplementedError, "implement the set_provider callback"
211
+ end
212
+
213
+ ##
214
+ # @return [Hash]
215
+ def set_context
216
+ EMPTY_HASH.dup
217
+ end
218
+
219
+ ##
220
+ # @return [LLM::Tracer]
221
+ def set_tracer
222
+ nil
223
+ end
224
+
207
225
  ##
208
226
  # @return [LLM::Context]
209
227
  def ctx
@@ -5,9 +5,9 @@ module LLM::ActiveRecord
5
5
  DEFAULTS = {
6
6
  data_column: :data,
7
7
  format: :string,
8
- tracer: nil,
9
- provider: nil,
10
- context: EMPTY_HASH
8
+ provider: :set_provider,
9
+ context: :set_context,
10
+ tracer: :set_tracer
11
11
  }.freeze
12
12
 
13
13
  ##
data/lib/llm/agent.rb CHANGED
@@ -61,7 +61,11 @@ module LLM
61
61
  # Returns the current tools when no argument is provided
62
62
  def self.tools(*tools, &block)
63
63
  return @tools || [] if tools.empty? && !block
64
- @tools = block || tools.flatten
64
+ if tools.size == 1 and tools.grep(Symbol).any?
65
+ @tools = tools.first
66
+ else
67
+ @tools = block || tools.flatten
68
+ end
65
69
  end
66
70
 
67
71
  ##
@@ -334,6 +338,15 @@ module LLM
334
338
  @tracer || @ctx.tracer
335
339
  end
336
340
 
341
+ ##
342
+ # @param [LLM::Tracer, nil] other
343
+ # A tracer, or nil.
344
+ # @return [void]
345
+ def tracer=(other)
346
+ @ctx.tracer = other
347
+ @tracer = other
348
+ end
349
+
337
350
  ##
338
351
  # @return [LLM::Stream, #<<, nil]
339
352
  # Returns a stream object, or nil
@@ -375,6 +388,17 @@ module LLM
375
388
  @ctx.context_window
376
389
  end
377
390
 
391
+ ##
392
+ # Start a minimalist repl that can interact
393
+ # with the agent and its current state. This
394
+ # method requires the 'curses' gem to be installed
395
+ # and available to require.
396
+ # @return [void]
397
+ def repl
398
+ require_relative "repl" unless defined?(::LLM::Repl)
399
+ LLM::Repl.new(self).start
400
+ end
401
+
378
402
  ##
379
403
  # @see LLM::Context#params
380
404
  # @return [Hash]
data/lib/llm/context.rb CHANGED
@@ -75,19 +75,22 @@ module LLM
75
75
  # The parameters to maintain throughout the conversation.
76
76
  # Any parameter the provider supports can be included and
77
77
  # not only those listed here.
78
- # @option params [Symbol] :mode Defaults to :completions
78
+ # @option params [Symbol] :mode
79
+ # Defaults to `:responses` for OpenAI, otherwise it defaults
80
+ # to `:completions`.
79
81
  # @option params [String] :model Defaults to the provider's default model
80
82
  # @option params [Array<LLM::Function>, nil] :tools Defaults to nil
81
83
  # @option params [Array<String>, nil] :skills Defaults to nil
82
84
  def initialize(llm, params = {})
83
85
  @llm = llm
84
- @mode = params.delete(:mode) || :completions
86
+ @mode = params.delete(:mode) || (llm.name == :openai ? :responses : :completions)
85
87
  @compactor = params.delete(:compactor)
86
88
  @guard = params.delete(:guard)
87
89
  @transformer = params.delete(:transformer)
88
90
  tools = [*params.delete(:tools), *load_skills(params.delete(:skills))]
89
91
  @params = {model: llm.default_model, schema: nil}.compact.merge!(params)
90
92
  @params[:tools] = tools unless tools.empty?
93
+ @params[:store] ||= false if @mode == :responses
91
94
  @messages = LLM::Buffer.new(llm)
92
95
  end
93
96
 
@@ -199,7 +202,7 @@ module LLM
199
202
  role = params[:role] || @llm.user_role
200
203
  role = @llm.tool_role if params[:role].nil? && [*prompt].grep(LLM::Function::Return).any?
201
204
  @messages.concat LLM::Prompt === prompt ? prompt.to_a : [LLM::Message.new(role, prompt)]
202
- @messages.concat [res.choices[-1]]
205
+ @messages.concat [res.choices[-1]].compact
203
206
  res
204
207
  end
205
208
 
@@ -418,6 +421,14 @@ module LLM
418
421
  @llm.tracer
419
422
  end
420
423
 
424
+ ##
425
+ # @param [LLM::Tracer, nil] other
426
+ # A tracer, or nil.
427
+ # @return [void]
428
+ def tracer=(other)
429
+ @llm.tracer = nil
430
+ end
431
+
421
432
  ##
422
433
  # @return [LLM::Stream, #<<, nil]
423
434
  # Returns a stream object, or nil
@@ -548,7 +559,8 @@ module LLM
548
559
  prompt, params = transform(prompt, params)
549
560
  bind!(params[:stream], params[:model], params[:tools])
550
561
  res_id = params[:store] == false ? nil : @messages.find(&:assistant?)&.response&.response_id
551
- params = params.merge(previous_response_id: res_id, input: @messages.to_a).compact
562
+ input = res_id ? [] : @messages.to_a
563
+ params = params.merge(previous_response_id: res_id, input:).compact
552
564
  [prompt, params, @llm.responses.create(prompt, params)]
553
565
  end
554
566
 
@@ -597,7 +609,7 @@ module LLM
597
609
  [*message.extra.tool_calls].each do |tool|
598
610
  next if returns.any? { _1.id == tool[:id] }
599
611
  attrs = {cancelled: true, reason: "function call cancelled"}
600
- cancelled << LLM::Function::Return.new(tool.id, tool.name, attrs)
612
+ cancelled << LLM::Function::Return.new(tool[:id], tool[:name], attrs)
601
613
  end
602
614
  messages << LLM::Message.new(@llm.tool_role, cancelled) unless cancelled.empty?
603
615
  end
@@ -98,10 +98,10 @@ module LLM::Contract
98
98
  end
99
99
 
100
100
  ##
101
- # @return [Hash]
101
+ # @return [LLM::Object]
102
102
  # Returns the LLM response after parsing it as JSON
103
103
  def content!
104
- LLM.json.load(content)
104
+ LLM::Object.from LLM.json.load(content)
105
105
  end
106
106
 
107
107
  ##
@@ -38,11 +38,11 @@ module LLM
38
38
  def self.dump(obj, state = nil, **options)
39
39
  require "json" unless defined?(::JSON)
40
40
  if ::JSON::State === state
41
- ::JSON.generate(obj, state)
41
+ ::JSON.generate(normalize(obj), state)
42
42
  elsif state
43
- ::JSON.dump(obj, state, **options)
43
+ ::JSON.dump(normalize(obj), state, **options)
44
44
  else
45
- ::JSON.dump(obj, **options)
45
+ ::JSON.dump(normalize(obj), **options)
46
46
  end
47
47
  end
48
48
 
@@ -59,6 +59,32 @@ module LLM
59
59
  require "json" unless defined?(::JSON)
60
60
  [::JSON::ParserError]
61
61
  end
62
+
63
+ ##
64
+ # JSON 3.0 compat
65
+ # Walks `obj` and encodes every string that is
66
+ # found into a UTF-8 compatible string.
67
+ def self.normalize(obj)
68
+ case obj
69
+ when String then normalize_string(obj)
70
+ when Array then obj.map { normalize(_1) }
71
+ when Hash then obj.map { [_1, normalize(_2)] }.to_h
72
+ when LLM::Object then obj.map { [_1, normalize(_2)] }.to_h
73
+ else obj
74
+ end
75
+ end
76
+ private_class_method :normalize
77
+
78
+ ##
79
+ # JSON 3.0 compat
80
+ # Normalizes a string as a UTF-8 encoded string
81
+ # that's compatible with the JSON spec.
82
+ def self.normalize_string(str)
83
+ return str if str.encoding == Encoding::UTF_8
84
+ str = (+str).force_encoding("UTF-8")
85
+ str.valid_encoding? ? str : str.scrub
86
+ end
87
+ private_class_method :normalize_string
62
88
  end
63
89
 
64
90
  ##
data/lib/llm/provider.rb CHANGED
@@ -286,7 +286,7 @@ class LLM::Provider
286
286
  # A tracer
287
287
  # @return [void]
288
288
  def tracer=(tracer)
289
- @tracer = tracer
289
+ @tracer = tracer || LLM::Tracer::Null.new(self)
290
290
  end
291
291
 
292
292
  ##
@@ -348,8 +348,8 @@ class LLM::Provider
348
348
 
349
349
  private
350
350
 
351
- def path(suffix)
352
- return suffix if @base_path.empty?
351
+ def path(suffix, base_path: true)
352
+ return suffix if !base_path || @base_path.empty?
353
353
  "#{@base_path}#{suffix}"
354
354
  end
355
355
 
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::DeepInfra
4
+ class Audio
5
+ ##
6
+ # @param [LLM::Provider] provider
7
+ # A provider
8
+ # @return [LLM::DeepInfra::Audio]
9
+ def initialize(provider)
10
+ @provider = provider
11
+ end
12
+
13
+ ##
14
+ # @param [String] input
15
+ # A string of text
16
+ # @param [String] model
17
+ # A text-to-speech model.
18
+ # Defaults to hexgrad/Kokoro-82M.
19
+ # @param [Hash] params
20
+ # Any other model-specific parameters
21
+ # @return [LLM::Response]
22
+ def create_speech(input:, model: "hexgrad/Kokoro-82M", **params)
23
+ path = path("/v1/inference/#{model}", base_path: false)
24
+ req = LLM::Transport::Request.post(path, headers)
25
+ req.body = JSON.dump(params.merge(text: input))
26
+ res, span, tracer = execute(request: req, operation: "request")
27
+ res = ResponseAdapter.adapt LLM::Response.new(res), type: :audio
28
+ tracer.on_request_finish(operation: "request", model:, res:, span:)
29
+ res
30
+ end
31
+
32
+ ##
33
+ # @see https://deepinfra.com/models/automatic-speech-recognition speech-to-text models
34
+ # @see https://docs.deepinfra.com/apis/speech API docs
35
+ # @param [String, LLM::File] file
36
+ # An audio file
37
+ # @param [String] model
38
+ # A speech-to-text model.
39
+ # @param [Hash] params
40
+ # Any other model-specific parameters
41
+ # @return [LLM::Response]
42
+ def create_transcription(file:, model: "openai/whisper-large-v3", **params)
43
+ path = path("/v1/inference/#{model}", base_path: false)
44
+ multi = LLM::Multipart.new(params.merge!(audio: LLM.File(file)))
45
+ req = LLM::Transport::Request.post(path, headers)
46
+ req["content-type"] = multi.content_type
47
+ transport.set_body_stream(req, multi.body)
48
+ res, span, tracer = execute(request: req, operation: "request")
49
+ res = LLM::Response.new(res)
50
+ tracer.on_request_finish(operation: "request", model:, res:, span:)
51
+ res
52
+ end
53
+
54
+ ##
55
+ # @raise [NotImplementedError]
56
+ def create_translation(...)
57
+ raise NotImplementedError
58
+ end
59
+
60
+ private
61
+
62
+ [:path, :headers, :execute, :transport].each do |m|
63
+ define_method(m) { |*args, **kwargs, &b| @provider.send(m, *args, **kwargs, &b) }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::DeepInfra
4
+ ##
5
+ # The {LLM::DeepInfra::Images LLM::DeepInfra::Images} class provides an
6
+ # interface for [DeepInfra's images API](https://docs.deepinfra.com/apis/image-generation).
7
+ # DeepInfra returns base64-encoded image data.
8
+ #
9
+ # @example
10
+ # #!/usr/bin/env ruby
11
+ # require "llm"
12
+ #
13
+ # llm = LLM.deepinfra(key: ENV["KEY"])
14
+ # res = llm.images.create prompt: "A dog on a rocket to the moon"
15
+ # IO.copy_stream res.images[0], "rocket.png"
16
+ class Images
17
+ ##
18
+ # @param [LLM::Provider] provider
19
+ # @return [LLM::DeepInfra::Images]
20
+ def initialize(provider)
21
+ @provider = provider
22
+ end
23
+
24
+ ##
25
+ # @see https://deepinfra.com/models/text-to-image DeepInfra Image Models
26
+ # @param [String] prompt
27
+ # A prompt
28
+ # @param [String] model
29
+ # A text-to-image model.
30
+ # Defaults to the black-forest-labs/FLUX-2-klein-4b.
31
+ # @param [String] size
32
+ # Image size (eg 1024x1024)
33
+ # @param [Integer] n
34
+ # The number of images to default
35
+ # @param [String] response_format
36
+ # No other options other than the default are supported.
37
+ # @param [String] quality
38
+ # Exists for compat. Noop.
39
+ # @param [String] style
40
+ # Exists for compat. Noop.
41
+ # @return [LLM::Response<LLM::OpenAI::ResponseAdapter::Image>]
42
+ # Returns a response
43
+ def create(prompt:, model: "black-forest-labs/FLUX-2-klein-4b", size: "1024x1024", n: 1, response_format: "b64_json", quality: nil, style: nil)
44
+ req = LLM::Transport::Request.post(path("/images/generations"), headers)
45
+ params = {prompt:, model:, size:, n:, response_format:, quality:, style:}.compact
46
+ req.body = LLM.json.dump(params)
47
+ res, span, tracer = execute(request: req, operation: "request")
48
+ res = LLM::OpenAI::ResponseAdapter.adapt(res, type: :image)
49
+ tracer.on_request_finish(operation: "request", model:, res:, span:)
50
+ res
51
+ end
52
+
53
+ ##
54
+ # @see https://deepinfra.com/models/text-to-image DeepInfra Image Models
55
+ # @param [String, LLM::File, File] image
56
+ # The image to edit.
57
+ # @param [String] prompt
58
+ # A text description of the desired edits.
59
+ # @param [String] model
60
+ # The model to use.
61
+ # @param [String] size
62
+ # Image size (eg 1024x1024)
63
+ # @param [Integer] n
64
+ # The number of images to generate.
65
+ # @param [String] response_format
66
+ # DeepInfra currently supports b64_json.
67
+ # @param [Hash] params
68
+ # Other parameters supported by DeepInfra, such as :mask or :user.
69
+ # @return [LLM::Response<LLM::OpenAI::ResponseAdapter::Image>]
70
+ # Returns a response
71
+ def edit(image:, prompt:, model: "black-forest-labs/FLUX-2-klein-4b", size: "1024x1024", n: 1, response_format: "b64_json", **params)
72
+ params = params.merge!(image: LLM.File(image), prompt:, model:, size:, n:, response_format:)
73
+ params[:mask] = LLM.File(params[:mask]) if params[:mask]
74
+ multi = LLM::Multipart.new(params)
75
+ req = LLM::Transport::Request.post(path("/images/edits"), headers)
76
+ req["content-type"] = multi.content_type
77
+ transport.set_body_stream(req, multi.body)
78
+ res, span, tracer = execute(request: req, operation: "request")
79
+ res = LLM::OpenAI::ResponseAdapter.adapt(res, type: :image)
80
+ tracer.on_request_finish(operation: "request", model:, res:, span:)
81
+ res
82
+ end
83
+
84
+ private
85
+
86
+ [:path, :headers, :execute, :transport].each do |m|
87
+ define_method(m) { |*args, **kwargs, &b| @provider.send(m, *args, **kwargs, &b) }
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class LLM::DeepInfra
4
+ ##
5
+ # @private
6
+ module ResponseAdapter
7
+ module Audio
8
+ ##
9
+ # @return [LLM::URIData]
10
+ def audio
11
+ @audio ||= LLM::URIData.parse(super)
12
+ end
13
+ end
14
+
15
+ module_function
16
+
17
+ ##
18
+ # @param [LLM::Response, Net::HTTPResponse] res
19
+ # @param [Symbol] type
20
+ # @return [LLM::Response]
21
+ def adapt(res, type:)
22
+ response = (LLM::Response === res) ? res : LLM::Response.new(res)
23
+ adapter = select(type)
24
+ response.extend(adapter)
25
+ end
26
+
27
+ ##
28
+ # @api private
29
+ def select(type)
30
+ case type
31
+ when :audio then Audio
32
+ else LLM::OpenAI::ResponseAdapter.select(type)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "openai" unless defined?(LLM::OpenAI)
4
+
5
+ module LLM
6
+ ##
7
+ # The DeepInfra class implements a provider for
8
+ # [DeepInfra](https://deepinfra.com)
9
+ # through its OpenAI-compatible API.
10
+ #
11
+ # @example
12
+ # #!/usr/bin/env ruby
13
+ # require "llm"
14
+ #
15
+ # llm = LLM.deepinfra(key: ENV["KEY"])
16
+ # ctx = LLM::Context.new(llm)
17
+ # ctx.talk "Hello"
18
+ # ctx.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
19
+ class DeepInfra < OpenAI
20
+ HOST = "api.deepinfra.com"
21
+ BASE_PATH = "/v1/openai"
22
+ require_relative "deepinfra/images"
23
+ require_relative "deepinfra/audio"
24
+ require_relative "deepinfra/response_adapter"
25
+
26
+ ##
27
+ # @param key (see LLM::Provider#initialize)
28
+ # @param host (see LLM::Provider#initialize)
29
+ # @param base_path (see LLM::Provider#initialize)
30
+ # @return [LLM::DeepInfra]
31
+ def initialize(host: HOST, base_path: BASE_PATH, **)
32
+ super
33
+ end
34
+
35
+ ##
36
+ # @return [Symbol]
37
+ # Returns the provider's name
38
+ def name
39
+ :deepinfra
40
+ end
41
+
42
+ ##
43
+ # Provides an interface to DeepInfra's OpenAI-compatible image API.
44
+ # @see https://deepinfra.com/models/text-to-image DeepInfra image models
45
+ # @return [LLM::DeepInfra::Images]
46
+ def images
47
+ LLM::DeepInfra::Images.new(self)
48
+ end
49
+
50
+ ##
51
+ # Provides an embedding.
52
+ # @see https://deepinfra.com/BAAI/bge-m3 BAAI/bge-m3
53
+ # @param input (see LLM::Provider#embed)
54
+ # @param model (see LLM::Provider#embed)
55
+ # @param params (see LLM::Provider#embed)
56
+ # @raise (see LLM::Provider#request)
57
+ # @return (see LLM::Provider#embed)
58
+ def embed(input, model: "BAAI/bge-m3", **params)
59
+ super
60
+ end
61
+
62
+ ##
63
+ # @raise [NotImplementedError]
64
+ def responses
65
+ raise NotImplementedError
66
+ end
67
+
68
+ ##
69
+ # @return [LLM::DeepInfra::Audio]
70
+ def audio
71
+ LLM::DeepInfra::Audio.new(self)
72
+ end
73
+
74
+ ##
75
+ # @raise [NotImplementedError]
76
+ def files
77
+ raise NotImplementedError
78
+ end
79
+
80
+ ##
81
+ # @raise [NotImplementedError]
82
+ def moderations
83
+ raise NotImplementedError
84
+ end
85
+
86
+ ##
87
+ # @raise [NotImplementedError]
88
+ def vector_stores
89
+ raise NotImplementedError
90
+ end
91
+
92
+ ##
93
+ # Returns the default model for chat completions
94
+ # @see https://deepinfra.com/models/zai-org/GLM-5.2 zai-org/GLM-5.2
95
+ # @return [String]
96
+ def default_model
97
+ "zai-org/GLM-5.2"
98
+ end
99
+ end
100
+ end