llm.rb 0.12.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/README.md +10 -10
- 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 +3 -3
- data/lib/llm/buffer.rb +8 -1
- data/lib/llm/error.rb +4 -0
- data/lib/llm/function.rb +2 -2
- data/lib/llm/mime.rb +88 -6
- data/lib/llm/provider.rb +2 -2
- data/lib/llm/providers/anthropic/error_handler.rb +2 -0
- data/lib/llm/providers/gemini/error_handler.rb +2 -0
- data/lib/llm/providers/gemini/response/completion.rb +2 -0
- data/lib/llm/providers/ollama/error_handler.rb +2 -0
- data/lib/llm/providers/openai/error_handler.rb +13 -1
- data/lib/llm/providers/xai/images.rb +1 -1
- 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/version.rb +1 -1
- data/lib/llm.rb +1 -1
- data/llm.gemspec +1 -1
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f83248fa3bce285d75952b726cc7d1b097e61b61fb77edd55f39168922a8614c
|
4
|
+
data.tar.gz: 52fa691fad7eaa2e77e24245aba97ea469688f8307f869d533fac47707aa6caf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d17e104601295bbbd8dd5bd34fe5378e96367aacb832ddb81b7f0fdfd3fa54f39d98dff6568f3190a4d979de4382a6ebd2eba8ee2fb2cce0e67db2478be9a1df
|
7
|
+
data.tar.gz: d2e6bb0f507bdcf140bd7f44f9edd40c057f061a9b4bc7123a87bdf6fa40a6f16a99642965bdaf81aaaeab5e995a01587c79cd47e19c6a2b5f86e456fa4813a9
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
llm.rb is a zero-dependency Ruby toolkit for Large Language Models that
|
4
4
|
includes OpenAI, Gemini, Anthropic, xAI (grok), DeepSeek, Ollama, and
|
5
5
|
LlamaCpp. The toolkit includes full support for chat, streaming, tool calling,
|
6
|
-
audio, images, files, and JSON Schema
|
6
|
+
audio, images, files, and structured outputs (JSON Schema).
|
7
7
|
|
8
8
|
## Features
|
9
9
|
|
@@ -22,7 +22,7 @@ audio, images, files, and JSON Schema generation.
|
|
22
22
|
- 🗣️ Text-to-speech, transcription, and translation
|
23
23
|
- 🖼️ Image generation, editing, and variation support
|
24
24
|
- 📎 File uploads and prompt-aware file interaction
|
25
|
-
- 💡 Multimodal prompts (text, images,
|
25
|
+
- 💡 Multimodal prompts (text, documents, audio, images, videos, URLs, etc)
|
26
26
|
|
27
27
|
#### Embeddings
|
28
28
|
- 🧮 Text embeddings and vector support
|
@@ -47,9 +47,9 @@ breaks things down by provider, so you can see exactly what’s supported where.
|
|
47
47
|
| **Audio (TTS / Transcribe / Translate)** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
48
48
|
| **Image Generation & Editing** | ✅ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ |
|
49
49
|
| **File Uploads** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
50
|
-
| **Multimodal Prompts** *(text
|
50
|
+
| **Multimodal Prompts** *(text, documents, audio, images, videos, URLs, etc)* | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
51
51
|
| **Embeddings** | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
|
52
|
-
| **Models API**
|
52
|
+
| **Models API** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
53
53
|
| **Local Model Support** | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
|
54
54
|
| **Vector Stores (RAG)** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
55
55
|
| **Responses** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
@@ -110,12 +110,12 @@ require "llm"
|
|
110
110
|
|
111
111
|
llm = LLM.openai(key: ENV["KEY"])
|
112
112
|
bot = LLM::Bot.new(llm)
|
113
|
-
url = "https://
|
113
|
+
url = "https://en.wikipedia.org/wiki/Special:FilePath/Cognac_glass.jpg"
|
114
114
|
msgs = bot.chat do |prompt|
|
115
115
|
prompt.system "Your task is to answer all user queries"
|
116
116
|
prompt.user ["Tell me about this URL", URI(url)]
|
117
|
-
prompt.user ["Tell me about this
|
118
|
-
prompt.user "
|
117
|
+
prompt.user ["Tell me about this PDF", File.open("handbook.pdf", "rb")]
|
118
|
+
prompt.user "Are the URL and PDF similar to each other?"
|
119
119
|
end
|
120
120
|
|
121
121
|
# At this point, we execute a single request
|
@@ -142,12 +142,12 @@ require "llm"
|
|
142
142
|
|
143
143
|
llm = LLM.openai(key: ENV["KEY"])
|
144
144
|
bot = LLM::Bot.new(llm)
|
145
|
-
url = "https://
|
145
|
+
url = "https://en.wikipedia.org/wiki/Special:FilePath/Cognac_glass.jpg"
|
146
146
|
bot.chat(stream: $stdout) do |prompt|
|
147
147
|
prompt.system "Your task is to answer all user queries"
|
148
148
|
prompt.user ["Tell me about this URL", URI(url)]
|
149
|
-
prompt.user ["Tell me about this
|
150
|
-
prompt.user "
|
149
|
+
prompt.user ["Tell me about this PDF", File.open("handbook.pdf", "rb")]
|
150
|
+
prompt.user "Are the URL and PDF similar to each other?"
|
151
151
|
end.to_a
|
152
152
|
```
|
153
153
|
|
data/lib/llm/bot/conversable.rb
CHANGED
@@ -12,8 +12,12 @@ class LLM::Bot
|
|
12
12
|
# @param [Hash] params
|
13
13
|
# @return [void]
|
14
14
|
def async_response(prompt, params = {})
|
15
|
-
|
16
|
-
|
15
|
+
if Array === prompt and prompt.empty?
|
16
|
+
@messages
|
17
|
+
else
|
18
|
+
role = params.delete(:role)
|
19
|
+
@messages << [LLM::Message.new(role, prompt), @params.merge(params), :respond]
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
23
|
##
|
@@ -22,8 +26,12 @@ class LLM::Bot
|
|
22
26
|
# @param [Hash] params
|
23
27
|
# @return [void]
|
24
28
|
def async_completion(prompt, params = {})
|
25
|
-
|
26
|
-
|
29
|
+
if Array === prompt and prompt.empty?
|
30
|
+
@messages
|
31
|
+
else
|
32
|
+
role = params.delete(:role)
|
33
|
+
@messages << [LLM::Message.new(role, prompt), @params.merge(params), :complete]
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -27,5 +27,23 @@ module LLM::Bot::Prompt
|
|
27
27
|
params = defaults.merge(params)
|
28
28
|
bot.chat prompt, params.merge(role: :user)
|
29
29
|
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# @param [String] prompt
|
33
|
+
# @param [Hash] params (see LLM::Provider#complete)
|
34
|
+
# @return [LLM::Bot]
|
35
|
+
def assistant(prompt, params = {})
|
36
|
+
params = defaults.merge(params)
|
37
|
+
bot.chat prompt, params.merge(role: :assistant)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# @param [String] prompt
|
42
|
+
# @param [Hash] params (see LLM::Provider#complete)
|
43
|
+
# @return [LLM::Bot]
|
44
|
+
def model(prompt, params = {})
|
45
|
+
params = defaults.merge(params)
|
46
|
+
bot.chat prompt, params.merge(role: :model)
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
@@ -36,5 +36,14 @@ module LLM::Bot::Prompt
|
|
36
36
|
params = defaults.merge(params)
|
37
37
|
bot.respond prompt, params.merge(role: :user)
|
38
38
|
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# @param [String] prompt
|
42
|
+
# @param [Hash] params (see LLM::Provider#complete)
|
43
|
+
# @return [LLM::Bot]
|
44
|
+
def assistant(prompt, params = {})
|
45
|
+
params = defaults.merge(params)
|
46
|
+
bot.chat prompt, params.merge(role: :assistant)
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
data/lib/llm/bot.rb
CHANGED
@@ -13,12 +13,12 @@ module LLM
|
|
13
13
|
#
|
14
14
|
# llm = LLM.openai(key: ENV["KEY"])
|
15
15
|
# bot = LLM::Bot.new(llm)
|
16
|
-
# url = "https://
|
16
|
+
# url = "https://en.wikipedia.org/wiki/Special:FilePath/Cognac_glass.jpg"
|
17
17
|
# msgs = bot.chat do |prompt|
|
18
18
|
# prompt.system "Your task is to answer all user queries"
|
19
19
|
# prompt.user ["Tell me about this URL", URI(url)]
|
20
|
-
# prompt.user ["Tell me about this
|
21
|
-
# prompt.user "
|
20
|
+
# prompt.user ["Tell me about this PDF", File.open("handbook.pdf", "rb")]
|
21
|
+
# prompt.user "Are the URL and PDF similar to each other?"
|
22
22
|
# end
|
23
23
|
#
|
24
24
|
# # At this point, we execute a single request
|
data/lib/llm/buffer.rb
CHANGED
@@ -55,7 +55,7 @@ module LLM
|
|
55
55
|
end
|
56
56
|
|
57
57
|
##
|
58
|
-
# @param [[LLM::Message, Hash]] item
|
58
|
+
# @param [[LLM::Message, Hash, Symbol]] item
|
59
59
|
# A message and its parameters
|
60
60
|
# @return [void]
|
61
61
|
def <<(item)
|
@@ -80,6 +80,13 @@ module LLM
|
|
80
80
|
"completed_count=#{@completed.size} pending_count=#{@pending.size}>"
|
81
81
|
end
|
82
82
|
|
83
|
+
##
|
84
|
+
# Returns true when the buffer is empty
|
85
|
+
# @return [Boolean]
|
86
|
+
def empty?
|
87
|
+
@pending.empty? and @completed.empty?
|
88
|
+
end
|
89
|
+
|
83
90
|
private
|
84
91
|
|
85
92
|
def empty!
|
data/lib/llm/error.rb
CHANGED
data/lib/llm/function.rb
CHANGED
@@ -53,7 +53,7 @@ class LLM::Function
|
|
53
53
|
# @yieldparam [LLM::Function] self The function object
|
54
54
|
def initialize(name, &b)
|
55
55
|
@name = name
|
56
|
-
@schema =
|
56
|
+
@schema = LLM::Schema.new
|
57
57
|
@called = false
|
58
58
|
@cancelled = false
|
59
59
|
yield(self)
|
@@ -72,7 +72,7 @@ class LLM::Function
|
|
72
72
|
end
|
73
73
|
|
74
74
|
##
|
75
|
-
# @yieldparam [
|
75
|
+
# @yieldparam [LLM::Schema] schema The schema object
|
76
76
|
# @return [void]
|
77
77
|
def params
|
78
78
|
@params = yield(@schema)
|
data/lib/llm/mime.rb
CHANGED
@@ -7,11 +7,8 @@ class LLM::Mime
|
|
7
7
|
# Lookup a mime type
|
8
8
|
# @return [String, nil]
|
9
9
|
def self.[](key)
|
10
|
-
|
11
|
-
|
12
|
-
else
|
13
|
-
types[key]
|
14
|
-
end
|
10
|
+
key = key.respond_to?(:path) ? File.extname(key.path) : key
|
11
|
+
types[key] || "application/octet-stream"
|
15
12
|
end
|
16
13
|
|
17
14
|
##
|
@@ -24,6 +21,15 @@ class LLM::Mime
|
|
24
21
|
".jpg" => "image/jpeg",
|
25
22
|
".jpeg" => "image/jpeg",
|
26
23
|
".webp" => "image/webp",
|
24
|
+
".gif" => "image/gif",
|
25
|
+
".bmp" => "image/bmp",
|
26
|
+
".tif" => "image/tiff",
|
27
|
+
".tiff" => "image/tiff",
|
28
|
+
".svg" => "image/svg+xml",
|
29
|
+
".ico" => "image/x-icon",
|
30
|
+
".apng" => "image/apng",
|
31
|
+
".jfif" => "image/jpeg",
|
32
|
+
".heic" => "image/heic",
|
27
33
|
|
28
34
|
# Videos
|
29
35
|
".flv" => "video/x-flv",
|
@@ -34,6 +40,12 @@ class LLM::Mime
|
|
34
40
|
".webm" => "video/webm",
|
35
41
|
".wmv" => "video/x-ms-wmv",
|
36
42
|
".3gp" => "video/3gpp",
|
43
|
+
".avi" => "video/x-msvideo",
|
44
|
+
".mkv" => "video/x-matroska",
|
45
|
+
".ogv" => "video/ogg",
|
46
|
+
".m4v" => "video/x-m4v",
|
47
|
+
".m2ts" => "video/mp2t",
|
48
|
+
".mts" => "video/mp2t",
|
37
49
|
|
38
50
|
# Audio
|
39
51
|
".aac" => "audio/aac",
|
@@ -45,10 +57,80 @@ class LLM::Mime
|
|
45
57
|
".pcm" => "audio/L16",
|
46
58
|
".wav" => "audio/wav",
|
47
59
|
".weba" => "audio/webm",
|
60
|
+
".oga" => "audio/ogg",
|
61
|
+
".ogg" => "audio/ogg",
|
62
|
+
".mid" => "audio/midi",
|
63
|
+
".midi" => "audio/midi",
|
64
|
+
".aiff" => "audio/aiff",
|
65
|
+
".aif" => "audio/aiff",
|
66
|
+
".amr" => "audio/amr",
|
67
|
+
".mka" => "audio/x-matroska",
|
68
|
+
".caf" => "audio/x-caf",
|
48
69
|
|
49
70
|
# Documents
|
50
71
|
".pdf" => "application/pdf",
|
51
|
-
".txt" => "text/plain"
|
72
|
+
".txt" => "text/plain",
|
73
|
+
".md" => "text/markdown",
|
74
|
+
".markdown" => "text/markdown",
|
75
|
+
".mkd" => "text/markdown",
|
76
|
+
".doc" => "application/msword",
|
77
|
+
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
78
|
+
".xls" => "application/vnd.ms-excel",
|
79
|
+
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
80
|
+
".ppt" => "application/vnd.ms-powerpoint",
|
81
|
+
".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
82
|
+
".csv" => "text/csv",
|
83
|
+
".json" => "application/json",
|
84
|
+
".xml" => "application/xml",
|
85
|
+
".html" => "text/html",
|
86
|
+
".htm" => "text/html",
|
87
|
+
".odt" => "application/vnd.oasis.opendocument.text",
|
88
|
+
".odp" => "application/vnd.oasis.opendocument.presentation",
|
89
|
+
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
|
90
|
+
".rtf" => "application/rtf",
|
91
|
+
".epub" => "application/epub+zip",
|
92
|
+
|
93
|
+
# Code
|
94
|
+
".js" => "application/javascript",
|
95
|
+
".jsx" => "text/jsx",
|
96
|
+
".ts" => "application/typescript",
|
97
|
+
".tsx" => "text/tsx",
|
98
|
+
".css" => "text/css",
|
99
|
+
".c" => "text/x-c",
|
100
|
+
".cpp" => "text/x-c++",
|
101
|
+
".h" => "text/x-c",
|
102
|
+
".rb" => "text/x-ruby",
|
103
|
+
".py" => "text/x-python",
|
104
|
+
".java" => "text/x-java-source",
|
105
|
+
".sh" => "application/x-sh",
|
106
|
+
".php" => "application/x-httpd-php",
|
107
|
+
".yml" => "text/yaml",
|
108
|
+
".yaml" => "text/yaml",
|
109
|
+
".go" => "text/x-go",
|
110
|
+
".rs" => "text/rust",
|
111
|
+
|
112
|
+
# Fonts
|
113
|
+
".woff" => "font/woff",
|
114
|
+
".woff2" => "font/woff2",
|
115
|
+
".ttf" => "font/ttf",
|
116
|
+
".otf" => "font/otf",
|
117
|
+
|
118
|
+
# Archives
|
119
|
+
".zip" => "application/zip",
|
120
|
+
".tar" => "application/x-tar",
|
121
|
+
".gz" => "application/gzip",
|
122
|
+
".bz2" => "application/x-bzip2",
|
123
|
+
".xz" => "application/x-xz",
|
124
|
+
".rar" => "application/vnd.rar",
|
125
|
+
".7z" => "application/x-7z-compressed",
|
126
|
+
".tar.gz" => "application/gzip",
|
127
|
+
".tar.bz2" => "application/x-bzip2",
|
128
|
+
".apk" => "application/vnd.android.package-archive",
|
129
|
+
".exe" => "application/x-msdownload",
|
130
|
+
|
131
|
+
# Others
|
132
|
+
".ics" => "text/calendar",
|
133
|
+
".vcf" => "text/vcard"
|
52
134
|
}
|
53
135
|
end
|
54
136
|
end
|
data/lib/llm/provider.rb
CHANGED
@@ -22,6 +22,8 @@ class LLM::Anthropic
|
|
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
|
@@ -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"
|
@@ -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
|
@@ -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
|
@@ -39,7 +39,7 @@ class LLM::XAI
|
|
39
39
|
# @param [Hash] params Other parameters (see xAI docs)
|
40
40
|
# @raise (see LLM::Provider#request)
|
41
41
|
# @return [LLM::Response]
|
42
|
-
def create(model: "grok-2-image-1212", **)
|
42
|
+
def create(prompt:, model: "grok-2-image-1212", **params)
|
43
43
|
super
|
44
44
|
end
|
45
45
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Array LLM::Schema::Array} class represents an
|
6
6
|
# array value in a JSON schema. It is a subclass of
|
7
|
-
# {
|
7
|
+
# {LLM::Schema::Leaf LLM::Schema::Leaf} and provides methods that
|
8
8
|
# can act as constraints.
|
9
9
|
class Array < Leaf
|
10
10
|
def initialize(items)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Boolean LLM::Schema::Boolean} class represents a
|
6
6
|
# boolean value in a JSON schema. It is a subclass of
|
7
|
-
# {
|
7
|
+
# {LLM::Schema::Leaf LLM::Schema::Leaf}.
|
8
8
|
class Boolean < Leaf
|
9
9
|
def to_h
|
10
10
|
super.merge!({type: "boolean"})
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Integer LLM::Schema::Integer} class represents a
|
6
6
|
# whole number value in a JSON schema. It is a subclass of
|
7
|
-
# {
|
7
|
+
# {LLM::Schema::Leaf LLM::Schema::Leaf} and provides methods that
|
8
8
|
# can act as constraints.
|
9
9
|
class Integer < Leaf
|
10
10
|
##
|
11
11
|
# Constrain the number to a minimum value
|
12
12
|
# @param [Integer] i The minimum value
|
13
|
-
# @return [
|
13
|
+
# @return [LLM::Schema::Number] Returns self
|
14
14
|
def min(i)
|
15
15
|
tap { @minimum = i }
|
16
16
|
end
|
@@ -18,7 +18,7 @@ class JSON::Schema
|
|
18
18
|
##
|
19
19
|
# Constrain the number to a maximum value
|
20
20
|
# @param [Integer] i The maximum value
|
21
|
-
# @return [
|
21
|
+
# @return [LLM::Schema::Number] Returns self
|
22
22
|
def max(i)
|
23
23
|
tap { @maximum = i }
|
24
24
|
end
|
@@ -26,7 +26,7 @@ class JSON::Schema
|
|
26
26
|
##
|
27
27
|
# Constrain the number to a multiple of a given value
|
28
28
|
# @param [Integer] i The multiple
|
29
|
-
# @return [
|
29
|
+
# @return [LLM::Schema::Number] Returns self
|
30
30
|
def multiple_of(i)
|
31
31
|
tap { @multiple_of = i }
|
32
32
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Leaf LLM::Schema::Leaf} class is the
|
6
6
|
# superclass of all values that can appear in a JSON schema.
|
7
|
-
# See the instance methods of {
|
8
|
-
# an example of how to create instances of {
|
7
|
+
# See the instance methods of {LLM::Schema LLM::Schema} for
|
8
|
+
# an example of how to create instances of {LLM::Schema::Leaf LLM::Schema::Leaf}
|
9
9
|
# through its subclasses.
|
10
10
|
class Leaf
|
11
11
|
def initialize
|
@@ -19,7 +19,7 @@ class JSON::Schema
|
|
19
19
|
##
|
20
20
|
# Set the description of a leaf
|
21
21
|
# @param [String] str The description
|
22
|
-
# @return [
|
22
|
+
# @return [LLM::Schema::Leaf]
|
23
23
|
def description(str)
|
24
24
|
tap { @description = str }
|
25
25
|
end
|
@@ -27,7 +27,7 @@ class JSON::Schema
|
|
27
27
|
##
|
28
28
|
# Set the default value of a leaf
|
29
29
|
# @param [Object] value The default value
|
30
|
-
# @return [
|
30
|
+
# @return [LLM::Schema::Leaf]
|
31
31
|
def default(value)
|
32
32
|
tap { @default = value }
|
33
33
|
end
|
@@ -36,7 +36,7 @@ class JSON::Schema
|
|
36
36
|
# Set the allowed values of a leaf
|
37
37
|
# @see https://tour.json-schema.org/content/02-Primitive-Types/07-Enumerated-Values-II Enumerated Values
|
38
38
|
# @param [Array] values The allowed values
|
39
|
-
# @return [
|
39
|
+
# @return [LLM::Schema::Leaf]
|
40
40
|
def enum(*values)
|
41
41
|
tap { @enum = values }
|
42
42
|
end
|
@@ -45,14 +45,14 @@ class JSON::Schema
|
|
45
45
|
# Set the value of a leaf to be a constant value
|
46
46
|
# @see https://tour.json-schema.org/content/02-Primitive-Types/08-Defining-Constant-Values Constant Values
|
47
47
|
# @param [Object] value The constant value
|
48
|
-
# @return [
|
48
|
+
# @return [LLM::Schema::Leaf]
|
49
49
|
def const(value)
|
50
50
|
tap { @const = value }
|
51
51
|
end
|
52
52
|
|
53
53
|
##
|
54
54
|
# Denote a leaf as required
|
55
|
-
# @return [
|
55
|
+
# @return [LLM::Schema::Leaf]
|
56
56
|
def required
|
57
57
|
tap { @required = true }
|
58
58
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Null LLM::Schema::Null} class represents a
|
6
6
|
# null value in a JSON schema. It is a subclass of
|
7
|
-
# {
|
7
|
+
# {LLM::Schema::Leaf LLM::Schema::Leaf}.
|
8
8
|
class Null < Leaf
|
9
9
|
def to_h
|
10
10
|
super.merge!({type: "null"})
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Number LLM::Schema::Number} class represents a
|
6
6
|
# a number (either whole or decimal) value in a JSON schema. It is a
|
7
|
-
# subclass of {
|
7
|
+
# subclass of {LLM::Schema::Leaf LLM::Schema::Leaf} and provides
|
8
8
|
# methods that can act as constraints.
|
9
9
|
class Number < Leaf
|
10
10
|
##
|
11
11
|
# Constrain the number to a minimum value
|
12
12
|
# @param [Integer, Float] i The minimum value
|
13
|
-
# @return [
|
13
|
+
# @return [LLM::Schema::Number] Returns self
|
14
14
|
def min(i)
|
15
15
|
tap { @minimum = i }
|
16
16
|
end
|
@@ -18,7 +18,7 @@ class JSON::Schema
|
|
18
18
|
##
|
19
19
|
# Constrain the number to a maximum value
|
20
20
|
# @param [Integer, Float] i The maximum value
|
21
|
-
# @return [
|
21
|
+
# @return [LLM::Schema::Number] Returns self
|
22
22
|
def max(i)
|
23
23
|
tap { @maximum = i }
|
24
24
|
end
|
@@ -26,7 +26,7 @@ class JSON::Schema
|
|
26
26
|
##
|
27
27
|
# Constrain the number to a multiple of a given value
|
28
28
|
# @param [Integer, Float] i The multiple
|
29
|
-
# @return [
|
29
|
+
# @return [LLM::Schema::Number] Returns self
|
30
30
|
def multiple_of(i)
|
31
31
|
tap { @multiple_of = i }
|
32
32
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::Object LLM::Schema::Object} class represents an
|
6
6
|
# object value in a JSON schema. It is a subclass of
|
7
|
-
# {
|
7
|
+
# {LLM::Schema::Leaf LLM::Schema::Leaf} and provides methods that
|
8
8
|
# can act as constraints.
|
9
9
|
class Object < Leaf
|
10
10
|
def initialize(properties)
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
3
|
+
class LLM::Schema
|
4
4
|
##
|
5
|
-
# The {
|
5
|
+
# The {LLM::Schema::String LLM::Schema::String} class represents a
|
6
6
|
# string value in a JSON schema. It is a subclass of
|
7
|
-
# {
|
7
|
+
# {LLM::Schema::Leaf LLM::Schema::Leaf} and provides methods that
|
8
8
|
# can act as constraints.
|
9
9
|
class String < Leaf
|
10
10
|
##
|
11
11
|
# Constrain the string to a minimum length
|
12
12
|
# @param [Integer] i The minimum length
|
13
|
-
# @return [
|
13
|
+
# @return [LLM::Schema::String] Returns self
|
14
14
|
def min(i)
|
15
15
|
tap { @minimum = i }
|
16
16
|
end
|
@@ -18,7 +18,7 @@ class JSON::Schema
|
|
18
18
|
##
|
19
19
|
# Constrain the string to a maximum length
|
20
20
|
# @param [Integer] i The maximum length
|
21
|
-
# @return [
|
21
|
+
# @return [LLM::Schema::String] Returns self
|
22
22
|
def max(i)
|
23
23
|
tap { @maximum = i }
|
24
24
|
end
|
@@ -1,10 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module JSON
|
4
|
-
end unless defined?(JSON)
|
5
|
-
|
6
3
|
##
|
7
|
-
# The {
|
4
|
+
# The {LLM::Schema LLM::Schema} class represents a JSON schema,
|
8
5
|
# and provides methods that let you describe and produce a schema
|
9
6
|
# that can be used in various contexts that include the validation
|
10
7
|
# and generation of JSON data.
|
@@ -13,14 +10,14 @@ end unless defined?(JSON)
|
|
13
10
|
# @see https://tour.json-schema.org/ JSON Schema Tour
|
14
11
|
#
|
15
12
|
# @example
|
16
|
-
# schema =
|
13
|
+
# schema = LLM::Schema.new
|
17
14
|
# schema.object({
|
18
15
|
# name: schema.string.enum("John", "Jane").required,
|
19
16
|
# age: schema.integer.required,
|
20
17
|
# hobbies: schema.array(schema.string, schema.null).required,
|
21
18
|
# address: schema.object({street: schema.string}).required,
|
22
19
|
# })
|
23
|
-
class
|
20
|
+
class LLM::Schema
|
24
21
|
require_relative "schema/version"
|
25
22
|
require_relative "schema/leaf"
|
26
23
|
require_relative "schema/object"
|
@@ -34,7 +31,7 @@ class JSON::Schema
|
|
34
31
|
##
|
35
32
|
# Returns an object
|
36
33
|
# @param [Hash] properties A hash of properties
|
37
|
-
# @return [
|
34
|
+
# @return [LLM::Schema::Object]
|
38
35
|
def object(properties)
|
39
36
|
Object.new(properties)
|
40
37
|
end
|
@@ -42,42 +39,42 @@ class JSON::Schema
|
|
42
39
|
##
|
43
40
|
# Returns an array
|
44
41
|
# @param [Array] items An array of items
|
45
|
-
# @return [
|
42
|
+
# @return [LLM::Schema::Array]
|
46
43
|
def array(*items)
|
47
44
|
Array.new(*items)
|
48
45
|
end
|
49
46
|
|
50
47
|
##
|
51
48
|
# Returns a string
|
52
|
-
# @return [
|
49
|
+
# @return [LLM::Schema::String]
|
53
50
|
def string
|
54
51
|
String.new
|
55
52
|
end
|
56
53
|
|
57
54
|
##
|
58
55
|
# Returns a number
|
59
|
-
# @return [
|
56
|
+
# @return [LLM::Schema::Number] a number
|
60
57
|
def number
|
61
58
|
Number.new
|
62
59
|
end
|
63
60
|
|
64
61
|
##
|
65
62
|
# Returns an integer
|
66
|
-
# @return [
|
63
|
+
# @return [LLM::Schema::Integer]
|
67
64
|
def integer
|
68
65
|
Integer.new
|
69
66
|
end
|
70
67
|
|
71
68
|
##
|
72
69
|
# Returns a boolean
|
73
|
-
# @return [
|
70
|
+
# @return [LLM::Schema::Boolean]
|
74
71
|
def boolean
|
75
72
|
Boolean.new
|
76
73
|
end
|
77
74
|
|
78
75
|
##
|
79
76
|
# Returns null
|
80
|
-
# @return [
|
77
|
+
# @return [LLM::Schema::Null]
|
81
78
|
def null
|
82
79
|
Null.new
|
83
80
|
end
|
data/lib/llm/version.rb
CHANGED
data/lib/llm.rb
CHANGED
data/llm.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
llm.rb is a zero-dependency Ruby toolkit for Large Language Models that
|
13
13
|
includes OpenAI, Gemini, Anthropic, xAI (grok), DeepSeek, Ollama, and
|
14
14
|
LlamaCpp. The toolkit includes full support for chat, streaming, tool calling,
|
15
|
-
audio, images, files, and JSON Schema
|
15
|
+
audio, images, files, and structured outputs (JSON Schema).
|
16
16
|
SUMMARY
|
17
17
|
|
18
18
|
spec.description = spec.summary
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: llm.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Antar Azri
|
@@ -153,7 +153,7 @@ dependencies:
|
|
153
153
|
description: llm.rb is a zero-dependency Ruby toolkit for Large Language Models that
|
154
154
|
includes OpenAI, Gemini, Anthropic, xAI (grok), DeepSeek, Ollama, and LlamaCpp.
|
155
155
|
The toolkit includes full support for chat, streaming, tool calling, audio, images,
|
156
|
-
files, and JSON Schema
|
156
|
+
files, and structured outputs (JSON Schema).
|
157
157
|
email:
|
158
158
|
- azantar@proton.me
|
159
159
|
- 0x1eef@proton.me
|
@@ -177,16 +177,6 @@ files:
|
|
177
177
|
- lib/llm/eventstream/parser.rb
|
178
178
|
- lib/llm/file.rb
|
179
179
|
- lib/llm/function.rb
|
180
|
-
- lib/llm/json/schema.rb
|
181
|
-
- lib/llm/json/schema/array.rb
|
182
|
-
- lib/llm/json/schema/boolean.rb
|
183
|
-
- lib/llm/json/schema/integer.rb
|
184
|
-
- lib/llm/json/schema/leaf.rb
|
185
|
-
- lib/llm/json/schema/null.rb
|
186
|
-
- lib/llm/json/schema/number.rb
|
187
|
-
- lib/llm/json/schema/object.rb
|
188
|
-
- lib/llm/json/schema/string.rb
|
189
|
-
- lib/llm/json/schema/version.rb
|
190
180
|
- lib/llm/message.rb
|
191
181
|
- lib/llm/mime.rb
|
192
182
|
- lib/llm/multipart.rb
|
@@ -250,6 +240,16 @@ files:
|
|
250
240
|
- lib/llm/providers/xai.rb
|
251
241
|
- lib/llm/providers/xai/images.rb
|
252
242
|
- lib/llm/response.rb
|
243
|
+
- lib/llm/schema.rb
|
244
|
+
- lib/llm/schema/array.rb
|
245
|
+
- lib/llm/schema/boolean.rb
|
246
|
+
- lib/llm/schema/integer.rb
|
247
|
+
- lib/llm/schema/leaf.rb
|
248
|
+
- lib/llm/schema/null.rb
|
249
|
+
- lib/llm/schema/number.rb
|
250
|
+
- lib/llm/schema/object.rb
|
251
|
+
- lib/llm/schema/string.rb
|
252
|
+
- lib/llm/schema/version.rb
|
253
253
|
- lib/llm/utils.rb
|
254
254
|
- lib/llm/version.rb
|
255
255
|
- llm.gemspec
|
@@ -278,5 +278,5 @@ specification_version: 4
|
|
278
278
|
summary: llm.rb is a zero-dependency Ruby toolkit for Large Language Models that includes
|
279
279
|
OpenAI, Gemini, Anthropic, xAI (grok), DeepSeek, Ollama, and LlamaCpp. The toolkit
|
280
280
|
includes full support for chat, streaming, tool calling, audio, images, files, and
|
281
|
-
JSON Schema
|
281
|
+
structured outputs (JSON Schema).
|
282
282
|
test_files: []
|