MisterAI's picture
download
raw
70.9 kB
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <sentencepiece.pb.h>
#include <sentencepiece_processor.h>
#include <sentencepiece_trainer.h>
#include <algorithm>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "absl/status/status.h"
namespace py = pybind11;
namespace {
// Helper to check if a python object is bytes or unicode, and get it as
// std::string_view without copying.
struct PyInputStringView {
bool is_bytes = false;
std::string_view value;
explicit PyInputStringView(const py::object& obj) {
if (py::isinstance<py::bytes>(obj)) {
is_bytes = true;
value = obj.cast<std::string_view>();
} else if (py::isinstance<py::str>(obj)) {
is_bytes = false;
value = obj.cast<std::string_view>();
} else {
throw py::type_error("Input must be str or bytes");
}
}
};
// Helper to convert std::string to py::str or py::bytes based on flag.
py::object ToPyString(const std::string& str, bool is_bytes) {
if (is_bytes) {
return py::bytes(str);
} else {
return py::str(str);
}
}
// Helper to convert std::vector<std::string> to py::list of py::str or
// py::bytes.
py::list ToPyStringList(const std::vector<std::string>& vec, bool is_bytes) {
py::list l(vec.size());
if (is_bytes) {
for (size_t i = 0; i < vec.size(); ++i) {
l[i] = py::bytes(vec[i]);
}
} else {
for (size_t i = 0; i < vec.size(); ++i) {
l[i] = py::str(vec[i]);
}
}
return l;
}
// Exception translator
void RegisterExceptionTranslator() {
py::register_exception_translator([](std::exception_ptr p) {
try {
if (p) std::rethrow_exception(p);
} catch (const absl::Status& status) {
std::string msg = status.ToString();
switch (status.code()) {
case absl::StatusCode::kNotFound:
throw std::runtime_error(msg); // Maps to RuntimeError
case absl::StatusCode::kOutOfRange:
throw std::out_of_range(msg); // Maps to IndexError
case absl::StatusCode::kInvalidArgument:
throw std::invalid_argument(msg); // Maps to ValueError
default:
throw std::runtime_error(msg); // Maps to RuntimeError
}
}
});
}
// PySentenceIterator wraps a Python iterator and implements
// sentencepiece::SentenceIterator.
class PySentenceIterator final : public sentencepiece::SentenceIterator {
public:
explicit PySentenceIterator(py::iterator it) : it_(std::move(it)) { Next(); }
bool done() const override { return done_; }
void Next() override {
py::gil_scoped_acquire acquire;
if (it_ == py::iterator::sentinel()) {
done_ = true;
return;
}
try {
py::object item = py::reinterpret_borrow<py::object>(*it_);
if (py::isinstance<py::str>(item) || py::isinstance<py::bytes>(item)) {
value_ = item.cast<std::string>();
} else {
status_ = absl::Status(absl::StatusCode::kInvalidArgument,
"Iterator must return str or bytes.");
done_ = true;
return;
}
// Strip trailing \r or \n
while (!value_.empty() &&
(value_.back() == '\r' || value_.back() == '\n')) {
value_.pop_back();
}
++it_;
} catch (const py::error_already_set& e) {
status_ = absl::Status(absl::StatusCode::kInternal, e.what());
done_ = true;
}
}
const std::string& value() const override { return value_; }
absl::Status status() const override { return status_; }
private:
py::iterator it_;
std::string value_;
absl::Status status_;
bool done_ = false;
};
// Helper functions for RewriteIds (adapted from sentencepiece_swig.h)
absl::Status RewriteIds(const sentencepiece::SentencePieceProcessor& sp,
std::vector<int>* ids, bool add_bos, bool add_eos,
bool reverse) {
if (add_bos && sp.bos_id() == -1) {
return absl::InvalidArgumentError(
"BOS token is not defined as a control symbol in this model.");
}
if (add_eos && sp.eos_id() == -1) {
return absl::InvalidArgumentError(
"EOS token is not defined as a control symbol in this model.");
}
if (!add_bos && !add_eos && !reverse) return absl::OkStatus();
if (reverse) std::reverse(ids->begin(), ids->end());
if (add_bos) {
ids->insert(ids->begin(), sp.bos_id());
}
if (add_eos) {
ids->push_back(sp.eos_id());
}
return absl::OkStatus();
}
absl::Status RewriteIds(const sentencepiece::SentencePieceProcessor& sp,
std::vector<std::string>* pieces, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece) {
if (add_bos && sp.bos_id() == -1) {
return absl::InvalidArgumentError(
"BOS token is not defined as a control symbol in this model.");
}
if (add_eos && sp.eos_id() == -1) {
return absl::InvalidArgumentError(
"EOS token is not defined as a control symbol in this model.");
}
if (!add_bos && !add_eos && !reverse && !emit_unk_piece)
return absl::OkStatus();
if (reverse) std::reverse(pieces->begin(), pieces->end());
if (add_bos) {
pieces->insert(pieces->begin(), sp.IdToPiece(sp.bos_id()));
}
if (add_eos) {
pieces->push_back(sp.IdToPiece(sp.eos_id()));
}
if (emit_unk_piece) {
const auto& unk = sp.IdToPiece(sp.unk_id());
for (auto& piece : *pieces) {
const int id = sp.PieceToId(piece);
if (id == sp.unk_id()) {
piece = unk;
}
}
}
return absl::OkStatus();
}
void RewriteIdsThrowException(const sentencepiece::SentencePieceProcessor& sp,
std::vector<int>* ids, bool add_bos, bool add_eos,
bool reverse) {
auto status = RewriteIds(sp, ids, add_bos, add_eos, reverse);
if (!status.ok()) throw status;
}
void RewriteIdsThrowException(const sentencepiece::SentencePieceProcessor& sp,
std::vector<std::string>* pieces, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece) {
auto status =
RewriteIds(sp, pieces, add_bos, add_eos, reverse, emit_unk_piece);
if (!status.ok()) throw status;
}
absl::Status CheckIds(absl::Span<const int> ids, int num_pieces) {
for (int id : ids) {
if (id < 0 || id >= num_pieces) {
return absl::Status(absl::StatusCode::kOutOfRange,
"piece id is out of range.");
}
}
return absl::OkStatus();
}
void CheckIdsThrowException(absl::Span<const int> ids, int num_pieces) {
auto status = CheckIds(ids, num_pieces);
if (!status.ok()) throw status;
}
int GetNumThreads(int num_threads) {
if (num_threads < 0) {
return std::thread::hardware_concurrency();
}
return std::max<int>(1, std::min<int>(num_threads, 65536));
}
class PyThreadPool {
public:
explicit PyThreadPool(int num_threads)
: resolved_num_threads_(GetNumThreads(num_threads)),
pool_(std::make_unique<sentencepiece::ThreadPool>(
resolved_num_threads_)) {}
sentencepiece::ThreadPool* get() { return pool_.get(); }
int num_threads() const { return resolved_num_threads_; }
private:
int resolved_num_threads_;
std::unique_ptr<sentencepiece::ThreadPool> pool_;
};
// Helper class to manage ThreadPool lifetime and acquisition in bindings
class WorkerPool {
public:
WorkerPool(int num_threads, py::object thread_pool) {
if (!thread_pool.is_none()) {
pool_ = thread_pool.cast<PyThreadPool*>()->get();
} else {
pool_impl_ = std::make_unique<sentencepiece::ThreadPool>(
GetNumThreads(num_threads));
pool_ = pool_impl_.get();
}
}
sentencepiece::ThreadPool* get() { return pool_; }
private:
sentencepiece::ThreadPool* pool_ = nullptr;
std::unique_ptr<sentencepiece::ThreadPool> pool_impl_;
};
// Wrapper to cast py::list to std::vector<std::string_view> and keep
// the underlying Python objects alive for the lifetime of this object.
class PyListStringViewVector {
public:
explicit PyListStringViewVector(const py::list& ins) {
keep_alive_.reserve(ins.size());
views_.reserve(ins.size());
for (size_t i = 0; i < ins.size(); ++i) {
try {
py::object obj = py::reinterpret_borrow<py::object>(ins[i]);
views_.push_back(obj.cast<std::string_view>());
keep_alive_.push_back(std::move(obj));
} catch (const py::cast_error&) {
throw py::type_error("List elements must be str or bytes");
}
}
}
absl::Span<const absl::string_view> views() const { return views_; }
size_t size() const { return views_.size(); }
bool empty() const { return views_.empty(); }
absl::string_view operator[](size_t i) const { return views_[i]; }
private:
std::vector<py::object> keep_alive_;
std::vector<absl::string_view> views_;
};
// Wrapper class to hold either a zero-copy Span or an owned vector of ints.
class IntSpanOrVector {
public:
IntSpanOrVector() : is_owned_(false) {}
explicit IntSpanOrVector(absl::Span<const int> span)
: span_(span), is_owned_(false) {}
explicit IntSpanOrVector(std::vector<int>&& vec)
: vec_(std::move(vec)), is_owned_(true) {}
IntSpanOrVector(const IntSpanOrVector&) = delete;
IntSpanOrVector& operator=(const IntSpanOrVector&) = delete;
IntSpanOrVector(IntSpanOrVector&&) = default;
IntSpanOrVector& operator=(IntSpanOrVector&&) = default;
absl::Span<const int> span() const {
return is_owned_ ? absl::Span<const int>(vec_) : span_;
}
private:
std::vector<int> vec_;
absl::Span<const int> span_;
bool is_owned_;
};
// Wrapper for std::vector<int> to expose it as a Python buffer
class VectorBuffer {
public:
VectorBuffer() = default;
explicit VectorBuffer(std::vector<int>&& vec) : vec_(std::move(vec)) {}
const int* data() const { return vec_.data(); }
size_t size() const { return vec_.size(); }
private:
std::vector<int> vec_;
};
bool IsIntegerFormat(const std::string& format) {
if (format.empty()) return false;
char c = format.back();
return (c == 'b' || c == 'B' || c == 'h' || c == 'H' || c == 'i' ||
c == 'I' || c == 'l' || c == 'L' || c == 'q' || c == 'Q' ||
c == 'n' || c == 'N');
}
// Helper to convert py::object (list, tuple, numpy array, etc) to
// std::vector<int>
IntSpanOrVector CastToIntSpanOrVector(const py::object& ids_obj) {
if (py::isinstance<py::buffer>(ids_obj)) {
try {
py::buffer_info info = ids_obj.cast<py::buffer>().request();
if (!IsIntegerFormat(info.format)) {
throw py::type_error("Buffer must be of integer type");
}
if (info.ndim != 1) {
throw py::type_error("Buffer must be 1-dimensional");
}
if (info.itemsize != 4 && info.itemsize != 8) {
throw py::type_error(
"Unsupported buffer integer size (must be 32-bit or 64-bit)");
}
const py::ssize_t stride =
info.strides.empty() ? info.itemsize : info.strides[0];
// Zero-copy path: read-only, contiguous, 32-bit int
if (info.readonly && stride == info.itemsize && info.itemsize == 4) {
return IntSpanOrVector(absl::Span<const int>(
static_cast<const int*>(info.ptr), info.shape[0]));
}
std::vector<int> ids(info.shape[0]);
const char* base = static_cast<const char*>(info.ptr);
if (info.itemsize == 4) {
if (stride == info.itemsize) {
std::memcpy(ids.data(), info.ptr, info.shape[0] * 4);
} else {
for (py::ssize_t i = 0; i < info.shape[0]; ++i) {
ids[i] = *reinterpret_cast<const int32_t*>(base + i * stride);
}
}
} else { // info.itemsize == 8
for (py::ssize_t i = 0; i < info.shape[0]; ++i) {
const auto* p = reinterpret_cast<const int64_t*>(base + i * stride);
ids[i] = static_cast<int>(*p);
}
}
return IntSpanOrVector(std::move(ids));
} catch (const py::error_already_set&) {
// Fallback if buffer request fails
}
}
try {
return IntSpanOrVector(ids_obj.cast<std::vector<int>>());
} catch (const py::cast_error&) {
throw py::type_error(
"Input must be an integer buffer, list of integers, or a sequence of "
"integers.");
}
}
// Helper to convert py::object (sequence of sequences/buffers) to
// std::vector<std::vector<int>>
std::vector<IntSpanOrVector> CastToVectorIntSpanOrVector(
const py::object& ins_obj) {
py::sequence seq;
try {
seq = ins_obj.cast<py::sequence>();
} catch (const py::cast_error&) {
throw py::type_error(
"Batch input must be a sequence of sequences or a 2D integer array.");
}
std::vector<IntSpanOrVector> outs;
outs.reserve(seq.size());
for (size_t i = 0; i < seq.size(); ++i) {
outs.push_back(
CastToIntSpanOrVector(py::reinterpret_borrow<py::object>(seq[i])));
}
return outs;
}
template <typename FuncType>
decltype(auto) SingleCall(const sentencepiece::SentencePieceProcessor& self, int id, FuncType func) {
CheckIdsThrowException(absl::Span<const int>(&id, 1), self.GetPieceSize());
return (self.*func)(id);
}
template <typename FuncType>
py::list BatchCall(const sentencepiece::SentencePieceProcessor& self, const py::object& ids_obj, FuncType func) {
IntSpanOrVector ids = CastToIntSpanOrVector(ids_obj);
CheckIdsThrowException(ids.span(), self.GetPieceSize());
py::list outs(ids.span().size());
for (size_t i = 0; i < ids.span().size(); ++i) {
outs[i] = (self.*func)(ids.span()[i]);
}
return outs;
}
#define REGISTER_ID_METHOD(NAME) \
.def(#NAME, \
[](const sentencepiece::SentencePieceProcessor& self, int id) { \
return SingleCall(self, id, &sentencepiece::SentencePieceProcessor::NAME);\
}) \
.def(#NAME, [](const sentencepiece::SentencePieceProcessor& self, \
const py::object& ids_obj) { \
return BatchCall(self, ids_obj, &sentencepiece::SentencePieceProcessor::NAME);\
})
void CheckProtoArgsThrowException(bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece) {
if (add_bos || add_eos || reverse || emit_unk_piece) {
throw absl::Status(absl::StatusCode::kUnimplemented,
"add_bos, add_eos, reverse, and emit_unk_piece is not "
"supported in proto API");
}
}
inline size_t OneCharLen(const char* src) {
return "\1\1\1\1\1\1\1\1\1\1\1\1\2\2\3\4"[(*src & 0xFF) >> 4];
}
std::vector<int> BuildUtf8ToUnicodeMap(absl::string_view orig) {
std::vector<int> utf8_to_unicode(orig.size() + 1, 0);
size_t prev = 0;
int ulen = 0;
absl::string_view str = orig;
while (!str.empty()) {
const size_t mblen =
std::min(str.size(),
static_cast<size_t>(std::max<int>(1, OneCharLen(str.data()))));
for (size_t i = prev; i < prev + mblen; ++i) {
utf8_to_unicode[i] = ulen;
}
++ulen;
prev += mblen;
str.remove_prefix(mblen);
}
utf8_to_unicode[prev] = ulen;
return utf8_to_unicode;
}
py::dict ExtractOffsetMapping(const sentencepiece::SentencePieceText& spt,
bool return_bytes) {
std::vector<int> utf8_to_unicode;
if (!return_bytes) {
utf8_to_unicode = BuildUtf8ToUnicodeMap(spt.text());
}
const size_t num_pieces = spt.pieces_size();
py::list ids_list(num_pieces);
py::list pieces(num_pieces);
py::list offsets(num_pieces);
for (size_t i = 0; i < num_pieces; ++i) {
const auto& piece = spt.pieces(i);
ids_list[i] = piece.id();
if (return_bytes) {
pieces[i] = py::bytes(piece.piece());
offsets[i] = py::make_tuple(piece.begin(), piece.end());
} else {
pieces[i] = py::str(piece.piece());
if (piece.begin() >= utf8_to_unicode.size() ||
piece.end() >= utf8_to_unicode.size()) {
throw py::value_error("Invalid piece offsets in SentencePieceText");
}
int start_unicode = utf8_to_unicode[piece.begin()];
int end_unicode = utf8_to_unicode[piece.end()];
offsets[i] = py::make_tuple(start_unicode, end_unicode);
}
}
py::dict result;
result["text"] = ToPyString(spt.text(), return_bytes);
result["ids"] = ids_list;
result["pieces"] = pieces;
result["offsets"] = offsets;
return result;
}
} // namespace
PYBIND11_MODULE(_sentencepiece, m, py::mod_gil_not_used()) {
RegisterExceptionTranslator();
py::class_<VectorBuffer>(m, "VectorBuffer", py::buffer_protocol())
.def_buffer([](const VectorBuffer& m) -> py::buffer_info {
return py::buffer_info(const_cast<int*>(m.data()), sizeof(int),
py::format_descriptor<int>::format(), 1,
{m.size()}, {sizeof(int)},
true // readonly
);
})
.def("data_address", [](const VectorBuffer& self) {
return reinterpret_cast<uintptr_t>(self.data());
});
// Global functions
m.def("SetRandomGeneratorSeed", &sentencepiece::SetRandomGeneratorSeed);
m.def("SetMinLogLevel", &sentencepiece::SetMinLogLevel);
m.def("SetNBestTimeout", &sentencepiece::SetNBestTimeout);
m.def("SetDataDir", [](const std::string& data_dir) {
sentencepiece::SetDataDir(data_dir);
});
py::class_<PyThreadPool>(m, "ThreadPool")
.def(py::init<int>())
.def("num_threads", &PyThreadPool::num_threads);
py::class_<sentencepiece::SentencePieceProcessor>(m, "SentencePieceProcessor")
.def(py::init<>())
.def("LoadFromFile",
[](sentencepiece::SentencePieceProcessor& self,
const std::string& filename) {
py::gil_scoped_release release;
auto status = self.Load(filename);
if (!status.ok()) throw status;
return true;
})
.def("LoadFromSerializedProto",
[](sentencepiece::SentencePieceProcessor& self,
const py::bytes& serialized) {
std::string_view serialized_view = serialized.cast<std::string_view>();
py::gil_scoped_release release;
auto status = self.LoadFromSerializedProto(serialized_view);
if (!status.ok()) throw status;
return true;
})
.def("status", &sentencepiece::SentencePieceProcessor::status)
.def("SetEncodeExtraOptions",
&sentencepiece::SentencePieceProcessor::SetEncodeExtraOptions)
.def("SetDecodeExtraOptions",
&sentencepiece::SentencePieceProcessor::SetDecodeExtraOptions)
// Single Encode APIs
.def("_EncodeAsIds",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, bool enable_sampling, int nbest_size,
float alpha, bool add_bos, bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<int> ids;
{
py::gil_scoped_release release;
absl::Status status;
if (enable_sampling) {
status = self.SampleEncode(in.value, nbest_size, alpha, &ids);
} else {
status = self.Encode(in.value, &ids);
}
if (!status.ok()) throw status;
RewriteIdsThrowException(self, &ids, add_bos, add_eos, reverse);
}
return ids;
})
.def("_EncodeAsBuffer",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, bool enable_sampling, int nbest_size,
float alpha, bool add_bos, bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<int> ids;
{
py::gil_scoped_release release;
absl::Status status;
if (enable_sampling) {
status = self.SampleEncode(in.value, nbest_size, alpha, &ids);
} else {
status = self.Encode(in.value, &ids);
}
if (!status.ok()) throw status;
RewriteIdsThrowException(self, &ids, add_bos, add_eos, reverse);
}
return VectorBuffer(std::move(ids));
})
.def("_EncodeAsPieces",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, bool enable_sampling, int nbest_size,
float alpha, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece, bool return_bytes) {
PyInputStringView in(input);
std::vector<std::string> pieces;
{
py::gil_scoped_release release;
absl::Status status;
if (enable_sampling) {
status =
self.SampleEncode(in.value, nbest_size, alpha, &pieces);
} else {
status = self.Encode(in.value, &pieces);
}
if (!status.ok()) throw status;
RewriteIdsThrowException(self, &pieces, add_bos, add_eos,
reverse, emit_unk_piece);
}
return ToPyStringList(pieces, return_bytes);
})
.def("_EncodeAsSerializedProto",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, bool enable_sampling, int nbest_size,
float alpha, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece) {
CheckProtoArgsThrowException(add_bos, add_eos, reverse,
emit_unk_piece);
PyInputStringView in(input);
sentencepiece::SentencePieceText spt;
{
py::gil_scoped_release release;
absl::Status status;
if (enable_sampling) {
status = self.SampleEncode(in.value, nbest_size, alpha, &spt);
} else {
status = self.Encode(in.value, &spt);
}
if (!status.ok()) throw status;
}
return py::bytes(spt.SerializeAsString());
})
.def("_EncodeAsOffsetMapping",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, bool enable_sampling, int nbest_size,
float alpha, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece, bool return_bytes) {
PyInputStringView in(input);
sentencepiece::SentencePieceText spt;
{
py::gil_scoped_release release;
absl::Status status;
if (enable_sampling) {
status = self.SampleEncode(in.value, nbest_size, alpha, &spt);
} else {
status = self.Encode(in.value, &spt);
}
if (!status.ok()) throw status;
}
std::vector<int> utf8_to_unicode;
if (!return_bytes) {
utf8_to_unicode = BuildUtf8ToUnicodeMap(in.value);
}
const size_t num_pieces = spt.pieces_size();
py::list ids(num_pieces);
py::list pieces(num_pieces);
py::list offsets(num_pieces);
for (size_t i = 0; i < num_pieces; ++i) {
const auto& piece = spt.pieces(i);
ids[i] = piece.id();
if (return_bytes) {
pieces[i] = py::bytes(piece.piece());
offsets[i] = py::make_tuple(piece.begin(), piece.end());
} else {
pieces[i] = py::str(piece.piece());
if (piece.begin() >= utf8_to_unicode.size() ||
piece.end() >= utf8_to_unicode.size()) {
throw py::value_error("Invalid piece offsets in SentencePieceText");
}
int start_unicode = utf8_to_unicode[piece.begin()];
int end_unicode = utf8_to_unicode[piece.end()];
offsets[i] = py::make_tuple(start_unicode, end_unicode);
}
}
py::dict result;
result["ids"] = ids;
result["pieces"] = pieces;
result["offsets"] = offsets;
return result;
})
// Batch Encode APIs
.def("_EncodeAsIdsBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool,
bool enable_sampling, int nbest_size, float alpha, bool add_bos,
bool add_eos, bool reverse) {
PyListStringViewVector C_ins(ins);
std::vector<std::vector<int>> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
std::vector<int> out;
absl::Status s;
if (enable_sampling) {
s = self.SampleEncode(C_ins[i], nbest_size, alpha, &out);
} else {
s = self.Encode(C_ins[i], &out);
}
if (!s.ok()) return s;
s = RewriteIds(self, &out, add_bos, add_eos, reverse);
if (!s.ok()) return s;
outs[i] = std::move(out);
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
return outs;
})
.def("_EncodeAsBufferBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool,
bool enable_sampling, int nbest_size, float alpha, bool add_bos,
bool add_eos, bool reverse) {
PyListStringViewVector C_ins(ins);
std::vector<std::vector<int>> temp_outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
std::vector<int> out;
absl::Status s;
if (enable_sampling) {
s = self.SampleEncode(C_ins[i], nbest_size, alpha, &out);
} else {
s = self.Encode(C_ins[i], &out);
}
if (!s.ok()) return s;
s = RewriteIds(self, &out, add_bos, add_eos, reverse);
if (!s.ok()) return s;
temp_outs[i] = std::move(out);
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
std::vector<VectorBuffer> outs(ins.size());
for (size_t i = 0; i < ins.size(); ++i) {
outs[i] = VectorBuffer(std::move(temp_outs[i]));
}
return outs;
})
.def("_EncodeAsPiecesBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool,
bool enable_sampling, int nbest_size, float alpha, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece,
bool return_bytes) {
if (ins.empty()) return py::list();
PyListStringViewVector C_ins(ins);
std::vector<std::vector<std::string>> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
std::vector<std::string> out;
absl::Status s;
if (enable_sampling) {
s = self.SampleEncode(C_ins[i], nbest_size, alpha, &out);
} else {
s = self.Encode(C_ins[i], &out);
}
if (!s.ok()) return s;
s = RewriteIds(self, &out, add_bos, add_eos, reverse,
emit_unk_piece);
if (!s.ok()) return s;
outs[i] = std::move(out);
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = ToPyStringList(outs[i], return_bytes);
}
return py_outs;
})
.def("_EncodeAsSerializedProtoBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool,
bool enable_sampling, int nbest_size, float alpha, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece) {
CheckProtoArgsThrowException(add_bos, add_eos, reverse,
emit_unk_piece);
PyListStringViewVector C_ins(ins);
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
sentencepiece::SentencePieceText spt;
absl::Status s;
if (enable_sampling) {
s = self.SampleEncode(C_ins[i], nbest_size, alpha, &spt);
} else {
s = self.Encode(C_ins[i], &spt);
}
if (!s.ok()) return s;
outs[i] = spt.SerializeAsString();
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = py::bytes(outs[i]);
}
return py_outs;
})
.def("_EncodeAsOffsetMappingBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool,
bool enable_sampling, int nbest_size, float alpha, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece,
bool return_bytes) {
PyListStringViewVector C_ins(ins);
std::vector<sentencepiece::SentencePieceText> spts(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
absl::Status s;
if (enable_sampling) {
s = self.SampleEncode(C_ins[i], nbest_size, alpha,
&spts[i]);
} else {
s = self.Encode(C_ins[i], &spts[i]);
}
if (!s.ok()) return s;
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_results(ins.size());
for (size_t batch_idx = 0; batch_idx < ins.size(); ++batch_idx) {
absl::string_view orig = C_ins[batch_idx];
const auto& spt = spts[batch_idx];
std::vector<int> utf8_to_unicode;
if (!return_bytes) {
utf8_to_unicode = BuildUtf8ToUnicodeMap(orig);
}
const size_t num_pieces = spt.pieces_size();
py::list ids(num_pieces);
py::list pieces(num_pieces);
py::list offsets(num_pieces);
for (size_t i = 0; i < num_pieces; ++i) {
const auto& piece = spt.pieces(i);
ids[i] = piece.id();
if (return_bytes) {
pieces[i] = py::bytes(piece.piece());
offsets[i] = py::make_tuple(piece.begin(), piece.end());
} else {
pieces[i] = py::str(piece.piece());
if (piece.begin() >= utf8_to_unicode.size() ||
piece.end() >= utf8_to_unicode.size()) {
throw py::value_error("Invalid piece offsets in SentencePieceText");
}
int start_unicode = utf8_to_unicode[piece.begin()];
int end_unicode = utf8_to_unicode[piece.end()];
offsets[i] = py::make_tuple(start_unicode, end_unicode);
}
}
py::dict result;
result["ids"] = ids;
result["pieces"] = pieces;
result["offsets"] = offsets;
py_results[batch_idx] = result;
}
return py_results;
})
// Single Decode APIs
.def("_DecodeIds",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ids_obj) {
IntSpanOrVector ids = CastToIntSpanOrVector(ids_obj);
std::string detok;
{
py::gil_scoped_release release;
CheckIdsThrowException(ids.span(), self.GetPieceSize());
auto status = self.Decode(ids.span(), &detok);
if (!status.ok()) throw status;
}
return py::str(detok);
})
.def("_DecodeIdsAsBytes",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ids_obj) {
IntSpanOrVector ids = CastToIntSpanOrVector(ids_obj);
std::string detok;
{
py::gil_scoped_release release;
CheckIdsThrowException(ids.span(), self.GetPieceSize());
auto status = self.Decode(ids.span(), &detok);
if (!status.ok()) throw status;
}
return py::bytes(detok);
})
.def("_DecodePieces",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& pieces) {
if (pieces.empty()) return py::object(py::str(""));
bool is_bytes = py::isinstance<py::bytes>(pieces[0]);
PyListStringViewVector C_pieces(pieces);
std::string detok;
{
py::gil_scoped_release release;
auto status = self.Decode(C_pieces.views(), &detok);
if (!status.ok()) throw status;
}
return ToPyString(detok, is_bytes);
})
.def("_DecodePiecesAsBytes",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& pieces) {
if (pieces.empty()) return py::bytes("");
PyListStringViewVector C_pieces(pieces);
std::string detok;
{
py::gil_scoped_release release;
auto status = self.Decode(C_pieces.views(), &detok);
if (!status.ok()) throw status;
}
return py::bytes(detok);
})
.def("_DecodeIdsAsSerializedProto",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ids_obj) {
IntSpanOrVector ids = CastToIntSpanOrVector(ids_obj);
sentencepiece::SentencePieceText spt;
{
py::gil_scoped_release release;
CheckIdsThrowException(ids.span(), self.GetPieceSize());
auto status = self.Decode(ids.span(), &spt);
if (!status.ok()) throw status;
}
return py::bytes(spt.SerializeAsString());
})
.def("_DecodePiecesAsSerializedProto",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& pieces) {
PyListStringViewVector C_pieces(pieces);
sentencepiece::SentencePieceText spt;
{
py::gil_scoped_release release;
auto status = self.Decode(C_pieces.views(), &spt);
if (!status.ok()) throw status;
}
return py::bytes(spt.SerializeAsString());
})
.def("_DecodeAsOffsetMapping",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, bool return_bytes) {
py::object normalized_input = input;
if (py::isinstance<py::tuple>(input)) {
normalized_input = py::list(input);
}
bool input_is_pieces = false;
bool detected_bytes = false;
if (py::isinstance<py::list>(normalized_input)) {
py::sequence seq = normalized_input;
if (!seq.empty()) {
py::object first = seq[0];
if (py::isinstance<py::str>(first)) {
input_is_pieces = true;
} else if (py::isinstance<py::bytes>(first)) {
input_is_pieces = true;
detected_bytes = true;
}
}
}
if (input_is_pieces) {
return_bytes = detected_bytes;
}
sentencepiece::SentencePieceText spt;
absl::Status status;
if (input_is_pieces) {
PyListStringViewVector pieces(normalized_input.cast<py::list>());
{
py::gil_scoped_release release;
status = self.Decode(pieces.views(), &spt);
}
} else {
IntSpanOrVector ids = CastToIntSpanOrVector(normalized_input);
{
py::gil_scoped_release release;
CheckIdsThrowException(ids.span(), self.GetPieceSize());
status = self.Decode(ids.span(), &spt);
}
}
if (!status.ok()) throw status;
return ExtractOffsetMapping(spt, return_bytes);
})
// Batch Decode APIs
.def("_DecodeIdsBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ins_obj, int num_threads,
py::object thread_pool) {
std::vector<IntSpanOrVector> ins = CastToVectorIntSpanOrVector(ins_obj);
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
auto s = CheckIds(ins[i].span(), self.GetPieceSize());
if (!s.ok()) return s;
return self.Decode(ins[i].span(), &outs[i]);
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = py::str(outs[i]);
}
return py_outs;
})
.def("_DecodeIdsAsBytesBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ins_obj, int num_threads,
py::object thread_pool) {
std::vector<IntSpanOrVector> ins = CastToVectorIntSpanOrVector(ins_obj);
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
auto s = CheckIds(ins[i].span(), self.GetPieceSize());
if (!s.ok()) return s;
return self.Decode(ins[i].span(), &outs[i]);
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = py::bytes(outs[i]);
}
return py_outs;
})
.def("_DecodeIdsAsSerializedProtoBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ins_obj, int num_threads,
py::object thread_pool) {
std::vector<IntSpanOrVector> ins = CastToVectorIntSpanOrVector(ins_obj);
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
auto s = CheckIds(ins[i].span(), self.GetPieceSize());
if (!s.ok()) return s;
sentencepiece::SentencePieceText spt;
s = self.Decode(ins[i].span(), &spt);
if (!s.ok()) return s;
outs[i] = spt.SerializeAsString();
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = py::bytes(outs[i]);
}
return py_outs;
})
.def("_DecodePiecesBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool) {
if (ins.empty()) return py::list();
py::list sublist0 = ins[0].cast<py::list>();
if (sublist0.empty()) return py::list();
bool is_bytes = py::isinstance<py::bytes>(sublist0[0]);
std::vector<PyListStringViewVector> C_ins_wrappers;
C_ins_wrappers.reserve(ins.size());
std::vector<absl::Span<const absl::string_view>> C_ins(ins.size());
for (size_t i = 0; i < ins.size(); ++i) {
C_ins_wrappers.emplace_back(ins[i].cast<py::list>());
C_ins[i] = C_ins_wrappers.back().views();
}
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) { return self.Decode(C_ins[i], &outs[i]); },
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = ToPyString(outs[i], is_bytes);
}
return py_outs;
})
.def("_DecodePiecesAsBytesBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool) {
if (ins.empty()) return py::list();
std::vector<PyListStringViewVector> C_ins_wrappers;
C_ins_wrappers.reserve(ins.size());
std::vector<absl::Span<const absl::string_view>> C_ins(ins.size());
for (size_t i = 0; i < ins.size(); ++i) {
C_ins_wrappers.emplace_back(ins[i].cast<py::list>());
C_ins[i] = C_ins_wrappers.back().views();
}
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) { return self.Decode(C_ins[i], &outs[i]); },
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = py::bytes(outs[i]);
}
return py_outs;
})
.def("_DecodePiecesAsSerializedProtoBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::list& ins, int num_threads, py::object thread_pool) {
if (ins.empty()) return py::list();
std::vector<PyListStringViewVector> C_ins_wrappers;
C_ins_wrappers.reserve(ins.size());
std::vector<absl::Span<const absl::string_view>> C_ins(ins.size());
for (size_t i = 0; i < ins.size(); ++i) {
C_ins_wrappers.emplace_back(ins[i].cast<py::list>());
C_ins[i] = C_ins_wrappers.back().views();
}
std::vector<std::string> outs(ins.size());
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
ins.size(),
[&](size_t i) {
sentencepiece::SentencePieceText spt;
auto s = self.Decode(C_ins[i], &spt);
if (!s.ok()) return s;
outs[i] = spt.SerializeAsString();
return absl::OkStatus();
},
*pool.get());
if (!status.ok()) throw status;
}
py::list py_outs(outs.size());
for (size_t i = 0; i < outs.size(); ++i) {
py_outs[i] = py::bytes(outs[i]);
}
return py_outs;
})
.def("_DecodeAsOffsetMappingBatch",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& ins_obj, int num_threads,
py::object thread_pool, bool return_bytes) {
py::object normalized_ins = ins_obj;
if (py::isinstance<py::tuple>(ins_obj)) {
normalized_ins = py::list(ins_obj);
}
py::sequence seq_ins = normalized_ins;
if (seq_ins.empty()) return py::list();
py::list py_ins(seq_ins.size());
for (size_t i = 0; i < seq_ins.size(); ++i) {
py::object inner = seq_ins[i];
if (py::isinstance<py::tuple>(inner)) {
py_ins[i] = py::list(inner);
} else {
py_ins[i] = inner;
}
}
bool is_pieces_batch = false;
bool detected_bytes = false;
if (!py_ins.empty()) {
py::object first_seq = py_ins[0];
if (py::isinstance<py::list>(first_seq) ||
py::isinstance<py::tuple>(first_seq)) {
py::sequence inner_seq = first_seq;
if (!inner_seq.empty()) {
py::object first_item = inner_seq[0];
if (py::isinstance<py::str>(first_item)) {
is_pieces_batch = true;
} else if (py::isinstance<py::bytes>(first_item)) {
is_pieces_batch = true;
detected_bytes = true;
}
}
}
}
if (is_pieces_batch) {
return_bytes = detected_bytes;
}
std::vector<sentencepiece::SentencePieceText> spts(py_ins.size());
WorkerPool pool(num_threads, thread_pool);
if (is_pieces_batch) {
std::vector<PyListStringViewVector> C_ins_wrappers;
C_ins_wrappers.reserve(py_ins.size());
std::vector<absl::Span<const absl::string_view>> C_ins(py_ins.size());
for (size_t i = 0; i < py_ins.size(); ++i) {
C_ins_wrappers.emplace_back(py_ins[i].cast<py::list>());
C_ins[i] = C_ins_wrappers.back().views();
}
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
py_ins.size(),
[&](size_t i) { return self.Decode(C_ins[i], &spts[i]); },
*pool.get());
if (!status.ok()) throw status;
}
} else {
std::vector<IntSpanOrVector> C_ins =
CastToVectorIntSpanOrVector(py_ins);
{
py::gil_scoped_release release;
auto status = sentencepiece::RunBatch(
py_ins.size(),
[&](size_t i) {
auto s = CheckIds(C_ins[i].span(), self.GetPieceSize());
if (!s.ok()) return s;
return self.Decode(C_ins[i].span(), &spts[i]);
},
*pool.get());
if (!status.ok()) throw status;
}
}
py::list py_outs(spts.size());
for (size_t i = 0; i < spts.size(); ++i) {
py_outs[i] = ExtractOffsetMapping(spts[i], return_bytes);
}
return py_outs;
})
// NBest APIs
.def("_NBestEncodeAsIds",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int nbest_size, bool add_bos,
bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<std::vector<int>> idss;
{
py::gil_scoped_release release;
auto status = self.NBestEncode(in.value, nbest_size, &idss);
if (!status.ok()) throw status;
for (auto& ids : idss) {
RewriteIdsThrowException(self, &ids, add_bos, add_eos,
reverse);
}
}
return idss;
})
.def("_NBestEncodeAsBuffer",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int nbest_size, bool add_bos,
bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<std::vector<int>> idss;
{
py::gil_scoped_release release;
auto status = self.NBestEncode(in.value, nbest_size, &idss);
if (!status.ok()) throw status;
for (auto& ids : idss) {
RewriteIdsThrowException(self, &ids, add_bos, add_eos,
reverse);
}
}
std::vector<VectorBuffer> outs(idss.size());
for (size_t i = 0; i < idss.size(); ++i) {
outs[i] = VectorBuffer(std::move(idss[i]));
}
return outs;
})
.def("_NBestEncodeAsPieces",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int nbest_size, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece,
bool return_bytes) {
PyInputStringView in(input);
std::vector<std::vector<std::string>> piecess;
{
py::gil_scoped_release release;
auto status = self.NBestEncode(in.value, nbest_size, &piecess);
if (!status.ok()) throw status;
for (auto& pieces : piecess) {
RewriteIdsThrowException(self, &pieces, add_bos, add_eos,
reverse, emit_unk_piece);
}
}
py::list py_outs(piecess.size());
for (size_t i = 0; i < piecess.size(); ++i) {
py_outs[i] = ToPyStringList(piecess[i], return_bytes);
}
return py_outs;
})
.def("_NBestEncodeAsSerializedProto",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int nbest_size, bool add_bos,
bool add_eos, bool reverse, bool emit_unk_piece) {
CheckProtoArgsThrowException(add_bos, add_eos, reverse,
emit_unk_piece);
PyInputStringView in(input);
sentencepiece::NBestSentencePieceText nbest_spt;
{
py::gil_scoped_release release;
auto status = self.NBestEncode(in.value, nbest_size, &nbest_spt);
if (!status.ok()) throw status;
}
return py::bytes(nbest_spt.SerializeAsString());
})
// Sample and Score APIs
.def("_SampleEncodeAndScoreAsIds",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int num_samples, float alpha, bool wor,
bool include_best, bool add_bos, bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<std::pair<std::vector<int>, float>> idss;
{
py::gil_scoped_release release;
auto status = self.SampleEncodeAndScore(
in.value, num_samples, alpha, wor, include_best, &idss);
if (!status.ok()) throw status;
for (auto& ids : idss) {
RewriteIdsThrowException(self, &ids.first, add_bos, add_eos,
reverse);
}
}
return idss;
})
.def("_SampleEncodeAndScoreAsPieces",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int num_samples, float alpha, bool wor,
bool include_best, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece, bool return_bytes) {
PyInputStringView in(input);
std::vector<std::pair<std::vector<std::string>, float>> piecess;
{
py::gil_scoped_release release;
auto status = self.SampleEncodeAndScore(
in.value, num_samples, alpha, wor, include_best, &piecess);
if (!status.ok()) throw status;
for (auto& pieces : piecess) {
RewriteIdsThrowException(self, &pieces.first, add_bos, add_eos,
reverse, emit_unk_piece);
}
}
py::list py_outs(piecess.size());
for (size_t i = 0; i < piecess.size(); ++i) {
py_outs[i] = py::make_tuple(
ToPyStringList(piecess[i].first, return_bytes),
piecess[i].second);
}
return py_outs;
})
.def("_SampleEncodeAndScoreAsSerializedProto",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int num_samples, float alpha, bool wor,
bool include_best, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece) {
CheckProtoArgsThrowException(add_bos, add_eos, reverse,
emit_unk_piece);
PyInputStringView in(input);
sentencepiece::NBestSentencePieceText samples_spt;
{
py::gil_scoped_release release;
auto status =
self.SampleEncodeAndScore(in.value, num_samples, alpha, wor,
include_best, &samples_spt);
if (!status.ok()) throw status;
}
return py::bytes(samples_spt.SerializeAsString());
})
// Parallel Encode APIs
.def(
"_ParallelEncodeAsIds",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int chunk_len, int num_threads,
py::object thread_pool, bool add_bos, bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<int> ids;
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status =
self.ParallelEncode(in.value, chunk_len, *pool.get(), &ids);
if (!status.ok()) throw status;
RewriteIdsThrowException(self, &ids, add_bos, add_eos, reverse);
}
return ids;
})
.def(
"_ParallelEncodeAsBuffer",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int chunk_len, int num_threads,
py::object thread_pool, bool add_bos, bool add_eos, bool reverse) {
PyInputStringView in(input);
std::vector<int> ids;
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status =
self.ParallelEncode(in.value, chunk_len, *pool.get(), &ids);
if (!status.ok()) throw status;
RewriteIdsThrowException(self, &ids, add_bos, add_eos, reverse);
}
return VectorBuffer(std::move(ids));
})
.def("_ParallelEncodeAsPieces",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int chunk_len, int num_threads,
py::object thread_pool, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece, bool return_bytes) {
PyInputStringView in(input);
std::vector<std::string> pieces;
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status = self.ParallelEncode(in.value, chunk_len,
*pool.get(), &pieces);
if (!status.ok()) throw status;
RewriteIdsThrowException(self, &pieces, add_bos, add_eos,
reverse, emit_unk_piece);
}
return ToPyStringList(pieces, return_bytes);
})
.def("_ParallelEncodeAsSerializedProto",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, int chunk_len, int num_threads,
py::object thread_pool, bool add_bos, bool add_eos, bool reverse,
bool emit_unk_piece) {
CheckProtoArgsThrowException(add_bos, add_eos, reverse,
emit_unk_piece);
PyInputStringView in(input);
sentencepiece::SentencePieceText spt;
WorkerPool pool(num_threads, thread_pool);
{
py::gil_scoped_release release;
auto status =
self.ParallelEncode(in.value, chunk_len, *pool.get(), &spt);
if (!status.ok()) throw status;
}
return py::bytes(spt.SerializeAsString());
})
// Normalize APIs
.def("_Normalize",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input) {
PyInputStringView in(input);
std::string norm;
{
py::gil_scoped_release release;
norm = self.Normalize(in.value);
}
return ToPyString(norm, in.is_bytes);
})
.def("_NormalizeWithOffsets",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input) {
PyInputStringView in(input);
std::string norm;
std::vector<size_t> offsets;
{
py::gil_scoped_release release;
auto status = self.Normalize(in.value, &norm, &offsets);
if (!status.ok()) throw status;
}
if (!in.is_bytes) {
sentencepiece::ConvertToUnicodeAlignment(in.value, norm,
&offsets);
}
return py::make_tuple(ToPyString(norm, in.is_bytes), offsets);
})
// Entropy API
.def("_CalculateEntropy",
[](const sentencepiece::SentencePieceProcessor& self,
const py::object& input, float alpha) {
PyInputStringView in(input);
float entropy = 0.0;
{
py::gil_scoped_release release;
auto status = self.CalculateEntropy(in.value, alpha, &entropy);
if (!status.ok()) throw status;
}
return entropy;
})
// Normalizer Spec Override
.def("_OverrideNormalizerSpec",
[](sentencepiece::SentencePieceProcessor& self,
const std::unordered_map<std::string, std::string>& args) {
absl::Status status;
for (const auto& [key, value] : args) {
status = sentencepiece::SentencePieceTrainer::SetProtoField(
key, value, self.mutable_normalizer_spec());
if (!status.ok()) throw status;
}
})
// Vocab management
.def("GetPieceSize", &sentencepiece::SentencePieceProcessor::GetPieceSize)
.def("PieceToId",
[](const sentencepiece::SentencePieceProcessor& self,
std::string_view piece) { return self.PieceToId(piece); })
.def("PieceToId",
[](const sentencepiece::SentencePieceProcessor& self,
const py::sequence& pieces) {
py::list ids(pieces.size());
for (size_t i = 0; i < pieces.size(); ++i) {
try {
ids[i] = self.PieceToId(pieces[i].cast<std::string_view>());
} catch (const py::cast_error&) {
throw py::type_error("Sequence elements must be str or bytes");
}
}
return ids;
})
REGISTER_ID_METHOD(IdToPiece)
REGISTER_ID_METHOD(GetScore)
REGISTER_ID_METHOD(IsUnknown)
REGISTER_ID_METHOD(IsControl)
REGISTER_ID_METHOD(IsUnused)
REGISTER_ID_METHOD(IsByte)
.def("unk_id", &sentencepiece::SentencePieceProcessor::unk_id)
.def("bos_id", &sentencepiece::SentencePieceProcessor::bos_id)
.def("eos_id", &sentencepiece::SentencePieceProcessor::eos_id)
.def("pad_id", &sentencepiece::SentencePieceProcessor::pad_id)
// Serialization
.def("serialized_model_proto",
[](const sentencepiece::SentencePieceProcessor& self) {
return py::bytes(self.serialized_model_proto());
});
// Bind Trainer. Use py::nodelete holder to avoid trying to delete it because
// destructor is private.
py::class_<
sentencepiece::SentencePieceTrainer,
std::unique_ptr<sentencepiece::SentencePieceTrainer, py::nodelete>>(
m, "SentencePieceTrainer")
.def_static("_TrainFromString",
[](const std::string& arg) {
py::gil_scoped_release release;
auto status =
sentencepiece::SentencePieceTrainer::Train(arg);
if (!status.ok()) throw status;
return true;
})
.def_static("_TrainFromMap",
[](const std::unordered_map<std::string, std::string>& args) {
py::gil_scoped_release release;
auto status =
sentencepiece::SentencePieceTrainer::Train(args);
if (!status.ok()) throw status;
return true;
})
.def_static("_TrainFromMap2",
[](const std::unordered_map<std::string, std::string>& args,
py::iterator iter) {
PySentenceIterator py_iter(std::move(iter));
{
py::gil_scoped_release release;
auto status = sentencepiece::SentencePieceTrainer::Train(
args, &py_iter);
if (!status.ok()) throw status;
}
return true;
})
.def_static("_TrainFromMap3",
[](const std::unordered_map<std::string, std::string>& args) {
std::string model_proto;
{
py::gil_scoped_release release;
auto status = sentencepiece::SentencePieceTrainer::Train(
args, nullptr, &model_proto);
if (!status.ok()) throw status;
}
return py::bytes(model_proto);
})
.def_static("_TrainFromMap4",
[](const std::unordered_map<std::string, std::string>& args,
py::iterator iter) {
std::string model_proto;
PySentenceIterator py_iter(std::move(iter));
{
py::gil_scoped_release release;
auto status = sentencepiece::SentencePieceTrainer::Train(
args, &py_iter, &model_proto);
if (!status.ok()) throw status;
}
return py::bytes(model_proto);
});
// Bind Normalizer
py::class_<sentencepiece::SentencePieceNormalizer>(m,
"SentencePieceNormalizer")
.def(py::init<>())
.def("LoadFromFile",
[](sentencepiece::SentencePieceNormalizer& self,
const std::string& filename) {
py::gil_scoped_release release;
auto status = self.Load(filename);
if (!status.ok()) throw status;
return true;
})
.def("LoadFromSerializedProto",
[](sentencepiece::SentencePieceNormalizer& self,
const py::bytes& serialized) {
std::string_view serialized_view = serialized.cast<std::string_view>();
py::gil_scoped_release release;
auto status = self.LoadFromSerializedProto(serialized_view);
if (!status.ok()) throw status;
return true;
})
.def("LoadFromRuleTSV",
[](sentencepiece::SentencePieceNormalizer& self,
const std::string& filename) {
py::gil_scoped_release release;
auto status = self.LoadFromRuleTSV(filename);
if (!status.ok()) throw status;
return true;
})
.def("LoadFromRuleName",
[](sentencepiece::SentencePieceNormalizer& self,
const std::string& name) {
py::gil_scoped_release release;
auto status = self.LoadFromRuleName(name);
if (!status.ok()) throw status;
return true;
})
.def(
"LoadFromMap",
[](sentencepiece::SentencePieceNormalizer& self,
const std::vector<std::pair<std::string, std::string>>& norm_map) {
py::gil_scoped_release release;
auto status = self.LoadFromMap(norm_map);
if (!status.ok()) throw status;
return true;
})
.def("Decompile",
[](const sentencepiece::SentencePieceNormalizer& self) {
std::vector<std::pair<std::string, std::string>> norm_map;
{
py::gil_scoped_release release;
auto status = self.Decompile(&norm_map);
if (!status.ok()) throw status;
}
return norm_map;
})
.def("_Normalize",
[](const sentencepiece::SentencePieceNormalizer& self,
const py::object& input) {
PyInputStringView in(input);
std::string norm;
{
py::gil_scoped_release release;
auto status = self.Normalize(in.value, &norm);
if (!status.ok()) throw status;
}
return ToPyString(norm, in.is_bytes);
})
.def("_NormalizeWithOffsets",
[](const sentencepiece::SentencePieceNormalizer& self,
const py::object& input) {
PyInputStringView in(input);
std::string norm;
std::vector<size_t> offsets;
{
py::gil_scoped_release release;
auto status = self.Normalize(in.value, &norm, &offsets);
if (!status.ok()) throw status;
}
if (!in.is_bytes) {
sentencepiece::ConvertToUnicodeAlignment(in.value, norm,
&offsets);
}
return py::make_tuple(ToPyString(norm, in.is_bytes), offsets);
})
.def("_SetProtoField",
[](sentencepiece::SentencePieceNormalizer& self,
const std::string& name, bool value) {
auto status = sentencepiece::SentencePieceTrainer::SetProtoField(
name, value ? "1" : "0", self.mutable_normalizer_spec());
if (!status.ok()) throw status;
})
.def("serialized_model_proto",
[](const sentencepiece::SentencePieceNormalizer& self) {
return py::bytes(self.serialized_model_proto());
})
.def("serialized_normalizer_spec",
[](const sentencepiece::SentencePieceNormalizer& self) {
return py::bytes(self.serialized_normalizer_spec());
});
}

Xet Storage Details

Size:
70.9 kB
·
Xet hash:
4bcc363180070963434475dad96463133cb15a58a672ce7b092c2c77accef122

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.