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 @@
+
\ 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 @@
-
\ 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 @@
+
\ 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 this.relu(x));
+ hidden.push(row);
}
- }
- 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 < seqLen; i++) {
+ scores[i] = [];
+ for (let j = 0; j < seqLen; j++) {
+ let s = 0;
+ for (let d = 0; d < embedDim; d++) {
+ s += Q[i][d] * K[j][d];
+ }
+ scores[i][j] = s / Math.sqrt(embedDim);
+ }
+ }
- forward(seq) {
- while(seq.length < this.seqLen) seq.unshift(0);
- seq = seq.slice(-this.seqLen);
+ for (let i = 0; i < seqLen; i++) {
+ scores[i] = this.softmax(scores[i]);
+ }
- let out = seq.map(i => this.W_embed[i]);
- for(let i=0; i this.W_embed[i]);
+ for (let i = 0; i < this.numLayers; i++) {
+ out = this.transformerLayer(out, i);
+ out = this.feedForward(out, i);
+ }
- generate(prefix, length=10) {
- if(!this.initialized) return 'Model not initialized';
+ const last = out[out.length - 1];
+ const logits = [];
+ for (let i = 0; i < this.vocab.length; i++) {
+ let s = 0;
+ for (let d = 0; d < this.embedDim; d++) {
+ s += last[d] * this.finalOutputWeights[d][i];
+ }
+ logits[i] = s;
+ }
- let tokens = this.tokenize(prefix);
- while(tokens.length < this.seqLen){
- tokens.unshift(0);
+ return logits;
}
- tokens = tokens.slice(-this.seqLen);
-
- const generated = [...tokens];
- for(let i=0; i setTimeout(r, 1000)); // Delay 1 sec
}
- await new Promise(r => setTimeout(r, 1000)); // Delay 1 sec
}
- }
- setTemperature(t) {
- this.temperature = t;
- }
+ 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;
+ 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;
- }
+ 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},
+ 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: "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: "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: "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: "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: "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: "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'),
- },
- ],
- };
- }
+ {
+ 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;
- }
+ 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));
- }
+ generate({ PROMPT, LENGTH }) {
+ return this.model.generate(PROMPT, Number(LENGTH));
+ }
- setTemperature({TEMP}) {
- this.model.setTemperature(Number(TEMP));
- }
+ 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);
- }
+ 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);
- }
+ 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);
- }
+ 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);
- }
+ 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;
+ getCurrentEpoch() {
+ return this.currentEpoch;
+ }
}
-}
-Scratch.extensions.register(new MountainAIExtension());
-})(Scratch);
\ No newline at end of file
+ Scratch.extensions.register(new MountainAIExtension());
+})(Scratch);