bx_builder_chain 0.1.4 → 0.1.6
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 006f8200cd1577b6ad0687b1607cad5aabf833971d3a3e3fe2f7718988ec6fbb
|
4
|
+
data.tar.gz: 51be4ca4f480ab7ed47582fc2482b10e53e2a858eb798889ed65cbcf85f76e91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f2e15d46fa5c06974fa10df37d27204d30bf92e4615a0c77dfbed4a0e84091a0e304ae5b98e52b2434c8937d97616e4d4e3213f959226657c84075431663b9c
|
7
|
+
data.tar.gz: b4762d1c53c6ccc6fc434f53ed058f55273798af70da586e722981a53159d49fc93f197321a7ece9b95b90c80d2c731c5de76d0830782e488571caf7cee8c682
|
data/README.md
CHANGED
@@ -20,30 +20,57 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
20
20
|
|
21
21
|
$ gem install bx_builder_chain
|
22
22
|
|
23
|
-
TODO: add rake task to create db structure
|
24
|
-
### Optional
|
25
23
|
|
26
|
-
|
24
|
+
### Setup
|
27
25
|
|
28
|
-
|
26
|
+
generate the endpoints & Active admin contollers for Builder Chain along with the DB migrations
|
27
|
+
|
28
|
+
$ rails generate builder_chain:install
|
29
29
|
|
30
30
|
this will add the following endpoint controllers
|
31
31
|
- File upload
|
32
|
+
- List files
|
33
|
+
- Delete file
|
32
34
|
- OpenAi ask / completion
|
33
35
|
- ActiveAdmin documents controller
|
34
36
|
|
37
|
+
It will also add a test view / controller for testing the endpoints. ensure this is removed prior to production release
|
38
|
+
|
35
39
|
## Usage
|
36
40
|
```ruby
|
37
41
|
require "bx_builder_chain"
|
38
42
|
```
|
39
43
|
|
44
|
+
edit the initializer config 'bx_builder_chain.rb' setup the api keys and db credentials
|
45
|
+
```
|
46
|
+
BxBuilderChain.configure do |config|
|
47
|
+
config.openai_api_key = ENV['OPENAI_API_KEY']
|
48
|
+
|
49
|
+
# for db use this
|
50
|
+
config.pg_url = ENV['DB_URL'] || nil # eg 'postgres://postgres:password@localhost:5432/my_db'
|
51
|
+
|
52
|
+
# or this - pg_url with take preference
|
53
|
+
config.database_host = ENV['DB_HOSTNAME'] || 'localhost'
|
54
|
+
config.database_name = ENV['DB_NAME']
|
55
|
+
config.database_user = ENV['DB_USER']
|
56
|
+
config.database_password = ENV['DB_PASSWORD']
|
57
|
+
config.database_port = ENV['DB_PORT'] || '5432' # Defaulting to 5432 if not set
|
58
|
+
|
59
|
+
config.public_namespace = "public"
|
60
|
+
config.threshold = 0.25
|
61
|
+
config.default_prompt_template = "Context information is below
|
62
|
+
--------------------
|
63
|
+
%{context}
|
64
|
+
--------------------
|
65
|
+
Given the context information and not prior knowledge
|
66
|
+
answer the question: %{question}"
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
40
70
|
create the llm and client
|
41
71
|
```ruby
|
42
|
-
llm = BxBuilderChain::Llm::OpenAi.new(api_key: 'open-ai-api-key')
|
43
72
|
client = BxBuilderChain::Vectorsearch::Pgvector.new(
|
44
|
-
|
45
|
-
table_name: "embeddings", # table name for the documents to be stored
|
46
|
-
llm: llm,
|
73
|
+
llm: BxBuilderChain::Llm::OpenAi.new,
|
47
74
|
namespace: user_id # default is nil, nil is used for global public documents
|
48
75
|
)
|
49
76
|
```
|
@@ -68,6 +95,22 @@ my_docx = "path/to/my.docx"
|
|
68
95
|
client.add_data(paths: [my_pdf, my_text, my_docx])
|
69
96
|
```
|
70
97
|
|
98
|
+
Or via the service object
|
99
|
+
```ruby
|
100
|
+
my_pdf = "path/to/my.pdf"
|
101
|
+
my_text = "path/to/my.txt"
|
102
|
+
my_docx = "path/to/my.docx"
|
103
|
+
|
104
|
+
service = DocumentUploadService.new(
|
105
|
+
files: [my_pdf, my_text, my_docx],
|
106
|
+
user_groups: current_user_document_groups, # optional defaults to ['public'] an uses the first value to store the docs
|
107
|
+
client_class_name: CLIENT_CLASS_NAME, # optional defaults to 'BxBuilderChain::Vectorsearch::Pgvector'
|
108
|
+
llm_class_name: LLM_CLASS_NAME # optional defaults to 'BxBuilderChain::Llm::OpenAi'
|
109
|
+
)
|
110
|
+
|
111
|
+
result = service.upload_and_process
|
112
|
+
```
|
113
|
+
|
71
114
|
Then ask the question
|
72
115
|
```ruby
|
73
116
|
client.ask(question: "What is Frogger?")
|
@@ -39,7 +39,8 @@ module BxBuilderChain
|
|
39
39
|
def self.token_limit(model_name)
|
40
40
|
TOKEN_LIMITS[model_name]
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
|
+
def self.validate_max_tokens!(content, model_name, options = {})
|
43
44
|
text_token_length = if content.is_a?(Array)
|
44
45
|
content.sum { |item| token_length(item.to_json, model_name, options) }
|
45
46
|
else
|
data/lib/generators/bx_builder_chain/templates/app/views/bx_builder_chain/test/form.html.erb
CHANGED
@@ -23,6 +23,10 @@
|
|
23
23
|
height: 340px;
|
24
24
|
}
|
25
25
|
|
26
|
+
div.fullwidth {
|
27
|
+
width: 100%;
|
28
|
+
}
|
29
|
+
|
26
30
|
form {
|
27
31
|
width: 100%;
|
28
32
|
}
|
@@ -73,6 +77,19 @@
|
|
73
77
|
.console_message {
|
74
78
|
white-space: pre-line; /* To respect new lines in the content */
|
75
79
|
}
|
80
|
+
|
81
|
+
div.fullwidth .form-group {
|
82
|
+
width: 80%;
|
83
|
+
float: left;
|
84
|
+
}
|
85
|
+
|
86
|
+
div.fullwidth .actions {
|
87
|
+
float: right;
|
88
|
+
margin-top: 17px;
|
89
|
+
}
|
90
|
+
#file_list button {
|
91
|
+
margin-left: 10px;
|
92
|
+
}
|
76
93
|
</style>
|
77
94
|
|
78
95
|
<div class="page_wrapper">
|
@@ -119,7 +136,24 @@
|
|
119
136
|
<% end %>
|
120
137
|
</div>
|
121
138
|
<div style="clear: both;"></div>
|
139
|
+
<div class="form_wrapper fullwidth">
|
140
|
+
<h2>Retrieve Files</h2>
|
122
141
|
|
142
|
+
<%= form_tag(bx_builder_chain_documents_list_path, method: :get, id: 'list_form') do %>
|
143
|
+
<div class="form-group">
|
144
|
+
<label for="current_user_groups">Enter User ID:</label>
|
145
|
+
<%= text_field_tag 'current_user_groups', 1, class: 'form-control' %>
|
146
|
+
</div>
|
147
|
+
|
148
|
+
<div class="actions">
|
149
|
+
<%= submit_tag "Retrieve Files", class: 'btn btn-primary' %>
|
150
|
+
</div>
|
151
|
+
<% end %>
|
152
|
+
|
153
|
+
<div style="clear: both;"></div>
|
154
|
+
<ul id="file_list"></ul>
|
155
|
+
</div>
|
156
|
+
<div style="clear: both;"></div>
|
123
157
|
<!-- Console box -->
|
124
158
|
<div class="console_wrapper">
|
125
159
|
<div class="console_message">
|
@@ -161,4 +195,83 @@
|
|
161
195
|
});
|
162
196
|
});
|
163
197
|
});
|
198
|
+
document.addEventListener('DOMContentLoaded', function () {
|
199
|
+
const listForm = document.querySelector('#list_form');
|
200
|
+
const fileList = document.querySelector('#file_list');
|
201
|
+
const consoleBox = document.querySelector('.console_message');
|
202
|
+
|
203
|
+
listForm.addEventListener('submit', async function (event) {
|
204
|
+
event.preventDefault();
|
205
|
+
|
206
|
+
consoleBox.textContent = "Retrieving files...";
|
207
|
+
const current_user_groups = event.target.current_user_groups.value;
|
208
|
+
const endpoint = `${
|
209
|
+
event.target.action
|
210
|
+
}?current_user_groups=${current_user_groups}`;
|
211
|
+
|
212
|
+
try {
|
213
|
+
let response = await fetch(endpoint, {
|
214
|
+
method: 'GET',
|
215
|
+
headers: {
|
216
|
+
'Content-Type': 'application/json'
|
217
|
+
}
|
218
|
+
});
|
219
|
+
|
220
|
+
if (response.ok) {
|
221
|
+
let files = await response.json();
|
222
|
+
|
223
|
+
fileList.innerHTML = ''; // Clear the file list
|
224
|
+
|
225
|
+
const current_user_groups = event.target.current_user_groups.value;
|
226
|
+
files.forEach(file => {
|
227
|
+
let listItem = document.createElement('li');
|
228
|
+
listItem.textContent = file.name; // Assuming the file object has a 'name' property
|
229
|
+
|
230
|
+
let deleteBtn = document.createElement('button');
|
231
|
+
deleteBtn.textContent = "Delete";
|
232
|
+
deleteBtn.addEventListener('click', async function () {
|
233
|
+
event.preventDefault();
|
234
|
+
await deleteFile(file.id, current_user_groups); // Assuming the file object has an 'id' property
|
235
|
+
});
|
236
|
+
|
237
|
+
listItem.appendChild(deleteBtn);
|
238
|
+
fileList.appendChild(listItem);
|
239
|
+
});
|
240
|
+
|
241
|
+
consoleBox.textContent = "Files retrieved successfully.";
|
242
|
+
} else {
|
243
|
+
let errorData = await response.json();
|
244
|
+
consoleBox.textContent = errorData.error || "An error occurred while retrieving files.";
|
245
|
+
}
|
246
|
+
} catch (error) {
|
247
|
+
consoleBox.textContent = "Failed to retrieve files. Please try again.";
|
248
|
+
}
|
249
|
+
});
|
250
|
+
|
251
|
+
async function deleteFile(fileId, current_user_groups) {
|
252
|
+
consoleBox.textContent = "Deleting file...";
|
253
|
+
|
254
|
+
try {
|
255
|
+
let response = await fetch('/bx_builder_chain/documents/delete', {
|
256
|
+
method: 'DELETE',
|
257
|
+
headers: {
|
258
|
+
'Content-Type': 'application/json'
|
259
|
+
},
|
260
|
+
body: JSON.stringify(
|
261
|
+
{ids: [fileId], current_user_groups: current_user_groups}
|
262
|
+
)
|
263
|
+
});
|
264
|
+
|
265
|
+
if (response.ok) {
|
266
|
+
consoleBox.textContent = "File deleted successfully.";
|
267
|
+
document.querySelector('#list_form .actions input').click()
|
268
|
+
} else {
|
269
|
+
let errorData = await response.json();
|
270
|
+
consoleBox.textContent = errorData.error || "An error occurred while deleting the file.";
|
271
|
+
}
|
272
|
+
} catch (error) {
|
273
|
+
consoleBox.textContent = "Failed to delete file. Please try again.";
|
274
|
+
}
|
275
|
+
}
|
276
|
+
});
|
164
277
|
</script>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bx_builder_chain
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Ketelle
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|