From 6f37963329e179d1ea246be0d8d7e94bad7942b4 Mon Sep 17 00:00:00 2001 From: ttstudio300 <143816235+ttstudio300@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:02:17 +0300 Subject: [PATCH 1/7] Add files via upload --- extensions/MountainAI-main/Banner.svg | 1 + extensions/MountainAI-main/Logo.svg | 1 + extensions/MountainAI-main/MountainAI.js | 413 ++++++++++++++++++ extensions/MountainAI-main/README.md | 71 +++ .../MountainAI-main/extension-info.json | 10 + 5 files changed, 496 insertions(+) create mode 100644 extensions/MountainAI-main/Banner.svg create mode 100644 extensions/MountainAI-main/Logo.svg create mode 100644 extensions/MountainAI-main/MountainAI.js create mode 100644 extensions/MountainAI-main/README.md create mode 100644 extensions/MountainAI-main/extension-info.json diff --git a/extensions/MountainAI-main/Banner.svg b/extensions/MountainAI-main/Banner.svg new file mode 100644 index 0000000000..a4b6310801 --- /dev/null +++ b/extensions/MountainAI-main/Banner.svg @@ -0,0 +1 @@ +111010011010001100101100000111011111011111110010111001111101001000001100001110100110000011000011100100110010011011111101110100000110100110000011010001100001111011011001011000001100101111011011001011110010100000111001111001011100101110111010111010000011010011000001101101110010111000011101110101110100000111001011001011100001110110011011001111001 \ No newline at end of file diff --git a/extensions/MountainAI-main/Logo.svg b/extensions/MountainAI-main/Logo.svg new file mode 100644 index 0000000000..a423ab02db --- /dev/null +++ b/extensions/MountainAI-main/Logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/MountainAI-main/MountainAI.js b/extensions/MountainAI-main/MountainAI.js new file mode 100644 index 0000000000..2a46cf592e --- /dev/null +++ b/extensions/MountainAI-main/MountainAI.js @@ -0,0 +1,413 @@ +// Name: MountainAI +// ID: MountainAI +// Description: Create and run your own GPT like ai models localy! +// By: RadereDev +// Original: RadereDev +// License: MPL-2.0 + +class MountainAI { + //MountainAI Build-15 public beta + constructor(numLayers=2, embedDim=16, ffDim=64, seqLen=8) { + this.numLayers = numLayers; + this.embedDim = embedDim; + this.ffDim = ffDim; + this.seqLen = seqLen; + this.temperature = 1.0; + + this.vocab = []; + this.tokenToIdx = {}; + this.idxToToken = {}; + + this.W_embed = null; + + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + + this.W_ff1 = []; + this.W_ff2 = []; + + this.finalOutputWeights = null; + + this.initialized = false; + this.learningRate = 0.01; + } + + randomMatrix(rows, cols) { + const m = []; + for(let i=0; i { + this.tokenToIdx[t] = i; + this.idxToToken[i] = t; + }); + const vocabSize = this.vocab.length; + + this.W_embed = this.randomMatrix(vocabSize, this.embedDim); + + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + this.W_ff1 = []; + this.W_ff2 = []; + + for(let i=0; i this.tokenToIdx[t] !== undefined ? this.tokenToIdx[t] : 0); + } + + detokenize(indices) { + return indices.map(i => this.idxToToken[i]).join(' '); + } + + matMulVec(mat, vec) { + const res = []; + for(let i=0; i Math.exp((x - max)/this.temperature)); + const sum = exps.reduce((a,b)=>a+b,0); + return exps.map(e => e/sum); + } + + relu(x) {return x>0 ? x : 0;} + + feedForward(input, layerIndex) { + const W1 = this.W_ff1[layerIndex]; + const W2 = this.W_ff2[layerIndex]; + const seqLen = input.length; + const ffDim = this.ffDim; + const embedDim = this.embedDim; + + let hidden = []; + for(let i=0; i this.relu(x)); + hidden.push(row); + } + + let output = []; + for(let i=0; i this.matMulVec(Wq, v)); + const K = emb.map(v => this.matMulVec(Wk, v)); + const V = emb.map(v => this.matMulVec(Wv, v)); + + const scores = []; + for(let i=0; i this.W_embed[i]); + for(let i=0; i setTimeout(r, 1000)); // Задержка 1 сек + } + } + + setTemperature(t) { + this.temperature = t; + } + + resetModel(numLayers, embedDim, ffDim, seqLen) { + this.numLayers = numLayers; + this.embedDim = embedDim; + this.ffDim = ffDim; + this.seqLen = seqLen; + this.initialized = false; + this.vocab = []; + this.tokenToIdx = {}; + this.idxToToken = {}; + this.W_embed = null; + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + this.W_ff1 = []; + this.W_ff2 = []; + this.finalOutputWeights = null; + } +} + +class MountainAIExtension { + constructor() { + this.model = new MountainAI(2, 16, 64, 8); + this.currentEpoch = 0; + } + + getInfo() { + return { + id: 'MountainAI', + name: 'MountainAI', + blocks: [ + { + opcode: 'train', + blockType: Scratch.BlockType.COMMAND, + text: 'train on text [TEXT], [EPOCHS] epochs', + arguments: { + TEXT: {type: Scratch.ArgumentType.STRING, defaultValue: 'any train text here'}, + EPOCHS: {type: Scratch.ArgumentType.NUMBER, defaultValue: 50}, + }, + }, + { + opcode: 'generate', + blockType: Scratch.BlockType.REPORTER, + text: 'generate from [PROMPT] with length [LENGTH]', + arguments: { + PROMPT: {type: Scratch.ArgumentType.STRING, defaultValue: 'He look in his eyes and '}, + LENGTH: {type: Scratch.ArgumentType.NUMBER, defaultValue: 50}, + }, + }, + { + opcode: 'setTemperature', + blockType: Scratch.BlockType.COMMAND, + text: 'set temperature [TEMP]', + arguments: { + TEMP: {type: Scratch.ArgumentType.NUMBER, defaultValue: 0.3}, + }, + }, + { + opcode: 'setNumLayers', + blockType: Scratch.BlockType.COMMAND, + text: 'Layer Count [NUM]', + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 2}, + }, + }, + { + opcode: 'setEmbedDim', + blockType: Scratch.BlockType.COMMAND, + text: 'embedding size [NUM]', + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 16}, + }, + }, + { + opcode: 'setFFDim', + blockType: Scratch.BlockType.COMMAND, + text: 'FFN size [NUM]', + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 64}, + }, + }, + { + opcode: 'setSeqLen', + blockType: Scratch.BlockType.COMMAND, + text: 'context size [NUM]', + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 16}, + }, + }, + { + opcode: 'getCurrentEpoch', + blockType: Scratch.BlockType.REPORTER, + text: 'current epoch', + }, + ], + }; + } + + async train({TEXT, EPOCHS}) { + this.currentEpoch = 0; + const onEpoch = (epoch) => { + this.currentEpoch = epoch; + }; + await this.model.train(TEXT, Number(EPOCHS), onEpoch); + this.currentEpoch = 0; + } + + generate({PROMPT, LENGTH}) { + return this.model.generate(PROMPT, Number(LENGTH)); + } + + setTemperature({TEMP}) { + this.model.setTemperature(Number(TEMP)); + } + + setNumLayers({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(n, this.model.embedDim, this.model.ffDim, this.model.seqLen); + } + + setEmbedDim({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(this.model.numLayers, n, this.model.ffDim, this.model.seqLen); + } + + setFFDim({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(this.model.numLayers, this.model.embedDim, n, this.model.seqLen); + } + + setSeqLen({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(this.model.numLayers, this.model.embedDim, this.model.ffDim, n); + } + + getCurrentEpoch() { + return this.currentEpoch; + } +} + +Scratch.extensions.register(new MountainAIExtension()); diff --git a/extensions/MountainAI-main/README.md b/extensions/MountainAI-main/README.md new file mode 100644 index 0000000000..e23cf991b6 --- /dev/null +++ b/extensions/MountainAI-main/README.md @@ -0,0 +1,71 @@ +## 🧠 MountainAI — GPT-like Neural Network for TurboWarp + +**MountainAI** is a simple GPT-style neural network extension for [TurboWarp](https://turbowarp.org), written entirely in JavaScript. +It allows users to **train** and **use** a text-generation model directly inside Scratch projects — no server or installation required. + +--- + +### ⚠️ Performance Notice + +**Training the model may cause the page to temporarily freeze**, especially on slower or older computers. +This is expected behavior — **the training is still running in the background**, and the browser will resume responsiveness when it's complete. +If you see a warning like *“This page is unresponsive”* — just wait, and don’t reload. + +--- + +### 🔧 Features + +* 🧠 Fully local training and inference +* 📄 Custom dataset support (text-based) +* ⚙️ Adjustable hyperparameters: + + * Number of transformer layers + * Feed-forward size + * Sequence length +* 📝 Text continuation instead of random generation +* 🪄 Easy to use through custom blocks + +--- + +### 📦 How to Use + +1. Open [TurboWarp Editor](https://turbowarp.org/editor?extension=https://ttstudio300.github.io/MountainAI/MountainAI.js) +2. Load the extension using this URL: + `https://ttstudio300.github.io/MountainAI/MountainAI.js` +3. Use the blocks from the “MountainAI” category to: + + * Set model parameters + * Load or input training data + * Train the model + * Generate text + +--- + +### 🧱 Example Blocks + +* `Set number of layers [3]` +* `Set sequence length [8]` +* `Train model` +* `Generate text from [start phrase] for [length] tokens` + +--- + +### 🔗 Links + +* 🌐 Live Extension URL: + `https://ttstudio300.github.io/MountainAI/MountainAI.js` +* 💾 GitHub Repository: + [github.com/ttstudio300/MountainAI](https://github.com/ttstudio300/MountainAI) +* 🧪 Try it instantly: + [TurboWarp + Extension](https://turbowarp.org/editor?extension=https://ttstudio300.github.io/MountainAI/MountainAI.js) + +--- + +### ✨ Author + +Made with care by **RadereDev** +Special thanks to GPT and open learning communities. + +--- + +Хочешь добавить ещё секцию о том, как устроена архитектура внутри? Или, например, гифку, показывающую, как оно работает? diff --git a/extensions/MountainAI-main/extension-info.json b/extensions/MountainAI-main/extension-info.json new file mode 100644 index 0000000000..adbf814141 --- /dev/null +++ b/extensions/MountainAI-main/extension-info.json @@ -0,0 +1,10 @@ +{ + "name": "MountainAI", + "description": "A simple GPT-like neural network that can be trained and used directly in TurboWarp using JavaScript.", + "icon": "icon.png", + "banner": "banner.png", + "tags": ["ai", "gpt", "machine learning", "neural network", "text generation"], + "developer": "RadereDev", + "extensionUrl": "https://ttstudio300.github.io/MountainAI/MountainAI.js", + "github": "https://github.com/ttstudio300/MountainAI" +} From 2bc1064ebcc693e0a8b559f1d5ace125f2f2d3cd Mon Sep 17 00:00:00 2001 From: ttstudio300 <143816235+ttstudio300@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:21:16 +0300 Subject: [PATCH 2/7] Delete extensions/MountainAI-main/MountainAI.js --- extensions/MountainAI-main/MountainAI.js | 413 ----------------------- 1 file changed, 413 deletions(-) delete mode 100644 extensions/MountainAI-main/MountainAI.js diff --git a/extensions/MountainAI-main/MountainAI.js b/extensions/MountainAI-main/MountainAI.js deleted file mode 100644 index 2a46cf592e..0000000000 --- a/extensions/MountainAI-main/MountainAI.js +++ /dev/null @@ -1,413 +0,0 @@ -// Name: MountainAI -// ID: MountainAI -// Description: Create and run your own GPT like ai models localy! -// By: RadereDev -// Original: RadereDev -// License: MPL-2.0 - -class MountainAI { - //MountainAI Build-15 public beta - constructor(numLayers=2, embedDim=16, ffDim=64, seqLen=8) { - this.numLayers = numLayers; - this.embedDim = embedDim; - this.ffDim = ffDim; - this.seqLen = seqLen; - this.temperature = 1.0; - - this.vocab = []; - this.tokenToIdx = {}; - this.idxToToken = {}; - - this.W_embed = null; - - this.W_q = []; - this.W_k = []; - this.W_v = []; - this.W_out = []; - - this.W_ff1 = []; - this.W_ff2 = []; - - this.finalOutputWeights = null; - - this.initialized = false; - this.learningRate = 0.01; - } - - randomMatrix(rows, cols) { - const m = []; - for(let i=0; i { - this.tokenToIdx[t] = i; - this.idxToToken[i] = t; - }); - const vocabSize = this.vocab.length; - - this.W_embed = this.randomMatrix(vocabSize, this.embedDim); - - this.W_q = []; - this.W_k = []; - this.W_v = []; - this.W_out = []; - this.W_ff1 = []; - this.W_ff2 = []; - - for(let i=0; i this.tokenToIdx[t] !== undefined ? this.tokenToIdx[t] : 0); - } - - detokenize(indices) { - return indices.map(i => this.idxToToken[i]).join(' '); - } - - matMulVec(mat, vec) { - const res = []; - for(let i=0; i Math.exp((x - max)/this.temperature)); - const sum = exps.reduce((a,b)=>a+b,0); - return exps.map(e => e/sum); - } - - relu(x) {return x>0 ? x : 0;} - - feedForward(input, layerIndex) { - const W1 = this.W_ff1[layerIndex]; - const W2 = this.W_ff2[layerIndex]; - const seqLen = input.length; - const ffDim = this.ffDim; - const embedDim = this.embedDim; - - let hidden = []; - for(let i=0; i this.relu(x)); - hidden.push(row); - } - - let output = []; - for(let i=0; i this.matMulVec(Wq, v)); - const K = emb.map(v => this.matMulVec(Wk, v)); - const V = emb.map(v => this.matMulVec(Wv, v)); - - const scores = []; - for(let i=0; i this.W_embed[i]); - for(let i=0; i setTimeout(r, 1000)); // Задержка 1 сек - } - } - - setTemperature(t) { - this.temperature = t; - } - - resetModel(numLayers, embedDim, ffDim, seqLen) { - this.numLayers = numLayers; - this.embedDim = embedDim; - this.ffDim = ffDim; - this.seqLen = seqLen; - this.initialized = false; - this.vocab = []; - this.tokenToIdx = {}; - this.idxToToken = {}; - this.W_embed = null; - this.W_q = []; - this.W_k = []; - this.W_v = []; - this.W_out = []; - this.W_ff1 = []; - this.W_ff2 = []; - this.finalOutputWeights = null; - } -} - -class MountainAIExtension { - constructor() { - this.model = new MountainAI(2, 16, 64, 8); - this.currentEpoch = 0; - } - - getInfo() { - return { - id: 'MountainAI', - name: 'MountainAI', - blocks: [ - { - opcode: 'train', - blockType: Scratch.BlockType.COMMAND, - text: 'train on text [TEXT], [EPOCHS] epochs', - arguments: { - TEXT: {type: Scratch.ArgumentType.STRING, defaultValue: 'any train text here'}, - EPOCHS: {type: Scratch.ArgumentType.NUMBER, defaultValue: 50}, - }, - }, - { - opcode: 'generate', - blockType: Scratch.BlockType.REPORTER, - text: 'generate from [PROMPT] with length [LENGTH]', - arguments: { - PROMPT: {type: Scratch.ArgumentType.STRING, defaultValue: 'He look in his eyes and '}, - LENGTH: {type: Scratch.ArgumentType.NUMBER, defaultValue: 50}, - }, - }, - { - opcode: 'setTemperature', - blockType: Scratch.BlockType.COMMAND, - text: 'set temperature [TEMP]', - arguments: { - TEMP: {type: Scratch.ArgumentType.NUMBER, defaultValue: 0.3}, - }, - }, - { - opcode: 'setNumLayers', - blockType: Scratch.BlockType.COMMAND, - text: 'Layer Count [NUM]', - arguments: { - NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 2}, - }, - }, - { - opcode: 'setEmbedDim', - blockType: Scratch.BlockType.COMMAND, - text: 'embedding size [NUM]', - arguments: { - NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 16}, - }, - }, - { - opcode: 'setFFDim', - blockType: Scratch.BlockType.COMMAND, - text: 'FFN size [NUM]', - arguments: { - NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 64}, - }, - }, - { - opcode: 'setSeqLen', - blockType: Scratch.BlockType.COMMAND, - text: 'context size [NUM]', - arguments: { - NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 16}, - }, - }, - { - opcode: 'getCurrentEpoch', - blockType: Scratch.BlockType.REPORTER, - text: 'current epoch', - }, - ], - }; - } - - async train({TEXT, EPOCHS}) { - this.currentEpoch = 0; - const onEpoch = (epoch) => { - this.currentEpoch = epoch; - }; - await this.model.train(TEXT, Number(EPOCHS), onEpoch); - this.currentEpoch = 0; - } - - generate({PROMPT, LENGTH}) { - return this.model.generate(PROMPT, Number(LENGTH)); - } - - setTemperature({TEMP}) { - this.model.setTemperature(Number(TEMP)); - } - - setNumLayers({NUM}) { - const n = Math.max(1, Math.floor(NUM)); - this.model.resetModel(n, this.model.embedDim, this.model.ffDim, this.model.seqLen); - } - - setEmbedDim({NUM}) { - const n = Math.max(1, Math.floor(NUM)); - this.model.resetModel(this.model.numLayers, n, this.model.ffDim, this.model.seqLen); - } - - setFFDim({NUM}) { - const n = Math.max(1, Math.floor(NUM)); - this.model.resetModel(this.model.numLayers, this.model.embedDim, n, this.model.seqLen); - } - - setSeqLen({NUM}) { - const n = Math.max(1, Math.floor(NUM)); - this.model.resetModel(this.model.numLayers, this.model.embedDim, this.model.ffDim, n); - } - - getCurrentEpoch() { - return this.currentEpoch; - } -} - -Scratch.extensions.register(new MountainAIExtension()); From 0f7e097577a9ed57e7e9787cabef81fa779f9771 Mon Sep 17 00:00:00 2001 From: ttstudio300 <143816235+ttstudio300@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:21:44 +0300 Subject: [PATCH 3/7] add MountainAI extension --- extensions/MountainAI-main/Mountain-AI.js | 408 ++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 extensions/MountainAI-main/Mountain-AI.js diff --git a/extensions/MountainAI-main/Mountain-AI.js b/extensions/MountainAI-main/Mountain-AI.js new file mode 100644 index 0000000000..5dbe3b31cd --- /dev/null +++ b/extensions/MountainAI-main/Mountain-AI.js @@ -0,0 +1,408 @@ +(function(Scratch) { +'use strict'; + +class MountainAI { + // MountainAI Build-15 public beta + constructor(numLayers=2, embedDim=16, ffDim=64, seqLen=8) { + this.numLayers = numLayers; + this.embedDim = embedDim; + this.ffDim = ffDim; + this.seqLen = seqLen; + this.temperature = 1.0; + + this.vocab = []; + this.tokenToIdx = {}; + this.idxToToken = {}; + + this.W_embed = null; + + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + + this.W_ff1 = []; + this.W_ff2 = []; + + this.finalOutputWeights = null; + + this.initialized = false; + this.learningRate = 0.01; + } + + randomMatrix(rows, cols) { + const m = []; + for(let i=0; i { + this.tokenToIdx[t] = i; + this.idxToToken[i] = t; + }); + const vocabSize = this.vocab.length; + + this.W_embed = this.randomMatrix(vocabSize, this.embedDim); + + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + this.W_ff1 = []; + this.W_ff2 = []; + + for(let i=0; i this.tokenToIdx[t] !== undefined ? this.tokenToIdx[t] : 0); + } + + detokenize(indices) { + return indices.map(i => this.idxToToken[i]).join(' '); + } + + matMulVec(mat, vec) { + const res = []; + for(let i=0; i Math.exp((x - max)/this.temperature)); + const sum = exps.reduce((a,b)=>a+b,0); + return exps.map(e => e/sum); + } + + relu(x) {return x>0 ? x : 0;} + + feedForward(input, layerIndex) { + const W1 = this.W_ff1[layerIndex]; + const W2 = this.W_ff2[layerIndex]; + const seqLen = input.length; + + let hidden = []; + for(let i=0; i this.relu(x)); + hidden.push(row); + } + + let output = []; + for(let i=0; i this.matMulVec(Wq, v)); + const K = emb.map(v => this.matMulVec(Wk, v)); + const V = emb.map(v => this.matMulVec(Wv, v)); + + const scores = []; + for(let i=0; i this.W_embed[i]); + for(let i=0; i setTimeout(r, 1000)); // Delay 1 sec + } + } + + setTemperature(t) { + this.temperature = t; + } + + resetModel(numLayers, embedDim, ffDim, seqLen) { + this.numLayers = numLayers; + this.embedDim = embedDim; + this.ffDim = ffDim; + this.seqLen = seqLen; + this.initialized = false; + this.vocab = []; + this.tokenToIdx = {}; + this.idxToToken = {}; + this.W_embed = null; + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + this.W_ff1 = []; + this.W_ff2 = []; + this.finalOutputWeights = null; + } +} + +class MountainAIExtension { + constructor() { + this.model = new MountainAI(2, 16, 64, 8); + this.currentEpoch = 0; + } + + getInfo() { + return { + id: 'MountainAI', + name: Scratch.translate('MountainAI'), + blocks: [ + { + opcode: 'train', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('train on text [TEXT], [EPOCHS] epochs'), + arguments: { + TEXT: {type: Scratch.ArgumentType.STRING, defaultValue: 'any train text here'}, + EPOCHS: {type: Scratch.ArgumentType.NUMBER, defaultValue: 50}, + }, + }, + { + opcode: 'generate', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('generate from [PROMPT] with length [LENGTH]'), + arguments: { + PROMPT: {type: Scratch.ArgumentType.STRING, defaultValue: 'He look in his eyes and '}, + LENGTH: {type: Scratch.ArgumentType.NUMBER, defaultValue: 50}, + }, + }, + { + opcode: 'setTemperature', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('set temperature [TEMP]'), + arguments: { + TEMP: {type: Scratch.ArgumentType.NUMBER, defaultValue: 0.3}, + }, + }, + { + opcode: 'setNumLayers', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('Layer Count [NUM]'), + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 2}, + }, + }, + { + opcode: 'setEmbedDim', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('embedding size [NUM]'), + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 16}, + }, + }, + { + opcode: 'setFFDim', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('FFN size [NUM]'), + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 64}, + }, + }, + { + opcode: 'setSeqLen', + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate('context size [NUM]'), + arguments: { + NUM: {type: Scratch.ArgumentType.NUMBER, defaultValue: 16}, + }, + }, + { + opcode: 'getCurrentEpoch', + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate('current epoch'), + }, + ], + }; + } + + async train({TEXT, EPOCHS}) { + this.currentEpoch = 0; + const onEpoch = (epoch) => { + this.currentEpoch = epoch; + }; + await this.model.train(TEXT, Number(EPOCHS), onEpoch); + this.currentEpoch = 0; + } + + generate({PROMPT, LENGTH}) { + return this.model.generate(PROMPT, Number(LENGTH)); + } + + setTemperature({TEMP}) { + this.model.setTemperature(Number(TEMP)); + } + + setNumLayers({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(n, this.model.embedDim, this.model.ffDim, this.model.seqLen); + } + + setEmbedDim({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(this.model.numLayers, n, this.model.ffDim, this.model.seqLen); + } + + setFFDim({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(this.model.numLayers, this.model.embedDim, n, this.model.seqLen); + } + + setSeqLen({NUM}) { + const n = Math.max(1, Math.floor(NUM)); + this.model.resetModel(this.model.numLayers, this.model.embedDim, this.model.ffDim, n); + } + + getCurrentEpoch() { + return this.currentEpoch; + } +} + +Scratch.extensions.register(new MountainAIExtension()); +})(Scratch); \ No newline at end of file From e3fcedacc6ee21e881e31e29e49ce039531f551f Mon Sep 17 00:00:00 2001 From: ttstudio300 <143816235+ttstudio300@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:39:38 +0300 Subject: [PATCH 4/7] Delete extensions/MountainAI-main/Logo.svg --- extensions/MountainAI-main/Logo.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 extensions/MountainAI-main/Logo.svg diff --git a/extensions/MountainAI-main/Logo.svg b/extensions/MountainAI-main/Logo.svg deleted file mode 100644 index a423ab02db..0000000000 --- a/extensions/MountainAI-main/Logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file From fc2fe6b21d58b8c1cd1d686658a9aa23bd87d61b Mon Sep 17 00:00:00 2001 From: ttstudio300 <143816235+ttstudio300@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:40:21 +0300 Subject: [PATCH 5/7] Delete extensions/MountainAI-main/Banner.svg --- extensions/MountainAI-main/Banner.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 extensions/MountainAI-main/Banner.svg diff --git a/extensions/MountainAI-main/Banner.svg b/extensions/MountainAI-main/Banner.svg deleted file mode 100644 index a4b6310801..0000000000 --- a/extensions/MountainAI-main/Banner.svg +++ /dev/null @@ -1 +0,0 @@ -111010011010001100101100000111011111011111110010111001111101001000001100001110100110000011000011100100110010011011111101110100000110100110000011010001100001111011011001011000001100101111011011001011110010100000111001111001011100101110111010111010000011010011000001101101110010111000011101110101110100000111001011001011100001110110011011001111001 \ No newline at end of file From 5f10d412ef9b58e6e7a7d14abd8668c9c3a58ff7 Mon Sep 17 00:00:00 2001 From: ttstudio300 <143816235+ttstudio300@users.noreply.github.com> Date: Sat, 2 Aug 2025 23:40:39 +0300 Subject: [PATCH 6/7] Add files via upload --- extensions/MountainAI-main/Banner.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 extensions/MountainAI-main/Banner.svg diff --git a/extensions/MountainAI-main/Banner.svg b/extensions/MountainAI-main/Banner.svg new file mode 100644 index 0000000000..ed560ee39b --- /dev/null +++ b/extensions/MountainAI-main/Banner.svg @@ -0,0 +1 @@ +111010011010001100101100000111011111011111110010111001111101001000001100001110100110000011000011100100110010011011111101110100000110100110000011010001100001111011011001011000001100101111011011001011110010100000111001111001011100101110111010111010000011010011000001101101110010111000011101110101110100000111001011001011100001110110011011001111001 \ No newline at end of file From b4ae334d007242525fde04475e7576bc3db5f8e2 Mon Sep 17 00:00:00 2001 From: "DangoCat[bot]" Date: Fri, 12 Sep 2025 21:56:53 +0000 Subject: [PATCH 7/7] [Automated] Format code --- extensions/MountainAI-main/Mountain-AI.js | 704 ++++++++++++---------- 1 file changed, 370 insertions(+), 334 deletions(-) diff --git a/extensions/MountainAI-main/Mountain-AI.js b/extensions/MountainAI-main/Mountain-AI.js index 5dbe3b31cd..b74b8f1406 100644 --- a/extensions/MountainAI-main/Mountain-AI.js +++ b/extensions/MountainAI-main/Mountain-AI.js @@ -1,408 +1,444 @@ -(function(Scratch) { -'use strict'; +(function (Scratch) { + "use strict"; -class MountainAI { - // MountainAI Build-15 public beta - constructor(numLayers=2, embedDim=16, ffDim=64, seqLen=8) { - this.numLayers = numLayers; - this.embedDim = embedDim; - this.ffDim = ffDim; - this.seqLen = seqLen; - this.temperature = 1.0; + class MountainAI { + // MountainAI Build-15 public beta + constructor(numLayers = 2, embedDim = 16, ffDim = 64, seqLen = 8) { + this.numLayers = numLayers; + this.embedDim = embedDim; + this.ffDim = ffDim; + this.seqLen = seqLen; + this.temperature = 1.0; - this.vocab = []; - this.tokenToIdx = {}; - this.idxToToken = {}; + this.vocab = []; + this.tokenToIdx = {}; + this.idxToToken = {}; - this.W_embed = null; + this.W_embed = null; - this.W_q = []; - this.W_k = []; - this.W_v = []; - this.W_out = []; + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; - this.W_ff1 = []; - this.W_ff2 = []; + this.W_ff1 = []; + this.W_ff2 = []; - this.finalOutputWeights = null; + this.finalOutputWeights = null; - this.initialized = false; - this.learningRate = 0.01; - } + this.initialized = false; + this.learningRate = 0.01; + } - randomMatrix(rows, cols) { - const m = []; - for(let i=0; i { - this.tokenToIdx[t] = i; - this.idxToToken[i] = t; - }); - const vocabSize = this.vocab.length; - - this.W_embed = this.randomMatrix(vocabSize, this.embedDim); - - this.W_q = []; - this.W_k = []; - this.W_v = []; - this.W_out = []; - this.W_ff1 = []; - this.W_ff2 = []; - - for(let i=0; i { + this.tokenToIdx[t] = i; + this.idxToToken[i] = t; + }); + const vocabSize = this.vocab.length; + + this.W_embed = this.randomMatrix(vocabSize, this.embedDim); + + this.W_q = []; + this.W_k = []; + this.W_v = []; + this.W_out = []; + this.W_ff1 = []; + this.W_ff2 = []; + + for (let i = 0; i < this.numLayers; i++) { + this.W_q.push(this.randomMatrix(this.embedDim, this.embedDim)); + this.W_k.push(this.randomMatrix(this.embedDim, this.embedDim)); + this.W_v.push(this.randomMatrix(this.embedDim, this.embedDim)); + this.W_out.push(this.randomMatrix(this.embedDim, this.embedDim)); + this.W_ff1.push(this.randomMatrix(this.embedDim, this.ffDim)); + this.W_ff2.push(this.randomMatrix(this.ffDim, this.embedDim)); + } - this.finalOutputWeights = this.randomMatrix(this.embedDim, vocabSize); - this.initialized = true; - } + this.finalOutputWeights = this.randomMatrix(this.embedDim, vocabSize); + this.initialized = true; + } - tokenize(text) { - const tokens = text.toLowerCase().split(/\s+/).filter(Boolean); - return tokens.map(t => this.tokenToIdx[t] !== undefined ? this.tokenToIdx[t] : 0); - } + tokenize(text) { + const tokens = text.toLowerCase().split(/\s+/).filter(Boolean); + return tokens.map((t) => + this.tokenToIdx[t] !== undefined ? this.tokenToIdx[t] : 0 + ); + } - detokenize(indices) { - return indices.map(i => this.idxToToken[i]).join(' '); - } + detokenize(indices) { + return indices.map((i) => this.idxToToken[i]).join(" "); + } - matMulVec(mat, vec) { - const res = []; - for(let i=0; i Math.exp((x - max)/this.temperature)); - const sum = exps.reduce((a,b)=>a+b,0); - return exps.map(e => e/sum); - } - - relu(x) {return x>0 ? x : 0;} - - feedForward(input, layerIndex) { - const W1 = this.W_ff1[layerIndex]; - const W2 = this.W_ff2[layerIndex]; - const seqLen = input.length; - - let hidden = []; - for(let i=0; i this.relu(x)); - hidden.push(row); + softmax(arr) { + const max = Math.max(...arr); + const exps = arr.map((x) => Math.exp((x - max) / this.temperature)); + const sum = exps.reduce((a, b) => a + b, 0); + return exps.map((e) => e / sum); } - let output = []; - for(let i=0; i 0 ? x : 0; } - return output; - } + feedForward(input, layerIndex) { + const W1 = this.W_ff1[layerIndex]; + const W2 = this.W_ff2[layerIndex]; + const seqLen = input.length; - transformerLayer(emb, layerIndex) { - const Wq = this.W_q[layerIndex]; - const Wk = this.W_k[layerIndex]; - const Wv = this.W_v[layerIndex]; - const Wout = this.W_out[layerIndex]; - - const seqLen = emb.length; - const embedDim = this.embedDim; - - const Q = emb.map(v => this.matMulVec(Wq, v)); - const K = emb.map(v => this.matMulVec(Wk, v)); - const V = emb.map(v => this.matMulVec(Wv, v)); - - const scores = []; - for(let i=0; i