@@ -261,66 +261,113 @@ class TextGenerationPlayground {
261261 promptSpan . textContent = prompt ;
262262 this . elements . generatedText . appendChild ( promptSpan ) ;
263263
264- // Since Transformers.js doesn't expose raw logits easily in streaming mode,
265- // we'll use a workaround: generate token by token and simulate the process
266-
267- let currentText = prompt ;
268- let generatedCount = 0 ;
269-
270- while ( generatedCount < maxLength && ! this . shouldStop ) {
271- // Generate next token
272- const output = await this . model ( currentText , {
273- max_new_tokens : 1 ,
274- temperature : strategy === 'greedy' ? 0.001 : temperature ,
264+ try {
265+ // Generate all tokens at once for better performance
266+ // Note: Transformers.js doesn't expose raw logits, so we simulate probabilities
267+ const output = await this . model ( prompt , {
268+ max_new_tokens : maxLength ,
269+ temperature : strategy === 'greedy' ? 0.1 : temperature ,
275270 top_k : strategy === 'topk' || strategy === 'combined' ? topK : 0 ,
276271 top_p : strategy === 'topp' || strategy === 'combined' ? topP : 1.0 ,
277272 repetition_penalty : repetitionPenalty ,
278- return_full_text : false
273+ do_sample : strategy !== 'greedy' ,
274+ num_return_sequences : 1
279275 } ) ;
280276
281- if ( ! output || output . length === 0 ) break ;
277+ if ( ! output || output . length === 0 ) {
278+ throw new Error ( 'Model returned no output' ) ;
279+ }
282280
283281 const generatedText = output [ 0 ] . generated_text ;
284- if ( ! generatedText ) break ;
282+ if ( ! generatedText ) {
283+ throw new Error ( 'Generated text is empty' ) ;
284+ }
285285
286- // Extract new token
287- const newToken = generatedText ;
288- currentText += newToken ;
286+ // Split the generated text into tokens (approximate)
287+ // This is a simple approximation since we don't have access to the actual tokenization
288+ const fullText = generatedText . substring ( prompt . length ) ;
289+ const tokens = this . approximateTokenize ( fullText ) ;
289290
290- // Simulate token info (since we don't have direct access to logits)
291- const tokenInfo = this . simulateTokenInfo ( newToken , strategy , {
292- temperature,
293- topK,
294- topP
295- } ) ;
291+ // Display tokens with simulated animations
292+ for ( let i = 0 ; i < tokens . length && ! this . shouldStop ; i ++ ) {
293+ const token = tokens [ i ] ;
296294
297- // Update display
298- this . displayToken ( newToken , tokenInfo ) ;
295+ // Simulate token info (since we don't have direct access to logits)
296+ const tokenInfo = this . simulateTokenInfo ( token , strategy , {
297+ temperature,
298+ topK,
299+ topP
300+ } ) ;
299301
300- // Update statistics
301- this . updateStatistics ( tokenInfo ) ;
302+ // Update display
303+ this . displayToken ( token , tokenInfo ) ;
302304
303- // Update visualizations
304- if ( this . elements . showProbabilities . checked ) {
305- this . visualizer . displayTokenProbabilities ( tokenInfo , generatedCount + 1 ) ;
306- }
305+ // Update statistics
306+ this . updateStatistics ( tokenInfo ) ;
307307
308- if ( this . elements . showAlternatives . checked ) {
309- this . visualizer . displayAlternatives ( tokenInfo . topTokens ) ;
310- }
308+ // Update visualizations
309+ if ( this . elements . showProbabilities . checked ) {
310+ this . visualizer . displayTokenProbabilities ( tokenInfo , i + 1 ) ;
311+ }
312+
313+ if ( this . elements . showAlternatives . checked ) {
314+ this . visualizer . displayAlternatives ( tokenInfo . topTokens ) ;
315+ }
311316
312- if ( this . elements . showEntropy . checked ) {
313- this . visualizer . updateEntropyChart ( tokenInfo . entropy ) ;
317+ if ( this . elements . showEntropy . checked ) {
318+ this . visualizer . updateEntropyChart ( tokenInfo . entropy ) ;
319+ }
320+
321+ // Small delay for visualization effect
322+ await new Promise ( resolve => setTimeout ( resolve , 50 ) ) ;
314323 }
315324
316- generatedCount ++ ;
325+ // Final statistics update
326+ this . updateFinalStats ( ) ;
317327
318- // Small delay for visualization
319- await new Promise ( resolve => setTimeout ( resolve , 50 ) ) ;
328+ } catch ( error ) {
329+ console . error ( 'Generation error:' , error ) ;
330+ throw error ;
331+ }
332+ }
333+
334+ /**
335+ * Approximate tokenization for visualization
336+ * Splits text into word-like tokens for display
337+ * @param {string } text - Text to tokenize
338+ * @returns {Array } Array of token strings
339+ */
340+ approximateTokenize ( text ) {
341+ // Simple approximation: split on spaces and punctuation boundaries
342+ // This is not the real BPE tokenization, but good enough for visualization
343+ const tokens = [ ] ;
344+ let currentToken = '' ;
345+
346+ for ( let i = 0 ; i < text . length ; i ++ ) {
347+ const char = text [ i ] ;
348+
349+ if ( char === ' ' || char === '\n' || char === '\t' ) {
350+ if ( currentToken ) {
351+ tokens . push ( currentToken ) ;
352+ currentToken = '' ;
353+ }
354+ tokens . push ( char ) ;
355+ } else if ( / [ . , ! ? ; : ] / . test ( char ) ) {
356+ if ( currentToken ) {
357+ tokens . push ( currentToken ) ;
358+ currentToken = '' ;
359+ }
360+ tokens . push ( char ) ;
361+ } else {
362+ currentToken += char ;
363+ }
364+ }
365+
366+ if ( currentToken ) {
367+ tokens . push ( currentToken ) ;
320368 }
321369
322- // Final statistics update
323- this . updateFinalStats ( ) ;
370+ return tokens ;
324371 }
325372
326373 /**
@@ -461,8 +508,8 @@ class TextGenerationPlayground {
461508 const maxLength = parseInt ( this . elements . maxLength . value ) ;
462509
463510 const strategies = [
464- { name : 'Greedy' , value : 'greedy' , temp : 0.001 } ,
465- { name : 'Temperature (1.0)' , value : 'temperature' , temp : 1.0 }
511+ { name : 'Greedy' , value : 'greedy' , temp : 0.1 , doSample : false } ,
512+ { name : 'Temperature (1.0)' , value : 'temperature' , temp : 1.0 , doSample : true }
466513 ] ;
467514
468515 document . getElementById ( 'strategyAName' ) . textContent = strategies [ 0 ] . name ;
@@ -471,8 +518,8 @@ class TextGenerationPlayground {
471518 const outputA = document . getElementById ( 'outputA' ) ;
472519 const outputB = document . getElementById ( 'outputB' ) ;
473520
474- outputA . textContent = prompt ;
475- outputB . textContent = prompt ;
521+ outputA . innerHTML = `<span style="color: #94a3b8;"> ${ prompt } </span>` ;
522+ outputB . innerHTML = `<span style="color: #94a3b8;"> ${ prompt } </span>` ;
476523
477524 this . elements . generateBtn . disabled = true ;
478525
@@ -482,17 +529,26 @@ class TextGenerationPlayground {
482529 this . model ( prompt , {
483530 max_new_tokens : maxLength ,
484531 temperature : strategies [ 0 ] . temp ,
485- return_full_text : false
532+ do_sample : strategies [ 0 ] . doSample ,
533+ num_return_sequences : 1
486534 } ) ,
487535 this . model ( prompt , {
488536 max_new_tokens : maxLength ,
489537 temperature : strategies [ 1 ] . temp ,
490- return_full_text : false
538+ do_sample : strategies [ 1 ] . doSample ,
539+ num_return_sequences : 1
491540 } )
492541 ] ) ;
493542
494- outputA . textContent = prompt + ( resultA [ 0 ] ?. generated_text || '' ) ;
495- outputB . textContent = prompt + ( resultB [ 0 ] ?. generated_text || '' ) ;
543+ if ( resultA && resultA [ 0 ] ) {
544+ const textA = resultA [ 0 ] . generated_text . substring ( prompt . length ) ;
545+ outputA . innerHTML = `<span style="color: #94a3b8;">${ prompt } </span>${ textA } ` ;
546+ }
547+
548+ if ( resultB && resultB [ 0 ] ) {
549+ const textB = resultB [ 0 ] . generated_text . substring ( prompt . length ) ;
550+ outputB . innerHTML = `<span style="color: #94a3b8;">${ prompt } </span>${ textB } ` ;
551+ }
496552
497553 } catch ( error ) {
498554 console . error ( 'Comparison generation error:' , error ) ;
0 commit comments