llm.rb 11.0.0 → 11.2.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/CHANGELOG.md +126 -1
- data/README.md +58 -18
- data/lib/llm/a2a/transport/http.rb +9 -8
- data/lib/llm/a2a.rb +14 -7
- data/lib/llm/agent.rb +6 -3
- data/lib/llm/context.rb +41 -6
- data/lib/llm/function/array.rb +6 -0
- data/lib/llm/function.rb +38 -4
- data/lib/llm/json_adapter.rb +8 -2
- data/lib/llm/mcp/transport/http.rb +7 -5
- data/lib/llm/mcp.rb +6 -7
- data/lib/llm/object/builder.rb +1 -0
- data/lib/llm/object.rb +9 -0
- data/lib/llm/provider.rb +1 -18
- data/lib/llm/providers/anthropic/files.rb +6 -6
- data/lib/llm/providers/anthropic/models.rb +1 -1
- data/lib/llm/providers/anthropic.rb +1 -1
- data/lib/llm/providers/bedrock/models.rb +4 -4
- data/lib/llm/providers/bedrock/signature.rb +3 -3
- data/lib/llm/providers/bedrock.rb +1 -1
- data/lib/llm/providers/google/files.rb +5 -5
- data/lib/llm/providers/google/images.rb +1 -1
- data/lib/llm/providers/google/models.rb +1 -1
- data/lib/llm/providers/google.rb +2 -2
- data/lib/llm/providers/ollama/models.rb +1 -1
- data/lib/llm/providers/ollama.rb +2 -2
- data/lib/llm/providers/openai/audio.rb +3 -3
- data/lib/llm/providers/openai/files.rb +5 -5
- data/lib/llm/providers/openai/images.rb +3 -3
- data/lib/llm/providers/openai/models.rb +1 -1
- data/lib/llm/providers/openai/moderations.rb +1 -1
- data/lib/llm/providers/openai/responses.rb +3 -3
- data/lib/llm/providers/openai/vector_stores.rb +11 -11
- data/lib/llm/providers/openai.rb +2 -2
- data/lib/llm/schema.rb +23 -5
- data/lib/llm/skill.rb +44 -14
- data/lib/llm/tool.rb +21 -0
- data/lib/llm/tracer/telemetry.rb +3 -1
- data/lib/llm/transport/curb.rb +246 -0
- data/lib/llm/transport/execution.rb +1 -1
- data/lib/llm/transport/http.rb +9 -4
- data/lib/llm/transport/net_http_adapter.rb +61 -0
- data/lib/llm/transport/persistent_http.rb +10 -5
- data/lib/llm/transport/request.rb +121 -0
- data/lib/llm/transport/response/curb.rb +112 -0
- data/lib/llm/transport/response.rb +1 -0
- data/lib/llm/transport/utils.rb +42 -17
- data/lib/llm/transport.rb +17 -45
- data/lib/llm/version.rb +1 -1
- data/llm.gemspec +3 -3
- metadata +8 -4
|
@@ -16,16 +16,18 @@ module LLM::MCP::Transport
|
|
|
16
16
|
# Extra headers to send with requests
|
|
17
17
|
# @param [Integer, nil] timeout
|
|
18
18
|
# The timeout in seconds. Defaults to nil
|
|
19
|
-
# @param [
|
|
20
|
-
#
|
|
19
|
+
# @param [Boolean] persistent
|
|
20
|
+
# Whether to use persistent HTTP connections
|
|
21
|
+
# @param [LLM::Transport, Class, Symbol, nil] transport
|
|
22
|
+
# Optional override with any {LLM::Transport} instance, subclass, or shortcut
|
|
21
23
|
# @return [LLM::MCP::Transport::HTTP]
|
|
22
|
-
def initialize(url:, headers: {}, timeout: nil, transport: nil)
|
|
24
|
+
def initialize(url:, headers: {}, timeout: nil, persistent: false, transport: nil)
|
|
23
25
|
@uri = URI.parse(url)
|
|
24
26
|
@headers = headers
|
|
25
|
-
@transport = resolve_transport(uri, transport, timeout)
|
|
26
27
|
@queue = []
|
|
27
28
|
@monitor = Monitor.new
|
|
28
29
|
@running = false
|
|
30
|
+
@transport = resolve_transport(host: uri.host, port: uri.port, ssl: uri.scheme == "https", timeout:, persistent:, transport:)
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
##
|
|
@@ -62,7 +64,7 @@ module LLM::MCP::Transport
|
|
|
62
64
|
# @return [void]
|
|
63
65
|
def write(message)
|
|
64
66
|
raise LLM::MCP::Error, "MCP transport is not running" unless running?
|
|
65
|
-
req =
|
|
67
|
+
req = LLM::Transport::Request.post(uri.request_uri, headers.merge("content-type" => "application/json"))
|
|
66
68
|
req.body = LLM.json.dump(message)
|
|
67
69
|
res = transport.request(req, owner: self) { consume(_1) }
|
|
68
70
|
res = LLM::Transport::Response.from(res)
|
data/lib/llm/mcp.rb
CHANGED
|
@@ -55,9 +55,11 @@ class LLM::MCP
|
|
|
55
55
|
# The URL for the MCP HTTP endpoint
|
|
56
56
|
# @option http [Hash] :headers
|
|
57
57
|
# Extra headers for requests
|
|
58
|
-
# @option http [
|
|
59
|
-
#
|
|
60
|
-
#
|
|
58
|
+
# @option http [Boolean] :persistent
|
|
59
|
+
# Whether to use persistent HTTP connections
|
|
60
|
+
# @option http [LLM::Transport, Class, Symbol] :transport
|
|
61
|
+
# Optional override with any {LLM::Transport} instance, subclass, or
|
|
62
|
+
# shortcut, similar to {LLM::Provider}
|
|
61
63
|
# @param [Integer] timeout
|
|
62
64
|
# The maximum amount of time to wait when reading from an MCP process
|
|
63
65
|
# @return [LLM::MCP] A new MCP instance
|
|
@@ -69,10 +71,7 @@ class LLM::MCP
|
|
|
69
71
|
@command = Command.new(**stdio)
|
|
70
72
|
@transport = Transport::Stdio.new(command:)
|
|
71
73
|
elsif http
|
|
72
|
-
|
|
73
|
-
transport = http.delete(:transport)
|
|
74
|
-
transport ||= LLM::Transport::PersistentHTTP if persistent
|
|
75
|
-
@transport = Transport::HTTP.new(**http, timeout:, transport:)
|
|
74
|
+
@transport = Transport::HTTP.new(**http, timeout:)
|
|
76
75
|
else
|
|
77
76
|
raise ArgumentError, "stdio or http is required"
|
|
78
77
|
end
|
data/lib/llm/object/builder.rb
CHANGED
data/lib/llm/object.rb
CHANGED
|
@@ -184,6 +184,15 @@ class LLM::Object < BasicObject
|
|
|
184
184
|
@h.slice(*args)
|
|
185
185
|
end
|
|
186
186
|
|
|
187
|
+
##
|
|
188
|
+
# @param [Hash, #to_h] other
|
|
189
|
+
# @return [Boolean]
|
|
190
|
+
def ==(other)
|
|
191
|
+
return false unless other.respond_to?(:to_h)
|
|
192
|
+
to_h == other.to_h || to_hash == other.to_h
|
|
193
|
+
end
|
|
194
|
+
alias_method :eql?, :==
|
|
195
|
+
|
|
187
196
|
private
|
|
188
197
|
|
|
189
198
|
def method_missing(m, *args, &b)
|
data/lib/llm/provider.rb
CHANGED
|
@@ -35,7 +35,7 @@ class LLM::Provider
|
|
|
35
35
|
@base_path = LLM::Utils.normalize_base_path(base_path)
|
|
36
36
|
@base_uri = URI("#{ssl ? "https" : "http"}://#{host}:#{port}/")
|
|
37
37
|
@headers = {"User-Agent" => "llm.rb v#{LLM::VERSION}"}
|
|
38
|
-
@transport = resolve_transport(transport
|
|
38
|
+
@transport = LLM::Transport::Utils.resolve_transport(host:, port:, timeout:, ssl:, transport:, persistent:)
|
|
39
39
|
@monitor = Monitor.new
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -417,23 +417,6 @@ class LLM::Provider
|
|
|
417
417
|
@monitor.synchronize(&)
|
|
418
418
|
end
|
|
419
419
|
|
|
420
|
-
##
|
|
421
|
-
# @api private
|
|
422
|
-
def default_transport(persistent:)
|
|
423
|
-
transport_class = persistent ? LLM::Transport::PersistentHTTP : LLM::Transport::HTTP
|
|
424
|
-
transport_class.new(host:, port:, timeout:, ssl:)
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
##
|
|
428
|
-
# @api private
|
|
429
|
-
def resolve_transport(transport, persistent:)
|
|
430
|
-
return default_transport(persistent:) if transport.nil?
|
|
431
|
-
if Class === transport && transport <= LLM::Transport
|
|
432
|
-
return transport.new(host:, port:, timeout:, ssl:)
|
|
433
|
-
end
|
|
434
|
-
transport
|
|
435
|
-
end
|
|
436
|
-
|
|
437
420
|
##
|
|
438
421
|
# @api private
|
|
439
422
|
def thread
|
|
@@ -37,7 +37,7 @@ class LLM::Anthropic
|
|
|
37
37
|
# @return [LLM::Response]
|
|
38
38
|
def all(**params)
|
|
39
39
|
query = URI.encode_www_form(params)
|
|
40
|
-
req =
|
|
40
|
+
req = LLM::Transport::Request.get("/v1/files?#{query}", headers)
|
|
41
41
|
res, span, tracer = execute(request: req, operation: "request")
|
|
42
42
|
res = ResponseAdapter.adapt(res, type: :enumerable)
|
|
43
43
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -56,7 +56,7 @@ class LLM::Anthropic
|
|
|
56
56
|
# @return [LLM::Response]
|
|
57
57
|
def create(file:, **params)
|
|
58
58
|
multi = LLM::Multipart.new(params.merge!(file: LLM.File(file)))
|
|
59
|
-
req =
|
|
59
|
+
req = LLM::Transport::Request.post("/v1/files", headers)
|
|
60
60
|
req["content-type"] = multi.content_type
|
|
61
61
|
transport.set_body_stream(req, multi.body)
|
|
62
62
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -79,7 +79,7 @@ class LLM::Anthropic
|
|
|
79
79
|
def get(file:, **params)
|
|
80
80
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
81
81
|
query = URI.encode_www_form(params)
|
|
82
|
-
req =
|
|
82
|
+
req = LLM::Transport::Request.get("/v1/files/#{file_id}?#{query}", headers)
|
|
83
83
|
res, span, tracer = execute(request: req, operation: "request")
|
|
84
84
|
res = ResponseAdapter.adapt(res, type: :file)
|
|
85
85
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -100,7 +100,7 @@ class LLM::Anthropic
|
|
|
100
100
|
def get_metadata(file:, **params)
|
|
101
101
|
query = URI.encode_www_form(params)
|
|
102
102
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
103
|
-
req =
|
|
103
|
+
req = LLM::Transport::Request.get("/v1/files/#{file_id}?#{query}", headers)
|
|
104
104
|
res, span, tracer = execute(request: req, operation: "request")
|
|
105
105
|
res = ResponseAdapter.adapt(res, type: :file)
|
|
106
106
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -120,7 +120,7 @@ class LLM::Anthropic
|
|
|
120
120
|
# @return [LLM::Response]
|
|
121
121
|
def delete(file:)
|
|
122
122
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
123
|
-
req =
|
|
123
|
+
req = LLM::Transport::Request.delete("/v1/files/#{file_id}", headers)
|
|
124
124
|
res, span, tracer = execute(request: req, operation: "request")
|
|
125
125
|
res = LLM::Response.new(res)
|
|
126
126
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -145,7 +145,7 @@ class LLM::Anthropic
|
|
|
145
145
|
def download(file:, **params)
|
|
146
146
|
query = URI.encode_www_form(params)
|
|
147
147
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
148
|
-
req =
|
|
148
|
+
req = LLM::Transport::Request.get("/v1/files/#{file_id}/content?#{query}", headers)
|
|
149
149
|
io = StringIO.new("".b)
|
|
150
150
|
res, span, tracer = execute(request: req, operation: "request") { |res| res.read_body { |chunk| io << chunk } }
|
|
151
151
|
res = LLM::Response.new(res).tap { _1.define_singleton_method(:file) { io } }
|
|
@@ -39,7 +39,7 @@ class LLM::Anthropic
|
|
|
39
39
|
# @return [LLM::Response]
|
|
40
40
|
def all(**params)
|
|
41
41
|
query = URI.encode_www_form(params)
|
|
42
|
-
req =
|
|
42
|
+
req = LLM::Transport::Request.get("/v1/models?#{query}", headers)
|
|
43
43
|
res, span, tracer = execute(request: req, operation: "request")
|
|
44
44
|
res = ResponseAdapter.adapt(res, type: :models)
|
|
45
45
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -160,7 +160,7 @@ module LLM
|
|
|
160
160
|
messages = build_complete_messages(prompt, params, role)
|
|
161
161
|
payload = adapt(messages)
|
|
162
162
|
body = LLM.json.dump(payload.merge!(params))
|
|
163
|
-
req =
|
|
163
|
+
req = LLM::Transport::Request.post("/v1/messages", headers)
|
|
164
164
|
transport.set_body_stream(req, StringIO.new(body))
|
|
165
165
|
req
|
|
166
166
|
end
|
|
@@ -57,13 +57,13 @@ class LLM::Bedrock
|
|
|
57
57
|
##
|
|
58
58
|
# @param [String] host
|
|
59
59
|
# @param [Hash] params
|
|
60
|
-
# @return [
|
|
60
|
+
# @return [LLM::Transport::Request]
|
|
61
61
|
def build_request(host, params)
|
|
62
62
|
path = "/foundation-models"
|
|
63
63
|
query = URI.encode_www_form(params) unless params.empty?
|
|
64
64
|
path = "#{path}?#{query}" if query && !query.empty?
|
|
65
65
|
body = ""
|
|
66
|
-
req =
|
|
66
|
+
req = LLM::Transport::Request.get(path, {"Content-Type" => "application/json", "Accept" => "application/json"})
|
|
67
67
|
req.tap { sign!(req, body, host, query) }
|
|
68
68
|
end
|
|
69
69
|
|
|
@@ -84,11 +84,11 @@ class LLM::Bedrock
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
##
|
|
87
|
-
# @param [
|
|
87
|
+
# @param [LLM::Transport::Request] req
|
|
88
88
|
# @param [String] body
|
|
89
89
|
# @param [String] host
|
|
90
90
|
# @param [String, nil] query
|
|
91
|
-
# @return [
|
|
91
|
+
# @return [LLM::Transport::Request]
|
|
92
92
|
def sign!(req, body, host = credentials.host, query = nil)
|
|
93
93
|
creds = credentials.tap { _1.host = host }
|
|
94
94
|
Signature.new(credentials: creds, method: "GET", path: "/foundation-models", query:, body:).sign!(req)
|
|
@@ -8,7 +8,7 @@ class LLM::Bedrock
|
|
|
8
8
|
# Signs HTTP requests and headers with AWS Signature V4.
|
|
9
9
|
#
|
|
10
10
|
# Returns the signed headers as a Hash through #to_h, ready to merge
|
|
11
|
-
# into
|
|
11
|
+
# into an {LLM::Transport::Request} or other HTTP client. Everything else is
|
|
12
12
|
# private.
|
|
13
13
|
#
|
|
14
14
|
# Uses only Ruby's stdlib (openssl, digest) with no external deps.
|
|
@@ -89,8 +89,8 @@ class LLM::Bedrock
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
##
|
|
92
|
-
# @param [
|
|
93
|
-
# @return [
|
|
92
|
+
# @param [LLM::Transport::Request] req
|
|
93
|
+
# @return [LLM::Transport::Request]
|
|
94
94
|
def sign!(req)
|
|
95
95
|
to_h.each { |k, v| req[k] = v }
|
|
96
96
|
req
|
|
@@ -217,7 +217,7 @@ module LLM
|
|
|
217
217
|
body = LLM.json.dump(payload)
|
|
218
218
|
path = stream ? "/model/#{model_id}/converse-stream" \
|
|
219
219
|
: "/model/#{model_id}/converse"
|
|
220
|
-
req =
|
|
220
|
+
req = LLM::Transport::Request.post(path, headers)
|
|
221
221
|
transport.set_body_stream(req, StringIO.new(body))
|
|
222
222
|
[req, messages, body]
|
|
223
223
|
end
|
|
@@ -45,7 +45,7 @@ class LLM::Google
|
|
|
45
45
|
# @return [LLM::Response]
|
|
46
46
|
def all(**params)
|
|
47
47
|
query = URI.encode_www_form(params.merge!(key: key))
|
|
48
|
-
req =
|
|
48
|
+
req = LLM::Transport::Request.get("/v1beta/files?#{query}", headers)
|
|
49
49
|
res, span, tracer = execute(request: req, operation: "request")
|
|
50
50
|
res = ResponseAdapter.adapt(res, type: :files)
|
|
51
51
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -64,7 +64,7 @@ class LLM::Google
|
|
|
64
64
|
# @return [LLM::Response]
|
|
65
65
|
def create(file:, **params)
|
|
66
66
|
file = LLM.File(file)
|
|
67
|
-
req =
|
|
67
|
+
req = LLM::Transport::Request.post(request_upload_url(file:), {})
|
|
68
68
|
req["content-length"] = file.bytesize
|
|
69
69
|
req["X-Goog-Upload-Offset"] = 0
|
|
70
70
|
req["X-Goog-Upload-Command"] = "upload, finalize"
|
|
@@ -91,7 +91,7 @@ class LLM::Google
|
|
|
91
91
|
def get(file:, **params)
|
|
92
92
|
file_id = file.respond_to?(:name) ? file.name : file.to_s
|
|
93
93
|
query = URI.encode_www_form(params.merge!(key: key))
|
|
94
|
-
req =
|
|
94
|
+
req = LLM::Transport::Request.get("/v1beta/#{file_id}?#{query}", headers)
|
|
95
95
|
res, span, tracer = execute(request: req, operation: "request")
|
|
96
96
|
res = ResponseAdapter.adapt(res, type: :file)
|
|
97
97
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -111,7 +111,7 @@ class LLM::Google
|
|
|
111
111
|
def delete(file:, **params)
|
|
112
112
|
file_id = file.respond_to?(:name) ? file.name : file.to_s
|
|
113
113
|
query = URI.encode_www_form(params.merge!(key: key))
|
|
114
|
-
req =
|
|
114
|
+
req = LLM::Transport::Request.delete("/v1beta/#{file_id}?#{query}", headers)
|
|
115
115
|
res, span, tracer = execute(request: req, operation: "request")
|
|
116
116
|
res = LLM::Response.new(res)
|
|
117
117
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -128,7 +128,7 @@ class LLM::Google
|
|
|
128
128
|
private
|
|
129
129
|
|
|
130
130
|
def request_upload_url(file:)
|
|
131
|
-
req =
|
|
131
|
+
req = LLM::Transport::Request.post("/upload/v1beta/files?key=#{key}", headers)
|
|
132
132
|
req["X-Goog-Upload-Protocol"] = "resumable"
|
|
133
133
|
req["X-Goog-Upload-Command"] = "start"
|
|
134
134
|
req["X-Goog-Upload-Header-Content-Length"] = file.bytesize
|
|
@@ -40,7 +40,7 @@ class LLM::Google
|
|
|
40
40
|
# @raise (see LLM::Provider#request)
|
|
41
41
|
# @return [LLM::Response]
|
|
42
42
|
def create(prompt:, n: 1, image_size: nil, aspect_ratio: nil, person_generation: nil, model: "imagen-4.0-generate-001", **params)
|
|
43
|
-
req =
|
|
43
|
+
req = LLM::Transport::Request.post("/v1beta/models/#{model}:predict?key=#{key}", headers)
|
|
44
44
|
body = LLM.json.dump({
|
|
45
45
|
parameters: {
|
|
46
46
|
sampleCount: n,
|
|
@@ -39,7 +39,7 @@ class LLM::Google
|
|
|
39
39
|
# @return [LLM::Response]
|
|
40
40
|
def all(**params)
|
|
41
41
|
query = URI.encode_www_form(params.merge!(key: key))
|
|
42
|
-
req =
|
|
42
|
+
req = LLM::Transport::Request.get("/v1beta/models?#{query}", headers)
|
|
43
43
|
res, span, tracer = execute(request: req, operation: "request")
|
|
44
44
|
res = ResponseAdapter.adapt(res, type: :models)
|
|
45
45
|
tracer.on_request_finish(operation: "request", res:, span:)
|
data/lib/llm/providers/google.rb
CHANGED
|
@@ -56,7 +56,7 @@ module LLM
|
|
|
56
56
|
def embed(input, model: "gemini-embedding-001", **params)
|
|
57
57
|
model = model.respond_to?(:id) ? model.id : model
|
|
58
58
|
path = ["/v1beta/models/#{model}", "embedContent?key=#{@key}"].join(":")
|
|
59
|
-
req =
|
|
59
|
+
req = LLM::Transport::Request.post(path, headers)
|
|
60
60
|
req.body = LLM.json.dump({content: {parts: [{text: input}]}})
|
|
61
61
|
res, span, tracer = execute(request: req, operation: "embeddings", model:)
|
|
62
62
|
res = ResponseAdapter.adapt(res, type: :embedding)
|
|
@@ -205,7 +205,7 @@ module LLM
|
|
|
205
205
|
action = stream ? "streamGenerateContent?key=#{@key}&alt=sse" : "generateContent?key=#{@key}"
|
|
206
206
|
model.respond_to?(:id) ? model.id : model
|
|
207
207
|
path = ["/v1beta/models/#{model}", action].join(":")
|
|
208
|
-
req =
|
|
208
|
+
req = LLM::Transport::Request.post(path, headers)
|
|
209
209
|
messages = build_complete_messages(prompt, params, role)
|
|
210
210
|
body = LLM.json.dump({contents: adapt(messages)}.merge!(params))
|
|
211
211
|
transport.set_body_stream(req, StringIO.new(body))
|
|
@@ -40,7 +40,7 @@ class LLM::Ollama
|
|
|
40
40
|
# @return [LLM::Response]
|
|
41
41
|
def all(**params)
|
|
42
42
|
query = URI.encode_www_form(params)
|
|
43
|
-
req =
|
|
43
|
+
req = LLM::Transport::Request.get("/api/tags?#{query}", headers)
|
|
44
44
|
res, span, tracer = execute(request: req, operation: "request")
|
|
45
45
|
res = ResponseAdapter.adapt(res, type: :models)
|
|
46
46
|
tracer.on_request_finish(operation: "request", res:, span:)
|
data/lib/llm/providers/ollama.rb
CHANGED
|
@@ -48,7 +48,7 @@ module LLM
|
|
|
48
48
|
# @return [LLM::Response]
|
|
49
49
|
def embed(input, model: default_model, **params)
|
|
50
50
|
params = {model:}.merge!(params)
|
|
51
|
-
req =
|
|
51
|
+
req = LLM::Transport::Request.post("/v1/embeddings", headers)
|
|
52
52
|
req.body = LLM.json.dump({input:}.merge!(params))
|
|
53
53
|
res, span, tracer = execute(request: req, operation: "embeddings", model:)
|
|
54
54
|
res = ResponseAdapter.adapt(res, type: :embedding)
|
|
@@ -129,7 +129,7 @@ module LLM
|
|
|
129
129
|
def build_complete_request(prompt, params, role)
|
|
130
130
|
messages = build_complete_messages(prompt, params, role)
|
|
131
131
|
body = LLM.json.dump({messages: [adapt(messages)].flatten}.merge!(params))
|
|
132
|
-
req =
|
|
132
|
+
req = LLM::Transport::Request.post("/api/chat", headers)
|
|
133
133
|
transport.set_body_stream(req, StringIO.new(body))
|
|
134
134
|
req
|
|
135
135
|
end
|
|
@@ -32,7 +32,7 @@ class LLM::OpenAI
|
|
|
32
32
|
# @raise (see LLM::Provider#request)
|
|
33
33
|
# @return [LLM::Response]
|
|
34
34
|
def create_speech(input:, voice: "alloy", model: "gpt-4o-mini-tts", response_format: "mp3", **params)
|
|
35
|
-
req =
|
|
35
|
+
req = LLM::Transport::Request.post(path("/audio/speech"), headers)
|
|
36
36
|
req.body = LLM.json.dump({input:, voice:, model:, response_format:}.merge!(params))
|
|
37
37
|
io = StringIO.new("".b)
|
|
38
38
|
res, span, tracer = execute(request: req, operation: "request") { _1.read_body { |chunk| io << chunk } }
|
|
@@ -55,7 +55,7 @@ class LLM::OpenAI
|
|
|
55
55
|
# @return [LLM::Response]
|
|
56
56
|
def create_transcription(file:, model: "whisper-1", **params)
|
|
57
57
|
multi = LLM::Multipart.new(params.merge!(file: LLM.File(file), model:))
|
|
58
|
-
req =
|
|
58
|
+
req = LLM::Transport::Request.post(path("/audio/transcriptions"), headers)
|
|
59
59
|
req["content-type"] = multi.content_type
|
|
60
60
|
transport.set_body_stream(req, multi.body)
|
|
61
61
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -79,7 +79,7 @@ class LLM::OpenAI
|
|
|
79
79
|
# @return [LLM::Response]
|
|
80
80
|
def create_translation(file:, model: "whisper-1", **params)
|
|
81
81
|
multi = LLM::Multipart.new(params.merge!(file: LLM.File(file), model:))
|
|
82
|
-
req =
|
|
82
|
+
req = LLM::Transport::Request.post(path("/audio/translations"), headers)
|
|
83
83
|
req["content-type"] = multi.content_type
|
|
84
84
|
transport.set_body_stream(req, multi.body)
|
|
85
85
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -40,7 +40,7 @@ class LLM::OpenAI
|
|
|
40
40
|
# @return [LLM::Response]
|
|
41
41
|
def all(**params)
|
|
42
42
|
query = URI.encode_www_form(params)
|
|
43
|
-
req =
|
|
43
|
+
req = LLM::Transport::Request.get(path("/files?#{query}"), headers)
|
|
44
44
|
res, span, tracer = execute(request: req, operation: "request")
|
|
45
45
|
res = ResponseAdapter.adapt(res, type: :enumerable)
|
|
46
46
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -60,7 +60,7 @@ class LLM::OpenAI
|
|
|
60
60
|
# @return [LLM::Response]
|
|
61
61
|
def create(file:, purpose: "assistants", **params)
|
|
62
62
|
multi = LLM::Multipart.new(params.merge!(file: LLM.File(file), purpose:))
|
|
63
|
-
req =
|
|
63
|
+
req = LLM::Transport::Request.post(path("/files"), headers)
|
|
64
64
|
req["content-type"] = multi.content_type
|
|
65
65
|
transport.set_body_stream(req, multi.body)
|
|
66
66
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -83,7 +83,7 @@ class LLM::OpenAI
|
|
|
83
83
|
def get(file:, **params)
|
|
84
84
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
85
85
|
query = URI.encode_www_form(params)
|
|
86
|
-
req =
|
|
86
|
+
req = LLM::Transport::Request.get(path("/files/#{file_id}?#{query}"), headers)
|
|
87
87
|
res, span, tracer = execute(request: req, operation: "request")
|
|
88
88
|
res = ResponseAdapter.adapt(res, type: :file)
|
|
89
89
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -105,7 +105,7 @@ class LLM::OpenAI
|
|
|
105
105
|
def download(file:, **params)
|
|
106
106
|
query = URI.encode_www_form(params)
|
|
107
107
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
108
|
-
req =
|
|
108
|
+
req = LLM::Transport::Request.get(path("/files/#{file_id}/content?#{query}"), headers)
|
|
109
109
|
io = StringIO.new("".b)
|
|
110
110
|
res, span, tracer = execute(request: req, operation: "request") { |res| res.read_body { |chunk| io << chunk } }
|
|
111
111
|
res = LLM::Response.new(res).tap { _1.define_singleton_method(:file) { io } }
|
|
@@ -125,7 +125,7 @@ class LLM::OpenAI
|
|
|
125
125
|
# @return [LLM::Response]
|
|
126
126
|
def delete(file:)
|
|
127
127
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
128
|
-
req =
|
|
128
|
+
req = LLM::Transport::Request.delete(path("/files/#{file_id}"), headers)
|
|
129
129
|
res, span, tracer = execute(request: req, operation: "request")
|
|
130
130
|
res = LLM::Response.new(res)
|
|
131
131
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -50,7 +50,7 @@ class LLM::OpenAI
|
|
|
50
50
|
# @raise (see LLM::Provider#request)
|
|
51
51
|
# @return [LLM::Response]
|
|
52
52
|
def create(prompt:, model: "dall-e-3", response_format: "b64_json", **params)
|
|
53
|
-
req =
|
|
53
|
+
req = LLM::Transport::Request.post(path("/images/generations"), headers)
|
|
54
54
|
req.body = LLM.json.dump({prompt:, n: 1, model:, response_format:}.merge!(params))
|
|
55
55
|
res, span, tracer = execute(request: req, operation: "request")
|
|
56
56
|
res = ResponseAdapter.adapt(res, type: :image)
|
|
@@ -76,7 +76,7 @@ class LLM::OpenAI
|
|
|
76
76
|
def create_variation(image:, model: "dall-e-2", response_format: "b64_json", **params)
|
|
77
77
|
image = LLM.File(image)
|
|
78
78
|
multi = LLM::Multipart.new(params.merge!(image:, model:, response_format:))
|
|
79
|
-
req =
|
|
79
|
+
req = LLM::Transport::Request.post(path("/images/variations"), headers)
|
|
80
80
|
req["content-type"] = multi.content_type
|
|
81
81
|
transport.set_body_stream(req, multi.body)
|
|
82
82
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -102,7 +102,7 @@ class LLM::OpenAI
|
|
|
102
102
|
def edit(image:, prompt:, model: "dall-e-2", response_format: "b64_json", **params)
|
|
103
103
|
image = LLM.File(image)
|
|
104
104
|
multi = LLM::Multipart.new(params.merge!(image:, prompt:, model:, response_format:))
|
|
105
|
-
req =
|
|
105
|
+
req = LLM::Transport::Request.post(path("/images/edits"), headers)
|
|
106
106
|
req["content-type"] = multi.content_type
|
|
107
107
|
transport.set_body_stream(req, multi.body)
|
|
108
108
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -39,7 +39,7 @@ class LLM::OpenAI
|
|
|
39
39
|
# @return [LLM::Response]
|
|
40
40
|
def all(**params)
|
|
41
41
|
query = URI.encode_www_form(params)
|
|
42
|
-
req =
|
|
42
|
+
req = LLM::Transport::Request.get(path("/models?#{query}"), headers)
|
|
43
43
|
res, span, tracer = execute(request: req, operation: "request")
|
|
44
44
|
res = ResponseAdapter.adapt(res, type: :models)
|
|
45
45
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -47,7 +47,7 @@ class LLM::OpenAI
|
|
|
47
47
|
# @param [String, LLM::Model] model The model to use
|
|
48
48
|
# @return [LLM::Response]
|
|
49
49
|
def create(input:, model: "omni-moderation-latest", **params)
|
|
50
|
-
req =
|
|
50
|
+
req = LLM::Transport::Request.post(path("/moderations"), headers)
|
|
51
51
|
input = RequestAdapter::Moderation.new(input).adapt
|
|
52
52
|
req.body = LLM.json.dump({input:, model:}.merge!(params))
|
|
53
53
|
res, span, tracer = execute(request: req, operation: "request")
|
|
@@ -40,7 +40,7 @@ class LLM::OpenAI
|
|
|
40
40
|
params = [params, adapt_schema(params), adapt_tools(tools)].inject({}, &:merge!).compact
|
|
41
41
|
role, stream = params.delete(:role), params.delete(:stream)
|
|
42
42
|
params[:stream] = true if @provider.streamable?(stream) || stream == true
|
|
43
|
-
req =
|
|
43
|
+
req = LLM::Transport::Request.post(path("/responses"), headers)
|
|
44
44
|
messages = build_complete_messages(prompt, params, role)
|
|
45
45
|
@provider.tracer.set_request_metadata(user_input: extract_user_input(messages, fallback: prompt))
|
|
46
46
|
body = LLM.json.dump({input: [adapt(messages, mode: :response)].flatten}.merge!(params))
|
|
@@ -61,7 +61,7 @@ class LLM::OpenAI
|
|
|
61
61
|
def get(response, **params)
|
|
62
62
|
response_id = response.respond_to?(:id) ? response.id : response
|
|
63
63
|
query = URI.encode_www_form(params)
|
|
64
|
-
req =
|
|
64
|
+
req = LLM::Transport::Request.get(path("/responses/#{response_id}?#{query}"), headers)
|
|
65
65
|
res, span, tracer = execute(request: req, operation: "request")
|
|
66
66
|
res = ResponseAdapter.adapt(res, type: :responds)
|
|
67
67
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -76,7 +76,7 @@ class LLM::OpenAI
|
|
|
76
76
|
# @return [LLM::Object] Response body
|
|
77
77
|
def delete(response)
|
|
78
78
|
response_id = response.respond_to?(:id) ? response.id : response
|
|
79
|
-
req =
|
|
79
|
+
req = LLM::Transport::Request.delete(path("/responses/#{response_id}"), headers)
|
|
80
80
|
res, span, tracer = execute(request: req, operation: "request")
|
|
81
81
|
res = LLM::Response.new(res)
|
|
82
82
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -31,7 +31,7 @@ class LLM::OpenAI
|
|
|
31
31
|
# @return [LLM::Response]
|
|
32
32
|
def all(**params)
|
|
33
33
|
query = URI.encode_www_form(params)
|
|
34
|
-
req =
|
|
34
|
+
req = LLM::Transport::Request.get(path("/vector_stores?#{query}"), headers)
|
|
35
35
|
res, span, tracer = execute(request: req, operation: "request")
|
|
36
36
|
res = ResponseAdapter.adapt(res, type: :enumerable)
|
|
37
37
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -47,7 +47,7 @@ class LLM::OpenAI
|
|
|
47
47
|
# @return [LLM::Response]
|
|
48
48
|
# @see https://platform.openai.com/docs/api-reference/vector_stores/create OpenAI docs
|
|
49
49
|
def create(name:, file_ids: nil, **params)
|
|
50
|
-
req =
|
|
50
|
+
req = LLM::Transport::Request.post(path("/vector_stores"), headers)
|
|
51
51
|
req.body = LLM.json.dump(params.merge({name:, file_ids:}).compact)
|
|
52
52
|
res, span, tracer = execute(request: req, operation: "request")
|
|
53
53
|
res = LLM::Response.new(res)
|
|
@@ -72,7 +72,7 @@ class LLM::OpenAI
|
|
|
72
72
|
# @see https://platform.openai.com/docs/api-reference/vector_stores/retrieve OpenAI docs
|
|
73
73
|
def get(vector:)
|
|
74
74
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
75
|
-
req =
|
|
75
|
+
req = LLM::Transport::Request.get(path("/vector_stores/#{vector_id}"), headers)
|
|
76
76
|
res, span, tracer = execute(request: req, operation: "request")
|
|
77
77
|
res = LLM::Response.new(res)
|
|
78
78
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -89,7 +89,7 @@ class LLM::OpenAI
|
|
|
89
89
|
# @see https://platform.openai.com/docs/api-reference/vector_stores/modify OpenAI docs
|
|
90
90
|
def modify(vector:, name: nil, **params)
|
|
91
91
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
92
|
-
req =
|
|
92
|
+
req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}"), headers)
|
|
93
93
|
req.body = LLM.json.dump(params.merge({name:}).compact)
|
|
94
94
|
res, span, tracer = execute(request: req, operation: "request")
|
|
95
95
|
res = LLM::Response.new(res)
|
|
@@ -105,7 +105,7 @@ class LLM::OpenAI
|
|
|
105
105
|
# @see https://platform.openai.com/docs/api-reference/vector_stores/delete OpenAI docs
|
|
106
106
|
def delete(vector:)
|
|
107
107
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
108
|
-
req =
|
|
108
|
+
req = LLM::Transport::Request.delete(path("/vector_stores/#{vector_id}"), headers)
|
|
109
109
|
res, span, tracer = execute(request: req, operation: "request")
|
|
110
110
|
res = LLM::Response.new(res)
|
|
111
111
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -122,7 +122,7 @@ class LLM::OpenAI
|
|
|
122
122
|
# @see https://platform.openai.com/docs/api-reference/vector_stores/search OpenAI docs
|
|
123
123
|
def search(vector:, query:, **params)
|
|
124
124
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
125
|
-
req =
|
|
125
|
+
req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}/search"), headers)
|
|
126
126
|
req.body = LLM.json.dump(params.merge({query:}).compact)
|
|
127
127
|
res, span, tracer = execute(request: req, operation: "retrieval")
|
|
128
128
|
res = ResponseAdapter.adapt(res, type: :enumerable)
|
|
@@ -140,7 +140,7 @@ class LLM::OpenAI
|
|
|
140
140
|
def all_files(vector:, **params)
|
|
141
141
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
142
142
|
query = URI.encode_www_form(params)
|
|
143
|
-
req =
|
|
143
|
+
req = LLM::Transport::Request.get(path("/vector_stores/#{vector_id}/files?#{query}"), headers)
|
|
144
144
|
res, span, tracer = execute(request: req, operation: "request")
|
|
145
145
|
res = ResponseAdapter.adapt(res, type: :enumerable)
|
|
146
146
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -159,7 +159,7 @@ class LLM::OpenAI
|
|
|
159
159
|
def add_file(vector:, file:, attributes: nil, **params)
|
|
160
160
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
161
161
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
162
|
-
req =
|
|
162
|
+
req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}/files"), headers)
|
|
163
163
|
req.body = LLM.json.dump(params.merge({file_id:, attributes:}).compact)
|
|
164
164
|
res, span, tracer = execute(request: req, operation: "request")
|
|
165
165
|
res = LLM::Response.new(res)
|
|
@@ -190,7 +190,7 @@ class LLM::OpenAI
|
|
|
190
190
|
def update_file(vector:, file:, attributes:, **params)
|
|
191
191
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
192
192
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
193
|
-
req =
|
|
193
|
+
req = LLM::Transport::Request.post(path("/vector_stores/#{vector_id}/files/#{file_id}"), headers)
|
|
194
194
|
req.body = LLM.json.dump(params.merge({attributes:}).compact)
|
|
195
195
|
res, span, tracer = execute(request: req, operation: "request")
|
|
196
196
|
res = LLM::Response.new(res)
|
|
@@ -209,7 +209,7 @@ class LLM::OpenAI
|
|
|
209
209
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
210
210
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
211
211
|
query = URI.encode_www_form(params)
|
|
212
|
-
req =
|
|
212
|
+
req = LLM::Transport::Request.get(path("/vector_stores/#{vector_id}/files/#{file_id}?#{query}"), headers)
|
|
213
213
|
res, span, tracer = execute(request: req, operation: "request")
|
|
214
214
|
res = LLM::Response.new(res)
|
|
215
215
|
tracer.on_request_finish(operation: "request", res:, span:)
|
|
@@ -226,7 +226,7 @@ class LLM::OpenAI
|
|
|
226
226
|
def delete_file(vector:, file:)
|
|
227
227
|
vector_id = vector.respond_to?(:id) ? vector.id : vector
|
|
228
228
|
file_id = file.respond_to?(:id) ? file.id : file
|
|
229
|
-
req =
|
|
229
|
+
req = LLM::Transport::Request.delete(path("/vector_stores/#{vector_id}/files/#{file_id}"), headers)
|
|
230
230
|
res, span, tracer = execute(request: req, operation: "request")
|
|
231
231
|
res = LLM::Response.new(res)
|
|
232
232
|
tracer.on_request_finish(operation: "request", res:, span:)
|
data/lib/llm/providers/openai.rb
CHANGED
|
@@ -52,7 +52,7 @@ module LLM
|
|
|
52
52
|
# @raise (see LLM::Provider#request)
|
|
53
53
|
# @return (see LLM::Provider#embed)
|
|
54
54
|
def embed(input, model: "text-embedding-3-small", **params)
|
|
55
|
-
req =
|
|
55
|
+
req = LLM::Transport::Request.post(path("/embeddings"), headers)
|
|
56
56
|
req.body = LLM.json.dump({input:, model:}.merge!(params))
|
|
57
57
|
res, span, tracer = execute(request: req, operation: "embeddings", model:)
|
|
58
58
|
res = ResponseAdapter.adapt(res, type: :embedding)
|
|
@@ -222,7 +222,7 @@ module LLM
|
|
|
222
222
|
def build_complete_request(prompt, params, role)
|
|
223
223
|
messages = build_complete_messages(prompt, params, role)
|
|
224
224
|
body = LLM.json.dump({messages: adapt(messages, mode: :complete).flatten}.merge!(params))
|
|
225
|
-
req =
|
|
225
|
+
req = LLM::Transport::Request.post(completions_path, headers)
|
|
226
226
|
transport.set_body_stream(req, StringIO.new(body))
|
|
227
227
|
[req, messages]
|
|
228
228
|
end
|