Je voulais mieux maîtriser l’API Stream, alors j’ai expérimenté avec la manipulation de chaînes comme exemple. Cela implémente un TransformStream qui divise un long texte en tableaux d’une taille spécifique.
Implémentation
type Ctrl = TransformStreamDefaultController<string[]>;
class TextArrayTransformStream extends TransformStream<string, string[]> { #chunk: string[] = []; #chunkSize: number; #splitReg: RegExp;
constructor(chunkSize: number, maxTextLength: number) { super({ transform: (chunk, controller) => this.#handle(chunk, controller), flush: (controller) => this.#flush(controller), }); this.#chunkSize = chunkSize; this.#splitReg = new RegExp(`.{1,${maxTextLength}}`, "g"); }
#handle(chunk: string, controller: Ctrl): void { for (const str of chunk.match(this.#splitReg) || []) { if (this.#chunk.length >= this.#chunkSize) { controller.enqueue(this.#chunk); this.#chunk = []; } else { this.#chunk.push(str); } } }
#flush(controller: Ctrl): void { if (this.#chunk.length > 0) { controller.enqueue(this.#chunk); } }}Fonction auxiliaire
function toReadableStream(text: string): ReadableStream<string> { return new ReadableStream({ start(controller) { controller.enqueue(text); controller.close(); } });}Exemple d’utilisation
async function main() { const text = "Long texte..."; const arrayLength = 5; // Grouper par 5 éléments const textLength = 10; // Chaque élément fait 10 caractères
const stream = toReadableStream(text) .pipeThrough(new TextArrayTransformStream(arrayLength, textLength));
const reader = stream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break;
console.log(value); // string[] sort séquentiellement }}Cas d’utilisation
Ce pattern est efficace dans les scénarios suivants :
- Lors du traitement par lots des réponses d’API LLM
- Lors de l’affichage paginé de grands textes
- Prétraitement avant envoi à des API avec limitation de caractères
Notez qu’AsyncIterator n’est pas implémenté, donc la syntaxe for await…of ne peut pas être utilisée.
hsb.horse