Update assets/app.js
Browse files- assets/app.js +90 -50
assets/app.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
| 1 |
import { env, AutoModelForCausalLM, Tensor } from '/assets/transformers.min.js';
|
| 2 |
|
|
|
|
|
|
|
| 3 |
|
| 4 |
// Declare global UI elements and model instance
|
| 5 |
-
let dnaInput, maxTokensInput, temperatureInput, deviceInput, generateBtn, outputDisplay, statusDisplay;
|
| 6 |
let carbonModel = null;
|
| 7 |
|
| 8 |
const MODEL_REPO = 'huggingworld/Carbon-500M-ONNX';
|
|
@@ -56,6 +58,22 @@ function idToKmer(id) {
|
|
| 56 |
return kmer;
|
| 57 |
}
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
// ==========================================
|
| 60 |
// 🧠 MODEL INITIALIZATION
|
| 61 |
// ==========================================
|
|
@@ -64,33 +82,50 @@ async function initializeModel() {
|
|
| 64 |
generateBtn.disabled = true;
|
| 65 |
|
| 66 |
const selectedDevice = deviceInput.value;
|
| 67 |
-
const dtypeSetting =
|
| 68 |
|
| 69 |
try {
|
| 70 |
-
statusDisplay.textContent = `📥 Allocating Carbon layers onto engine target [${selectedDevice.toUpperCase()}]...`;
|
| 71 |
statusDisplay.style.color = '#888888';
|
| 72 |
|
|
|
|
|
|
|
| 73 |
if (carbonModel) {
|
| 74 |
carbonModel = null;
|
| 75 |
}
|
| 76 |
|
| 77 |
carbonModel = await AutoModelForCausalLM.from_pretrained(MODEL_REPO, {
|
| 78 |
device: selectedDevice,
|
| 79 |
-
dtype: dtypeSetting
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
});
|
| 81 |
|
| 82 |
-
statusDisplay.textContent = `🟢 Carbon Model Ready (${selectedDevice.toUpperCase()} Activated)`;
|
| 83 |
statusDisplay.style.color = '#317f3f';
|
|
|
|
| 84 |
generateBtn.disabled = false;
|
| 85 |
} catch (error) {
|
| 86 |
console.error('❌ Initialization Failure:', error);
|
| 87 |
statusDisplay.textContent = `❌ Engine Error: ${error.message}`;
|
| 88 |
statusDisplay.style.color = '#b00020';
|
|
|
|
| 89 |
}
|
| 90 |
}
|
| 91 |
|
| 92 |
// ==========================================
|
| 93 |
-
// ⚡ INFERENCE EXECUTION LOOP
|
| 94 |
// ==========================================
|
| 95 |
|
| 96 |
async function runInference() {
|
|
@@ -102,19 +137,16 @@ async function runInference() {
|
|
| 102 |
outputDisplay.textContent = 'Processing structure...';
|
| 103 |
|
| 104 |
try {
|
| 105 |
-
// 1. Clean data inputs
|
| 106 |
let rawInput = dnaInput.value.toUpperCase().replace(/[^ACGTN]/g, '');
|
| 107 |
if (rawInput.length === 0) {
|
| 108 |
throw new Error("No valid genomic bases found.");
|
| 109 |
}
|
| 110 |
|
| 111 |
-
// 2. Pad to fit 6-mer boundary block splits
|
| 112 |
const remainder = rawInput.length % 6;
|
| 113 |
if (remainder !== 0) {
|
| 114 |
rawInput = "A".repeat(6 - remainder) + rawInput;
|
| 115 |
}
|
| 116 |
|
| 117 |
-
// 3. Build numerical token array manually using Carbon offsets
|
| 118 |
let inputIds = [DNA_TAG_ID];
|
| 119 |
for (let i = 0; i < rawInput.length; i += 6) {
|
| 120 |
const chunk = rawInput.slice(i, i + 6);
|
|
@@ -125,7 +157,6 @@ async function runInference() {
|
|
| 125 |
let temp = temperatureInput ? parseFloat(temperatureInput.value) : 0.7;
|
| 126 |
if (temp <= 0) temp = 0.7;
|
| 127 |
|
| 128 |
-
// 4. Instantiate Int64 Tensors matching model layout expectations
|
| 129 |
const inputSequenceTensor = new Tensor(
|
| 130 |
'int64',
|
| 131 |
BigInt64Array.from(inputIds.map(id => BigInt(id))),
|
|
@@ -138,59 +169,63 @@ async function runInference() {
|
|
| 138 |
[1, inputIds.length]
|
| 139 |
);
|
| 140 |
|
| 141 |
-
//
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
input_ids: inputSequenceTensor,
|
| 144 |
attention_mask: attentionMaskTensor,
|
| 145 |
max_new_tokens: maxTokens,
|
| 146 |
min_new_tokens: 15,
|
| 147 |
do_sample: temp > 0,
|
| 148 |
temperature: temp,
|
| 149 |
-
repetition_penalty: 1.15
|
|
|
|
| 150 |
});
|
| 151 |
|
| 152 |
const duration = ((performance.now() - startTime) / 1000).toFixed(2);
|
| 153 |
|
| 154 |
-
// 6. Comprehensive extraction pattern to parse out BigInt matrix output arrays
|
| 155 |
-
let fullSequenceArray = [];
|
| 156 |
-
if (generatedOutputTensors) {
|
| 157 |
-
if (typeof generatedOutputTensors.tolist === 'function') {
|
| 158 |
-
const nestedList = generatedOutputTensors.tolist();
|
| 159 |
-
// Check if nested inside a batch index array wrapper: [[tokens]] vs [tokens]
|
| 160 |
-
fullSequenceArray = Array.isArray(nestedList[0]) ? nestedList[0] : nestedList;
|
| 161 |
-
} else if (generatedOutputTensors.data) {
|
| 162 |
-
fullSequenceArray = Array.from(generatedOutputTensors.data);
|
| 163 |
-
} else if (Array.isArray(generatedOutputTensors)) {
|
| 164 |
-
fullSequenceArray = generatedOutputTensors;
|
| 165 |
-
}
|
| 166 |
-
}
|
| 167 |
-
|
| 168 |
-
// 7. Isolate newly generated tokens from the prompt sequence inputs
|
| 169 |
-
let generatedTokens = [];
|
| 170 |
-
const plainOutputArray = fullSequenceArray.map(t => Number(t));
|
| 171 |
-
|
| 172 |
-
// FIX: Safely check index position zero of array primitive elements instead of matching arrays to integers
|
| 173 |
-
if (plainOutputArray.length > inputIds.length && plainOutputArray[0] === DNA_TAG_ID) {
|
| 174 |
-
generatedTokens = plainOutputArray.slice(inputIds.length);
|
| 175 |
-
} else {
|
| 176 |
-
generatedTokens = plainOutputArray;
|
| 177 |
-
}
|
| 178 |
-
|
| 179 |
-
// 8. Translate vocabulary ranges back into pure DNA characters
|
| 180 |
-
let generatedSequence = "";
|
| 181 |
-
for (let token of generatedTokens) {
|
| 182 |
-
const numericId = Number(token);
|
| 183 |
-
if (numericId >= DNA_OFFSET && numericId <= OOV_TOKEN_ID) {
|
| 184 |
-
generatedSequence += idToKmer(numericId);
|
| 185 |
-
}
|
| 186 |
-
}
|
| 187 |
-
|
| 188 |
if (!generatedSequence) {
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
| 190 |
}
|
| 191 |
|
| 192 |
-
outputDisplay.textContent = `${generatedSequence}\n\n[Inference completed in ${duration}s on ${deviceInput.value.toUpperCase()}]`;
|
| 193 |
-
|
| 194 |
} catch (error) {
|
| 195 |
console.error("❌ Generation Pipeline Error:", error);
|
| 196 |
outputDisplay.textContent = `❌ Generation Error: ${error.message}`;
|
|
@@ -209,9 +244,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 209 |
maxTokensInput = document.getElementById('max-tokens');
|
| 210 |
temperatureInput = document.getElementById('temperature');
|
| 211 |
deviceInput = document.getElementById('execution-device');
|
|
|
|
| 212 |
generateBtn = document.getElementById('generateBtn');
|
| 213 |
outputDisplay = document.getElementById('output-display');
|
| 214 |
statusDisplay = document.getElementById('status-message');
|
|
|
|
| 215 |
|
| 216 |
if (generateBtn) {
|
| 217 |
generateBtn.addEventListener('click', runInference);
|
|
@@ -219,6 +256,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 219 |
if (deviceInput) {
|
| 220 |
deviceInput.addEventListener('change', initializeModel);
|
| 221 |
}
|
|
|
|
|
|
|
|
|
|
| 222 |
|
| 223 |
initializeModel();
|
| 224 |
});
|
|
|
|
| 1 |
import { env, AutoModelForCausalLM, Tensor } from '/assets/transformers.min.js';
|
| 2 |
|
| 3 |
+
// Prevent browser cache exhaustion under quota restrictions
|
| 4 |
+
env.useBrowserCache = false;
|
| 5 |
|
| 6 |
// Declare global UI elements and model instance
|
| 7 |
+
let dnaInput, maxTokensInput, temperatureInput, deviceInput, dtypeInput, generateBtn, outputDisplay, statusDisplay, progressBar;
|
| 8 |
let carbonModel = null;
|
| 9 |
|
| 10 |
const MODEL_REPO = 'huggingworld/Carbon-500M-ONNX';
|
|
|
|
| 58 |
return kmer;
|
| 59 |
}
|
| 60 |
|
| 61 |
+
/**
|
| 62 |
+
* Converts a raw string of DNA bases into HTML color-coded spans matching international standards.
|
| 63 |
+
*/
|
| 64 |
+
function colorCodeSequence(sequence) {
|
| 65 |
+
return sequence.split('').map(base => {
|
| 66 |
+
switch(base) {
|
| 67 |
+
case 'A': return '<span class="dna-a">A</span>';
|
| 68 |
+
case 'C': return '<span class="dna-c">C</span>';
|
| 69 |
+
case 'G': return '<span class="dna-g">G</span>';
|
| 70 |
+
case 'T': return '<span class="dna-t">T</span>';
|
| 71 |
+
case 'N': return '<span class="dna-n">N</span>';
|
| 72 |
+
default: return base; // Pass through unexpected layout structures or spacing safely
|
| 73 |
+
}
|
| 74 |
+
}).join('');
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
// ==========================================
|
| 78 |
// 🧠 MODEL INITIALIZATION
|
| 79 |
// ==========================================
|
|
|
|
| 82 |
generateBtn.disabled = true;
|
| 83 |
|
| 84 |
const selectedDevice = deviceInput.value;
|
| 85 |
+
const dtypeSetting = dtypeInput ? dtypeInput.value : 'fp16';
|
| 86 |
|
| 87 |
try {
|
| 88 |
+
statusDisplay.textContent = `📥 Allocating Carbon layers onto engine target [${selectedDevice.toUpperCase()} (${dtypeSetting.toUpperCase()})]...`;
|
| 89 |
statusDisplay.style.color = '#888888';
|
| 90 |
|
| 91 |
+
if (progressBar) progressBar.style.width = '0%';
|
| 92 |
+
|
| 93 |
if (carbonModel) {
|
| 94 |
carbonModel = null;
|
| 95 |
}
|
| 96 |
|
| 97 |
carbonModel = await AutoModelForCausalLM.from_pretrained(MODEL_REPO, {
|
| 98 |
device: selectedDevice,
|
| 99 |
+
dtype: dtypeSetting,
|
| 100 |
+
progress_callback: (e) => {
|
| 101 |
+
if (e.status === 'initiate') {
|
| 102 |
+
statusDisplay.textContent = `🎬 Initiating download: ${e.file}...`;
|
| 103 |
+
} else if (e.status === 'progress') {
|
| 104 |
+
statusDisplay.textContent = `📥 Downloading ${e.file}: ${Math.round(e.progress)}%`;
|
| 105 |
+
if (progressBar) progressBar.style.width = `${e.progress}%`;
|
| 106 |
+
} else if (e.status === 'progress_total') {
|
| 107 |
+
if (progressBar) progressBar.style.width = `${e.progress}%`;
|
| 108 |
+
statusDisplay.textContent = `📦 Aggregate Downloading Progress: ${Math.round(e.progress)}%`;
|
| 109 |
+
} else if (e.status === 'done') {
|
| 110 |
+
statusDisplay.textContent = `✨ Loaded file asset: ${e.file}`;
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
});
|
| 114 |
|
| 115 |
+
statusDisplay.textContent = `🟢 Carbon Model Ready (${selectedDevice.toUpperCase()} + ${dtypeSetting.toUpperCase()} Activated)`;
|
| 116 |
statusDisplay.style.color = '#317f3f';
|
| 117 |
+
if (progressBar) progressBar.style.width = '100%';
|
| 118 |
generateBtn.disabled = false;
|
| 119 |
} catch (error) {
|
| 120 |
console.error('❌ Initialization Failure:', error);
|
| 121 |
statusDisplay.textContent = `❌ Engine Error: ${error.message}`;
|
| 122 |
statusDisplay.style.color = '#b00020';
|
| 123 |
+
if (progressBar) progressBar.style.width = '0%';
|
| 124 |
}
|
| 125 |
}
|
| 126 |
|
| 127 |
// ==========================================
|
| 128 |
+
// ⚡ INFERENCE EXECUTION LOOP (STREAMING + COLOR ACTIVATED)
|
| 129 |
// ==========================================
|
| 130 |
|
| 131 |
async function runInference() {
|
|
|
|
| 137 |
outputDisplay.textContent = 'Processing structure...';
|
| 138 |
|
| 139 |
try {
|
|
|
|
| 140 |
let rawInput = dnaInput.value.toUpperCase().replace(/[^ACGTN]/g, '');
|
| 141 |
if (rawInput.length === 0) {
|
| 142 |
throw new Error("No valid genomic bases found.");
|
| 143 |
}
|
| 144 |
|
|
|
|
| 145 |
const remainder = rawInput.length % 6;
|
| 146 |
if (remainder !== 0) {
|
| 147 |
rawInput = "A".repeat(6 - remainder) + rawInput;
|
| 148 |
}
|
| 149 |
|
|
|
|
| 150 |
let inputIds = [DNA_TAG_ID];
|
| 151 |
for (let i = 0; i < rawInput.length; i += 6) {
|
| 152 |
const chunk = rawInput.slice(i, i + 6);
|
|
|
|
| 157 |
let temp = temperatureInput ? parseFloat(temperatureInput.value) : 0.7;
|
| 158 |
if (temp <= 0) temp = 0.7;
|
| 159 |
|
|
|
|
| 160 |
const inputSequenceTensor = new Tensor(
|
| 161 |
'int64',
|
| 162 |
BigInt64Array.from(inputIds.map(id => BigInt(id))),
|
|
|
|
| 169 |
[1, inputIds.length]
|
| 170 |
);
|
| 171 |
|
| 172 |
+
// Streaming state monitors
|
| 173 |
+
let generatedSequence = "";
|
| 174 |
+
let tokenCount = 0;
|
| 175 |
+
|
| 176 |
+
// Custom Streamer configuration to hook directly into the token stream
|
| 177 |
+
const customStreamer = {
|
| 178 |
+
put: (value) => {
|
| 179 |
+
let tokens = [];
|
| 180 |
+
if (value && typeof value.tolist === 'function') {
|
| 181 |
+
tokens = value.tolist().flat();
|
| 182 |
+
} else if (value && value.data) {
|
| 183 |
+
tokens = Array.from(value.data);
|
| 184 |
+
} else if (Array.isArray(value)) {
|
| 185 |
+
tokens = value;
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
for (const token of tokens) {
|
| 189 |
+
const numericId = Number(token);
|
| 190 |
+
tokenCount++;
|
| 191 |
+
|
| 192 |
+
// Skip prompt sequence tokens
|
| 193 |
+
if (tokenCount <= inputIds.length) {
|
| 194 |
+
continue;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
// Decode token step-by-step, wrap in color spans, and stream
|
| 198 |
+
if (numericId >= DNA_OFFSET && numericId <= OOV_TOKEN_ID) {
|
| 199 |
+
generatedSequence += idToKmer(numericId);
|
| 200 |
+
|
| 201 |
+
const HTMLRenderMatrix = colorCodeSequence(generatedSequence);
|
| 202 |
+
outputDisplay.innerHTML = `${HTMLRenderMatrix}\n\n<span class="meta-text">[Streaming on ${deviceInput.value.toUpperCase()}...]</span>`;
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
},
|
| 206 |
+
end: () => {}
|
| 207 |
+
};
|
| 208 |
+
|
| 209 |
+
await carbonModel.generate({
|
| 210 |
input_ids: inputSequenceTensor,
|
| 211 |
attention_mask: attentionMaskTensor,
|
| 212 |
max_new_tokens: maxTokens,
|
| 213 |
min_new_tokens: 15,
|
| 214 |
do_sample: temp > 0,
|
| 215 |
temperature: temp,
|
| 216 |
+
repetition_penalty: 1.15,
|
| 217 |
+
streamer: customStreamer
|
| 218 |
});
|
| 219 |
|
| 220 |
const duration = ((performance.now() - startTime) / 1000).toFixed(2);
|
| 221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
if (!generatedSequence) {
|
| 223 |
+
outputDisplay.textContent = `[Generation tracking completed but output vector map evaluation was empty]`;
|
| 224 |
+
} else {
|
| 225 |
+
const FinalHTMLRenderMatrix = colorCodeSequence(generatedSequence);
|
| 226 |
+
outputDisplay.innerHTML = `${FinalHTMLRenderMatrix}\n\n<span class="meta-text">[Inference completed in ${duration}s on ${deviceInput.value.toUpperCase()}]</span>`;
|
| 227 |
}
|
| 228 |
|
|
|
|
|
|
|
| 229 |
} catch (error) {
|
| 230 |
console.error("❌ Generation Pipeline Error:", error);
|
| 231 |
outputDisplay.textContent = `❌ Generation Error: ${error.message}`;
|
|
|
|
| 244 |
maxTokensInput = document.getElementById('max-tokens');
|
| 245 |
temperatureInput = document.getElementById('temperature');
|
| 246 |
deviceInput = document.getElementById('execution-device');
|
| 247 |
+
dtypeInput = document.getElementById('execution-dtype');
|
| 248 |
generateBtn = document.getElementById('generateBtn');
|
| 249 |
outputDisplay = document.getElementById('output-display');
|
| 250 |
statusDisplay = document.getElementById('status-message');
|
| 251 |
+
progressBar = document.getElementById('progress-bar');
|
| 252 |
|
| 253 |
if (generateBtn) {
|
| 254 |
generateBtn.addEventListener('click', runInference);
|
|
|
|
| 256 |
if (deviceInput) {
|
| 257 |
deviceInput.addEventListener('change', initializeModel);
|
| 258 |
}
|
| 259 |
+
if (dtypeInput) {
|
| 260 |
+
dtypeInput.addEventListener('change', initializeModel);
|
| 261 |
+
}
|
| 262 |
|
| 263 |
initializeModel();
|
| 264 |
});
|