From bef41f710a7f936e5d40ecb0cdf47606ceacb521 Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Tue, 15 Sep 2026 19:09:27 -0700 Subject: [PATCH] tokenizer, ml: remove dead code Several pieces outlived the code that used them. The root tokenizer package implemented the GGUF-side vocabularies for the Go engine and the safetensors-to-GGUF converter; nothing has imported it since the converter went. ml/backend.go held the Go engine's Backend, Context and Tensor interfaces, with fs.Config existing only to be returned from them, and a single CUDA template instance under ml/backend/ggml survived the engine removal along with the gitattributes entries for that tree and the CI change-filter globs for it and for the long-gone llama/llama.cpp. From the image generation engine, an integration test group that no test registers, its build tag, and the StepBar progress widget remained. DeviceInfo.IsBetter has no caller at all. All of it goes. Tidying the module file drops the regexp2 dependency and leaves protobuf as an indirect requirement. The llama3.2 tokenizer fixtures stay: the MLX runner's tokenizer uses them for its GGML parity test. --- .gitattributes | 13 - .github/workflows/test.yaml | 2 - fs/config.go | 20 - go.mod | 3 +- go.sum | 2 - integration/reg_groups_test.go | 4 - integration/reg_required_test.go | 2 +- ml/backend.go | 413 - .../fattn-tile-instance-dkq512-dv512.cu | 5 - ml/device.go | 26 - progress/stepbar.go | 33 - tokenizer/bytepairencoding.go | 359 - tokenizer/bytepairencoding_test.go | 639 - tokenizer/sentencepiece.go | 241 - .../sentencepiece/sentencepiece_model.pb.go | 1497 - .../sentencepiece/sentencepiece_model.proto | 333 - tokenizer/sentencepiece_test.go | 172 - tokenizer/testdata/gemma2/tokenizer.model | Bin 4241003 -> 0 bytes tokenizer/testdata/war-and-peace.txt | 63845 ---------------- tokenizer/tokenizer.go | 15 - tokenizer/vocabulary.go | 94 - tokenizer/vocabulary_test.go | 107 - tokenizer/wordpiece.go | 161 - tokenizer/wordpiece_test.go | 53 - 24 files changed, 2 insertions(+), 68037 deletions(-) delete mode 100644 fs/config.go delete mode 100644 ml/backend.go delete mode 100644 ml/backend/ggml/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu delete mode 100644 progress/stepbar.go delete mode 100644 tokenizer/bytepairencoding.go delete mode 100644 tokenizer/bytepairencoding_test.go delete mode 100644 tokenizer/sentencepiece.go delete mode 100644 tokenizer/sentencepiece/sentencepiece_model.pb.go delete mode 100644 tokenizer/sentencepiece/sentencepiece_model.proto delete mode 100644 tokenizer/sentencepiece_test.go delete mode 100644 tokenizer/testdata/gemma2/tokenizer.model delete mode 100644 tokenizer/testdata/war-and-peace.txt delete mode 100644 tokenizer/tokenizer.go delete mode 100644 tokenizer/vocabulary.go delete mode 100644 tokenizer/vocabulary_test.go delete mode 100644 tokenizer/wordpiece.go delete mode 100644 tokenizer/wordpiece_test.go diff --git a/.gitattributes b/.gitattributes index 10c214441..ff7972b6e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,22 +7,9 @@ llama/**/*.cuh linguist-vendored llama/**/*.m linguist-vendored llama/**/*.metal linguist-vendored -ml/backend/**/*.c linguist-vendored -ml/backend/**/*.h linguist-vendored -ml/backend/**/*.cpp linguist-vendored -ml/backend/**/*.hpp linguist-vendored -ml/backend/**/*.cu linguist-vendored -ml/backend/**/*.cuh linguist-vendored -ml/backend/**/*.m linguist-vendored -ml/backend/**/*.metal linguist-vendored -ml/backend/**/*.comp linguist-vendored -ml/backend/**/*.glsl linguist-vendored -ml/backend/**/CMakeLists.txt linguist-vendored - app/webview linguist-vendored llama/build-info.cpp linguist-generated -ml/backend/ggml/ggml/src/ggml-metal/ggml-metal-embed.s linguist-generated * text=auto *.go text eol=lf diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9c6138ccc..e1ebd0bdc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -49,8 +49,6 @@ jobs: 'LLAMA_CPP_VERSION' \ 'MLX_VERSION' \ 'MLX_C_VERSION' \ - 'llama/llama.cpp/**/*' \ - 'ml/backend/ggml/ggml/**/*' \ 'x/imagegen/mlx/**' \ 'x/imagegen/mlx/**/*' \ 'x/mlxrunner/xgrammar/native/**' \ diff --git a/fs/config.go b/fs/config.go deleted file mode 100644 index db305bc56..000000000 --- a/fs/config.go +++ /dev/null @@ -1,20 +0,0 @@ -package fs - -import "iter" - -type Config interface { - Architecture() string - String(string, ...string) string - Uint(string, ...uint32) uint32 - Float(string, ...float32) float32 - Bool(string, ...bool) bool - - Strings(string, ...[]string) []string - Ints(string, ...[]int32) []int32 - Floats(string, ...[]float32) []float32 - Bools(string, ...[]bool) []bool - - Len() int - Keys() iter.Seq[string] - Value(key string) any -} diff --git a/go.mod b/go.mod index ef3eea892..afc6733b8 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/agnivade/levenshtein v1.1.1 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/lipgloss v1.1.0 - github.com/dlclark/regexp2 v1.11.5 github.com/emirpasic/gods/v2 v2.0.0-alpha github.com/klauspost/compress v1.18.3 github.com/mattn/go-runewidth v0.0.16 @@ -87,6 +86,6 @@ require ( golang.org/x/net v0.46.0 // indirect golang.org/x/term v0.36.0 golang.org/x/text v0.30.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 081135636..b2981189e 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU= github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= diff --git a/integration/reg_groups_test.go b/integration/reg_groups_test.go index 03a5394d4..3a2f86563 100644 --- a/integration/reg_groups_test.go +++ b/integration/reg_groups_test.go @@ -137,10 +137,6 @@ func TestQuantization(t *testing.T) { runIntegrationGroup(t, "quantization") } -func TestImageGeneration(t *testing.T) { - runIntegrationGroup(t, "image-generation") -} - func testName(s string) string { return strings.NewReplacer("/", "~", " ", "_").Replace(s) } diff --git a/integration/reg_required_test.go b/integration/reg_required_test.go index c178ce627..19294869f 100644 --- a/integration/reg_required_test.go +++ b/integration/reg_required_test.go @@ -1,4 +1,4 @@ -//go:build integration && !fast && !release && !library && !imagegen +//go:build integration && !fast && !release && !library package integration diff --git a/ml/backend.go b/ml/backend.go deleted file mode 100644 index 7c3340edd..000000000 --- a/ml/backend.go +++ /dev/null @@ -1,413 +0,0 @@ -package ml - -import ( - "bytes" - "context" - "encoding/binary" - "fmt" - "math" - "slices" - "strconv" - "strings" - - "github.com/ollama/ollama/fs" -) - -type Backend interface { - // Close frees all memory associated with this backend - Close() - - Load(ctx context.Context, progress func(float32)) error - - Config() fs.Config - Get(name string) Tensor - NewContext() Context - NewContextSize(size int) Context - - // Enumerate the devices available for inference via this backend - BackendDevices() []DeviceInfo -} - -// BackendCacheConfig should be implemented by backends that need special output -// from the cache to meet specific requirements. It is frequently implemented in -// conjunction with ScaledDotProductAttention. -type BackendCacheConfig interface { - CacheConfig() CacheConfig -} - -// CacheConfig controls optimizations (mostly backend-specific) that may transform -// the output the cache to work better with specific kernels. -type CacheConfig struct { - // CachePadding specifies the multiple for the number of tokens of cache history - // that will be returned from cache Get for k, v and mask. The capacity of the - // cache itself will also be increased to a multiple of this size if needed. - CachePadding int - - // PermutedV performs Permute(ctx, 1, 2, 0, 3) on v tensors stored via Put - // and return the permuted version via Get. This uses the cache copy operation - // to avoid a Contiguous call on the permuted tensor. - PermutedV bool - - // MaskDType specifies the data type for generating the mask. If unset it will - // default to DTypeF32. - MaskDType DType -} - -// BackendParams controls how the backend loads and executes models -type BackendParams struct { - // AllocMemory causes the backend to allocate memory for the model. If - // false, this is only being used for discovering the required amount of - // memory and cannot load the model for running. - AllocMemory bool - - // NumThreads sets the number of threads to use if running on the CPU - NumThreads int - - // FlashAttention indicates that we should use a fused flash attention kernel - FlashAttention FlashAttentionType -} - -var backends = make(map[string]func(string, BackendParams) (Backend, error)) - -func RegisterBackend(name string, f func(string, BackendParams) (Backend, error)) { - if _, ok := backends[name]; ok { - panic("backend: backend already registered") - } - - backends[name] = f -} - -func NewBackend(modelPath string, params BackendParams) (Backend, error) { - if backend, ok := backends["ggml"]; ok { - return backend(modelPath, params) - } - - return nil, fmt.Errorf("unsupported backend") -} - -type Context interface { - Empty(dtype DType, shape ...int) Tensor - Zeros(dtype DType, shape ...int) Tensor - FromBytes(dtype DType, s []byte, shape ...int) Tensor - FromFloats(s []float32, shape ...int) Tensor - FromInts(s []int32, shape ...int) Tensor - - // Arange creates a 1D tensor with values within an interval (start, stop] increased by step. - Arange(start, stop, step float32, dtype DType) Tensor - - Forward(...Tensor) Context - - // SetBatchSize provides a hint on the batch size to optimize processing - // Uses heuristics if not set - SetBatchSize(int) - - Compute(...Tensor) - ComputeWithNotify(func(), ...Tensor) // notify callback once compute has begun - - // Reserve is analogous to Compute but rather than executing a - // graph, simply preallocates memory. Typically called with a - // worst case graph to ensure all resources are available for - // for future inference. - Reserve() - - MaxGraphNodes() int - Close() - - // Input returns a context appropriate for creating tensors that are - // inputs to the model (which includes things like output locations) - Input() Context - - // Layer returns a context appropriate for creating intermediate tensors - Layer(int) Context -} - -type Tensor interface { - Dim(n int) int - Stride(n int) int - - Shape() []int - DType() DType - Cast(ctx Context, dtype DType) Tensor - - Bytes() []byte - Floats() []float32 - BackendGet() []float32 - - FromBytes([]byte) - FromFloats([]float32) - FromInts([]int32) - - Add(ctx Context, t2 Tensor) Tensor - Sub(ctx Context, t2 Tensor) Tensor - Mul(ctx Context, t2 Tensor) Tensor - Div(ctx Context, t2 Tensor) Tensor - - Mulmat(ctx Context, t2 Tensor) Tensor - MulmatFullPrec(ctx Context, t2 Tensor) Tensor - MulmatID(ctx Context, t2, ids Tensor) Tensor - AddID(ctx Context, t2, ids Tensor) Tensor - - Softmax(ctx Context) Tensor - L2Norm(ctx Context, eps float32) Tensor - LayerNorm(ctx Context, weight, bias Tensor, eps float32) Tensor - RMSNorm(ctx Context, weight Tensor, eps float32) Tensor - Scale(ctx Context, s float64) Tensor - SumRows(ctx Context) Tensor - - AvgPool2D(ctx Context, k, s int, p float32) Tensor - Conv2D(ctx Context, weight Tensor, s0, s1, p0, p1, d0, d1 int) Tensor - Conv3D(ctx Context, weight Tensor, c, s0, s1, s2, p0, p1, p2, d0, d1, d2 int) Tensor - Conv1DDW(ctx Context, weight Tensor, s, p, d int) Tensor - SSMConv(ctx Context, kernel Tensor) Tensor - SSMScan(ctx Context, x, dt, A, B, C, ids Tensor) Tensor - - IM2Col(ctx Context, weight Tensor, s0, s1, p0, p1, d0, d1 int) Tensor - - Sin(ctx Context) Tensor - Cos(ctx Context) Tensor - Tanh(ctx Context) Tensor - GELU(ctx Context, up ...Tensor) Tensor - GELU_ERF(ctx Context) Tensor - QuickGELU(ctx Context, up ...Tensor) Tensor - SILU(ctx Context, up ...Tensor) Tensor - RELU(ctx Context, up ...Tensor) Tensor - Sigmoid(ctx Context) Tensor - SigmoidOut(ctx Context) Tensor - - // AlphaLimitSILU is a variant of SILU that clamps the input to the range [-limit, limit] - SILUAlphaLimit(ctx Context, up Tensor, alpha, limit float32) Tensor - - Reshape(ctx Context, shape ...int) Tensor - View(ctx Context, offset int, shape ...int) Tensor - Permute(ctx Context, shape ...int) Tensor - Contiguous(ctx Context, shape ...int) Tensor - - Pad(ctx Context, shape ...int) Tensor - // PadExt pads with independent left/right amounts per dimension. - // Arguments: lp0, rp0, lp1, rp1, lp2, rp2, lp3, rp3 for dims 0-3. - PadExt(ctx Context, lp0, rp0, lp1, rp1, lp2, rp2, lp3, rp3 int) Tensor - - Stack(ctx Context, dim int, s ...Tensor) Tensor - - // Repeat repeats the tensor n times along dimension dim - Repeat(ctx Context, dim, n int) Tensor - Concat(ctx Context, t2 Tensor, dim int) Tensor - Rows(ctx Context, t2 Tensor) Tensor - SetRows(ctx Context, src Tensor, idxs Tensor) Tensor - SetInplace(ctx Context, src Tensor, nb1, nb2, nb3, offset int) Tensor - Copy(ctx Context, t2 Tensor) Tensor - Duplicate(ctx Context) Tensor - - Slice(ctx Context, dim, low, high, step int) Tensor - Chunk(ctx Context, dim int, size int) []Tensor - ChunkSections(ctx Context, dim int, sections ...int) []Tensor - - TopK(ctx Context, k int) Tensor - Argsort(ctx Context) Tensor - Mean(ctx Context) Tensor - Variance(ctx Context) Tensor - Stddev(ctx Context) Tensor - Sqr(ctx Context) Tensor - Sqrt(ctx Context) Tensor - Exp(ctx Context) Tensor - Neg(ctx Context) Tensor - - // Clamp clamps values to [min, max] range - Clamp(ctx Context, min, max float32) Tensor - - // Softplus computes ln(1 + exp(x)) - Softplus(ctx Context) Tensor - - // CumSum computes cumulative sum along dimension 0 - CumSum(ctx Context) Tensor - - // Diag creates a diagonal matrix from a 1D tensor - Diag(ctx Context) Tensor - - // Tri converts a matrix to triangular form (0=upper+diag, 1=upper, 2=lower+diag, 3=lower) - Tri(ctx Context, triType int) Tensor - - // Fill fills a tensor with a constant value (in-place) - Fill(ctx Context, value float32) Tensor - - // Repeat4D repeats tensor to match target shape - Repeat4D(ctx Context, dim0, dim1, dim2, dim3 int) Tensor - - // SolveTri solves a triangular system Ax = B - SolveTri(ctx Context, b Tensor, lower, left, unitDiag bool) Tensor - - Interpolate(ctx Context, dims [4]int, samplingMode SamplingMode) Tensor -} - -// ScaledDotProductAttention implements a fused attention -// operation equivalent to following code on a tensor named -// query: -// -// query = query.Permute(ctx, 0, 2, 1, 3) -// key = key.Permute(ctx, 0, 2, 1, 3) -// value = value.Permute(ctx, 1, 2, 0, 3).Contiguous(ctx) -// -// kq := key.MulmatFullPrec(ctx, query) -// -// kq = kq.Scale(ctx, scale) -// -// if mask != nil { -// kq = kq.Add(ctx, mask) -// } -// -// kq = kq.Softmax(ctx) -// -// kqv := value.Mulmat(ctx, kq) -// return kqv.Permute(ctx, 0, 2, 1, 3).Contiguous(ctx) -// -// cacheConfigApplied indicates whether the optimizations requested through CacheConfig have been performed -type ScaledDotProductAttention interface { - ScaledDotProductAttention(ctx Context, key, value, mask, sinks Tensor, vmla Tensor, scale float64, cacheConfigApplied bool) Tensor -} - -type number interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 | - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | - ~float32 | ~float64 | - ~complex64 | ~complex128 -} - -func mul[T number](s ...T) T { - p := T(1) - for _, v := range s { - p *= v - } - - return p -} - -type DumpOptions func(*dumpOptions) - -// DumpWithPrecision sets the number of decimal places to print. Applies to float32 and float64. -func DumpWithPrecision(n int) DumpOptions { - return func(opts *dumpOptions) { - opts.Precision = n - } -} - -// DumpWithThreshold sets the threshold for printing the entire tensor. If the number of elements -// is less than or equal to this value, the entire tensor will be printed. Otherwise, only the -// beginning and end of each dimension will be printed. -func DumpWithThreshold(n int) DumpOptions { - return func(opts *dumpOptions) { - opts.Threshold = n - } -} - -// DumpWithEdgeItems sets the number of elements to print at the beginning and end of each dimension. -func DumpWithEdgeItems(n int) DumpOptions { - return func(opts *dumpOptions) { - opts.EdgeItems = n - } -} - -type dumpOptions struct { - Precision, Threshold, EdgeItems int -} - -func Dump(ctx Context, t Tensor, optsFuncs ...DumpOptions) string { - opts := dumpOptions{Precision: 4, Threshold: 1000, EdgeItems: 3} - for _, optsFunc := range optsFuncs { - optsFunc(&opts) - } - - if mul(t.Shape()...) <= opts.Threshold { - opts.EdgeItems = math.MaxInt - } - - switch t.DType() { - case DTypeF32: - return dump[[]float32](ctx, t, opts.EdgeItems, func(f float32) string { - return strconv.FormatFloat(float64(f), 'f', opts.Precision, 32) - }) - case DTypeF16, DTypeQ80, DTypeQ40: - f32 := ctx.Input().Empty(DTypeF32, t.Shape()...) - f32 = t.Copy(ctx, f32) - return dump[[]float32](ctx, f32, opts.EdgeItems, func(f float32) string { - return strconv.FormatFloat(float64(f), 'f', opts.Precision, 32) - }) - case DTypeI32: - return dump[[]int32](ctx, t, opts.EdgeItems, func(i int32) string { - return strconv.FormatInt(int64(i), 10) - }) - default: - return "" - } -} - -func dump[S ~[]E, E number](ctx Context, t Tensor, items int, fn func(E) string) string { - if t.Bytes() == nil { - ctx.Forward(t).Compute(t) - } - - s := make(S, mul(t.Shape()...)) - if err := binary.Read(bytes.NewBuffer(t.Bytes()), binary.LittleEndian, &s); err != nil { - panic(err) - } - - shape := t.Shape() - slices.Reverse(shape) - - var sb strings.Builder - var f func([]int, int) - f = func(dims []int, stride int) { - prefix := strings.Repeat(" ", len(shape)-len(dims)+1) - sb.WriteString("[") - defer func() { sb.WriteString("]") }() - for i := 0; i < dims[0]; i++ { - if i >= items && i < dims[0]-items { - sb.WriteString("..., ") - // skip to next printable element - skip := dims[0] - 2*items - if len(dims) > 1 { - stride += mul(append(dims[1:], skip)...) - fmt.Fprint(&sb, strings.Repeat("\n", len(dims)-1), prefix) - } - i += skip - 1 - } else if len(dims) > 1 { - f(dims[1:], stride) - stride += mul(dims[1:]...) - if i < dims[0]-1 { - fmt.Fprint(&sb, ",", strings.Repeat("\n", len(dims)-1), prefix) - } - } else { - text := fn(s[stride+i]) - if len(text) > 0 && text[0] != '-' { - sb.WriteString(" ") - } - - sb.WriteString(text) - if i < dims[0]-1 { - sb.WriteString(", ") - } - } - } - } - f(shape, 0) - - return sb.String() -} - -type DType int - -const ( - DTypeOther DType = iota - DTypeF32 - DTypeF16 - DTypeQ80 - DTypeQ40 - DTypeI32 - DTypeMXFP4 -) - -type SamplingMode int - -const ( - SamplingModeNearest SamplingMode = iota - SamplingModeBilinear -) diff --git a/ml/backend/ggml/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu b/ml/backend/ggml/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu deleted file mode 100644 index 7c61d8d2e..000000000 --- a/ml/backend/ggml/ggml/src/ggml-cuda/template-instances/fattn-tile-instance-dkq512-dv512.cu +++ /dev/null @@ -1,5 +0,0 @@ -// This file has been autogenerated by generate_cu_files.py, do not edit manually. - -#include "../fattn-tile.cuh" - -DECL_FATTN_TILE_CASE(512, 512); diff --git a/ml/device.go b/ml/device.go index c9255088d..9a5792fe4 100644 --- a/ml/device.go +++ b/ml/device.go @@ -6,7 +6,6 @@ import ( "log/slog" "os" "runtime" - "sort" "strconv" "strings" @@ -271,31 +270,6 @@ func SimilarDeviceMemory(a, b uint64) bool { return maxMemory-min(a, b) <= tolerance } -// For a SameBackendDevice, return true if b is better than a -// e.g. newer GPU library version -func (a DeviceInfo) IsBetter(b DeviceInfo) bool { - aLib := a.LibraryPath[len(a.LibraryPath)-1] - bLib := b.LibraryPath[len(b.LibraryPath)-1] - if aLib == bLib { - return false - } - aLibSplit := strings.SplitN(aLib, "_", 2) - bLibSplit := strings.SplitN(bLib, "_", 2) - if len(aLibSplit) < 2 || len(bLibSplit) < 2 { - return false - } - if aLibSplit[0] != bLibSplit[0] { - slog.Debug("unexpected libraries", "a", aLib, "b", bLib) - return false - } - if aLibSplit[1] == bLibSplit[1] { - return false - } - cmp := []string{aLibSplit[1], bLibSplit[1]} - sort.Sort(sort.Reverse(sort.StringSlice(cmp))) - return cmp[0] == bLibSplit[1] -} - // FlashAttentionSupported reports whether flash attention can be used across // all selected devices. func FlashAttentionSupported(l []DeviceInfo) bool { diff --git a/progress/stepbar.go b/progress/stepbar.go deleted file mode 100644 index fe10a9e10..000000000 --- a/progress/stepbar.go +++ /dev/null @@ -1,33 +0,0 @@ -package progress - -import ( - "fmt" - "strings" -) - -// StepBar displays step-based progress. -type StepBar struct { - message string - current int - total int -} - -func NewStepBar(message string, total int) *StepBar { - return &StepBar{message: message, total: total} -} - -func (s *StepBar) Set(current int) { - s.current = current -} - -func (s *StepBar) String() string { - percent := float64(s.current) / float64(s.total) * 100 - barWidth := s.total - empty := barWidth - s.current - - // "Generating 0% ▕ ▏ 0/9" - return fmt.Sprintf("%s %3.0f%% ▕%s%s▏ %d/%d", - s.message, percent, - strings.Repeat("█", s.current), strings.Repeat(" ", empty), - s.current, s.total) -} diff --git a/tokenizer/bytepairencoding.go b/tokenizer/bytepairencoding.go deleted file mode 100644 index f1a4c3c96..000000000 --- a/tokenizer/bytepairencoding.go +++ /dev/null @@ -1,359 +0,0 @@ -package tokenizer - -import ( - "cmp" - "fmt" - "iter" - "log/slog" - "slices" - "strconv" - "strings" - - "github.com/dlclark/regexp2" - heap "github.com/emirpasic/gods/v2/trees/binaryheap" - "github.com/ollama/ollama/logutil" -) - -type BytePairEncoding struct { - vocab *Vocabulary - regexps []*regexp2.Regexp - spaceToSpmSep bool // When true, normalize spaces to ▁ instead of GPT-2 byte-level encoding -} - -var _ Tokenizer = (*BytePairEncoding)(nil) - -// BPEOption configures BytePairEncoding behavior -type BPEOption func(*BytePairEncoding) - -// WithSentencePieceNormalizer enables ▁ space normalization instead of GPT-2 byte-level encoding. -func WithSentencePieceNormalizer() BPEOption { - return func(bpe *BytePairEncoding) { - bpe.spaceToSpmSep = true - } -} - -func NewBytePairEncoding(vocab *Vocabulary, pretokenizer ...string) BytePairEncoding { - return newBytePairEncoding(vocab, pretokenizer) -} - -func NewBytePairEncodingWithOptions(vocab *Vocabulary, pretokenizer []string, opts ...BPEOption) BytePairEncoding { - bpe := newBytePairEncoding(vocab, pretokenizer, opts...) - return bpe -} - -func newBytePairEncoding(vocab *Vocabulary, pretokenizer []string, opts ...BPEOption) BytePairEncoding { - bpe := BytePairEncoding{ - vocab: vocab, - } - - for _, opt := range opts { - opt(&bpe) - } - - if len(pretokenizer) == 0 && !bpe.spaceToSpmSep { - // set default byte-level pretokenizer if none provided, e.g. - // https://github.com/huggingface/tokenizer/blob/main/tokenizer/src/pre_tokenizer/byte_level.rs#L44 - pretokenizer = []string{`'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+`} - } - - bpe.regexps = slices.Collect(func(yield func(*regexp2.Regexp) bool) { - for _, p := range pretokenizer { - if !yield(regexp2.MustCompile(p, regexp2.RE2)) { - return - } - } - }) - - return bpe -} - -func (bpe *BytePairEncoding) split(s string) iter.Seq[string] { - parts := []string{s} - for _, re := range bpe.regexps { - parts = slices.Collect(func(yield func(string) bool) { - for _, part := range parts { - r := []rune(part) - var offset int - for m, _ := re.FindRunesMatch(r); m != nil; m, _ = re.FindNextMatch(m) { - if offset-m.Index != 0 { - if !yield(string(r[offset:m.Index])) { - return - } - } - - if !yield(m.String()) { - return - } - - offset = m.Index + m.Length - } - - if offset < len(r) { - if !yield(string(r[offset:])) { - return - } - } - } - }) - } - - return slices.Values(parts) -} - -// fragment is a string fragment and their corresponding token IDs -type fragment struct { - value string - ids []int32 -} - -// pair is a pair of runes and its rank -type pair struct { - a, b int - rank int - value string -} - -type merge struct { - p, n int - runes []rune -} - -func (bpe BytePairEncoding) Encode(s string, addSpecial bool) ([]int32, error) { - fragments := []fragment{{value: s}} - for _, special := range bpe.vocab.SpecialVocabulary() { - // TODO: process special tokens concurrently - id := bpe.vocab.Encode(special) - for i := 0; i < len(fragments); i++ { - frag := fragments[i] - if len(frag.ids) > 0 { - continue - } - - var middle []fragment - switch i := strings.Index(frag.value, special); { - case i < 0: - middle = append(middle, frag) - case i > 0: - middle = append(middle, fragment{value: frag.value[:i]}) - fallthrough - default: - middle = append(middle, fragment{value: special, ids: []int32{id}}) - if rest := frag.value[i+len(special):]; rest != "" { - middle = append(middle, fragment{value: rest}) - } - } - - fragments = append(fragments[:i], append(middle, fragments[i+1:]...)...) - } - } - - var ids []int32 - for _, frag := range fragments { - if len(frag.ids) > 0 { - ids = append(ids, frag.ids...) - continue - } - - for split := range bpe.split(frag.value) { - // TODO: process splits concurrently - var normalized string - if bpe.spaceToSpmSep { - // SentencePiece-style: replace spaces with ▁ - normalized = strings.ReplaceAll(split, " ", spmWhitespaceSep) - } else { - // GPT-2 byte-level: map bytes to shifted Unicode codepoints - var sb strings.Builder - for _, b := range []byte(split) { - r := rune(b) - switch { - case r == 0x00ad: - r = 0x0143 - case r <= 0x0020: - r = r + 0x0100 - case r >= 0x007f && r <= 0x00a0: - r = r + 0x00a2 - } - sb.WriteRune(r) - } - normalized = sb.String() - } - - // short circuit if the fragment is in the vocabulary - if id := bpe.vocab.Encode(normalized); id >= 0 { - ids = append(ids, id) - continue - } - - runes := []rune(normalized) - merges := make([]merge, len(runes)) - for r := range runes { - merges[r] = merge{ - p: r - 1, - n: r + 1, - runes: []rune{runes[r]}, - } - } - - pairwise := func(a, b int) *pair { - if a < 0 || b >= len(runes) { - return nil - } - - left, right := string(merges[a].runes), string(merges[b].runes) - rank := bpe.vocab.Merge(left, right) - if rank < 0 { - return nil - } - - return &pair{ - a: a, - b: b, - rank: rank, - value: left + right, - } - } - - pairs := heap.NewWith(func(i, j *pair) int { - return cmp.Compare(i.rank, j.rank) - }) - - for i := range len(runes) - 1 { - if pair := pairwise(i, i+1); pair != nil { - pairs.Push(pair) - } - } - - for !pairs.Empty() { - pair, _ := pairs.Pop() - - left, right := merges[pair.a], merges[pair.b] - if len(left.runes) == 0 || len(right.runes) == 0 || - string(left.runes)+string(right.runes) != pair.value { - continue - } - - if id := bpe.vocab.Encode(pair.value); id < 0 { - continue - } - - merges[pair.a].runes = append(left.runes, right.runes...) - merges[pair.b].runes = nil - - merges[pair.a].n = right.n - if right.n < len(merges) { - merges[right.n].p = pair.a - } - - if pair := pairwise(merges[pair.a].p, pair.a); pair != nil { - pairs.Push(pair) - } - - if pair := pairwise(pair.a, merges[pair.a].n); pair != nil { - pairs.Push(pair) - } - } - - for _, merge := range merges { - if len(merge.runes) > 0 { - if id := bpe.vocab.Encode(string(merge.runes)); id >= 0 { - ids = append(ids, id) - } else if bpe.spaceToSpmSep { - // SentencePiece byte fallback: encode each UTF-8 byte as <0xHH> - for _, b := range []byte(string(merge.runes)) { - if id := bpe.vocab.Encode(fmt.Sprintf("<0x%02X>", b)); id >= 0 { - ids = append(ids, id) - } else { - slog.Debug("unknown byte token", "byte", b) - } - } - } - } - } - } - } - - if addSpecial { - ids = bpe.vocab.addSpecials(ids) - } - - logutil.Trace("encoded", "string", s, "ids", ids) - return ids, nil -} - -type lazyIdsString struct { - ids []int32 -} - -func (l lazyIdsString) LogValue() slog.Value { - return slog.AnyValue(fmt.Sprint(l.ids)) -} - -func (bpe BytePairEncoding) Decode(ids []int32) (string, error) { - var sb strings.Builder - - // SentencePiece-style BPE stores true Unicode codepoints in the vocab - // (plus ▁ as a whitespace marker), so decoding should pass runes through - // directly instead of applying the GPT-2 byte-level reverse mapping. - // Without this, codepoints in the 0x0100-0x0142 range (e.g. ą ę ć ł) - // get mangled by the GPT-2 reversal into control characters. - if bpe.spaceToSpmSep { - for _, id := range ids { - data := bpe.vocab.Decode(id) - - // SentencePiece byte tokens: "<0xHH>" → raw byte - if len(data) == 6 && strings.HasPrefix(data, "<0x") && strings.HasSuffix(data, ">") { - if b, err := strconv.ParseUint(data[3:5], 16, 8); err == nil { - sb.WriteByte(byte(b)) - continue - } - } - - for _, r := range data { - if r == 0x2581 { // ▁ (LOWER ONE EIGHTH BLOCK) - sb.WriteByte(' ') - } else { - sb.WriteRune(r) - } - } - } - - logutil.Trace("decoded", "string", sb.String(), "from", lazyIdsString{ids: ids}) - return sb.String(), nil - } - - for _, id := range ids { - for _, r := range bpe.vocab.Decode(id) { - // GPT-2 byte-level BPE uses Unicode chars in the 0x0100-0x0143 - // range to represent bytes. Remap them back to actual bytes. - switch { - case r == 0x0100: - // this produces 0x00 aka NULL - continue - case r == 0x0143: - r = 0x00ad - case r > 0x0100 && r <= 0x0120: - r = r - 0x0100 - case r > 0x0120 && r <= 0x0142: - r = r - 0x00a2 - case r > 0x0143: - // Non-GPT2 rune (e.g., SentencePiece-style BPE). - // Handle ▁ as word separator, otherwise write the rune as-is. - if r == 0x2581 { // ▁ (LOWER ONE EIGHTH BLOCK) - sb.WriteByte(' ') - } else { - sb.WriteRune(r) - } - continue - } - - // NOTE: not using WriteRune here because it writes the UTF-8 - // encoding of the rune which is _not_ what we want - if err := sb.WriteByte(byte(r)); err != nil { - return "", err - } - } - } - - logutil.Trace("decoded", "string", sb.String(), "from", lazyIdsString{ids: ids}) - return sb.String(), nil -} diff --git a/tokenizer/bytepairencoding_test.go b/tokenizer/bytepairencoding_test.go deleted file mode 100644 index f0a2c0369..000000000 --- a/tokenizer/bytepairencoding_test.go +++ /dev/null @@ -1,639 +0,0 @@ -package tokenizer - -import ( - "bufio" - "encoding/json" - "fmt" - "math" - "os" - "path/filepath" - "slices" - "strconv" - "strings" - "testing" - - "github.com/google/go-cmp/cmp" -) - -func llama(t testing.TB) BytePairEncoding { - t.Helper() - - f, err := os.Open(filepath.FromSlash("testdata/llama3.2/encoder.json")) - if err != nil { - t.Fatal(err) - } - defer f.Close() - - vocab := make(map[string]int32) - if err := json.NewDecoder(f).Decode(&vocab); err != nil { - t.Fatal(err) - } - - types := make([]int32, len(vocab)) - tokens := make([]string, len(vocab)) - for token, id := range vocab { - tokens[id] = token - types[id] = 1 - } - - for _, token := range []string{"<|begin_of_text|>", "<|end_of_text|>"} { - if _, ok := vocab[token]; !ok { - tokens = append(tokens, token) //nolint:makezero - types = append(types, 3) //nolint:makezero - vocab[token] = int32(len(vocab)) - } - } - - f, err = os.Open(filepath.FromSlash("testdata/llama3.2/vocab.bpe")) - if err != nil { - t.Fatal(err) - } - defer f.Close() - - merges := make([]string, 0, 50000) - - scanner := bufio.NewScanner(f) - for scanner.Scan() { - if !strings.HasPrefix(scanner.Text(), "#") { - merges = append(merges, scanner.Text()) - } - } - - return NewBytePairEncoding( - &Vocabulary{ - Values: tokens, - Types: types, - Merges: merges, - }, - "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}{1,3}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+", - ) -} - -func TestLlama(t *testing.T) { - tokenizer := llama(t) - - t.Run("simple", func(t *testing.T) { - t.Parallel() - - ids, err := tokenizer.Encode("hello world", true) - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff([]int32{15339, 1917}, ids); diff != "" { - t.Errorf("no match (-theirs +ours):\n%s", diff) - } - - s, err := tokenizer.Decode([]int32{15339, 1917}) - if err != nil { - t.Fatal(err) - } - - if s != "hello world" { - t.Errorf("got %q, want hello world", s) - } - - ids, err = tokenizer.Encode("hello <|end_of_text|>", true) - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff([]int32{15339, 220, 128001}, ids); diff != "" { - t.Errorf("no match (-theirs +ours):\n%s", diff) - } - }) - - t.Run("simple repeated", func(t *testing.T) { - t.Parallel() - - cases := map[string][]int32{ - strings.Repeat("0", 1): {15}, - strings.Repeat("0", 2): {410}, - strings.Repeat("0", 3): {931}, - strings.Repeat("0", 4): {931, 15}, - strings.Repeat("0", 5): {931, 410}, - strings.Repeat("0", 6): {931, 931}, - strings.Repeat("0", 7): {931, 931, 15}, - strings.Repeat("0", 8): {931, 931, 410}, - strings.Repeat("0", 9): {931, 931, 931}, - strings.Repeat("0", 10): {931, 931, 931, 15}, - strings.Repeat("0", 11): {931, 931, 931, 410}, - strings.Repeat("0", 12): {931, 931, 931, 931}, - strings.Repeat("0", 13): {931, 931, 931, 931, 15}, - strings.Repeat("0", 14): {931, 931, 931, 931, 410}, - strings.Repeat("0", 15): {931, 931, 931, 931, 931}, - strings.Repeat("0", 16): {931, 931, 931, 931, 931, 15}, - strings.Repeat("0", 17): {931, 931, 931, 931, 931, 410}, - } - - for s, want := range cases { - ids, err := tokenizer.Encode(s, true) - if err != nil { - t.Error(err) - } - - if diff := cmp.Diff(want, ids); diff != "" { - t.Errorf("%q no match (-theirs +ours):\n%s", s, diff) - } - } - }) - - t.Run("basic roundtrip", func(t *testing.T) { - t.Parallel() - - cases := []string{ - "hello", - "hello ", - "hello ", - " hello", - " hello ", - " hello ", - "hello world", - "请考试我的软件!12345", - } - - for _, want := range cases { - ids, err := tokenizer.Encode(want, true) - if err != nil { - t.Error(err) - } - - if got, err := tokenizer.Decode(ids); err != nil { - t.Fatal(err) - } else if got != want { - t.Errorf("got %q, want %q", got, want) - } - } - }) - - t.Run("special", func(t *testing.T) { - t.Parallel() - - cases := map[string][]int32{ - "<|begin_of_text|>A B!": {128000, 32, 426, 0}, - "<|begin_of_text|>A<|end_of_text|>B!": {128000, 32, 128001, 33, 0}, - "<|begin_of_text|>A<|end_of_text|>B<|begin_of_text|>!": {128000, 32, 128001, 33, 128000, 0}, - "<|begin_of_text|>A<|end_of_text|>B<|begin_of_text|>!<|end_of_text|>": {128000, 32, 128001, 33, 128000, 0, 128001}, - } - - for s, want := range cases { - ids, err := tokenizer.Encode(s, true) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(want, ids); diff != "" { - t.Errorf("no match (-theirs +ours):\n%s", diff) - } - } - }) - - t.Run("split", func(t *testing.T) { - t.Parallel() - - cases := map[string][]string{ - "Hello World!": {"Hello", " World", "!"}, - "I'm don't won't": {"I", "'m", " don", "'t", " won", "'t"}, - "In 2024 there are 366 days": {"In", " ", "202", "4", " there", " are", " ", "366", " days"}, - "Hello!! ...world": {"Hello", "!!", " ...", "world"}, - "Hello World": {"Hello", " ", " World"}, - "Hello\nWorld": {"Hello", "\n", "World"}, - "Hello, WORLD!! How's it going?": {"Hello", ",", " WORLD", "!!", " How", "'s", " it", " going", "?"}, - } - - for s, want := range cases { - got := slices.Collect(tokenizer.split(s)) - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("no match (-theirs +ours):\n%s", diff) - } - } - }) - - t.Run("roundtriping 0x00-0xFF", func(t *testing.T) { - t.Parallel() - - for b := 0x00; b <= 0xFF; b++ { - input := string(rune(b)) - ids, err := tokenizer.Encode(input, false) - if err != nil { - t.Errorf("failed to encode rune 0x%02X: %v", b, err) - continue - } - - decoded, err := tokenizer.Decode(ids) - if err != nil { - t.Errorf("failed to decode rune 0x%02X: %v", b, err) - continue - } - - if b == 0x00 { - if len(decoded) != 0 { - t.Errorf("Decode(Encode(0x00)) should be empty, got %v", ids) - } - continue - } - - if decoded != input { - t.Errorf("rune 0x%02X failed roundtrip: got %q, want %q", b, decoded, input) - } - } - }) -} - -// spmBPE builds a SentencePiece-style BPE tokenizer for testing. -// -// Models that use SentencePiece BPE differ from GPT-2 BPE in how they -// handle spaces: the vocabulary stores ▁ (U+2581) instead of GPT-2's -// shifted-byte encoding (0x0100–0x0143). Without WithSentencePieceNormalizer, -// spaces are mapped through the GPT-2 byte table which produces wrong token -// IDs for any vocabulary that uses ▁-prefixed tokens. The decode path has -// the inverse problem: high codepoints like CJK characters and ▁ itself -// would be mangled by the GPT-2 reverse mapping instead of being passed -// through (or converted to spaces in the ▁ case). -func spmBPE(t testing.TB) BytePairEncoding { - t.Helper() - - tokens := []string{ - // Control tokens (low IDs, as in real SentencePiece vocabs) - "", // 0 - "", // 1 - "", // 2 - "<|start>", // 3 - asymmetric open/close special tokens - "", // 4 - "<|q>", // 5 - short special token (like <|"|>) - - // ▁-prefixed word tokens (the core of what SPM BPE changes) - "▁hello", // 6 - "▁world", // 7 - "hello", // 8 - "▁Run", // 9 - "▁a", // 10 - - // Punctuation and structure - ",", // 11 - "!", // 12 - ":", // 13 - "{", // 14 - "}", // 15 - - // Whitespace separator - "▁", // 16 - - // Subword tokens used in tool-declaration-like patterns - "description", // 17 - "▁command", // 18 - "declaration", // 19 - - // Unicode token for decode passthrough testing (must be > U+0143 - // to exercise the SPM decode path rather than GPT-2 byte reversal) - "▁中文", // 20 - - // Unicode tokens with codepoints in the GPT-2 byte range (0x0100-0x0142). - // Without the SPM decode path, these get mangled by GPT-2 byte reversal. - "ą", // 21 (U+0105) — would become 0x05 via GPT-2 reversal - "ę", // 22 (U+0119) — would become 0x19 - "ć", // 23 (U+0107) — would become 0x07 - "ł", // 24 (U+0142) — would become 0xA0 - - // Byte fallback tokens (SentencePiece BYTE type) - "<0x00>", // 25 - "<0x01>", // 26 - } - - // Add all 256 byte tokens starting at index 27 - for b := 2; b < 256; b++ { - tokens = append(tokens, fmt.Sprintf("<0x%02X>", b)) - } - - types := make([]int32, len(tokens)) - for i := range types { - types[i] = TOKEN_TYPE_NORMAL - } - types[0] = TOKEN_TYPE_CONTROL // - types[1] = TOKEN_TYPE_CONTROL // - types[2] = TOKEN_TYPE_CONTROL // - types[3] = TOKEN_TYPE_USER_DEFINED // <|start> - types[4] = TOKEN_TYPE_USER_DEFINED // - types[5] = TOKEN_TYPE_USER_DEFINED // <|q> - for i := 21; i < len(types); i++ { - types[i] = TOKEN_TYPE_BYTE - } - - return NewBytePairEncodingWithOptions( - &Vocabulary{ - Values: tokens, - Types: types, - BOS: []int32{2}, - EOS: []int32{1}, - AddBOS: false, - }, - // Empty pretokenizer list: falls back to the default pattern. - // Real SentencePiece BPE models are configured this way. - []string{}, - WithSentencePieceNormalizer(), - ) -} - -func TestSentencePieceBPE(t *testing.T) { - tok := spmBPE(t) - - // Test 1: Space-to-▁ normalization and roundtrip. - // - // SentencePiece BPE has no pretokenizer — the BPE merges handle word - // boundaries via ▁ markers. With no merges in the test vocab, multi-char - // tokens won't be found, but the roundtrip must still be lossless. - t.Run("spm space normalization roundtrip", func(t *testing.T) { - t.Parallel() - - for _, input := range []string{ - "hello", - " hello", - "hello, world!", - " leading spaces", - "multiple spaces", - } { - ids, err := tok.Encode(input, false) - if err != nil { - t.Fatalf("Encode(%q): %v", input, err) - } - if len(ids) == 0 { - t.Fatalf("Encode(%q) returned empty IDs", input) - } - - got, err := tok.Decode(ids) - if err != nil { - t.Fatalf("Decode(%v): %v", ids, err) - } - if got != input { - t.Errorf("roundtrip %q: Decode(Encode) = %q", input, got) - } - } - }) - - // Test 2: Special tokens interleaved with SPM-normalized text. - // - // This mimics tool declaration patterns like: - // <|tool>declaration:bash{description:<|"|>Run a command<|"|>} - // where special tokens (<|tool>, <|"|>, ) must be extracted - // first, then the remaining text fragments go through SPM normalization. - t.Run("special tokens with spm text fragments", func(t *testing.T) { - t.Parallel() - - input := "<|start>declaration:description:<|q> Run a command<|q>}" - ids, err := tok.Encode(input, false) - if err != nil { - t.Fatal(err) - } - - // Special tokens should be extracted as single IDs at the right positions. - // The text between them is SPM-normalized and BPE-encoded (specific IDs - // depend on merges, so we verify the special token positions + roundtrip). - specialPositions := map[int32]bool{3: true, 4: true, 5: true} // <|start>, , <|q> - foundSpecials := 0 - for _, id := range ids { - if specialPositions[id] { - foundSpecials++ - } - } - if foundSpecials != 4 { // <|start>, <|q>, <|q>, - t.Errorf("expected 4 special tokens, found %d in %v", foundSpecials, ids) - } - - // First token must be <|start>(3), last must be (4) - if ids[0] != 3 { - t.Errorf("first token = %d, want 3 (<|start>)", ids[0]) - } - if ids[len(ids)-1] != 4 { - t.Errorf("last token = %d, want 4 ()", ids[len(ids)-1]) - } - }) - - // Test 3: Byte fallback for characters not in the vocabulary. - // - // SentencePiece vocabs include <0xHH> byte tokens for every byte value. - // When a character (e.g. "ą" = U+0105 = C4 85) isn't in the vocab as a - // direct token, the encoder must fall back to its UTF-8 bytes: - // <0xC4> <0x85>. Without this fallback, the character is silently dropped. - // See: https://github.com/ollama/ollama/issues/15229 - t.Run("byte fallback for unknown chars", func(t *testing.T) { - t.Parallel() - - // "ą" is not in the vocab — should fall back to byte tokens - ids, err := tok.Encode("ą", false) - if err != nil { - t.Fatalf("Encode(ą): %v", err) - } - if len(ids) == 0 { - t.Fatal("Encode(ą) returned empty IDs — character was silently dropped") - } - - got, err := tok.Decode(ids) - if err != nil { - t.Fatalf("Decode: %v", err) - } - if got != "ą" { - t.Errorf("roundtrip = %q, want %q", got, "ą") - } - }) - - // Test 4: Byte fallback preserves known tokens around unknown chars. - t.Run("byte fallback mixed with known tokens", func(t *testing.T) { - t.Parallel() - - // "hello" is in vocab, "é" is not - ids, err := tok.Encode("helloé", false) - if err != nil { - t.Fatalf("Encode: %v", err) - } - - got, err := tok.Decode(ids) - if err != nil { - t.Fatalf("Decode: %v", err) - } - if got != "helloé" { - t.Errorf("roundtrip = %q, want %q", got, "helloé") - } - }) - - // Test 5: Decode doesn't mangle Unicode in the GPT-2 byte range. - // - // Characters like ą (U+0105), ę (U+0119), ć (U+0107), ł (U+0142) have - // codepoints in the 0x0100-0x0142 range that GPT-2 byte reversal would - // remap to control characters. SentencePiece decode must pass them through. - t.Run("decode unicode in gpt2 byte range", func(t *testing.T) { - t.Parallel() - - // Token IDs 21-24 are ą, ę, ć, ł - ids := []int32{21, 22, 23, 24} - got, err := tok.Decode(ids) - if err != nil { - t.Fatalf("Decode: %v", err) - } - if got != "ąęćł" { - t.Errorf("Decode = %q, want %q", got, "ąęćł") - } - }) - - // Test 6: Decode handles non-GPT2 Unicode correctly. - // - // GPT-2 BPE decode reverses the byte→codepoint shift for runes in - // 0x0100–0x0143. But SentencePiece vocabs store real Unicode (CJK, - // accented chars, etc.) which have codepoints well above 0x0143. - // Without the > 0x0143 passthrough in Decode, these would be mangled - // by the GPT-2 reverse mapping (e.g., written as raw bytes instead - // of the original characters). - t.Run("decode non-gpt2 unicode passthrough", func(t *testing.T) { - t.Parallel() - - cases := map[string][]int32{ - " 中文": {20}, // ▁→space, then CJK passes through as-is - } - - for want, ids := range cases { - got, err := tok.Decode(ids) - if err != nil { - t.Fatalf("Decode(%v): %v", ids, err) - } - if got != want { - t.Errorf("Decode(%v) = %q, want %q", ids, got, want) - } - } - }) -} - -func BenchmarkBytePairEncoding(b *testing.B) { - tokenizer := llama(b) - bts, err := os.ReadFile(filepath.Join("testdata", "war-and-peace.txt")) - if err != nil { - b.Fatal(err) - } - - for i := range 8 { - n := min(int(math.Pow10(i)), len(bts)) - bts := bts[:n] - b.Run("encode"+strconv.Itoa(n), func(b *testing.B) { - b.ResetTimer() - for b.Loop() { - _, err := tokenizer.Encode(string(bts), true) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("decode"+strconv.Itoa(n), func(b *testing.B) { - ids, err := tokenizer.Encode(string(bts), true) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for b.Loop() { - _, err := tokenizer.Decode(ids) - if err != nil { - b.Fatal(err) - } - } - }) - - b.Run("split"+strconv.Itoa(n), func(b *testing.B) { - b.ResetTimer() - for b.Loop() { - slices.Collect(tokenizer.split(string(bts))) - } - }) - } -} - -func TestBytePairEncodingSplitMultipleRegexpsPreservesOffsets(t *testing.T) { - t.Parallel() - - bpe := NewBytePairEncoding( - nil, - `(?:\r?\n)+(?!\r?\n)`, - `(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+`, - ) - - input := "One line\nTwo lines\n\nThree" - got := slices.Collect(bpe.split(input)) - want := []string{"One", " line", "\n", "Two", " lines", "\n\n", "Three"} - - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("split mismatch (-want +got):\n%s", diff) - } -} - -func TestBytePairEncodingSplitRefactPreservesOffsets(t *testing.T) { - t.Parallel() - - bpe := NewBytePairEncoding( - nil, - `\p{N}`, - `'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+`, - ) - - input := "One line\nTwo lines\n\nThree" - got := slices.Collect(bpe.split(input)) - want := []string{"One", " line", "\n", "Two", " lines", "\n", "\n", "Three"} - - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("split mismatch (-want +got):\n%s", diff) - } -} - -func TestBytePairEncodingSplitDeepSeekV3PreservesOffsets(t *testing.T) { - t.Parallel() - - bpe := NewBytePairEncoding( - nil, - "\\p{N}{1,3}", - `[一-龥぀-ゟ゠-ヿ]+`, - "[!\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~][A-Za-z]+|[^\\r\\n\\p{L}\\p{P}\\p{S}]?[\\p{L}\\p{M}]+| ?[\\p{P}\\p{S}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+", - ) - - input := "One line\nTwo lines\n\nThree" - got := slices.Collect(bpe.split(input)) - want := []string{"One", " line", "\n", "Two", " lines", "\n\n", "Three"} - - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("split mismatch (-want +got):\n%s", diff) - } -} - -func TestSplit(t *testing.T) { - cases := []struct { - name string - patterns, - want []string - }{ - { - name: "default", - want: []string{"Hello", ",", " WORLD", "!!", " How", "'s", " it", " going", "?", " 123", " 一二三"}, - }, - { - name: "unicode", - patterns: []string{ - "\\p{N}{1,3}", - `[一-龥぀-ゟ゠-ヿ]+`, - "[!\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~][A-Za-z]+|[^\r\n\\p{L}\\p{P}\\p{S}]?[\\p{L}\\p{M}]+| ?[\\p{P}\\p{S}]+[\r\n]*|\\s*[\r\n]+|\\s+(?!\\S)|\\s+", - }, - want: []string{"Hello", ",", " WORLD", "!!", " How", "'s", " it", " going", "?", " ", "123", " ", "一二三"}, - }, - { - name: "individual digits", - patterns: []string{ - "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?\\p{L}+|\\p{N}| ?[^\\s\\p{L}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+", - }, - want: []string{"Hello", ",", " WORLD", "!!", " How", "'s", " it", " going", "?", " ", "1", "2", "3", " 一二三"}, - }, - } - - for _, tt := range cases { - t.Run(tt.name, func(t *testing.T) { - tokenizer := NewBytePairEncoding(nil, tt.patterns...) - if diff := cmp.Diff(tt.want, slices.Collect(tokenizer.split("Hello, WORLD!! How's it going? 123 一二三"))); diff != "" { - t.Errorf("no match (-theirs +ours):\n%s", diff) - } - }) - } -} diff --git a/tokenizer/sentencepiece.go b/tokenizer/sentencepiece.go deleted file mode 100644 index b2dee8c86..000000000 --- a/tokenizer/sentencepiece.go +++ /dev/null @@ -1,241 +0,0 @@ -package tokenizer - -import ( - "container/heap" - "fmt" - "log/slog" - "strconv" - "strings" - - "github.com/ollama/ollama/logutil" -) - -const spmWhitespaceSep = "▁" - -type SentencePiece struct { - maxTokenLen int - vocab *Vocabulary -} - -var _ Tokenizer = (*SentencePiece)(nil) - -func NewSentencePiece(vocab *Vocabulary) SentencePiece { - logutil.Trace("Tokens", "num tokens", len(vocab.Values), "vals", vocab.Values[:5], "scores", vocab.Scores[:5], "types", vocab.Types[:5]) - - counter := map[int]int{} - var maxTokenLen int - for cnt := range vocab.Types { - switch vocab.Types[cnt] { - case TOKEN_TYPE_NORMAL, TOKEN_TYPE_USER_DEFINED, TOKEN_TYPE_UNUSED: - maxTokenLen = max(maxTokenLen, len(vocab.Values[cnt])) - fallthrough - default: - counter[int(vocab.Types[cnt])] += 1 - } - } - - logutil.Trace("Token counts", "normal", counter[TOKEN_TYPE_NORMAL], "unknown", counter[TOKEN_TYPE_UNKNOWN], "control", counter[TOKEN_TYPE_CONTROL], - "user defined", counter[TOKEN_TYPE_USER_DEFINED], "unused", counter[TOKEN_TYPE_UNUSED], "byte", counter[TOKEN_TYPE_BYTE], - "max token len", maxTokenLen) - - return SentencePiece{ - maxTokenLen: maxTokenLen, - vocab: vocab, - } -} - -func (spm SentencePiece) Encode(s string, addSpecial bool) ([]int32, error) { - fragments := []fragment{{value: s}} - for _, special := range spm.vocab.SpecialVocabulary() { - id := spm.vocab.Encode(special) - for i := 0; i < len(fragments); i++ { - frag := fragments[i] - if len(frag.ids) > 0 { - continue - } - - var middle []fragment - switch i := strings.Index(frag.value, special); { - case i < 0: - middle = append(middle, frag) - case i > 0: - middle = append(middle, fragment{value: frag.value[:i]}) - fallthrough - default: - middle = append(middle, fragment{value: special, ids: []int32{id}}) - if rest := frag.value[i+len(special):]; rest != "" { - middle = append(middle, fragment{value: rest}) - } - } - - fragments = append(fragments[:i], append(middle, fragments[i+1:]...)...) - } - } - - var ids []int32 - for _, frag := range fragments { - if len(frag.ids) > 0 { - ids = append(ids, frag.ids...) - continue - } - - text := strings.ReplaceAll(frag.value, " ", spmWhitespaceSep) - - if id := spm.vocab.Encode(text); id >= 0 { - ids = append(ids, id) - continue - } - - q := &queue{} - heap.Init(q) - - runes := []rune(text) - merges := make([]merge, len(runes)) - for r := range runes { - merges[r] = merge{ - p: r - 1, - n: r + 1, - runes: []rune{runes[r]}, - } - } - - pairwise := func(a, b int) *candidate { - if a < 0 || b >= len(runes) { - return nil - } - - left, right := string(merges[a].runes), string(merges[b].runes) - if id := spm.vocab.Encode(left + right); id >= 0 { - return &candidate{ - a: a, - b: b, - score: spm.vocab.Scores[id], - size: len(left) + len(right), - } - } - - return nil - } - - for i := range len(runes) - 1 { - if pair := pairwise(i, i+1); pair != nil { - heap.Push(q, pair) - } - } - - for q.Len() > 0 { - pair := heap.Pop(q).(*candidate) - left, right := merges[pair.a], merges[pair.b] - - if string(left.runes) == "" || string(right.runes) == "" || len(string(left.runes))+len(string(right.runes)) != pair.size { - continue - } - - merges[pair.a].runes = append(left.runes, right.runes...) - merges[pair.b].runes = nil - merges[pair.a].n = right.n - if right.n < len(merges) { - merges[right.n].p = pair.a - } - - if pair := pairwise(merges[pair.a].p, pair.a); pair != nil { - heap.Push(q, pair) - } - - if pair := pairwise(pair.a, merges[pair.a].n); pair != nil { - heap.Push(q, pair) - } - } - - for _, merge := range merges { - if token := string(merge.runes); token != "" { - id := spm.vocab.Encode(token) - - if id >= 0 { - ids = append(ids, id) - continue - } - - // Fallback to byte tokenization - var result []int32 - for _, b := range []byte(token) { - byteToken := fmt.Sprintf("<0x%02X>", b) - unknownID := spm.vocab.Encode(byteToken) - if unknownID >= 0 { - result = append(result, unknownID) - } else { - slog.Debug("unknown byte token", "byte", b, "token", byteToken) - } - } - - ids = append(ids, result...) - } - } - } - - if addSpecial { - ids = spm.vocab.addSpecials(ids) - } - - logutil.Trace("encoded", "string", s, "ids", ids) - return ids, nil -} - -type candidate struct { - a, b int - score float32 - size int -} - -type queue []*candidate - -func (q queue) Len() int { return len(q) } - -func (q queue) Less(i, j int) bool { - return (q[i].score > q[j].score) || (q[i].score == q[j].score && q[i].a < q[j].a) -} - -func (q queue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } - -func (q *queue) Push(x interface{}) { - item := x.(*candidate) - *q = append(*q, item) -} - -func (q *queue) Pop() interface{} { - old := *q - n := len(old) - item := old[n-1] - *q = old[0 : n-1] - return item -} - -func (spm SentencePiece) Decode(ids []int32) (string, error) { - var sb strings.Builder - for _, id := range ids { - data := spm.vocab.Decode(id) - data = strings.ReplaceAll(data, spmWhitespaceSep, " ") - - // For tokenizer that use byte tokens like "<0xEA>" - // convert them to the partial unicode character - // so they are buffered correctly by the runner instead - // of being sent back to the api as "<0xEA>" - if len(data) == 6 && strings.HasPrefix(data, "<0x") && strings.HasSuffix(data, ">") { - byteVal, err := strconv.ParseUint(data[1:5], 0, 8) - if err != nil { - return "", fmt.Errorf("failed to parse hex byte: %v", err) - } - - if err := sb.WriteByte(byte(byteVal)); err != nil { - return "", err - } - } else { - if _, err := sb.WriteString(data); err != nil { - return "", err - } - } - } - - logutil.Trace("decoded", "ids", ids, "string", sb.String()) - return sb.String(), nil -} diff --git a/tokenizer/sentencepiece/sentencepiece_model.pb.go b/tokenizer/sentencepiece/sentencepiece_model.pb.go deleted file mode 100644 index 76d136e84..000000000 --- a/tokenizer/sentencepiece/sentencepiece_model.pb.go +++ /dev/null @@ -1,1497 +0,0 @@ -// Copyright 2016 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License.! - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc v4.25.2 -// source: sentencepiece_model.proto - -package sentencepiece - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Model type. only have UNIGRAM now. -type TrainerSpec_ModelType int32 - -const ( - TrainerSpec_UNIGRAM TrainerSpec_ModelType = 1 // Unigram language model with dynamic algorithm - TrainerSpec_BPE TrainerSpec_ModelType = 2 // Byte Pair Encoding - TrainerSpec_WORD TrainerSpec_ModelType = 3 // Delimitered by whitespace. - TrainerSpec_CHAR TrainerSpec_ModelType = 4 // tokenizes into character sequence -) - -// Enum value maps for TrainerSpec_ModelType. -var ( - TrainerSpec_ModelType_name = map[int32]string{ - 1: "UNIGRAM", - 2: "BPE", - 3: "WORD", - 4: "CHAR", - } - TrainerSpec_ModelType_value = map[string]int32{ - "UNIGRAM": 1, - "BPE": 2, - "WORD": 3, - "CHAR": 4, - } -) - -func (x TrainerSpec_ModelType) Enum() *TrainerSpec_ModelType { - p := new(TrainerSpec_ModelType) - *p = x - return p -} - -func (x TrainerSpec_ModelType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (TrainerSpec_ModelType) Descriptor() protoreflect.EnumDescriptor { - return file_sentencepiece_model_proto_enumTypes[0].Descriptor() -} - -func (TrainerSpec_ModelType) Type() protoreflect.EnumType { - return &file_sentencepiece_model_proto_enumTypes[0] -} - -func (x TrainerSpec_ModelType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Do not use. -func (x *TrainerSpec_ModelType) UnmarshalJSON(b []byte) error { - num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) - if err != nil { - return err - } - *x = TrainerSpec_ModelType(num) - return nil -} - -// Deprecated: Use TrainerSpec_ModelType.Descriptor instead. -func (TrainerSpec_ModelType) EnumDescriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{0, 0} -} - -type ModelProto_SentencePiece_Type int32 - -const ( - ModelProto_SentencePiece_NORMAL ModelProto_SentencePiece_Type = 1 // normal symbol - ModelProto_SentencePiece_UNKNOWN ModelProto_SentencePiece_Type = 2 // unknown symbol. only for now. - ModelProto_SentencePiece_CONTROL ModelProto_SentencePiece_Type = 3 // control symbols. , , <2ja> etc. - ModelProto_SentencePiece_USER_DEFINED ModelProto_SentencePiece_Type = 4 // user defined symbols. - // Typical usage of USER_DEFINED symbol - // is placeholder. - ModelProto_SentencePiece_BYTE ModelProto_SentencePiece_Type = 6 // byte symbols. Used when `byte_fallback` is true. - ModelProto_SentencePiece_UNUSED ModelProto_SentencePiece_Type = 5 // this piece is not used. -) - -// Enum value maps for ModelProto_SentencePiece_Type. -var ( - ModelProto_SentencePiece_Type_name = map[int32]string{ - 1: "NORMAL", - 2: "UNKNOWN", - 3: "CONTROL", - 4: "USER_DEFINED", - 6: "BYTE", - 5: "UNUSED", - } - ModelProto_SentencePiece_Type_value = map[string]int32{ - "NORMAL": 1, - "UNKNOWN": 2, - "CONTROL": 3, - "USER_DEFINED": 4, - "BYTE": 6, - "UNUSED": 5, - } -) - -func (x ModelProto_SentencePiece_Type) Enum() *ModelProto_SentencePiece_Type { - p := new(ModelProto_SentencePiece_Type) - *p = x - return p -} - -func (x ModelProto_SentencePiece_Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ModelProto_SentencePiece_Type) Descriptor() protoreflect.EnumDescriptor { - return file_sentencepiece_model_proto_enumTypes[1].Descriptor() -} - -func (ModelProto_SentencePiece_Type) Type() protoreflect.EnumType { - return &file_sentencepiece_model_proto_enumTypes[1] -} - -func (x ModelProto_SentencePiece_Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Do not use. -func (x *ModelProto_SentencePiece_Type) UnmarshalJSON(b []byte) error { - num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b) - if err != nil { - return err - } - *x = ModelProto_SentencePiece_Type(num) - return nil -} - -// Deprecated: Use ModelProto_SentencePiece_Type.Descriptor instead. -func (ModelProto_SentencePiece_Type) EnumDescriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{3, 0, 0} -} - -// TrainerSpec encodes a various parameters for SentencePiece training. -// Next id: 55 -type TrainerSpec struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - extensionFields protoimpl.ExtensionFields - - // ///////////////////////////////////////////////////////////////// - // General parameters - // - // Input corpus files. - // - // Trainer accepts the following two formats: - // A) Monolingual: plain text, one sentence per line. - // B) Bilingual: TSV, source sentence target sentence - // When bilingual data is passed, shared vocabulary model is built. - // Note that the input file must be raw corpus, not a preprocessed corpus. - // Trainer only loads the first `input_sentence_size` sentences specified - // with this parameter. - Input []string `protobuf:"bytes,1,rep,name=input" json:"input,omitempty"` - // Input corpus format: - // "text": one-sentence-per-line text format (default) - // "tsv": sentence freq - InputFormat *string `protobuf:"bytes,7,opt,name=input_format,json=inputFormat" json:"input_format,omitempty"` - // Output model file prefix. - // .model and .vocab are generated. - ModelPrefix *string `protobuf:"bytes,2,opt,name=model_prefix,json=modelPrefix" json:"model_prefix,omitempty"` - ModelType *TrainerSpec_ModelType `protobuf:"varint,3,opt,name=model_type,json=modelType,enum=sentencepiece.TrainerSpec_ModelType,def=1" json:"model_type,omitempty"` - // Vocabulary size. 8k is the default size. - VocabSize *int32 `protobuf:"varint,4,opt,name=vocab_size,json=vocabSize,def=8000" json:"vocab_size,omitempty"` - // List of the languages this model can accept. - // Since the model is language-agnostic, this field is used as a reference. - AcceptLanguage []string `protobuf:"bytes,5,rep,name=accept_language,json=acceptLanguage" json:"accept_language,omitempty"` - // Size of self-test samples, which are encoded in the model file. - SelfTestSampleSize *int32 `protobuf:"varint,6,opt,name=self_test_sample_size,json=selfTestSampleSize,def=0" json:"self_test_sample_size,omitempty"` - // Whether to use DP version of sentencepiece. Use it with TSV input format - // (requires precomputed word tab counts to work). - EnableDifferentialPrivacy *bool `protobuf:"varint,50,opt,name=enable_differential_privacy,json=enableDifferentialPrivacy,def=0" json:"enable_differential_privacy,omitempty"` - // Set these parameters if you need DP version of sentencepiece. - // std of noise to add. - DifferentialPrivacyNoiseLevel *float32 `protobuf:"fixed32,51,opt,name=differential_privacy_noise_level,json=differentialPrivacyNoiseLevel,def=0" json:"differential_privacy_noise_level,omitempty"` - // Clipping threshold to apply after adding noise. All the words with - // frequency less than this value are dropped. - DifferentialPrivacyClippingThreshold *uint64 `protobuf:"varint,52,opt,name=differential_privacy_clipping_threshold,json=differentialPrivacyClippingThreshold,def=0" json:"differential_privacy_clipping_threshold,omitempty"` - // ///////////////////////////////////////////////////////////////// - // Training parameters. - // - // Uses characters which cover the corpus with the ratio of `chars_coverage`. - // This parameter determines the set of basic Alphabet of sentence piece. - // 1.0 - `chars_coverage` characters are treated as UNK. - // See also required_chars field. - CharacterCoverage *float32 `protobuf:"fixed32,10,opt,name=character_coverage,json=characterCoverage,def=0.9995" json:"character_coverage,omitempty"` - // Maximum size of sentences the trainer loads from `input` parameter. - // Trainer simply loads the `input` files in sequence. - // It is better to shuffle the input corpus randomly. - InputSentenceSize *uint64 `protobuf:"varint,11,opt,name=input_sentence_size,json=inputSentenceSize,def=0" json:"input_sentence_size,omitempty"` - ShuffleInputSentence *bool `protobuf:"varint,19,opt,name=shuffle_input_sentence,json=shuffleInputSentence,def=1" json:"shuffle_input_sentence,omitempty"` - // Maximum size of sentences to make seed sentence pieces. - // Extended suffix array is constructed to extract frequent - // sub-strings from the corpus. This uses 20N working space, - // where N is the size of corpus. - // - // Deprecated: Marked as deprecated in sentencepiece_model.proto. - MiningSentenceSize *int32 `protobuf:"varint,12,opt,name=mining_sentence_size,json=miningSentenceSize" json:"mining_sentence_size,omitempty"` - // Maximum size of sentences to train sentence pieces. - // - // Deprecated: Marked as deprecated in sentencepiece_model.proto. - TrainingSentenceSize *int32 `protobuf:"varint,13,opt,name=training_sentence_size,json=trainingSentenceSize" json:"training_sentence_size,omitempty"` - // The size of seed sentencepieces. - // `seed_sentencepiece_size` must be larger than `vocab_size`. - SeedSentencepieceSize *int32 `protobuf:"varint,14,opt,name=seed_sentencepiece_size,json=seedSentencepieceSize,def=1000000" json:"seed_sentencepiece_size,omitempty"` - // In every EM sub-iterations, keeps top - // `shrinking_factor` * `current sentencepieces size` with respect to - // the loss of the sentence piece. This value should be smaller than 1.0. - ShrinkingFactor *float32 `protobuf:"fixed32,15,opt,name=shrinking_factor,json=shrinkingFactor,def=0.75" json:"shrinking_factor,omitempty"` - // The maximum sentence length in byte. The sentences with the length - // larger than `max_sentence_length` is simply ignored. - // Longer input tends to bring the following risks: - // - Overflow during EM training (unigram language model only) - // - Performance drop because of O(n log n) cost in BPE. - MaxSentenceLength *int32 `protobuf:"varint,18,opt,name=max_sentence_length,json=maxSentenceLength,def=4192" json:"max_sentence_length,omitempty"` - // Number of threads in the training. - NumThreads *int32 `protobuf:"varint,16,opt,name=num_threads,json=numThreads,def=16" json:"num_threads,omitempty"` - // Number of EM sub iterations. - NumSubIterations *int32 `protobuf:"varint,17,opt,name=num_sub_iterations,json=numSubIterations,def=2" json:"num_sub_iterations,omitempty"` - // ///////////////////////////////////////////////////////////////// - // SentencePiece parameters which control the shapes of sentence piece. - // - // Maximum length of sentencepiece. - MaxSentencepieceLength *int32 `protobuf:"varint,20,opt,name=max_sentencepiece_length,json=maxSentencepieceLength,def=16" json:"max_sentencepiece_length,omitempty"` - // Uses Unicode script to split sentence pieces. - // When `split_by_unicode_script` is true, we do not allow sentence piece to - // include multiple Unicode scripts, e.g. "F1" is not a valid piece. - // Exception: CJ characters (Hiragana/Katakana/Han) are all handled - // as one script type, since Japanese word can consist of multiple scripts. - // This exception is always applied regardless of the accept-language - // parameter. - SplitByUnicodeScript *bool `protobuf:"varint,21,opt,name=split_by_unicode_script,json=splitByUnicodeScript,def=1" json:"split_by_unicode_script,omitempty"` - // When `split_by_number` is true, put a boundary between number and - // non-number transition. If we want to treat "F1" is one token, set this flag - // to be false. - SplitByNumber *bool `protobuf:"varint,23,opt,name=split_by_number,json=splitByNumber,def=1" json:"split_by_number,omitempty"` - // Use a white space to split sentence pieces. - // When `split_by_whitespace` is false, we may have the piece containing - // a white space in the middle. e.g., "in_the". - SplitByWhitespace *bool `protobuf:"varint,22,opt,name=split_by_whitespace,json=splitByWhitespace,def=1" json:"split_by_whitespace,omitempty"` - // Adds whitespace symbol (_) as a suffix instead of prefix. e.g., _hello => - // hello_. When `treat_whitespace_as_suffix` is true, - // NormalizerSpec::add_dummy_prefix will add the dummy whitespace to the end - // of sentence. - TreatWhitespaceAsSuffix *bool `protobuf:"varint,24,opt,name=treat_whitespace_as_suffix,json=treatWhitespaceAsSuffix,def=0" json:"treat_whitespace_as_suffix,omitempty"` - // Allows pieces that only contain whitespaces instead of appearing only as - // prefix or suffix of other pieces. - AllowWhitespaceOnlyPieces *bool `protobuf:"varint,26,opt,name=allow_whitespace_only_pieces,json=allowWhitespaceOnlyPieces,def=0" json:"allow_whitespace_only_pieces,omitempty"` - // Split all digits (0-9) into separate pieces. - SplitDigits *bool `protobuf:"varint,25,opt,name=split_digits,json=splitDigits,def=0" json:"split_digits,omitempty"` - // Defines the pre-tokenization delimiter. - // When specified, no pieces crossing this delimiter is not included - // in the vocab. Then the delimiter string is virtually ignored - // during the training. This field can allows constraints on the vocabulary - // selection. Note that this field is available on unigram mode. - PretokenizationDelimiter *string `protobuf:"bytes,53,opt,name=pretokenization_delimiter,json=pretokenizationDelimiter,def=" json:"pretokenization_delimiter,omitempty"` - // ///////////////////////////////////////////////////////////////// - // Vocabulary management - // - // Defines control symbols used as an indicator to - // change the behavior of the decoder. and are pre-defined. - // We can use this field to encode various meta information, - // including language indicator in multilingual model. - // These symbols are not visible to users, but visible to - // the decoder. Note that when the input sentence contains control symbols, - // they are not treated as one token, but segmented into normal pieces. - // Control symbols must be inserted independently from the segmentation. - ControlSymbols []string `protobuf:"bytes,30,rep,name=control_symbols,json=controlSymbols" json:"control_symbols,omitempty"` - // Defines user defined symbols. - // These symbols are added with extremely high score - // so they are always treated as one unique symbol in any context. - // Typical usage of user_defined_symbols is placeholder for named entities. - UserDefinedSymbols []string `protobuf:"bytes,31,rep,name=user_defined_symbols,json=userDefinedSymbols" json:"user_defined_symbols,omitempty"` - // Defines required characters. Each UTF8 character in this string is included - // in the character set regardless of character_coverage value. Unlike - // user_defined_symbols, these characters have scores based on the frequency - // on input sentences, and the model can form subwords using characters - // in this field. - RequiredChars *string `protobuf:"bytes,36,opt,name=required_chars,json=requiredChars" json:"required_chars,omitempty"` - // Decomposes unknown pieces into UTF-8 bytes. - ByteFallback *bool `protobuf:"varint,35,opt,name=byte_fallback,json=byteFallback,def=0" json:"byte_fallback,omitempty"` - // When creating the vocabulary file, defines whether or not to additionally - // output the score for each piece. - VocabularyOutputPieceScore *bool `protobuf:"varint,32,opt,name=vocabulary_output_piece_score,json=vocabularyOutputPieceScore,def=1" json:"vocabulary_output_piece_score,omitempty"` - // `vocab_size` is treated as hard limit. Crash if - // the model can not produce the vocab of size `vocab_size`, - // When `hard_vocab_limit` is false, vocab_size is treated - // as soft limit. Note that when model_type=char, - // always assumes hard_vocab_limit = false. - HardVocabLimit *bool `protobuf:"varint,33,opt,name=hard_vocab_limit,json=hardVocabLimit,def=1" json:"hard_vocab_limit,omitempty"` - // use all symbols for vocab extraction. This flag is valid - // if model type is either CHAR or WORD - UseAllVocab *bool `protobuf:"varint,34,opt,name=use_all_vocab,json=useAllVocab,def=0" json:"use_all_vocab,omitempty"` - // ///////////////////////////////////////////////////////////////// - // Reserved special meta tokens. - // * -1 is not used. - // * unk_id must not be -1. - // Id must start with 0 and be contiguous. - UnkId *int32 `protobuf:"varint,40,opt,name=unk_id,json=unkId,def=0" json:"unk_id,omitempty"` // - BosId *int32 `protobuf:"varint,41,opt,name=bos_id,json=bosId,def=1" json:"bos_id,omitempty"` // - EosId *int32 `protobuf:"varint,42,opt,name=eos_id,json=eosId,def=2" json:"eos_id,omitempty"` // - PadId *int32 `protobuf:"varint,43,opt,name=pad_id,json=padId,def=-1" json:"pad_id,omitempty"` // (padding) - UnkPiece *string `protobuf:"bytes,45,opt,name=unk_piece,json=unkPiece,def=" json:"unk_piece,omitempty"` - BosPiece *string `protobuf:"bytes,46,opt,name=bos_piece,json=bosPiece,def=" json:"bos_piece,omitempty"` - EosPiece *string `protobuf:"bytes,47,opt,name=eos_piece,json=eosPiece,def=" json:"eos_piece,omitempty"` - PadPiece *string `protobuf:"bytes,48,opt,name=pad_piece,json=padPiece,def=" json:"pad_piece,omitempty"` - // Encodes into U+2047 (DOUBLE QUESTION MARK), - // since this character can be useful both for user and - // developer. We can easily figure out that is emitted. - UnkSurface *string `protobuf:"bytes,44,opt,name=unk_surface,json=unkSurface,def= ⁇ " json:"unk_surface,omitempty"` - // Increase bit depth to allow unigram model training on large - // (>10M sentences) corpora. A Side-effect of enabling this flag - // is increased memory usage. - TrainExtremelyLargeCorpus *bool `protobuf:"varint,49,opt,name=train_extremely_large_corpus,json=trainExtremelyLargeCorpus,def=0" json:"train_extremely_large_corpus,omitempty"` - // Path to a seed sentencepieces file, with one tab-separated - // seed sentencepiece frequency per line. - SeedSentencepiecesFile *string `protobuf:"bytes,54,opt,name=seed_sentencepieces_file,json=seedSentencepiecesFile,def=" json:"seed_sentencepieces_file,omitempty"` -} - -// Default values for TrainerSpec fields. -const ( - Default_TrainerSpec_ModelType = TrainerSpec_UNIGRAM - Default_TrainerSpec_VocabSize = int32(8000) - Default_TrainerSpec_SelfTestSampleSize = int32(0) - Default_TrainerSpec_EnableDifferentialPrivacy = bool(false) - Default_TrainerSpec_DifferentialPrivacyNoiseLevel = float32(0) - Default_TrainerSpec_DifferentialPrivacyClippingThreshold = uint64(0) - Default_TrainerSpec_CharacterCoverage = float32(0.9994999766349792) - Default_TrainerSpec_InputSentenceSize = uint64(0) - Default_TrainerSpec_ShuffleInputSentence = bool(true) - Default_TrainerSpec_SeedSentencepieceSize = int32(1000000) - Default_TrainerSpec_ShrinkingFactor = float32(0.75) - Default_TrainerSpec_MaxSentenceLength = int32(4192) - Default_TrainerSpec_NumThreads = int32(16) - Default_TrainerSpec_NumSubIterations = int32(2) - Default_TrainerSpec_MaxSentencepieceLength = int32(16) - Default_TrainerSpec_SplitByUnicodeScript = bool(true) - Default_TrainerSpec_SplitByNumber = bool(true) - Default_TrainerSpec_SplitByWhitespace = bool(true) - Default_TrainerSpec_TreatWhitespaceAsSuffix = bool(false) - Default_TrainerSpec_AllowWhitespaceOnlyPieces = bool(false) - Default_TrainerSpec_SplitDigits = bool(false) - Default_TrainerSpec_PretokenizationDelimiter = string("") - Default_TrainerSpec_ByteFallback = bool(false) - Default_TrainerSpec_VocabularyOutputPieceScore = bool(true) - Default_TrainerSpec_HardVocabLimit = bool(true) - Default_TrainerSpec_UseAllVocab = bool(false) - Default_TrainerSpec_UnkId = int32(0) - Default_TrainerSpec_BosId = int32(1) - Default_TrainerSpec_EosId = int32(2) - Default_TrainerSpec_PadId = int32(-1) - Default_TrainerSpec_UnkPiece = string("") - Default_TrainerSpec_BosPiece = string("") - Default_TrainerSpec_EosPiece = string("") - Default_TrainerSpec_PadPiece = string("") - Default_TrainerSpec_UnkSurface = string(" ⁇ ") - Default_TrainerSpec_TrainExtremelyLargeCorpus = bool(false) - Default_TrainerSpec_SeedSentencepiecesFile = string("") -) - -func (x *TrainerSpec) Reset() { - *x = TrainerSpec{} - if protoimpl.UnsafeEnabled { - mi := &file_sentencepiece_model_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TrainerSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TrainerSpec) ProtoMessage() {} - -func (x *TrainerSpec) ProtoReflect() protoreflect.Message { - mi := &file_sentencepiece_model_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TrainerSpec.ProtoReflect.Descriptor instead. -func (*TrainerSpec) Descriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{0} -} - -func (x *TrainerSpec) GetInput() []string { - if x != nil { - return x.Input - } - return nil -} - -func (x *TrainerSpec) GetInputFormat() string { - if x != nil && x.InputFormat != nil { - return *x.InputFormat - } - return "" -} - -func (x *TrainerSpec) GetModelPrefix() string { - if x != nil && x.ModelPrefix != nil { - return *x.ModelPrefix - } - return "" -} - -func (x *TrainerSpec) GetModelType() TrainerSpec_ModelType { - if x != nil && x.ModelType != nil { - return *x.ModelType - } - return Default_TrainerSpec_ModelType -} - -func (x *TrainerSpec) GetVocabSize() int32 { - if x != nil && x.VocabSize != nil { - return *x.VocabSize - } - return Default_TrainerSpec_VocabSize -} - -func (x *TrainerSpec) GetAcceptLanguage() []string { - if x != nil { - return x.AcceptLanguage - } - return nil -} - -func (x *TrainerSpec) GetSelfTestSampleSize() int32 { - if x != nil && x.SelfTestSampleSize != nil { - return *x.SelfTestSampleSize - } - return Default_TrainerSpec_SelfTestSampleSize -} - -func (x *TrainerSpec) GetEnableDifferentialPrivacy() bool { - if x != nil && x.EnableDifferentialPrivacy != nil { - return *x.EnableDifferentialPrivacy - } - return Default_TrainerSpec_EnableDifferentialPrivacy -} - -func (x *TrainerSpec) GetDifferentialPrivacyNoiseLevel() float32 { - if x != nil && x.DifferentialPrivacyNoiseLevel != nil { - return *x.DifferentialPrivacyNoiseLevel - } - return Default_TrainerSpec_DifferentialPrivacyNoiseLevel -} - -func (x *TrainerSpec) GetDifferentialPrivacyClippingThreshold() uint64 { - if x != nil && x.DifferentialPrivacyClippingThreshold != nil { - return *x.DifferentialPrivacyClippingThreshold - } - return Default_TrainerSpec_DifferentialPrivacyClippingThreshold -} - -func (x *TrainerSpec) GetCharacterCoverage() float32 { - if x != nil && x.CharacterCoverage != nil { - return *x.CharacterCoverage - } - return Default_TrainerSpec_CharacterCoverage -} - -func (x *TrainerSpec) GetInputSentenceSize() uint64 { - if x != nil && x.InputSentenceSize != nil { - return *x.InputSentenceSize - } - return Default_TrainerSpec_InputSentenceSize -} - -func (x *TrainerSpec) GetShuffleInputSentence() bool { - if x != nil && x.ShuffleInputSentence != nil { - return *x.ShuffleInputSentence - } - return Default_TrainerSpec_ShuffleInputSentence -} - -// Deprecated: Marked as deprecated in sentencepiece_model.proto. -func (x *TrainerSpec) GetMiningSentenceSize() int32 { - if x != nil && x.MiningSentenceSize != nil { - return *x.MiningSentenceSize - } - return 0 -} - -// Deprecated: Marked as deprecated in sentencepiece_model.proto. -func (x *TrainerSpec) GetTrainingSentenceSize() int32 { - if x != nil && x.TrainingSentenceSize != nil { - return *x.TrainingSentenceSize - } - return 0 -} - -func (x *TrainerSpec) GetSeedSentencepieceSize() int32 { - if x != nil && x.SeedSentencepieceSize != nil { - return *x.SeedSentencepieceSize - } - return Default_TrainerSpec_SeedSentencepieceSize -} - -func (x *TrainerSpec) GetShrinkingFactor() float32 { - if x != nil && x.ShrinkingFactor != nil { - return *x.ShrinkingFactor - } - return Default_TrainerSpec_ShrinkingFactor -} - -func (x *TrainerSpec) GetMaxSentenceLength() int32 { - if x != nil && x.MaxSentenceLength != nil { - return *x.MaxSentenceLength - } - return Default_TrainerSpec_MaxSentenceLength -} - -func (x *TrainerSpec) GetNumThreads() int32 { - if x != nil && x.NumThreads != nil { - return *x.NumThreads - } - return Default_TrainerSpec_NumThreads -} - -func (x *TrainerSpec) GetNumSubIterations() int32 { - if x != nil && x.NumSubIterations != nil { - return *x.NumSubIterations - } - return Default_TrainerSpec_NumSubIterations -} - -func (x *TrainerSpec) GetMaxSentencepieceLength() int32 { - if x != nil && x.MaxSentencepieceLength != nil { - return *x.MaxSentencepieceLength - } - return Default_TrainerSpec_MaxSentencepieceLength -} - -func (x *TrainerSpec) GetSplitByUnicodeScript() bool { - if x != nil && x.SplitByUnicodeScript != nil { - return *x.SplitByUnicodeScript - } - return Default_TrainerSpec_SplitByUnicodeScript -} - -func (x *TrainerSpec) GetSplitByNumber() bool { - if x != nil && x.SplitByNumber != nil { - return *x.SplitByNumber - } - return Default_TrainerSpec_SplitByNumber -} - -func (x *TrainerSpec) GetSplitByWhitespace() bool { - if x != nil && x.SplitByWhitespace != nil { - return *x.SplitByWhitespace - } - return Default_TrainerSpec_SplitByWhitespace -} - -func (x *TrainerSpec) GetTreatWhitespaceAsSuffix() bool { - if x != nil && x.TreatWhitespaceAsSuffix != nil { - return *x.TreatWhitespaceAsSuffix - } - return Default_TrainerSpec_TreatWhitespaceAsSuffix -} - -func (x *TrainerSpec) GetAllowWhitespaceOnlyPieces() bool { - if x != nil && x.AllowWhitespaceOnlyPieces != nil { - return *x.AllowWhitespaceOnlyPieces - } - return Default_TrainerSpec_AllowWhitespaceOnlyPieces -} - -func (x *TrainerSpec) GetSplitDigits() bool { - if x != nil && x.SplitDigits != nil { - return *x.SplitDigits - } - return Default_TrainerSpec_SplitDigits -} - -func (x *TrainerSpec) GetPretokenizationDelimiter() string { - if x != nil && x.PretokenizationDelimiter != nil { - return *x.PretokenizationDelimiter - } - return Default_TrainerSpec_PretokenizationDelimiter -} - -func (x *TrainerSpec) GetControlSymbols() []string { - if x != nil { - return x.ControlSymbols - } - return nil -} - -func (x *TrainerSpec) GetUserDefinedSymbols() []string { - if x != nil { - return x.UserDefinedSymbols - } - return nil -} - -func (x *TrainerSpec) GetRequiredChars() string { - if x != nil && x.RequiredChars != nil { - return *x.RequiredChars - } - return "" -} - -func (x *TrainerSpec) GetByteFallback() bool { - if x != nil && x.ByteFallback != nil { - return *x.ByteFallback - } - return Default_TrainerSpec_ByteFallback -} - -func (x *TrainerSpec) GetVocabularyOutputPieceScore() bool { - if x != nil && x.VocabularyOutputPieceScore != nil { - return *x.VocabularyOutputPieceScore - } - return Default_TrainerSpec_VocabularyOutputPieceScore -} - -func (x *TrainerSpec) GetHardVocabLimit() bool { - if x != nil && x.HardVocabLimit != nil { - return *x.HardVocabLimit - } - return Default_TrainerSpec_HardVocabLimit -} - -func (x *TrainerSpec) GetUseAllVocab() bool { - if x != nil && x.UseAllVocab != nil { - return *x.UseAllVocab - } - return Default_TrainerSpec_UseAllVocab -} - -func (x *TrainerSpec) GetUnkId() int32 { - if x != nil && x.UnkId != nil { - return *x.UnkId - } - return Default_TrainerSpec_UnkId -} - -func (x *TrainerSpec) GetBosId() int32 { - if x != nil && x.BosId != nil { - return *x.BosId - } - return Default_TrainerSpec_BosId -} - -func (x *TrainerSpec) GetEosId() int32 { - if x != nil && x.EosId != nil { - return *x.EosId - } - return Default_TrainerSpec_EosId -} - -func (x *TrainerSpec) GetPadId() int32 { - if x != nil && x.PadId != nil { - return *x.PadId - } - return Default_TrainerSpec_PadId -} - -func (x *TrainerSpec) GetUnkPiece() string { - if x != nil && x.UnkPiece != nil { - return *x.UnkPiece - } - return Default_TrainerSpec_UnkPiece -} - -func (x *TrainerSpec) GetBosPiece() string { - if x != nil && x.BosPiece != nil { - return *x.BosPiece - } - return Default_TrainerSpec_BosPiece -} - -func (x *TrainerSpec) GetEosPiece() string { - if x != nil && x.EosPiece != nil { - return *x.EosPiece - } - return Default_TrainerSpec_EosPiece -} - -func (x *TrainerSpec) GetPadPiece() string { - if x != nil && x.PadPiece != nil { - return *x.PadPiece - } - return Default_TrainerSpec_PadPiece -} - -func (x *TrainerSpec) GetUnkSurface() string { - if x != nil && x.UnkSurface != nil { - return *x.UnkSurface - } - return Default_TrainerSpec_UnkSurface -} - -func (x *TrainerSpec) GetTrainExtremelyLargeCorpus() bool { - if x != nil && x.TrainExtremelyLargeCorpus != nil { - return *x.TrainExtremelyLargeCorpus - } - return Default_TrainerSpec_TrainExtremelyLargeCorpus -} - -func (x *TrainerSpec) GetSeedSentencepiecesFile() string { - if x != nil && x.SeedSentencepiecesFile != nil { - return *x.SeedSentencepiecesFile - } - return Default_TrainerSpec_SeedSentencepiecesFile -} - -// NormalizerSpec encodes a various parameters for string normalizaiton -type NormalizerSpec struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - extensionFields protoimpl.ExtensionFields - - // name of normalization rule. - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // Pre-compiled normalization rule created by - // Builder::GetPrecompiledCharsMap() or Builder::CompileCharsMap() method. - // Usually this field is set by Builder::GetNormalizerSpec() method. - PrecompiledCharsmap []byte `protobuf:"bytes,2,opt,name=precompiled_charsmap,json=precompiledCharsmap" json:"precompiled_charsmap,omitempty"` - // Adds dummy whitespace at the beginning of text in order to - // treat "world" in "world" and "hello world" in the same way. - AddDummyPrefix *bool `protobuf:"varint,3,opt,name=add_dummy_prefix,json=addDummyPrefix,def=1" json:"add_dummy_prefix,omitempty"` - // Removes leading, trailing, and duplicate internal whitespace. - RemoveExtraWhitespaces *bool `protobuf:"varint,4,opt,name=remove_extra_whitespaces,json=removeExtraWhitespaces,def=1" json:"remove_extra_whitespaces,omitempty"` - // Replaces whitespace with meta symbol. - // This field must be true to train sentence piece model. - EscapeWhitespaces *bool `protobuf:"varint,5,opt,name=escape_whitespaces,json=escapeWhitespaces,def=1" json:"escape_whitespaces,omitempty"` - // Custom normalization rule file in TSV format. - // https://github.com/google/sentencepiece/blob/master/doc/normalization.md - // This field is only used in SentencePieceTrainer::Train() method, which - // compiles the rule into the binary rule stored in `precompiled_charsmap`. - NormalizationRuleTsv *string `protobuf:"bytes,6,opt,name=normalization_rule_tsv,json=normalizationRuleTsv" json:"normalization_rule_tsv,omitempty"` -} - -// Default values for NormalizerSpec fields. -const ( - Default_NormalizerSpec_AddDummyPrefix = bool(true) - Default_NormalizerSpec_RemoveExtraWhitespaces = bool(true) - Default_NormalizerSpec_EscapeWhitespaces = bool(true) -) - -func (x *NormalizerSpec) Reset() { - *x = NormalizerSpec{} - if protoimpl.UnsafeEnabled { - mi := &file_sentencepiece_model_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NormalizerSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NormalizerSpec) ProtoMessage() {} - -func (x *NormalizerSpec) ProtoReflect() protoreflect.Message { - mi := &file_sentencepiece_model_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NormalizerSpec.ProtoReflect.Descriptor instead. -func (*NormalizerSpec) Descriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{1} -} - -func (x *NormalizerSpec) GetName() string { - if x != nil && x.Name != nil { - return *x.Name - } - return "" -} - -func (x *NormalizerSpec) GetPrecompiledCharsmap() []byte { - if x != nil { - return x.PrecompiledCharsmap - } - return nil -} - -func (x *NormalizerSpec) GetAddDummyPrefix() bool { - if x != nil && x.AddDummyPrefix != nil { - return *x.AddDummyPrefix - } - return Default_NormalizerSpec_AddDummyPrefix -} - -func (x *NormalizerSpec) GetRemoveExtraWhitespaces() bool { - if x != nil && x.RemoveExtraWhitespaces != nil { - return *x.RemoveExtraWhitespaces - } - return Default_NormalizerSpec_RemoveExtraWhitespaces -} - -func (x *NormalizerSpec) GetEscapeWhitespaces() bool { - if x != nil && x.EscapeWhitespaces != nil { - return *x.EscapeWhitespaces - } - return Default_NormalizerSpec_EscapeWhitespaces -} - -func (x *NormalizerSpec) GetNormalizationRuleTsv() string { - if x != nil && x.NormalizationRuleTsv != nil { - return *x.NormalizationRuleTsv - } - return "" -} - -// Proto to store samples for self-testing. -type SelfTestData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - extensionFields protoimpl.ExtensionFields - - Samples []*SelfTestData_Sample `protobuf:"bytes,1,rep,name=samples" json:"samples,omitempty"` -} - -func (x *SelfTestData) Reset() { - *x = SelfTestData{} - if protoimpl.UnsafeEnabled { - mi := &file_sentencepiece_model_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SelfTestData) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SelfTestData) ProtoMessage() {} - -func (x *SelfTestData) ProtoReflect() protoreflect.Message { - mi := &file_sentencepiece_model_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SelfTestData.ProtoReflect.Descriptor instead. -func (*SelfTestData) Descriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{2} -} - -func (x *SelfTestData) GetSamples() []*SelfTestData_Sample { - if x != nil { - return x.Samples - } - return nil -} - -// ModelProto stores model parameters. -// SentencePieceProcessor is supposed to be self-contained. -// All settings/parameters which may change the behavior must be encoded -// in ModelProto. -type ModelProto struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - extensionFields protoimpl.ExtensionFields - - // Sentence pieces with scores. - Pieces []*ModelProto_SentencePiece `protobuf:"bytes,1,rep,name=pieces" json:"pieces,omitempty"` - // Spec used to generate this model file. - TrainerSpec *TrainerSpec `protobuf:"bytes,2,opt,name=trainer_spec,json=trainerSpec" json:"trainer_spec,omitempty"` - // Spec for text normalization. - NormalizerSpec *NormalizerSpec `protobuf:"bytes,3,opt,name=normalizer_spec,json=normalizerSpec" json:"normalizer_spec,omitempty"` - // Stores sample input and its expected segmentation to verify the model. - SelfTestData *SelfTestData `protobuf:"bytes,4,opt,name=self_test_data,json=selfTestData" json:"self_test_data,omitempty"` - // Spec for text de-normalization. - DenormalizerSpec *NormalizerSpec `protobuf:"bytes,5,opt,name=denormalizer_spec,json=denormalizerSpec" json:"denormalizer_spec,omitempty"` -} - -func (x *ModelProto) Reset() { - *x = ModelProto{} - if protoimpl.UnsafeEnabled { - mi := &file_sentencepiece_model_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ModelProto) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ModelProto) ProtoMessage() {} - -func (x *ModelProto) ProtoReflect() protoreflect.Message { - mi := &file_sentencepiece_model_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ModelProto.ProtoReflect.Descriptor instead. -func (*ModelProto) Descriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{3} -} - -func (x *ModelProto) GetPieces() []*ModelProto_SentencePiece { - if x != nil { - return x.Pieces - } - return nil -} - -func (x *ModelProto) GetTrainerSpec() *TrainerSpec { - if x != nil { - return x.TrainerSpec - } - return nil -} - -func (x *ModelProto) GetNormalizerSpec() *NormalizerSpec { - if x != nil { - return x.NormalizerSpec - } - return nil -} - -func (x *ModelProto) GetSelfTestData() *SelfTestData { - if x != nil { - return x.SelfTestData - } - return nil -} - -func (x *ModelProto) GetDenormalizerSpec() *NormalizerSpec { - if x != nil { - return x.DenormalizerSpec - } - return nil -} - -type SelfTestData_Sample struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Input *string `protobuf:"bytes,1,opt,name=input" json:"input,omitempty"` - Expected *string `protobuf:"bytes,2,opt,name=expected" json:"expected,omitempty"` -} - -func (x *SelfTestData_Sample) Reset() { - *x = SelfTestData_Sample{} - if protoimpl.UnsafeEnabled { - mi := &file_sentencepiece_model_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SelfTestData_Sample) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SelfTestData_Sample) ProtoMessage() {} - -func (x *SelfTestData_Sample) ProtoReflect() protoreflect.Message { - mi := &file_sentencepiece_model_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SelfTestData_Sample.ProtoReflect.Descriptor instead. -func (*SelfTestData_Sample) Descriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{2, 0} -} - -func (x *SelfTestData_Sample) GetInput() string { - if x != nil && x.Input != nil { - return *x.Input - } - return "" -} - -func (x *SelfTestData_Sample) GetExpected() string { - if x != nil && x.Expected != nil { - return *x.Expected - } - return "" -} - -type ModelProto_SentencePiece struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - extensionFields protoimpl.ExtensionFields - - Piece *string `protobuf:"bytes,1,opt,name=piece" json:"piece,omitempty"` // piece must not be empty. - Score *float32 `protobuf:"fixed32,2,opt,name=score" json:"score,omitempty"` - Type *ModelProto_SentencePiece_Type `protobuf:"varint,3,opt,name=type,enum=sentencepiece.ModelProto_SentencePiece_Type,def=1" json:"type,omitempty"` -} - -// Default values for ModelProto_SentencePiece fields. -const ( - Default_ModelProto_SentencePiece_Type = ModelProto_SentencePiece_NORMAL -) - -func (x *ModelProto_SentencePiece) Reset() { - *x = ModelProto_SentencePiece{} - if protoimpl.UnsafeEnabled { - mi := &file_sentencepiece_model_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ModelProto_SentencePiece) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ModelProto_SentencePiece) ProtoMessage() {} - -func (x *ModelProto_SentencePiece) ProtoReflect() protoreflect.Message { - mi := &file_sentencepiece_model_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ModelProto_SentencePiece.ProtoReflect.Descriptor instead. -func (*ModelProto_SentencePiece) Descriptor() ([]byte, []int) { - return file_sentencepiece_model_proto_rawDescGZIP(), []int{3, 0} -} - -func (x *ModelProto_SentencePiece) GetPiece() string { - if x != nil && x.Piece != nil { - return *x.Piece - } - return "" -} - -func (x *ModelProto_SentencePiece) GetScore() float32 { - if x != nil && x.Score != nil { - return *x.Score - } - return 0 -} - -func (x *ModelProto_SentencePiece) GetType() ModelProto_SentencePiece_Type { - if x != nil && x.Type != nil { - return *x.Type - } - return Default_ModelProto_SentencePiece_Type -} - -var File_sentencepiece_model_proto protoreflect.FileDescriptor - -var file_sentencepiece_model_proto_rawDesc = []byte{ - 0x0a, 0x19, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x73, 0x65, 0x6e, - 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x22, 0xc6, 0x12, 0x0a, 0x0b, 0x54, - 0x72, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x70, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x4c, 0x0a, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x73, 0x65, 0x6e, - 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, - 0x3a, 0x07, 0x55, 0x4e, 0x49, 0x47, 0x52, 0x41, 0x4d, 0x52, 0x09, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0a, 0x76, 0x6f, 0x63, 0x61, 0x62, 0x5f, 0x73, 0x69, - 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x04, 0x38, 0x30, 0x30, 0x30, 0x52, 0x09, - 0x76, 0x6f, 0x63, 0x61, 0x62, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x73, 0x65, 0x6c, 0x66, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x5f, - 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x05, 0x3a, 0x01, 0x30, 0x52, 0x12, 0x73, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x53, 0x61, - 0x6d, 0x70, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x45, 0x0a, 0x1b, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x18, 0x32, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, - 0x61, 0x6c, 0x73, 0x65, 0x52, 0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x69, 0x66, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x12, - 0x4a, 0x0a, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x63, 0x79, 0x5f, 0x6e, 0x6f, 0x69, 0x73, 0x65, 0x5f, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x18, 0x33, 0x20, 0x01, 0x28, 0x02, 0x3a, 0x01, 0x30, 0x52, 0x1d, 0x64, 0x69, - 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x69, 0x76, 0x61, 0x63, - 0x79, 0x4e, 0x6f, 0x69, 0x73, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x58, 0x0a, 0x27, 0x64, - 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x63, 0x79, 0x5f, 0x63, 0x6c, 0x69, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, - 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x34, 0x20, 0x01, 0x28, 0x04, 0x3a, 0x01, 0x30, 0x52, - 0x24, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x69, - 0x76, 0x61, 0x63, 0x79, 0x43, 0x6c, 0x69, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x35, 0x0a, 0x12, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, - 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x02, 0x3a, 0x06, 0x30, 0x2e, 0x39, 0x39, 0x39, 0x35, 0x52, 0x11, 0x63, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12, 0x31, 0x0a, 0x13, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x3a, 0x01, 0x30, 0x52, 0x11, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x3a, 0x0a, 0x16, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x3a, - 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x14, 0x73, 0x68, 0x75, 0x66, 0x66, 0x6c, 0x65, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x14, 0x6d, - 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x6d, - 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x38, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, - 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x05, 0x42, 0x02, 0x18, 0x01, 0x52, 0x14, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x53, - 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x3f, 0x0a, 0x17, 0x73, - 0x65, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, - 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x07, 0x31, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x52, 0x15, 0x73, 0x65, 0x65, 0x64, 0x53, 0x65, 0x6e, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x2f, 0x0a, 0x10, - 0x73, 0x68, 0x72, 0x69, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x02, 0x3a, 0x04, 0x30, 0x2e, 0x37, 0x35, 0x52, 0x0f, 0x73, 0x68, - 0x72, 0x69, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x34, 0x0a, - 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x04, 0x34, 0x31, 0x39, 0x32, - 0x52, 0x11, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x4c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x12, 0x23, 0x0a, 0x0b, 0x6e, 0x75, 0x6d, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, - 0x64, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x02, 0x31, 0x36, 0x52, 0x0a, 0x6e, 0x75, - 0x6d, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x12, 0x6e, 0x75, 0x6d, 0x5f, - 0x73, 0x75, 0x62, 0x5f, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x05, 0x3a, 0x01, 0x32, 0x52, 0x10, 0x6e, 0x75, 0x6d, 0x53, 0x75, 0x62, 0x49, - 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x18, 0x6d, 0x61, 0x78, - 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x5f, 0x6c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x02, 0x31, 0x36, 0x52, - 0x16, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, - 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x3b, 0x0a, 0x17, 0x73, 0x70, 0x6c, 0x69, 0x74, - 0x5f, 0x62, 0x79, 0x5f, 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x14, - 0x73, 0x70, 0x6c, 0x69, 0x74, 0x42, 0x79, 0x55, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x12, 0x2c, 0x0a, 0x0f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x62, 0x79, - 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, 0x74, - 0x72, 0x75, 0x65, 0x52, 0x0d, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x42, 0x79, 0x4e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x12, 0x34, 0x0a, 0x13, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x77, - 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x3a, - 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x11, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x42, 0x79, 0x57, 0x68, - 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x42, 0x0a, 0x1a, 0x74, 0x72, 0x65, 0x61, - 0x74, 0x5f, 0x77, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x73, 0x5f, - 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, - 0x6c, 0x73, 0x65, 0x52, 0x17, 0x74, 0x72, 0x65, 0x61, 0x74, 0x57, 0x68, 0x69, 0x74, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x41, 0x73, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x46, 0x0a, 0x1c, - 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x77, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x18, 0x1a, 0x20, 0x01, - 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x57, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x50, 0x69, - 0x65, 0x63, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0c, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x64, 0x69, - 0x67, 0x69, 0x74, 0x73, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, - 0x65, 0x52, 0x0b, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x44, 0x69, 0x67, 0x69, 0x74, 0x73, 0x12, 0x3d, - 0x0a, 0x19, 0x70, 0x72, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x18, 0x35, 0x20, 0x01, 0x28, - 0x09, 0x3a, 0x00, 0x52, 0x18, 0x70, 0x72, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, 0x12, 0x27, 0x0a, - 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, - 0x18, 0x1e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x53, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, - 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x1f, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x73, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, - 0x2a, 0x0a, 0x0d, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, - 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0c, 0x62, - 0x79, 0x74, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x47, 0x0a, 0x1d, 0x76, - 0x6f, 0x63, 0x61, 0x62, 0x75, 0x6c, 0x61, 0x72, 0x79, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x20, 0x20, 0x01, - 0x28, 0x08, 0x3a, 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x1a, 0x76, 0x6f, 0x63, 0x61, 0x62, 0x75, - 0x6c, 0x61, 0x72, 0x79, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x69, 0x65, 0x63, 0x65, 0x53, - 0x63, 0x6f, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x10, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x76, 0x6f, 0x63, - 0x61, 0x62, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, - 0x74, 0x72, 0x75, 0x65, 0x52, 0x0e, 0x68, 0x61, 0x72, 0x64, 0x56, 0x6f, 0x63, 0x61, 0x62, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x12, 0x29, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, - 0x76, 0x6f, 0x63, 0x61, 0x62, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, - 0x73, 0x65, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x41, 0x6c, 0x6c, 0x56, 0x6f, 0x63, 0x61, 0x62, 0x12, - 0x18, 0x0a, 0x06, 0x75, 0x6e, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x28, 0x20, 0x01, 0x28, 0x05, 0x3a, - 0x01, 0x30, 0x52, 0x05, 0x75, 0x6e, 0x6b, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x06, 0x62, 0x6f, 0x73, - 0x5f, 0x69, 0x64, 0x18, 0x29, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x01, 0x31, 0x52, 0x05, 0x62, 0x6f, - 0x73, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x06, 0x65, 0x6f, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x2a, 0x20, - 0x01, 0x28, 0x05, 0x3a, 0x01, 0x32, 0x52, 0x05, 0x65, 0x6f, 0x73, 0x49, 0x64, 0x12, 0x19, 0x0a, - 0x06, 0x70, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x05, 0x3a, 0x02, 0x2d, - 0x31, 0x52, 0x05, 0x70, 0x61, 0x64, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x09, 0x75, 0x6e, 0x6b, 0x5f, - 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x09, 0x3a, 0x05, 0x3c, 0x75, 0x6e, - 0x6b, 0x3e, 0x52, 0x08, 0x75, 0x6e, 0x6b, 0x50, 0x69, 0x65, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x09, - 0x62, 0x6f, 0x73, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x3a, - 0x03, 0x3c, 0x73, 0x3e, 0x52, 0x08, 0x62, 0x6f, 0x73, 0x50, 0x69, 0x65, 0x63, 0x65, 0x12, 0x21, - 0x0a, 0x09, 0x65, 0x6f, 0x73, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, - 0x09, 0x3a, 0x04, 0x3c, 0x2f, 0x73, 0x3e, 0x52, 0x08, 0x65, 0x6f, 0x73, 0x50, 0x69, 0x65, 0x63, - 0x65, 0x12, 0x22, 0x0a, 0x09, 0x70, 0x61, 0x64, 0x5f, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x30, - 0x20, 0x01, 0x28, 0x09, 0x3a, 0x05, 0x3c, 0x70, 0x61, 0x64, 0x3e, 0x52, 0x08, 0x70, 0x61, 0x64, - 0x50, 0x69, 0x65, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x0b, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x75, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x09, 0x3a, 0x05, 0x20, 0xe2, 0x81, 0x87, - 0x20, 0x52, 0x0a, 0x75, 0x6e, 0x6b, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x46, 0x0a, - 0x1c, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x6c, 0x79, - 0x5f, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x72, 0x70, 0x75, 0x73, 0x18, 0x31, 0x20, - 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x19, 0x74, 0x72, 0x61, 0x69, - 0x6e, 0x45, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x6c, 0x79, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x43, - 0x6f, 0x72, 0x70, 0x75, 0x73, 0x12, 0x3a, 0x0a, 0x18, 0x73, 0x65, 0x65, 0x64, 0x5f, 0x73, 0x65, - 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x5f, 0x66, 0x69, 0x6c, - 0x65, 0x18, 0x36, 0x20, 0x01, 0x28, 0x09, 0x3a, 0x00, 0x52, 0x16, 0x73, 0x65, 0x65, 0x64, 0x53, - 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x46, 0x69, 0x6c, - 0x65, 0x22, 0x35, 0x0a, 0x09, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, - 0x0a, 0x07, 0x55, 0x4e, 0x49, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x42, - 0x50, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x4f, 0x52, 0x44, 0x10, 0x03, 0x12, 0x08, - 0x0a, 0x04, 0x43, 0x48, 0x41, 0x52, 0x10, 0x04, 0x2a, 0x09, 0x08, 0xc8, 0x01, 0x10, 0x80, 0x80, - 0x80, 0x80, 0x02, 0x22, 0xbd, 0x02, 0x0a, 0x0e, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, - 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x70, 0x72, - 0x65, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x73, 0x6d, - 0x61, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x70, 0x72, 0x65, 0x63, 0x6f, 0x6d, - 0x70, 0x69, 0x6c, 0x65, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x6d, 0x61, 0x70, 0x12, 0x2e, 0x0a, - 0x10, 0x61, 0x64, 0x64, 0x5f, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, - 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x0e, 0x61, - 0x64, 0x64, 0x44, 0x75, 0x6d, 0x6d, 0x79, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x3e, 0x0a, - 0x18, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x77, 0x68, - 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x3a, - 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x78, 0x74, - 0x72, 0x61, 0x57, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x33, 0x0a, - 0x12, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5f, 0x77, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x04, 0x74, 0x72, 0x75, 0x65, 0x52, - 0x11, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x57, 0x68, 0x69, 0x74, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x73, 0x76, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x14, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x73, 0x76, 0x2a, 0x09, 0x08, 0xc8, 0x01, 0x10, 0x80, 0x80, - 0x80, 0x80, 0x02, 0x22, 0x93, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x3c, 0x0a, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, - 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x44, 0x61, - 0x74, 0x61, 0x2e, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x07, 0x73, 0x61, 0x6d, 0x70, 0x6c, - 0x65, 0x73, 0x1a, 0x3a, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2a, 0x09, - 0x08, 0xc8, 0x01, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xd7, 0x04, 0x0a, 0x0a, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x3f, 0x0a, 0x06, 0x70, 0x69, 0x65, 0x63, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x69, 0x65, 0x63, - 0x65, 0x52, 0x06, 0x70, 0x69, 0x65, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0c, 0x74, 0x72, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, - 0x54, 0x72, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0b, 0x74, 0x72, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x46, 0x0a, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, - 0x65, 0x2e, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, - 0x52, 0x0e, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, - 0x12, 0x41, 0x0a, 0x0e, 0x73, 0x65, 0x6c, 0x66, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x73, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x11, 0x64, 0x65, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x4e, - 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x10, 0x64, - 0x65, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x1a, - 0xe6, 0x01, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x69, 0x65, 0x63, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x69, 0x65, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x70, 0x69, 0x65, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x48, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x73, 0x65, - 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x65, - 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, - 0x69, 0x65, 0x63, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, - 0x4c, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x54, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, - 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x54, - 0x52, 0x4f, 0x4c, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x44, 0x45, - 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x59, 0x54, 0x45, 0x10, - 0x06, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x10, 0x05, 0x2a, 0x09, 0x08, - 0xc8, 0x01, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x2a, 0x09, 0x08, 0xc8, 0x01, 0x10, 0x80, 0x80, - 0x80, 0x80, 0x02, 0x42, 0x13, 0x48, 0x03, 0x5a, 0x0f, 0x2e, 0x2f, 0x73, 0x65, 0x6e, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x70, 0x69, 0x65, 0x63, 0x65, -} - -var ( - file_sentencepiece_model_proto_rawDescOnce sync.Once - file_sentencepiece_model_proto_rawDescData = file_sentencepiece_model_proto_rawDesc -) - -func file_sentencepiece_model_proto_rawDescGZIP() []byte { - file_sentencepiece_model_proto_rawDescOnce.Do(func() { - file_sentencepiece_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_sentencepiece_model_proto_rawDescData) - }) - return file_sentencepiece_model_proto_rawDescData -} - -var file_sentencepiece_model_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_sentencepiece_model_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_sentencepiece_model_proto_goTypes = []any{ - (TrainerSpec_ModelType)(0), // 0: sentencepiece.TrainerSpec.ModelType - (ModelProto_SentencePiece_Type)(0), // 1: sentencepiece.ModelProto.SentencePiece.Type - (*TrainerSpec)(nil), // 2: sentencepiece.TrainerSpec - (*NormalizerSpec)(nil), // 3: sentencepiece.NormalizerSpec - (*SelfTestData)(nil), // 4: sentencepiece.SelfTestData - (*ModelProto)(nil), // 5: sentencepiece.ModelProto - (*SelfTestData_Sample)(nil), // 6: sentencepiece.SelfTestData.Sample - (*ModelProto_SentencePiece)(nil), // 7: sentencepiece.ModelProto.SentencePiece -} -var file_sentencepiece_model_proto_depIdxs = []int32{ - 0, // 0: sentencepiece.TrainerSpec.model_type:type_name -> sentencepiece.TrainerSpec.ModelType - 6, // 1: sentencepiece.SelfTestData.samples:type_name -> sentencepiece.SelfTestData.Sample - 7, // 2: sentencepiece.ModelProto.pieces:type_name -> sentencepiece.ModelProto.SentencePiece - 2, // 3: sentencepiece.ModelProto.trainer_spec:type_name -> sentencepiece.TrainerSpec - 3, // 4: sentencepiece.ModelProto.normalizer_spec:type_name -> sentencepiece.NormalizerSpec - 4, // 5: sentencepiece.ModelProto.self_test_data:type_name -> sentencepiece.SelfTestData - 3, // 6: sentencepiece.ModelProto.denormalizer_spec:type_name -> sentencepiece.NormalizerSpec - 1, // 7: sentencepiece.ModelProto.SentencePiece.type:type_name -> sentencepiece.ModelProto.SentencePiece.Type - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name -} - -func init() { file_sentencepiece_model_proto_init() } -func file_sentencepiece_model_proto_init() { - if File_sentencepiece_model_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_sentencepiece_model_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*TrainerSpec); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - case 3: - return &v.extensionFields - default: - return nil - } - } - file_sentencepiece_model_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*NormalizerSpec); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - case 3: - return &v.extensionFields - default: - return nil - } - } - file_sentencepiece_model_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*SelfTestData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - case 3: - return &v.extensionFields - default: - return nil - } - } - file_sentencepiece_model_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*ModelProto); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - case 3: - return &v.extensionFields - default: - return nil - } - } - file_sentencepiece_model_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*SelfTestData_Sample); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_sentencepiece_model_proto_msgTypes[5].Exporter = func(v any, i int) any { - switch v := v.(*ModelProto_SentencePiece); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - case 3: - return &v.extensionFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_sentencepiece_model_proto_rawDesc, - NumEnums: 2, - NumMessages: 6, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_sentencepiece_model_proto_goTypes, - DependencyIndexes: file_sentencepiece_model_proto_depIdxs, - EnumInfos: file_sentencepiece_model_proto_enumTypes, - MessageInfos: file_sentencepiece_model_proto_msgTypes, - }.Build() - File_sentencepiece_model_proto = out.File - file_sentencepiece_model_proto_rawDesc = nil - file_sentencepiece_model_proto_goTypes = nil - file_sentencepiece_model_proto_depIdxs = nil -} diff --git a/tokenizer/sentencepiece/sentencepiece_model.proto b/tokenizer/sentencepiece/sentencepiece_model.proto deleted file mode 100644 index 370887a4a..000000000 --- a/tokenizer/sentencepiece/sentencepiece_model.proto +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2016 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License.! - -syntax = "proto2"; - -// TODO(taku): Needs to use LITE RUNTIME in OSS release. -option optimize_for = LITE_RUNTIME; -option go_package = "./sentencepiece"; - -package sentencepiece; - -// TrainerSpec encodes a various parameters for SentencePiece training. -// Next id: 55 -message TrainerSpec { - /////////////////////////////////////////////////////////////////// - // General parameters - // - // Input corpus files. - // Trainer accepts the following two formats: - // A) Monolingual: plain text, one sentence per line. - // B) Bilingual: TSV, source sentence target sentence - // When bilingual data is passed, shared vocabulary model is built. - // Note that the input file must be raw corpus, not a preprocessed corpus. - // Trainer only loads the first `input_sentence_size` sentences specified - // with this parameter. - repeated string input = 1; - - // Input corpus format: - // "text": one-sentence-per-line text format (default) - // "tsv": sentence freq - optional string input_format = 7; - - // Output model file prefix. - // .model and .vocab are generated. - optional string model_prefix = 2; - - // Model type. only have UNIGRAM now. - enum ModelType { - UNIGRAM = 1; // Unigram language model with dynamic algorithm - BPE = 2; // Byte Pair Encoding - WORD = 3; // Delimitered by whitespace. - CHAR = 4; // tokenizes into character sequence - } - optional ModelType model_type = 3 [default = UNIGRAM]; - - // Vocabulary size. 8k is the default size. - optional int32 vocab_size = 4 [default = 8000]; - - // List of the languages this model can accept. - // Since the model is language-agnostic, this field is used as a reference. - repeated string accept_language = 5; - - // Size of self-test samples, which are encoded in the model file. - optional int32 self_test_sample_size = 6 [default = 0]; - - // Whether to use DP version of sentencepiece. Use it with TSV input format - // (requires precomputed word tab counts to work). - optional bool enable_differential_privacy = 50 [default = false]; - // Set these parameters if you need DP version of sentencepiece. - // std of noise to add. - optional float differential_privacy_noise_level = 51 [default = 0.0]; - // Clipping threshold to apply after adding noise. All the words with - // frequency less than this value are dropped. - optional uint64 differential_privacy_clipping_threshold = 52 [default = 0]; - - /////////////////////////////////////////////////////////////////// - // Training parameters. - // - // Uses characters which cover the corpus with the ratio of `chars_coverage`. - // This parameter determines the set of basic Alphabet of sentence piece. - // 1.0 - `chars_coverage` characters are treated as UNK. - // See also required_chars field. - optional float character_coverage = 10 [default = 0.9995]; - - // Maximum size of sentences the trainer loads from `input` parameter. - // Trainer simply loads the `input` files in sequence. - // It is better to shuffle the input corpus randomly. - optional uint64 input_sentence_size = 11 [default = 0]; - optional bool shuffle_input_sentence = 19 [default = true]; - - // Maximum size of sentences to make seed sentence pieces. - // Extended suffix array is constructed to extract frequent - // sub-strings from the corpus. This uses 20N working space, - // where N is the size of corpus. - optional int32 mining_sentence_size = 12 [deprecated = true]; - - // Maximum size of sentences to train sentence pieces. - optional int32 training_sentence_size = 13 [deprecated = true]; - - // The size of seed sentencepieces. - // `seed_sentencepiece_size` must be larger than `vocab_size`. - optional int32 seed_sentencepiece_size = 14 [default = 1000000]; - - // In every EM sub-iterations, keeps top - // `shrinking_factor` * `current sentencepieces size` with respect to - // the loss of the sentence piece. This value should be smaller than 1.0. - optional float shrinking_factor = 15 [default = 0.75]; - - // The maximum sentence length in byte. The sentences with the length - // larger than `max_sentence_length` is simply ignored. - // Longer input tends to bring the following risks: - // * Overflow during EM training (unigram language model only) - // * Performance drop because of O(n log n) cost in BPE. - optional int32 max_sentence_length = 18 [default = 4192]; - - // Number of threads in the training. - optional int32 num_threads = 16 [default = 16]; - - // Number of EM sub iterations. - optional int32 num_sub_iterations = 17 [default = 2]; - - /////////////////////////////////////////////////////////////////// - // SentencePiece parameters which control the shapes of sentence piece. - // - // Maximum length of sentencepiece. - optional int32 max_sentencepiece_length = 20 [default = 16]; - - // Uses Unicode script to split sentence pieces. - // When `split_by_unicode_script` is true, we do not allow sentence piece to - // include multiple Unicode scripts, e.g. "F1" is not a valid piece. - // Exception: CJ characters (Hiragana/Katakana/Han) are all handled - // as one script type, since Japanese word can consist of multiple scripts. - // This exception is always applied regardless of the accept-language - // parameter. - optional bool split_by_unicode_script = 21 [default = true]; - - // When `split_by_number` is true, put a boundary between number and - // non-number transition. If we want to treat "F1" is one token, set this flag - // to be false. - optional bool split_by_number = 23 [default = true]; - - // Use a white space to split sentence pieces. - // When `split_by_whitespace` is false, we may have the piece containing - // a white space in the middle. e.g., "in_the". - optional bool split_by_whitespace = 22 [default = true]; - - // Adds whitespace symbol (_) as a suffix instead of prefix. e.g., _hello => - // hello_. When `treat_whitespace_as_suffix` is true, - // NormalizerSpec::add_dummy_prefix will add the dummy whitespace to the end - // of sentence. - optional bool treat_whitespace_as_suffix = 24 [default = false]; - - // Allows pieces that only contain whitespaces instead of appearing only as - // prefix or suffix of other pieces. - optional bool allow_whitespace_only_pieces = 26 [default = false]; - - // Split all digits (0-9) into separate pieces. - optional bool split_digits = 25 [default = false]; - - // Defines the pre-tokenization delimiter. - // When specified, no pieces crossing this delimiter is not included - // in the vocab. Then the delimiter string is virtually ignored - // during the training. This field can allows constraints on the vocabulary - // selection. Note that this field is available on unigram mode. - optional string pretokenization_delimiter = 53 [ default = ""]; - - /////////////////////////////////////////////////////////////////// - // Vocabulary management - // - // Defines control symbols used as an indicator to - // change the behavior of the decoder. and are pre-defined. - // We can use this field to encode various meta information, - // including language indicator in multilingual model. - // These symbols are not visible to users, but visible to - // the decoder. Note that when the input sentence contains control symbols, - // they are not treated as one token, but segmented into normal pieces. - // Control symbols must be inserted independently from the segmentation. - repeated string control_symbols = 30; - - // Defines user defined symbols. - // These symbols are added with extremely high score - // so they are always treated as one unique symbol in any context. - // Typical usage of user_defined_symbols is placeholder for named entities. - repeated string user_defined_symbols = 31; - - // Defines required characters. Each UTF8 character in this string is included - // in the character set regardless of character_coverage value. Unlike - // user_defined_symbols, these characters have scores based on the frequency - // on input sentences, and the model can form subwords using characters - // in this field. - optional string required_chars = 36; - - // Decomposes unknown pieces into UTF-8 bytes. - optional bool byte_fallback = 35 [default = false]; - - // When creating the vocabulary file, defines whether or not to additionally - // output the score for each piece. - optional bool vocabulary_output_piece_score = 32 [default = true]; - - // `vocab_size` is treated as hard limit. Crash if - // the model can not produce the vocab of size `vocab_size`, - // When `hard_vocab_limit` is false, vocab_size is treated - // as soft limit. Note that when model_type=char, - // always assumes hard_vocab_limit = false. - optional bool hard_vocab_limit = 33 [default = true]; - - // use all symbols for vocab extraction. This flag is valid - // if model type is either CHAR or WORD - optional bool use_all_vocab = 34 [default = false]; - - /////////////////////////////////////////////////////////////////// - // Reserved special meta tokens. - // * -1 is not used. - // * unk_id must not be -1. - // Id must start with 0 and be contiguous. - optional int32 unk_id = 40 [default = 0]; // - optional int32 bos_id = 41 [default = 1]; // - optional int32 eos_id = 42 [default = 2]; // - optional int32 pad_id = 43 [default = -1]; // (padding) - optional string unk_piece = 45 [default = ""]; - optional string bos_piece = 46 [default = ""]; - optional string eos_piece = 47 [default = ""]; - optional string pad_piece = 48 [default = ""]; - - // Encodes into U+2047 (DOUBLE QUESTION MARK), - // since this character can be useful both for user and - // developer. We can easily figure out that is emitted. - optional string unk_surface = 44 [default = " \xE2\x81\x87 "]; - - // Increase bit depth to allow unigram model training on large - // (>10M sentences) corpora. A Side-effect of enabling this flag - // is increased memory usage. - optional bool train_extremely_large_corpus = 49 [default = false]; - - // Path to a seed sentencepieces file, with one tab-separated - // seed sentencepiece frequency per line. - optional string seed_sentencepieces_file = 54 [default = ""]; - - // Customized extensions: the range of field numbers - // are open to third-party extensions. - extensions 200 to max; -} - -// NormalizerSpec encodes a various parameters for string normalizaiton -message NormalizerSpec { - // name of normalization rule. - optional string name = 1; - - // Pre-compiled normalization rule created by - // Builder::GetPrecompiledCharsMap() or Builder::CompileCharsMap() method. - // Usually this field is set by Builder::GetNormalizerSpec() method. - optional bytes precompiled_charsmap = 2; - - // Adds dummy whitespace at the beginning of text in order to - // treat "world" in "world" and "hello world" in the same way. - optional bool add_dummy_prefix = 3 [default = true]; - - // Removes leading, trailing, and duplicate internal whitespace. - optional bool remove_extra_whitespaces = 4 [default = true]; - - // Replaces whitespace with meta symbol. - // This field must be true to train sentence piece model. - optional bool escape_whitespaces = 5 [default = true]; - - // Custom normalization rule file in TSV format. - // https://github.com/google/sentencepiece/blob/master/doc/normalization.md - // This field is only used in SentencePieceTrainer::Train() method, which - // compiles the rule into the binary rule stored in `precompiled_charsmap`. - optional string normalization_rule_tsv = 6; - - // Customized extensions: the range of field numbers - // are open to third-party extensions. - extensions 200 to max; -} - -// Proto to store samples for self-testing. -message SelfTestData { - message Sample { - optional string input = 1; - optional string expected = 2; - } - repeated Sample samples = 1; - - // Customized extensions: the range of field numbers - // are open to third-party extensions. - extensions 200 to max; -} - -// ModelProto stores model parameters. -// SentencePieceProcessor is supposed to be self-contained. -// All settings/parameters which may change the behavior must be encoded -// in ModelProto. -message ModelProto { - message SentencePiece { - enum Type { - NORMAL = 1; // normal symbol - UNKNOWN = 2; // unknown symbol. only for now. - CONTROL = 3; // control symbols. , , <2ja> etc. - USER_DEFINED = 4; // user defined symbols. - // Typical usage of USER_DEFINED symbol - // is placeholder. - BYTE = 6; // byte symbols. Used when `byte_fallback` is true. - UNUSED = 5; // this piece is not used. - } - optional string piece = 1; // piece must not be empty. - optional float score = 2; - optional Type type = 3 [default = NORMAL]; - - // Customized extensions: the range of field numbers - // are open to third-party extensions. - extensions 200 to max; - } - - // Sentence pieces with scores. - repeated SentencePiece pieces = 1; - - // Spec used to generate this model file. - optional TrainerSpec trainer_spec = 2; - - // Spec for text normalization. - optional NormalizerSpec normalizer_spec = 3; - - // Stores sample input and its expected segmentation to verify the model. - optional SelfTestData self_test_data = 4; - - // Spec for text de-normalization. - optional NormalizerSpec denormalizer_spec = 5; - - // Customized extensions: the range of field numbers - // are open to third-party extensions. - extensions 200 to max; -} diff --git a/tokenizer/sentencepiece_test.go b/tokenizer/sentencepiece_test.go deleted file mode 100644 index 500b10e30..000000000 --- a/tokenizer/sentencepiece_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package tokenizer - -import ( - "log/slog" - "os" - "path/filepath" - "slices" - "testing" - - "google.golang.org/protobuf/proto" - - "github.com/ollama/ollama/tokenizer/sentencepiece" -) - -func loadSentencePieceVocab(t *testing.T) SentencePiece { - t.Helper() - - bts, err := os.ReadFile(filepath.FromSlash("testdata/gemma2/tokenizer.model")) - if err != nil { - t.Fatal(err) - } - - var spm sentencepiece.ModelProto - if err := proto.Unmarshal(bts, &spm); err != nil { - t.Fatal(err) - } - - var v Vocabulary - - for _, piece := range spm.GetPieces() { - v.Values = append(v.Values, piece.GetPiece()) - v.Scores = append(v.Scores, piece.GetScore()) - switch t := piece.GetType(); t { - case sentencepiece.ModelProto_SentencePiece_UNKNOWN, - sentencepiece.ModelProto_SentencePiece_CONTROL, - sentencepiece.ModelProto_SentencePiece_UNUSED, - sentencepiece.ModelProto_SentencePiece_BYTE: - v.Types = append(v.Types, int32(t)) - default: - tt := int32(sentencepiece.ModelProto_SentencePiece_NORMAL) - // todo parse the special tokens file - // - this will roundtrip correctly but the and - // tokens aren't processed - v.Types = append(v.Types, tt) - } - } - - return NewSentencePiece(&v) -} - -func TestSentencePieceEncode(t *testing.T) { - logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug})) - slog.SetDefault(logger) - - tokenizer := loadSentencePieceVocab(t) - - t.Run("basic roundtrip", func(t *testing.T) { - t.Parallel() - - cases := []string{ - "hello", - "hello ", - "hello ", - " hello", - " hello ", - " hello ", - "hello world", - "请考试我的软件!12345", - "你好", - "Hello 你好 world!", - "Special characters: !@#$%^&*()_+-=[]{}|;':\",./<>?", - "Multilingual: 你好 こんにちは Привет Hola مرحبا", - "Numbers and symbols: 123456789 +- */", - "Special tokens: text ", - "Code snippets: func main() { fmt.Println(\"Hello World\") }", - "Long text: " + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + - "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + - "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.", - } - - for _, want := range cases { - ids, err := tokenizer.Encode(want, true) - if err != nil { - t.Fatal(err) - } - - if got, err := tokenizer.Decode(ids); err != nil { - t.Fatal(err) - } else if got != want { - t.Errorf("got %q, want %q [%#v]", got, want, ids) - } - } - }) - - t.Run("special tokens", func(t *testing.T) { - type candidate struct { - token string - ids []int32 - } - - cases := []candidate{ - {"", []int32{2}}, - {"", []int32{1}}, - } - - for _, want := range cases { - ids, err := tokenizer.Encode(want.token, true) - if err != nil { - t.Fatal(err) - } - if !slices.Equal(ids, want.ids) { - t.Errorf("got %#v, want %#v", ids, want.ids) - } - } - }) -} - -func TestSentencePieceDecodeByteTokens(t *testing.T) { - vocab := &Vocabulary{ - Values: []string{ - "normal", - "<0xEA>", - "<0x41>", - "<0xC3>", - "<0xA3>", - }, - Types: []int32{ - TOKEN_TYPE_NORMAL, - TOKEN_TYPE_BYTE, - TOKEN_TYPE_BYTE, - TOKEN_TYPE_BYTE, - TOKEN_TYPE_BYTE, - }, - Scores: []float32{0, 0, 0, 0, 0}, - } - - spm := NewSentencePiece(vocab) - - tests := []struct { - name string - ids []int32 - expected string - }{ - { - name: "single byte token", - ids: []int32{1}, - expected: "\xea", - }, - { - name: "ASCII byte token", - ids: []int32{2}, - expected: "A", - }, - { - name: "multiple byte tokens forming UTF-8 character", - ids: []int32{3, 4}, - expected: "ã", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, err := spm.Decode(tt.ids) - if err != nil { - t.Errorf("failed to decode token IDs %v: %v", tt.ids, err) - } - if result != tt.expected { - t.Errorf("got %q, want %q", result, tt.expected) - } - }) - } -} diff --git a/tokenizer/testdata/gemma2/tokenizer.model b/tokenizer/testdata/gemma2/tokenizer.model deleted file mode 100644 index 14a242262f212c6e4e55913220f22c7d768e0524..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4241003 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