@@ -2,7 +2,204 @@ import { useRef, useState, } from 'react'
22import { useNavigation } from '@/hooks/useNavigation'
33import { useTranslationContext } from '@/i18n'
44import { PATHS } from '@/routes/paths'
5- import { spamOrHam } from '@/features/spam-demo/detector.ts'
5+ import * as ort from 'onnxruntime-web' ;
6+
7+ let sessionFull : any = null ;
8+ const mergesPath : string = "public/vocab/merges_all_18k.txt" ;
9+ const vocabPath : string = "public/vocab/vocab_all_18k.json" ;
10+ const modelPath : string = "public/onnx/mail_180226_02.onnx" ;
11+ const wasmPath : string = '/public/wasm/onnxruntime/' ;
12+
13+ ort . env . wasm . wasmPaths = wasmPath ;
14+ const LENTOKENS = 128 ;
15+ const finalVocab = await loadVocab ( vocabPath ) ;
16+ const vstr = await v2str ( vocabPath ) ;
17+ const mstr = await m2str ( mergesPath ) ;
18+
19+ try {
20+ sessionFull = await ort . InferenceSession . create ( modelPath , {
21+ executionProviders : [ 'wasm' ]
22+ } ) ;
23+ console . log ( "Spam-detector Model loaded!" ) ;
24+ } catch ( e ) {
25+ console . error ( "Failed to load full model:" , e ) ;
26+ }
27+ const tensorial = new ort . Tensor ( "int64" , new BigInt64Array ( LENTOKENS ) , [ 1 , LENTOKENS ] ) ;
28+ const atten = new ort . Tensor ( 'float32' , new Float32Array ( LENTOKENS ) , [ 1 , LENTOKENS ] )
29+ const input_tensor = {
30+ input : tensorial ,
31+ attention : atten ,
32+ } ;
33+
34+ async function loadTokenizer ( ) {
35+ return new Promise ( ( resolve ) => {
36+ const script = document . createElement ( 'script' )
37+ script . src = 'public/wasm/tokenizer/tokenizer.js'
38+ script . onload = ( ) => {
39+ ( window as any ) . Module ( {
40+ onRuntimeInitialized ( ) {
41+ resolve ( this )
42+ }
43+ } )
44+ }
45+ document . body . appendChild ( script )
46+ } )
47+ }
48+
49+ const tokenizer : any = await loadTokenizer ( )
50+
51+
52+ async function loadVocab ( path : string ) {
53+ const response = await fetch ( path ) ;
54+ let text = await response . text ( ) ;
55+ text = text . slice ( 1 , - 1 ) ;
56+ const rows = text . trim ( ) . split ( '\n' ) . map ( row => row . split ( ': ' ) ) ;
57+ const dict = new Map ( ) ;
58+ for ( let i = 0 ; i < rows . length ; i ++ ) {
59+ let key = rows [ i ] [ 1 ] ;
60+ let key_int = BigInt ( Number ( key . replace ( "," , "" ) . trim ( ) ) ) ;
61+ let value :string = rows [ i ] [ 0 ] ;
62+ let value1 = value . replace ( "\"" , "" ) . trim ( ) ;
63+ let value2 = value1 . replace ( "\"" , "" ) . trim ( ) ;
64+ dict . set ( key_int , value2 ) ;
65+ }
66+ return dict ;
67+ }
68+
69+
70+ async function v2str ( path : string ) {
71+ const response = await fetch ( path ) ;
72+ let text = await response . text ( ) ;
73+ text = text . slice ( 2 , - 2 ) ;
74+ const text_split = text . split ( "\n" ) ;
75+ for ( let i = 0 ; i < text . length ; i ++ ) {
76+ if ( typeof text_split [ i ] == "string" ) {
77+ text_split [ i ] = text_split [ i ] . trim ( ) ;
78+ if ( text_split [ i ] . endsWith ( "," ) ) {
79+ text_split [ i ] = text_split [ i ] . slice ( 0 , - 1 ) ;
80+ }
81+ text_split [ i ] = text_split [ i ] . replaceAll ( "\"" , "" ) ;
82+ text_split [ i ] = text_split [ i ] . replaceAll ( ": " , " " ) ;
83+ const couple = text_split [ i ] . split ( " " ) ;
84+ text_split [ i ] = couple [ 1 ] . concat ( " " , couple [ 0 ] ) ;
85+ }
86+ }
87+ return text_split ;
88+ }
89+
90+
91+ async function m2str ( path : string ) {
92+ const response = await fetch ( path ) ;
93+ let text = await response . text ( ) ;
94+ const text_split = text . split ( "\n" ) . slice ( 0 , - 1 ) ;
95+ for ( let i = 0 ; i < text . length ; i ++ ) {
96+ if ( typeof text_split [ i ] == "string" ) {
97+ text_split [ i ] = text_split [ i ] . trim ( ) ;
98+ }
99+ }
100+ return text_split ;
101+ }
102+
103+
104+ function runTokenizer ( text : string ) :[ BigInt64Array , Float32Array ] {
105+
106+ const vocabPtr = vstr . map ( str => {
107+ const utf8Length = tokenizer . lengthBytesUTF8 ( str ) ;
108+ const ptr = tokenizer . _malloc ( utf8Length + 1 ) ;
109+ tokenizer . stringToUTF8 ( str , ptr , utf8Length + 1 ) ;
110+ return ptr ;
111+ } ) ;
112+ const vPtr = tokenizer . _malloc ( vstr . length * 4 ) ;
113+ vocabPtr . forEach ( ( ptr , i ) => {
114+ tokenizer . setValue ( vPtr + i * 4 , ptr , '*' ) ;
115+ } ) ;
116+
117+ const mergePtr = mstr . map ( str => {
118+ const utf8Length = tokenizer . lengthBytesUTF8 ( str ) ;
119+ const ptr = tokenizer . _malloc ( utf8Length + 1 ) ;
120+ tokenizer . stringToUTF8 ( str , ptr , utf8Length + 1 ) ;
121+ return ptr ;
122+ } ) ;
123+ const mPtr = tokenizer . _malloc ( mstr . length * 4 ) ;
124+ mergePtr . forEach ( ( ptr , i ) => {
125+ tokenizer . setValue ( mPtr + i * 4 , ptr , '*' ) ;
126+ } ) ;
127+
128+ const utf8Length = tokenizer . lengthBytesUTF8 ( text ) ;
129+ const textPtr = tokenizer . _malloc ( utf8Length + 1 ) ;
130+ tokenizer . stringToUTF8 ( text , textPtr , utf8Length + 1 ) ;
131+
132+ const tokensPtr = tokenizer . _malloc ( LENTOKENS * 4 ) ;
133+ const maskPtr = tokenizer . _malloc ( LENTOKENS * 4 ) ;
134+ tokenizer . _tokenizer ( tokensPtr , maskPtr , textPtr , vPtr , mPtr , LENTOKENS , vstr . length , mstr . length ) ;
135+
136+ const tokens = new Int32Array ( tokenizer . HEAP32 . buffer , tokensPtr , LENTOKENS ) ;
137+ const mask = new Float32Array ( tokenizer . HEAPF32 . buffer , maskPtr , LENTOKENS ) ;
138+ const tokens64 = new BigInt64Array ( LENTOKENS ) ;
139+ for ( let i = 0 ; i < LENTOKENS ; i ++ ) {
140+ tokens64 [ i ] = BigInt ( tokens [ i ] ) ;
141+ }
142+
143+ tokenizer . _free ( tokensPtr ) ;
144+ tokenizer . _free ( maskPtr ) ;
145+ tokenizer . _free ( textPtr ) ;
146+ vocabPtr . forEach ( ptr => tokenizer . _free ( ptr ) ) ;
147+ tokenizer . _free ( vPtr ) ;
148+ mergePtr . forEach ( ptr => tokenizer . _free ( ptr ) ) ;
149+ tokenizer . _free ( mPtr ) ;
150+ return [ tokens64 , mask ] ;
151+ }
152+
153+
154+ function color ( v : any ) {
155+ const g = Math . round ( 255 * v ) ;
156+ return `rgb(${ 255 - g } ,${ 255 - g } ,255)` ;
157+ }
158+
159+
160+ function provide_explanation ( s_result : string , relevancies : any , outputTokens : any , container : any ) {
161+ let strexp = `<span style ="color:white"> Your document was labeled as ` + s_result + ` because of the blue tokens: \n </span>` ;
162+ for ( let i = 0 ; i < LENTOKENS ; i ++ ) {
163+ strexp = strexp . concat ( `<span style="color:${ color ( relevancies [ i ] ) } "> <b> ${ finalVocab . get ( outputTokens [ i ] ) } </b> </s> </span> ` ) ;
164+ }
165+ container ( strexp ) ;
166+ }
167+
168+
169+ async function spamOrHam ( message : string | undefined , result : any , explanation : any ) {
170+ if ( message ) {
171+ let messagec = message . replaceAll ( "\n" , " " )
172+ console . time ( "Token" ) ;
173+ const [ outputTokens , atten ] = runTokenizer ( messagec ) ;
174+ console . timeEnd ( "Token" )
175+ console . time ( "Tensor" ) ;
176+ input_tensor . input . data . set ( outputTokens ) ;
177+ input_tensor . attention . data . set ( atten ) ;
178+ console . timeEnd ( "Tensor" )
179+ console . time ( "Inferencia" )
180+ let running = null ;
181+ running = await sessionFull . run ( input_tensor ) ;
182+ console . timeEnd ( "Inferencia" )
183+ const spam = running . output . data ;
184+ let relevancies ;
185+ if ( Object . values ( running ) . length > 1 ) {
186+ relevancies = running . saliencies . data ;
187+ }
188+ console . log ( "Salida del modelo: " , spam ) ;
189+ let s_result = "" ;
190+ if ( spam [ 0 ] > spam [ 1 ] ) {
191+ result ( "This mail is: ham" ) ;
192+ s_result = "ham" ;
193+ }
194+ else {
195+ result ( "The mail is: spam" ) ;
196+ s_result = "spam" ;
197+ }
198+ if ( Object . values ( running ) . length > 1 && s_result == "spam" ) {
199+ provide_explanation ( s_result , relevancies , outputTokens , explanation ) ;
200+ }
201+ }
202+ }
6203
7204interface MailViewProps {
8205 folder : string
0 commit comments