red-candle 1.8.1 → 1.8.2
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 +4 -0
- data/ext/candle/src/ruby/embedding_model.rs +22 -6
- data/lib/candle/embedding_model.rb +7 -4
- data/lib/candle/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7b7ecd1fe8f4551498e553e005c09ffb75f858b107525cbc8bd77948a040ff6
|
|
4
|
+
data.tar.gz: cf84d7690cfdc13eaf210745b38e73bbf72806a4f94be2c1a0315d1d3827ff8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f33e7cad2a4678a77f54c6997b31ebd3880a5916a473f8f15bdb58b8bfe78960077efa713ebab265dfa25acc0bd28bd952bb8a7cd48549593f6aee107cbc887
|
|
7
|
+
data.tar.gz: f4ba719c4f721fa23155bf11df9bd779d423eba03676ba5c3aa1c92c29e0551455267b07bc6f15b2996cd2a8592cdf7c471aed6d11d1706c0f5371de054f739d
|
data/README.md
CHANGED
|
@@ -107,6 +107,10 @@ device = Candle::Device.cpu # or Candle::Device.metal, Candle::Device.cuda
|
|
|
107
107
|
model = Candle::EmbeddingModel.from_pretrained("jinaai/jina-embeddings-v2-base-en", device: device)
|
|
108
108
|
embedding = model.embedding("Hi there!")
|
|
109
109
|
|
|
110
|
+
# Cap input length (in tokens) to cut memory; longer inputs are truncated.
|
|
111
|
+
# JinaBERT otherwise reserves ~3.2 GB for its full 8192-token context.
|
|
112
|
+
model = Candle::EmbeddingModel.from_pretrained(max_length: 512)
|
|
113
|
+
|
|
110
114
|
# Reranker also supports device selection
|
|
111
115
|
reranker = Candle::Reranker.from_pretrained("cross-encoder/ms-marco-MiniLM-L-12-v2", device: device)
|
|
112
116
|
results = reranker.rerank("query", ["doc1", "doc2", "doc3"])
|
|
@@ -72,10 +72,11 @@ pub struct EmbeddingModelInner {
|
|
|
72
72
|
model: Option<EmbeddingModelVariant>,
|
|
73
73
|
tokenizer: Option<TokenizerWrapper>,
|
|
74
74
|
embedding_size: Option<usize>,
|
|
75
|
+
max_length: Option<usize>,
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
impl EmbeddingModel {
|
|
78
|
-
pub fn new(model_id: Option<String>, tokenizer: Option<String>, device: Option<Device>, model_type: Option<String>, embedding_size: Option<usize>) -> Result<Self> {
|
|
79
|
+
pub fn new(model_id: Option<String>, tokenizer: Option<String>, device: Option<Device>, model_type: Option<String>, embedding_size: Option<usize>, max_length: Option<usize>) -> Result<Self> {
|
|
79
80
|
let device = device.unwrap_or(Device::best()).as_device()?;
|
|
80
81
|
let model_type = model_type
|
|
81
82
|
.and_then(|mt| EmbeddingModelType::from_string(&mt))
|
|
@@ -86,14 +87,15 @@ impl EmbeddingModel {
|
|
|
86
87
|
tokenizer_id: tokenizer.clone(),
|
|
87
88
|
model_type: Some(model_type),
|
|
88
89
|
model: match model_id.as_ref() {
|
|
89
|
-
Some(id) => Some(Self::build_embedding_model(id, device, model_type, embedding_size)?),
|
|
90
|
+
Some(id) => Some(Self::build_embedding_model(id, device, model_type, embedding_size, max_length)?),
|
|
90
91
|
None => None
|
|
91
92
|
},
|
|
92
93
|
tokenizer: match tokenizer {
|
|
93
|
-
Some(tid) => Some(Self::build_tokenizer(tid)?),
|
|
94
|
+
Some(tid) => Some(Self::build_tokenizer(tid, max_length)?),
|
|
94
95
|
None => None
|
|
95
96
|
},
|
|
96
97
|
embedding_size,
|
|
98
|
+
max_length,
|
|
97
99
|
}))
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -177,7 +179,7 @@ impl EmbeddingModel {
|
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
|
|
180
|
-
fn build_embedding_model(model_id: &str, device: CoreDevice, model_type: EmbeddingModelType, embedding_size: Option<usize>) -> Result<EmbeddingModelVariant> {
|
|
182
|
+
fn build_embedding_model(model_id: &str, device: CoreDevice, model_type: EmbeddingModelType, embedding_size: Option<usize>, max_length: Option<usize>) -> Result<EmbeddingModelVariant> {
|
|
181
183
|
use hf_hub::{api::sync::Api, Repo, RepoType};
|
|
182
184
|
let api = Api::new().map_err(wrap_hf_err)?;
|
|
183
185
|
let repo = Repo::new(model_id.to_string(), RepoType::Model);
|
|
@@ -194,6 +196,11 @@ impl EmbeddingModel {
|
|
|
194
196
|
let final_emb_dim = Self::resolve_embedding_size(Path::new(&model_path), embedding_size)?;
|
|
195
197
|
let mut config = JinaConfig::v2_base();
|
|
196
198
|
config.hidden_size = final_emb_dim;
|
|
199
|
+
// JinaBERT precomputes an ALiBi bias of heads x len x len floats
|
|
200
|
+
// (3.2 GB at the default 8192), so size it to the inputs we accept.
|
|
201
|
+
if let Some(max_length) = max_length {
|
|
202
|
+
config.max_position_embeddings = max_length;
|
|
203
|
+
}
|
|
197
204
|
let vb = unsafe {
|
|
198
205
|
VarBuilder::from_mmaped_safetensors(&[model_path], CoreDType::F32, &device)
|
|
199
206
|
.map_err(wrap_candle_err)?
|
|
@@ -268,7 +275,7 @@ impl EmbeddingModel {
|
|
|
268
275
|
}
|
|
269
276
|
}
|
|
270
277
|
|
|
271
|
-
fn build_tokenizer(tokenizer_id: String) -> Result<TokenizerWrapper> {
|
|
278
|
+
fn build_tokenizer(tokenizer_id: String, max_length: Option<usize>) -> Result<TokenizerWrapper> {
|
|
272
279
|
use hf_hub::{api::sync::Api, Repo, RepoType};
|
|
273
280
|
let tokenizer_path = Api::new()
|
|
274
281
|
.map_err(wrap_hf_err)?
|
|
@@ -282,6 +289,10 @@ impl EmbeddingModel {
|
|
|
282
289
|
.map_err(wrap_std_err)?;
|
|
283
290
|
|
|
284
291
|
let tokenizer = TokenizerLoader::with_padding(tokenizer, None);
|
|
292
|
+
let tokenizer = match max_length {
|
|
293
|
+
Some(max_length) => TokenizerLoader::with_truncation(tokenizer, max_length),
|
|
294
|
+
None => tokenizer,
|
|
295
|
+
};
|
|
285
296
|
Ok(TokenizerWrapper::new(tokenizer))
|
|
286
297
|
}
|
|
287
298
|
|
|
@@ -451,6 +462,11 @@ impl EmbeddingModel {
|
|
|
451
462
|
if let Some(size) = self.0.embedding_size {
|
|
452
463
|
hash.aset("embedding_size", size)?;
|
|
453
464
|
}
|
|
465
|
+
|
|
466
|
+
// Add max_length
|
|
467
|
+
if let Some(max_length) = self.0.max_length {
|
|
468
|
+
hash.aset("max_length", max_length)?;
|
|
469
|
+
}
|
|
454
470
|
|
|
455
471
|
Ok(hash)
|
|
456
472
|
}
|
|
@@ -459,7 +475,7 @@ impl EmbeddingModel {
|
|
|
459
475
|
pub fn init(rb_candle: RModule) -> Result<()> {
|
|
460
476
|
let ruby = Ruby::get().unwrap();
|
|
461
477
|
let rb_embedding_model = rb_candle.define_class("EmbeddingModel", ruby.class_object())?;
|
|
462
|
-
rb_embedding_model.define_singleton_method("_create", function!(EmbeddingModel::new,
|
|
478
|
+
rb_embedding_model.define_singleton_method("_create", function!(EmbeddingModel::new, 6))?;
|
|
463
479
|
// Expose embedding with an optional pooling_method argument (default: "pooled")
|
|
464
480
|
rb_embedding_model.define_method("_embedding", method!(EmbeddingModel::embedding, 2))?;
|
|
465
481
|
rb_embedding_model.define_method("embeddings", method!(EmbeddingModel::embeddings, 1))?;
|
|
@@ -15,8 +15,9 @@ module Candle
|
|
|
15
15
|
# @param tokenizer [String, nil] The tokenizer to use (defaults to using the model's tokenizer)
|
|
16
16
|
# @param model_type [String, nil] The type of embedding model (auto-detected if nil)
|
|
17
17
|
# @param embedding_size [Integer, nil] Override for the embedding size (optional)
|
|
18
|
+
# @param max_length [Integer, nil] Longest input in tokens; longer inputs are truncated (optional)
|
|
18
19
|
# @return [EmbeddingModel] A new EmbeddingModel instance
|
|
19
|
-
def self.from_pretrained(model_id = DEFAULT_MODEL_PATH, device: Candle::Device.best, tokenizer: nil, model_type: nil, embedding_size: nil)
|
|
20
|
+
def self.from_pretrained(model_id = DEFAULT_MODEL_PATH, device: Candle::Device.best, tokenizer: nil, model_type: nil, embedding_size: nil, max_length: nil)
|
|
20
21
|
# Auto-detect model type based on model_id if not provided
|
|
21
22
|
if model_type.nil?
|
|
22
23
|
model_type = case model_id.downcase
|
|
@@ -34,7 +35,7 @@ module Candle
|
|
|
34
35
|
# Use model_id as tokenizer if not specified (usually what you want)
|
|
35
36
|
tokenizer_id = tokenizer || model_id
|
|
36
37
|
|
|
37
|
-
_create(model_id, tokenizer_id, device, model_type, embedding_size)
|
|
38
|
+
_create(model_id, tokenizer_id, device, model_type, embedding_size, max_length)
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
# Constructor for creating a new EmbeddingModel with optional parameters
|
|
@@ -44,13 +45,15 @@ module Candle
|
|
|
44
45
|
# @param device [Candle::Device, Candle::Device.cpu] The device to use for computation
|
|
45
46
|
# @param model_type [String, nil] The type of embedding model to use
|
|
46
47
|
# @param embedding_size [Integer, nil] Override for the embedding size (optional)
|
|
48
|
+
# @param max_length [Integer, nil] Longest input in tokens; longer inputs are truncated (optional)
|
|
47
49
|
def self.new(model_path: DEFAULT_MODEL_PATH,
|
|
48
50
|
tokenizer_path: DEFAULT_TOKENIZER_PATH,
|
|
49
51
|
device: Candle::Device.best,
|
|
50
52
|
model_type: DEFAULT_EMBEDDING_MODEL_TYPE,
|
|
51
|
-
embedding_size: nil
|
|
53
|
+
embedding_size: nil,
|
|
54
|
+
max_length: nil)
|
|
52
55
|
$stderr.puts "[DEPRECATION] `EmbeddingModel.new` is deprecated. Please use `EmbeddingModel.from_pretrained` instead."
|
|
53
|
-
_create(model_path, tokenizer_path, device, model_type, embedding_size)
|
|
56
|
+
_create(model_path, tokenizer_path, device, model_type, embedding_size, max_length)
|
|
54
57
|
end
|
|
55
58
|
# Returns the embedding for a string using the specified pooling method.
|
|
56
59
|
# @param str [String] The input text
|
data/lib/candle/version.rb
CHANGED