orgo 0.0.34__tar.gz → 0.0.37__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.
- {orgo-0.0.34 → orgo-0.0.37}/PKG-INFO +5 -3
- {orgo-0.0.34 → orgo-0.0.37}/pyproject.toml +5 -3
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/computer.py +164 -122
- orgo-0.0.37/src/orgo/prompt.py +848 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo.egg-info/PKG-INFO +5 -3
- orgo-0.0.37/src/orgo.egg-info/requires.txt +4 -0
- orgo-0.0.34/src/orgo/prompt.py +0 -452
- orgo-0.0.34/src/orgo.egg-info/requires.txt +0 -2
- {orgo-0.0.34 → orgo-0.0.37}/README.md +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/setup.cfg +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/__init__.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/api/__init__.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/api/client.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/forge.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/project.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/utils/__init__.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo/utils/auth.py +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo.egg-info/SOURCES.txt +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo.egg-info/dependency_links.txt +0 -0
- {orgo-0.0.34 → orgo-0.0.37}/src/orgo.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: orgo
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.37
|
|
4
4
|
Summary: Computers for AI agents
|
|
5
5
|
Author: Orgo Team
|
|
6
6
|
License: MIT
|
|
@@ -8,8 +8,10 @@ Project-URL: Homepage, https://www.orgo.ai
|
|
|
8
8
|
Project-URL: Documentation, https://docs.orgo.ai
|
|
9
9
|
Requires-Python: >=3.7
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
|
-
Requires-Dist: requests>=2.
|
|
12
|
-
Requires-Dist: pillow>=
|
|
11
|
+
Requires-Dist: requests>=2.28.0
|
|
12
|
+
Requires-Dist: pillow>=9.0.0
|
|
13
|
+
Requires-Dist: anthropic>=0.50.0
|
|
14
|
+
Requires-Dist: websocket-client>=1.6.0
|
|
13
15
|
|
|
14
16
|
# Orgo SDK
|
|
15
17
|
|
|
@@ -4,15 +4,17 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "orgo"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.37"
|
|
8
8
|
description = "Computers for AI agents"
|
|
9
9
|
authors = [{name = "Orgo Team"}]
|
|
10
10
|
license = {text = "MIT"}
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.7"
|
|
13
13
|
dependencies = [
|
|
14
|
-
"requests>=2.
|
|
15
|
-
"pillow>=
|
|
14
|
+
"requests>=2.28.0",
|
|
15
|
+
"pillow>=9.0.0",
|
|
16
|
+
"anthropic>=0.50.0",
|
|
17
|
+
"websocket-client>=1.6.0",
|
|
16
18
|
]
|
|
17
19
|
|
|
18
20
|
[project.urls]
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Orgo Computer - Control virtual computers with AI.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from orgo import Computer
|
|
6
|
+
|
|
7
|
+
computer = Computer(computer_id="your-computer-id")
|
|
8
|
+
computer.prompt("Open Firefox and search for AI news")
|
|
9
|
+
"""
|
|
10
|
+
|
|
2
11
|
import os as operating_system
|
|
3
12
|
import base64
|
|
4
13
|
import logging
|
|
@@ -14,7 +23,25 @@ from .prompt import get_provider
|
|
|
14
23
|
|
|
15
24
|
logger = logging.getLogger(__name__)
|
|
16
25
|
|
|
26
|
+
|
|
17
27
|
class Computer:
|
|
28
|
+
"""
|
|
29
|
+
Control an Orgo virtual computer.
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
# Connect to existing computer
|
|
33
|
+
computer = Computer(computer_id="abc123")
|
|
34
|
+
|
|
35
|
+
# Create new computer in project
|
|
36
|
+
computer = Computer(project="my-project", ram=4, cpu=2)
|
|
37
|
+
|
|
38
|
+
# AI control (uses Orgo by default)
|
|
39
|
+
computer.prompt("Open Firefox")
|
|
40
|
+
|
|
41
|
+
# AI control with Anthropic directly
|
|
42
|
+
computer.prompt("Open Firefox", provider="anthropic")
|
|
43
|
+
"""
|
|
44
|
+
|
|
18
45
|
def __init__(self,
|
|
19
46
|
project: Optional[Union[str, 'Project']] = None,
|
|
20
47
|
name: Optional[str] = None,
|
|
@@ -31,187 +58,136 @@ class Computer:
|
|
|
31
58
|
Initialize an Orgo virtual computer.
|
|
32
59
|
|
|
33
60
|
Args:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
computer_id: Connect to existing computer by ID
|
|
62
|
+
project: Project name or Project instance
|
|
63
|
+
name: Computer name (auto-generated if not provided)
|
|
37
64
|
api_key: Orgo API key (defaults to ORGO_API_KEY env var)
|
|
38
|
-
base_api_url: Custom API URL
|
|
39
|
-
ram/memory: RAM in GB (1, 2, 4, 8, 16, 32,
|
|
40
|
-
cpu: CPU cores (1, 2, 4, 8,
|
|
41
|
-
os:
|
|
42
|
-
gpu:
|
|
43
|
-
image: Custom image reference
|
|
44
|
-
|
|
45
|
-
Examples:
|
|
46
|
-
# Create computer in new project
|
|
47
|
-
computer = Computer(ram=4, cpu=2)
|
|
48
|
-
|
|
49
|
-
# Create computer with custom image
|
|
50
|
-
forge = Forge(org_id="myorg", project_id="myproj").base("ubuntu").run("echo hello")
|
|
51
|
-
computer = Computer(image=forge)
|
|
52
|
-
|
|
53
|
-
# Create computer in existing project
|
|
54
|
-
computer = Computer(project="manus", ram=4, cpu=2)
|
|
55
|
-
|
|
56
|
-
# Connect to existing computer by ID
|
|
57
|
-
computer = Computer(computer_id="11c4fd46-e069-4c32-be65-f82d9f87b9b8")
|
|
65
|
+
base_api_url: Custom API URL
|
|
66
|
+
ram/memory: RAM in GB (1, 2, 4, 8, 16, 32, 64)
|
|
67
|
+
cpu: CPU cores (1, 2, 4, 8, 16)
|
|
68
|
+
os: "linux" or "windows"
|
|
69
|
+
gpu: "none", "a10", "l40s", "a100-40gb", "a100-80gb"
|
|
70
|
+
image: Custom image reference or Forge object
|
|
58
71
|
"""
|
|
59
72
|
self.api_key = api_key or operating_system.environ.get("ORGO_API_KEY")
|
|
60
73
|
self.base_api_url = base_api_url
|
|
61
74
|
self.api = ApiClient(self.api_key, self.base_api_url)
|
|
62
75
|
|
|
63
|
-
# Handle memory parameter as an alias for ram
|
|
64
76
|
if ram is None and memory is not None:
|
|
65
77
|
ram = memory
|
|
66
78
|
|
|
67
|
-
# Store configuration
|
|
68
79
|
self.os = os or "linux"
|
|
69
80
|
self.ram = ram or 2
|
|
70
81
|
self.cpu = cpu or 2
|
|
71
82
|
self.gpu = gpu or "none"
|
|
72
|
-
|
|
73
|
-
# Handle image
|
|
74
83
|
self.image = image
|
|
84
|
+
|
|
75
85
|
if hasattr(self.image, 'build') and callable(self.image.build):
|
|
76
86
|
logger.info("Building image from Forge object...")
|
|
77
87
|
self.image = self.image.build()
|
|
78
88
|
|
|
79
89
|
if computer_id:
|
|
80
|
-
|
|
81
|
-
# Just store the computer ID, no API call needed
|
|
82
90
|
self.computer_id = computer_id
|
|
83
91
|
self.name = name
|
|
84
92
|
self.project_id = None
|
|
85
93
|
self.project_name = None
|
|
86
|
-
logger.info(f"Connected to computer
|
|
94
|
+
logger.info(f"Connected to computer: {self.computer_id}")
|
|
87
95
|
elif project:
|
|
88
|
-
# Work with specified project
|
|
89
96
|
if isinstance(project, str):
|
|
90
|
-
# Project name provided
|
|
91
97
|
self.project_name = project
|
|
92
98
|
self._initialize_with_project_name(project, name)
|
|
93
99
|
else:
|
|
94
|
-
# Project instance provided
|
|
95
100
|
from .project import Project as ProjectClass
|
|
96
101
|
if isinstance(project, ProjectClass):
|
|
97
102
|
self.project_name = project.name
|
|
98
103
|
self.project_id = project.id
|
|
99
104
|
self._initialize_with_project_instance(project, name)
|
|
100
105
|
else:
|
|
101
|
-
raise ValueError("project must be a string
|
|
106
|
+
raise ValueError("project must be a string or Project instance")
|
|
102
107
|
else:
|
|
103
|
-
# No project specified, create a new one
|
|
104
108
|
self._create_new_project_and_computer(name)
|
|
105
109
|
|
|
110
|
+
# =========================================================================
|
|
111
|
+
# Initialization Helpers
|
|
112
|
+
# =========================================================================
|
|
113
|
+
|
|
106
114
|
def _initialize_with_project_name(self, project_name: str, computer_name: Optional[str]):
|
|
107
|
-
"""Initialize with a project name (create project if needed)"""
|
|
108
115
|
try:
|
|
109
|
-
# Try to get existing project
|
|
110
116
|
project = self.api.get_project_by_name(project_name)
|
|
111
117
|
self.project_id = project.get("id")
|
|
112
|
-
|
|
113
|
-
# Check for existing computers
|
|
114
118
|
computers = self.api.list_computers(self.project_id)
|
|
115
119
|
|
|
116
120
|
if computer_name:
|
|
117
|
-
# Look for specific computer
|
|
118
121
|
existing = next((c for c in computers if c.get("name") == computer_name), None)
|
|
119
122
|
if existing:
|
|
120
123
|
self._connect_to_existing_computer(existing)
|
|
121
124
|
else:
|
|
122
|
-
# Create new computer with specified name
|
|
123
125
|
self._create_computer(self.project_id, computer_name)
|
|
124
126
|
elif computers:
|
|
125
|
-
# No name specified, use first available computer
|
|
126
127
|
self._connect_to_existing_computer(computers[0])
|
|
127
128
|
else:
|
|
128
|
-
# No computers exist, create new one
|
|
129
129
|
self._create_computer(self.project_id, computer_name)
|
|
130
|
-
|
|
131
130
|
except Exception:
|
|
132
|
-
|
|
133
|
-
logger.info(f"Project {project_name} not found, creating new project")
|
|
131
|
+
logger.info(f"Creating new project: {project_name}")
|
|
134
132
|
project = self.api.create_project(project_name)
|
|
135
133
|
self.project_id = project.get("id")
|
|
136
134
|
self._create_computer(self.project_id, computer_name)
|
|
137
135
|
|
|
138
136
|
def _initialize_with_project_instance(self, project: 'Project', computer_name: Optional[str]):
|
|
139
|
-
"""Initialize with a Project instance"""
|
|
140
137
|
computers = project.list_computers()
|
|
141
138
|
|
|
142
139
|
if computer_name:
|
|
143
|
-
# Look for specific computer
|
|
144
140
|
existing = next((c for c in computers if c.get("name") == computer_name), None)
|
|
145
141
|
if existing:
|
|
146
142
|
self._connect_to_existing_computer(existing)
|
|
147
143
|
else:
|
|
148
|
-
# Create new computer with specified name
|
|
149
144
|
self._create_computer(project.id, computer_name)
|
|
150
145
|
elif computers:
|
|
151
|
-
# No name specified, use first available computer
|
|
152
146
|
self._connect_to_existing_computer(computers[0])
|
|
153
147
|
else:
|
|
154
|
-
# No computers exist, create new one
|
|
155
148
|
self._create_computer(project.id, computer_name)
|
|
156
149
|
|
|
157
150
|
def _create_new_project_and_computer(self, computer_name: Optional[str]):
|
|
158
|
-
"""Create a new project and computer"""
|
|
159
|
-
# Generate a unique project name
|
|
160
151
|
project_name = f"project-{uuid.uuid4().hex[:8]}"
|
|
161
|
-
|
|
162
|
-
# Create the project
|
|
163
152
|
project = self.api.create_project(project_name)
|
|
164
153
|
self.project_id = project.get("id")
|
|
165
154
|
self.project_name = project_name
|
|
166
|
-
|
|
167
|
-
# Create a computer in the new project
|
|
168
155
|
self._create_computer(self.project_id, computer_name)
|
|
169
156
|
|
|
170
157
|
def _connect_to_existing_computer(self, computer_info: Dict[str, Any]):
|
|
171
|
-
"""Connect to an existing computer"""
|
|
172
158
|
self.computer_id = computer_info.get("id")
|
|
173
159
|
self.name = computer_info.get("name")
|
|
174
|
-
logger.info(f"Connected to
|
|
160
|
+
logger.info(f"Connected to: {self.name} ({self.computer_id})")
|
|
175
161
|
|
|
176
162
|
def _create_computer(self, project_id: str, computer_name: Optional[str]):
|
|
177
|
-
"""Create a new computer in the project"""
|
|
178
|
-
# Generate name if not provided
|
|
179
163
|
if not computer_name:
|
|
180
164
|
computer_name = f"desktop-{uuid.uuid4().hex[:8]}"
|
|
181
165
|
|
|
182
166
|
self.name = computer_name
|
|
183
167
|
|
|
184
|
-
# Validate
|
|
168
|
+
# Validate
|
|
185
169
|
if self.ram not in [1, 2, 4, 8, 16, 32, 64]:
|
|
186
|
-
raise ValueError("ram must be
|
|
170
|
+
raise ValueError("ram must be: 1, 2, 4, 8, 16, 32, or 64 GB")
|
|
187
171
|
if self.cpu not in [1, 2, 4, 8, 16]:
|
|
188
|
-
raise ValueError("cpu must be
|
|
172
|
+
raise ValueError("cpu must be: 1, 2, 4, 8, or 16 cores")
|
|
189
173
|
if self.os not in ["linux", "windows"]:
|
|
190
|
-
raise ValueError("os must be
|
|
174
|
+
raise ValueError("os must be: 'linux' or 'windows'")
|
|
191
175
|
if self.gpu not in ["none", "a10", "l40s", "a100-40gb", "a100-80gb"]:
|
|
192
|
-
raise ValueError("gpu must be
|
|
193
|
-
|
|
194
|
-
# Resolve image
|
|
176
|
+
raise ValueError("gpu must be: 'none', 'a10', 'l40s', 'a100-40gb', or 'a100-80gb'")
|
|
177
|
+
|
|
178
|
+
# Resolve image
|
|
195
179
|
image_ref = self.image
|
|
196
180
|
if image_ref and isinstance(image_ref, str) and not image_ref.startswith("registry.fly.io"):
|
|
197
|
-
logger.info(f"Resolving image name '{image_ref}'...")
|
|
198
181
|
try:
|
|
199
|
-
# Try to get org_id from project info
|
|
200
182
|
project_info = self.api.get_project(project_id)
|
|
201
|
-
org_id = project_info.get("org_id", "orgo")
|
|
202
|
-
|
|
183
|
+
org_id = project_info.get("org_id", "orgo")
|
|
203
184
|
response = self.api.get_latest_build(org_id, project_id, image_ref)
|
|
204
185
|
if response and response.get("build"):
|
|
205
|
-
|
|
206
|
-
if
|
|
207
|
-
|
|
208
|
-
image_ref = resolved_ref
|
|
209
|
-
else:
|
|
210
|
-
logger.warning(f"Build found for '{image_ref}' but no imageRef present.")
|
|
211
|
-
else:
|
|
212
|
-
logger.warning(f"Could not resolve image name '{self.image}'. Using as is.")
|
|
186
|
+
resolved = response.get("build", {}).get("imageRef")
|
|
187
|
+
if resolved:
|
|
188
|
+
image_ref = resolved
|
|
213
189
|
except Exception as e:
|
|
214
|
-
logger.warning(f"Failed to resolve image
|
|
190
|
+
logger.warning(f"Failed to resolve image: {e}")
|
|
215
191
|
|
|
216
192
|
computer = self.api.create_computer(
|
|
217
193
|
project_id=project_id,
|
|
@@ -223,54 +199,67 @@ class Computer:
|
|
|
223
199
|
image=image_ref
|
|
224
200
|
)
|
|
225
201
|
self.computer_id = computer.get("id")
|
|
226
|
-
logger.info(f"Created
|
|
202
|
+
logger.info(f"Created: {self.name} ({self.computer_id})")
|
|
203
|
+
|
|
204
|
+
# =========================================================================
|
|
205
|
+
# Computer Management
|
|
206
|
+
# =========================================================================
|
|
227
207
|
|
|
228
208
|
def status(self) -> Dict[str, Any]:
|
|
229
|
-
"""Get current computer status"""
|
|
209
|
+
"""Get current computer status."""
|
|
230
210
|
return self.api.get_computer(self.computer_id)
|
|
231
211
|
|
|
232
212
|
def restart(self) -> Dict[str, Any]:
|
|
233
|
-
"""Restart the computer"""
|
|
213
|
+
"""Restart the computer."""
|
|
234
214
|
return self.api.restart_computer(self.computer_id)
|
|
235
215
|
|
|
236
216
|
def destroy(self) -> Dict[str, Any]:
|
|
237
|
-
"""
|
|
217
|
+
"""Delete the computer."""
|
|
238
218
|
return self.api.delete_computer(self.computer_id)
|
|
239
219
|
|
|
240
|
-
#
|
|
220
|
+
# =========================================================================
|
|
221
|
+
# Mouse Actions
|
|
222
|
+
# =========================================================================
|
|
223
|
+
|
|
241
224
|
def left_click(self, x: int, y: int) -> Dict[str, Any]:
|
|
242
|
-
"""
|
|
225
|
+
"""Left click at coordinates."""
|
|
243
226
|
return self.api.left_click(self.computer_id, x, y)
|
|
244
227
|
|
|
245
228
|
def right_click(self, x: int, y: int) -> Dict[str, Any]:
|
|
246
|
-
"""
|
|
229
|
+
"""Right click at coordinates."""
|
|
247
230
|
return self.api.right_click(self.computer_id, x, y)
|
|
248
231
|
|
|
249
232
|
def double_click(self, x: int, y: int) -> Dict[str, Any]:
|
|
250
|
-
"""
|
|
233
|
+
"""Double click at coordinates."""
|
|
251
234
|
return self.api.double_click(self.computer_id, x, y)
|
|
252
235
|
|
|
253
236
|
def drag(self, start_x: int, start_y: int, end_x: int, end_y: int,
|
|
254
237
|
button: str = "left", duration: float = 0.5) -> Dict[str, Any]:
|
|
255
|
-
"""
|
|
238
|
+
"""Drag from start to end coordinates."""
|
|
256
239
|
return self.api.drag(self.computer_id, start_x, start_y, end_x, end_y, button, duration)
|
|
257
240
|
|
|
258
241
|
def scroll(self, direction: str = "down", amount: int = 3) -> Dict[str, Any]:
|
|
259
|
-
"""Scroll in
|
|
242
|
+
"""Scroll in direction."""
|
|
260
243
|
return self.api.scroll(self.computer_id, direction, amount)
|
|
261
244
|
|
|
262
|
-
#
|
|
245
|
+
# =========================================================================
|
|
246
|
+
# Keyboard Actions
|
|
247
|
+
# =========================================================================
|
|
248
|
+
|
|
263
249
|
def type(self, text: str) -> Dict[str, Any]:
|
|
264
|
-
"""Type
|
|
250
|
+
"""Type text."""
|
|
265
251
|
return self.api.type_text(self.computer_id, text)
|
|
266
252
|
|
|
267
253
|
def key(self, key: str) -> Dict[str, Any]:
|
|
268
|
-
"""Press
|
|
254
|
+
"""Press key (e.g., "Enter", "ctrl+c")."""
|
|
269
255
|
return self.api.key_press(self.computer_id, key)
|
|
270
256
|
|
|
271
|
-
#
|
|
257
|
+
# =========================================================================
|
|
258
|
+
# Screen Capture
|
|
259
|
+
# =========================================================================
|
|
260
|
+
|
|
272
261
|
def screenshot(self) -> Image.Image:
|
|
273
|
-
"""Capture screenshot
|
|
262
|
+
"""Capture screenshot as PIL Image."""
|
|
274
263
|
response = self.api.get_screenshot(self.computer_id)
|
|
275
264
|
image_data = response.get("image", "")
|
|
276
265
|
|
|
@@ -279,11 +268,10 @@ class Computer:
|
|
|
279
268
|
img_response.raise_for_status()
|
|
280
269
|
return Image.open(io.BytesIO(img_response.content))
|
|
281
270
|
else:
|
|
282
|
-
|
|
283
|
-
return Image.open(io.BytesIO(img_data))
|
|
271
|
+
return Image.open(io.BytesIO(base64.b64decode(image_data)))
|
|
284
272
|
|
|
285
273
|
def screenshot_base64(self) -> str:
|
|
286
|
-
"""Capture screenshot
|
|
274
|
+
"""Capture screenshot as base64 string."""
|
|
287
275
|
response = self.api.get_screenshot(self.computer_id)
|
|
288
276
|
image_data = response.get("image", "")
|
|
289
277
|
|
|
@@ -291,58 +279,102 @@ class Computer:
|
|
|
291
279
|
img_response = requests.get(image_data)
|
|
292
280
|
img_response.raise_for_status()
|
|
293
281
|
return base64.b64encode(img_response.content).decode('utf-8')
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
return image_data
|
|
283
|
+
|
|
284
|
+
# =========================================================================
|
|
285
|
+
# Code Execution
|
|
286
|
+
# =========================================================================
|
|
296
287
|
|
|
297
|
-
# Execution methods
|
|
298
288
|
def bash(self, command: str) -> str:
|
|
299
|
-
"""Execute
|
|
289
|
+
"""Execute bash command."""
|
|
300
290
|
response = self.api.execute_bash(self.computer_id, command)
|
|
301
291
|
return response.get("output", "")
|
|
302
292
|
|
|
303
293
|
def exec(self, code: str, timeout: int = 10) -> Dict[str, Any]:
|
|
304
|
-
"""Execute Python code
|
|
305
|
-
|
|
306
|
-
return response
|
|
294
|
+
"""Execute Python code."""
|
|
295
|
+
return self.api.execute_python(self.computer_id, code, timeout)
|
|
307
296
|
|
|
308
297
|
def wait(self, seconds: float) -> Dict[str, Any]:
|
|
309
|
-
"""Wait for
|
|
298
|
+
"""Wait for seconds."""
|
|
310
299
|
return self.api.wait(self.computer_id, seconds)
|
|
311
300
|
|
|
312
|
-
#
|
|
301
|
+
# =========================================================================
|
|
302
|
+
# Streaming
|
|
303
|
+
# =========================================================================
|
|
304
|
+
|
|
313
305
|
def start_stream(self, connection: str) -> Dict[str, Any]:
|
|
314
|
-
"""Start
|
|
306
|
+
"""Start RTMP stream."""
|
|
315
307
|
return self.api.start_stream(self.computer_id, connection)
|
|
316
308
|
|
|
317
309
|
def stop_stream(self) -> Dict[str, Any]:
|
|
318
|
-
"""Stop
|
|
310
|
+
"""Stop stream."""
|
|
319
311
|
return self.api.stop_stream(self.computer_id)
|
|
320
312
|
|
|
321
313
|
def stream_status(self) -> Dict[str, Any]:
|
|
322
|
-
"""Get
|
|
314
|
+
"""Get stream status."""
|
|
323
315
|
return self.api.get_stream_status(self.computer_id)
|
|
324
316
|
|
|
325
|
-
#
|
|
317
|
+
# =========================================================================
|
|
318
|
+
# AI Control
|
|
319
|
+
# =========================================================================
|
|
320
|
+
|
|
326
321
|
def prompt(self,
|
|
327
322
|
instruction: str,
|
|
328
|
-
provider: str =
|
|
323
|
+
provider: Optional[str] = None,
|
|
324
|
+
verbose: bool = True,
|
|
325
|
+
callback: Optional[Callable[[str, Any], None]] = None,
|
|
329
326
|
model: str = "claude-sonnet-4-5-20250929",
|
|
330
327
|
display_width: int = 1024,
|
|
331
328
|
display_height: int = 768,
|
|
332
|
-
|
|
333
|
-
thinking_enabled: bool = False,
|
|
329
|
+
thinking_enabled: bool = True,
|
|
334
330
|
thinking_budget: int = 1024,
|
|
335
331
|
max_tokens: int = 4096,
|
|
336
|
-
max_iterations: int =
|
|
337
|
-
max_saved_screenshots: int =
|
|
332
|
+
max_iterations: int = 100,
|
|
333
|
+
max_saved_screenshots: int = 3,
|
|
334
|
+
system_prompt: Optional[str] = None,
|
|
338
335
|
api_key: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
339
|
-
"""
|
|
336
|
+
"""
|
|
337
|
+
Control the computer with natural language.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
instruction: What you want the computer to do
|
|
341
|
+
provider: "orgo" (default) or "anthropic"
|
|
342
|
+
verbose: Show progress logs (default: True)
|
|
343
|
+
callback: Optional callback for events
|
|
344
|
+
model: AI model to use
|
|
345
|
+
display_width: Screen width
|
|
346
|
+
display_height: Screen height
|
|
347
|
+
thinking_enabled: Enable extended thinking
|
|
348
|
+
thinking_budget: Token budget for thinking
|
|
349
|
+
max_tokens: Max response tokens
|
|
350
|
+
max_iterations: Max agent iterations
|
|
351
|
+
max_saved_screenshots: Screenshots to keep in context
|
|
352
|
+
system_prompt: Custom instructions
|
|
353
|
+
api_key: Anthropic key (only for provider="anthropic")
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
List of conversation messages
|
|
357
|
+
|
|
358
|
+
Examples:
|
|
359
|
+
# Default: Uses Orgo hosted agent
|
|
360
|
+
computer.prompt("Open Firefox and search for AI news")
|
|
361
|
+
|
|
362
|
+
# Quiet mode (no logs)
|
|
363
|
+
computer.prompt("Open Firefox", verbose=False)
|
|
364
|
+
|
|
365
|
+
# Use Anthropic directly
|
|
366
|
+
computer.prompt("Open Firefox", provider="anthropic")
|
|
367
|
+
|
|
368
|
+
# With callback
|
|
369
|
+
computer.prompt("Search Google", callback=lambda t, d: print(f"{t}: {d}"))
|
|
370
|
+
"""
|
|
340
371
|
provider_instance = get_provider(provider)
|
|
341
372
|
|
|
342
373
|
return provider_instance.execute(
|
|
343
374
|
computer_id=self.computer_id,
|
|
344
375
|
instruction=instruction,
|
|
345
376
|
callback=callback,
|
|
377
|
+
verbose=verbose,
|
|
346
378
|
api_key=api_key,
|
|
347
379
|
model=model,
|
|
348
380
|
display_width=display_width,
|
|
@@ -352,11 +384,21 @@ class Computer:
|
|
|
352
384
|
max_tokens=max_tokens,
|
|
353
385
|
max_iterations=max_iterations,
|
|
354
386
|
max_saved_screenshots=max_saved_screenshots,
|
|
387
|
+
system_prompt=system_prompt,
|
|
355
388
|
orgo_api_key=self.api_key,
|
|
356
389
|
orgo_base_url=self.base_api_url
|
|
357
390
|
)
|
|
358
391
|
|
|
392
|
+
# =========================================================================
|
|
393
|
+
# URL Helper
|
|
394
|
+
# =========================================================================
|
|
395
|
+
|
|
396
|
+
@property
|
|
397
|
+
def url(self) -> str:
|
|
398
|
+
"""Get the URL to view this computer."""
|
|
399
|
+
return f"https://orgo.ai/workspaces/{self.computer_id}"
|
|
400
|
+
|
|
359
401
|
def __repr__(self):
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return f"Computer({
|
|
402
|
+
if hasattr(self, 'name') and self.name:
|
|
403
|
+
return f"Computer(name='{self.name}', id='{self.computer_id}')"
|
|
404
|
+
return f"Computer(id='{self.computer_id}')"
|