--- base_model: - Qwen/Qwen2.5-VL-3B-Instruct language: - en license: apache-2.0 pipeline_tag: image-text-to-text library_name: transformers --- This repository contains the model based on Qwen2.5-VL-3B as presented in [Scaling Computer-Use Grounding via User Interface Decomposition and Synthesis](https://arxiv.org/abs/2505.13227). Project page: https://osworld-grounding.github.io For code and sample usage, see https://github.com/xlang-ai/OSWorld-G. To use our model, we recommend using `vllm`. You need to carefully follow the computer use agent template from Qwen-2.5-VL, and be very careful with the image size to enable the best performance. We show a small example here (You can also run [`demo.py`](demo.py) to see the demo): ``` python import json import re from PIL import Image, ImageDraw from transformers import AutoTokenizer from vllm import LLM, SamplingParams from agent_function_call import ComputerUse from transformers.models.qwen2_vl.image_processing_qwen2_vl_fast import smart_resize from transformers import Qwen2_5_VLProcessor from huggingface_hub import hf_hub_download model_path = "xlangai/Jedi-3B-1080p" # model_path = "xlangai/Jedi-7B-1080p" FN_CALL_TEMPLATE = """You are a helpful assistant. # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: {tool_descs} For each function call, return a json object with function name and arguments within XML tags: {{"name": , "arguments": }} """ def visualize_click_position(image, coords, circle_radius=9, point_radius=3): draw = ImageDraw.Draw(image) x, y = coords draw.ellipse( [x - circle_radius, y - circle_radius, x + circle_radius, y + circle_radius], outline="lightgreen", width=2, ) draw.ellipse( [x - point_radius, y - point_radius, x + point_radius, y + point_radius], fill="lightgreen", ) return image def parse_coordinates(response): match = re.search(r"(.*?)", response, re.DOTALL) action = None if not match: raise ValueError("No block found in response.") try: action = json.loads(match.group(1)) except json.JSONDecodeError as e: raise ValueError(f"Failed to parse tool_call JSON: {e}") action_name = action["name"] action_type = action["arguments"]["action"] action_args = action["arguments"]["coordinate"] if ( action_name != "computer_use" or action_type not in ("mouse_move", "left_click", "right_click", "double_click") or action_args is None ): print(f"Error parsing coordinates: {response}") return None return action_args def main(): processor = Qwen2_5_VLProcessor.from_pretrained(model_path) input_image = Image.open("demo_image.png") instruction = "Open the filter function for search settings." resized_height, resized_width = smart_resize( input_image.height, input_image.width, factor=processor.image_processor.patch_size * processor.image_processor.merge_size, min_pixels=processor.image_processor.min_pixels, max_pixels=processor.image_processor.max_pixels, ) computer_use = ComputerUse( cfg={ "display_width_px": resized_width, "display_height_px": resized_height, } ) tools = [computer_use.function] tool_descs = [{"type": "function", "function": f} for f in tools] tool_descs = " ".join([json.dumps(f, ensure_ascii=False) for f in tool_descs]) llm = LLM( model=model_path, tokenizer_mode="slow", dtype="bfloat16", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained( model_path, trust_remote_code=True, use_fast=False ) chat_template_path = hf_hub_download( repo_id=model_path, filename="chat_template.json" ) with open(chat_template_path, "r") as f: tokenizer.chat_template = json.load(f)["chat_template"] messages = [ { "role": "system", "content": [ { "type": "text", "text": FN_CALL_TEMPLATE.format(tool_descs=tool_descs), } ], }, { "role": "user", "content": [ { "type": "image", }, { "type": "text", "text": instruction, }, ], }, ] sampling_params = SamplingParams( temperature=0.01, max_tokens=1024, top_k=1, ) message = tokenizer.apply_chat_template(messages, add_generation_prompt=True) outputs = llm.generate( { "prompt_token_ids": message, "multi_modal_data": { "image": input_image, }, }, sampling_params=sampling_params, ) generated_tokens = outputs[0].outputs[0].token_ids response = tokenizer.decode(generated_tokens, skip_special_tokens=True) predicted_coords = parse_coordinates(response) print("predicted_coords: ", predicted_coords) if predicted_coords: viz_image = visualize_click_position(input_image, predicted_coords) viz_image.save("click_visualization.png") return predicted_coords if __name__ == "__main__": main() ``` ## 📄 Citation If you find this work useful, please consider citing our paper: ```bibtex @misc{xie2025scalingcomputerusegroundinguser, title={Scaling Computer-Use Grounding via User Interface Decomposition and Synthesis}, author={Tianbao Xie and Jiaqi Deng and Xiaochuan Li and Junlin Yang and Haoyuan Wu and Jixuan Chen and Wenjing Hu and Xinyuan Wang and Yuhui Xu and Zekun Wang and Yiheng Xu and Junli Wang and Doyen Sahoo and Tao Yu and Caiming Xiong}, year={2025}, eprint={2505.13227}, archivePrefix={arXiv}, primaryClass={cs.AI}, url={https://arxiv.org/abs/2505.13227}, } ```