Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bindings/node/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions bindings/node/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ export declare class Normalizer {
normalizeString(sequence: string): string
}

export declare class PipelineTokenizer {
static fromFile(path: string): PipelineTokenizer
/**
* `Uint32Array`, not `Vec<u32>`: a JS `Array` costs one napi value per token, which
* on token-dense input is 13× the encode itself (gpt2 chinese 31 vs 616 MB/s).
*/
encode(text: string, addSpecialTokens?: boolean | undefined | null): Uint32Array
/**
* Drops the two remaining per-call costs of `encode`: the JS string → UTF-8 copy
* (as fast as the tokenizer itself, so it halves throughput) and the fresh
* ArrayBuffer (388 ns of a 789 ns call). Returns how many ids were written.
*/
encodeBytesInto(text: Uint8Array, out: Uint32Array, addSpecialTokens?: boolean | undefined | null): number
}

/** PreTokenizers */
export declare class PreTokenizer {
preTokenizeString(sequence: string): [string, [number, number]][]
Expand Down
1 change: 1 addition & 0 deletions bindings/node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@ module.exports.Encoding = nativeBinding.Encoding
module.exports.JsEncoding = nativeBinding.JsEncoding
module.exports.Model = nativeBinding.Model
module.exports.Normalizer = nativeBinding.Normalizer
module.exports.PipelineTokenizer = nativeBinding.PipelineTokenizer
module.exports.PreTokenizer = nativeBinding.PreTokenizer
module.exports.Processor = nativeBinding.Processor
module.exports.Tokenizer = nativeBinding.Tokenizer
Expand Down
1 change: 1 addition & 0 deletions bindings/node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod decoders;
pub mod encoding;
pub mod models;
pub mod normalizers;
pub mod pipeline;
pub mod pre_tokenizers;
pub mod processors;
pub mod tasks;
Expand Down
62 changes: 62 additions & 0 deletions bindings/node/src/pipeline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
extern crate tokenizers as tk;

use napi::bindgen_prelude::*;
use napi_derive::napi;
use tk::tokenizer::pipeline::PipelineTokenizer as Pipeline;

fn err<E: std::fmt::Display>(e: E) -> Error {
Error::from_reason(format!("{e}"))
}

#[napi]
pub struct PipelineTokenizer(Pipeline);

#[napi]
impl PipelineTokenizer {
#[napi(factory)]
pub fn from_file(path: String) -> Result<Self> {
let tok = tk::Tokenizer::from_file(path).map_err(err)?;
Ok(Self(Pipeline::try_from(&tok).map_err(err)?))
}

/// `Uint32Array`, not `Vec<u32>`: a JS `Array` costs one napi value per token, which
/// on token-dense input is 13× the encode itself (gpt2 chinese 31 vs 616 MB/s).
#[napi]
pub fn encode(&self, text: String, add_special_tokens: Option<bool>) -> Result<Uint32Array> {
let ids = self
.0
.encode(&text, add_special_tokens.unwrap_or(true))
.map_err(err)?;
Ok(Uint32Array::new(ids.iter().map(|t| t.id).collect()))
}

/// Drops the two remaining per-call costs of `encode`: the JS string → UTF-8 copy
/// (as fast as the tokenizer itself, so it halves throughput) and the fresh
/// ArrayBuffer (388 ns of a 789 ns call). Returns how many ids were written.
#[napi]
pub fn encode_bytes_into(
&self,
text: &[u8],
mut out: Uint32Array,
add_special_tokens: Option<bool>,
) -> Result<u32> {
let text = std::str::from_utf8(text).map_err(err)?;
let ids = self
.0
.encode(text, add_special_tokens.unwrap_or(true))
.map_err(err)?;
// SAFETY: JS is blocked for this synchronous call, so nothing else aliases `out`.
let dst = unsafe { out.as_mut() };
if ids.len() > dst.len() {
return Err(err(format!(
"need {} ids, buffer holds {}",
ids.len(),
dst.len()
)));
}
for (d, t) in dst.iter_mut().zip(&ids) {
*d = t.id;
}
Ok(ids.len() as u32)
}
}
Loading