Spaces:
Running
Running
Deploy Ekko v1 Tiny + PnC browser demo
Browse files- app.js +1 -1
- pnc-worker.js +21 -10
app.js
CHANGED
|
@@ -92,7 +92,7 @@ const state = {
|
|
| 92 |
};
|
| 93 |
|
| 94 |
const worker = new Worker(signedAsset("worker.js?v=20260927-2"));
|
| 95 |
-
const pncWorker = new Worker(signedAsset("pnc-worker.js?v=
|
| 96 |
|
| 97 |
function setStatus(text, detail = "", kind = "loading") {
|
| 98 |
elements.statusText.textContent = text;
|
|
|
|
| 92 |
};
|
| 93 |
|
| 94 |
const worker = new Worker(signedAsset("worker.js?v=20260927-2"));
|
| 95 |
+
const pncWorker = new Worker(signedAsset("pnc-worker.js?v=20261001-1"));
|
| 96 |
|
| 97 |
function setStatus(text, detail = "", kind = "loading") {
|
| 98 |
elements.statusText.textContent = text;
|
pnc-worker.js
CHANGED
|
@@ -21,8 +21,8 @@ let initialization = null;
|
|
| 21 |
let session = null;
|
| 22 |
let vocabulary = null;
|
| 23 |
let labels = null;
|
| 24 |
-
let maximumLength =
|
| 25 |
-
const INFERENCE_WINDOW_TOKENS =
|
| 26 |
let pendingRequest = null;
|
| 27 |
let processing = false;
|
| 28 |
|
|
@@ -101,7 +101,7 @@ async function initialize(configUrl) {
|
|
| 101 |
vocabulary = tokenizer.model.vocab;
|
| 102 |
labels = modelConfig.id2label;
|
| 103 |
// Shorter context restores sentence stops in long, continuous dictation.
|
| 104 |
-
maximumLength = Math.min(config.maxLength ||
|
| 105 |
session = await ort.InferenceSession.create(downloaded.get("pnc.int8.onnx"), {
|
| 106 |
executionProviders: ["wasm"],
|
| 107 |
graphOptimizationLevel: "all",
|
|
@@ -172,16 +172,26 @@ function encodeWords(words) {
|
|
| 172 |
return {identifiers, wordIds};
|
| 173 |
}
|
| 174 |
|
| 175 |
-
function
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
}
|
| 178 |
|
| 179 |
-
function maximumWindowEnd(words, start) {
|
|
|
|
| 180 |
let low = start + 1;
|
| 181 |
let high = Math.min(words.length, start + maximumLength);
|
| 182 |
while (low < high) {
|
| 183 |
const middle = Math.floor((low + high + 1) / 2);
|
| 184 |
-
if (
|
| 185 |
low = middle;
|
| 186 |
} else {
|
| 187 |
high = middle - 1;
|
|
@@ -253,15 +263,16 @@ async function predictWindow(words) {
|
|
| 253 |
|
| 254 |
async function punctuateWords(words) {
|
| 255 |
if (!words.length) return [];
|
|
|
|
| 256 |
const output = [];
|
| 257 |
let nextWord = 0;
|
| 258 |
-
const overlapWords =
|
| 259 |
while (nextWord < words.length) {
|
| 260 |
let contextStart = Math.max(0, nextWord - overlapWords);
|
| 261 |
-
let windowEnd = maximumWindowEnd(words, contextStart);
|
| 262 |
if (windowEnd <= nextWord) {
|
| 263 |
contextStart = nextWord;
|
| 264 |
-
windowEnd = maximumWindowEnd(words, contextStart);
|
| 265 |
}
|
| 266 |
const rendered = await predictWindow(words.slice(contextStart, windowEnd));
|
| 267 |
const emitEnd = windowEnd === words.length
|
|
|
|
| 21 |
let session = null;
|
| 22 |
let vocabulary = null;
|
| 23 |
let labels = null;
|
| 24 |
+
let maximumLength = 128;
|
| 25 |
+
const INFERENCE_WINDOW_TOKENS = 128;
|
| 26 |
let pendingRequest = null;
|
| 27 |
let processing = false;
|
| 28 |
|
|
|
|
| 101 |
vocabulary = tokenizer.model.vocab;
|
| 102 |
labels = modelConfig.id2label;
|
| 103 |
// Shorter context restores sentence stops in long, continuous dictation.
|
| 104 |
+
maximumLength = Math.min(config.maxLength || 128, INFERENCE_WINDOW_TOKENS);
|
| 105 |
session = await ort.InferenceSession.create(downloaded.get("pnc.int8.onnx"), {
|
| 106 |
executionProviders: ["wasm"],
|
| 107 |
graphOptimizationLevel: "all",
|
|
|
|
| 172 |
return {identifiers, wordIds};
|
| 173 |
}
|
| 174 |
|
| 175 |
+
function windowTokenIndex(words) {
|
| 176 |
+
const {wordIds} = encodeWords(words);
|
| 177 |
+
const counts = new Array(words.length).fill(0);
|
| 178 |
+
let specialTokens = 0;
|
| 179 |
+
for (const wordId of wordIds) {
|
| 180 |
+
if (wordId === null) specialTokens += 1;
|
| 181 |
+
else counts[wordId] += 1;
|
| 182 |
+
}
|
| 183 |
+
const prefix = [0];
|
| 184 |
+
for (const count of counts) prefix.push(prefix.at(-1) + count);
|
| 185 |
+
return {prefix, specialTokens};
|
| 186 |
}
|
| 187 |
|
| 188 |
+
function maximumWindowEnd(words, start, prefix, specialTokens) {
|
| 189 |
+
const tokenBudget = prefix[start] + maximumLength - specialTokens;
|
| 190 |
let low = start + 1;
|
| 191 |
let high = Math.min(words.length, start + maximumLength);
|
| 192 |
while (low < high) {
|
| 193 |
const middle = Math.floor((low + high + 1) / 2);
|
| 194 |
+
if (prefix[middle] <= tokenBudget) {
|
| 195 |
low = middle;
|
| 196 |
} else {
|
| 197 |
high = middle - 1;
|
|
|
|
| 263 |
|
| 264 |
async function punctuateWords(words) {
|
| 265 |
if (!words.length) return [];
|
| 266 |
+
const {prefix, specialTokens} = windowTokenIndex(words);
|
| 267 |
const output = [];
|
| 268 |
let nextWord = 0;
|
| 269 |
+
const overlapWords = 12;
|
| 270 |
while (nextWord < words.length) {
|
| 271 |
let contextStart = Math.max(0, nextWord - overlapWords);
|
| 272 |
+
let windowEnd = maximumWindowEnd(words, contextStart, prefix, specialTokens);
|
| 273 |
if (windowEnd <= nextWord) {
|
| 274 |
contextStart = nextWord;
|
| 275 |
+
windowEnd = maximumWindowEnd(words, contextStart, prefix, specialTokens);
|
| 276 |
}
|
| 277 |
const rendered = await predictWindow(words.slice(contextStart, windowEnd));
|
| 278 |
const emitEnd = windowEnd === words.length
|