gui-shift-implementation / build_data.py
xxilu's picture
Upload build_data.py
a61addb verified
Raw
History Blame Contribute Delete
4.58 kB
"""
Build K-step GUI Transition data from AndroidControl dataset for GUI-Shift training.
Run this first to construct the training dataset.
"""
import json
import os
import random
from collections import defaultdict
from datasets import load_dataset
def parse_action(action_str):
"""Parse AndroidControl action string to GUI-Shift action format."""
if not action_str.startswith("!FUNCTIONCALL"):
return None
json_str = action_str.replace("!FUNCTIONCALL", "")
try:
action = json.loads(json_str)
name = action["name"]
params = action.get("parameters", {})
if name == "click":
return {"action_type": "click", "x": params["x"], "y": params["y"]}
elif name == "long_press":
return {"action_type": "long_press", "x": params["x"], "y": params["y"]}
elif name == "scroll":
return {"action_type": "scroll", "direction": params["direction"]}
elif name == "open_app":
return {"action_type": "open_app", "app_name": params["app_name"]}
elif name == "input_text":
return {"action_type": "input_text", "text": params["text"]}
elif name == "navigate_back":
return {"action_type": "navigate_back"}
elif name == "navigate_home":
return {"action_type": "navigate_home"}
elif name == "wait":
return {"action_type": "wait"}
elif name == "status":
return None
else:
return None
except Exception:
return None
def build_k_step_transitions(dataset_split, k=1, max_samples=2000, output_dir="./data"):
os.makedirs(output_dir, exist_ok=True)
episodes = defaultdict(list)
for i, item in enumerate(dataset_split):
json_data = item["json"]
episode_id = json_data["episode_id"]
episodes[episode_id].append({
"index": i,
"step_id": json_data["step_id"],
"messages": json_data["messages"],
"png": item["png"],
})
for ep_id in episodes:
episodes[ep_id].sort(key=lambda x: x["step_id"])
transitions = []
for ep_id, steps in episodes.items():
for i in range(len(steps) - k):
state_t = steps[i]
state_tk = steps[i + k]
messages = state_t["messages"]
action_str = None
for msg in messages:
if msg["role"] == "assistant":
action_str = msg["content"]
break
if not action_str:
continue
action = parse_action(action_str)
if action is None:
continue
img_t = state_t["png"]
img_tk = state_tk["png"]
img_t_path = os.path.join(output_dir, f"ep{ep_id}_step{state_t['step_id']}.png")
img_tk_path = os.path.join(output_dir, f"ep{ep_id}_step{state_tk['step_id']}.png")
img_t.save(img_t_path)
img_tk.save(img_tk_path)
problem = (
"You are given two GUI screenshots. The first screenshot shows the current state, "
"and the second screenshot shows the state after executing some actions. "
"Predict the first action that transitions from the first screenshot to the second screenshot."
)
transitions.append({
"image": [img_t_path, img_tk_path],
"conversations": [
{"from": "human", "value": f"<image>\n<image>\n{problem}"},
{"from": "gpt", "value": json.dumps(action)}
],
"problem": problem,
"solution": json.dumps(action),
})
random.shuffle(transitions)
transitions = transitions[:max_samples]
output_path = os.path.join(output_dir, f"ui_transition_training_{max_samples}_k_{k}_no_reasoning.jsonl")
with open(output_path, "w") as f:
for item in transitions:
f.write(json.dumps(item) + "\n")
print(f"Saved {len(transitions)} K-step transitions to {output_path}")
return output_path
if __name__ == "__main__":
print("Loading AndroidControl dataset...")
ds = load_dataset("ckg/AndroidControlParsedWithImages-20k", split="train", streaming=True)
items = []
for i, item in enumerate(ds):
items.append(item)
if i >= 10000:
break
print(f"Processing {len(items)} items...")
output = build_k_step_transitions(items, k=1, max_samples=2000, output_dir="./data")
print(f"Done: {output}")