|
import gradio as gr |
|
import torch |
|
from transformers import AutoModelForCausalLM, AutoTokenizer |
|
from scratch3 import Project |
|
|
|
|
|
model_name = "TheBloke/LLaMa-7B-GGML" |
|
tokenizer = AutoTokenizer.from_pretrained(model_name) |
|
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto") |
|
|
|
|
|
scratch_blocks = { |
|
"motion": [ |
|
{"name": "move", "parameters": {"steps": "number"}}, |
|
{"name": "turn", "parameters": {"angle": "number"}}, |
|
{"name": "go to", "parameters": {"x": "number", "y": "number"}}, |
|
{"name": "point in direction", "parameters": {"direction": "number"}} |
|
], |
|
"sound": [ |
|
{"name": "play sound", "parameters": {"sound": "string"}}, |
|
{"name": "stop all sounds", "parameters": {}} |
|
], |
|
"event": [ |
|
{"name": "when green flag clicked", "parameters": {}}, |
|
{"name": "when this sprite clicked", "parameters": {}} |
|
], |
|
"control": [ |
|
{"name": "wait", "parameters": {"seconds": "number"}}, |
|
{"name": "repeat", "parameters": {"times": "number"}}, |
|
{"name": "if then", "parameters": {"condition": "string"}}, |
|
], |
|
"variables": [ |
|
{"name": "set variable", "parameters": {"variable": "string", "value": "number"}}, |
|
{"name": "change variable", "parameters": {"variable": "string", "value": "number"}}, |
|
], |
|
"looks": [ |
|
{"name": "say", "parameters": {"message": "string"}}, |
|
{"name": "think", "parameters": {"message": "string"}}, |
|
{"name": "show", "parameters": {}}, |
|
{"name": "hide", "parameters": {}} |
|
], |
|
"drawing": [ |
|
{"name": "draw line", "parameters": {"x1": "number", "y1": "number", "x2": "number", "y2": "number"}}, |
|
{"name": "draw circle", "parameters": {"x": "number", "y": "number", "radius": "number"}}, |
|
{"name": "draw rectangle", "parameters": {"x": "number", "y": "number", "width": "number", "height": "number"}}, |
|
] |
|
} |
|
|
|
def generate_script(prompt): |
|
""" |
|
Generate a Scratch script based on the provided prompt using the AI model. |
|
""" |
|
inputs = tokenizer(prompt, return_tensors="pt").to(model.device) |
|
with torch.no_grad(): |
|
outputs = model.generate(inputs["input_ids"], max_length=300, num_return_sequences=1) |
|
|
|
generated_script = tokenizer.decode(outputs[0], skip_special_tokens=True) |
|
return generated_script |
|
|
|
def parse_generated_script(generated_script): |
|
""" |
|
Parse the generated script into instructions. |
|
""" |
|
instructions = [] |
|
for line in generated_script.split('.'): |
|
line = line.strip() |
|
if line: |
|
instructions.append(line) |
|
return instructions |
|
|
|
def create_scratch_project(title, sprites, sounds, custom_prompt): |
|
""" |
|
Create a Scratch project based on user input and generated script. |
|
""" |
|
|
|
prompt = f"Create a Scratch project titled '{title}' featuring sprites {sprites} with sounds {sounds}. " \ |
|
f"Additional details: {custom_prompt}" |
|
generated_script = generate_script(prompt) |
|
|
|
|
|
instructions = parse_generated_script(generated_script) |
|
|
|
|
|
project = Project() |
|
|
|
|
|
sprite_list = [sprite.strip() for sprite in sprites.split(',')] |
|
for sprite in sprite_list: |
|
sprite_block = { |
|
"objName": sprite, |
|
"skin": 1, |
|
"x": 0, |
|
"y": 0, |
|
"direction": 90, |
|
"visible": True, |
|
"size": 100 |
|
} |
|
project.sprites.append(sprite_block) |
|
|
|
|
|
for sound in sounds.split(','): |
|
sound = sound.strip() |
|
if sound: |
|
sound_block = { |
|
"name": sound, |
|
"path": f"sounds/{sound}.wav" |
|
} |
|
project.sounds.append(sound_block) |
|
|
|
|
|
for instruction in instructions: |
|
if "move" in instruction: |
|
steps = extract_number(instruction) |
|
project.add_motion_block({"name": "move", "parameters": {"steps": steps}}) |
|
elif "turn" in instruction: |
|
angle = extract_number(instruction) |
|
project.add_motion_block({"name": "turn", "parameters": {"angle": angle}}) |
|
elif "play sound" in instruction: |
|
project.add_sound_block({"name": "play sound", "parameters": {"sound": sounds.split(',')[0].strip()}}) |
|
elif "when green flag clicked" in instruction: |
|
project.add_event_block({"name": "when green flag clicked"}) |
|
elif "repeat" in instruction: |
|
times = extract_number(instruction) |
|
project.add_control_block({"name": "repeat", "parameters": {"times": times}}) |
|
elif "say" in instruction: |
|
message = extract_message(instruction) |
|
project.add_looks_block({"name": "say", "parameters": {"message": message}}) |
|
elif "draw line" in instruction: |
|
x1, y1, x2, y2 = extract_drawing_parameters(instruction) |
|
project.add_drawing_block({"name": "draw line", "parameters": {"x1": x1, "y1": y1, "x2": x2, "y2": y2}}) |
|
elif "draw circle" in instruction: |
|
x, y, radius = extract_circle_parameters(instruction) |
|
project.add_drawing_block({"name": "draw circle", "parameters": {"x": x, "y": y, "radius": radius}}) |
|
elif "draw rectangle" in instruction: |
|
x, y, width, height = extract_rectangle_parameters(instruction) |
|
project.add_drawing_block({"name": "draw rectangle", "parameters": {"x": x, "y": y, "width": width, "height": height}}) |
|
elif "remove sprite" in instruction: |
|
sprite_name = extract_sprite_name(instruction) |
|
project.remove_sprite(sprite_name) |
|
elif "rename sprite" in instruction: |
|
old_name, new_name = extract_rename_sprite_parameters(instruction) |
|
project.rename_sprite(old_name, new_name) |
|
|
|
|
|
sb3_file_path = f"{title}.sb3" |
|
with open(sb3_file_path, "wb") as f: |
|
f.write(project.export()) |
|
|
|
return sb3_file_path |
|
|
|
def extract_number(instruction): |
|
""" |
|
Extract a number from the instruction string. |
|
""" |
|
import re |
|
match = re.search(r'\d+', instruction) |
|
return int(match.group(0)) if match else 0 |
|
|
|
def extract_message(instruction): |
|
""" |
|
Extract a message from the instruction string. |
|
""" |
|
return instruction.split("say")[-1].strip().strip('"') |
|
|
|
def extract_drawing_parameters(instruction): |
|
""" |
|
Extract parameters for drawing a line from the instruction string. |
|
""" |
|
numbers = list(map(int, re.findall(r'\d+', instruction))) |
|
return numbers |
|
|
|
def extract_circle_parameters(instruction): |
|
""" |
|
Extract parameters for drawing a circle from the instruction string. |
|
""" |
|
numbers = list(map(int, re.findall(r'\d+', instruction))) |
|
return numbers |
|
|
|
def extract_rectangle_parameters(instruction): |
|
""" |
|
Extract parameters for drawing a rectangle from the instruction string. |
|
""" |
|
numbers = list(map(int, re.findall(r'\d+', instruction))) |
|
return numbers |
|
|
|
def extract_sprite_name(instruction): |
|
""" |
|
Extract the sprite name from the instruction string for removal. |
|
""" |
|
return instruction.split("remove sprite")[-1].strip() |
|
|
|
def extract_rename_sprite_parameters(instruction): |
|
""" |
|
Extract old and new names for renaming a sprite from the instruction string. |
|
""" |
|
parts = instruction.split("rename sprite")[-1].strip().split("to") |
|
old_name = parts[0].strip() |
|
new_name = parts[1].strip() if len(parts) > 1 else None |
|
return old_name, new_name |
|
|
|
|
|
iface = gr.Interface( |
|
fn=create_scratch_project, |
|
inputs=[ |
|
gr.Textbox(label="Project Title"), |
|
gr.Textbox(label="Sprite Names (comma separated)"), |
|
gr.Textbox(label="Sound Names (comma separated)"), |
|
gr.Textbox(label="Custom Prompt (optional)"), |
|
], |
|
outputs=gr.File(label="Download Your Scratch Project (.sb3)"), |
|
title="Scratch Project Generator", |
|
description="Create a Scratch project using AI. Provide a title, sprites, sounds, and a custom prompt." |
|
) |
|
|
|
|
|
iface.launch() |
|
|