33< head >
44 < meta charset ="UTF-8 ">
55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6- < title > HuggingFace Tokenizer Playground </ title >
6+ < title > HuggingFace Tokenizer online </ title >
77 < script src ="https://cdn.jsdelivr.net/npm/nunjucks@3.2.4/browser/nunjucks.min.js "> </ script >
88 < style >
99 * {
5353 font-weight : 500 ;
5454 color : # c9d1d9 ;
5555 }
56- input [type = "text" ], input [type = "url" ], textarea , select {
56+ input [type = "text" ], input [type = "url" ], input [ type = "password" ] , textarea , select {
5757 width : 100% ;
5858 padding : 10px ;
5959 border : 1px solid # 30363d ;
349349 </ div >
350350 </ div >
351351
352- < h1 > HuggingFace Tokenizer Playground </ h1 >
353- < p class ="subtitle "> Load tokenizers and chat templates from HuggingFace, visualize token encoding/decoding</ p >
352+ < h1 > Online Tokenizer</ h1 >
353+ < p class ="subtitle "> Load tokenizer config and chat templates from HuggingFace, visualize token encoding/decoding. Built with < a href =" https://github.com/daulet/tokenizers " > daulet/tokenizers </ a > library for Go and Wasm. </ p >
354354
355355 <!-- Model Selection -->
356356 < div class ="section ">
@@ -373,6 +373,15 @@ <h2>1. Select Model</h2>
373373 < button id ="loadModelBtn "> Load Model</ button >
374374 </ div >
375375
376+ < div id ="tokenSection " class ="hidden " style ="margin-top: 10px; ">
377+ < label for ="hfToken "> HuggingFace Token (for gated models)</ label >
378+ < input type ="password " id ="hfToken " placeholder ="hf_xxxxxxxxxxxx ">
379+ < p style ="font-size: 12px; color: #8b949e; margin-top: 4px; ">
380+ Get your token at < a href ="https://huggingface.co/settings/tokens " target ="_blank "> huggingface.co/settings/tokens</ a > .
381+ Token is only used in your browser and never sent to any server except HuggingFace.
382+ </ p >
383+ </ div >
384+
376385 < div id ="loadStatus "> </ div >
377386
378387 < div id ="modelInfo " class ="hidden ">
@@ -570,10 +579,29 @@ <h2 style="margin: 0;">Chat Template (Jinja2)</h2>
570579 showStatus ( 'Loading tokenizer...' , 'info' ) ;
571580
572581 const baseUrl = `https://huggingface.co/${ modelId } /resolve/main` ;
582+ const hfToken = document . getElementById ( 'hfToken' ) . value . trim ( ) ;
583+
584+ // Prepare fetch options with optional auth header
585+ const fetchOptions = { } ;
586+ if ( hfToken ) {
587+ fetchOptions . headers = { 'Authorization' : `Bearer ${ hfToken } ` } ;
588+ }
573589
574590 try {
575591 // Load tokenizer.json
576- const tokenizerResponse = await fetch ( `${ baseUrl } /tokenizer.json` ) ;
592+ const tokenizerResponse = await fetch ( `${ baseUrl } /tokenizer.json` , fetchOptions ) ;
593+
594+ if ( tokenizerResponse . status === 401 ) {
595+ // Gated model - show token input
596+ document . getElementById ( 'tokenSection' ) . classList . remove ( 'hidden' ) ;
597+ throw new Error ( 'This model is gated. Please enter your HuggingFace token to access it.' ) ;
598+ }
599+
600+ if ( tokenizerResponse . status === 403 ) {
601+ document . getElementById ( 'tokenSection' ) . classList . remove ( 'hidden' ) ;
602+ throw new Error ( 'Access denied. You may need to accept the model license at huggingface.co/' + modelId ) ;
603+ }
604+
577605 if ( ! tokenizerResponse . ok ) {
578606 throw new Error ( `Failed to load tokenizer.json: ${ tokenizerResponse . status } ` ) ;
579607 }
@@ -584,7 +612,7 @@ <h2 style="margin: 0;">Chat Template (Jinja2)</h2>
584612
585613 // Try to load tokenizer_config.json for chat template
586614 try {
587- const configResponse = await fetch ( `${ baseUrl } /tokenizer_config.json` ) ;
615+ const configResponse = await fetch ( `${ baseUrl } /tokenizer_config.json` , fetchOptions ) ;
588616 if ( configResponse . ok ) {
589617 tokenizerConfig = await configResponse . json ( ) ;
590618 chatTemplate = tokenizerConfig . chat_template || null ;
0 commit comments