llm.rb 0.11.0 → 0.13.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.
- checksums.yaml +4 -4
- data/LICENSE +0 -0
- data/README.md +56 -19
- data/lib/llm/bot/builder.rb +0 -0
- data/lib/llm/bot/conversable.rb +12 -4
- data/lib/llm/bot/prompt/completion.rb +18 -0
- data/lib/llm/bot/prompt/respond.rb +9 -0
- data/lib/llm/bot.rb +10 -17
- data/lib/llm/buffer.rb +15 -1
- data/lib/llm/error.rb +4 -0
- data/lib/llm/{event_handler.rb → eventhandler.rb} +0 -0
- data/lib/llm/eventstream/event.rb +0 -0
- data/lib/llm/eventstream/parser.rb +0 -0
- data/lib/llm/eventstream.rb +0 -0
- data/lib/llm/file.rb +4 -3
- data/lib/llm/function.rb +4 -4
- data/lib/llm/message.rb +0 -0
- data/lib/llm/mime.rb +88 -6
- data/lib/llm/multipart.rb +0 -1
- data/lib/llm/object/builder.rb +0 -0
- data/lib/llm/object/kernel.rb +0 -0
- data/lib/llm/object.rb +2 -3
- data/lib/llm/provider.rb +3 -3
- data/lib/llm/providers/anthropic/error_handler.rb +2 -0
- data/lib/llm/providers/anthropic/format/completion_format.rb +0 -0
- data/lib/llm/providers/anthropic/format.rb +0 -0
- data/lib/llm/providers/anthropic/models.rb +2 -2
- data/lib/llm/providers/anthropic/response/completion.rb +0 -0
- data/lib/llm/providers/anthropic/stream_parser.rb +0 -0
- data/lib/llm/providers/anthropic.rb +10 -1
- data/lib/llm/providers/deepseek/format/completion_format.rb +0 -0
- data/lib/llm/providers/deepseek/format.rb +0 -0
- data/lib/llm/providers/deepseek.rb +10 -1
- data/lib/llm/providers/gemini/audio.rb +3 -3
- data/lib/llm/providers/gemini/error_handler.rb +2 -0
- data/lib/llm/providers/gemini/files.rb +8 -20
- data/lib/llm/providers/gemini/format/completion_format.rb +2 -2
- data/lib/llm/providers/gemini/format.rb +0 -0
- data/lib/llm/providers/gemini/images.rb +4 -4
- data/lib/llm/providers/gemini/models.rb +2 -2
- data/lib/llm/providers/gemini/response/completion.rb +2 -0
- data/lib/llm/providers/gemini/response/embedding.rb +1 -1
- data/lib/llm/providers/gemini/response/file.rb +0 -0
- data/lib/llm/providers/gemini/response/image.rb +0 -0
- data/lib/llm/providers/gemini/stream_parser.rb +0 -0
- data/lib/llm/providers/gemini.rb +13 -21
- data/lib/llm/providers/llamacpp.rb +12 -1
- data/lib/llm/providers/ollama/error_handler.rb +2 -0
- data/lib/llm/providers/ollama/format/completion_format.rb +0 -0
- data/lib/llm/providers/ollama/format.rb +0 -0
- data/lib/llm/providers/ollama/models.rb +0 -0
- data/lib/llm/providers/ollama/response/completion.rb +0 -0
- data/lib/llm/providers/ollama/response/embedding.rb +1 -2
- data/lib/llm/providers/ollama/stream_parser.rb +0 -0
- data/lib/llm/providers/ollama.rb +8 -11
- data/lib/llm/providers/openai/audio.rb +4 -4
- data/lib/llm/providers/openai/error_handler.rb +13 -1
- data/lib/llm/providers/openai/files.rb +8 -19
- data/lib/llm/providers/openai/format/completion_format.rb +0 -0
- data/lib/llm/providers/openai/format/moderation_format.rb +0 -0
- data/lib/llm/providers/openai/format/respond_format.rb +0 -0
- data/lib/llm/providers/openai/format.rb +0 -0
- data/lib/llm/providers/openai/images.rb +10 -10
- data/lib/llm/providers/openai/models.rb +2 -2
- data/lib/llm/providers/openai/moderations.rb +0 -0
- data/lib/llm/providers/openai/response/audio.rb +0 -0
- data/lib/llm/providers/openai/response/completion.rb +2 -2
- data/lib/llm/providers/openai/response/embedding.rb +3 -3
- data/lib/llm/providers/openai/response/file.rb +0 -0
- data/lib/llm/providers/openai/response/image.rb +0 -0
- data/lib/llm/providers/openai/response/moderations.rb +0 -0
- data/lib/llm/providers/openai/response/responds.rb +0 -1
- data/lib/llm/providers/openai/responses.rb +6 -25
- data/lib/llm/providers/openai/stream_parser.rb +1 -0
- data/lib/llm/providers/openai/vector_stores.rb +85 -3
- data/lib/llm/providers/openai.rb +10 -1
- data/lib/llm/providers/xai/images.rb +58 -0
- data/lib/llm/providers/xai.rb +72 -0
- data/lib/llm/response.rb +5 -0
- data/lib/llm/{json/schema → schema}/array.rb +3 -3
- data/lib/llm/{json/schema → schema}/boolean.rb +3 -3
- data/lib/llm/{json/schema → schema}/integer.rb +6 -6
- data/lib/llm/{json/schema → schema}/leaf.rb +9 -9
- data/lib/llm/{json/schema → schema}/null.rb +3 -3
- data/lib/llm/{json/schema → schema}/number.rb +6 -6
- data/lib/llm/{json/schema → schema}/object.rb +3 -3
- data/lib/llm/{json/schema → schema}/string.rb +5 -5
- data/lib/llm/{json/schema → schema}/version.rb +1 -1
- data/lib/llm/{json/schema.rb → schema.rb} +10 -13
- data/lib/llm/utils.rb +0 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +11 -2
- data/llm.gemspec +4 -4
- metadata +22 -20
@@ -22,6 +22,8 @@ class LLM::Gemini
|
|
22
22
|
# Raises a subclass of {LLM::Error LLM::Error}
|
23
23
|
def raise_error!
|
24
24
|
case res
|
25
|
+
when Net::HTTPServerError
|
26
|
+
raise LLM::ServerError.new { _1.response = res }, "Server error"
|
25
27
|
when Net::HTTPBadRequest
|
26
28
|
reason = body.dig("error", "details", 0, "reason")
|
27
29
|
if reason == "API_KEY_INVALID"
|
@@ -17,22 +17,10 @@ class LLM::Gemini
|
|
17
17
|
# #!/usr/bin/env ruby
|
18
18
|
# require "llm"
|
19
19
|
#
|
20
|
-
# llm = LLM.gemini(ENV["KEY"])
|
20
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
21
21
|
# bot = LLM::Bot.new(llm)
|
22
|
-
# file = llm.files.create
|
23
|
-
# bot.chat
|
24
|
-
# bot.chat("Describe the audio file I sent to you")
|
25
|
-
# bot.chat("The audio file is the first message I sent to you.")
|
26
|
-
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
27
|
-
#
|
28
|
-
# @example example #2
|
29
|
-
# #!/usr/bin/env ruby
|
30
|
-
# require "llm"
|
31
|
-
#
|
32
|
-
# llm = LLM.gemini(ENV["KEY"])
|
33
|
-
# bot = LLM::Bot.new(llm)
|
34
|
-
# file = llm.files.create file: "/audio/haiku.mp3"
|
35
|
-
# bot.chat(["Describe the audio file I sent to you", file])
|
22
|
+
# file = llm.files.create(file: "/audio/haiku.mp3")
|
23
|
+
# bot.chat ["Tell me about this file", file]
|
36
24
|
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
37
25
|
class Files
|
38
26
|
require_relative "response/file"
|
@@ -48,7 +36,7 @@ class LLM::Gemini
|
|
48
36
|
##
|
49
37
|
# List all files
|
50
38
|
# @example
|
51
|
-
# llm = LLM.gemini(ENV["KEY"])
|
39
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
52
40
|
# res = llm.files.all
|
53
41
|
# res.each do |file|
|
54
42
|
# print "name: ", file.name, "\n"
|
@@ -67,8 +55,8 @@ class LLM::Gemini
|
|
67
55
|
##
|
68
56
|
# Create a file
|
69
57
|
# @example
|
70
|
-
# llm = LLM.gemini(ENV["KEY"])
|
71
|
-
# res = llm.files.create
|
58
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
59
|
+
# res = llm.files.create(file: "/audio/haiku.mp3")
|
72
60
|
# @see https://ai.google.dev/gemini-api/docs/files Gemini docs
|
73
61
|
# @param [String, LLM::File] file The file
|
74
62
|
# @param [Hash] params Other parameters (see Gemini docs)
|
@@ -90,7 +78,7 @@ class LLM::Gemini
|
|
90
78
|
##
|
91
79
|
# Get a file
|
92
80
|
# @example
|
93
|
-
# llm = LLM.gemini(ENV["KEY"])
|
81
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
94
82
|
# res = llm.files.get(file: "files/1234567890")
|
95
83
|
# print "name: ", res.name, "\n"
|
96
84
|
# @see https://ai.google.dev/gemini-api/docs/files Gemini docs
|
@@ -109,7 +97,7 @@ class LLM::Gemini
|
|
109
97
|
##
|
110
98
|
# Delete a file
|
111
99
|
# @example
|
112
|
-
# llm = LLM.gemini(ENV["KEY"])
|
100
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
113
101
|
# res = llm.files.delete(file: "files/1234567890")
|
114
102
|
# @see https://ai.google.dev/gemini-api/docs/files Gemini docs
|
115
103
|
# @param [#name, String] file The file to delete
|
@@ -59,8 +59,8 @@ module LLM::Gemini::Format
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def prompt_error!(object)
|
62
|
-
|
63
|
-
|
62
|
+
raise LLM::PromptError, "The given object (an instance of #{object.class}) " \
|
63
|
+
"is not supported by the Gemini API"
|
64
64
|
end
|
65
65
|
|
66
66
|
def message = @message
|
File without changes
|
@@ -11,7 +11,7 @@ class LLM::Gemini
|
|
11
11
|
# #!/usr/bin/env ruby
|
12
12
|
# require "llm"
|
13
13
|
#
|
14
|
-
# llm = LLM.gemini(ENV["KEY"])
|
14
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
15
15
|
# res = llm.images.create prompt: "A dog on a rocket to the moon"
|
16
16
|
# IO.copy_stream res.images[0], "rocket.png"
|
17
17
|
class Images
|
@@ -29,7 +29,7 @@ class LLM::Gemini
|
|
29
29
|
##
|
30
30
|
# Create an image
|
31
31
|
# @example
|
32
|
-
# llm = LLM.gemini(ENV["KEY"])
|
32
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
33
33
|
# res = llm.images.create prompt: "A dog on a rocket to the moon"
|
34
34
|
# IO.copy_stream res.images[0], "rocket.png"
|
35
35
|
# @see https://ai.google.dev/gemini-api/docs/image-generation Gemini docs
|
@@ -55,8 +55,8 @@ class LLM::Gemini
|
|
55
55
|
##
|
56
56
|
# Edit an image
|
57
57
|
# @example
|
58
|
-
# llm = LLM.gemini(ENV["KEY"])
|
59
|
-
# res = llm.images.edit image:
|
58
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
59
|
+
# res = llm.images.edit image: "cat.png", prompt: "Add a hat to the cat"
|
60
60
|
# IO.copy_stream res.images[0], "hatoncat.png"
|
61
61
|
# @see https://ai.google.dev/gemini-api/docs/image-generation Gemini docs
|
62
62
|
# @param [String, LLM::File] image The image to edit
|
@@ -11,7 +11,7 @@ class LLM::Gemini
|
|
11
11
|
# #!/usr/bin/env ruby
|
12
12
|
# require "llm"
|
13
13
|
#
|
14
|
-
# llm = LLM.gemini(ENV["KEY"])
|
14
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
15
15
|
# res = llm.models.all
|
16
16
|
# res.each do |model|
|
17
17
|
# print "id: ", model.id, "\n"
|
@@ -30,7 +30,7 @@ class LLM::Gemini
|
|
30
30
|
##
|
31
31
|
# List all models
|
32
32
|
# @example
|
33
|
-
# llm = LLM.gemini(ENV["KEY"])
|
33
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
34
34
|
# res = llm.models.all
|
35
35
|
# res.each do |model|
|
36
36
|
# print "id: ", model.id, "\n"
|
File without changes
|
File without changes
|
File without changes
|
data/lib/llm/providers/gemini.rb
CHANGED
@@ -3,30 +3,19 @@
|
|
3
3
|
module LLM
|
4
4
|
##
|
5
5
|
# The Gemini class implements a provider for
|
6
|
-
# [Gemini](https://ai.google.dev/).
|
6
|
+
# [Gemini](https://ai.google.dev/). The Gemini provider
|
7
|
+
# can accept multiple inputs (text, images, audio, and video).
|
8
|
+
# The inputs can be provided inline via the prompt for files
|
9
|
+
# under 20MB or via the Gemini Files API for files
|
10
|
+
# that are over 20MB.
|
7
11
|
#
|
8
|
-
#
|
9
|
-
# audio, and video). The inputs can be provided inline via the
|
10
|
-
# prompt for files under 20MB or via the Gemini Files API for
|
11
|
-
# files that are over 20MB
|
12
|
-
#
|
13
|
-
# @example example #1
|
14
|
-
# #!/usr/bin/env ruby
|
15
|
-
# require "llm"
|
16
|
-
#
|
17
|
-
# llm = LLM.gemini(ENV["KEY"])
|
18
|
-
# bot = LLM::Bot.new(llm)
|
19
|
-
# bot.chat LLM.File("/images/capybara.png")
|
20
|
-
# bot.chat "Describe the image"
|
21
|
-
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
22
|
-
#
|
23
|
-
# @example example #2
|
12
|
+
# @example
|
24
13
|
# #!/usr/bin/env ruby
|
25
14
|
# require "llm"
|
26
15
|
#
|
27
|
-
# llm = LLM.gemini(ENV["KEY"])
|
16
|
+
# llm = LLM.gemini(key: ENV["KEY"])
|
28
17
|
# bot = LLM::Bot.new(llm)
|
29
|
-
# bot.chat ["
|
18
|
+
# bot.chat ["Tell me about this photo", File.open("/images/horse.jpg", "rb")]
|
30
19
|
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
31
20
|
class Gemini < Provider
|
32
21
|
require_relative "gemini/response/embedding"
|
@@ -55,7 +44,7 @@ module LLM
|
|
55
44
|
# @param model (see LLM::Provider#embed)
|
56
45
|
# @param params (see LLM::Provider#embed)
|
57
46
|
# @raise (see LLM::Provider#request)
|
58
|
-
# @return
|
47
|
+
# @return [LLM::Response]
|
59
48
|
def embed(input, model: "text-embedding-004", **params)
|
60
49
|
model = model.respond_to?(:id) ? model.id : model
|
61
50
|
path = ["/v1beta/models/#{model}", "embedContent?key=#{@key}"].join(":")
|
@@ -74,7 +63,7 @@ module LLM
|
|
74
63
|
# @raise (see LLM::Provider#request)
|
75
64
|
# @raise [LLM::PromptError]
|
76
65
|
# When given an object a provider does not understand
|
77
|
-
# @return
|
66
|
+
# @return [LLM::Response]
|
78
67
|
def complete(prompt, params = {})
|
79
68
|
params = {role: :user, model: default_model}.merge!(params)
|
80
69
|
params = [params, format_schema(params), format_tools(params)].inject({}, &:merge!).compact
|
@@ -93,6 +82,7 @@ module LLM
|
|
93
82
|
##
|
94
83
|
# Provides an interface to Gemini's audio API
|
95
84
|
# @see https://ai.google.dev/gemini-api/docs/audio Gemini docs
|
85
|
+
# @return [LLM::Gemini::Audio]
|
96
86
|
def audio
|
97
87
|
LLM::Gemini::Audio.new(self)
|
98
88
|
end
|
@@ -108,6 +98,7 @@ module LLM
|
|
108
98
|
##
|
109
99
|
# Provides an interface to Gemini's file management API
|
110
100
|
# @see https://ai.google.dev/gemini-api/docs/files Gemini docs
|
101
|
+
# @return [LLM::Gemini::Files]
|
111
102
|
def files
|
112
103
|
LLM::Gemini::Files.new(self)
|
113
104
|
end
|
@@ -115,6 +106,7 @@ module LLM
|
|
115
106
|
##
|
116
107
|
# Provides an interface to Gemini's models API
|
117
108
|
# @see https://ai.google.dev/gemini-api/docs/models Gemini docs
|
109
|
+
# @return [LLM::Gemini::Models]
|
118
110
|
def models
|
119
111
|
LLM::Gemini::Models.new(self)
|
120
112
|
end
|
@@ -7,7 +7,18 @@ module LLM
|
|
7
7
|
# The LlamaCpp class implements a provider for
|
8
8
|
# [llama.cpp](https://github.com/ggml-org/llama.cpp)
|
9
9
|
# through the OpenAI-compatible API provided by the
|
10
|
-
# llama-server binary.
|
10
|
+
# llama-server binary. Similar to the ollama provider,
|
11
|
+
# this provider supports a wide range of models and
|
12
|
+
# is straightforward to run on your own hardware.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# #!/usr/bin/env ruby
|
16
|
+
# require "llm"
|
17
|
+
#
|
18
|
+
# llm = LLM.llamacpp(key: nil)
|
19
|
+
# bot = LLM::Bot.new(llm)
|
20
|
+
# bot.chat ["Tell me about this photo", File.open("/images/frog.jpg", "rb")]
|
21
|
+
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
11
22
|
class LlamaCpp < OpenAI
|
12
23
|
##
|
13
24
|
# @param (see LLM::Provider#initialize)
|
@@ -22,6 +22,8 @@ class LLM::Ollama
|
|
22
22
|
# Raises a subclass of {LLM::Error LLM::Error}
|
23
23
|
def raise_error!
|
24
24
|
case res
|
25
|
+
when Net::HTTPServerError
|
26
|
+
raise LLM::ServerError.new { _1.response = res }, "Server error"
|
25
27
|
when Net::HTTPUnauthorized
|
26
28
|
raise LLM::UnauthorizedError.new { _1.response = res }, "Authentication error"
|
27
29
|
when Net::HTTPTooManyRequests
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/llm/providers/ollama.rb
CHANGED
@@ -2,21 +2,18 @@
|
|
2
2
|
|
3
3
|
module LLM
|
4
4
|
##
|
5
|
-
# The Ollama class implements a provider for [Ollama](https://ollama.ai/)
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# models that can process images and text. See the example for a demonstration
|
10
|
-
# of a multi-modal model by the name `llava`
|
5
|
+
# The Ollama class implements a provider for [Ollama](https://ollama.ai/) –
|
6
|
+
# and the provider supports a wide range of models. It is straight forward
|
7
|
+
# to run on your own hardware, and there are a number of multi-modal models
|
8
|
+
# that can process both images and text.
|
11
9
|
#
|
12
10
|
# @example
|
13
11
|
# #!/usr/bin/env ruby
|
14
12
|
# require "llm"
|
15
13
|
#
|
16
|
-
# llm = LLM.ollama(nil)
|
14
|
+
# llm = LLM.ollama(key: nil)
|
17
15
|
# bot = LLM::Bot.new(llm, model: "llava")
|
18
|
-
# bot.chat
|
19
|
-
# bot.chat "Describe the image"
|
16
|
+
# bot.chat ["Tell me about this image", File.open("/images/parrot.png", "rb")]
|
20
17
|
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
21
18
|
class Ollama < Provider
|
22
19
|
require_relative "ollama/response/embedding"
|
@@ -42,7 +39,7 @@ module LLM
|
|
42
39
|
# @param model (see LLM::Provider#embed)
|
43
40
|
# @param params (see LLM::Provider#embed)
|
44
41
|
# @raise (see LLM::Provider#request)
|
45
|
-
# @return
|
42
|
+
# @return [LLM::Response]
|
46
43
|
def embed(input, model: default_model, **params)
|
47
44
|
params = {model:}.merge!(params)
|
48
45
|
req = Net::HTTP::Post.new("/v1/embeddings", headers)
|
@@ -60,7 +57,7 @@ module LLM
|
|
60
57
|
# @raise (see LLM::Provider#request)
|
61
58
|
# @raise [LLM::PromptError]
|
62
59
|
# When given an object a provider does not understand
|
63
|
-
# @return
|
60
|
+
# @return [LLM::Response]
|
64
61
|
def complete(prompt, params = {})
|
65
62
|
params = {role: :user, model: default_model, stream: true}.merge!(params)
|
66
63
|
params = [params, {format: params[:schema]}, format_tools(params)].inject({}, &:merge!).compact
|
@@ -5,7 +5,7 @@ class LLM::OpenAI
|
|
5
5
|
# The {LLM::OpenAI::Audio LLM::OpenAI::Audio} class provides an audio
|
6
6
|
# object for interacting with [OpenAI's audio API](https://platform.openai.com/docs/api-reference/audio/createSpeech).
|
7
7
|
# @example
|
8
|
-
# llm = LLM.openai(ENV["KEY"])
|
8
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
9
9
|
# res = llm.audio.create_speech(input: "A dog on a rocket to the moon")
|
10
10
|
# IO.copy_stream res.audio, "rocket.mp3"
|
11
11
|
class Audio
|
@@ -20,7 +20,7 @@ class LLM::OpenAI
|
|
20
20
|
##
|
21
21
|
# Create an audio track
|
22
22
|
# @example
|
23
|
-
# llm = LLM.openai(ENV["KEY"])
|
23
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
24
24
|
# res = llm.images.create_speech(input: "A dog on a rocket to the moon")
|
25
25
|
# File.binwrite("rocket.mp3", res.audio.string)
|
26
26
|
# @see https://platform.openai.com/docs/api-reference/audio/createSpeech OpenAI docs
|
@@ -42,7 +42,7 @@ class LLM::OpenAI
|
|
42
42
|
##
|
43
43
|
# Create an audio transcription
|
44
44
|
# @example
|
45
|
-
# llm = LLM.openai(ENV["KEY"])
|
45
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
46
46
|
# res = llm.audio.create_transcription(file: "/audio/rocket.mp3")
|
47
47
|
# res.text # => "A dog on a rocket to the moon"
|
48
48
|
# @see https://platform.openai.com/docs/api-reference/audio/createTranscription OpenAI docs
|
@@ -64,7 +64,7 @@ class LLM::OpenAI
|
|
64
64
|
# Create an audio translation (in English)
|
65
65
|
# @example
|
66
66
|
# # Arabic => English
|
67
|
-
# llm = LLM.openai(ENV["KEY"])
|
67
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
68
68
|
# res = llm.audio.create_translation(file: "/audio/bismillah.mp3")
|
69
69
|
# res.text # => "In the name of Allah, the Beneficent, the Merciful."
|
70
70
|
# @see https://platform.openai.com/docs/api-reference/audio/createTranslation OpenAI docs
|
@@ -22,13 +22,25 @@ class LLM::OpenAI
|
|
22
22
|
# Raises a subclass of {LLM::Error LLM::Error}
|
23
23
|
def raise_error!
|
24
24
|
case res
|
25
|
+
when Net::HTTPServerError
|
26
|
+
raise LLM::ServerError.new { _1.response = res }, "Server error"
|
25
27
|
when Net::HTTPUnauthorized
|
26
28
|
raise LLM::UnauthorizedError.new { _1.response = res }, "Authentication error"
|
27
29
|
when Net::HTTPTooManyRequests
|
28
30
|
raise LLM::RateLimitError.new { _1.response = res }, "Too many requests"
|
29
31
|
else
|
30
|
-
|
32
|
+
error = body["error"] || {}
|
33
|
+
case error["type"]
|
34
|
+
when "server_error" then raise LLM::ServerError.new { _1.response = res }, error["message"]
|
35
|
+
else raise LLM::ResponseError.new { _1.response = res }, error["message"] || "Unexpected response"
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def body
|
43
|
+
@body ||= JSON.parse(res.body)
|
44
|
+
end
|
33
45
|
end
|
34
46
|
end
|
@@ -12,21 +12,10 @@ class LLM::OpenAI
|
|
12
12
|
# #!/usr/bin/env ruby
|
13
13
|
# require "llm"
|
14
14
|
#
|
15
|
-
# llm = LLM.openai(ENV["KEY"])
|
15
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
16
16
|
# bot = LLM::Bot.new(llm)
|
17
|
-
# file = llm.files.create file: "/
|
18
|
-
# bot.chat
|
19
|
-
# bot.chat("Describe the document")
|
20
|
-
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
21
|
-
#
|
22
|
-
# @example example #2
|
23
|
-
# #!/usr/bin/env ruby
|
24
|
-
# require "llm"
|
25
|
-
#
|
26
|
-
# llm = LLM.openai(ENV["KEY"])
|
27
|
-
# bot = LLM::Bot.new(llm)
|
28
|
-
# file = llm.files.create file: "/documents/openbsd.pdf"
|
29
|
-
# bot.chat(["Describe the document I sent to you", file])
|
17
|
+
# file = llm.files.create file: "/books/goodread.pdf"
|
18
|
+
# bot.chat ["Tell me about this PDF", file]
|
30
19
|
# bot.messages.select(&:assistant?).each { print "[#{_1.role}]", _1.content, "\n" }
|
31
20
|
class Files
|
32
21
|
require_relative "response/file"
|
@@ -42,7 +31,7 @@ class LLM::OpenAI
|
|
42
31
|
##
|
43
32
|
# List all files
|
44
33
|
# @example
|
45
|
-
# llm = LLM.openai(ENV["KEY"])
|
34
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
46
35
|
# res = llm.files.all
|
47
36
|
# res.each do |file|
|
48
37
|
# print "id: ", file.id, "\n"
|
@@ -61,7 +50,7 @@ class LLM::OpenAI
|
|
61
50
|
##
|
62
51
|
# Create a file
|
63
52
|
# @example
|
64
|
-
# llm = LLM.openai(ENV["KEY"])
|
53
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
65
54
|
# res = llm.files.create file: "/documents/haiku.txt"
|
66
55
|
# @see https://platform.openai.com/docs/api-reference/files/create OpenAI docs
|
67
56
|
# @param [File, LLM::File, String] file The file
|
@@ -81,7 +70,7 @@ class LLM::OpenAI
|
|
81
70
|
##
|
82
71
|
# Get a file
|
83
72
|
# @example
|
84
|
-
# llm = LLM.openai(ENV["KEY"])
|
73
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
85
74
|
# res = llm.files.get(file: "file-1234567890")
|
86
75
|
# print "id: ", res.id, "\n"
|
87
76
|
# @see https://platform.openai.com/docs/api-reference/files/get OpenAI docs
|
@@ -100,7 +89,7 @@ class LLM::OpenAI
|
|
100
89
|
##
|
101
90
|
# Download the content of a file
|
102
91
|
# @example
|
103
|
-
# llm = LLM.openai(ENV["KEY"])
|
92
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
104
93
|
# res = llm.files.download(file: "file-1234567890")
|
105
94
|
# File.binwrite "haiku1.txt", res.file.read
|
106
95
|
# print res.file.read, "\n"
|
@@ -121,7 +110,7 @@ class LLM::OpenAI
|
|
121
110
|
##
|
122
111
|
# Delete a file
|
123
112
|
# @example
|
124
|
-
# llm = LLM.openai(ENV["KEY"])
|
113
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
125
114
|
# res = llm.files.delete(file: "file-1234567890")
|
126
115
|
# print res.deleted, "\n"
|
127
116
|
# @see https://platform.openai.com/docs/api-reference/files/delete OpenAI docs
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -2,27 +2,27 @@
|
|
2
2
|
|
3
3
|
class LLM::OpenAI
|
4
4
|
##
|
5
|
-
# The {LLM::OpenAI::Images LLM::OpenAI::Images} class provides an
|
6
|
-
#
|
5
|
+
# The {LLM::OpenAI::Images LLM::OpenAI::Images} class provides an interface
|
6
|
+
# for [OpenAI's images API](https://platform.openai.com/docs/api-reference/images).
|
7
7
|
# OpenAI supports multiple response formats: temporary URLs, or binary strings
|
8
8
|
# encoded in base64. The default is to return temporary URLs.
|
9
9
|
#
|
10
|
-
# @example
|
10
|
+
# @example Temporary URLs
|
11
11
|
# #!/usr/bin/env ruby
|
12
12
|
# require "llm"
|
13
13
|
# require "open-uri"
|
14
14
|
# require "fileutils"
|
15
15
|
#
|
16
|
-
# llm = LLM.openai(ENV["KEY"])
|
16
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
17
17
|
# res = llm.images.create prompt: "A dog on a rocket to the moon"
|
18
18
|
# FileUtils.mv OpenURI.open_uri(res.urls[0]).path,
|
19
19
|
# "rocket.png"
|
20
20
|
#
|
21
|
-
# @example
|
21
|
+
# @example Binary strings
|
22
22
|
# #!/usr/bin/env ruby
|
23
23
|
# require "llm"
|
24
24
|
#
|
25
|
-
# llm = LLM.openai(ENV["KEY"])
|
25
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
26
26
|
# res = llm.images.create prompt: "A dog on a rocket to the moon",
|
27
27
|
# response_format: "b64_json"
|
28
28
|
# IO.copy_stream res.images[0], "rocket.png"
|
@@ -39,9 +39,9 @@ class LLM::OpenAI
|
|
39
39
|
##
|
40
40
|
# Create an image
|
41
41
|
# @example
|
42
|
-
# llm = LLM.openai(ENV["KEY"])
|
42
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
43
43
|
# res = llm.images.create prompt: "A dog on a rocket to the moon"
|
44
|
-
#
|
44
|
+
# res.urls.each { print _1, "\n" }
|
45
45
|
# @see https://platform.openai.com/docs/api-reference/images/create OpenAI docs
|
46
46
|
# @param [String] prompt The prompt
|
47
47
|
# @param [String] model The model to use
|
@@ -58,7 +58,7 @@ class LLM::OpenAI
|
|
58
58
|
##
|
59
59
|
# Create image variations
|
60
60
|
# @example
|
61
|
-
# llm = LLM.openai(ENV["KEY"])
|
61
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
62
62
|
# res = llm.images.create_variation(image: "/images/hat.png", n: 5)
|
63
63
|
# p res.urls
|
64
64
|
# @see https://platform.openai.com/docs/api-reference/images/createVariation OpenAI docs
|
@@ -80,7 +80,7 @@ class LLM::OpenAI
|
|
80
80
|
##
|
81
81
|
# Edit an image
|
82
82
|
# @example
|
83
|
-
# llm = LLM.openai(ENV["KEY"])
|
83
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
84
84
|
# res = llm.images.edit(image: "/images/hat.png", prompt: "A cat wearing this hat")
|
85
85
|
# p res.urls
|
86
86
|
# @see https://platform.openai.com/docs/api-reference/images/createEdit OpenAI docs
|
@@ -11,7 +11,7 @@ class LLM::OpenAI
|
|
11
11
|
# #!/usr/bin/env ruby
|
12
12
|
# require "llm"
|
13
13
|
#
|
14
|
-
# llm = LLM.openai(ENV["KEY"])
|
14
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
15
15
|
# res = llm.models.all
|
16
16
|
# res.each do |model|
|
17
17
|
# print "id: ", model.id, "\n"
|
@@ -28,7 +28,7 @@ class LLM::OpenAI
|
|
28
28
|
##
|
29
29
|
# List all models
|
30
30
|
# @example
|
31
|
-
# llm = LLM.openai(ENV["KEY"])
|
31
|
+
# llm = LLM.openai(key: ENV["KEY"])
|
32
32
|
# res = llm.models.all
|
33
33
|
# res.each do |model|
|
34
34
|
# print "id: ", model.id, "\n"
|
File without changes
|
File without changes
|
@@ -16,7 +16,7 @@ module LLM::OpenAI::Response
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
alias_method :messages, :choices
|
19
|
-
|
19
|
+
|
20
20
|
def model = body.model
|
21
21
|
def prompt_tokens = body.usage&.prompt_tokens
|
22
22
|
def completion_tokens = body.usage&.completion_tokens
|
@@ -36,4 +36,4 @@ module LLM::OpenAI::Response
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
-
end
|
39
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
module LLM::OpenAI::Response
|
4
4
|
module Embedding
|
5
5
|
def embeddings = data.map { _1["embedding"] }
|
6
6
|
def prompt_tokens = data.dig(0, "usage", "prompt_tokens")
|
7
7
|
def total_tokens = data.dig(0, "usage", "total_tokens")
|
8
8
|
end
|
9
|
-
end
|
9
|
+
end
|
File without changes
|
File without changes
|
File without changes
|