Skip to content

Commit 20c58f6

Browse files
authored
feat: Make python tokenizer config pad strategy better (#344)
Before, `pad_strategy` was passed to `TokenizerConfig` as a str: ```python3 tokenizer_config = TokenizerConfig(pad_strategy="batch_longest") ``` This was nonfunctional, as `fixed` needs a number attached to it. `fixed` means that the tokenizer always pads or truncates to a fixed sequence length, and is used a lot in production deployments. This pull request modifies the interface so that pad strategy options are passed as classes instead of just a string value/enum. For `batch_longest`: ```python3 from encoderfile import TokenizerConfig, BatchLongest, Fixed # batch longest tokenizer_config = TokenizerConfig(pad_strategy=BatchLongest()) # fixed tokenizer_config = TokenizerConfig(pad_strategy=Fixed(512)) ```
1 parent af055cd commit 20c58f6

4 files changed

Lines changed: 90 additions & 16 deletions

File tree

encoderfile-py/python/encoderfile/_core.pyi

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,19 @@ class EncoderfileBuilder:
3737
no_download: bool = False,
3838
): ...
3939

40+
@final
41+
class BatchLongest:
42+
pass
43+
44+
@final
45+
class Fixed:
46+
n: int
47+
48+
def __new__(cls, *, n: int) -> "Fixed": ...
49+
4050
@final
4151
class TokenizerBuildConfig:
42-
pad_strategy: Optional[str]
52+
pad_strategy: Optional[BatchLongest | Fixed]
4353
truncation_side: Optional[str]
4454
truncation_strategy: Optional[str]
4555
max_length: Optional[int]
@@ -48,7 +58,7 @@ class TokenizerBuildConfig:
4858
def __new__(
4959
cls,
5060
*,
51-
pad_strategy: Optional[str] = None,
61+
pad_strategy: Optional[BatchLongest | Fixed] = None,
5262
truncation_side: Optional[str] = None,
5363
truncation_strategy: Optional[str] = None,
5464
max_length: Optional[int] = None,

encoderfile-py/python/encoderfile/enums.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,3 @@ class TokenizerTruncationStrategy(StrEnum):
1717
LongestFirst = "longest_first"
1818
OnlyFirst = "only_first"
1919
OnlySecond = "only_second"
20-
21-
22-
class TokenizerPadStrategy(StrEnum):
23-
BatchLongest = "batch_longest"
24-
Fixed = "fixed"

encoderfile-py/src/builder.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,59 @@ use encoderfile::builder::{
1313
},
1414
};
1515
use encoderfile::common::{Config, ModelConfig};
16+
use pyo3::IntoPyObjectExt;
17+
use pyo3::exceptions::PyTypeError;
1618
use pyo3::types::PyString;
1719
use pyo3::{
1820
exceptions::{PyIOError, PyRuntimeError, PyValueError},
1921
prelude::*,
2022
};
2123

24+
#[pyclass(name = "BatchLongest")]
25+
#[derive(Debug, Clone)]
26+
pub struct PyBatchLongest;
27+
28+
#[pymethods]
29+
impl PyBatchLongest {
30+
#[new]
31+
pub fn new() -> Self {
32+
PyBatchLongest
33+
}
34+
}
35+
36+
impl Default for PyBatchLongest {
37+
fn default() -> Self {
38+
PyBatchLongest
39+
}
40+
}
41+
42+
impl From<PyBatchLongest> for TokenizerPadStrategy {
43+
fn from(_value: PyBatchLongest) -> Self {
44+
TokenizerPadStrategy::BatchLongest
45+
}
46+
}
47+
48+
#[pyclass(name = "Fixed")]
49+
#[derive(Debug, Clone)]
50+
pub struct PyFixed {
51+
#[pyo3(get, set)]
52+
n: usize,
53+
}
54+
55+
#[pymethods]
56+
impl PyFixed {
57+
#[new]
58+
pub fn new(n: usize) -> Self {
59+
Self { n }
60+
}
61+
}
62+
63+
impl From<PyFixed> for TokenizerPadStrategy {
64+
fn from(value: PyFixed) -> Self {
65+
TokenizerPadStrategy::Fixed { fixed: value.n }
66+
}
67+
}
68+
2269
#[pyclass(name = "TokenizerBuildConfig", frozen)]
2370
pub struct PyTokenizerBuildConfig(TokenizerBuildConfig);
2471

@@ -28,18 +75,27 @@ impl PyTokenizerBuildConfig {
2875
#[new]
2976
#[pyo3(signature = (*, pad_strategy = None, truncation_side = None, truncation_strategy = None, max_length = None, stride = None))]
3077
pub fn new(
31-
pad_strategy: Option<String>,
78+
pad_strategy: Option<Bound<'_, PyAny>>,
3279
truncation_side: Option<String>,
3380
truncation_strategy: Option<String>,
3481
max_length: Option<usize>,
3582
stride: Option<usize>,
3683
) -> PyResult<Self> {
37-
let pad_strategy = pad_strategy
38-
.map(|s| {
39-
s.parse::<TokenizerPadStrategy>()
40-
.map_err(|e| PyRuntimeError::new_err(format!("Failed to parse: {:?}", e)))
41-
})
42-
.transpose()?;
84+
let pad_strategy = match pad_strategy {
85+
Some(ps) => {
86+
if let Ok(batch_longest) = ps.cast::<PyBatchLongest>() {
87+
Some(batch_longest.extract::<PyBatchLongest>().unwrap().into())
88+
} else if let Ok(fixed) = ps.cast::<PyFixed>() {
89+
Some(fixed.extract::<PyFixed>().unwrap().into())
90+
} else {
91+
return Err(PyTypeError::new_err(
92+
"Class must be BatchLongest, Fixed, or None",
93+
));
94+
}
95+
}
96+
None => None,
97+
};
98+
4399
let truncation_side = truncation_side
44100
.map(|s| {
45101
s.parse::<TokenizerTruncationSide>()
@@ -63,8 +119,15 @@ impl PyTokenizerBuildConfig {
63119
}
64120

65121
#[getter]
66-
pub fn get_pad_strategy(&self) -> Option<String> {
67-
self.0.pad_strategy.as_ref().map(|s| s.into())
122+
pub fn get_pad_strategy(&self, py: Python<'_>) -> PyResult<Option<Py<PyAny>>> {
123+
match self.0.pad_strategy.as_ref() {
124+
Some(ps) => match ps {
125+
TokenizerPadStrategy::BatchLongest => Some(PyBatchLongest::new().into_py_any(py)),
126+
TokenizerPadStrategy::Fixed { fixed } => Some(PyFixed::new(*fixed).into_py_any(py)),
127+
},
128+
None => None,
129+
}
130+
.transpose()
68131
}
69132

70133
#[getter]

encoderfile-py/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ mod encoderfile {
2323
#[pymodule_export]
2424
use super::builder::PyInspectInfo;
2525

26+
#[pymodule_export]
27+
use super::builder::PyBatchLongest;
28+
29+
#[pymodule_export]
30+
use super::builder::PyFixed;
31+
2632
#[pymodule_export]
2733
use super::builder::PyTokenizerBuildConfig;
2834
}

0 commit comments

Comments
 (0)