llm.rb 4.14.0 → 4.16.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 +83 -0
- data/README.md +93 -28
- data/data/anthropic.json +218 -198
- data/data/deepseek.json +1 -1
- data/data/google.json +481 -429
- data/data/openai.json +742 -704
- data/data/xai.json +277 -277
- data/data/zai.json +160 -126
- data/lib/llm/active_record/acts_as_llm.rb +238 -0
- data/lib/llm/active_record.rb +3 -0
- data/lib/llm/context.rb +15 -10
- data/lib/llm/eventstream/parser.rb +40 -8
- data/lib/llm/provider.rb +16 -1
- data/lib/llm/providers/anthropic/stream_parser.rb +6 -3
- data/lib/llm/providers/google/stream_parser.rb +6 -3
- data/lib/llm/providers/ollama/stream_parser.rb +3 -2
- data/lib/llm/providers/openai/audio.rb +4 -4
- data/lib/llm/providers/openai/files.rb +6 -6
- data/lib/llm/providers/openai/images.rb +4 -4
- data/lib/llm/providers/openai/models.rb +2 -2
- data/lib/llm/providers/openai/moderations.rb +2 -2
- data/lib/llm/providers/openai/responses/stream_parser.rb +216 -91
- data/lib/llm/providers/openai/responses.rb +4 -4
- data/lib/llm/providers/openai/stream_parser.rb +111 -57
- data/lib/llm/providers/openai/vector_stores.rb +12 -12
- data/lib/llm/providers/openai.rb +4 -4
- data/lib/llm/response.rb +12 -4
- data/lib/llm/sequel/plugin.rb +252 -0
- data/lib/llm/stream/queue.rb +2 -2
- data/lib/llm/stream.rb +2 -2
- data/lib/llm/version.rb +1 -1
- data/lib/sequel/plugins/llm.rb +8 -0
- metadata +5 -1
|
@@ -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 = Net::HTTP::Get.new("/
|
|
34
|
+
req = Net::HTTP::Get.new(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 = Net::HTTP::Post.new("/
|
|
50
|
+
req = Net::HTTP::Post.new(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 = Net::HTTP::Get.new("/
|
|
75
|
+
req = Net::HTTP::Get.new(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 = Net::HTTP::Post.new("/
|
|
92
|
+
req = Net::HTTP::Post.new(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 = Net::HTTP::Delete.new("/
|
|
108
|
+
req = Net::HTTP::Delete.new(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 = Net::HTTP::Post.new("/
|
|
125
|
+
req = Net::HTTP::Post.new(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 = Net::HTTP::Get.new("/
|
|
143
|
+
req = Net::HTTP::Get.new(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 = Net::HTTP::Post.new("/
|
|
162
|
+
req = Net::HTTP::Post.new(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 = Net::HTTP::Post.new("/
|
|
193
|
+
req = Net::HTTP::Post.new(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 = Net::HTTP::Get.new("/
|
|
212
|
+
req = Net::HTTP::Get.new(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 = Net::HTTP::Delete.new("/
|
|
229
|
+
req = Net::HTTP::Delete.new(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:)
|
|
@@ -259,7 +259,7 @@ class LLM::OpenAI
|
|
|
259
259
|
|
|
260
260
|
private
|
|
261
261
|
|
|
262
|
-
[:headers, :execute, :set_body_stream].each do |m|
|
|
262
|
+
[:path, :headers, :execute, :set_body_stream].each do |m|
|
|
263
263
|
define_method(m) { |*args, **kwargs, &b| @provider.send(m, *args, **kwargs, &b) }
|
|
264
264
|
end
|
|
265
265
|
end
|
data/lib/llm/providers/openai.rb
CHANGED
|
@@ -32,8 +32,8 @@ module LLM
|
|
|
32
32
|
|
|
33
33
|
##
|
|
34
34
|
# @param key (see LLM::Provider#initialize)
|
|
35
|
-
def initialize(**)
|
|
36
|
-
super(host: HOST, **)
|
|
35
|
+
def initialize(base_path: "/v1", **)
|
|
36
|
+
super(host: HOST, base_path:, **)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
##
|
|
@@ -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 = Net::HTTP::Post.new("/
|
|
55
|
+
req = Net::HTTP::Post.new(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)
|
|
@@ -187,7 +187,7 @@ module LLM
|
|
|
187
187
|
private
|
|
188
188
|
|
|
189
189
|
def completions_path
|
|
190
|
-
"/
|
|
190
|
+
path("/chat/completions")
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
def headers
|
data/lib/llm/response.rb
CHANGED
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module LLM
|
|
4
4
|
##
|
|
5
|
-
# {LLM::Response LLM::Response}
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
5
|
+
# {LLM::Response LLM::Response} is the normalized base shape for
|
|
6
|
+
# provider and endpoint responses in llm.rb.
|
|
7
|
+
#
|
|
8
|
+
# Provider calls return an instance of this class, then extend it
|
|
9
|
+
# with provider-, endpoint-, or context-specific modules so response
|
|
10
|
+
# handling can share one common surface without flattening away
|
|
11
|
+
# specialized behavior.
|
|
12
|
+
#
|
|
13
|
+
# The normalized response still keeps the original
|
|
14
|
+
# {Net::HTTPResponse Net::HTTPResponse} available through {#res}
|
|
15
|
+
# when callers need direct access to raw HTTP details such as
|
|
16
|
+
# headers, status codes, or unadapted bodies.
|
|
9
17
|
class Response
|
|
10
18
|
require "json"
|
|
11
19
|
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LLM::Sequel
|
|
4
|
+
##
|
|
5
|
+
# Sequel plugin for persisting {LLM::Context LLM::Context} state.
|
|
6
|
+
#
|
|
7
|
+
# This plugin maps model columns onto provider selection, model
|
|
8
|
+
# selection, usage accounting, and serialized context data while
|
|
9
|
+
# leaving application-specific concerns such as credentials,
|
|
10
|
+
# associations, and UI shaping to the host app.
|
|
11
|
+
#
|
|
12
|
+
# Context state can be stored as a JSON string (`format: :string`, the
|
|
13
|
+
# default) or as a structured object (`format: :json` / `:jsonb`) for
|
|
14
|
+
# databases such as PostgreSQL that can persist JSON natively.
|
|
15
|
+
# `:json` and `:jsonb` expect a real JSON column type with Sequel handling
|
|
16
|
+
# JSON typecasting for the model.
|
|
17
|
+
module Plugin
|
|
18
|
+
EMPTY_HASH = {}.freeze
|
|
19
|
+
DEFAULT_USAGE_COLUMNS = {
|
|
20
|
+
input_tokens: :input_tokens,
|
|
21
|
+
output_tokens: :output_tokens,
|
|
22
|
+
total_tokens: :total_tokens
|
|
23
|
+
}.freeze
|
|
24
|
+
DEFAULTS = {
|
|
25
|
+
provider_column: :provider,
|
|
26
|
+
model_column: :model,
|
|
27
|
+
data_column: :data,
|
|
28
|
+
format: :string,
|
|
29
|
+
usage_columns: DEFAULT_USAGE_COLUMNS,
|
|
30
|
+
provider: EMPTY_HASH,
|
|
31
|
+
context: EMPTY_HASH
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Called by Sequel when the plugin is first applied to a model class.
|
|
36
|
+
#
|
|
37
|
+
# This hook installs the plugin's class- and instance-level behavior on
|
|
38
|
+
# the target model. It runs before {configure}, so it should only attach
|
|
39
|
+
# methods and not depend on per-model plugin options.
|
|
40
|
+
#
|
|
41
|
+
# @param [Class] model
|
|
42
|
+
# @return [void]
|
|
43
|
+
def self.apply(model, **)
|
|
44
|
+
model.extend ClassMethods
|
|
45
|
+
model.include InstanceMethods
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Called by Sequel after {apply} with the options passed to
|
|
50
|
+
# `plugin :llm, ...`.
|
|
51
|
+
#
|
|
52
|
+
# This hook merges plugin defaults with the model's explicit settings and
|
|
53
|
+
# stores the resolved configuration on the model class for later use by
|
|
54
|
+
# instance methods such as {InstanceMethods#llm} and {InstanceMethods#ctx}.
|
|
55
|
+
#
|
|
56
|
+
# @param [Class] model
|
|
57
|
+
# @param [Hash] options
|
|
58
|
+
# @option options [Symbol] :format
|
|
59
|
+
# Storage format for the serialized context. Use `:string` for text
|
|
60
|
+
# columns, or `:json` / `:jsonb` for structured JSON columns with Sequel
|
|
61
|
+
# JSON typecasting enabled.
|
|
62
|
+
# @return [void]
|
|
63
|
+
def self.configure(model, options = EMPTY_HASH)
|
|
64
|
+
options = DEFAULTS.merge(options)
|
|
65
|
+
usage_columns = DEFAULT_USAGE_COLUMNS.merge(options[:usage_columns] || EMPTY_HASH)
|
|
66
|
+
model.instance_variable_set(
|
|
67
|
+
:@llm_plugin_options,
|
|
68
|
+
options.merge(usage_columns: usage_columns.freeze).freeze
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module Plugin::ClassMethods
|
|
74
|
+
##
|
|
75
|
+
# @return [Hash]
|
|
76
|
+
def llm_plugin_options
|
|
77
|
+
@llm_plugin_options || DEFAULTS
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module Plugin::InstanceMethods
|
|
82
|
+
##
|
|
83
|
+
# Continues the stored context with new input and flushes it.
|
|
84
|
+
# @see LLM::Context#talk
|
|
85
|
+
# @return [LLM::Response]
|
|
86
|
+
def talk(...)
|
|
87
|
+
ctx.talk(...).tap { flush }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# Continues the stored context through the Responses API and flushes it.
|
|
92
|
+
# @see LLM::Context#respond
|
|
93
|
+
# @return [LLM::Response]
|
|
94
|
+
def respond(...)
|
|
95
|
+
ctx.respond(...).tap { flush }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# Waits for queued tool work to finish.
|
|
100
|
+
# @see LLM::Context#wait
|
|
101
|
+
# @return [Array<LLM::Function::Return>]
|
|
102
|
+
def wait(...)
|
|
103
|
+
ctx.wait(...)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
# Calls into the stored context.
|
|
108
|
+
# @see LLM::Context#call
|
|
109
|
+
# @return [Object]
|
|
110
|
+
def call(...)
|
|
111
|
+
ctx.call(...)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
##
|
|
115
|
+
# @see LLM::Context#messages
|
|
116
|
+
# @return [Array<LLM::Message>]
|
|
117
|
+
def messages
|
|
118
|
+
ctx.messages
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
##
|
|
122
|
+
# @note The bang is used because Sequel reserves `model` for the
|
|
123
|
+
# underlying model class on instances.
|
|
124
|
+
# @see LLM::Context#model
|
|
125
|
+
# @return [String]
|
|
126
|
+
def model!
|
|
127
|
+
ctx.model
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
##
|
|
131
|
+
# @see LLM::Context#functions
|
|
132
|
+
# @return [Array<LLM::Function>]
|
|
133
|
+
def functions
|
|
134
|
+
ctx.functions
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
##
|
|
138
|
+
# @see LLM::Context#cost
|
|
139
|
+
# @return [LLM::Cost]
|
|
140
|
+
def cost
|
|
141
|
+
ctx.cost
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
##
|
|
145
|
+
# @see LLM::Context#context_window
|
|
146
|
+
# @return [Integer]
|
|
147
|
+
def context_window
|
|
148
|
+
ctx.context_window
|
|
149
|
+
rescue LLM::NoSuchModelError, LLM::NoSuchRegistryError
|
|
150
|
+
0
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
##
|
|
154
|
+
# Returns usage from the mapped usage columns.
|
|
155
|
+
# @return [LLM::Object]
|
|
156
|
+
def usage
|
|
157
|
+
LLM::Object.from(
|
|
158
|
+
input_tokens: self[columns[:input_tokens]] || 0,
|
|
159
|
+
output_tokens: self[columns[:output_tokens]] || 0,
|
|
160
|
+
total_tokens: self[columns[:total_tokens]] || 0
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
# Returns the resolved provider instance for this record.
|
|
168
|
+
# @return [LLM::Provider]
|
|
169
|
+
def llm
|
|
170
|
+
options = self.class.llm_plugin_options
|
|
171
|
+
provider = self[columns[:provider_column]]
|
|
172
|
+
kwargs = resolve_options(options[:provider])
|
|
173
|
+
@llm ||= LLM.method(provider).call(**kwargs)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
##
|
|
177
|
+
# @return [LLM::Context]
|
|
178
|
+
def ctx
|
|
179
|
+
@ctx ||= begin
|
|
180
|
+
options = self.class.llm_plugin_options
|
|
181
|
+
params = resolve_options(options[:context]).dup
|
|
182
|
+
params[:model] ||= self[columns[:model_column]]
|
|
183
|
+
ctx = LLM::Context.new(llm, params.compact)
|
|
184
|
+
data = self[columns[:data_column]]
|
|
185
|
+
if data.nil? || data == ""
|
|
186
|
+
ctx
|
|
187
|
+
else
|
|
188
|
+
string = case options[:format]
|
|
189
|
+
when :string then data
|
|
190
|
+
when :json, :jsonb then LLM.json.dump(data)
|
|
191
|
+
else raise ArgumentError, "Unknown format: #{options[:format].inspect}"
|
|
192
|
+
end
|
|
193
|
+
ctx.restore(string:)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
##
|
|
199
|
+
# @return [void]
|
|
200
|
+
def flush
|
|
201
|
+
options = self.class.llm_plugin_options
|
|
202
|
+
update({
|
|
203
|
+
columns[:data_column] => serialize_context(options[:format]),
|
|
204
|
+
columns[:input_tokens] => ctx.usage.input_tokens,
|
|
205
|
+
columns[:output_tokens] => ctx.usage.output_tokens,
|
|
206
|
+
columns[:total_tokens] => ctx.usage.total_tokens
|
|
207
|
+
})
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
##
|
|
211
|
+
# @return [Hash]
|
|
212
|
+
def resolve_option(option)
|
|
213
|
+
case option
|
|
214
|
+
when Proc then instance_exec(&option)
|
|
215
|
+
when Hash then option.dup
|
|
216
|
+
else option
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
##
|
|
221
|
+
# @return [Hash]
|
|
222
|
+
def resolve_options(option)
|
|
223
|
+
case option
|
|
224
|
+
when Proc, Hash then resolve_option(option)
|
|
225
|
+
else EMPTY_HASH.dup
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def serialize_context(format)
|
|
230
|
+
case format
|
|
231
|
+
when :string then ctx.to_json
|
|
232
|
+
when :json, :jsonb then ctx.to_h
|
|
233
|
+
else raise ArgumentError, "Unknown format: #{format.inspect}"
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def columns
|
|
238
|
+
@columns ||= begin
|
|
239
|
+
options = self.class.llm_plugin_options
|
|
240
|
+
usage_columns = options[:usage_columns]
|
|
241
|
+
{
|
|
242
|
+
provider_column: options[:provider_column],
|
|
243
|
+
model_column: options[:model_column],
|
|
244
|
+
data_column: options[:data_column],
|
|
245
|
+
input_tokens: usage_columns[:input_tokens],
|
|
246
|
+
output_tokens: usage_columns[:output_tokens],
|
|
247
|
+
total_tokens: usage_columns[:total_tokens]
|
|
248
|
+
}.freeze
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
data/lib/llm/stream/queue.rb
CHANGED
|
@@ -54,9 +54,9 @@ class LLM::Stream
|
|
|
54
54
|
private
|
|
55
55
|
|
|
56
56
|
def fire_hooks(tasks, results)
|
|
57
|
-
results.each_with_index do |
|
|
57
|
+
results.each_with_index do |result, idx|
|
|
58
58
|
tool = tasks[idx]&.function
|
|
59
|
-
@stream.on_tool_return(tool,
|
|
59
|
+
@stream.on_tool_return(tool, result) if tool
|
|
60
60
|
end
|
|
61
61
|
results
|
|
62
62
|
end
|
data/lib/llm/stream.rb
CHANGED
|
@@ -86,10 +86,10 @@ module LLM
|
|
|
86
86
|
# `tool.spawn(:fiber)`, or `tool.spawn(:task)`.
|
|
87
87
|
# @param [LLM::Function] tool
|
|
88
88
|
# The tool that returned.
|
|
89
|
-
# @param [LLM::Function::Return]
|
|
89
|
+
# @param [LLM::Function::Return] result
|
|
90
90
|
# The completed tool return.
|
|
91
91
|
# @return [nil]
|
|
92
|
-
def on_tool_return(tool,
|
|
92
|
+
def on_tool_return(tool, result)
|
|
93
93
|
nil
|
|
94
94
|
end
|
|
95
95
|
|
data/lib/llm/version.rb
CHANGED
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: 4.
|
|
4
|
+
version: 4.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Antar Azri
|
|
@@ -228,6 +228,8 @@ files:
|
|
|
228
228
|
- data/xai.json
|
|
229
229
|
- data/zai.json
|
|
230
230
|
- lib/llm.rb
|
|
231
|
+
- lib/llm/active_record.rb
|
|
232
|
+
- lib/llm/active_record/acts_as_llm.rb
|
|
231
233
|
- lib/llm/agent.rb
|
|
232
234
|
- lib/llm/bot.rb
|
|
233
235
|
- lib/llm/buffer.rb
|
|
@@ -367,6 +369,7 @@ files:
|
|
|
367
369
|
- lib/llm/schema/parser.rb
|
|
368
370
|
- lib/llm/schema/string.rb
|
|
369
371
|
- lib/llm/schema/version.rb
|
|
372
|
+
- lib/llm/sequel/plugin.rb
|
|
370
373
|
- lib/llm/server_tool.rb
|
|
371
374
|
- lib/llm/session.rb
|
|
372
375
|
- lib/llm/stream.rb
|
|
@@ -381,6 +384,7 @@ files:
|
|
|
381
384
|
- lib/llm/usage.rb
|
|
382
385
|
- lib/llm/utils.rb
|
|
383
386
|
- lib/llm/version.rb
|
|
387
|
+
- lib/sequel/plugins/llm.rb
|
|
384
388
|
- llm.gemspec
|
|
385
389
|
homepage: https://github.com/llmrb/llm.rb
|
|
386
390
|
licenses:
|