@@ -370,7 +370,7 @@ <h2>Comparison Charts</h2>
370370 } ,
371371 flare : {
372372 id : 'smollm2-135m-flare' ,
373- url : 'https://huggingface.co/HuggingFaceTB/smollm2 -135M-instruct -GGUF/resolve/main/smollm2-135m-instruct-q8_0 .gguf' ,
373+ url : 'https://huggingface.co/bartowski/SmolLM2 -135M-Instruct -GGUF/resolve/main/SmolLM2-135M-Instruct-Q8_0 .gguf' ,
374374 } ,
375375 } ,
376376 'llama-3.2-1b' : {
@@ -520,7 +520,7 @@ <h2>Comparison Charts</h2>
520520 async function runTransformersInference ( prompt , opts ) {
521521 const t0 = performance . now ( ) ;
522522 let firstTokenTime = null ;
523- let outputTokenCount = 0 ;
523+ let prevTokenCount = 0 ;
524524
525525 const messages = [ { role : 'user' , content : prompt } ] ;
526526 let input ;
@@ -533,33 +533,44 @@ <h2>Comparison Charts</h2>
533533 input = prompt ;
534534 }
535535
536- const inputTokens = transformersPipeline . tokenizer ( input ) ;
537- const inputLen = inputTokens ?. input_ids ?. dims ?. [ 1 ] || input . length / 4 ;
538-
539536 const result = await transformersPipeline ( input , {
540537 max_new_tokens : opts . maxTokens ,
541538 temperature : opts . temperature || 0.001 ,
542539 do_sample : opts . temperature > 0 ,
543- callback_function : ( beams ) => {
544- const generated = beams ?. [ 0 ] ?. output_token_ids ?. length || 0 ;
545- if ( firstTokenTime === null && generated > inputLen ) {
540+ callback_function : ( output ) => {
541+ // output can be [{generated_text}] or beams with output_token_ids
542+ const ids = output ?. [ 0 ] ?. output_token_ids ;
543+ const currentLen = ids ?. length || ids ?. size || 0 ;
544+ if ( firstTokenTime === null && currentLen > prevTokenCount ) {
546545 firstTokenTime = performance . now ( ) - t0 ;
547546 }
548- outputTokenCount = Math . max ( 0 , generated - inputLen ) ;
547+ if ( currentLen > 0 ) prevTokenCount = currentLen ;
549548 } ,
550549 } ) ;
551550
552551 const totalTime = performance . now ( ) - t0 ;
553552 const output = result [ 0 ] ?. generated_text || '' ;
554553 const generatedPart = typeof output === 'string' ? output . slice ( input . length ) : '' ;
555- const decodeTime = totalTime - ( firstTokenTime || totalTime ) ;
556554
555+ // Count output tokens by tokenizing the generated part
556+ let outputTokenCount = 0 ;
557+ try {
558+ const genTokens = transformersPipeline . tokenizer ( generatedPart ) ;
559+ outputTokenCount = genTokens ?. input_ids ?. dims ?. [ 1 ] || genTokens ?. input_ids ?. length || 0 ;
560+ } catch {
561+ // Rough fallback: ~4 chars per token
562+ outputTokenCount = Math . max ( 1 , Math . round ( generatedPart . length / 4 ) ) ;
563+ }
564+
565+ // Transformers.js generates all tokens in a single batch call (no true
566+ // streaming), so firstTokenTime ≈ totalTime. Use tokens/totalTime for
567+ // throughput since there's no separate decode phase.
557568 return {
558569 output : generatedPart ,
559570 ttft : firstTokenTime || totalTime ,
560571 totalTime,
561572 tokenCount : outputTokenCount ,
562- tokensPerSec : ( outputTokenCount > 1 && decodeTime > 0 ) ? ( ( outputTokenCount - 1 ) / ( decodeTime / 1000 ) ) : 0 ,
573+ tokensPerSec : outputTokenCount > 0 ? ( outputTokenCount / ( totalTime / 1000 ) ) : 0 ,
563574 } ;
564575 }
565576
@@ -575,9 +586,38 @@ <h2>Comparison Charts</h2>
575586 if ( ! flareLib ) {
576587 log ( 'Loading @sauravpanda/flare WASM from CDN...' , 'info' ) ;
577588 const CDN = 'https://cdn.jsdelivr.net/npm/@sauravpanda/flare@0.2.0/pkg' ;
578- flareLib = await import ( `${ CDN } /flare_web.js` ) ;
589+ const wasmUrl = `${ CDN } /flare_web_bg.wasm` ;
590+
591+ // Fetch the JS module source, patch the WASM URL, and load via blob
592+ // to avoid cross-origin ES module import restrictions.
593+ const jsResp = await fetch ( `${ CDN } /flare_web.js` , { cache : 'no-cache' } ) ;
594+ if ( ! jsResp . ok ) throw new Error ( `HTTP ${ jsResp . status } fetching flare_web.js` ) ;
595+ let jsSrc = await jsResp . text ( ) ;
596+ log ( `Fetched flare_web.js (${ ( jsSrc . length / 1024 ) . toFixed ( 0 ) } KB)` , 'info' ) ;
597+
598+ // Patch import.meta.url references so the WASM file resolves to CDN
599+ jsSrc = jsSrc . replaceAll ( 'import.meta.url' , `'${ CDN } /flare_web.js'` ) ;
600+
601+ // Fix wasm-pack codegen bug: a JSDoc block contains "/* done */"
602+ // which prematurely closes the outer /** */ comment, causing
603+ // "Unexpected token '*'" parse error. Remove the nested comment.
604+ jsSrc = jsSrc . replaceAll ( '/* done */' , '/* done -/' ) ;
605+
606+ const blob = new Blob ( [ jsSrc ] , { type : 'application/javascript' } ) ;
607+ const blobUrl = URL . createObjectURL ( blob ) ;
608+ try {
609+ flareLib = await import ( /* webpackIgnore: true */ blobUrl ) ;
610+ } catch ( importErr ) {
611+ URL . revokeObjectURL ( blobUrl ) ;
612+ log ( `Blob import failed: ${ importErr . message } ` , 'error' ) ;
613+ // Fallback: try direct CDN import
614+ log ( 'Trying direct CDN import...' , 'info' ) ;
615+ flareLib = await import ( `${ CDN } /flare_web.js` ) ;
616+ }
617+ URL . revokeObjectURL ( blobUrl ) ;
618+
579619 // Initialize the WASM module
580- await flareLib . default ( ` ${ CDN } /flare_web_bg.wasm` ) ;
620+ await flareLib . default ( wasmUrl ) ;
581621 log ( 'Flare WASM initialized.' , 'success' ) ;
582622 }
583623
0 commit comments