gigatoken 0.1.0-aarch64-linux → 0.2.0-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gigatoken
4
+ # The tiktoken encodings gigatoken vendors ranks for, and the pieces a
5
+ # .tiktoken file doesn't carry: its pretokenizer scheme and special-token
6
+ # table (see Tokenizer.from_tiktoken and lib/gigatoken/encodings/
7
+ # PROVENANCE.md, the source of truth this is transcribed from).
8
+ module Encodings
9
+ DATA_DIR = File.expand_path("encodings", __dir__)
10
+ private_constant :DATA_DIR
11
+
12
+ # The non-contiguous head of openai_public.py's o200k_harmony()
13
+ # special-token table, transcribed verbatim (see PROVENANCE.md): its ten
14
+ # named control tokens, plus the six reserved slots sitting in the gaps
15
+ # between them (200000, 200001, 200004, 200009, 200010, 200011). The
16
+ # reserved range only goes contiguous at 200013.
17
+ HARMONY_HEAD_TOKENS = {
18
+ "<|startoftext|>" => 199998,
19
+ "<|endoftext|>" => 199999,
20
+ "<|reserved_200000|>" => 200000,
21
+ "<|reserved_200001|>" => 200001,
22
+ "<|return|>" => 200002,
23
+ "<|constrain|>" => 200003,
24
+ "<|reserved_200004|>" => 200004,
25
+ "<|channel|>" => 200005,
26
+ "<|start|>" => 200006,
27
+ "<|end|>" => 200007,
28
+ "<|message|>" => 200008,
29
+ "<|reserved_200009|>" => 200009,
30
+ "<|reserved_200010|>" => 200010,
31
+ "<|reserved_200011|>" => 200011,
32
+ "<|call|>" => 200012,
33
+ "<|endofprompt|>" => 200018
34
+ }.freeze
35
+ private_constant :HARMONY_HEAD_TOKENS
36
+
37
+ # The contiguous tail of that reserved range: 200013..201087. With the
38
+ # head above, 1091 entries total — 10 named, 1081 reserved (see
39
+ # PROVENANCE.md).
40
+ HARMONY_RESERVED_TAIL = (200013..201087).to_h { |id| ["<|reserved_#{id}|>", id] }.freeze
41
+ private_constant :HARMONY_RESERVED_TAIL
42
+
43
+ REGISTRY = {
44
+ "r50k_base" => {
45
+ rank_file: File.join(DATA_DIR, "r50k_base.tiktoken"),
46
+ pretokenizer: "gpt2",
47
+ special_tokens: {"<|endoftext|>" => 50256}
48
+ },
49
+ "cl100k_base" => {
50
+ rank_file: File.join(DATA_DIR, "cl100k_base.tiktoken"),
51
+ pretokenizer: "gpt4",
52
+ special_tokens: {
53
+ "<|endoftext|>" => 100257,
54
+ "<|fim_prefix|>" => 100258,
55
+ "<|fim_middle|>" => 100259,
56
+ "<|fim_suffix|>" => 100260,
57
+ "<|endofprompt|>" => 100276
58
+ }
59
+ },
60
+ "o200k_base" => {
61
+ rank_file: File.join(DATA_DIR, "o200k_base.tiktoken"),
62
+ pretokenizer: "o200k",
63
+ special_tokens: {"<|endoftext|>" => 199999, "<|endofprompt|>" => 200018}
64
+ },
65
+ "o200k_harmony" => {
66
+ rank_file: File.join(DATA_DIR, "o200k_base.tiktoken"),
67
+ pretokenizer: "o200k",
68
+ special_tokens: HARMONY_HEAD_TOKENS.merge(HARMONY_RESERVED_TAIL).freeze
69
+ }
70
+ }.freeze
71
+ private_constant :REGISTRY
72
+
73
+ # The packaged encoding names — the single source error messages naming
74
+ # what's available are built from (the same discipline
75
+ # Native.pretokenizer_names / PretokenizerType::NAMES applies to
76
+ # pretokenizer scheme names).
77
+ NAMES = REGISTRY.keys.freeze
78
+
79
+ # Encodings known by name but deliberately not packaged, keyed to the
80
+ # reason a caller asking for one by name deserves to hear.
81
+ UNPACKABLE_REASONS = {
82
+ "p50k_base" => "its ranks are not dense (50256 is left free for <|endoftext|>), " \
83
+ "and the rank loader rejects non-dense ranks",
84
+ "p50k_edit" => "it loads the same p50k_base.tiktoken ranks, which are not dense " \
85
+ "(50256 is left free for <|endoftext|>), and the rank loader rejects non-dense ranks"
86
+ }.freeze
87
+ private_constant :UNPACKABLE_REASONS
88
+
89
+ class << self
90
+ # The {rank_file:, pretokenizer:, special_tokens:} registered for a
91
+ # packaged encoding name, or nil.
92
+ def [](name)
93
+ REGISTRY[name]
94
+ end
95
+
96
+ # Why `name` can't be packaged, or nil when there's no reason on
97
+ # record (it's either packaged, or simply not one gigatoken knows of).
98
+ def unpackable_reason(name)
99
+ UNPACKABLE_REASONS[name]
100
+ end
101
+ end
102
+ end
103
+ end
@@ -7,9 +7,6 @@ module Gigatoken
7
7
  # over a native `Gigatoken::Native::BPETokenizer` or
