Instructions to use yuta24/ui-test-vision-2b-alpha with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use yuta24/ui-test-vision-2b-alpha with PEFT:
Task type is invalid.
- MLX
How to use yuta24/ui-test-vision-2b-alpha with MLX:
# Make sure mlx-vlm is installed # pip install --upgrade mlx-vlm from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template from mlx_vlm.utils import load_config # Load the model model, processor = load("yuta24/ui-test-vision-2b-alpha") config = load_config("yuta24/ui-test-vision-2b-alpha") # Prepare input image = ["http://images.cocodataset.org/val2017/000000039769.jpg"] prompt = "Describe this image." # Apply chat template formatted_prompt = apply_chat_template( processor, config, prompt, num_images=1 ) # Generate output output = generate(model, processor, formatted_prompt, image) print(output) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
File size: 12,499 Bytes
e379257 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | """Generate a tiny synthetic dataset of dialog-like iPhone screenshots (PIL) for smoke tests.
!! The "teacher" distributions here are DUMMY (derived from the gold label + noise), NOT Clef outputs. !!
Replace them later with real teacher labels (scripts/label_workers_ai.py or label_local_clef.py).
--box adds Jev-style box questions (`primary_button_box` + presence `primary_button_box_visible`) whose gold is the
exact drawn button rectangle (ground truth, not the teacher). Images are identical with and without --box."""
import argparse, json, random
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
FONT_CANDIDATES = ["C:/Windows/Fonts/YuGothM.ttc", "C:/Windows/Fonts/meiryo.ttc", "C:/Windows/Fonts/msgothic.ttc",
"/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc", "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"]
def font(size):
for p in FONT_CANDIDATES:
if Path(p).exists():
try:
return ImageFont.truetype(p, size)
except OSError:
pass
return ImageFont.load_default(size=size)
TXT = {
"ja": {"err_title": ["エラー", "通信エラー", "保存に失敗しました"], "err_msg": ["ネットワークに接続できません。", "もう一度お試しください。", "サーバーが応答しません。"],
"ok": "OK", "retry": "再試行", "cancel": "キャンセル", "delete": "削除", "confirm_title": ["削除しますか?", "変更を破棄しますか?"],
"confirm_msg": ["この操作は取り消せません。"], "loading": ["読み込み中…", "お待ちください"], "login": "ログイン",
"email": "メールアドレス", "password": "パスワード", "home": ["ホーム", "最近の項目"], "row": ["メモ", "写真", "設定", "お気に入り", "ダウンロード"]},
"en": {"err_title": ["Error", "Network Error", "Save Failed"], "err_msg": ["Unable to connect to the network.", "Please try again.", "The server is not responding."],
"ok": "OK", "retry": "Retry", "cancel": "Cancel", "delete": "Delete", "confirm_title": ["Delete this item?", "Discard changes?"],
"confirm_msg": ["This action cannot be undone."], "loading": ["Loading…", "Please wait"], "login": "Log In",
"email": "Email", "password": "Password", "home": ["Home", "Recents"], "row": ["Notes", "Photos", "Settings", "Favorites", "Downloads"]},
}
SCREENS = ["error_dialog", "confirm_dialog", "loading", "login", "home"]
BUTTONS = ["ok", "retry", "delete", "login", "none"]
def questions(lang):
ja = lang == "ja"
return {
"screen": {"type": "choice",
"instructions": "現在の画面状態はどれか?" if ja else "What is the current screen state?",
"criteria": {"error_dialog": "An error alert/dialog is shown / エラーダイアログが表示されている",
"confirm_dialog": "A confirmation dialog asks the user to confirm / 確認ダイアログ",
"loading": "A loading spinner or progress indicator / 読み込み中",
"login": "A login form with email and password / ログインフォーム",
"home": "A normal home/list screen without dialogs / 通常のホーム・一覧画面"}},
"has_error": {"type": "noul", "instructions": "エラーダイアログが表示されているか?" if ja else "Is an error dialog visible?"},
"primary_button": {"type": "choice",
"instructions": "最も目立つ操作ボタンはどれか?" if ja else "Which is the primary action button?",
"criteria": {"ok": "OK button", "retry": "Retry / 再試行", "delete": "Delete / 削除",
"login": "Log In / ログイン", "none": "No primary button"}},
}
def box_questions(lang):
ja = lang == "ja"
return {
"primary_button_box": {"type": "box",
"instructions": "`screen` 内の最も目立つ操作ボタン" if ja else "the primary action button in `screen`"},
"primary_button_box_visible": {"type": "noul",
"instructions": "`screen` に主要な操作ボタンが見えているか?" if ja else "Is a primary action button visible in `screen`?"},
}
def box_gold(rect, W, H):
"""Pixel rect (x0, y0, x1, y1) -> Jev box gold (fractions of height/width). None -> not present."""
if rect is None:
return {"present": False}
x0, y0, x1, y1 = rect
return {"top": round(y0 / H, 4), "bottom": round(y1 / H, 4), "left": round(x0 / W, 4), "right": round(x1 / W, 4), "present": True}
def draw_status_bar(d, W, dark):
fg = (255, 255, 255) if dark else (0, 0, 0)
d.text((90, 50), f"{random.randint(9,23)}:{random.randint(0,59):02d}", fill=fg, font=font(44))
d.rectangle([W - 190, 60, W - 100, 95], outline=fg, width=4)
def dialog(d, W, H, title, msg, buttons, dark, primary_color):
bw, bh = int(W * 0.72), int(H * 0.2)
x0, y0 = (W - bw) // 2, int(H * random.uniform(0.33, 0.45))
bg = (44, 44, 46) if dark else (242, 242, 247)
fg = (255, 255, 255) if dark else (0, 0, 0)
d.rounded_rectangle([x0, y0, x0 + bw, y0 + bh], radius=40, fill=bg)
d.text((x0 + bw // 2, y0 + 80), title, fill=fg, font=font(52), anchor="mm")
d.text((x0 + bw // 2, y0 + 170), msg, fill=fg, font=font(38), anchor="mm")
d.line([x0, y0 + bh - 140, x0 + bw, y0 + bh - 140], fill=(120, 120, 128), width=2)
n = len(buttons); primary_rect = None
for i, (label, is_primary) in enumerate(buttons):
if is_primary: # the tappable cell of the primary button
primary_rect = (x0 + bw * i // n, y0 + bh - 140, x0 + bw * (i + 1) // n, y0 + bh)
cx = x0 + bw * (2 * i + 1) // (2 * n)
d.text((cx, y0 + bh - 70), label, fill=primary_color if is_primary else (10, 132, 255), font=font(46 if is_primary else 42), anchor="mm")
if i:
d.line([x0 + bw * i // n, y0 + bh - 140, x0 + bw * i // n, y0 + bh], fill=(120, 120, 128), width=2)
return primary_rect
def render(screen, lang, W, H):
t = TXT[lang]; dark = random.random() < 0.3
img = Image.new("RGB", (W, H), (0, 0, 0) if dark else (255, 255, 255)); d = ImageDraw.Draw(img)
fg = (255, 255, 255) if dark else (0, 0, 0)
draw_status_bar(d, W, dark)
d.text((80, 200), random.choice(t["home"]), fill=fg, font=font(96))
for i in range(random.randint(4, 9)): # background list
y = 380 + i * 170
d.text((90, y), random.choice(t["row"]) + f" {random.randint(1, 99)}", fill=fg, font=font(50))
d.line([90, y + 110, W, y + 110], fill=(200, 200, 205) if not dark else (60, 60, 64), width=2)
primary = "none"; rect = None
if screen in ("error_dialog", "confirm_dialog", "loading"):
ov = Image.new("RGBA", (W, H), (0, 0, 0, random.randint(80, 140)))
img = Image.alpha_composite(img.convert("RGBA"), ov).convert("RGB"); d = ImageDraw.Draw(img)
if screen == "error_dialog":
if random.random() < 0.5:
rect = dialog(d, W, H, random.choice(t["err_title"]), random.choice(t["err_msg"]), [(t["ok"], True)], dark, (10, 132, 255)); primary = "ok"
else:
rect = dialog(d, W, H, random.choice(t["err_title"]), random.choice(t["err_msg"]), [(t["cancel"], False), (t["retry"], True)], dark, (10, 132, 255)); primary = "retry"
elif screen == "confirm_dialog":
rect = dialog(d, W, H, random.choice(t["confirm_title"]), random.choice(t["confirm_msg"]), [(t["cancel"], False), (t["delete"], True)], dark, (255, 59, 48)); primary = "delete"
elif screen == "loading":
cx, cy, r = W // 2, H // 2, 90
d.rounded_rectangle([cx - 220, cy - 220, cx + 220, cy + 220], radius=40, fill=(60, 60, 64))
for k in range(12):
d.arc([cx - r, cy - r - 40, cx + r, cy + r - 40], k * 30, k * 30 + 18, fill=(255, 255, 255), width=14)
d.text((cx, cy + 150), random.choice(t["loading"]), fill=(255, 255, 255), font=font(44), anchor="mm")
elif screen == "login":
img = Image.new("RGB", (W, H), (0, 0, 0) if dark else (255, 255, 255)); d = ImageDraw.Draw(img); draw_status_bar(d, W, dark)
d.text((W // 2, 500), t["login"], fill=fg, font=font(96), anchor="mm")
for i, ph in enumerate([t["email"], t["password"]]):
y = 800 + i * 220
d.rounded_rectangle([100, y, W - 100, y + 150], radius=24, outline=(160, 160, 166), width=4)
d.text((140, y + 75), ph, fill=(140, 140, 146), font=font(48), anchor="lm")
d.rounded_rectangle([100, 1350, W - 100, 1500], radius=30, fill=(10, 132, 255))
d.text((W // 2, 1425), t["login"], fill=(255, 255, 255), font=font(54), anchor="mm"); primary = "login"
rect = (100, 1350, W - 100, 1500)
return img, {"screen": screen, "has_error": "true" if screen == "error_dialog" else "false", "primary_button": primary}, rect
def dummy_teacher(qs, gold, rng=random):
"""DUMMY: gold-centred noisy distribution. Not a real teacher."""
out = {}
for qid, q in qs.items():
if q["type"] == "box": # box gold is ground truth; Clef has no box type
continue
opts = ["true", "false"] if q["type"] == "noul" else list(q["criteria"])
p_gold = rng.uniform(0.55, 0.95)
if rng.random() < 0.08: # occasional "teacher mistake" to mimic label noise
p_gold = rng.uniform(0.05, 0.3)
rest = [rng.random() for _ in opts[1:]]; s = sum(rest)
others = [o for o in opts if o != gold[qid]]
dist = {gold[qid]: p_gold}
for o, r in zip(others, rest):
dist[o] = (1 - p_gold) * r / s
out[qid] = {o: round(dist[o], 4) for o in opts}
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", default="data/synthetic")
ap.add_argument("--n-train", type=int, default=60)
ap.add_argument("--n-eval", type=int, default=20)
ap.add_argument("--width", type=int, default=1179)
ap.add_argument("--height", type=int, default=2556)
ap.add_argument("--seed", type=int, default=0)
ap.add_argument("--box", action="store_true", help="also add Jev-style box questions (+ presence) with ground-truth gold")
args = ap.parse_args(); random.seed(args.seed); box_rng = random.Random(args.seed + 1)
out = Path(args.out); (out / "images").mkdir(parents=True, exist_ok=True)
for split, n in (("train", args.n_train), ("eval", args.n_eval)):
with open(out / f"{split}.jsonl", "w", encoding="utf-8") as f:
for i in range(n):
screen = SCREENS[i % len(SCREENS)]; lang = random.choice(["ja", "en"])
img, gold, rect = render(screen, lang, args.width, args.height)
rid = f"syn-{split}-{i:04d}"; rel = f"images/{rid}.png"; img.save(out / rel)
qs = questions(lang); teacher = dummy_teacher(qs, gold)
if args.box: # separate RNG: images/labels of the base questions stay identical
bq = box_questions(lang)
gold["primary_button_box"] = box_gold(rect, args.width, args.height)
gold["primary_button_box_visible"] = "true" if rect else "false"
teacher.update(dummy_teacher(bq, gold, box_rng)); qs.update(bq)
rec = {"id": rid, "images": [rel],
"state": {"app": "SyntheticApp", "locale": "ja-JP" if lang == "ja" else "en-US", "device": "iPhone (synthetic)"},
"questions": qs, "teacher": teacher, "gold": gold,
"meta": {"source": "synthetic_pil", "teacher_model": "DUMMY(gold+noise)", "teacher_is_dummy": True,
"split_group": "SyntheticApp", "lang": lang}}
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
print(f"wrote {args.n_train}+{args.n_eval} records to {out.resolve()}")
if __name__ == "__main__":
main()
|