Skip to content

Commit 7ef3ce0

Browse files
committed
All the logic was written in just one tsx file as suggested by CTO
1 parent d179690 commit 7ef3ce0

3 files changed

Lines changed: 318 additions & 199 deletions

File tree

src/features/spam-demo/SpamDemo.tsx

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,204 @@ import { useRef, useState, } from 'react'
22
import { useNavigation } from '@/hooks/useNavigation'
33
import { useTranslationContext } from '@/i18n'
44
import { 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

7204
interface MailViewProps {
8205
folder: string

0 commit comments

Comments
 (0)