8
8
  # `Gigatoken::Native::SentencePieceTokenizer`.
9
9
  class Tokenizer
10
- TIKTOKEN_ENDOFTEXT = "<|endoftext|>"
11
- private_constant :TIKTOKEN_ENDOFTEXT
12
-
13
10
  FILE_SOURCE_CLASSES = [Native::TextFileSource, Native::JsonlFileSource, Native::ParquetFileSource].freeze
14
11
  private_constant :FILE_SOURCE_CLASSES
15
12
 
@@ -27,10 +24,27 @@ module Gigatoken
27
24
  from_json(File.binread(path))
28
25
  end
29
26
 
30
- # Load from a .tiktoken mergeable-ranks file.
31
- def self.from_tiktoken(path)
32
- native = Native::BPETokenizer.from_tiktoken(path.to_s)
33
- new(native, special_tokens: {TIKTOKEN_ENDOFTEXT => native.vocab_size - 1})
27
+ # Load from a .tiktoken mergeable-ranks file. The file carries neither a
28
+ # pretokenization scheme nor special tokens — nothing is guessed here,
29
+ # so `pretokenizer:` is required: one of the schemes gigatoken ships
30
+ # (see Native.pretokenizer_names, e.g. "gpt2"/"r50k", "gpt4"/"cl100k",
31
+ # "o200k", "qwen2", "qwen35", "olmo3", "deepseek_v3", "nemotron", "kimi").
32
+ # `special_tokens:` maps token content to id (none by default).
33
+ def self.from_tiktoken(path, pretokenizer:, special_tokens: {})
34
+ native = Native::BPETokenizer.from_tiktoken(path.to_s, pretokenizer, special_tokens)
35
+ new(native, special_tokens: special_tokens)
36
+ end
37
+
38
+ # Load one of the tiktoken encodings gigatoken vendors ranks for, by
39
+ # name — see Gigatoken::Encodings::NAMES — entirely from the vendored
40
+ # files: no network, no writable cache.
41
+ def self.from_encoding(name)
42
+ encoding = Encodings[name]
43
+ return from_tiktoken(encoding[:rank_file], pretokenizer: encoding[:pretokenizer], special_tokens: encoding[:special_tokens]) if encoding
44
+
45
+ reason = Encodings.unpackable_reason(name)
46
+ detail = reason ? " — #{reason}" : ""
47
+ raise Error, "#{name.inspect}: not a packaged encoding#{detail} (packaged encodings: #{Encodings::NAMES.join(", ")})"
34
48
  end
35
49
 
36
50
  # Load tokenizer.json from HuggingFace Hub repo `repo_id` at `revision`
@@ -41,12 +55,29 @@ module Gigatoken
41
55
 
42
56
  # Load from any of the supported source shapes: an existing file or
43
57
  # directory path (a tokenizer.json, or a directory containing one), a
44
- # .tiktoken vocabulary file, or a HuggingFace Hub repo id like
45
- # "openai-community/gpt2".
46
- def self.load(source, revision: "main", hub: Hub.new)
58
+ # .tiktoken vocabulary file, a packaged encoding name (see
59
+ # Gigatoken::Encodings::NAMES, e.g. "cl100k_base"), or a HuggingFace Hub
60
+ # repo id like "openai-community/gpt2". A .tiktoken file carries no
61
+ # pretokenizer scheme of its own, so one must be named explicitly via
62
+ # `pretokenizer:` — nothing here is guessed. Packaged encoding names are
63
+ # checked before the Hub-repo-id shape: a bare name like "o200k_base" is
64
+ # also shaped like a legacy repo id, and must resolve locally rather
65
+ # than reach the network. Names the registry knows but doesn't package
66
+ # (see Encodings.unpackable_reason, e.g. "p50k_base") are intercepted
67
+ # here too, raising the same explanation from_encoding gives rather than
68
+ # reaching the Hub — but only those; an unrecognized bare name like
69
+ # "gpt2" still dispatches to the Hub.
70
+ def self.load(source, pretokenizer: nil, special_tokens: {}, revision: "main", hub: Hub.new)
47
71
  source = source.to_s
