Skip to content

Commit 8d615c5

Browse files
authored
Cached generation improvements (+ past_key_values via pipeline function) (#1638)
* add `multi-turn with past_key_values matches without` test * Add dynamic cache sequence length retrieval test * sequence length = 0 if empty * Create DynamicCache update method * Remove private PreTrainedModel functions & use new methods * Align with python transformers: load_audio instead of read_audio * Add past_key_values to possible generation config parameters * Add test case for past_key_values type * Type improvements * Far better error messages! * Improve typescript tests ensure good typescript errors * Apply same typescript improvements to other pipelines * new typescript tests * Update preprocess.js * improve error message
1 parent 60379e2 commit 8d615c5

33 files changed

Lines changed: 761 additions & 410 deletions

packages/transformers/docs/plugins/preprocess.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,39 @@ function extractBalancedBraces(text, start) {
1414
return depth === 0 ? { content: text.slice(start + 1, i - 1), endIndex: i } : null;
1515
}
1616

17+
function stripLeadingGenericParams(expr) {
18+
if (expr[0] !== "<") return expr;
19+
let depth = 1, i = 1;
20+
while (i < expr.length && depth > 0) {
21+
if (expr[i] === "<") depth++;
22+
else if (expr[i] === ">") depth--;
23+
i++;
24+
}
25+
return depth === 0 ? expr.slice(i) : expr;
26+
}
27+
1728
function transformType(expr) {
1829
let result = expr,
1930
prev = "";
2031
while (result !== prev) {
2132
prev = result;
33+
result = stripLeadingGenericParams(result); // <T extends X>(...) -> (...)
2234
result = result
2335
.replace(/import\([^)]+\)((?:\.\w+)*)/g, (_, p) => p?.slice(1) || "any") // import() -> Type
2436
.replace(/\w+\[['"][^\]]+['"]\]\s+extends\s+[^}]+/g, "any") // X['p'] extends ... -> any
2537
.replace(/(\w+)(?:<[^>]+>)?\[[^\]]+\]/g, "$1") // Type[x] or Type<T>[x] -> Type
2638
.replace(/keyof\s+typeof\s+\w+/g, "string") // keyof typeof X -> string
2739
.replace(/typeof\s+\w+/g, "Object") // typeof X -> Object
40+
.replace(/\binfer\s+\w+/g, "any") // infer K -> any
41+
.replace(/\(\s*\w[\w<>, ]*\s+extends\s+\w+\s*\?[^)]*\)/g, "any") // (X extends Y ? A : B) -> any
42+
.replace(/(?<!\w)\(\s*(\w+)\s*\)/g, "$1") // (any) -> any (unwrap parens around simple types, not after words like "function")
2843
.replace(/(\w+)\?\s*:/g, "$1:") // x?: T -> x: T
2944
.replace(/;\s*([}\n])/g, " $1")
3045
.replace(/;\s+/g, ", ") // semicolons -> commas
3146
.replace(/\{\s*\[\w+\s+in\s+[^\]]+\][^}]*\}/g, "Object") // mapped types -> Object
3247
.replace(/new\s*\([^)]*\)\s*=>\s*\{[^}]*\}/g, "Function") // new () => {...} -> Function
3348
.replace(/new\s*\([^)]*\)\s*=>\s*\w+/g, "Function") // new () => T -> Function
34-
.replace(/\([^()]*\)\s*=>\s*\w[\w<>|[\]]*/g, "Function") // () => T -> Function
49+
.replace(/\([^()]*\)\s*=>\s*\w[\w<>|[\], ]*/g, "Function") // () => T -> Function
3550
.replace(/\{[^{}]*\}\[\]/g, "Array") // {x:T}[] -> Array
3651
.replace(/(\w+)<[^>]+>\[\]/g, "Array.<$1>") // T<U>[] -> Array.<T>
3752
.replace(/\([^()]+\)\[\]/g, "Array") // (A|B)[] -> Array

packages/transformers/src/cache_utils.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ class _DynamicCache {
2929
get_seq_length() {
3030
/** @type {Record<string, Tensor>} */
3131
const self = /** @type {any} */ (this);
32+
33+
if (Object.keys(self).length === 0) {
34+
return 0;
35+
}
36+
3237
for (const name in self) {
3338
if (name.startsWith('past_key_values.')) {
3439
return self[name].dims.at(-2);
@@ -37,6 +42,21 @@ class _DynamicCache {
3742
throw new Error('Unable to determine sequence length from the cache.');
3843
}
3944

45+
/**
46+
* Update the cache in-place with new entries, disposing replaced GPU tensors.
47+
* @param {Record<string, Tensor>} newEntries The new name → Tensor mappings.
48+
*/
49+
update(newEntries) {
50+
for (const key in newEntries) {
51+
const oldValue = this[key];
52+
const newValue = newEntries[key];
53+
if (oldValue && oldValue !== newValue && oldValue.location === 'gpu-buffer') {
54+
oldValue.dispose();
55+
}
56+
this[key] = newValue;
57+
}
58+
}
59+
4060
/**
4161
* Dispose all contained tensors whose data resides on the GPU.
4262
* Returns a promise that resolves when all disposals are complete.
@@ -54,7 +74,7 @@ class _DynamicCache {
5474
}
5575

5676
/**
57-
* @typedef {_DynamicCache & Record<string, Tensor>} DynamicCache
77+
* @typedef {Record<string, Tensor> & _DynamicCache} DynamicCache
5878
*/
5979

6080
export const DynamicCache = /** @type {new (entries?: Record<string, Tensor>) => DynamicCache} */ (

packages/transformers/src/generation/parameters.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@
3030
* through `streamer.put(token_ids)` and the streamer is responsible for any further processing.
3131
* @property {number[]} [decoder_input_ids=null] (`number[]`, *optional*):
3232
* If the model is an encoder-decoder model, this argument is used to pass the `decoder_input_ids`.
33+
* @property {import('../cache_utils.js').DynamicCache | null} [past_key_values=null] (`DynamicCache`, *optional*):
34+
* A cache object that stores previously computed key/value states. When provided, the model will
35+
* use these cached states to avoid recomputing them, significantly speeding up sequential generation.
3336
*/
3437

3538
/**
36-
* @typedef {GenerationFunctionParametersBase & Partial<import('./configuration_utils.js').GenerationConfig> & Record<string, any>} GenerationFunctionParameters
39+
* @typedef {GenerationFunctionParametersBase & Partial<import('./configuration_utils.js').GenerationConfig> & {[key: string]: unknown}} GenerationFunctionParameters
3740
*/
3841

3942
export {}; // Ensure this file is treated as a module

packages/transformers/src/models/chatterbox/modeling_chatterbox.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class ChatterboxModel extends ChatterboxPreTrainedModel {
168168
);
169169

170170
const new_tokens = sequences.slice(null, [
171-
params.input_ids.dims[1], // Exclude start of speech token
171+
/** @type {Tensor} */ (params.input_ids).dims[1], // Exclude start of speech token
172172
-1, // Exclude end of speech token
173173
]);
174174

0 commit comments

Comments
 (0)