cua-computer 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {cua_computer-0.2.1 → cua_computer-0.2.2}/PKG-INFO +2 -2
- {cua_computer-0.2.1 → cua_computer-0.2.2}/README.md +1 -1
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/computer.py +67 -58
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/ui/gradio/app.py +43 -13
- {cua_computer-0.2.1 → cua_computer-0.2.2}/pyproject.toml +3 -3
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/interface/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/interface/base.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/interface/factory.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/interface/linux.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/interface/macos.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/interface/models.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/logger.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/models.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/base.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/cloud/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/cloud/provider.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/factory.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/lume/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/lume/provider.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/lume_api.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/lumier/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/providers/lumier/provider.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/telemetry.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/ui/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/ui/gradio/__init__.py +0 -0
- {cua_computer-0.2.1 → cua_computer-0.2.2}/computer/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: cua-computer
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
Summary: Computer-Use Interface (CUI) framework powering Cua
|
5
5
|
Author-Email: TryCua <gh@trycua.com>
|
6
6
|
Requires-Python: >=3.10
|
@@ -78,7 +78,7 @@ finally:
|
|
78
78
|
To install the Computer-Use Interface (CUI):
|
79
79
|
|
80
80
|
```bash
|
81
|
-
pip install cua-computer
|
81
|
+
pip install "cua-computer[all]"
|
82
82
|
```
|
83
83
|
|
84
84
|
The `cua-computer` PyPi package pulls automatically the latest executable version of Lume through [pylume](https://github.com/trycua/pylume).
|
@@ -54,7 +54,7 @@ finally:
|
|
54
54
|
To install the Computer-Use Interface (CUI):
|
55
55
|
|
56
56
|
```bash
|
57
|
-
pip install cua-computer
|
57
|
+
pip install "cua-computer[all]"
|
58
58
|
```
|
59
59
|
|
60
60
|
The `cua-computer` PyPi package pulls automatically the latest executable version of Lume through [pylume](https://github.com/trycua/pylume).
|
@@ -78,11 +78,11 @@ class Computer:
|
|
78
78
|
self.provider_type = provider_type
|
79
79
|
self.ephemeral = ephemeral
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
81
|
+
# The default is currently to use non-ephemeral storage
|
82
|
+
if storage and ephemeral and storage != "ephemeral":
|
83
|
+
raise ValueError("Storage path and ephemeral flag cannot be used together")
|
84
|
+
self.storage = "ephemeral" if ephemeral else storage
|
85
|
+
|
86
86
|
# For Lumier provider, store the first shared directory path to use
|
87
87
|
# for VM file sharing
|
88
88
|
self.shared_path = None
|
@@ -279,12 +279,14 @@ class Computer:
|
|
279
279
|
raise RuntimeError(f"Failed to initialize VM provider: {e}")
|
280
280
|
|
281
281
|
# Check if VM exists or create it
|
282
|
+
is_running = False
|
282
283
|
try:
|
283
284
|
if self.config.vm_provider is None:
|
284
285
|
raise RuntimeError(f"VM provider not initialized for {self.config.name}")
|
285
286
|
|
286
287
|
vm = await self.config.vm_provider.get_vm(self.config.name)
|
287
288
|
self.logger.verbose(f"Found existing VM: {self.config.name}")
|
289
|
+
is_running = vm.get("status") == "running"
|
288
290
|
except Exception as e:
|
289
291
|
self.logger.error(f"VM not found: {self.config.name}")
|
290
292
|
self.logger.error(f"Error: {e}")
|
@@ -292,63 +294,67 @@ class Computer:
|
|
292
294
|
f"VM {self.config.name} could not be found or created."
|
293
295
|
)
|
294
296
|
|
295
|
-
#
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
297
|
+
# Start the VM if it's not running
|
298
|
+
if not is_running:
|
299
|
+
self.logger.info(f"VM {self.config.name} is not running, starting it...")
|
300
|
+
|
301
|
+
# Convert paths to dictionary format for shared directories
|
302
|
+
shared_dirs = []
|
303
|
+
for path in self.shared_directories:
|
304
|
+
self.logger.verbose(f"Adding shared directory: {path}")
|
305
|
+
path = os.path.abspath(os.path.expanduser(path))
|
306
|
+
if os.path.exists(path):
|
307
|
+
# Add path in format expected by Lume API
|
308
|
+
shared_dirs.append({
|
309
|
+
"hostPath": path,
|
310
|
+
"readOnly": False
|
311
|
+
})
|
312
|
+
else:
|
313
|
+
self.logger.warning(f"Shared directory does not exist: {path}")
|
314
|
+
|
315
|
+
# Prepare run options to pass to the provider
|
316
|
+
run_opts = {}
|
317
|
+
|
318
|
+
# Add display information if available
|
319
|
+
if self.config.display is not None:
|
320
|
+
display_info = {
|
321
|
+
"width": self.config.display.width,
|
322
|
+
"height": self.config.display.height,
|
323
|
+
}
|
308
324
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
display_info = {
|
315
|
-
"width": self.config.display.width,
|
316
|
-
"height": self.config.display.height,
|
317
|
-
}
|
318
|
-
|
319
|
-
# Check if scale_factor exists before adding it
|
320
|
-
if hasattr(self.config.display, "scale_factor"):
|
321
|
-
display_info["scale_factor"] = self.config.display.scale_factor
|
322
|
-
|
323
|
-
run_opts["display"] = display_info
|
325
|
+
# Check if scale_factor exists before adding it
|
326
|
+
if hasattr(self.config.display, "scale_factor"):
|
327
|
+
display_info["scale_factor"] = self.config.display.scale_factor
|
328
|
+
|
329
|
+
run_opts["display"] = display_info
|
324
330
|
|
325
|
-
|
326
|
-
|
327
|
-
|
331
|
+
# Add shared directories if available
|
332
|
+
if self.shared_directories:
|
333
|
+
run_opts["shared_directories"] = shared_dirs.copy()
|
328
334
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
335
|
+
# Run the VM with the provider
|
336
|
+
try:
|
337
|
+
if self.config.vm_provider is None:
|
338
|
+
raise RuntimeError(f"VM provider not initialized for {self.config.name}")
|
339
|
+
|
340
|
+
# Use the complete run_opts we prepared earlier
|
341
|
+
# Handle ephemeral storage for run_vm method too
|
342
|
+
storage_param = "ephemeral" if self.ephemeral else self.storage
|
333
343
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
self.logger.info(f"VM run response: {response if response else 'None'}")
|
349
|
-
except Exception as run_error:
|
350
|
-
self.logger.error(f"Failed to run VM: {run_error}")
|
351
|
-
raise RuntimeError(f"Failed to start VM: {run_error}")
|
344
|
+
# Log the image being used
|
345
|
+
self.logger.info(f"Running VM using image: {self.image}")
|
346
|
+
|
347
|
+
# Call provider.run_vm with explicit image parameter
|
348
|
+
response = await self.config.vm_provider.run_vm(
|
349
|
+
image=self.image,
|
350
|
+
name=self.config.name,
|
351
|
+
run_opts=run_opts,
|
352
|
+
storage=storage_param
|
353
|
+
)
|
354
|
+
self.logger.info(f"VM run response: {response if response else 'None'}")
|
355
|
+
except Exception as run_error:
|
356
|
+
self.logger.error(f"Failed to run VM: {run_error}")
|
357
|
+
raise RuntimeError(f"Failed to start VM: {run_error}")
|
352
358
|
|
353
359
|
# Wait for VM to be ready with a valid IP address
|
354
360
|
self.logger.info("Waiting for VM to be ready with a valid IP address...")
|
@@ -406,6 +412,9 @@ class Computer:
|
|
406
412
|
raise TimeoutError(
|
407
413
|
f"Could not connect to WebSocket interface at {ip_address}:8000/ws: {str(e)}"
|
408
414
|
)
|
415
|
+
# self.logger.warning(
|
416
|
+
# f"Could not connect to WebSocket interface at {ip_address}:8000/ws: {str(e)}, expect missing functionality"
|
417
|
+
# )
|
409
418
|
|
410
419
|
# Create an event to keep the VM running in background if needed
|
411
420
|
if not self.use_host_computer_server:
|
@@ -17,7 +17,7 @@ import base64
|
|
17
17
|
from datetime import datetime
|
18
18
|
from PIL import Image
|
19
19
|
from huggingface_hub import DatasetCard, DatasetCardData
|
20
|
-
from computer import Computer
|
20
|
+
from computer import Computer, VMProviderType
|
21
21
|
from gradio.components import ChatMessage
|
22
22
|
import pandas as pd
|
23
23
|
from datasets import Dataset, Features, Sequence, concatenate_datasets
|
@@ -528,21 +528,44 @@ async def execute(name, action, arguments):
|
|
528
528
|
|
529
529
|
return results
|
530
530
|
|
531
|
-
async def handle_init_computer():
|
532
|
-
"""Initialize the computer instance and tools"""
|
531
|
+
async def handle_init_computer(os_choice: str):
|
532
|
+
"""Initialize the computer instance and tools for macOS or Ubuntu"""
|
533
533
|
global computer, tool_call_logs, tools
|
534
|
-
|
535
|
-
|
534
|
+
|
535
|
+
if os_choice == "Ubuntu":
|
536
|
+
computer = Computer(
|
537
|
+
image="ubuntu-noble-vanilla:latest",
|
538
|
+
os_type="linux",
|
539
|
+
provider_type=VMProviderType.LUME,
|
540
|
+
display="1024x768",
|
541
|
+
memory="8GB",
|
542
|
+
cpu="4"
|
543
|
+
)
|
544
|
+
os_type_str = "linux"
|
545
|
+
image_str = "ubuntu-noble-vanilla:latest"
|
546
|
+
else:
|
547
|
+
computer = Computer(
|
548
|
+
image="macos-sequoia-cua:latest",
|
549
|
+
os_type="macos",
|
550
|
+
provider_type=VMProviderType.LUME,
|
551
|
+
display="1024x768",
|
552
|
+
memory="8GB",
|
553
|
+
cpu="4"
|
554
|
+
)
|
555
|
+
os_type_str = "macos"
|
556
|
+
image_str = "macos-sequoia-cua:latest"
|
557
|
+
|
536
558
|
await computer.run()
|
537
|
-
|
559
|
+
|
538
560
|
# Log computer initialization as a tool call
|
539
561
|
result = await execute("computer", "initialize", {
|
540
|
-
"os":
|
541
|
-
"
|
542
|
-
"
|
562
|
+
"os": os_type_str,
|
563
|
+
"image": image_str,
|
564
|
+
"display": "1024x768",
|
565
|
+
"memory": "8GB",
|
543
566
|
"cpu": "4"
|
544
567
|
})
|
545
|
-
|
568
|
+
|
546
569
|
return result["screenshot"], json.dumps(tool_call_logs, indent=2)
|
547
570
|
|
548
571
|
async def handle_screenshot():
|
@@ -1004,8 +1027,15 @@ def create_gradio_ui():
|
|
1004
1027
|
run_setup_btn = gr.Button("⚙️ Run Task Setup")
|
1005
1028
|
# Setup status textbox
|
1006
1029
|
setup_status = gr.Textbox(label="Setup Status", value="")
|
1007
|
-
|
1008
|
-
|
1030
|
+
|
1031
|
+
with gr.Group():
|
1032
|
+
os_choice = gr.Radio(
|
1033
|
+
label="OS",
|
1034
|
+
choices=["macOS", "Ubuntu"],
|
1035
|
+
value="macOS",
|
1036
|
+
interactive=False # disable until the ubuntu image is ready
|
1037
|
+
)
|
1038
|
+
start_btn = gr.Button("Initialize Computer")
|
1009
1039
|
|
1010
1040
|
with gr.Group():
|
1011
1041
|
input_text = gr.Textbox(label="Type Text")
|
@@ -1169,7 +1199,7 @@ def create_gradio_ui():
|
|
1169
1199
|
)
|
1170
1200
|
|
1171
1201
|
img.select(handle_click, inputs=[img, click_type], outputs=[img, action_log])
|
1172
|
-
start_btn.click(handle_init_computer, outputs=[img, action_log])
|
1202
|
+
start_btn.click(handle_init_computer, inputs=[os_choice], outputs=[img, action_log])
|
1173
1203
|
wait_btn.click(handle_wait, outputs=[img, action_log])
|
1174
1204
|
|
1175
1205
|
# DONE and FAIL buttons just do a placeholder action
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "cua-computer"
|
9
|
-
version = "0.2.
|
9
|
+
version = "0.2.2"
|
10
10
|
description = "Computer-Use Interface (CUI) framework powering Cua"
|
11
11
|
readme = "README.md"
|
12
12
|
authors = [
|
@@ -57,7 +57,7 @@ target-version = [
|
|
57
57
|
|
58
58
|
[tool.ruff]
|
59
59
|
line-length = 100
|
60
|
-
target-version = "0.2.
|
60
|
+
target-version = "0.2.2"
|
61
61
|
select = [
|
62
62
|
"E",
|
63
63
|
"F",
|
@@ -71,7 +71,7 @@ docstring-code-format = true
|
|
71
71
|
|
72
72
|
[tool.mypy]
|
73
73
|
strict = true
|
74
|
-
python_version = "0.2.
|
74
|
+
python_version = "0.2.2"
|
75
75
|
ignore_missing_imports = true
|
76
76
|
disallow_untyped_defs = true
|
77
77
|
check_untyped_defs = true
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|