48
- return from_tiktoken(source) if source.end_with?(".tiktoken")
72
+ if source.end_with?(".tiktoken")
73
+ unless pretokenizer
74
+ raise Error, "#{source.inspect}: a .tiktoken file carries no pretokenizer scheme of its own — " \
75
+ "pass pretokenizer: (one of #{Native.pretokenizer_names.join(", ")})"
76
+ end
77
+ return from_tiktoken(source, pretokenizer: pretokenizer, special_tokens: special_tokens)
78
+ end
49
79
  return from_file(source) if File.exist?(source)
80
+ return from_encoding(source) if Encodings::NAMES.include?(source) || Encodings.unpackable_reason(source)
50
81
  return from_hub(source, revision: revision, hub: hub) if Hub.looks_like_repo_id?(source)
51
82
 
52
83
  raise Error, "#{source.inspect}: no such file or directory, not a .tiktoken path, and doesn't look like a HuggingFace Hub repo id"
@@ -112,6 +143,12 @@ module Gigatoken
112
143
  @native.merges
113
144
  end
114
145
 
146
+ # Cached pretoken/unit entries on this tokenizer's single-document
147
+ # encode path — see Gigatoken.max_cache_bytes.
148
+ def cache_entries
149
+ @native.cache_entries
150
+ end
151
+
115
152
  attr_reader :special_tokens
116
153
  end
117
154
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gigatoken
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/gigatoken.rb CHANGED
@@ -7,6 +7,20 @@ module Gigatoken
7
7
  # extension — never a raw Rust panic across the Ruby boundary.
8
8
  class Error < StandardError; end
9
9
 
10
+ class << self
11
+ # The process-global encode-cache budget in bytes per worker (a parallel
12
+ # batch encode may use up to workers x budget), applied to tokenizers of
13
+ # either backend constructed *afterward* — changing it never affects an
14
+ # already-built Tokenizer. nil means unbounded. Default: 512 MiB.
15
+ def max_cache_bytes
16
+ Native.get_max_cache_bytes
17
+ end
18
+
19
+ def max_cache_bytes=(bytes)
20
+ Native.set_max_cache_bytes(bytes)
21
+ end
22
+ end
23
+
10
24
  NATIVE_EXTENSIONS = %w[.bundle .so .rb].freeze
11
25
 
12
26
  # Precompiled native gems ship per-ABI subdirs (`gigatoken/4.0/...`),
@@ -26,4 +40,5 @@ require native
26
40
 
27
41
  require_relative "gigatoken/hub"
28
42
  require_relative "gigatoken/packed_result"
43
+ require_relative "gigatoken/encodings"
29
44
  require_relative "gigatoken/tokenizer"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gigatoken
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Eric Jacobs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-24 00:00:00.000000000 Z
11
+ date: 2026-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async
@@ -72,6 +72,11 @@ files:
72
72
  - lib/gigatoken/cli/bench.rb
73
73
  - lib/gigatoken/cli/support.rb
74
74
  - lib/gigatoken/cli/validate.rb
75
+ - lib/gigatoken/encodings.rb
76
+ - lib/gigatoken/encodings/PROVENANCE.md
77
+ - lib/gigatoken/encodings/cl100k_base.tiktoken
78
+ - lib/gigatoken/encodings/o200k_base.tiktoken
79
+ - lib/gigatoken/encodings/r50k_base.tiktoken
75
80
  - lib/gigatoken/hub.rb
76
81
  - lib/gigatoken/packed_result.rb
77
82
  - lib/gigatoken/tokenizer.rb
@@ -82,6 +87,7 @@ licenses:
82
87
  - MIT
83
88
  metadata:
84
89
  bug_tracker_uri: https://github.com/jetpks/gigatoken-rb/issues
90
+ changelog_uri: https://github.com/jetpks/gigatoken-rb/blob/main/CHANGELOG.md
85
91
  homepage_uri: https://github.com/jetpks/gigatoken-rb
86
92
  source_code_uri: https://github.com/jetpks/gigatoken-rb
87
93
  post_install_message: