import * as ort from "onnxruntime-web"; import "./style.css"; const TASK_LABELS = ["general_chat", "writing", "translation", "summarization", "research", "coding", "mathematics", "document_analysis", "high_stakes"]; const COMPLEXITY_LABELS = ["low", "medium", "high"]; const WORD_BINS = 1024; const CHAR_BINS = 512; const INPUT_SIZE = 1544; function fnv1a(text) { let hash = 2166136261; for (let i = 0; i < text.length; i += 1) { hash ^= text.charCodeAt(i); hash = Math.imul(hash, 16777619); } return hash >>> 0; } function buildFeatures(text) { const source = String(text || ""); const lower = source.toLocaleLowerCase(); const values = new Float32Array(INPUT_SIZE); const words = lower.match(/[\p{L}\p{N}_]+/gu) || []; const grams = [...words]; for (let i = 0; i + 1 < words.length; i += 1) grams.push(`${words[i]}_${words[i + 1]}`); for (const gram of grams) values[fnv1a(gram) % WORD_BINS] += 1; const compact = lower.replace(/\s+/g, " "); for (let i = 0; i + 2 < compact.length; i += 1) { values[WORD_BINS + (fnv1a(compact.slice(i, i + 3)) % CHAR_BINS)] += 0.25; } const sparseCount = WORD_BINS + CHAR_BINS; let norm = 0; for (let i = 0; i < sparseCount; i += 1) norm += values[i] * values[i]; norm = Math.sqrt(norm); if (norm > 0) for (let i = 0; i < sparseCount; i += 1) values[i] /= norm; const base = sparseCount; values[base] = Math.min(source.length, 4000) / 4000; values[base + 1] = Math.min(words.length, 800) / 800; values[base + 2] = Math.min((source.match(/\?/g) || []).length, 10) / 10; values[base + 3] = Math.min((source.match(/\n/g) || []).length, 30) / 30; values[base + 4] = Math.min((source.match(/[{}[\]();=<>/]/g) || []).length, 100) / 100; values[base + 5] = /https?:\/\/|www\./i.test(source) ? 1 : 0; values[base + 6] = /\b(pdf|document|dokument|file|súbor|attachment|príloha)\b/i.test(source) ? 1 : 0; values[base + 7] = 1; return values; } function argMax(values) { let index = 0; for (let i = 1; i < values.length; i += 1) if (values[i] > values[index]) index = i; return index; } const result = document.querySelector("#result"); const modelUrl = new URL("../../../model.onnx", import.meta.url).href; ort.env.wasm.numThreads = 1; const session = await ort.InferenceSession.create(modelUrl, { executionProviders: ["wasm"] }); result.textContent = "Model ready."; document.querySelector("#classify").addEventListener("click", async () => { const text = document.querySelector("#prompt").value; const features = buildFeatures(text); const outputs = await session.run({ features: new ort.Tensor("float32", features, [1, INPUT_SIZE]) }); const task = Array.from(outputs.task_probabilities.data); const complexity = Array.from(outputs.complexity_probabilities.data); const taskIndex = argMax(task); const complexityIndex = argMax(complexity); result.textContent = JSON.stringify({ task: { label: TASK_LABELS[taskIndex], confidence: task[taskIndex], probabilities: Object.fromEntries(TASK_LABELS.map((label, i) => [label, task[i]])) }, complexity: { label: COMPLEXITY_LABELS[complexityIndex], confidence: complexity[complexityIndex], probabilities: Object.fromEntries(COMPLEXITY_LABELS.map((label, i) => [label, complexity[i]])) } }, null, 2); });