pulpie-webgpu / src /lib /chunker.js
sizzlebop's picture
feat: initial release of Pulpie WebGPU Hugging Face Space
99c7c9f
Raw History Blame Contribute Delete
3.06 kB
/**
* Pulpie WebUI - Block Chunker
*
* Implements token sequence packing matching Pulpie's exact chunker algorithm.
* Groups blocks into sequences bounded by max_tokens (default 8,192).
*
* Sequence structure:
* [BOS] block_0 [<|sep|>] block_1 [<|sep|>] ... block_N [<|sep|>] [EOS]
*/
import { TOKEN_IDS } from './tokenizer.js';
export const DEFAULT_MAX_TOKENS = 1024;
/**
* @typedef {Object} Chunk
* @property {number[]} chunkIds - Array of token IDs including BOS, EOS, and SEP tokens
* @property {number[]} blockIndices - Indices of the blocks included in this chunk
*/
/**
* Pack block tokens into chunks conforming to EuroBERT's max_tokens limit.
*
* @param {number[][]} blockTokenIds - Array of token ID lists for each block
* @param {Object} [options]
* @param {number} [options.maxTokens=8192] - Maximum sequence length per forward pass
* @param {number} [options.sepTokenId] - Token ID for <|sep|> (default 128256)
* @param {number} [options.bosTokenId] - Token ID for <|begin_of_text|> (default 128000)
* @param {number} [options.eosTokenId] - Token ID for <|end_of_text|> (default 128001)
* @returns {Chunk[]}
*/
export function packChunks(
blockTokenIds,
{
maxTokens = DEFAULT_MAX_TOKENS,
sepTokenId = TOKEN_IDS.SEP,
bosTokenId = TOKEN_IDS.BOS,
eosTokenId = TOKEN_IDS.EOS,
} = {}
) {
if (!blockTokenIds || blockTokenIds.length === 0) {
return [];
}
const chunks = [];
let currentTokens = [];
let currentBlockIndices = [];
const overhead = (bosTokenId !== null ? 1 : 0) + (eosTokenId !== null ? 1 : 0);
const budget = maxTokens - overhead;
let currentLen = 0;
for (let blockIdx = 0; blockIdx < blockTokenIds.length; blockIdx++) {
const blockToks = blockTokenIds[blockIdx];
const blockCost = blockToks.length + 1; // +1 for SEP token
if (currentLen + blockCost > budget) {
if (currentTokens.length > 0) {
const chunkIds = [];
if (bosTokenId !== null) chunkIds.push(bosTokenId);
chunkIds.push(...currentTokens);
if (eosTokenId !== null) chunkIds.push(eosTokenId);
chunks.push({
chunkIds,
blockIndices: [...currentBlockIndices],
});
}
currentTokens = [];
currentBlockIndices = [];
currentLen = 0;
// Single block exceeds total budget on its own
if (blockCost > budget) {
const truncated = blockToks.slice(0, budget - 1);
currentTokens.push(...truncated, sepTokenId);
currentBlockIndices.push(blockIdx);
currentLen = truncated.length + 1;
continue;
}
}
currentTokens.push(...blockToks, sepTokenId);
currentBlockIndices.push(blockIdx);
currentLen += blockCost;
}
if (currentTokens.length > 0) {
const chunkIds = [];
if (bosTokenId !== null) chunkIds.push(bosTokenId);
chunkIds.push(...currentTokens);
if (eosTokenId !== null) chunkIds.push(eosTokenId);
chunks.push({
chunkIds,
blockIndices: [...currentBlockIndices],
});
}
return chunks;
}