Views
No views yet
This repo is deprecated. Please use the new repos:
Version HuggingFace fp16 (full precision) Mano-CUA-4B-Thinking-1.1 MLX-8bit (Apple Silicon) Mano-CUA-4B-Thinking-1.1-MLX-8bit
1pip install mlx-vlm
2pip install git+https://github.com/Mininglamp-AI/cider.gitpip install transformers torch torchvision qwen-vl-utils1from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
2from qwen_vl_utils import process_vision_info
3from PIL import Image
4
5# 1. Load model
6model = Qwen3VLForConditionalGeneration.from_pretrained(
7 "Mininglamp-2718/Mano-P",
8 torch_dtype="auto",
9 device_map="auto",
10)
11processor = AutoProcessor.from_pretrained("Mininglamp-2718/Mano-P")
12
13# 2. Load a screenshot
14img = Image.open("screenshot.png")
15ratio = 1280 / img.width
16img = img.resize((1280, int(img.height * ratio)), Image.LANCZOS)
17
18# 3. Build prompt
19task = "Click the search bar and type hello"
20
21prompt_text = f"""You are a GUI agent. You are given a task and your action history, with screenshots. You need to perform the next action to complete the task.
22
23## Output Format
24<action>具体动作</action>
25
26## Action Space
27open_app(app_name='') # Open an application by name.
28open_url(url='') # Open a URL in the browser.
29hover(start_box='<|box_start|>(x1,y1)<|box_end|>')
30click(start_box='<|box_start|>(x1,y1)<|box_end|>')
31triple_click(start_box='<|box_start|>(x1,y1)<|box_end|>') left click at the coordinate (x1,y1) three times.
32hotkey_click(start_box='<|box_start|>(x1,y1)<|box_end|>', key='') press command key and click at the coordinate (x1,y1).
33right_single(start_box='<|box_start|>(x1,y1)<|box_end|>') right click at the coordinate (x1,y1).
34type(content='') type the content.
35doubleclick(start_box='<|box_start|>(x1,y1)<|box_end|>')
36drag(start_box='<|box_start|>(x1,y1)<|box_end|>', end_box='<|box_start|>(x3,y3)<|box_end|>') # Drag an element from the start coordinate (x1,y1) to the end coordinate (x3,y3).
37hotkey(key='') # Trigger a keyboard shortcut.
38wait(duration='') # Sleep for specified duration (in seconds) and take a screenshot to check for any changes.
39call_user() # Request human assistance
40stop(reason='') # If the item can not found in the image, give the reason
41scroll(start_box='<|box_start|>(x1,y1)<|box_end|>', direction='down or up or right or left', amount='scroll_amount') # Scroll on the specified direction at the coordinate (x1,y1) by the given amount
42finish() # The task is completed.
43
44## User Instruction
45{task}"""
46
47
48messages = [
49 {"role": "system", "content": "You are a helpful assistant."},
50 {"role": "user", "content": [
51 {"type": "image", "image": img},
52 {"type": "text", "text": prompt_text},
53 ]},
54]
55
56# 4. Run inference
57text_input = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
58image_inputs, video_inputs = process_vision_info(messages)
59inputs = processor(
60 text=[text_input], images=image_inputs, videos=video_inputs,
61 padding=True, return_tensors="pt",
62).to(model.device)
63
64output_ids = model.generate(**inputs, max_new_tokens=512, temperature=0.0, do_sample=False)
65output_ids = output_ids[:, inputs.input_ids.shape[1]:]
66output = processor.batch_decode(output_ids, skip_special_tokens=True)[0]
67
68print(output)1import mlx_vlm as pm
2from vlm_service import custom_generate
3from PIL import Image
4
5# 1. Load model
6model, processor = pm.load("Mininglamp-2718/Mano-P")
7
8# 2. Load a screenshot (or any desktop screenshot image)
9img = Image.open("screenshot.png")
10# Resize to 1280px width (model's expected input resolution)
11ratio = 1280 / img.width
12img = img.resize((1280, int(img.height * ratio)), Image.LANCZOS)
13
14# 3. Build prompt
15task = "Click the search bar and type hello"
16
17prompt_text = f"""You are a GUI agent. You are given a task and your action history, with screenshots. You need to perform the next action to complete the task.
18
19## Output Format
20<action>具体动作</action>
21
22## Action Space
23open_app(app_name='') # Open an application by name.
24open_url(url='') # Open a URL in the browser.
25hover(start_box='<|box_start|>(x1,y1)<|box_end|>')
26click(start_box='<|box_start|>(x1,y1)<|box_end|>')
27triple_click(start_box='<|box_start|>(x1,y1)<|box_end|>') left click at the coordinate (x1,y1) three times.
28hotkey_click(start_box='<|box_start|>(x1,y1)<|box_end|>', key='') press command key and click at the coordinate (x1,y1).
29right_single(start_box='<|box_start|>(x1,y1)<|box_end|>') right click at the coordinate (x1,y1).
30type(content='') type the content.
31doubleclick(start_box='<|box_start|>(x1,y1)<|box_end|>')
32drag(start_box='<|box_start|>(x1,y1)<|box_end|>', end_box='<|box_start|>(x3,y3)<|box_end|>') # Drag an element from the start coordinate (x1,y1) to the end coordinate (x3,y3).
33hotkey(key='') # Trigger a keyboard shortcut.
34wait(duration='') # Sleep for specified duration (in seconds) and take a screenshot to check for any changes.
35call_user() # Request human assistance
36stop(reason='') # If the item can not found in the image, give the reason
37scroll(start_box='<|box_start|>(x1,y1)<|box_end|>', direction='down or up or right or left', amount='scroll_amount') # Scroll on the specified direction at the coordinate (x1,y1) by the given amount
38finish() # The task is completed.
39
40## User Instruction
41{task}"""
42
43
44messages = [
45 {"role": "system", "content": "You are a helpful assistant."},
46 {"role": "user", "content": prompt_text},
47]
48prompt = processor.tokenizer.apply_chat_template(
49 messages, tokenize=False, add_generation_prompt=True
50)
51prompt = prompt.replace("<image>", "<|vision_start|><|image_pad|><|vision_end|>")
52
53# 4. Run inference
54result = custom_generate(
55 model, processor, prompt,
56 [img],
57 max_tokens=512,
58 temperature=0.0,
59 prefill_step_size=2048,
60)
61
62print(f"Tokens: {result.generation_tokens}, Speed: {result.generation_tps:.1f} tok/s")
63print(result.text)1import mlx_vlm as pm
2from vlm_service import custom_generate
3from PIL import Image
4import re
5
6model, processor = pm.load("Mininglamp-2718/Mano-P")
7
8SYSTEM_PROMPT = "You are a helpful assistant."
9
10INSTRUCTION_TEMPLATE = """You are a GUI agent. You are given a task and your action history, with screenshots. You need to perform the next action to complete the task.
11
12## Output Format
13<think>思考过程</think>
14<action_desp>动作描述</action_desp>
15<action>具体动作</action>
16
17## Action Space
18open_app(app_name='') # Open an application by name.
19open_url(url='') # Open a URL in the browser.
20hover(start_box='<|box_start|>(x1,y1)<|box_end|>')
21click(start_box='<|box_start|>(x1,y1)<|box_end|>')
22triple_click(start_box='<|box_start|>(x1,y1)<|box_end|>') left click at the coordinate (x1,y1) three times.
23hotkey_click(start_box='<|box_start|>(x1,y1)<|box_end|>', key='') press command key and click at the coordinate (x1,y1).
24right_single(start_box='<|box_start|>(x1,y1)<|box_end|>') right click at the coordinate (x1,y1).
25type(content='') type the content.
26doubleclick(start_box='<|box_start|>(x1,y1)<|box_end|>')
27drag(start_box='<|box_start|>(x1,y1)<|box_end|>', end_box='<|box_start|>(x3,y3)<|box_end|>') # Drag an element from the start coordinate (x1,y1) to the end coordinate (x3,y3).
28hotkey(key='') # Trigger a keyboard shortcut.
29wait(duration='') # Sleep for specified duration (in seconds) and take a screenshot to check for any changes.
30call_user() # Request human assistance
31stop(reason='') # If the item can not found in the image, give the reason
32scroll(start_box='<|box_start|>(x1,y1)<|box_end|>', direction='down or up or right or left', amount='scroll_amount') # Scroll on the specified direction at the coordinate (x1,y1) by the given amount
33finish() # The task is completed.
34
35## Note
36- Use Chinese in `<think>` part.
37- Write a small plan and finally summarize your next action (with its target element) in one sentence in `<action_desp>` part.
38
39## User Instruction
40{task}
41
42{history}
43当前步骤的截图为<image>"""
44
45
46def resize(img, width=1280):
47 ratio = width / img.width
48 return img.resize((width, int(img.height * ratio)), Image.LANCZOS)
49
50
51def build_prompt(task, history_steps, current_img):
52 """Build prompt with action history and current screenshot."""
53 images = []
54
55 # Include last history screenshot + current screenshot
56 history_lines = []
57 for i, step in enumerate(history_steps):
58 if i == len(history_steps) - 1 and step.get("screenshot"):
59 images.append(step["screenshot"])
60 history_lines.append(f"第{i+1}步: {step['desc']}, 对应截图为: <image>")
61 else:
62 history_lines.append(f"第{i+1}步: {step['desc']}")
63
64 history_text = "\n".join(history_lines) if history_lines else ""
65 images.append(current_img)
66
67 text = INSTRUCTION_TEMPLATE.format(task=task, history=history_text)
68
69 messages = [
70 {"role": "system", "content": SYSTEM_PROMPT},
71 {"role": "user", "content": text},
72 ]
73 prompt = processor.tokenizer.apply_chat_template(
74 messages, tokenize=False, add_generation_prompt=True
75 )
76 # Replace <image> placeholders with vision tokens (right-to-left)
77 for _ in range(len(images)):
78 pos = prompt.rfind("<image>")
79 if pos >= 0:
80 prompt = prompt[:pos] + "<|vision_start|><|image_pad|><|vision_end|>" + prompt[pos + 7:]
81 return prompt, images
82
83
84def parse_output(text):
85 """Extract think, action_desp, action from model output."""
86 def extract(tag):
87 m = re.search(rf"<{tag}>(.*?)</{tag}>", text, re.DOTALL)
88 return m.group(1).strip() if m else ""
89 return extract("think"), extract("action_desp"), extract("action")
90
91
92# --- Agent loop ---
93task = "Open Safari and search for 'MLX framework'"
94history = []
95max_steps = 10
96
97for step in range(max_steps):
98 # Take screenshot (replace with your own screenshot capture)
99 screenshot = resize(Image.open(f"step_{step}.png"))
100
101 # Build prompt and run inference
102 prompt, images = build_prompt(task, history, screenshot)
103 result = custom_generate(
104 model, processor, prompt, images,
105 max_tokens=512, temperature=0.0, prefill_step_size=2048,
106 )
107
108 think, action_desp, action = parse_output(result.text)
109 print(f"[Step {step+1}] {action_desp}")
110 print(f" Action: {action}")
111
112 # Check terminal actions
113 if action.startswith("finish"):
114 print("Task completed!")
115 break
116 if action.startswith("stop"):
117 print("Task infeasible.")
118 break
119
120 # Record history for next step
121 history.append({"desc": action_desp, "screenshot": screenshot})
122
123 # >>> Execute the action on screen, then loop back to take new screenshot <<<1<think>The search bar is at the top of the page...</think>
2<action_desp>Click the search bar to focus it</action_desp>
3<action>click(start_box='<|box_start|>(500,38)<|box_end|>')</action>[0, 1000] range. To convert to pixel coordinates:1pixel_x = int(x / 1000 * screen_width)
2pixel_y = int(y / 1000 * screen_height)1from cider import convert_model, is_available
2
3if is_available():
4 convert_model(model.language_model)| Action | Syntax | Description |
|---|---|---|
| open_app | open_app(app_name='') | Open an application |
| open_url | open_url(url='') | Open a URL |
| click | click(start_box='<|box_start|>(x,y)<|box_end|>') | Left click |
| doubleclick | doubleclick(start_box='<|box_start|>(x,y)<|box_end|>') | Double click |
| triple_click | triple_click(start_box='<|box_start|>(x,y)<|box_end|>') | Triple click (select line) |
| right_single | right_single(start_box='<|box_start|>(x,y)<|box_end|>') | Right click |
| hover | hover(start_box='<|box_start|>(x,y)<|box_end|>') | Mouse hover |
| type | type(content='text') | Type text |
| hotkey | hotkey(key='cmd+c') | Keyboard shortcut |
| hotkey_click | hotkey_click(start_box='<|box_start|>(x,y)<|box_end|>', key='shift') | Modifier + click |
| scroll | scroll(start_box='<|box_start|>(x,y)<|box_end|>', direction='down', amount='3') | Scroll |
| drag | drag(start_box='<|box_start|>(x1,y1)<|box_end|>', end_box='<|box_start|>(x2,y2)<|box_end|>') | Drag and drop |
| wait | wait(duration='2') | Wait (seconds) |
| finish | finish() | Task completed |
| stop | stop(reason='...') | Task infeasible |
| call_user | call_user() | Request human help |