import torch
from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
from PIL import Image
MODEL_PATH = "Gyubeum/Qwen3-VL-8B-Instruct-UI-Genie-scoring"
MAX_LEN = 8192
model = Qwen3VLForConditionalGeneration.from_pretrained(
MODEL_PATH,
torch_dtype=torch.bfloat16,
device_map="auto",
)
processor = AutoProcessor.from_pretrained(MODEL_PATH, max_pixels=1_048_576)
tokenizer = processor.tokenizer
score_head = torch.nn.Linear(model.config.hidden_size, 1, bias=False).to(model.device, dtype=torch.bfloat16)
from safetensors.torch import load_file
state = load_file(f"{MODEL_PATH}/model-00001-of-00004.safetensors")
def score_action(prefix_messages, response_text, images=None):
"""
Score a GUI agent action.
Args:
prefix_messages: Chat messages up to (not including) the assistant turn.
E.g. [{"role": "system", "content": "..."},
{"role": "user", "content": [{"type":"text","text":"..."},
{"type":"image"}]}]
response_text: The assistant tool-call response to score.
images: List of PIL.Image objects matching image placeholders.
Returns:
float: Scalar BT reward (higher = better action).
"""
prefix_text = processor.apply_chat_template(
prefix_messages, tokenize=False, add_generation_prompt=True
)
full_text = prefix_text + response_text.strip()
inputs = tokenizer(
full_text,
return_tensors="pt",
truncation=True,
max_length=MAX_LEN,
).to(model.device)
if images:
pixel_data = processor.image_processor(images=images, return_tensors="pt")
inputs["pixel_values"] = pixel_data["pixel_values"].to(model.device, dtype=torch.bfloat16)
inputs["image_grid_thw"] = pixel_data["image_grid_thw"].to(model.device)
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
hidden = outputs.hidden_states[-1]
last_idx = inputs["attention_mask"].sum(dim=1) - 1
last_hidden = hidden[0, last_idx[0], :]
reward = model.score(last_hidden.unsqueeze(0)).squeeze().item()
return reward
screenshot = Image.open("screenshot.png").convert("RGB")
SYSTEM_PROMPT = "You are a helpful assistant."
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": [
{"type": "text", "text": "The user query: tap the search button\n"},
{"type": "image"},
]},
]
action_a = '<tool_call>\n{"name": "mobile_use", "arguments": {"action": "click", "coordinate": [540, 120]}}\n</tool_call>'
action_b = '<tool_call>\n{"name": "mobile_use", "arguments": {"action": "click", "coordinate": [100, 800]}}\n</tool_call>'
score_a = score_action(messages, action_a, images=[screenshot])
score_b = score_action(messages, action_b, images=[screenshot])
print(f"Action A: {score_a:.4f}")
print(f"Action B: {score_b:.4f}")
print(f"Preferred: {'A' if score_a > score_b else 'B'}")