gpt-function 0.3.0 → 0.5.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/Gemfile.lock +1 -1
- data/README.md +73 -6
- data/lib/gpt-function.rb +4 -1
- data/lib/gpt_function/batch.rb +67 -20
- data/lib/gpt_function/file.rb +0 -1
- data/lib/gpt_function/simple_queue.rb +18 -0
- data/lib/gpt_function/storage.rb +20 -0
- data/lib/gpt_function/version.rb +1 -1
- metadata +4 -3
- data/lib/gpt/function.rb +0 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4382f4c9861d57510b154ab5179854eee8fad548dd849f3b283d870f736e599e
|
|
4
|
+
data.tar.gz: 498b2499d3d6ecdb03847a6b5c15f9d2f99c231d59655f3cf8a602898d525102
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ba7a96725e99edb9d87f9467011357fbd8e8e40dc62f902420b480334af7b432bf071e1b63df7ce5e0645611bcb6355caff08d275e9905a21a638ec6033cc06
|
|
7
|
+
data.tar.gz: 680facff33d5f9737fb4dbc6cfc10a08e35f221245cb1e90c82a3e280c4ed32a0f1129d52561e471ca210f984a4c973b671aec01445d7776fdb5815e1d25339b
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -27,27 +27,94 @@ gem 'gpt-function'
|
|
|
27
27
|
require 'gpt-function'
|
|
28
28
|
|
|
29
29
|
# 你需要設定你的 api key 和 model name
|
|
30
|
-
|
|
30
|
+
GptFunction.configure(api_key: '...', model: 'gpt-4o-mini', batch_storage: MyBatchStorage)
|
|
31
31
|
|
|
32
32
|
# 使用內建的翻譯方法
|
|
33
|
-
p
|
|
33
|
+
p GptFunctions.翻譯成中文.call("banana") # "香蕉"
|
|
34
34
|
|
|
35
35
|
# 使用內建的擷取關鍵字方法
|
|
36
|
-
p
|
|
36
|
+
p GptFunctions.擷取關鍵字.call("臺北市政府推動綠色交通計劃,鼓勵民眾使用公共運輸和自行車") # ["臺北市政府", "綠色交通計劃", "民眾", "公共運輸", "自行車"]
|
|
37
37
|
|
|
38
38
|
# 你也可以自己定義方法
|
|
39
|
-
def 擷取關鍵字
|
|
39
|
+
def 擷取關鍵字
|
|
40
40
|
# 創建一個簡單的 GPT 函數,你需要描述這個函數的功能,以及提供一些範例
|
|
41
|
-
|
|
41
|
+
GptFunction.new("Extract all keywords",
|
|
42
42
|
[
|
|
43
43
|
[
|
|
44
44
|
"臺灣最新5G網路覆蓋率達95%,推動智慧城市發展,領先亞洲多國",
|
|
45
45
|
["臺灣", "5G網路", "覆蓋率", "智慧城市", "亞洲"]
|
|
46
46
|
]
|
|
47
|
-
])
|
|
47
|
+
])
|
|
48
48
|
end
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
Batch Storage 是一個用來儲存 GPT 函數的結果的類別,你可以自己定義一個類似的類別,並且在 `GptFunction.configure` 中設定。
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
class MyBatchStorage
|
|
55
|
+
def initialize
|
|
56
|
+
@queue = []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def enqueue(value)
|
|
60
|
+
@queue << value
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def dequeue
|
|
65
|
+
@queue.shift
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
GptFunction.configure(api_key: '...', model: 'gpt-4o-mini', batch_storage: MyBatchStorage)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
你可以用 Batch.create 建立一個新的 Batch, 在 create 成功時,會自動將 Batch 存入 BatchStorage 中。
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
request1 = GptFunctions.翻譯成中文.to_request_body("apple")
|
|
76
|
+
request2 = GptFunctions.翻譯成中文.to_request_body("tesla")
|
|
77
|
+
batch = GptFunction::Batch.create([request1, request2])
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
你可以用 Batch.process 來處理 Batch,如果 Batch 的 status 在 "failed", "completed", "expired", "cancelled" 當中,Batch 會被從 queue 中移除,如果是其他狀態,Batch 會自動重新加入 queue 中,你只需要定期持續呼叫 process 就可以。
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
GptFunction::Batch.process do |batch|
|
|
84
|
+
puts "batch id: #{batch.id}, status: #{batch.status}, progress: #{batch.request_counts_completed}/#{batch.request_counts_total}"
|
|
85
|
+
batch.pairs.each do |input, output|
|
|
86
|
+
puts "input: #{input}, output: #{output}"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
可以用 count 參數來限制每次處理的數量,預設值為 1。
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
GptFunction::Batch.process(count: 2) do |batch|
|
|
95
|
+
...
|
|
96
|
+
end
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Batch Storage 整合 Active Record 的範例:
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
class Model < ApplicationRecord
|
|
103
|
+
class << self
|
|
104
|
+
def enqueue(hash)
|
|
105
|
+
create!(hash)
|
|
106
|
+
true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def dequeue
|
|
110
|
+
first&.destroy
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
GptFunction.configure(api_key: '...', model: 'gpt-4o-mini', batch_storage: Model)
|
|
116
|
+
```
|
|
117
|
+
|
|
51
118
|
## License
|
|
52
119
|
|
|
53
120
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/gpt-function.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "json"
|
|
|
5
5
|
|
|
6
6
|
require_relative "gpt_function/version"
|
|
7
7
|
require_relative "gpt_function/file"
|
|
8
|
+
require_relative "gpt_function/storage"
|
|
9
|
+
require_relative "gpt_function/simple_queue"
|
|
8
10
|
require_relative "gpt_function/batch"
|
|
9
11
|
require_relative "gpt_functions"
|
|
10
12
|
|
|
@@ -17,9 +19,10 @@ class GptFunction
|
|
|
17
19
|
class << self
|
|
18
20
|
attr_accessor :api_key, :model
|
|
19
21
|
|
|
20
|
-
def configure(api_key:, model:)
|
|
22
|
+
def configure(api_key:, model:, batch_storage: GptFunction::SimpleQueue.new)
|
|
21
23
|
@api_key = api_key
|
|
22
24
|
@model = model
|
|
25
|
+
GptFunction::Storage.batch_storage = batch_storage
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
|
data/lib/gpt_function/batch.rb
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
# lib/gpt_function/batch.rb
|
|
2
1
|
# frozen_string_literal: true
|
|
3
2
|
|
|
4
3
|
require "net/http"
|
|
5
4
|
require "json"
|
|
6
|
-
require "byebug"
|
|
7
5
|
|
|
8
6
|
class GptFunction
|
|
9
7
|
class Batch
|
|
@@ -30,8 +28,7 @@ class GptFunction
|
|
|
30
28
|
attr_reader :request_counts_completed
|
|
31
29
|
attr_reader :request_counts_failed
|
|
32
30
|
|
|
33
|
-
attr_reader :
|
|
34
|
-
attr_reader :metadata_batch_description
|
|
31
|
+
attr_reader :metadata
|
|
35
32
|
|
|
36
33
|
def initialize(hash)
|
|
37
34
|
@id = hash["id"]
|
|
@@ -57,8 +54,7 @@ class GptFunction
|
|
|
57
54
|
@request_counts_completed = hash.dig("request_counts", "completed")
|
|
58
55
|
@request_counts_failed = hash.dig("request_counts", "failed")
|
|
59
56
|
|
|
60
|
-
@
|
|
61
|
-
@metadata_batch_description = hash.dig("metadata", "batch_description")
|
|
57
|
+
@metadata = hash.dig("metadata")
|
|
62
58
|
end
|
|
63
59
|
|
|
64
60
|
def to_hash
|
|
@@ -84,8 +80,7 @@ class GptFunction
|
|
|
84
80
|
request_counts_total: request_counts_total,
|
|
85
81
|
request_counts_completed: request_counts_completed,
|
|
86
82
|
request_counts_failed: request_counts_failed,
|
|
87
|
-
|
|
88
|
-
metadata_batch_description: metadata_batch_description,
|
|
83
|
+
metadata: metadata
|
|
89
84
|
}
|
|
90
85
|
end
|
|
91
86
|
|
|
@@ -98,19 +93,21 @@ class GptFunction
|
|
|
98
93
|
end
|
|
99
94
|
|
|
100
95
|
def input_file
|
|
96
|
+
return nil if input_file_id.nil?
|
|
101
97
|
@input_file ||= File.from_id(input_file_id)
|
|
102
98
|
end
|
|
103
99
|
|
|
104
100
|
def output_file
|
|
101
|
+
return nil if output_file_id.nil?
|
|
105
102
|
@output_file ||= File.from_id(output_file_id)
|
|
106
103
|
end
|
|
107
104
|
|
|
108
105
|
def input_jsonl
|
|
109
|
-
@input_jsonl ||= input_file
|
|
106
|
+
@input_jsonl ||= input_file&.jsonl || []
|
|
110
107
|
end
|
|
111
108
|
|
|
112
109
|
def output_jsonl
|
|
113
|
-
@output_jsonl ||= output_file
|
|
110
|
+
@output_jsonl ||= output_file&.jsonl || []
|
|
114
111
|
end
|
|
115
112
|
|
|
116
113
|
def inputs
|
|
@@ -135,14 +132,16 @@ class GptFunction
|
|
|
135
132
|
|
|
136
133
|
def pairs
|
|
137
134
|
hash = {}
|
|
138
|
-
|
|
139
|
-
hash[input["custom_id"]] = {
|
|
140
|
-
"input" => input["content"],
|
|
141
|
-
}
|
|
142
|
-
end
|
|
135
|
+
|
|
143
136
|
outputs.each do |output|
|
|
144
|
-
hash[output["custom_id"]]
|
|
137
|
+
hash[output["custom_id"]] = [nil ,output["content"]]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
inputs.each do |input|
|
|
141
|
+
next if hash[input["custom_id"]].nil?
|
|
142
|
+
hash[input["custom_id"]][0] = input["content"]
|
|
145
143
|
end
|
|
144
|
+
|
|
146
145
|
hash.values
|
|
147
146
|
end
|
|
148
147
|
|
|
@@ -150,6 +149,24 @@ class GptFunction
|
|
|
150
149
|
Batch.cancel(id)
|
|
151
150
|
end
|
|
152
151
|
|
|
152
|
+
def enqueue
|
|
153
|
+
return false if GptFunction::Storage.batch_storage.nil?
|
|
154
|
+
|
|
155
|
+
GptFunction::Storage.batch_storage.enqueue(self.to_hash)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# validating the input file is being validated before the batch can begin
|
|
159
|
+
# failed the input file has failed the validation process
|
|
160
|
+
# in_progress the input file was successfully validated and the batch is currently being run
|
|
161
|
+
# finalizing the batch has completed and the results are being prepared
|
|
162
|
+
# completed the batch has been completed and the results are ready
|
|
163
|
+
# expired the batch was not able to be completed within the 24-hour time window
|
|
164
|
+
# cancelling the batch is being cancelled (may take up to 10 minutes)
|
|
165
|
+
# cancelled the batch was cancelled
|
|
166
|
+
def is_processed
|
|
167
|
+
["failed", "completed", "expired", "cancelled"].include? status
|
|
168
|
+
end
|
|
169
|
+
|
|
153
170
|
class << self
|
|
154
171
|
def list(limit: 20, after: nil)
|
|
155
172
|
# 創建批次請求
|
|
@@ -169,7 +186,7 @@ class GptFunction
|
|
|
169
186
|
end
|
|
170
187
|
end
|
|
171
188
|
|
|
172
|
-
def create(requests)
|
|
189
|
+
def create(requests, metadata: nil)
|
|
173
190
|
requests = requests.each_with_index.map do |request, index|
|
|
174
191
|
{
|
|
175
192
|
custom_id: "request-#{index + 1}",
|
|
@@ -186,11 +203,13 @@ class GptFunction
|
|
|
186
203
|
uri = URI('https://api.openai.com/v1/batches')
|
|
187
204
|
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
|
188
205
|
request['Authorization'] = "Bearer #{GptFunction.api_key}"
|
|
189
|
-
|
|
206
|
+
body = {
|
|
190
207
|
input_file_id: file.id,
|
|
191
208
|
endpoint: '/v1/chat/completions',
|
|
192
209
|
completion_window: '24h'
|
|
193
|
-
}
|
|
210
|
+
}
|
|
211
|
+
body[:metadata] = metadata unless metadata.nil?
|
|
212
|
+
request.body = body.to_json
|
|
194
213
|
|
|
195
214
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
196
215
|
http.request(request)
|
|
@@ -199,7 +218,9 @@ class GptFunction
|
|
|
199
218
|
raise "Batch creation failed: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
|
200
219
|
|
|
201
220
|
hash = JSON.parse(response.body)
|
|
202
|
-
Batch.new(hash)
|
|
221
|
+
batch = Batch.new(hash)
|
|
222
|
+
batch.enqueue
|
|
223
|
+
batch
|
|
203
224
|
rescue => e
|
|
204
225
|
file&.delete
|
|
205
226
|
raise e
|
|
@@ -245,6 +266,32 @@ class GptFunction
|
|
|
245
266
|
response.body
|
|
246
267
|
end
|
|
247
268
|
|
|
269
|
+
def dequeue
|
|
270
|
+
hash = GptFunction::Storage.batch_storage&.dequeue
|
|
271
|
+
id = hash&.dig("id") || hash&.dig(:id)
|
|
272
|
+
from_id(id) if id
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# 進行批次請求處理
|
|
276
|
+
# count: 處理批次請求的數量
|
|
277
|
+
# block: 處理批次請求的 block
|
|
278
|
+
# 返回值: 是否還有批次請求需要處理
|
|
279
|
+
def process(count: 1, &block)
|
|
280
|
+
# 從 Storage 取出 count 個批次請求
|
|
281
|
+
count.times do
|
|
282
|
+
batch = dequeue
|
|
283
|
+
|
|
284
|
+
# 如果沒有批次請求,則跳出迴圈
|
|
285
|
+
return false if batch.nil?
|
|
286
|
+
|
|
287
|
+
yield batch
|
|
288
|
+
|
|
289
|
+
# 如果 batch 還未處理完成,將批次請求重新加入 Storage
|
|
290
|
+
batch.enqueue unless batch.is_processed
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
true
|
|
294
|
+
end
|
|
248
295
|
end
|
|
249
296
|
end
|
|
250
297
|
end
|
data/lib/gpt_function/file.rb
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class GptFunction
|
|
4
|
+
module Storage
|
|
5
|
+
class << self
|
|
6
|
+
def batch_storage=(value)
|
|
7
|
+
# 檢查 value 有實作 enqueue 方法
|
|
8
|
+
raise "Invalid batch storage: should respond to #enqueue" unless value.respond_to?(:enqueue)
|
|
9
|
+
|
|
10
|
+
# 檢查 value 有實作 dequeue 方法
|
|
11
|
+
raise "Invalid batch storage: should respond to #dequeue" unless value.respond_to?(:dequeue)
|
|
12
|
+
@batch_storage = value
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def batch_storage
|
|
16
|
+
@batch_storage
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/gpt_function/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gpt-function
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- etrex kuo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-08-
|
|
11
|
+
date: 2024-08-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dotenv
|
|
@@ -41,9 +41,10 @@ files:
|
|
|
41
41
|
- Rakefile
|
|
42
42
|
- gpt-function.gemspec
|
|
43
43
|
- lib/gpt-function.rb
|
|
44
|
-
- lib/gpt/function.rb
|
|
45
44
|
- lib/gpt_function/batch.rb
|
|
46
45
|
- lib/gpt_function/file.rb
|
|
46
|
+
- lib/gpt_function/simple_queue.rb
|
|
47
|
+
- lib/gpt_function/storage.rb
|
|
47
48
|
- lib/gpt_function/version.rb
|
|
48
49
|
- lib/gpt_functions.rb
|
|
49
50
|
- workflows/main.yml
|