cua-agent 0.1.2__py3-none-any.whl → 0.1.3__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.
Potentially problematic release.
This version of cua-agent might be problematic. Click here for more details.
- agent/__init__.py +12 -0
- agent/core/agent.py +85 -153
- agent/core/base_agent.py +1 -1
- agent/core/experiment.py +11 -1
- agent/core/loop.py +1 -1
- agent/core/telemetry.py +138 -0
- agent/providers/omni/loop.py +2 -2
- agent/providers/omni/parser.py +1 -1
- agent/providers/omni/types.py +0 -6
- agent/telemetry.py +21 -0
- {cua_agent-0.1.2.dist-info → cua_agent-0.1.3.dist-info}/METADATA +2 -1
- {cua_agent-0.1.2.dist-info → cua_agent-0.1.3.dist-info}/RECORD +14 -12
- {cua_agent-0.1.2.dist-info → cua_agent-0.1.3.dist-info}/WHEEL +0 -0
- {cua_agent-0.1.2.dist-info → cua_agent-0.1.3.dist-info}/entry_points.txt +0 -0
agent/__init__.py
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
__version__ = "0.1.0"
|
|
4
4
|
|
|
5
|
+
# Initialize telemetry when the package is imported
|
|
6
|
+
try:
|
|
7
|
+
from core.telemetry import enable_telemetry, set_dimension
|
|
8
|
+
|
|
9
|
+
# Enable telemetry by default
|
|
10
|
+
enable_telemetry()
|
|
11
|
+
# Set the package version as a dimension
|
|
12
|
+
set_dimension("agent_version", __version__)
|
|
13
|
+
except ImportError:
|
|
14
|
+
# Core telemetry not available
|
|
15
|
+
pass
|
|
16
|
+
|
|
5
17
|
from .core.factory import AgentFactory
|
|
6
18
|
from .core.agent import ComputerAgent
|
|
7
19
|
from .providers.omni.types import LLMProvider, LLM
|
agent/core/agent.py
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import logging
|
|
5
5
|
import asyncio
|
|
6
|
+
import time
|
|
7
|
+
import uuid
|
|
6
8
|
from typing import Any, AsyncGenerator, Dict, List, Optional, TYPE_CHECKING, Union, cast
|
|
7
9
|
from datetime import datetime
|
|
10
|
+
from enum import Enum
|
|
8
11
|
|
|
9
12
|
from computer import Computer
|
|
10
13
|
|
|
11
14
|
from ..types.base import Provider, AgentLoop
|
|
12
15
|
from .base_agent import BaseComputerAgent
|
|
16
|
+
from ..core.telemetry import record_agent_initialization
|
|
13
17
|
|
|
14
18
|
# Only import types for type checking to avoid circular imports
|
|
15
19
|
if TYPE_CHECKING:
|
|
@@ -26,13 +30,11 @@ logger = logging.getLogger(__name__)
|
|
|
26
30
|
DEFAULT_MODELS = {
|
|
27
31
|
LLMProvider.OPENAI: "gpt-4o",
|
|
28
32
|
LLMProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
|
|
29
|
-
LLMProvider.GROQ: "llama3-70b-8192",
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
# Map providers to their environment variable names
|
|
33
36
|
ENV_VARS = {
|
|
34
37
|
LLMProvider.OPENAI: "OPENAI_API_KEY",
|
|
35
|
-
LLMProvider.GROQ: "GROQ_API_KEY",
|
|
36
38
|
LLMProvider.ANTHROPIC: "ANTHROPIC_API_KEY",
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -55,79 +57,68 @@ class ComputerAgent(BaseComputerAgent):
|
|
|
55
57
|
only_n_most_recent_images: Optional[int] = None,
|
|
56
58
|
max_retries: int = 3,
|
|
57
59
|
verbosity: int = logging.INFO,
|
|
60
|
+
telemetry_enabled: bool = True,
|
|
58
61
|
**kwargs,
|
|
59
62
|
):
|
|
60
|
-
"""Initialize
|
|
63
|
+
"""Initialize a ComputerAgent instance.
|
|
61
64
|
|
|
62
65
|
Args:
|
|
63
|
-
computer: Computer instance to control
|
|
64
|
-
loop: The
|
|
65
|
-
model:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
verbosity: Logging level (standard Python logging levels: logging.DEBUG, logging.INFO, etc.)
|
|
76
|
-
**kwargs: Additional keyword arguments to pass to the loop
|
|
66
|
+
computer: The Computer instance to control
|
|
67
|
+
loop: The agent loop to use: ANTHROPIC or OMNI
|
|
68
|
+
model: The model to use. Can be a string, dict or LLM object.
|
|
69
|
+
Defaults to LLM for the loop type.
|
|
70
|
+
api_key: The API key to use. If None, will use environment variables.
|
|
71
|
+
save_trajectory: Whether to save the trajectory.
|
|
72
|
+
trajectory_dir: The directory to save trajectories to.
|
|
73
|
+
only_n_most_recent_images: Only keep this many most recent images.
|
|
74
|
+
max_retries: Maximum number of retries for failed requests.
|
|
75
|
+
verbosity: Logging level (standard Python logging levels).
|
|
76
|
+
telemetry_enabled: Whether to enable telemetry tracking. Defaults to True.
|
|
77
|
+
**kwargs: Additional keyword arguments to pass to the loop.
|
|
77
78
|
"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
super().__init__(computer)
|
|
80
|
+
self._configure_logging(verbosity)
|
|
81
|
+
logger.info(f"Initializing ComputerAgent with {loop} loop")
|
|
82
|
+
|
|
83
|
+
# Store telemetry preference
|
|
84
|
+
self.telemetry_enabled = telemetry_enabled
|
|
85
|
+
|
|
86
|
+
# Pass telemetry preference to computer if available
|
|
87
|
+
if hasattr(computer, "telemetry_enabled"):
|
|
88
|
+
# Computer doesn't have a setter for telemetry_enabled
|
|
89
|
+
# Use disable_telemetry() method if telemetry is disabled
|
|
90
|
+
if not telemetry_enabled and hasattr(computer, "disable_telemetry"):
|
|
91
|
+
computer.disable_telemetry()
|
|
91
92
|
|
|
93
|
+
# Process the model configuration
|
|
94
|
+
self.model = self._process_model_config(model, loop)
|
|
92
95
|
self.loop_type = loop
|
|
96
|
+
self.api_key = api_key
|
|
97
|
+
|
|
98
|
+
# Store computer
|
|
99
|
+
self.computer = computer
|
|
100
|
+
|
|
101
|
+
# Save trajectory settings
|
|
93
102
|
self.save_trajectory = save_trajectory
|
|
94
103
|
self.trajectory_dir = trajectory_dir
|
|
95
104
|
self.only_n_most_recent_images = only_n_most_recent_images
|
|
96
|
-
self.verbosity = verbosity
|
|
97
|
-
self._kwargs = kwargs # Keep this for loop initialization
|
|
98
105
|
|
|
99
|
-
#
|
|
100
|
-
self.
|
|
106
|
+
# Store the max retries setting
|
|
107
|
+
self.max_retries = max_retries
|
|
101
108
|
|
|
102
|
-
#
|
|
103
|
-
self.
|
|
109
|
+
# Initialize message history
|
|
110
|
+
self.messages = []
|
|
104
111
|
|
|
105
|
-
#
|
|
106
|
-
|
|
107
|
-
env_var = (
|
|
108
|
-
ENV_VARS.get(self.model_config.provider)
|
|
109
|
-
if loop == AgentLoop.OMNI
|
|
110
|
-
else "ANTHROPIC_API_KEY"
|
|
111
|
-
)
|
|
112
|
-
if not env_var:
|
|
113
|
-
raise ValueError(
|
|
114
|
-
f"Unsupported provider: {self.model_config.provider}. Please use one of: {list(ENV_VARS.keys())}"
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
api_key = os.environ.get(env_var)
|
|
118
|
-
if not api_key:
|
|
119
|
-
raise ValueError(
|
|
120
|
-
f"No API key provided and {env_var} environment variable is not set.\n"
|
|
121
|
-
f"Please set the {env_var} environment variable or pass the api_key directly:\n"
|
|
122
|
-
f" - Export in terminal: export {env_var}=your_api_key_here\n"
|
|
123
|
-
f" - Add to .env file: {env_var}=your_api_key_here\n"
|
|
124
|
-
f" - Pass directly: api_key='your_api_key_here'"
|
|
125
|
-
)
|
|
126
|
-
self.api_key = api_key
|
|
112
|
+
# Extra kwargs for the loop
|
|
113
|
+
self.loop_kwargs = kwargs
|
|
127
114
|
|
|
128
|
-
# Initialize the
|
|
115
|
+
# Initialize the actual loop implementation
|
|
129
116
|
self.loop = self._init_loop()
|
|
130
117
|
|
|
118
|
+
# Record initialization in telemetry if enabled
|
|
119
|
+
if telemetry_enabled:
|
|
120
|
+
record_agent_initialization()
|
|
121
|
+
|
|
131
122
|
def _process_model_config(
|
|
132
123
|
self, model_input: Optional[Union[LLM, Dict[str, str], str]], loop: AgentLoop
|
|
133
124
|
) -> LLM:
|
|
@@ -200,7 +191,7 @@ class ComputerAgent(BaseComputerAgent):
|
|
|
200
191
|
from ..providers.anthropic.loop import AnthropicLoop
|
|
201
192
|
|
|
202
193
|
# Ensure we always have a valid model name
|
|
203
|
-
model_name = self.
|
|
194
|
+
model_name = self.model.name or DEFAULT_MODELS[LLMProvider.ANTHROPIC]
|
|
204
195
|
|
|
205
196
|
return AnthropicLoop(
|
|
206
197
|
api_key=self.api_key,
|
|
@@ -209,119 +200,60 @@ class ComputerAgent(BaseComputerAgent):
|
|
|
209
200
|
save_trajectory=self.save_trajectory,
|
|
210
201
|
base_dir=self.trajectory_dir,
|
|
211
202
|
only_n_most_recent_images=self.only_n_most_recent_images,
|
|
212
|
-
**self.
|
|
203
|
+
**self.loop_kwargs,
|
|
213
204
|
)
|
|
214
205
|
|
|
215
206
|
# Initialize parser for OmniLoop with appropriate device
|
|
216
|
-
if "parser" not in self.
|
|
217
|
-
self.
|
|
207
|
+
if "parser" not in self.loop_kwargs:
|
|
208
|
+
self.loop_kwargs["parser"] = OmniParser()
|
|
218
209
|
|
|
219
210
|
# Ensure we always have a valid model name
|
|
220
|
-
model_name = self.
|
|
211
|
+
model_name = self.model.name or DEFAULT_MODELS[self.model.provider]
|
|
221
212
|
|
|
222
213
|
return OmniLoop(
|
|
223
|
-
provider=self.
|
|
214
|
+
provider=self.model.provider,
|
|
224
215
|
api_key=self.api_key,
|
|
225
216
|
model=model_name,
|
|
226
217
|
computer=self.computer,
|
|
227
218
|
save_trajectory=self.save_trajectory,
|
|
228
219
|
base_dir=self.trajectory_dir,
|
|
229
220
|
only_n_most_recent_images=self.only_n_most_recent_images,
|
|
230
|
-
**self.
|
|
221
|
+
**self.loop_kwargs,
|
|
231
222
|
)
|
|
232
223
|
|
|
233
224
|
async def _execute_task(self, task: str) -> AsyncGenerator[Dict[str, Any], None]:
|
|
234
|
-
"""Execute a task using the appropriate loop.
|
|
225
|
+
"""Execute a task using the appropriate agent loop.
|
|
235
226
|
|
|
236
227
|
Args:
|
|
237
|
-
task:
|
|
228
|
+
task: The task to execute
|
|
238
229
|
|
|
239
|
-
|
|
240
|
-
|
|
230
|
+
Returns:
|
|
231
|
+
AsyncGenerator yielding task outputs
|
|
241
232
|
"""
|
|
233
|
+
logger.info(f"Executing task: {task}")
|
|
234
|
+
|
|
242
235
|
try:
|
|
243
|
-
#
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
break
|
|
256
|
-
|
|
257
|
-
# Handle error case
|
|
258
|
-
if "error" in result:
|
|
259
|
-
yield {
|
|
260
|
-
"role": "assistant",
|
|
261
|
-
"content": result["error"],
|
|
262
|
-
"metadata": {"title": "❌ Error"},
|
|
263
|
-
}
|
|
264
|
-
continue
|
|
265
|
-
|
|
266
|
-
# Extract content and metadata based on loop type
|
|
267
|
-
if self.loop_type == AgentLoop.ANTHROPIC:
|
|
268
|
-
# Handle Anthropic format
|
|
269
|
-
if "content" in result:
|
|
270
|
-
content_text = ""
|
|
271
|
-
for content_block in result["content"]:
|
|
272
|
-
try:
|
|
273
|
-
# Try to access the text attribute directly
|
|
274
|
-
content_text += content_block.text
|
|
275
|
-
except (AttributeError, TypeError):
|
|
276
|
-
# If it's a dictionary instead of an object
|
|
277
|
-
if isinstance(content_block, dict) and "text" in content_block:
|
|
278
|
-
content_text += content_block["text"]
|
|
279
|
-
|
|
280
|
-
yield {
|
|
281
|
-
"role": "assistant",
|
|
282
|
-
"content": content_text,
|
|
283
|
-
"metadata": result.get("parsed_screen", {}),
|
|
284
|
-
}
|
|
285
|
-
else:
|
|
286
|
-
yield {
|
|
287
|
-
"role": "assistant",
|
|
288
|
-
"content": str(result),
|
|
289
|
-
"metadata": {"title": "Screen Analysis"},
|
|
290
|
-
}
|
|
291
|
-
else:
|
|
292
|
-
# Handle Omni format
|
|
293
|
-
content = ""
|
|
294
|
-
metadata = {"title": "Screen Analysis"}
|
|
295
|
-
|
|
296
|
-
# If result has content (normal case)
|
|
297
|
-
if "content" in result:
|
|
298
|
-
content = result["content"]
|
|
299
|
-
|
|
300
|
-
# Ensure metadata has a title
|
|
301
|
-
if isinstance(content, dict) and "metadata" in content:
|
|
302
|
-
metadata = content["metadata"]
|
|
303
|
-
if "title" not in metadata:
|
|
304
|
-
metadata["title"] = "Screen Analysis"
|
|
305
|
-
|
|
306
|
-
# For string content, convert to proper format
|
|
307
|
-
if isinstance(content, str):
|
|
308
|
-
content = content
|
|
309
|
-
elif isinstance(content, dict) and "content" in content:
|
|
310
|
-
content = content.get("content", "")
|
|
311
|
-
|
|
312
|
-
yield {"role": "assistant", "content": content, "metadata": metadata}
|
|
313
|
-
except Exception as e:
|
|
314
|
-
logger.error(f"Error running the loop: {str(e)}")
|
|
315
|
-
yield {
|
|
316
|
-
"role": "assistant",
|
|
317
|
-
"content": f"Error running the agent loop: {str(e)}",
|
|
318
|
-
"metadata": {"title": "❌ Loop Error"},
|
|
319
|
-
}
|
|
236
|
+
# Create a message from the task
|
|
237
|
+
task_message = {"role": "user", "content": task}
|
|
238
|
+
messages_with_task = self.messages + [task_message]
|
|
239
|
+
|
|
240
|
+
# Use the run method of the loop
|
|
241
|
+
async for output in self.loop.run(messages_with_task):
|
|
242
|
+
yield output
|
|
243
|
+
except Exception as e:
|
|
244
|
+
logger.error(f"Error executing task: {e}")
|
|
245
|
+
raise
|
|
246
|
+
finally:
|
|
247
|
+
pass
|
|
320
248
|
|
|
249
|
+
async def _execute_action(self, action_type: str, **action_params) -> Any:
|
|
250
|
+
"""Execute an action with telemetry tracking."""
|
|
251
|
+
try:
|
|
252
|
+
# Execute the action
|
|
253
|
+
result = await super()._execute_action(action_type, **action_params)
|
|
254
|
+
return result
|
|
321
255
|
except Exception as e:
|
|
322
|
-
logger.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
"metadata": {"title": "❌ Error"},
|
|
327
|
-
}
|
|
256
|
+
logger.exception(f"Error executing action {action_type}: {e}")
|
|
257
|
+
raise
|
|
258
|
+
finally:
|
|
259
|
+
pass
|
agent/core/base_agent.py
CHANGED
|
@@ -113,7 +113,7 @@ class BaseComputerAgent(ABC):
|
|
|
113
113
|
# Take a test screenshot to verify the computer is working
|
|
114
114
|
logger.info("Testing computer with a screenshot...")
|
|
115
115
|
try:
|
|
116
|
-
test_screenshot = await self.computer.screenshot()
|
|
116
|
+
test_screenshot = await self.computer.interface.screenshot()
|
|
117
117
|
# Determine the screenshot size based on its type
|
|
118
118
|
if isinstance(test_screenshot, bytes):
|
|
119
119
|
size = len(test_screenshot)
|
agent/core/experiment.py
CHANGED
|
@@ -8,6 +8,7 @@ from datetime import datetime
|
|
|
8
8
|
from typing import Any, Dict, List, Optional
|
|
9
9
|
from PIL import Image
|
|
10
10
|
import json
|
|
11
|
+
import re
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
13
14
|
|
|
@@ -106,9 +107,18 @@ class ExperimentManager:
|
|
|
106
107
|
# Increment screenshot counter
|
|
107
108
|
self.screenshot_count += 1
|
|
108
109
|
|
|
110
|
+
# Sanitize action_type to ensure valid filename
|
|
111
|
+
# Replace characters that are not safe for filenames
|
|
112
|
+
sanitized_action = ""
|
|
113
|
+
if action_type:
|
|
114
|
+
# Replace invalid filename characters with underscores
|
|
115
|
+
sanitized_action = re.sub(r'[\\/*?:"<>|]', "_", action_type)
|
|
116
|
+
# Limit the length to avoid excessively long filenames
|
|
117
|
+
sanitized_action = sanitized_action[:50]
|
|
118
|
+
|
|
109
119
|
# Create a descriptive filename
|
|
110
120
|
timestamp = int(datetime.now().timestamp() * 1000)
|
|
111
|
-
action_suffix = f"_{
|
|
121
|
+
action_suffix = f"_{sanitized_action}" if sanitized_action else ""
|
|
112
122
|
filename = f"screenshot_{self.screenshot_count:03d}{action_suffix}_{timestamp}.png"
|
|
113
123
|
|
|
114
124
|
# Save directly to the turn directory
|
agent/core/loop.py
CHANGED
agent/core/telemetry.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Agent telemetry for tracking anonymous usage and feature usage."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
|
|
10
|
+
# Import the core telemetry module
|
|
11
|
+
TELEMETRY_AVAILABLE = False
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from core.telemetry import (
|
|
15
|
+
record_event,
|
|
16
|
+
increment,
|
|
17
|
+
get_telemetry_client,
|
|
18
|
+
flush,
|
|
19
|
+
is_telemetry_enabled,
|
|
20
|
+
is_telemetry_globally_disabled,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def increment_counter(counter_name: str, value: int = 1) -> None:
|
|
24
|
+
"""Wrapper for increment to maintain backward compatibility."""
|
|
25
|
+
if is_telemetry_enabled():
|
|
26
|
+
increment(counter_name, value)
|
|
27
|
+
|
|
28
|
+
def set_dimension(name: str, value: Any) -> None:
|
|
29
|
+
"""Set a dimension that will be attached to all events."""
|
|
30
|
+
logger = logging.getLogger("cua.agent.telemetry")
|
|
31
|
+
logger.debug(f"Setting dimension {name}={value}")
|
|
32
|
+
|
|
33
|
+
TELEMETRY_AVAILABLE = True
|
|
34
|
+
logger = logging.getLogger("cua.agent.telemetry")
|
|
35
|
+
logger.info("Successfully imported telemetry")
|
|
36
|
+
except ImportError as e:
|
|
37
|
+
logger = logging.getLogger("cua.agent.telemetry")
|
|
38
|
+
logger.warning(f"Could not import telemetry: {e}")
|
|
39
|
+
TELEMETRY_AVAILABLE = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Local fallbacks in case core telemetry isn't available
|
|
43
|
+
def _noop(*args: Any, **kwargs: Any) -> None:
|
|
44
|
+
"""No-op function for when telemetry is not available."""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
logger = logging.getLogger("cua.agent.telemetry")
|
|
49
|
+
|
|
50
|
+
# If telemetry isn't available, use no-op functions
|
|
51
|
+
if not TELEMETRY_AVAILABLE:
|
|
52
|
+
logger.debug("Telemetry not available, using no-op functions")
|
|
53
|
+
record_event = _noop # type: ignore
|
|
54
|
+
increment_counter = _noop # type: ignore
|
|
55
|
+
set_dimension = _noop # type: ignore
|
|
56
|
+
get_telemetry_client = lambda: None # type: ignore
|
|
57
|
+
flush = _noop # type: ignore
|
|
58
|
+
is_telemetry_enabled = lambda: False # type: ignore
|
|
59
|
+
is_telemetry_globally_disabled = lambda: True # type: ignore
|
|
60
|
+
|
|
61
|
+
# Get system info once to use in telemetry
|
|
62
|
+
SYSTEM_INFO = {
|
|
63
|
+
"os": platform.system().lower(),
|
|
64
|
+
"os_version": platform.release(),
|
|
65
|
+
"python_version": platform.python_version(),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def enable_telemetry() -> bool:
|
|
70
|
+
"""Enable telemetry if available.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
bool: True if telemetry was successfully enabled, False otherwise
|
|
74
|
+
"""
|
|
75
|
+
global TELEMETRY_AVAILABLE
|
|
76
|
+
|
|
77
|
+
# Check if globally disabled using core function
|
|
78
|
+
if TELEMETRY_AVAILABLE and is_telemetry_globally_disabled():
|
|
79
|
+
logger.info("Telemetry is globally disabled via environment variable - cannot enable")
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
# Already enabled
|
|
83
|
+
if TELEMETRY_AVAILABLE:
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
# Try to import and enable
|
|
87
|
+
try:
|
|
88
|
+
from core.telemetry import (
|
|
89
|
+
record_event,
|
|
90
|
+
increment,
|
|
91
|
+
get_telemetry_client,
|
|
92
|
+
flush,
|
|
93
|
+
is_telemetry_globally_disabled,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Check again after import
|
|
97
|
+
if is_telemetry_globally_disabled():
|
|
98
|
+
logger.info("Telemetry is globally disabled via environment variable - cannot enable")
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
TELEMETRY_AVAILABLE = True
|
|
102
|
+
logger.info("Telemetry successfully enabled")
|
|
103
|
+
return True
|
|
104
|
+
except ImportError as e:
|
|
105
|
+
logger.warning(f"Could not enable telemetry: {e}")
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def disable_telemetry() -> None:
|
|
110
|
+
"""Disable telemetry for this session."""
|
|
111
|
+
global TELEMETRY_AVAILABLE
|
|
112
|
+
TELEMETRY_AVAILABLE = False
|
|
113
|
+
logger.info("Telemetry disabled for this session")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def is_telemetry_enabled() -> bool:
|
|
117
|
+
"""Check if telemetry is enabled.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
bool: True if telemetry is enabled, False otherwise
|
|
121
|
+
"""
|
|
122
|
+
# Use the core function if available, otherwise use our local flag
|
|
123
|
+
if TELEMETRY_AVAILABLE:
|
|
124
|
+
from core.telemetry import is_telemetry_enabled as core_is_enabled
|
|
125
|
+
|
|
126
|
+
return core_is_enabled()
|
|
127
|
+
return False
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def record_agent_initialization() -> None:
|
|
131
|
+
"""Record when an agent instance is initialized."""
|
|
132
|
+
if TELEMETRY_AVAILABLE and is_telemetry_enabled():
|
|
133
|
+
record_event("agent_initialized", SYSTEM_INFO)
|
|
134
|
+
|
|
135
|
+
# Set dimensions that will be attached to all events
|
|
136
|
+
set_dimension("os", SYSTEM_INFO["os"])
|
|
137
|
+
set_dimension("os_version", SYSTEM_INFO["os_version"])
|
|
138
|
+
set_dimension("python_version", SYSTEM_INFO["python_version"])
|
agent/providers/omni/loop.py
CHANGED
|
@@ -731,7 +731,7 @@ class OmniLoop(BaseLoop):
|
|
|
731
731
|
action_type = f"hotkey_{content['Value'].replace('+', '_')}"
|
|
732
732
|
logger.info(f"Preparing hotkey with keys: {keys}")
|
|
733
733
|
# Get the method but call it with *args instead of **kwargs
|
|
734
|
-
method = getattr(self.computer, action)
|
|
734
|
+
method = getattr(self.computer.interface, action)
|
|
735
735
|
await method(*keys) # Unpack the keys list as positional arguments
|
|
736
736
|
logger.info(f"Tool execution completed successfully: {action}")
|
|
737
737
|
|
|
@@ -776,7 +776,7 @@ class OmniLoop(BaseLoop):
|
|
|
776
776
|
|
|
777
777
|
# Execute tool and handle result
|
|
778
778
|
try:
|
|
779
|
-
method = getattr(self.computer, action)
|
|
779
|
+
method = getattr(self.computer.interface, action)
|
|
780
780
|
logger.info(f"Found method for action '{action}': {method}")
|
|
781
781
|
await method(**kwargs)
|
|
782
782
|
logger.info(f"Tool execution completed successfully: {action}")
|
agent/providers/omni/parser.py
CHANGED
|
@@ -79,7 +79,7 @@ class OmniParser:
|
|
|
79
79
|
try:
|
|
80
80
|
# Get screenshot from computer
|
|
81
81
|
logger.info("Taking screenshot...")
|
|
82
|
-
screenshot = await computer.screenshot()
|
|
82
|
+
screenshot = await computer.interface.screenshot()
|
|
83
83
|
|
|
84
84
|
# Log screenshot info
|
|
85
85
|
logger.info(f"Screenshot type: {type(screenshot)}")
|
agent/providers/omni/types.py
CHANGED
|
@@ -10,8 +10,6 @@ class LLMProvider(StrEnum):
|
|
|
10
10
|
|
|
11
11
|
ANTHROPIC = "anthropic"
|
|
12
12
|
OPENAI = "openai"
|
|
13
|
-
GROQ = "groq"
|
|
14
|
-
QWEN = "qwen"
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
LLMProvider
|
|
@@ -39,14 +37,10 @@ Model = LLM
|
|
|
39
37
|
PROVIDER_TO_DEFAULT_MODEL: Dict[LLMProvider, str] = {
|
|
40
38
|
LLMProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
|
|
41
39
|
LLMProvider.OPENAI: "gpt-4o",
|
|
42
|
-
LLMProvider.GROQ: "deepseek-r1-distill-llama-70b",
|
|
43
|
-
LLMProvider.QWEN: "qwen2.5-vl-72b-instruct",
|
|
44
40
|
}
|
|
45
41
|
|
|
46
42
|
# Environment variable names for each provider
|
|
47
43
|
PROVIDER_TO_ENV_VAR: Dict[LLMProvider, str] = {
|
|
48
44
|
LLMProvider.ANTHROPIC: "ANTHROPIC_API_KEY",
|
|
49
45
|
LLMProvider.OPENAI: "OPENAI_API_KEY",
|
|
50
|
-
LLMProvider.GROQ: "GROQ_API_KEY",
|
|
51
|
-
LLMProvider.QWEN: "QWEN_API_KEY",
|
|
52
46
|
}
|
agent/telemetry.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Telemetry support for Agent class."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any, Dict, Optional
|
|
8
|
+
|
|
9
|
+
from core.telemetry import (
|
|
10
|
+
record_event,
|
|
11
|
+
is_telemetry_enabled,
|
|
12
|
+
flush,
|
|
13
|
+
get_telemetry_client,
|
|
14
|
+
increment,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# System information used for telemetry
|
|
18
|
+
SYSTEM_INFO = {
|
|
19
|
+
"os": sys.platform,
|
|
20
|
+
"python_version": platform.python_version(),
|
|
21
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cua-agent
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: CUA (Computer Use) Agent for AI-driven computer interaction
|
|
5
5
|
Author-Email: TryCua <gh@trycua.com>
|
|
6
6
|
Requires-Python: <3.13,>=3.10
|
|
@@ -13,6 +13,7 @@ Requires-Dist: pydantic<3.0.0,>=2.6.4
|
|
|
13
13
|
Requires-Dist: rich<14.0.0,>=13.7.1
|
|
14
14
|
Requires-Dist: python-dotenv<2.0.0,>=1.0.1
|
|
15
15
|
Requires-Dist: cua-computer<0.2.0,>=0.1.0
|
|
16
|
+
Requires-Dist: cua-core<0.2.0,>=0.1.0
|
|
16
17
|
Requires-Dist: certifi>=2024.2.2
|
|
17
18
|
Provides-Extra: anthropic
|
|
18
19
|
Requires-Dist: anthropic>=0.49.0; extra == "anthropic"
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
agent/README.md,sha256=8EFnLrKejthEcL9bZflQSbvA-KwpiPanBz8TEEwRub8,2153
|
|
2
|
-
agent/__init__.py,sha256=
|
|
2
|
+
agent/__init__.py,sha256=iX9e3iSpdUtqZAK9smiqwo3OcipR2v5rlXQyDiXXVxQ,691
|
|
3
3
|
agent/core/README.md,sha256=VOXNVbR0ugxf9gCXYmZtUU2kngZhfi29haT_oSxK0Lk,3559
|
|
4
4
|
agent/core/__init__.py,sha256=0htZ-VfsH9ixHB8j_SXu_uv6r3XXsq5TrghFNd-yRNE,709
|
|
5
|
-
agent/core/agent.py,sha256=
|
|
6
|
-
agent/core/base_agent.py,sha256=
|
|
5
|
+
agent/core/agent.py,sha256=MN_A5gVS_E_1e9UFdRR8iKeR2xMfbbIL-9xK6w7GNwo,9703
|
|
6
|
+
agent/core/base_agent.py,sha256=te9rk2tJZpEhDUEB1xSaFqe1zeOjmzMdHF5LaUDP2K0,6276
|
|
7
7
|
agent/core/callbacks.py,sha256=VbGIf5QkHh3Q0KsLM6wv7hRdIA5WExTVYLm64bckyUA,4306
|
|
8
8
|
agent/core/computer_agent.py,sha256=JGLMl_PwImUttmQh2amdLlXHS9CUyZ9MW20J1Xid7dM,2417
|
|
9
|
-
agent/core/experiment.py,sha256=
|
|
9
|
+
agent/core/experiment.py,sha256=FKmSDyA2YFSrO3q-91ZT29Jm1lm24YCuK59wQ6z-6IM,7930
|
|
10
10
|
agent/core/factory.py,sha256=WraOEHWPXBSN4R3DO7M2ctyadodeA8tzHM3dUjdQ_3A,3441
|
|
11
|
-
agent/core/loop.py,sha256=
|
|
11
|
+
agent/core/loop.py,sha256=vhdlSy_hIY3-a92uTGdF3oYE5Qcq0U2hyTJNmXunnfc,9009
|
|
12
12
|
agent/core/messages.py,sha256=N8pV8Eh-AJpMuDPRI5OGWUIOU6DRr-pQjK9XU0go9Hk,7637
|
|
13
|
+
agent/core/telemetry.py,sha256=bOP3z74dpXwvn1bGCVxe67jwu1m-4nYmKlypAJovqCQ,4304
|
|
13
14
|
agent/core/tools/__init__.py,sha256=xZen-PqUp2dUaMEHJowXCQm33_5Sxhsx9PSoD0rq6tI,489
|
|
14
15
|
agent/core/tools/base.py,sha256=CdzRFNuOjNfzgyTUN4ZoCGkUDR5HI0ECQVpvrUdEij8,2295
|
|
15
16
|
agent/core/tools/bash.py,sha256=jnJKVlHn8np8e0gWd8EO0_qqjMkfQzutSugA_Iol4jE,1585
|
|
@@ -43,23 +44,24 @@ agent/providers/omni/clients/openai.py,sha256=E4TAXMUFoYTunJETCWCNx5XAc6xutiN4rB
|
|
|
43
44
|
agent/providers/omni/clients/utils.py,sha256=Ani9CVVBm_J2Dl51WG6p1GVuoI6cq8scISrG0pmQ37o,688
|
|
44
45
|
agent/providers/omni/experiment.py,sha256=JGAdHi7Nf73I48c9k3TY1Xpr_i6D2VG1wurOzw5cNGk,9888
|
|
45
46
|
agent/providers/omni/image_utils.py,sha256=qIFuNi5cIMVwrqYBXG1T6PxUlbxz7gIngFFP39bZIlU,2782
|
|
46
|
-
agent/providers/omni/loop.py,sha256=
|
|
47
|
+
agent/providers/omni/loop.py,sha256=72o7q92nO7i0EUrVhEPCEHprRKdBYsg5iLTLfLHXAsw,43847
|
|
47
48
|
agent/providers/omni/messages.py,sha256=zdjQCAMH-hOyrQQesHhTiIsQbw43KqVSmVIzS8JOIFA,6134
|
|
48
|
-
agent/providers/omni/parser.py,sha256=
|
|
49
|
+
agent/providers/omni/parser.py,sha256=lTAoSMSf2zpwqR_8W0SXG3cYIFeUiZa5vXdpjqZwEHY,9161
|
|
49
50
|
agent/providers/omni/prompts.py,sha256=Mupjy0bUwBjcAeLXpE1r1jisYPSlhwsp-IXJKEKrEtw,3779
|
|
50
51
|
agent/providers/omni/tool_manager.py,sha256=O6DxyEI-Vg6jt99phh011o4q4me_vNhH2YffIxkO4GM,2585
|
|
51
52
|
agent/providers/omni/tools/__init__.py,sha256=l636hx9Q5z9eaFdPanPwPENUE-w-Xm8kAZhPUq0ZQF4,309
|
|
52
53
|
agent/providers/omni/tools/bash.py,sha256=y_ibfP9iRcbiU_E0faAoa4DCP_BlkMlKOOURdBBIGZE,2030
|
|
53
54
|
agent/providers/omni/tools/computer.py,sha256=xkMmAR0e_kbf0Zs2mggCDyWrQOJZyXOKPFjkutaQb94,9108
|
|
54
55
|
agent/providers/omni/tools/manager.py,sha256=V_tav2yU92PyQnFlxNXG1wvNEaJoEYudtKx5sRjj06Q,2619
|
|
55
|
-
agent/providers/omni/types.py,sha256=
|
|
56
|
+
agent/providers/omni/types.py,sha256=rpr7-mH9VK1R-nJ6tVu1gKp427j-hw1DpHc197b44nU,1017
|
|
56
57
|
agent/providers/omni/utils.py,sha256=JqSye1bEp4wxhUgmaMyZi172fTlgXtygJ7XlnvKdUtE,6337
|
|
57
58
|
agent/providers/omni/visualization.py,sha256=N3qVQLxYmia3iSVC5oCt5YRlMPuVfylCOyB99R33u8U,3924
|
|
59
|
+
agent/telemetry.py,sha256=pVGxbj0ewnvq4EGj28CydN4a1iOfvZR_XKL3vIOqhOM,390
|
|
58
60
|
agent/types/__init__.py,sha256=61UFJT-w0CT4YRn0LiTx4A7fsMdVQjlXO9vnmbI1A7Y,604
|
|
59
61
|
agent/types/base.py,sha256=Iy_Q2DIBMLtwWdLyfvHw_6E2ltYu3bIv8GUNy3LYkGs,1133
|
|
60
62
|
agent/types/messages.py,sha256=4-hwtxeAhto90_EZpHFducddtsHUsHauvXzYrpKG4RE,953
|
|
61
63
|
agent/types/tools.py,sha256=Jes2CFCFqC727WWHbO-sG7V03rBHnQe5X7Oi9ZkuScI,877
|
|
62
|
-
cua_agent-0.1.
|
|
63
|
-
cua_agent-0.1.
|
|
64
|
-
cua_agent-0.1.
|
|
65
|
-
cua_agent-0.1.
|
|
64
|
+
cua_agent-0.1.3.dist-info/METADATA,sha256=jrj3DGxV6UHOSezXqLY2BhjmZxI1Eunv9aFXcoUQmgs,1928
|
|
65
|
+
cua_agent-0.1.3.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
66
|
+
cua_agent-0.1.3.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
67
|
+
cua_agent-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|