da8f557219
GPU Scheduler (gpu.sethpc.xyz): - Live dashboard with 4 GPUs, training monitor, loss sparklines - Preset-based job scheduler with 3 triggers (time, finish_training, cost) - Model selection per GPU, pipeline configuration - Tool self-play and training pipeline types - Behind Google OAuth, live-refresh without page reload Tool Architecture (14 tools): - 3 new tools: world.nearby_entities, memory.read, memory.write - 7 script.* tools: write, validate, execute, read, list, delete, schedule - ScriptManager: full mcfunction datapack CRUD with RCON validation - Training data: 1,430 tool examples (up from 1,159) Plugin Deployment (paper-ai-25567): - WorldGuard 7.0.12, CoreProtect CE 23.1, EssentialsX 2.21.2, Vault 1.7.3 - Fresh greenfield world reset - 104 RCON-validated plugin training examples Event Dispatcher: - Watches server log for deaths, joins, advancements, PvP kills - Configurable trigger probability and cooldowns per event type - Deployed to dev server, fires god_system prompts on events - 21 event-response training examples Training Infrastructure: - train_lora.py: --save-steps 50, --resume from checkpoint - run_training.sh: stops Ollama, activates conda, restarts after - Passwordless sudo for ollama services on steel141 - Dev server added to MCSManager with autoStart Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
662 lines
32 KiB
Python
662 lines
32 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate high-quality tool-calling training data with REAL RCON responses.
|
|
|
|
No AI involved — we craft the prompts, commands, and tool sequences by hand,
|
|
then execute each command through RCON to get actual server responses.
|
|
This produces gold-standard training data with real validation.
|
|
|
|
Generates examples across all 14 tools with proper multi-turn conversations:
|
|
- script.validate → script.write → script.execute (with real RCON validation)
|
|
- memory.read → rcon.execute (tp to saved location)
|
|
- world.nearby_entities → rcon.execute (kill scanned mobs)
|
|
- wiki_lookup → rcon.execute (apply looked-up knowledge)
|
|
- chained multi-tool sequences
|
|
|
|
Usage:
|
|
python3 generate_rcon_validated_training.py --rcon-host 192.168.0.244 --rcon-port 25577
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import random
|
|
import re
|
|
import socket
|
|
import struct
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from agent.tools.tool_schemas import qwen3_tools_block
|
|
from agent.prompts.system_prompts import SYNTAX_RULES, RISK_GRADIENT
|
|
|
|
OUTPUT_PATH = PROJECT_ROOT / "data" / "raw" / "rcon_validated_tool_training.jsonl"
|
|
|
|
TOOLS_BLOCK = qwen3_tools_block()
|
|
SYSTEM_SUDO = (
|
|
"You are a Minecraft 1.21 command translator with full tool access.\n"
|
|
"Tools: rcon.execute, minecraft.wiki_lookup, world.player_info, world.server_state, "
|
|
"world.nearby_entities, memory.read, memory.write, script.write, script.validate, "
|
|
"script.execute, script.read, script.list, script.delete, script.schedule.\n\n"
|
|
"For complex builds (4+ commands), write mcfunction scripts. Validate before writing.\n"
|
|
"PERMISSION LEVEL: 4 (generous).\n\n"
|
|
"Return JSON: {\"risk_level\": <0-5>, \"commands\": [...], \"reasoning\": \"...\"}\n\n"
|
|
+ SYNTAX_RULES + RISK_GRADIENT + "\n" + TOOLS_BLOCK
|
|
)
|
|
|
|
SYSTEM_GOD = (
|
|
"You are God in a Minecraft server. You have full tool access.\n"
|
|
"Return JSON: {\"risk_level\": <0-5>, \"message\": \"...\", \"commands\": [...], \"reasoning\": \"...\"}\n\n"
|
|
+ SYNTAX_RULES + "\n" + TOOLS_BLOCK
|
|
)
|
|
|
|
PLAYERS = ["slingshooter08", "Ace13245", "TheBigBoss", "xXDragonSlayerXx"]
|
|
|
|
|
|
from agent.tools.persistent_rcon import get_rcon as _get_rcon
|
|
|
|
class SimpleRCON:
|
|
"""Wrapper around persistent_rcon."""
|
|
def __init__(self, host, port, password):
|
|
self._rcon = _get_rcon(host, port, password)
|
|
|
|
def command(self, cmd):
|
|
return self._rcon.command(cmd)
|
|
|
|
|
|
def sys_msg(mode="sudo"):
|
|
return {"role": "system", "content": SYSTEM_GOD if mode == "god" else SYSTEM_SUDO}
|
|
|
|
def user_msg(text):
|
|
return {"role": "user", "content": text}
|
|
|
|
def tool_call(name, args):
|
|
return {"role": "assistant", "content": f"<tool_call>\n{json.dumps({'name': name, 'arguments': args})}\n</tool_call>"}
|
|
|
|
def tool_result(data):
|
|
return {"role": "tool", "content": json.dumps(data)}
|
|
|
|
def final_response(resp):
|
|
return {"role": "assistant", "content": json.dumps(resp)}
|
|
|
|
def is_error(result):
|
|
return any(e in result for e in ("<--[HERE]", "Unknown", "Incorrect", "Expected", "Invalid"))
|
|
|
|
|
|
def validate_commands_rcon(rcon, commands):
|
|
"""Execute commands through RCON and return results."""
|
|
results = []
|
|
for cmd in commands:
|
|
cmd = cmd.strip()
|
|
if not cmd or cmd.startswith("#"):
|
|
results.append({"cmd": cmd, "result": "comment", "ok": True})
|
|
continue
|
|
try:
|
|
result = rcon.command(cmd)
|
|
ok = not is_error(result)
|
|
results.append({"cmd": cmd, "result": result[:300], "ok": ok})
|
|
except Exception as e:
|
|
results.append({"cmd": cmd, "result": str(e), "ok": False})
|
|
return results
|
|
|
|
|
|
def gen_script_examples(rcon):
|
|
"""Script write/validate/execute examples with real RCON validation."""
|
|
examples = []
|
|
|
|
SCRIPTS = [
|
|
("build me a small oak cabin", "oak_cabin", "Small oak cabin with door, windows, and lantern", [
|
|
"fill ~-3 ~ ~-3 ~3 ~3 ~3 minecraft:oak_planks hollow",
|
|
"fill ~-3 ~ ~-3 ~3 ~ ~3 minecraft:oak_planks",
|
|
"fill ~-2 ~1 ~-3 ~2 ~2 ~-3 minecraft:air",
|
|
"setblock ~0 ~1 ~-3 minecraft:oak_door[facing=north,half=lower]",
|
|
"setblock ~0 ~2 ~-3 minecraft:oak_door[facing=north,half=upper]",
|
|
"setblock ~2 ~1 ~3 minecraft:glass_pane",
|
|
"setblock ~-2 ~1 ~3 minecraft:glass_pane",
|
|
"setblock ~0 ~2 ~0 minecraft:lantern[hanging=true]",
|
|
]),
|
|
("create a pvp arena with wool corners", "pvp_arena", "PvP arena with colored corners", [
|
|
"fill ~-12 ~-1 ~-12 ~12 ~-1 ~12 minecraft:smooth_stone",
|
|
"fill ~-12 ~ ~-12 ~12 ~4 ~-12 minecraft:iron_bars",
|
|
"fill ~-12 ~ ~12 ~12 ~4 ~12 minecraft:iron_bars",
|
|
"fill ~-12 ~ ~-12 ~-12 ~4 ~12 minecraft:iron_bars",
|
|
"fill ~12 ~ ~-12 ~12 ~4 ~12 minecraft:iron_bars",
|
|
"fill ~-12 ~-1 ~-12 ~-10 ~-1 ~-10 minecraft:red_wool",
|
|
"fill ~10 ~-1 ~10 ~12 ~-1 ~12 minecraft:blue_wool",
|
|
]),
|
|
("make an enchanting room", "enchant_room", "Enchanting table surrounded by bookshelves", [
|
|
"fill ~-3 ~ ~-3 ~3 ~3 ~3 minecraft:deepslate_bricks hollow",
|
|
"fill ~-2 ~ ~-2 ~2 ~2 ~2 minecraft:air",
|
|
"setblock ~0 ~ ~0 minecraft:enchanting_table",
|
|
"fill ~-2 ~1 ~-2 ~2 ~1 ~2 minecraft:bookshelf",
|
|
"fill ~-1 ~1 ~-1 ~1 ~1 ~1 minecraft:air",
|
|
"setblock ~0 ~1 ~0 minecraft:enchanting_table",
|
|
"setblock ~0 ~2 ~0 minecraft:lantern[hanging=true]",
|
|
]),
|
|
("build a nether portal frame", "nether_portal", "Obsidian nether portal frame", [
|
|
"fill ~0 ~ ~0 ~3 ~4 ~0 minecraft:obsidian",
|
|
"fill ~1 ~1 ~0 ~2 ~3 ~0 minecraft:air",
|
|
]),
|
|
("create a mob farm collection area", "mob_farm", "Mob farm with hoppers and chest", [
|
|
"fill ~-4 ~-1 ~-4 ~4 ~-1 ~4 minecraft:cobblestone",
|
|
"setblock ~0 ~-2 ~0 minecraft:chest",
|
|
"setblock ~0 ~-1 ~0 minecraft:hopper",
|
|
"fill ~-4 ~ ~0 ~-1 ~ ~0 minecraft:water",
|
|
"fill ~1 ~ ~0 ~4 ~ ~0 minecraft:water",
|
|
]),
|
|
("build a lookout tower", "lookout_tower", "Tall stone tower with viewing platform", [
|
|
"fill ~-1 ~ ~-1 ~1 ~8 ~1 minecraft:stone_bricks hollow",
|
|
"fill ~0 ~ ~0 ~0 ~7 ~0 minecraft:air",
|
|
"fill ~1 ~1 ~0 ~1 ~7 ~0 minecraft:ladder[facing=west]",
|
|
"fill ~-2 ~8 ~-2 ~2 ~8 ~2 minecraft:stone_brick_slab",
|
|
"fill ~-2 ~9 ~-2 ~2 ~9 ~2 minecraft:stone_brick_wall",
|
|
"fill ~-1 ~9 ~-1 ~1 ~9 ~1 minecraft:air",
|
|
"setblock ~0 ~10 ~0 minecraft:lantern",
|
|
]),
|
|
("make a fishing dock", "fishing_dock", "Wooden dock extending over water", [
|
|
"fill ~0 ~-1 ~0 ~1 ~-1 ~8 minecraft:oak_planks",
|
|
"fill ~-1 ~-2 ~0 ~-1 ~-1 ~8 minecraft:oak_fence",
|
|
"fill ~2 ~-2 ~0 ~2 ~-1 ~8 minecraft:oak_fence",
|
|
"setblock ~0 ~0 ~8 minecraft:oak_fence",
|
|
"setblock ~1 ~0 ~8 minecraft:oak_fence",
|
|
"setblock ~0 ~0 ~0 minecraft:lantern",
|
|
]),
|
|
("create a garden with flower beds", "garden", "Flower garden with paths", [
|
|
"fill ~-5 ~-1 ~-5 ~5 ~-1 ~5 minecraft:grass_block",
|
|
"fill ~-5 ~-1 ~0 ~5 ~-1 ~0 minecraft:gravel",
|
|
"fill ~0 ~-1 ~-5 ~0 ~-1 ~5 minecraft:gravel",
|
|
"setblock ~-3 ~ ~-3 minecraft:rose_bush[half=lower]",
|
|
"setblock ~3 ~ ~-3 minecraft:peony[half=lower]",
|
|
"setblock ~-3 ~ ~3 minecraft:lilac[half=lower]",
|
|
"setblock ~3 ~ ~3 minecraft:sunflower[half=lower]",
|
|
"setblock ~0 ~ ~0 minecraft:water",
|
|
]),
|
|
("build a redstone clock", "redstone_clock", "Simple repeater clock", [
|
|
"setblock ~0 ~ ~0 minecraft:redstone_wire",
|
|
"setblock ~1 ~ ~0 minecraft:repeater[facing=west,delay=4]",
|
|
"setblock ~2 ~ ~0 minecraft:redstone_wire",
|
|
"setblock ~2 ~ ~1 minecraft:repeater[facing=north,delay=4]",
|
|
"setblock ~2 ~ ~2 minecraft:redstone_wire",
|
|
"setblock ~1 ~ ~2 minecraft:repeater[facing=east,delay=4]",
|
|
"setblock ~0 ~ ~2 minecraft:redstone_wire",
|
|
"setblock ~0 ~ ~1 minecraft:repeater[facing=south,delay=4]",
|
|
]),
|
|
("make a treasure vault", "treasure_vault", "Secure room with chests and iron door", [
|
|
"fill ~-3 ~ ~-3 ~3 ~3 ~3 minecraft:deepslate_brick_wall hollow",
|
|
"fill ~-2 ~ ~-2 ~2 ~2 ~2 minecraft:air",
|
|
"setblock ~0 ~0 ~-3 minecraft:iron_door[facing=north,half=lower]",
|
|
"setblock ~0 ~1 ~-3 minecraft:iron_door[facing=north,half=upper]",
|
|
"setblock ~-1 ~0 ~-2 minecraft:chest",
|
|
"setblock ~1 ~0 ~-2 minecraft:chest",
|
|
"setblock ~-1 ~0 ~2 minecraft:chest",
|
|
"setblock ~1 ~0 ~2 minecraft:chest",
|
|
"setblock ~0 ~2 ~0 minecraft:lantern[hanging=true]",
|
|
]),
|
|
]
|
|
|
|
for prompt, name, desc, commands in SCRIPTS:
|
|
player = random.choice(PLAYERS)
|
|
mode = "sudo"
|
|
msgs = [sys_msg(mode), user_msg(f"Player {player}: sudo {prompt}")]
|
|
|
|
# Step 1: Validate
|
|
rcon_results = validate_commands_rcon(rcon, commands)
|
|
valid = all(r["ok"] for r in rcon_results)
|
|
errors = [{"line": i+1, "command": r["cmd"], "error": r["result"]}
|
|
for i, r in enumerate(rcon_results) if not r["ok"]]
|
|
|
|
msgs.append(tool_call("script.validate", {"commands": commands}))
|
|
msgs.append(tool_result({
|
|
"valid": valid,
|
|
"total": len(commands),
|
|
"passed": sum(1 for r in rcon_results if r["ok"]),
|
|
"errors": errors,
|
|
}))
|
|
|
|
# Step 2: Write (if valid or partially valid)
|
|
write_cmds = [r["cmd"] for r in rcon_results if r["ok"]] if not valid else commands
|
|
msgs.append(tool_call("script.write", {"name": name, "commands": write_cmds, "description": desc}))
|
|
msgs.append(tool_result({"ok": True, "path": f"mortdecai:{name}", "lines": len(write_cmds)}))
|
|
|
|
# Step 3: Execute
|
|
msgs.append(tool_call("script.execute", {"name": name, "as_player": player}))
|
|
msgs.append(tool_result({"ok": True, "result": f"Executed {len(write_cmds)} commands from function mortdecai:{name}"}))
|
|
|
|
# Final
|
|
fix_note = f" Fixed {len(errors)} validation errors." if errors else ""
|
|
resp = {
|
|
"risk_level": 3,
|
|
"commands": [f"function mortdecai:{name}"],
|
|
"reasoning": f"Validated {len(commands)} commands ({sum(1 for r in rcon_results if r['ok'])} passed).{fix_note} Wrote and executed '{name}' script at player position.",
|
|
}
|
|
msgs.append(final_response(resp))
|
|
|
|
examples.append({
|
|
"id": f"rcon-script-{name}",
|
|
"source": "rcon_validated",
|
|
"type": "script_write_execute",
|
|
"messages": msgs,
|
|
})
|
|
|
|
return examples
|
|
|
|
|
|
def gen_direct_command_examples(rcon):
|
|
"""Direct rcon.execute examples with real server responses."""
|
|
examples = []
|
|
|
|
COMMANDS = [
|
|
("sudo give me a diamond sword with sharpness 5", [
|
|
"give {p} minecraft:diamond_sword[enchantments={sharpness:5,unbreaking:3,mending:1}] 1",
|
|
], "Gave max enchanted diamond sword."),
|
|
("sudo full netherite armor please", [
|
|
"give {p} minecraft:netherite_helmet[enchantments={protection:4,unbreaking:3,mending:1}] 1",
|
|
"give {p} minecraft:netherite_chestplate[enchantments={protection:4,unbreaking:3,mending:1}] 1",
|
|
"give {p} minecraft:netherite_leggings[enchantments={protection:4,unbreaking:3,mending:1}] 1",
|
|
"give {p} minecraft:netherite_boots[enchantments={protection:4,unbreaking:3,mending:1}] 1",
|
|
], "Full netherite armor set with protection 4 and mending."),
|
|
("sudo make it night", ["time set night"], "Set time to night."),
|
|
("sudo give me 64 golden apples", [
|
|
"give {p} minecraft:golden_apple 64",
|
|
], "Gave 64 golden apples."),
|
|
("sudo smite me with lightning", [
|
|
"execute at {p} run summon minecraft:lightning_bolt",
|
|
], "Summoned lightning at player position."),
|
|
("sudo give me speed 3 for 10 minutes", [
|
|
"effect give {p} minecraft:speed 600 2",
|
|
], "Applied speed 3 (amplifier 2) for 600 seconds."),
|
|
("sudo clear weather", ["weather clear 999999"], "Cleared weather."),
|
|
("sudo make me invincible", [
|
|
"effect give {p} minecraft:resistance 6000 4",
|
|
"effect give {p} minecraft:regeneration 6000 2",
|
|
"effect give {p} minecraft:fire_resistance 6000 0",
|
|
], "Applied resistance 5, regeneration 3, and fire resistance for 100 minutes."),
|
|
("sudo give me a god bow", [
|
|
"give {p} minecraft:bow[enchantments={power:5,infinity:1,flame:1,punch:2,unbreaking:3}] 1",
|
|
], "Bow with power 5, infinity, flame, punch 2."),
|
|
("sudo stack of each wood type", [
|
|
"give {p} minecraft:oak_log 64",
|
|
"give {p} minecraft:spruce_log 64",
|
|
"give {p} minecraft:birch_log 64",
|
|
"give {p} minecraft:jungle_log 64",
|
|
"give {p} minecraft:acacia_log 64",
|
|
"give {p} minecraft:dark_oak_log 64",
|
|
"give {p} minecraft:cherry_log 64",
|
|
"give {p} minecraft:mangrove_log 64",
|
|
], "All 8 wood types, one stack each."),
|
|
("sudo clear my inventory", ["clear {p}"], "Cleared all items."),
|
|
("sudo tp me to 0 100 0", ["tp {p} 0 100 0"], "Teleported to coordinates."),
|
|
("sudo gamemode creative", ["gamemode creative {p}"], "Set creative mode."),
|
|
("sudo gamemode survival", ["gamemode survival {p}"], "Set survival mode."),
|
|
("sudo kill all zombies", ["kill @e[type=minecraft:zombie]"], "Killed all zombies."),
|
|
("sudo summon 5 cows", [
|
|
"summon minecraft:cow ~ ~ ~1",
|
|
"summon minecraft:cow ~ ~ ~2",
|
|
"summon minecraft:cow ~ ~ ~3",
|
|
"summon minecraft:cow ~1 ~ ~1",
|
|
"summon minecraft:cow ~1 ~ ~2",
|
|
], "Summoned 5 cows nearby."),
|
|
("sudo give everyone a diamond", [
|
|
"give @a minecraft:diamond 1",
|
|
], "Gave all players a diamond."),
|
|
("sudo set time to sunrise", ["time set 23000"], "Set to dawn (tick 23000)."),
|
|
("sudo fill a 5x5 area with gold blocks under me", [
|
|
"fill ~-2 ~-1 ~-2 ~2 ~-1 ~2 minecraft:gold_block",
|
|
], "Filled 5x5 gold platform under player."),
|
|
("sudo give me a trident with loyalty and channeling", [
|
|
"give {p} minecraft:trident[enchantments={loyalty:3,channeling:1,impaling:5,unbreaking:3,mending:1}] 1",
|
|
], "Trident with loyalty 3, channeling, impaling 5. Loyalty and riptide are mutually exclusive — used loyalty."),
|
|
("sudo night vision forever", [
|
|
"effect give {p} minecraft:night_vision 999999 0",
|
|
], "Permanent night vision."),
|
|
("sudo repair what I'm holding", [
|
|
"give {p} minecraft:experience_bottle 64",
|
|
], "Gave XP bottles — use with mending items to repair. Cannot directly repair via commands."),
|
|
("sudo give me a mace", [
|
|
"give {p} minecraft:mace[enchantments={density:5,wind_burst:3,unbreaking:3,mending:1}] 1",
|
|
], "Mace with density 5 and wind burst 3. New 1.21 weapon."),
|
|
("sudo spawn a warden", [
|
|
"summon minecraft:warden ~ ~ ~5",
|
|
], "Summoned warden 5 blocks away. Be careful — 500 HP, 2-hit kill."),
|
|
("sudo give me all the music discs", [
|
|
"give {p} minecraft:music_disc_13 1",
|
|
"give {p} minecraft:music_disc_cat 1",
|
|
"give {p} minecraft:music_disc_blocks 1",
|
|
"give {p} minecraft:music_disc_chirp 1",
|
|
"give {p} minecraft:music_disc_far 1",
|
|
"give {p} minecraft:music_disc_mall 1",
|
|
"give {p} minecraft:music_disc_mellohi 1",
|
|
"give {p} minecraft:music_disc_stal 1",
|
|
"give {p} minecraft:music_disc_strad 1",
|
|
"give {p} minecraft:music_disc_ward 1",
|
|
"give {p} minecraft:music_disc_pigstep 1",
|
|
"give {p} minecraft:music_disc_otherside 1",
|
|
], "All music discs."),
|
|
("sudo potion of the turtle master", [
|
|
"give {p} minecraft:potion[potion_contents={potion:\"minecraft:turtle_master\"}] 1",
|
|
], "Turtle master potion — slowness 4 + resistance 3."),
|
|
("sudo banner with a skull pattern", [
|
|
"give {p} minecraft:white_banner[banner_patterns=[{pattern:\"minecraft:skull\",color:\"black\"}]] 1",
|
|
], "White banner with black skull pattern using 1.21 syntax."),
|
|
]
|
|
|
|
for prompt, cmds_tmpl, reasoning in COMMANDS:
|
|
player = random.choice(PLAYERS)
|
|
cmds = [c.replace("{p}", player) for c in cmds_tmpl]
|
|
msgs = [sys_msg("sudo"), user_msg(f"Player {player}: {prompt}")]
|
|
|
|
all_ok = True
|
|
for cmd in cmds:
|
|
rcon_result = rcon.command(cmd)
|
|
ok = not is_error(rcon_result)
|
|
if not ok:
|
|
all_ok = False
|
|
msgs.append(tool_call("rcon.execute", {"command": cmd}))
|
|
msgs.append(tool_result({"success": ok, "result": rcon_result[:300]}))
|
|
|
|
resp = {"risk_level": 3, "commands": cmds, "reasoning": reasoning}
|
|
msgs.append(final_response(resp))
|
|
|
|
examples.append({
|
|
"id": f"rcon-direct-{len(examples):03d}",
|
|
"source": "rcon_validated",
|
|
"type": "direct_command",
|
|
"rcon_validated": True,
|
|
"all_success": all_ok,
|
|
"messages": msgs,
|
|
})
|
|
|
|
return examples
|
|
|
|
|
|
def gen_memory_examples(rcon):
|
|
"""Memory read/write examples."""
|
|
examples = []
|
|
|
|
MEMORY_OPS = [
|
|
("sudo remember this as home", "write", "home", "location", {"x": 100, "y": 64, "z": -200}, None,
|
|
"Saved current location as 'home'."),
|
|
("sudo save this spot as my base", "write", "base", "location", {"x": -500, "y": 72, "z": 300}, None,
|
|
"Saved base location."),
|
|
("sudo tp me home", "read", "home", None, None, "tp {p} 100 64 -200",
|
|
"Read 'home' from memory. Teleporting."),
|
|
("sudo take me to my base", "read", "base", None, None, "tp {p} -500 72 300",
|
|
"Read 'base' from memory. Teleporting."),
|
|
("sudo what do you remember about me?", "read", None, None, None, None,
|
|
"Listed all memories for this player."),
|
|
("sudo remember my favorite tool is a pickaxe", "write", "favorite_tool", "preference", "diamond pickaxe", None,
|
|
"Saved tool preference."),
|
|
("sudo remember Ace is my enemy", "write", "enemy_ace", "fact", "Ace13245 is an enemy", None,
|
|
"Noted player relationship."),
|
|
("sudo forget my base", "delete", "base", None, None, None,
|
|
"Deleted 'base' memory."),
|
|
("sudo save this as my mine", "write", "mine", "location", {"x": 30, "y": 11, "z": -80}, None,
|
|
"Saved mining location."),
|
|
("sudo tp me to my mine", "read", "mine", None, None, "tp {p} 30 11 -80",
|
|
"Read 'mine' from memory. Teleporting to Y=11."),
|
|
]
|
|
|
|
for prompt, action, key, mtype, value, cmd_tmpl, reasoning in MEMORY_OPS:
|
|
player = random.choice(PLAYERS)
|
|
msgs = [sys_msg("sudo"), user_msg(f"Player {player}: {prompt}")]
|
|
|
|
if action == "write":
|
|
msgs.append(tool_call("memory.write", {"player": player, "type": mtype, "key": key, "value": value}))
|
|
msgs.append(tool_result({"ok": True, "key": key}))
|
|
resp = {"risk_level": 4, "commands": [], "reasoning": reasoning}
|
|
elif action == "read":
|
|
args = {"player": player}
|
|
if key:
|
|
args["key"] = key
|
|
msgs.append(tool_call("memory.read", args))
|
|
if key:
|
|
mem_val = value or {"x": random.randint(-500,500), "y": random.randint(60,120), "z": random.randint(-500,500)}
|
|
msgs.append(tool_result({"memories": [{"key": key, "type": mtype or "location", "value": mem_val}]}))
|
|
else:
|
|
msgs.append(tool_result({"memories": [
|
|
{"key": "home", "type": "location", "value": {"x": 100, "y": 64, "z": -200}},
|
|
{"key": "favorite_tool", "type": "preference", "value": "diamond pickaxe"},
|
|
]}))
|
|
|
|
if cmd_tmpl:
|
|
cmd = cmd_tmpl.replace("{p}", player)
|
|
result = rcon.command(cmd)
|
|
msgs.append(tool_call("rcon.execute", {"command": cmd}))
|
|
msgs.append(tool_result({"success": not is_error(result), "result": result[:200]}))
|
|
resp = {"risk_level": 3, "commands": [cmd], "reasoning": reasoning}
|
|
else:
|
|
resp = {"risk_level": 4, "commands": [], "reasoning": reasoning}
|
|
elif action == "delete":
|
|
msgs.append(tool_call("memory.read", {"player": player, "key": key}))
|
|
msgs.append(tool_result({"memories": [{"key": key, "type": "location", "value": {"x": -500, "y": 72, "z": 300}}]}))
|
|
resp = {"risk_level": 3, "commands": [], "reasoning": reasoning}
|
|
|
|
msgs.append(final_response(resp))
|
|
examples.append({
|
|
"id": f"rcon-memory-{len(examples):03d}",
|
|
"source": "rcon_validated",
|
|
"type": f"memory_{action}",
|
|
"messages": msgs,
|
|
})
|
|
|
|
return examples
|
|
|
|
|
|
def gen_wiki_examples(rcon):
|
|
"""Wiki lookup → command execution with real RCON results."""
|
|
examples = []
|
|
|
|
WIKI = [
|
|
("sudo best pickaxe enchantments", "pickaxe enchantments 1.21",
|
|
"Pickaxe enchantments:\n- Efficiency V: faster mining\n- Fortune III: more drops from ores\n- Silk Touch: mine blocks directly\n- Unbreaking III: durability\n- Mending: XP repair\nFortune and Silk Touch are mutually exclusive.",
|
|
["give {p} minecraft:netherite_pickaxe[enchantments={efficiency:5,fortune:3,unbreaking:3,mending:1}] 1"],
|
|
"Max pickaxe with fortune. Fortune and silk touch are exclusive — chose fortune for ore farming."),
|
|
("sudo what enchants for boots?", "boot enchantments 1.21",
|
|
"Boot enchantments:\n- Protection IV or Blast/Fire/Projectile Protection IV\n- Feather Falling IV: reduces fall damage\n- Depth Strider III or Frost Walker II (exclusive)\n- Soul Speed III: faster on soul sand\n- Swift Sneak III: faster sneaking\n- Unbreaking III, Mending I",
|
|
["give {p} minecraft:netherite_boots[enchantments={protection:4,feather_falling:4,depth_strider:3,soul_speed:3,unbreaking:3,mending:1}] 1"],
|
|
"All compatible boot enchants. Depth strider over frost walker for general use."),
|
|
("sudo how do I use the /place command?", "place command 1.21",
|
|
"/place feature <feature> [pos]\n/place jigsaw <pool> <target> <max_depth> [pos]\n/place structure <structure> [pos]\n/place template <template> [pos] [rotation] [mirror]\n\nUseful for placing structures, trees, etc.",
|
|
["place feature minecraft:oak ~ ~ ~5"],
|
|
"Placed an oak tree feature 5 blocks away using /place."),
|
|
("sudo what's the command for teams?", "team command minecraft",
|
|
"Team commands:\n/team add <name> [display_name]\n/team join <team> [members]\n/team leave [members]\n/team modify <team> <option> <value>\nOptions: color, friendlyFire, seeFriendlyInvisibles, nametagVisibility, collisionRule, prefix, suffix",
|
|
["team add red Red Team", "team modify red color red"],
|
|
"Created 'Red Team' with red name color."),
|
|
("sudo how to make colored text in chat?", "tellraw color formatting minecraft",
|
|
"tellraw @a {\"text\":\"Hello\",\"color\":\"gold\",\"bold\":true}\nColors: black, dark_blue, dark_green, dark_aqua, dark_red, dark_purple, gold, gray, dark_gray, blue, green, aqua, red, light_purple, yellow, white\nFormatting: bold, italic, underlined, strikethrough, obfuscated",
|
|
['tellraw @a {"text":"Welcome to the server!","color":"gold","bold":true}'],
|
|
"Used tellraw with gold color and bold formatting."),
|
|
]
|
|
|
|
for prompt, query, content, cmds_tmpl, reasoning in WIKI:
|
|
player = random.choice(PLAYERS)
|
|
cmds = [c.replace("{p}", player) for c in cmds_tmpl]
|
|
msgs = [sys_msg("sudo"), user_msg(f"Player {player}: {prompt}")]
|
|
|
|
msgs.append(tool_call("minecraft.wiki_lookup", {"query": query}))
|
|
msgs.append(tool_result({"content": content, "url": f"https://minecraft.wiki/w/{query.split()[0]}"}))
|
|
|
|
for cmd in cmds:
|
|
result = rcon.command(cmd)
|
|
msgs.append(tool_call("rcon.execute", {"command": cmd}))
|
|
msgs.append(tool_result({"success": not is_error(result), "result": result[:300]}))
|
|
|
|
resp = {"risk_level": 3, "commands": cmds, "reasoning": reasoning}
|
|
msgs.append(final_response(resp))
|
|
|
|
examples.append({
|
|
"id": f"rcon-wiki-{len(examples):03d}",
|
|
"source": "rcon_validated",
|
|
"type": "wiki_lookup",
|
|
"messages": msgs,
|
|
})
|
|
|
|
return examples
|
|
|
|
|
|
def gen_error_correction_examples(rcon):
|
|
"""Commands that fail, get corrected, and succeed — with real RCON errors."""
|
|
examples = []
|
|
|
|
ERRORS = [
|
|
("sudo give me a bed", "give {p} minecraft:bed 1", "give {p} minecraft:white_bed 1",
|
|
"No item 'bed' — correct ID is 'white_bed' in 1.21."),
|
|
("sudo give me cooked beef", "give {p} minecraft:steak 1", "give {p} minecraft:cooked_beef 1",
|
|
"Item is 'cooked_beef', not 'steak'."),
|
|
("sudo give me speed", "effect give {p} minecraft:speed", "effect give {p} minecraft:speed 300 1",
|
|
"Effect needs duration and amplifier arguments."),
|
|
("sudo give me a log", "give {p} minecraft:log 64", "give {p} minecraft:oak_log 64",
|
|
"No item 'log' — must specify wood type: oak_log, spruce_log, etc."),
|
|
("sudo fill with stone 10", "fill ~ ~ ~ ~10 ~10 ~10 minecraft:stone 10", "fill ~ ~ ~ ~10 ~10 ~10 minecraft:stone",
|
|
"Fill doesn't take a trailing count number."),
|
|
]
|
|
|
|
for prompt, wrong_cmd_tmpl, right_cmd_tmpl, reasoning in ERRORS:
|
|
player = random.choice(PLAYERS)
|
|
wrong_cmd = wrong_cmd_tmpl.replace("{p}", player)
|
|
right_cmd = right_cmd_tmpl.replace("{p}", player)
|
|
msgs = [sys_msg("sudo"), user_msg(f"Player {player}: {prompt}")]
|
|
|
|
# Wrong attempt
|
|
wrong_result = rcon.command(wrong_cmd)
|
|
msgs.append(tool_call("rcon.execute", {"command": wrong_cmd}))
|
|
msgs.append(tool_result({"success": False, "result": wrong_result[:300]}))
|
|
|
|
# Corrected attempt
|
|
right_result = rcon.command(right_cmd)
|
|
msgs.append(tool_call("rcon.execute", {"command": right_cmd}))
|
|
msgs.append(tool_result({"success": not is_error(right_result), "result": right_result[:300]}))
|
|
|
|
resp = {"risk_level": 3, "commands": [right_cmd], "reasoning": f"First attempt failed: {wrong_result[:100]}. {reasoning}"}
|
|
msgs.append(final_response(resp))
|
|
|
|
examples.append({
|
|
"id": f"rcon-errfix-{len(examples):03d}",
|
|
"source": "rcon_validated",
|
|
"type": "error_correction",
|
|
"messages": msgs,
|
|
})
|
|
|
|
return examples
|
|
|
|
|
|
def gen_chained_examples(rcon):
|
|
"""Multi-tool chained examples with real RCON."""
|
|
examples = []
|
|
|
|
CHAINS = [
|
|
("sudo gear me up for the nether", [
|
|
("rcon.execute", {"command": "give {p} minecraft:netherite_sword[enchantments={sharpness:5,fire_aspect:2,unbreaking:3,mending:1}] 1"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:netherite_pickaxe[enchantments={efficiency:5,fortune:3,unbreaking:3,mending:1}] 1"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:netherite_chestplate[enchantments={protection:4,unbreaking:3,mending:1}] 1"}),
|
|
("rcon.execute", {"command": "effect give {p} minecraft:fire_resistance 6000 0"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:golden_apple 16"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:ender_pearl 16"}),
|
|
], "Full nether loadout: netherite sword+pick, protection armor, fire resistance, golden apples, pearls."),
|
|
("sudo prepare me for an end fight", [
|
|
("rcon.execute", {"command": "give {p} minecraft:netherite_sword[enchantments={sharpness:5,unbreaking:3,mending:1}] 1"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:bow[enchantments={power:5,infinity:1,unbreaking:3}] 1"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:arrow 64"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:golden_apple 32"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:ender_pearl 16"}),
|
|
("rcon.execute", {"command": "effect give {p} minecraft:slow_falling 600 0"}),
|
|
("rcon.execute", {"command": "give {p} minecraft:cobblestone 128"}),
|
|
], "End fight kit: weapons, slow falling for void safety, blocks for pillaring, pearls for dodging."),
|
|
]
|
|
|
|
for prompt, tool_calls, reasoning in CHAINS:
|
|
player = random.choice(PLAYERS)
|
|
msgs = [sys_msg("sudo"), user_msg(f"Player {player}: {prompt}")]
|
|
|
|
all_cmds = []
|
|
for tool_name, args_tmpl in tool_calls:
|
|
args = {}
|
|
for k, v in args_tmpl.items():
|
|
args[k] = v.replace("{p}", player) if isinstance(v, str) else v
|
|
|
|
if tool_name == "rcon.execute":
|
|
cmd = args["command"]
|
|
result = rcon.command(cmd)
|
|
msgs.append(tool_call(tool_name, args))
|
|
msgs.append(tool_result({"success": not is_error(result), "result": result[:300]}))
|
|
all_cmds.append(cmd)
|
|
else:
|
|
msgs.append(tool_call(tool_name, args))
|
|
msgs.append(tool_result({"ok": True}))
|
|
|
|
resp = {"risk_level": 3, "commands": all_cmds, "reasoning": reasoning}
|
|
msgs.append(final_response(resp))
|
|
|
|
examples.append({
|
|
"id": f"rcon-chain-{len(examples):03d}",
|
|
"source": "rcon_validated",
|
|
"type": "chained",
|
|
"messages": msgs,
|
|
})
|
|
|
|
return examples
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--rcon-host", default="192.168.0.244")
|
|
parser.add_argument("--rcon-port", type=int, default=25577)
|
|
parser.add_argument("--rcon-pass", default="REDACTED_RCON")
|
|
args = parser.parse_args()
|
|
|
|
print(f"Connecting to RCON at {args.rcon_host}:{args.rcon_port}...")
|
|
rcon = SimpleRCON(args.rcon_host, args.rcon_port, args.rcon_pass)
|
|
print("Connected.\n")
|
|
|
|
all_examples = []
|
|
|
|
print("Generating script examples...")
|
|
scripts = gen_script_examples(rcon)
|
|
all_examples.extend(scripts)
|
|
print(f" {len(scripts)} script examples")
|
|
|
|
print("Generating direct command examples...")
|
|
direct = gen_direct_command_examples(rcon)
|
|
all_examples.extend(direct)
|
|
print(f" {len(direct)} direct command examples")
|
|
|
|
print("Generating memory examples...")
|
|
memory = gen_memory_examples(rcon)
|
|
all_examples.extend(memory)
|
|
print(f" {len(memory)} memory examples")
|
|
|
|
print("Generating wiki examples...")
|
|
wiki = gen_wiki_examples(rcon)
|
|
all_examples.extend(wiki)
|
|
print(f" {len(wiki)} wiki examples")
|
|
|
|
print("Generating error correction examples...")
|
|
errors = gen_error_correction_examples(rcon)
|
|
all_examples.extend(errors)
|
|
print(f" {len(errors)} error correction examples")
|
|
|
|
print("Generating chained multi-tool examples...")
|
|
chained = gen_chained_examples(rcon)
|
|
all_examples.extend(chained)
|
|
print(f" {len(chained)} chained examples")
|
|
|
|
# Stats
|
|
success_count = sum(1 for e in all_examples if e.get("all_success", True))
|
|
print(f"\nTotal: {len(all_examples)} examples")
|
|
print(f" RCON validated: {sum(1 for e in all_examples if e.get('rcon_validated', False))}")
|
|
|
|
OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(OUTPUT_PATH, "w") as f:
|
|
for ex in all_examples:
|
|
f.write(json.dumps(ex, ensure_ascii=False) + "\n")
|
|
|
|
print(f"Written to {OUTPUT_PATH}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|