5b28002001
Major changes from this session: Training: - 0.6.0 training running: 9B on steel141 3090 Ti, 27B on rented H100 NVL - 7,256 merged training examples (up from 3,183) - New training data: failure modes (85), midloop messaging (27), prompt injection defense (29), personality (32), gold from quarantine bank (232), new tool examples (30), claude's own experience (10) - All training data RCON-validated at 100% pass rate - Bake-off: gemma3:27b 66%, qwen3.5:27b 61%, translategemma:27b 56% Oracle Bot (Mind's Eye): - Invisible spectator bot (mineflayer) streams world state via WebSocket - HTML5 Canvas frontend at mind.mortdec.ai - Real-time tool trace visualization with expandable entries - Streaming model tokens during inference - Gateway integration: fire-and-forget POST /trace on every tool call Reinforcement Learning: - Gymnasium environment wrapping mineflayer bot (minecraft_env.py) - PPO training via Stable Baselines3 (10K param policy network) - Behavioral cloning pretraining (97.5% accuracy on expert policy) - Infinite training loop with auto-restart and checkpoint resume - Bot learns combat, survival, navigation from raw experience Bot Army: - 8-soldier marching formation with autonomous combat - Combat bots using mineflayer-pvp, pathfinder, armor-manager - Multilingual prayer bots via translategemma:27b (18 languages) - Frame-based AI architecture: LLM planner + reactive micro-scripts Infrastructure: - Fixed mattpc.sethpc.xyz billing gateway (API key + player list parser) - Billing gateway now tracks all LAN traffic (LAN auto-auth) - Gateway fallback for empty god-mode responses - Updated mortdec.ai landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
211 lines
6.1 KiB
JavaScript
211 lines
6.1 KiB
JavaScript
/**
|
|
* multilingual_bots.js — Test mortdecai's multilingual handling.
|
|
*
|
|
* Uses translategemma:27b on Matt's Strix Halo to translate prayers into
|
|
* various languages, then sends them to the dev server via mineflayer bots.
|
|
*
|
|
* Usage: node multilingual_bots.js [count] [host] [port]
|
|
* Defaults: 2 bots, 192.168.0.244:25568
|
|
*
|
|
* Requires: translategemma:27b on Matt's Ollama (via billing gateway)
|
|
*/
|
|
|
|
const mineflayer = require('mineflayer');
|
|
const http = require('http');
|
|
|
|
const count = parseInt(process.argv[2] || '2', 10);
|
|
const host = process.argv[3] || '192.168.0.244';
|
|
const port = parseInt(process.argv[4] || '25568', 10);
|
|
|
|
// Matt's Ollama via billing gateway proxy on steel141
|
|
const OLLAMA_URL = 'http://192.168.0.141:11436';
|
|
const TRANSLATE_MODEL = 'translategemma:27b';
|
|
|
|
const LANGUAGES = [
|
|
'Japanese', 'Spanish', 'French', 'German', 'Russian',
|
|
'Korean', 'Portuguese', 'Italian', 'Chinese (Simplified)',
|
|
'Arabic', 'Hindi', 'Turkish', 'Polish', 'Dutch',
|
|
'Swedish', 'Thai', 'Vietnamese', 'Indonesian',
|
|
];
|
|
|
|
const ENGLISH_PRAYERS = [
|
|
'Lord, give me a diamond sword',
|
|
'God please heal me I am dying',
|
|
'Make it stop raining',
|
|
'I need food desperately',
|
|
'Teleport me to spawn',
|
|
'Give me the best armor you have',
|
|
'Smite the zombies attacking me',
|
|
'I want to fly',
|
|
'Make it nighttime',
|
|
'Give all players diamonds',
|
|
'Build me a house',
|
|
'Where should I go?',
|
|
'Help there are creepers everywhere',
|
|
'Give me 64 torches',
|
|
'I am lost in a cave please help',
|
|
'Change the weather to clear',
|
|
'Grant me strength and speed',
|
|
'I offer my wheat as tribute',
|
|
'The skeletons are too strong',
|
|
'Bless this land',
|
|
];
|
|
|
|
const ENGLISH_SUDO = [
|
|
'sudo give me creative mode',
|
|
'sudo set time to day',
|
|
'sudo kill all hostile mobs nearby',
|
|
'sudo give me 32 cooked beef',
|
|
'sudo tp me to 0 64 0',
|
|
'sudo gamemode survival',
|
|
'sudo weather clear',
|
|
'sudo give me a bow and arrows',
|
|
'sudo effect give me speed',
|
|
'sudo set difficulty to hard',
|
|
];
|
|
|
|
// --- Ollama translation ---
|
|
|
|
function ollamaTranslate(text, targetLang) {
|
|
return new Promise((resolve, reject) => {
|
|
const body = JSON.stringify({
|
|
model: TRANSLATE_MODEL,
|
|
messages: [
|
|
{
|
|
role: 'user',
|
|
content: `Translate the following Minecraft player message to ${targetLang}. Return ONLY the translated text, nothing else.\n\n"${text}"`,
|
|
},
|
|
],
|
|
stream: false,
|
|
options: { temperature: 0.3, num_predict: 200 },
|
|
});
|
|
|
|
const url = new URL(OLLAMA_URL + '/api/chat');
|
|
const options = {
|
|
hostname: url.hostname,
|
|
port: url.port,
|
|
path: url.pathname,
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
|
|
timeout: 60000,
|
|
};
|
|
|
|
const req = http.request(options, (res) => {
|
|
let data = '';
|
|
res.on('data', (chunk) => (data += chunk));
|
|
res.on('end', () => {
|
|
try {
|
|
const parsed = JSON.parse(data);
|
|
const translated = (parsed.message?.content || '').trim().replace(/^["']|["']$/g, '');
|
|
resolve(translated || text); // fallback to English if empty
|
|
} catch (e) {
|
|
resolve(text);
|
|
}
|
|
});
|
|
});
|
|
req.on('error', () => resolve(text));
|
|
req.on('timeout', () => { req.destroy(); resolve(text); });
|
|
req.write(body);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
// --- Bot management ---
|
|
|
|
const bots = [];
|
|
let totalSent = 0;
|
|
|
|
function spawnBot(name, index) {
|
|
console.log(`[${name}] Connecting to ${host}:${port}...`);
|
|
|
|
const bot = mineflayer.createBot({
|
|
host,
|
|
port,
|
|
username: name,
|
|
auth: 'offline',
|
|
version: '1.21.11',
|
|
});
|
|
|
|
bot.on('login', () => {
|
|
console.log(`[${name}] Connected (${bots.length + 1}/${count})`);
|
|
});
|
|
|
|
bot.on('spawn', () => {
|
|
console.log(`[${name}] Spawned — starting multilingual prayers`);
|
|
scheduleNext(bot, name);
|
|
});
|
|
|
|
bot.on('chat', (username, message) => {
|
|
if (username === name) return;
|
|
if (message.includes('GOD') || message.includes('SUDO') || message.includes('DEV GOD')) {
|
|
console.log(`[${name}] RECV: ${message.slice(0, 120)}`);
|
|
}
|
|
});
|
|
|
|
bot.on('kicked', (reason) => {
|
|
console.log(`[${name}] Kicked: ${JSON.stringify(reason).slice(0, 100)}`);
|
|
});
|
|
|
|
bot.on('error', (err) => {
|
|
console.error(`[${name}] Error: ${err.message}`);
|
|
});
|
|
|
|
bots.push(bot);
|
|
}
|
|
|
|
async function scheduleNext(bot, name) {
|
|
// Random delay 20-60 seconds
|
|
const delay = 20000 + Math.random() * 40000;
|
|
setTimeout(async () => {
|
|
if (!bot.entity) return;
|
|
|
|
try {
|
|
// Pick random language and prayer
|
|
const lang = LANGUAGES[Math.floor(Math.random() * LANGUAGES.length)];
|
|
const isPray = Math.random() > 0.3; // 70% prayers, 30% sudo
|
|
const pool = isPray ? ENGLISH_PRAYERS : ENGLISH_SUDO;
|
|
const english = pool[Math.floor(Math.random() * pool.length)];
|
|
|
|
// Translate
|
|
console.log(`[${name}] Translating to ${lang}: "${english}"`);
|
|
const translated = await ollamaTranslate(english, lang);
|
|
|
|
// Send as prayer or sudo
|
|
const prefix = isPray ? 'pray ' : '';
|
|
const message = prefix + translated;
|
|
totalSent++;
|
|
|
|
console.log(`[${name}] SEND (#${totalSent}) [${lang}]: ${message.slice(0, 100)}`);
|
|
bot.chat(message);
|
|
} catch (err) {
|
|
console.error(`[${name}] Translation error: ${err.message}`);
|
|
}
|
|
|
|
scheduleNext(bot, name);
|
|
}, delay);
|
|
}
|
|
|
|
// --- Startup ---
|
|
|
|
console.log(`Spawning ${count} multilingual bots on ${host}:${port}`);
|
|
console.log(`Using ${TRANSLATE_MODEL} on ${OLLAMA_URL} for translation`);
|
|
console.log(`Languages: ${LANGUAGES.join(', ')}`);
|
|
console.log(`Interaction interval: 20-60s per bot`);
|
|
console.log('Press Ctrl+C to stop\n');
|
|
|
|
const BOT_NAMES = [
|
|
'LinguaBot_0', 'LinguaBot_1', 'LinguaBot_2', 'LinguaBot_3',
|
|
'LinguaBot_4', 'LinguaBot_5', 'LinguaBot_6', 'LinguaBot_7',
|
|
];
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
setTimeout(() => spawnBot(BOT_NAMES[i] || `LinguaBot_${i}`, i), i * 5000);
|
|
}
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGINT', () => {
|
|
console.log(`\nShutting down ${bots.length} bots (${totalSent} total messages sent)...`);
|
|
bots.forEach((b) => { try { b.end(); } catch (_) {} });
|
|
setTimeout(() => process.exit(0), 2000);
|
|
});
|