computer-use-ootb-internal 0.0.96__py3-none-any.whl → 0.0.96.post2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- computer_use_ootb_internal/app_teachmode.py +2 -2
- computer_use_ootb_internal/computer_use_demo/animation/click_animation.py +61 -134
- computer_use_ootb_internal/computer_use_demo/animation/test_animation.py +40 -0
- computer_use_ootb_internal/computer_use_demo/executor/teachmode_executor.py +16 -12
- computer_use_ootb_internal/computer_use_demo/tools/computer.py +14 -3
- computer_use_ootb_internal/run_teachmode_ootb_args.py +4 -7
- {computer_use_ootb_internal-0.0.96.dist-info → computer_use_ootb_internal-0.0.96.post2.dist-info}/METADATA +1 -1
- {computer_use_ootb_internal-0.0.96.dist-info → computer_use_ootb_internal-0.0.96.post2.dist-info}/RECORD +10 -12
- computer_use_ootb_internal/example_websocket_js.html +0 -41
- computer_use_ootb_internal/service_teachmode.py +0 -194
- computer_use_ootb_internal/service_teachmode_test.py +0 -41
- {computer_use_ootb_internal-0.0.96.dist-info → computer_use_ootb_internal-0.0.96.post2.dist-info}/WHEEL +0 -0
- {computer_use_ootb_internal-0.0.96.dist-info → computer_use_ootb_internal-0.0.96.post2.dist-info}/entry_points.txt +0 -0
@@ -104,8 +104,8 @@ async def update_parameters(request: Request):
|
|
104
104
|
shared_state.model = getattr(shared_state.args, 'model', "teach-mode-gpt-4o")
|
105
105
|
shared_state.task = getattr(shared_state.args, 'task', "Create a claim on the SAP system, using Receipt.pdf as attachment.")
|
106
106
|
shared_state.selected_screen = getattr(shared_state.args, 'selected_screen', 0)
|
107
|
-
shared_state.user_id = getattr(shared_state.args, 'user_id', "
|
108
|
-
shared_state.trace_id = getattr(shared_state.args, 'trace_id', "
|
107
|
+
shared_state.user_id = getattr(shared_state.args, 'user_id', "hero_cases")
|
108
|
+
shared_state.trace_id = getattr(shared_state.args, 'trace_id', "build_scroll_combat")
|
109
109
|
shared_state.api_keys = getattr(shared_state.args, 'api_keys', "sk-proj-1234567890")
|
110
110
|
shared_state.server_url = getattr(shared_state.args, 'server_url', "http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com")
|
111
111
|
|
@@ -1,154 +1,81 @@
|
|
1
|
-
""
|
2
|
-
|
3
|
-
→ 在屏幕 (x,y) 显示点击动画,停留 duration_ms 毫秒
|
4
|
-
依赖: pyside6
|
5
|
-
确保同目录有 click.gif
|
6
|
-
"""
|
7
|
-
import sys, time
|
1
|
+
# click_anim_async.py ← put this in its own file (important for Windows "spawn")
|
2
|
+
import sys, multiprocessing as mp
|
8
3
|
from pathlib import Path
|
9
|
-
from PySide6.QtCore
|
10
|
-
from PySide6.QtGui
|
4
|
+
from PySide6.QtCore import Qt, QPoint, QTimer, QEasingCurve, QPropertyAnimation, QSize
|
5
|
+
from PySide6.QtGui import QMovie
|
11
6
|
from PySide6.QtWidgets import QApplication, QWidget, QLabel
|
12
7
|
|
13
8
|
CLICK_GIF = Path(__file__).with_name("icons8-select-cursor-transparent-96.gif")
|
14
9
|
|
10
|
+
# ---------------------------- tiny in‑process GUI helpers ----------------------------
|
15
11
|
class ClickAnimation(QWidget):
|
16
|
-
def __init__(self, pos: QPoint, life_ms: int):
|
12
|
+
def __init__(self, pos: QPoint, life_ms: int, size_px: int = 50):
|
17
13
|
super().__init__(None,
|
18
14
|
Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint
|
19
15
|
| Qt.WindowTransparentForInput)
|
20
16
|
self.setAttribute(Qt.WA_TranslucentBackground)
|
21
|
-
|
22
|
-
if not CLICK_GIF.exists():
|
23
|
-
print(f"Error: click.gif not found at {CLICK_GIF}")
|
24
|
-
return
|
25
|
-
|
26
|
-
try:
|
27
|
-
# 创建标签显示GIF
|
28
|
-
self.label = QLabel(self)
|
29
|
-
self.movie = QMovie(str(CLICK_GIF))
|
30
|
-
|
31
|
-
# 获取原始尺寸并打印(仅供参考)
|
32
|
-
self.movie.jumpToFrame(0)
|
33
|
-
original_size = self.movie.currentPixmap().size()
|
34
|
-
print(f"GIF original size: {original_size.width()}x{original_size.height()}")
|
35
|
-
|
36
|
-
# 将GIF缩放到30x30像素
|
37
|
-
target_size = QSize(50, 50)
|
38
|
-
self.movie.setScaledSize(target_size)
|
39
|
-
|
40
|
-
# 设置标签尺寸和GIF
|
41
|
-
self.label.setMovie(self.movie)
|
42
|
-
self.label.setFixedSize(target_size)
|
43
|
-
|
44
|
-
# 设置窗口大小和位置
|
45
|
-
self.resize(target_size)
|
46
|
-
self.move(pos.x() - 15, pos.y() - 15) # 居中显示
|
47
|
-
|
48
|
-
# 提高播放性能
|
49
|
-
self.movie.setCacheMode(QMovie.CacheAll)
|
50
|
-
|
51
|
-
# 开始播放动画
|
52
|
-
self.movie.start()
|
53
|
-
|
54
|
-
# 设置定时器关闭窗口
|
55
|
-
QTimer.singleShot(life_ms, self.close)
|
56
|
-
|
57
|
-
self.show()
|
58
|
-
self.raise_()
|
59
|
-
print(f"Click animation created at ({pos.x()}, {pos.y()}), size: 30x30, duration: {life_ms}ms")
|
60
|
-
except Exception as e:
|
61
|
-
print(f"Error creating click animation: {str(e)}")
|
62
17
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
18
|
+
self.label = QLabel(self)
|
19
|
+
movie = QMovie(str(CLICK_GIF))
|
20
|
+
movie.setScaledSize(QSize(size_px, size_px))
|
21
|
+
self.label.setMovie(movie)
|
22
|
+
self.label.setFixedSize(size_px, size_px)
|
23
|
+
|
24
|
+
self.resize(size_px, size_px)
|
25
|
+
self.move(pos.x() - size_px//2, pos.y() - size_px//2)
|
26
|
+
|
27
|
+
movie.setCacheMode(QMovie.CacheAll)
|
28
|
+
movie.start()
|
29
|
+
QTimer.singleShot(life_ms, self.close)
|
30
|
+
self.show()
|
31
|
+
self.raise_()
|
32
|
+
|
33
|
+
# ------------------------- worker functions that live in a **child** -----------------
|
34
|
+
def _worker_click(x, y, duration_ms, existing_ms):
|
35
|
+
app = QApplication(sys.argv)
|
36
|
+
total = duration_ms + existing_ms
|
37
|
+
widget = ClickAnimation(QPoint(x, y), total) # Store in variable to prevent garbage collection
|
38
|
+
QTimer.singleShot(total + 200, app.quit) # close event‑loop afterwards
|
39
|
+
app.exec()
|
40
|
+
|
41
|
+
def _worker_move(x1, y1, x2, y2, duration_ms, existing_ms):
|
42
|
+
app = QApplication(sys.argv)
|
43
|
+
total = duration_ms + existing_ms
|
44
|
+
widget = ClickAnimation(QPoint(x1, y1), total)
|
74
45
|
|
75
|
-
def show_click(x: int, y: int, duration_ms: int = 2000): # 增加默认播放时间
|
76
|
-
"""阻塞式点击动画:调用后必定肉眼可见"""
|
77
|
-
print(f"Attempting to show click at ({x}, {y})")
|
78
|
-
|
79
|
-
if not CLICK_GIF.exists():
|
80
|
-
raise FileNotFoundError(f"click.gif not found at {CLICK_GIF}")
|
81
|
-
|
82
|
-
_ensure_app()
|
83
|
-
|
84
|
-
try:
|
85
|
-
animation = ClickAnimation(QPoint(x, y), duration_ms)
|
86
|
-
|
87
|
-
# 局部事件循环,动画结束后返回
|
88
|
-
loop = QEventLoop()
|
89
|
-
QTimer.singleShot(duration_ms + 150, loop.quit) # 增加等待时间
|
90
|
-
loop.exec()
|
91
|
-
print("Click animation completed")
|
92
|
-
except Exception as e:
|
93
|
-
print(f"Error during show_click: {str(e)}")
|
94
|
-
|
95
|
-
|
96
|
-
# --- 在原 import 区域追加 ---
|
97
|
-
from PySide6.QtCore import QEasingCurve, QPropertyAnimation
|
98
|
-
# --------------------------------------------------------
|
99
|
-
|
100
|
-
|
101
|
-
# ---------- 新增函数 ----------
|
102
|
-
def show_move_to(x1: int, y1: int, x2: int, y2: int, duration_ms: int = 1200):
|
103
|
-
"""
|
104
|
-
阻塞式移动动画:在 (x1, y1) 处出现光标 GIF,
|
105
|
-
并在 duration_ms 毫秒内平滑移动到 (x2, y2)。
|
106
|
-
|
107
|
-
Args:
|
108
|
-
x1, y1 : 起点屏幕坐标
|
109
|
-
x2, y2 : 终点屏幕坐标
|
110
|
-
duration_ms : 移动总时长
|
111
|
-
"""
|
112
|
-
print(f"Attempting to move click from ({x1}, {y1}) → ({x2}, {y2}) "
|
113
|
-
f"in {duration_ms} ms")
|
114
|
-
|
115
|
-
if not CLICK_GIF.exists():
|
116
|
-
raise FileNotFoundError(f"click.gif not found at {CLICK_GIF}")
|
117
|
-
|
118
|
-
_ensure_app()
|
119
|
-
|
120
|
-
# 让 widget 的生命周期略长于动画,避免提前销毁
|
121
|
-
life_ms = duration_ms + 200
|
122
|
-
widget = ClickAnimation(QPoint(x1, y1), life_ms)
|
123
|
-
|
124
|
-
# 用 QPropertyAnimation 平滑移动窗口
|
125
46
|
anim = QPropertyAnimation(widget, b"pos")
|
126
47
|
anim.setDuration(duration_ms)
|
127
|
-
|
128
|
-
anim.
|
129
|
-
anim.
|
130
|
-
anim.setEasingCurve(QEasingCurve.OutQuad) # 可自行更换缓动曲线
|
48
|
+
anim.setStartValue(widget.pos())
|
49
|
+
anim.setEndValue(QPoint(x2 - widget.width()//2, y2 - widget.height()//2))
|
50
|
+
anim.setEasingCurve(QEasingCurve.OutQuad)
|
131
51
|
anim.start()
|
132
52
|
|
133
|
-
|
134
|
-
|
135
|
-
anim.finished.connect(loop.quit)
|
136
|
-
QTimer.singleShot(life_ms, loop.quit) # 双保险
|
137
|
-
loop.exec()
|
53
|
+
QTimer.singleShot(total + 200, app.quit)
|
54
|
+
app.exec()
|
138
55
|
|
139
|
-
|
140
|
-
|
56
|
+
# ------------------------------- public API (non‑blocking) ---------------------------
|
57
|
+
def show_click(x: int, y: int, duration_ms: int = 800, existing_ms: int = 800):
|
58
|
+
if not CLICK_GIF.exists():
|
59
|
+
raise FileNotFoundError(f"GIF not found at {CLICK_GIF}")
|
60
|
+
mp.get_context("spawn").Process(
|
61
|
+
target=_worker_click,
|
62
|
+
args=(x, y, duration_ms, existing_ms),
|
63
|
+
daemon=False # keep running even if parent exits
|
64
|
+
).start()
|
65
|
+
|
66
|
+
def show_move_to(x1: int, y1: int, x2: int, y2: int,
|
67
|
+
duration_ms: int = 1000, existing_ms: int = 800):
|
68
|
+
if not CLICK_GIF.exists():
|
69
|
+
raise FileNotFoundError(f"GIF not found at {CLICK_GIF}")
|
70
|
+
mp.get_context("spawn").Process(
|
71
|
+
target=_worker_move,
|
72
|
+
args=(x1, y1, x2, y2, duration_ms, existing_ms),
|
73
|
+
daemon=False
|
74
|
+
).start()
|
141
75
|
|
142
76
|
|
143
|
-
# ---------- 命令行测试 ----------
|
144
|
-
if __name__ == "__main__":
|
145
|
-
# 测试点击
|
146
|
-
x, y = 500, 500
|
147
|
-
print(f"Testing click at ({x}, {y})")
|
148
|
-
show_click(x, y)
|
149
77
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
show_move_to(x1, y1, x2, y2, duration_ms=2000)
|
78
|
+
if __name__ == "__main__":
|
79
|
+
# from click_anim_async import show_click
|
80
|
+
show_click(500, 500)
|
81
|
+
show_move_to(300, 300, 600, 600)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""
|
2
|
+
Test script to verify cursor animation is working
|
3
|
+
"""
|
4
|
+
import asyncio
|
5
|
+
import sys
|
6
|
+
import time
|
7
|
+
from pathlib import Path
|
8
|
+
from computer_use_ootb_internal.computer_use_demo.tools.computer import ComputerTool
|
9
|
+
|
10
|
+
async def test_animations():
|
11
|
+
|
12
|
+
# Initialize the computer tool
|
13
|
+
computer = ComputerTool()
|
14
|
+
|
15
|
+
# Test mouse move animation
|
16
|
+
print("Testing mouse move animation...")
|
17
|
+
await computer(action="mouse_move_windll", coordinate=(500, 500))
|
18
|
+
print("Waiting 2 seconds...")
|
19
|
+
await asyncio.sleep(2)
|
20
|
+
|
21
|
+
# Test click animation
|
22
|
+
print("Testing click animation...")
|
23
|
+
await computer(action="left_click_windll", coordinate=(700, 300))
|
24
|
+
print("Waiting 2 seconds...")
|
25
|
+
await asyncio.sleep(2)
|
26
|
+
|
27
|
+
# Test another move
|
28
|
+
print("Testing move and click sequence...")
|
29
|
+
await computer(action="mouse_move_windll", coordinate=(300, 300))
|
30
|
+
await asyncio.sleep(1)
|
31
|
+
await computer(action="left_click_windll", coordinate=(300, 300))
|
32
|
+
|
33
|
+
# Wait for animations to complete
|
34
|
+
print("Waiting for animations to complete...")
|
35
|
+
await asyncio.sleep(3)
|
36
|
+
|
37
|
+
print("Test completed")
|
38
|
+
|
39
|
+
if __name__ == "__main__":
|
40
|
+
asyncio.run(test_animations())
|
@@ -2,18 +2,12 @@ import ast
|
|
2
2
|
import json
|
3
3
|
import asyncio
|
4
4
|
from typing import Any, Dict, cast, List, Union
|
5
|
-
from collections.abc import Callable
|
6
5
|
import uuid
|
7
6
|
from anthropic.types.beta import (
|
8
|
-
BetaContentBlock,
|
9
|
-
BetaContentBlockParam,
|
10
7
|
BetaImageBlockParam,
|
11
|
-
BetaMessage,
|
12
|
-
BetaMessageParam,
|
13
8
|
BetaTextBlockParam,
|
14
9
|
BetaToolResultBlockParam,
|
15
10
|
)
|
16
|
-
from anthropic.types import TextBlock
|
17
11
|
from anthropic.types.beta import BetaMessage, BetaTextBlock, BetaToolUseBlock
|
18
12
|
from computer_use_ootb_internal.computer_use_demo.tools import BashTool, ComputerTool, EditTool, ToolCollection, ToolResult
|
19
13
|
|
@@ -221,9 +215,13 @@ class TeachmodeExecutor:
|
|
221
215
|
|
222
216
|
def _add_starrail_alt_actions(self, action_list: list[Dict[str, Any]]) -> list[Dict[str, Any]]:
|
223
217
|
parsed_action_list = []
|
224
|
-
|
225
|
-
|
226
|
-
|
218
|
+
for action in action_list:
|
219
|
+
if action["action"] in ["left_click", "mouse_move"]:
|
220
|
+
parsed_action_list.append({"action": "key_down", "text": "alt", "coordinate": None})
|
221
|
+
parsed_action_list.append(action)
|
222
|
+
parsed_action_list.append({"action": "key_up", "text": "alt", "coordinate": None})
|
223
|
+
else:
|
224
|
+
parsed_action_list.append(action)
|
227
225
|
return parsed_action_list
|
228
226
|
|
229
227
|
def _reformat_starrail_browser_actions(self, action_list: list[Dict[str, Any]]) -> list[Dict[str, Any]]:
|
@@ -242,9 +240,15 @@ class TeachmodeExecutor:
|
|
242
240
|
|
243
241
|
def _add_starrail_browser_alt_actions(self, action_list: list[Dict[str, Any]]) -> list[Dict[str, Any]]:
|
244
242
|
parsed_action_list = []
|
245
|
-
|
246
|
-
|
247
|
-
|
243
|
+
|
244
|
+
for action in action_list:
|
245
|
+
if action["action"] in ["left_click", "mouse_move", "left_click_windll", "mouse_move_windll"]:
|
246
|
+
parsed_action_list.append({"action": "key_down_windll", "text": "alt", "coordinate": None})
|
247
|
+
parsed_action_list.append(action)
|
248
|
+
parsed_action_list.append({"action": "key_up_windll", "text": "alt", "coordinate": None})
|
249
|
+
else:
|
250
|
+
parsed_action_list.append(action)
|
251
|
+
|
248
252
|
return parsed_action_list
|
249
253
|
|
250
254
|
|
@@ -296,23 +296,35 @@ class ComputerTool(BaseAnthropicTool):
|
|
296
296
|
raise ToolError(f"coordinate is not accepted for {action}")
|
297
297
|
|
298
298
|
if action == "left_click":
|
299
|
+
x, y = pyautogui.position()
|
300
|
+
show_click(x, y)
|
299
301
|
pyautogui.click()
|
300
302
|
elif action == "right_click":
|
303
|
+
x, y = pyautogui.position()
|
304
|
+
show_click(x, y)
|
301
305
|
pyautogui.rightClick()
|
302
306
|
elif action == "middle_click":
|
307
|
+
x, y = pyautogui.position()
|
308
|
+
show_click(x, y)
|
303
309
|
pyautogui.middleClick()
|
304
310
|
elif action == "double_click":
|
311
|
+
x, y = pyautogui.position()
|
312
|
+
show_click(x, y)
|
305
313
|
pyautogui.doubleClick()
|
306
314
|
elif action == "left_press":
|
315
|
+
x, y = pyautogui.position()
|
316
|
+
show_click(x, y)
|
307
317
|
pyautogui.mouseDown()
|
308
318
|
time.sleep(1)
|
309
319
|
pyautogui.mouseUp()
|
310
320
|
elif action == "scroll_down":
|
311
321
|
pyautogui.scroll(-200) # Adjust scroll amount as needed
|
312
322
|
return ToolResult(output="Scrolled down")
|
323
|
+
|
313
324
|
elif action == "scroll_up":
|
314
325
|
pyautogui.scroll(200) # Adjust scroll amount as needed
|
315
326
|
return ToolResult(output="Scrolled up")
|
327
|
+
|
316
328
|
elif action == "wait":
|
317
329
|
time.sleep(30)
|
318
330
|
return ToolResult(output="Waited")
|
@@ -355,14 +367,13 @@ class ComputerTool(BaseAnthropicTool):
|
|
355
367
|
if action == "left_click_windll":
|
356
368
|
if coordinate is None:
|
357
369
|
x, y = pyautogui.position()
|
358
|
-
# x, y = self.scale_coordinates(ScalingSource.COMPUTER, x, y)
|
359
370
|
show_click(x, y)
|
360
371
|
self.marbot_auto_gui.click()
|
361
372
|
else:
|
362
373
|
x = coordinate[0]+self.offset_x
|
363
374
|
y = coordinate[1]+self.offset_y
|
364
|
-
self.marbot_auto_gui.click(x=x, y=y)
|
365
375
|
show_click(x, y)
|
376
|
+
self.marbot_auto_gui.click(x=x, y=y)
|
366
377
|
elif action == "mouse_move_windll":
|
367
378
|
if coordinate is None:
|
368
379
|
raise ToolError(f"coordinate is required for {action}")
|
@@ -371,8 +382,8 @@ class ComputerTool(BaseAnthropicTool):
|
|
371
382
|
x1 = coordinate[0]+self.offset_x
|
372
383
|
y1 = coordinate[1]+self.offset_y
|
373
384
|
|
385
|
+
show_move_to(x0, y0, x1, y1, duration_ms=1000)
|
374
386
|
self.marbot_auto_gui.moveTo(x=x1, y=y1)
|
375
|
-
show_move_to(x0, y0, x1, y1, duration_ms=2000)
|
376
387
|
|
377
388
|
# elif action == "right_click_windll":
|
378
389
|
# self.marbot_auto_gui.rightClick(x=coordinate[0], y=coordinate[1])
|
@@ -41,7 +41,7 @@ def simple_teachmode_sampling_loop(
|
|
41
41
|
if "star_rail" in user_id or "star_rail" in user_id:
|
42
42
|
full_screen_game_mode = 1
|
43
43
|
|
44
|
-
if "star_rail_dev" in trace_id or "star_rail_dev" in user_id:
|
44
|
+
if "star_rail_dev" in trace_id or "star_rail_dev" in user_id or "hero_case" in user_id:
|
45
45
|
full_screen_game_mode = 2
|
46
46
|
|
47
47
|
print(f"Full Screen Game Mode: {full_screen_game_mode}")
|
@@ -102,7 +102,7 @@ def simple_teachmode_sampling_loop(
|
|
102
102
|
|
103
103
|
try:
|
104
104
|
step_plan = infer_server_response["generated_plan"]
|
105
|
-
|
105
|
+
step_info = infer_server_response["generated_plan"]["step_info"]
|
106
106
|
step_action = infer_server_response["generated_action"]["content"]
|
107
107
|
step_traj_idx = infer_server_response["current_traj_step"]
|
108
108
|
|
@@ -115,7 +115,8 @@ def simple_teachmode_sampling_loop(
|
|
115
115
|
break
|
116
116
|
|
117
117
|
# action_history.append(f"Executing Step: {step_count} - Trajectory Step: {step_traj_idx} - Plan: {step_plan} - Action: {step_action};\n")
|
118
|
-
|
118
|
+
|
119
|
+
action_history.append(f"Executing guidance trajectory step [{step_traj_idx}], Plan: {step_info}, Action: {step_action};\n")
|
119
120
|
|
120
121
|
for message in executor({"role": "assistant", "content": step_action}):
|
121
122
|
yield message
|
@@ -124,8 +125,6 @@ def simple_teachmode_sampling_loop(
|
|
124
125
|
|
125
126
|
|
126
127
|
|
127
|
-
|
128
|
-
|
129
128
|
if __name__ == "__main__":
|
130
129
|
parser = argparse.ArgumentParser(
|
131
130
|
description="Run a synchronous sampling loop for assistant/tool interactions in teach-mode."
|
@@ -137,8 +136,6 @@ if __name__ == "__main__":
|
|
137
136
|
)
|
138
137
|
parser.add_argument(
|
139
138
|
"--task",
|
140
|
-
# default="Help me to complete the extraction of the viewer data of Downald Trump's first video on youtube,\
|
141
|
-
# fill in the excel sheet.",
|
142
139
|
default="Click on the Google Chorme icon",
|
143
140
|
help="The task to be completed by the assistant (e.g., 'Complete some data extraction.').",
|
144
141
|
)
|
@@ -1,15 +1,13 @@
|
|
1
1
|
computer_use_ootb_internal/README.md,sha256=FxpW95lyub2iX73ZDfK6ML7SdEKg060H5I6Grub7li4,31
|
2
|
-
computer_use_ootb_internal/app_teachmode.py,sha256=
|
2
|
+
computer_use_ootb_internal/app_teachmode.py,sha256=zmUPvFjqdhysnN1bD2QQhaAKONnAtMFd03Rb9gF1l6c,15646
|
3
3
|
computer_use_ootb_internal/app_teachmode_gradio.py,sha256=zAw-n3s20j1Jr0S4TzXHwllKV6APJ8HEHB1KqBuzriY,7907
|
4
4
|
computer_use_ootb_internal/dependency_check.py,sha256=y8RMEP6RXQzTgU1MS_1piBLtz4J-Hfn9RjUZg59dyvo,1333
|
5
|
-
computer_use_ootb_internal/example_websocket_js.html,sha256=BLYwDExVlgiAX4vXVXW3RuP5KD8FXE4EFXIl54bwF7w,1322
|
6
5
|
computer_use_ootb_internal/requirements-lite.txt,sha256=5DAHomz4A_P2BmTIXNkNqkHbnIF0AyZ4_1XAlb1LaYs,290
|
7
|
-
computer_use_ootb_internal/run_teachmode_ootb_args.py,sha256=
|
8
|
-
computer_use_ootb_internal/
|
9
|
-
computer_use_ootb_internal/service_teachmode_test.py,sha256=zpfBFFKD9WGLX4m77ajOBfmczpYsCa3_qTBweeSNRV8,1112
|
10
|
-
computer_use_ootb_internal/computer_use_demo/animation/click_animation.py,sha256=ZKmlZnMVW4zdxkUVWCTQF2rqq2h70qq5bAuLYVa4qxc,5312
|
6
|
+
computer_use_ootb_internal/run_teachmode_ootb_args.py,sha256=UEiwLSdERzBqbCH2Em20b4UNzAb__S8cJSnkceVcBsY,6686
|
7
|
+
computer_use_ootb_internal/computer_use_demo/animation/click_animation.py,sha256=QR_DEDk7bVON5EQ_xsJGrxNa3NoxqubYyXPFRB12pmQ,3183
|
11
8
|
computer_use_ootb_internal/computer_use_demo/animation/icons8-select-cursor-transparent-96.gif,sha256=4LfwsfFQnREXrNRs32aJU2jO65JXianJoL_8q7-8elg,30966
|
12
|
-
computer_use_ootb_internal/computer_use_demo/
|
9
|
+
computer_use_ootb_internal/computer_use_demo/animation/test_animation.py,sha256=SOJz2yffXTkjuAHqk0IZLcMriR0KQYTo7W1b8wGyRGY,1222
|
10
|
+
computer_use_ootb_internal/computer_use_demo/executor/teachmode_executor.py,sha256=Rr_I0Uk8hsqTmq3Fr3KOtjkXTqSb4pkNxmutENbv8dc,16368
|
13
11
|
computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/__init__.py,sha256=h2CNeuACklxVpJC65QR8_6AvSybEZLmeO45hY_-lLBs,61
|
14
12
|
computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/gui_capture.py,sha256=CxFJbsSb68ERKH7-C4RaaZy7FIhhzrzGx5qQJ4C37cA,13907
|
15
13
|
computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/gui_parser.py,sha256=KSTJ0cMwh3ahUMzHRaDgA2sVNUL4MNlF7qEBGN3G0SI,28993
|
@@ -33,12 +31,12 @@ computer_use_ootb_internal/computer_use_demo/tools/base.py,sha256=QDqpuuKlhUKJT2
|
|
33
31
|
computer_use_ootb_internal/computer_use_demo/tools/bash.py,sha256=rHetQ80_v-TTi-1oxIA7ncFEwJxFTh8FJCErIoZbGeY,4236
|
34
32
|
computer_use_ootb_internal/computer_use_demo/tools/collection.py,sha256=8RzHLobL44_Jjt8ltXS6I8XJlEAQOfc75dmnDUaHE-8,922
|
35
33
|
computer_use_ootb_internal/computer_use_demo/tools/colorful_text.py,sha256=cvlmnhAImDTwoRRwhT5au7mNFhfAD7ZfeoDEVdVzDKw,892
|
36
|
-
computer_use_ootb_internal/computer_use_demo/tools/computer.py,sha256=
|
34
|
+
computer_use_ootb_internal/computer_use_demo/tools/computer.py,sha256=kpcp3orAdSwzBJMvL8zt_OgF6kT9UdZaJuqkjja7bic,25493
|
37
35
|
computer_use_ootb_internal/computer_use_demo/tools/computer_marbot.py,sha256=zZuWz9ArfP3Zss-afnscrPkgCtB5UWbCy7HwAOvO2bo,5970
|
38
36
|
computer_use_ootb_internal/computer_use_demo/tools/edit.py,sha256=b0PwUitxckHCQqFP3ZwlthWdqNkn7WETeTHeB6-o98c,11486
|
39
37
|
computer_use_ootb_internal/computer_use_demo/tools/run.py,sha256=xhXdnBK1di9muaO44CEirL9hpGy3NmKbjfMpyeVmn8Y,1595
|
40
38
|
computer_use_ootb_internal/computer_use_demo/tools/screen_capture.py,sha256=L8qfvtUkPPQGt92N-2Zfw5ZTDBzLsDps39uMnX3_uSA,6857
|
41
|
-
computer_use_ootb_internal-0.0.96.dist-info/METADATA,sha256=
|
42
|
-
computer_use_ootb_internal-0.0.96.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
43
|
-
computer_use_ootb_internal-0.0.96.dist-info/entry_points.txt,sha256=-AbmawU7IRQuDZihgVMVDrFoY4E6rnXYOUB-5vSeBKs,93
|
44
|
-
computer_use_ootb_internal-0.0.96.dist-info/RECORD,,
|
39
|
+
computer_use_ootb_internal-0.0.96.post2.dist-info/METADATA,sha256=Q_cRfIeXWR-MO_d4c-GLxnC2wQZw68BPsvgrBYcaX7c,915
|
40
|
+
computer_use_ootb_internal-0.0.96.post2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
41
|
+
computer_use_ootb_internal-0.0.96.post2.dist-info/entry_points.txt,sha256=-AbmawU7IRQuDZihgVMVDrFoY4E6rnXYOUB-5vSeBKs,93
|
42
|
+
computer_use_ootb_internal-0.0.96.post2.dist-info/RECORD,,
|
@@ -1,41 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Teachmode Client</title>
|
5
|
-
<!-- include socket.io client -->
|
6
|
-
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
|
7
|
-
</head>
|
8
|
-
<body>
|
9
|
-
<h1>Teachmode WebSocket Test</h1>
|
10
|
-
<div id="messages"></div>
|
11
|
-
|
12
|
-
<script>
|
13
|
-
const socket = io("http://localhost:5000"); // Your server’s URL/port
|
14
|
-
|
15
|
-
// Listen for partial responses
|
16
|
-
socket.on("partial_response", (data) => {
|
17
|
-
console.log("Got partial:", data);
|
18
|
-
const div = document.createElement("div");
|
19
|
-
div.innerText = "Assistant: " + data.content;
|
20
|
-
document.getElementById("messages").appendChild(div);
|
21
|
-
});
|
22
|
-
|
23
|
-
// Listen for done event
|
24
|
-
socket.on("done", (data) => {
|
25
|
-
console.log("Task completed:", data);
|
26
|
-
const div = document.createElement("div");
|
27
|
-
div.innerText = "TASK DONE!";
|
28
|
-
document.getElementById("messages").appendChild(div);
|
29
|
-
});
|
30
|
-
|
31
|
-
// When connected, emit run_teachmode
|
32
|
-
socket.on("connect", () => {
|
33
|
-
console.log("Connected to server. Emitting run_teachmode event...");
|
34
|
-
socket.emit("run_teachmode", {
|
35
|
-
user_input: "Hello, I'd like to do X, Y, Z."
|
36
|
-
// you can include model, task, user_id, trace_id, etc. if desired
|
37
|
-
});
|
38
|
-
});
|
39
|
-
</script>
|
40
|
-
</body>
|
41
|
-
</html>
|
@@ -1,194 +0,0 @@
|
|
1
|
-
import argparse
|
2
|
-
import time
|
3
|
-
import json
|
4
|
-
import threading
|
5
|
-
|
6
|
-
from flask import Flask, request, jsonify
|
7
|
-
from flask_socketio import SocketIO, emit
|
8
|
-
|
9
|
-
from screeninfo import get_monitors
|
10
|
-
from computer_use_ootb_internal.computer_use_demo.tools.computer import get_screen_details
|
11
|
-
from computer_use_ootb_internal.run_teachmode_ootb_args import simple_teachmode_sampling_loop
|
12
|
-
|
13
|
-
###############################################################################
|
14
|
-
# Shared State
|
15
|
-
###############################################################################
|
16
|
-
class SharedState:
|
17
|
-
def __init__(self):
|
18
|
-
self.args = None # Will hold argparse-like namespace
|
19
|
-
self.messages = [] # If you want to store a global chat or last session
|
20
|
-
|
21
|
-
shared_state = SharedState()
|
22
|
-
|
23
|
-
###############################################################################
|
24
|
-
# Flask + SocketIO Application Setup
|
25
|
-
###############################################################################
|
26
|
-
app = Flask(__name__)
|
27
|
-
app.config["SECRET_KEY"] = "some-secret-key" # In production, change this
|
28
|
-
socketio = SocketIO(app, cors_allowed_origins="*")
|
29
|
-
|
30
|
-
###############################################################################
|
31
|
-
# Utility Functions
|
32
|
-
###############################################################################
|
33
|
-
def setup_default_args():
|
34
|
-
"""
|
35
|
-
Creates argparse-like defaults.
|
36
|
-
You can also parse real CLI args if you wish.
|
37
|
-
"""
|
38
|
-
parser = argparse.ArgumentParser(description="Teachmode SocketIO Server.")
|
39
|
-
parser.add_argument("--model", default="teach-mode-gpt-4o")
|
40
|
-
parser.add_argument("--task", default="Help me complete data extraction on YouTube video.")
|
41
|
-
parser.add_argument("--selected_screen", type=int, default=0)
|
42
|
-
parser.add_argument("--user_id", default="liziqi")
|
43
|
-
parser.add_argument("--trace_id", default="default_trace")
|
44
|
-
parser.add_argument("--api_key_file", default="api_key.json")
|
45
|
-
parser.add_argument("--api_keys", default="")
|
46
|
-
parser.add_argument(
|
47
|
-
"--server_url",
|
48
|
-
default="http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com/generate_action",
|
49
|
-
help="Server URL for the session (local='http://localhost:5000/generate_action', \
|
50
|
-
aws='http://ec2-44-234-43-86.us-west-2.compute.amazonaws.com/generate_action').",
|
51
|
-
)
|
52
|
-
|
53
|
-
# If you really want to parse sys.argv, do parser.parse_args().
|
54
|
-
# But you can also return the defaults for this example:
|
55
|
-
return parser.parse_args([])
|
56
|
-
|
57
|
-
def apply_args_to_state(args):
|
58
|
-
"""
|
59
|
-
Helper that prints or logs relevant arguments and stores them in shared_state.
|
60
|
-
"""
|
61
|
-
print("[apply_args_to_state] Applying arguments:", args)
|
62
|
-
shared_state.args = args
|
63
|
-
|
64
|
-
def run_teachmode_task(user_input):
|
65
|
-
"""
|
66
|
-
Calls simple_teachmode_sampling_loop and emits partial responses over SocketIO.
|
67
|
-
"""
|
68
|
-
# 1) Log or store user input
|
69
|
-
print(f"[run_teachmode_task] Received user_input: {user_input}")
|
70
|
-
# Optionally store or reset message history for this session
|
71
|
-
shared_state.messages = [{"role": "user", "content": user_input}]
|
72
|
-
|
73
|
-
# 2) Grab arguments from shared_state
|
74
|
-
args = shared_state.args
|
75
|
-
if not args:
|
76
|
-
print("[run_teachmode_task] No arguments in shared_state, applying defaults.")
|
77
|
-
args = setup_default_args()
|
78
|
-
apply_args_to_state(args)
|
79
|
-
|
80
|
-
# 3) Run the sampling loop
|
81
|
-
print(f"[run_teachmode_task] Starting the sampling loop with task: {args.task}")
|
82
|
-
sampling_loop = simple_teachmode_sampling_loop(
|
83
|
-
model=args.model,
|
84
|
-
task=args.task,
|
85
|
-
selected_screen=args.selected_screen,
|
86
|
-
user_id=args.user_id,
|
87
|
-
trace_id=args.trace_id,
|
88
|
-
api_keys=args.api_keys,
|
89
|
-
server_url=args.server_url
|
90
|
-
)
|
91
|
-
|
92
|
-
# 4) Send partial responses
|
93
|
-
for loop_msg in sampling_loop:
|
94
|
-
print(f"[run_teachmode_task] Emitting partial response: {loop_msg}")
|
95
|
-
# You can store it in shared_state messages
|
96
|
-
shared_state.messages.append({"role": "assistant", "content": loop_msg})
|
97
|
-
# Emit immediately so the client sees partial responses
|
98
|
-
emit("partial_response", {"role": "assistant", "content": loop_msg})
|
99
|
-
time.sleep(1) # Optional delay to simulate real-time streaming
|
100
|
-
|
101
|
-
# 5) Done event
|
102
|
-
print("[run_teachmode_task] Completed all messages.")
|
103
|
-
emit("done", {"messages": shared_state.messages, "status": "completed"})
|
104
|
-
|
105
|
-
###############################################################################
|
106
|
-
# HTTP Endpoint: update_params
|
107
|
-
###############################################################################
|
108
|
-
@app.route("/update_params", methods=["POST"])
|
109
|
-
def update_parameters():
|
110
|
-
"""
|
111
|
-
HTTP endpoint that allows updating the parameters (like Gradio's /update_params).
|
112
|
-
Expects JSON body with fields matching the argparse Namespace (model, task, etc.)
|
113
|
-
"""
|
114
|
-
data = request.json
|
115
|
-
if not data:
|
116
|
-
return jsonify({"status": "error", "message": "No JSON provided."}), 400
|
117
|
-
|
118
|
-
# Build an argparse.Namespace from the JSON keys
|
119
|
-
# Fallback to the existing arguments if some keys are missing
|
120
|
-
old_args = shared_state.args or setup_default_args()
|
121
|
-
new_args_dict = {**vars(old_args), **data} # Merge old with new
|
122
|
-
new_args = argparse.Namespace(**new_args_dict)
|
123
|
-
apply_args_to_state(new_args)
|
124
|
-
|
125
|
-
return jsonify({
|
126
|
-
"status": "success",
|
127
|
-
"message": "Parameters updated",
|
128
|
-
"new_args": vars(new_args)
|
129
|
-
})
|
130
|
-
|
131
|
-
###############################################################################
|
132
|
-
# HTTP Endpoint: get_messages
|
133
|
-
###############################################################################
|
134
|
-
@app.route("/get_messages", methods=["GET"])
|
135
|
-
def get_messages():
|
136
|
-
"""
|
137
|
-
Example new function: returns the current chat messages in shared_state.
|
138
|
-
"""
|
139
|
-
return jsonify(shared_state.messages)
|
140
|
-
|
141
|
-
###############################################################################
|
142
|
-
# HTTP Endpoint: clear_messages
|
143
|
-
###############################################################################
|
144
|
-
@app.route("/clear_messages", methods=["POST"])
|
145
|
-
def clear_messages():
|
146
|
-
"""
|
147
|
-
Example new function: clears the stored chat messages in shared_state.
|
148
|
-
"""
|
149
|
-
shared_state.messages = []
|
150
|
-
return jsonify({"status": "success", "message": "Chat history cleared."})
|
151
|
-
|
152
|
-
###############################################################################
|
153
|
-
# SocketIO Event: run_teachmode
|
154
|
-
###############################################################################
|
155
|
-
@socketio.on("run_teachmode")
|
156
|
-
def handle_run_teachmode(data):
|
157
|
-
"""
|
158
|
-
Websocket event that starts the teachmode sampling loop.
|
159
|
-
`data` can include e.g. {"user_input": "..."}.
|
160
|
-
"""
|
161
|
-
user_input = data.get("user_input", "Hello, let's start!")
|
162
|
-
run_teachmode_task(user_input)
|
163
|
-
|
164
|
-
###############################################################################
|
165
|
-
# SocketIO Event: connect
|
166
|
-
###############################################################################
|
167
|
-
@socketio.on("connect")
|
168
|
-
def on_connect():
|
169
|
-
print("[SocketIO] Client connected.")
|
170
|
-
|
171
|
-
@socketio.on("disconnect")
|
172
|
-
def on_disconnect():
|
173
|
-
print("[SocketIO] Client disconnected.")
|
174
|
-
|
175
|
-
###############################################################################
|
176
|
-
# Main
|
177
|
-
###############################################################################
|
178
|
-
def main():
|
179
|
-
# Pre-populate shared_state with default arguments
|
180
|
-
args = setup_default_args()
|
181
|
-
apply_args_to_state(args)
|
182
|
-
|
183
|
-
# Optional: Preload screen info if needed
|
184
|
-
screens = get_monitors()
|
185
|
-
print("Detected screens:", screens)
|
186
|
-
screen_names, primary_index = get_screen_details()
|
187
|
-
print("Screen names:", screen_names, "Default selected index:", primary_index)
|
188
|
-
|
189
|
-
# Run the Flask-SocketIO app
|
190
|
-
# eventlet is the default async_mode if installed, but we specify it explicitly.
|
191
|
-
socketio.run(app, host="0.0.0.0", port=5001, debug=True)
|
192
|
-
|
193
|
-
if __name__ == "__main__":
|
194
|
-
main()
|
@@ -1,41 +0,0 @@
|
|
1
|
-
import socketio
|
2
|
-
|
3
|
-
# Create a Socket.IO client instance
|
4
|
-
sio = socketio.Client()
|
5
|
-
|
6
|
-
@sio.on('connect')
|
7
|
-
def on_connect():
|
8
|
-
print("Connected to the server.")
|
9
|
-
# Once connected, send the event to start the teachmode process:
|
10
|
-
data = {
|
11
|
-
"user_input": "Hello, I'd like to open the Chrome browser."
|
12
|
-
# You can add more parameters here if needed, e.g.:
|
13
|
-
# "model": "teach-mode-gpt-4o",
|
14
|
-
# "task": "Some task",
|
15
|
-
# "user_id": "my_user",
|
16
|
-
# etc.
|
17
|
-
}
|
18
|
-
print("Emitting 'run_teachmode' event with data:", data)
|
19
|
-
sio.emit("run_teachmode", data)
|
20
|
-
|
21
|
-
@sio.on('partial_response')
|
22
|
-
def on_partial_response(data):
|
23
|
-
print("[partial_response] =>", data)
|
24
|
-
|
25
|
-
@sio.on('done')
|
26
|
-
def on_done(data):
|
27
|
-
print("[done] =>", data)
|
28
|
-
# Since the process is completed, you can disconnect:
|
29
|
-
sio.disconnect()
|
30
|
-
|
31
|
-
@sio.on('disconnect')
|
32
|
-
def on_disconnect():
|
33
|
-
print("Disconnected from server.")
|
34
|
-
|
35
|
-
|
36
|
-
if __name__ == "__main__":
|
37
|
-
# Connect to the Socket.IO server (adapt host/port as needed):
|
38
|
-
sio.connect("http://localhost:5001")
|
39
|
-
|
40
|
-
# Keep the client alive to receive events
|
41
|
-
sio.wait()
|
File without changes
|
File without changes
|