qikp commited on
Commit
87c1899
·
1 Parent(s): f57b01f

Replace localStorage tokenizer cache with IndexedDB

Browse files

IndexedDB can store large tokenizer JSON blobs without the ~5MB
localStorage cap. Adds async helpers for open/get/set/clear.

Files changed (1) hide show
  1. script.js +81 -24
script.js CHANGED
@@ -1,57 +1,114 @@
1
  import { Tokenizer } from "https://nobsdelivr.private.coffee/npm/@huggingface/tokenizers@0.1.3";
2
 
3
- if (localStorage.getItem("tokenizerCache")) {
4
- var tokenizerCache = JSON.parse(localStorage.tokenizerCache);
5
- } else {
6
- var tokenizerCache = {};
7
- localStorage.tokenizerCache = "{}";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  }
9
- var tokenizerJSON;
10
 
11
  let tokenizeButton = document.querySelector("#tokenize");
12
  tokenizeButton.addEventListener("click", async () => {
13
  tokenizeButton.disabled = true;
14
  let modelId = document.querySelector("#model").value;
15
-
16
- if (modelId in tokenizerCache) {
17
- tokenizerJSON = tokenizerCache[modelId];
 
 
 
18
  }
19
- else {
 
20
  let tokenizerJSONSocket = await fetch(`https://huggingface.co/${modelId}/resolve/main/tokenizer.json`);
21
  tokenizerJSON = await tokenizerJSONSocket.json();
22
- tokenizerCache[modelId] = tokenizerJSON;
23
- localStorage.tokenizerCache = JSON.stringify(tokenizerCache);
 
 
 
24
  }
25
-
26
  let tokenizer = new Tokenizer(tokenizerJSON, {});
27
-
28
  let text = document.querySelector("#text").value;
29
  let tokenized = tokenizer.encode(text);
30
-
31
  document.querySelector("#totalLength").textContent = `Total length: ${tokenized.ids.length}`;
32
-
33
  let output = document.querySelector("#output");
34
  output.contentDocument.body.innerHTML = "";
35
-
36
  let colors = ["#ff00007f", "#00ff007f", "#ffff007f", "#0000ff7f"].sort(() => Math.random() - 0.5);
37
-
38
  tokenized.ids.forEach((token, index) => {
39
  let content = document.createElement("span");
40
-
41
  content.textContent = tokenizer.decode([token]);
42
  content.style.backgroundColor = colors[index % colors.length];
43
  content.style.fontFamily = "monospace";
44
  content.style.whiteSpace = "pre-wrap";
45
-
46
  content.title = token.toString();
47
-
48
  output.contentDocument.body.appendChild(content);
49
  });
50
-
51
  tokenizeButton.disabled = false;
52
  });
53
 
54
  document.querySelector("#clear-cache").addEventListener("click", () => {
55
- tokenizerCache = {};
56
- localStorage.tokenizerCache = "{}";
57
  });
 
1
  import { Tokenizer } from "https://nobsdelivr.private.coffee/npm/@huggingface/tokenizers@0.1.3";
2
 
3
+ // IndexedDB helper for caching tokenizer JSON.
4
+ // IndexedDB can store large blobs, unlike localStorage's ~5MB cap.
5
+ const dbName = "tokenizerCacheDB";
6
+ const storeName = "tokenizerCache";
7
+
8
+ function openDB() {
9
+ return new Promise((resolve, reject) => {
10
+ const request = indexedDB.open(dbName, 1);
11
+
12
+ request.onupgradeneeded = () => {
13
+ request.result.createObjectStore(storeName);
14
+ };
15
+
16
+ request.onsuccess = () => resolve(request.result);
17
+ request.onerror = () => reject(request.error);
18
+ });
19
+ }
20
+
21
+ let dbPromise;
22
+
23
+ async function getCache(modelId) {
24
+ if (!dbPromise) dbPromise = openDB();
25
+ const db = await dbPromise;
26
+
27
+ return new Promise((resolve, reject) => {
28
+ const tx = db.transaction(storeName, "readonly");
29
+ const get = tx.objectStore(storeName).get(modelId);
30
+
31
+ get.onsuccess = () => resolve(get.result);
32
+ get.onerror = () => reject(get.error);
33
+ });
34
+ }
35
+
36
+ async function setCache(modelId, tokenizerJSON) {
37
+ if (!dbPromise) dbPromise = openDB();
38
+ const db = await dbPromise;
39
+
40
+ return new Promise((resolve, reject) => {
41
+ const tx = db.transaction(storeName, "readwrite");
42
+ tx.objectStore(storeName).put(tokenizerJSON, modelId);
43
+
44
+ tx.oncomplete = () => resolve();
45
+ tx.onerror = () => reject(tx.error);
46
+ });
47
+ }
48
+
49
+ async function clearCache() {
50
+ if (!dbPromise) dbPromise = openDB();
51
+ const db = await dbPromise;
52
+
53
+ return new Promise((resolve, reject) => {
54
+ const tx = db.transaction(storeName, "readwrite");
55
+ tx.objectStore(storeName).clear();
56
+
57
+ tx.oncomplete = () => resolve();
58
+ tx.onerror = () => reject(tx.error);
59
+ });
60
  }
 
61
 
62
  let tokenizeButton = document.querySelector("#tokenize");
63
  tokenizeButton.addEventListener("click", async () => {
64
  tokenizeButton.disabled = true;
65
  let modelId = document.querySelector("#model").value;
66
+
67
+ let tokenizerJSON;
68
+ try {
69
+ tokenizerJSON = await getCache(modelId);
70
+ } catch (err) {
71
+ console.error("Failed to read from IndexedDB:", err);
72
  }
73
+
74
+ if (tokenizerJSON === undefined) {
75
  let tokenizerJSONSocket = await fetch(`https://huggingface.co/${modelId}/resolve/main/tokenizer.json`);
76
  tokenizerJSON = await tokenizerJSONSocket.json();
77
+ try {
78
+ await setCache(modelId, tokenizerJSON);
79
+ } catch (err) {
80
+ console.error("Failed to write to IndexedDB:", err);
81
+ }
82
  }
83
+
84
  let tokenizer = new Tokenizer(tokenizerJSON, {});
85
+
86
  let text = document.querySelector("#text").value;
87
  let tokenized = tokenizer.encode(text);
88
+
89
  document.querySelector("#totalLength").textContent = `Total length: ${tokenized.ids.length}`;
90
+
91
  let output = document.querySelector("#output");
92
  output.contentDocument.body.innerHTML = "";
93
+
94
  let colors = ["#ff00007f", "#00ff007f", "#ffff007f", "#0000ff7f"].sort(() => Math.random() - 0.5);
95
+
96
  tokenized.ids.forEach((token, index) => {
97
  let content = document.createElement("span");
98
+
99
  content.textContent = tokenizer.decode([token]);
100
  content.style.backgroundColor = colors[index % colors.length];
101
  content.style.fontFamily = "monospace";
102
  content.style.whiteSpace = "pre-wrap";
103
+
104
  content.title = token.toString();
105
+
106
  output.contentDocument.body.appendChild(content);
107
  });
108
+
109
  tokenizeButton.disabled = false;
110
  });
111
 
112
  document.querySelector("#clear-cache").addEventListener("click", () => {
113
+ clearCache().catch((err) => console.error("Failed to clear cache:", err));
 
114
  });