cua-agent 0.1.1__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 CHANGED
@@ -2,9 +2,21 @@
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
- from .types.base import Provider, AgenticLoop
8
- from .providers.omni.types import LLMProvider, LLM, Model, LLMModel, APIProvider
19
+ from .providers.omni.types import LLMProvider, LLM
20
+ from .types.base import Provider, AgentLoop
9
21
 
10
- __all__ = ["AgentFactory", "Provider", "ComputerAgent", "AgenticLoop", "LLMProvider", "LLM", "Model", "LLMModel", "APIProvider"]
22
+ __all__ = ["AgentFactory", "Provider", "ComputerAgent", "AgentLoop", "LLMProvider", "LLM"]
agent/core/README.md CHANGED
@@ -34,7 +34,7 @@ Here's how to use the unified ComputerAgent:
34
34
  ```python
35
35
  from agent.core.agent import ComputerAgent
36
36
  from agent.types.base import AgenticLoop
37
- from agent.providers.omni.types import APIProvider
37
+ from agent.providers.omni.types import LLMProvider
38
38
  from computer import Computer
39
39
 
40
40
  # Create a Computer instance
@@ -44,7 +44,7 @@ computer = Computer()
44
44
  agent = ComputerAgent(
45
45
  computer=computer,
46
46
  loop_type=AgenticLoop.OMNI,
47
- provider=APIProvider.OPENAI,
47
+ provider=LLMProvider.OPENAI,
48
48
  model="gpt-4o",
49
49
  api_key="your_api_key_here", # Can also use OPENAI_API_KEY environment variable
50
50
  save_trajectory=True,
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
- from ..types.base import Provider, AgenticLoop
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:
@@ -18,7 +22,7 @@ if TYPE_CHECKING:
18
22
  from ..providers.omni.parser import OmniParser
19
23
 
20
24
  # Import the provider types
21
- from ..providers.omni.types import LLMProvider, LLM, Model, LLMModel, APIProvider
25
+ from ..providers.omni.types import LLMProvider, LLM, Model, LLMModel
22
26
 
23
27
  logger = logging.getLogger(__name__)
24
28
 
@@ -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
 
@@ -47,7 +49,7 @@ class ComputerAgent(BaseComputerAgent):
47
49
  def __init__(
48
50
  self,
49
51
  computer: Computer,
50
- loop_type: AgenticLoop = AgenticLoop.OMNI,
52
+ loop: AgentLoop = AgentLoop.OMNI,
51
53
  model: Optional[Union[LLM, Dict[str, str], str]] = None,
52
54
  api_key: Optional[str] = None,
53
55
  save_trajectory: bool = True,
@@ -55,88 +57,77 @@ 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 the computer agent.
63
+ """Initialize a ComputerAgent instance.
61
64
 
62
65
  Args:
63
- computer: Computer instance to control
64
- loop_type: The type of loop to use (Anthropic or Omni)
65
- model: LLM configuration. Can be:
66
- - LLM object with provider and name
67
- - Dict with 'provider' and 'name' keys
68
- - String with model name (defaults to OpenAI provider)
69
- - None (defaults based on loop_type)
70
- api_key: Optional API key (will use environment variable if not provided)
71
- save_trajectory: Whether to save screenshots and logs
72
- trajectory_dir: Directory to save trajectories (defaults to "trajectories")
73
- only_n_most_recent_images: Limit history to N most recent images
74
- max_retries: Maximum number of retry attempts for failed operations
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
- # Set up trajectory directories based on save_trajectory
79
- base_dir = trajectory_dir if save_trajectory else None
80
- # Don't create a redundant screenshots directory - directly use the timestamp folder
81
- screenshot_dir = None # This was previously set to os.path.join(base_dir, "screenshots")
82
- log_dir = None
83
-
84
- super().__init__(
85
- max_retries=max_retries,
86
- computer=computer,
87
- screenshot_dir=screenshot_dir,
88
- log_dir=log_dir,
89
- **kwargs,
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()
92
+
93
+ # Process the model configuration
94
+ self.model = self._process_model_config(model, loop)
95
+ self.loop_type = loop
96
+ self.api_key = api_key
97
+
98
+ # Store computer
99
+ self.computer = computer
91
100
 
92
- self.loop_type = loop_type
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
- # Configure logging based on verbosity
100
- self._configure_logging(verbosity)
106
+ # Store the max retries setting
107
+ self.max_retries = max_retries
101
108
 
102
- # Process model configuration
103
- self.model_config = self._process_model_config(model, loop_type)
104
-
105
- # Get API key from environment if not provided
106
- if api_key is None:
107
- env_var = (
108
- ENV_VARS.get(self.model_config.provider)
109
- if loop_type == AgenticLoop.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
109
+ # Initialize message history
110
+ self.messages = []
111
+
112
+ # Extra kwargs for the loop
113
+ self.loop_kwargs = kwargs
127
114
 
128
- # Initialize the appropriate loop based on loop_type
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
- self, model_input: Optional[Union[LLM, Dict[str, str], str]], loop_type: AgenticLoop
123
+ self, model_input: Optional[Union[LLM, Dict[str, str], str]], loop: AgentLoop
133
124
  ) -> LLM:
134
125
  """Process and normalize model configuration.
135
-
126
+
136
127
  Args:
137
128
  model_input: Input model configuration (LLM, dict, string, or None)
138
- loop_type: The loop type being used
139
-
129
+ loop: The loop type being used
130
+
140
131
  Returns:
141
132
  Normalized LLM instance
142
133
  """
@@ -144,31 +135,28 @@ class ComputerAgent(BaseComputerAgent):
144
135
  if model_input is None:
145
136
  # Use Anthropic for Anthropic loop, OpenAI for Omni loop
146
137
  default_provider = (
147
- LLMProvider.ANTHROPIC if loop_type == AgenticLoop.ANTHROPIC else LLMProvider.OPENAI
138
+ LLMProvider.ANTHROPIC if loop == AgentLoop.ANTHROPIC else LLMProvider.OPENAI
148
139
  )
149
140
  return LLM(provider=default_provider)
150
-
141
+
151
142
  # Handle case where model_input is already a LLM or one of its aliases
152
143
  if isinstance(model_input, (LLM, Model, LLMModel)):
153
144
  return model_input
154
-
145
+
155
146
  # Handle case where model_input is a dict
156
147
  if isinstance(model_input, dict):
157
148
  provider = model_input.get("provider", LLMProvider.OPENAI)
158
149
  if isinstance(provider, str):
159
150
  provider = LLMProvider(provider)
160
- return LLM(
161
- provider=provider,
162
- name=model_input.get("name")
163
- )
164
-
151
+ return LLM(provider=provider, name=model_input.get("name"))
152
+
165
153
  # Handle case where model_input is a string (model name)
166
154
  if isinstance(model_input, str):
167
155
  default_provider = (
168
- LLMProvider.ANTHROPIC if loop_type == AgenticLoop.ANTHROPIC else LLMProvider.OPENAI
156
+ LLMProvider.ANTHROPIC if loop == AgentLoop.ANTHROPIC else LLMProvider.OPENAI
169
157
  )
170
158
  return LLM(provider=default_provider, name=model_input)
171
-
159
+
172
160
  raise ValueError(f"Unsupported model configuration: {model_input}")
173
161
 
174
162
  def _configure_logging(self, verbosity: int):
@@ -199,12 +187,12 @@ class ComputerAgent(BaseComputerAgent):
199
187
  from ..providers.omni.loop import OmniLoop
200
188
  from ..providers.omni.parser import OmniParser
201
189
 
202
- if self.loop_type == AgenticLoop.ANTHROPIC:
190
+ if self.loop_type == AgentLoop.ANTHROPIC:
203
191
  from ..providers.anthropic.loop import AnthropicLoop
204
192
 
205
193
  # Ensure we always have a valid model name
206
- model_name = self.model_config.name or DEFAULT_MODELS[LLMProvider.ANTHROPIC]
207
-
194
+ model_name = self.model.name or DEFAULT_MODELS[LLMProvider.ANTHROPIC]
195
+
208
196
  return AnthropicLoop(
209
197
  api_key=self.api_key,
210
198
  model=model_name,
@@ -212,119 +200,60 @@ class ComputerAgent(BaseComputerAgent):
212
200
  save_trajectory=self.save_trajectory,
213
201
  base_dir=self.trajectory_dir,
214
202
  only_n_most_recent_images=self.only_n_most_recent_images,
215
- **self._kwargs,
203
+ **self.loop_kwargs,
216
204
  )
217
205
 
218
206
  # Initialize parser for OmniLoop with appropriate device
219
- if "parser" not in self._kwargs:
220
- self._kwargs["parser"] = OmniParser()
207
+ if "parser" not in self.loop_kwargs:
208
+ self.loop_kwargs["parser"] = OmniParser()
221
209
 
222
210
  # Ensure we always have a valid model name
223
- model_name = self.model_config.name or DEFAULT_MODELS[self.model_config.provider]
224
-
211
+ model_name = self.model.name or DEFAULT_MODELS[self.model.provider]
212
+
225
213
  return OmniLoop(
226
- provider=self.model_config.provider,
214
+ provider=self.model.provider,
227
215
  api_key=self.api_key,
228
216
  model=model_name,
229
217
  computer=self.computer,
230
218
  save_trajectory=self.save_trajectory,
231
219
  base_dir=self.trajectory_dir,
232
220
  only_n_most_recent_images=self.only_n_most_recent_images,
233
- **self._kwargs,
221
+ **self.loop_kwargs,
234
222
  )
235
223
 
236
224
  async def _execute_task(self, task: str) -> AsyncGenerator[Dict[str, Any], None]:
237
- """Execute a task using the appropriate loop.
225
+ """Execute a task using the appropriate agent loop.
238
226
 
239
227
  Args:
240
- task: Task description to execute
228
+ task: The task to execute
241
229
 
242
- Yields:
243
- Dict containing response content and metadata
230
+ Returns:
231
+ AsyncGenerator yielding task outputs
244
232
  """
233
+ logger.info(f"Executing task: {task}")
234
+
245
235
  try:
246
- # Format the messages based on loop type
247
- if self.loop_type == AgenticLoop.ANTHROPIC:
248
- # Anthropic format
249
- messages = [{"role": "user", "content": [{"type": "text", "text": task}]}]
250
- else:
251
- # Cua format
252
- messages = [{"role": "user", "content": task}]
253
-
254
- # Run the loop
255
- try:
256
- async for result in self.loop.run(messages):
257
- if result is None:
258
- break
259
-
260
- # Handle error case
261
- if "error" in result:
262
- yield {
263
- "role": "assistant",
264
- "content": result["error"],
265
- "metadata": {"title": "❌ Error"},
266
- }
267
- continue
268
-
269
- # Extract content and metadata based on loop type
270
- if self.loop_type == AgenticLoop.ANTHROPIC:
271
- # Handle Anthropic format
272
- if "content" in result:
273
- content_text = ""
274
- for content_block in result["content"]:
275
- try:
276
- # Try to access the text attribute directly
277
- content_text += content_block.text
278
- except (AttributeError, TypeError):
279
- # If it's a dictionary instead of an object
280
- if isinstance(content_block, dict) and "text" in content_block:
281
- content_text += content_block["text"]
282
-
283
- yield {
284
- "role": "assistant",
285
- "content": content_text,
286
- "metadata": result.get("parsed_screen", {}),
287
- }
288
- else:
289
- yield {
290
- "role": "assistant",
291
- "content": str(result),
292
- "metadata": {"title": "Screen Analysis"},
293
- }
294
- else:
295
- # Handle Omni format
296
- content = ""
297
- metadata = {"title": "Screen Analysis"}
298
-
299
- # If result has content (normal case)
300
- if "content" in result:
301
- content = result["content"]
302
-
303
- # Ensure metadata has a title
304
- if isinstance(content, dict) and "metadata" in content:
305
- metadata = content["metadata"]
306
- if "title" not in metadata:
307
- metadata["title"] = "Screen Analysis"
308
-
309
- # For string content, convert to proper format
310
- if isinstance(content, str):
311
- content = content
312
- elif isinstance(content, dict) and "content" in content:
313
- content = content.get("content", "")
314
-
315
- yield {"role": "assistant", "content": content, "metadata": metadata}
316
- except Exception as e:
317
- logger.error(f"Error running the loop: {str(e)}")
318
- yield {
319
- "role": "assistant",
320
- "content": f"Error running the agent loop: {str(e)}",
321
- "metadata": {"title": "❌ Loop Error"},
322
- }
236
+ # Create a message from the task
237
+ task_message = {"role": "user", "content": task}
238
+ messages_with_task = self.messages + [task_message]
323
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
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
324
255
  except Exception as e:
325
- logger.error(f"Error in _execute_task: {str(e)}")
326
- yield {
327
- "role": "assistant",
328
- "content": f"Error: {str(e)}",
329
- "metadata": {"title": "❌ Error"},
330
- }
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"_{action_type}" if action_type else ""
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
@@ -166,7 +166,7 @@ class BaseLoop(ABC):
166
166
  """
167
167
  try:
168
168
  # Take screenshot
169
- screenshot = await self.computer.screenshot()
169
+ screenshot = await self.computer.interface.screenshot()
170
170
 
171
171
  # Initialize with default values
172
172
  width, height = 1024, 768
@@ -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"])
@@ -1,6 +1,6 @@
1
1
  """Anthropic provider implementation."""
2
2
 
3
3
  from .loop import AnthropicLoop
4
- from .types import APIProvider
4
+ from .types import LLMProvider
5
5
 
6
- __all__ = ["AnthropicLoop", "APIProvider"]
6
+ __all__ = ["AnthropicLoop", "LLMProvider"]
@@ -3,25 +3,28 @@ import httpx
3
3
  import asyncio
4
4
  from anthropic import Anthropic, AnthropicBedrock, AnthropicVertex
5
5
  from anthropic.types.beta import BetaMessage, BetaMessageParam, BetaToolUnionParam
6
- from ..types import APIProvider
6
+ from ..types import LLMProvider
7
7
  from .logging import log_api_interaction
8
8
  import random
9
9
  import logging
10
10
 
11
11
  logger = logging.getLogger(__name__)
12
12
 
13
+
13
14
  class APIConnectionError(Exception):
14
15
  """Error raised when there are connection issues with the API."""
16
+
15
17
  pass
16
18
 
19
+
17
20
  class BaseAnthropicClient:
18
21
  """Base class for Anthropic API clients."""
19
-
22
+
20
23
  MAX_RETRIES = 10
21
24
  INITIAL_RETRY_DELAY = 1.0
22
25
  MAX_RETRY_DELAY = 60.0
23
26
  JITTER_FACTOR = 0.1
24
-
27
+
25
28
  async def create_message(
26
29
  self,
27
30
  *,
@@ -36,79 +39,67 @@ class BaseAnthropicClient:
36
39
 
37
40
  async def _make_api_call_with_retries(self, api_call):
38
41
  """Make an API call with exponential backoff retry logic.
39
-
42
+
40
43
  Args:
41
44
  api_call: Async function that makes the actual API call
42
-
45
+
43
46
  Returns:
44
47
  API response
45
-
48
+
46
49
  Raises:
47
50
  APIConnectionError: If all retries fail
48
51
  """
49
52
  retry_count = 0
50
53
  last_error = None
51
-
54
+
52
55
  while retry_count < self.MAX_RETRIES:
53
56
  try:
54
57
  return await api_call()
55
58
  except Exception as e:
56
59
  last_error = e
57
60
  retry_count += 1
58
-
61
+
59
62
  if retry_count == self.MAX_RETRIES:
60
63
  break
61
-
64
+
62
65
  # Calculate delay with exponential backoff and jitter
63
66
  delay = min(
64
- self.INITIAL_RETRY_DELAY * (2 ** (retry_count - 1)),
65
- self.MAX_RETRY_DELAY
67
+ self.INITIAL_RETRY_DELAY * (2 ** (retry_count - 1)), self.MAX_RETRY_DELAY
66
68
  )
67
69
  # Add jitter to avoid thundering herd
68
70
  jitter = delay * self.JITTER_FACTOR * (2 * random.random() - 1)
69
71
  final_delay = delay + jitter
70
-
72
+
71
73
  logger.info(
72
74
  f"Retrying request (attempt {retry_count}/{self.MAX_RETRIES}) "
73
75
  f"in {final_delay:.2f} seconds after error: {str(e)}"
74
76
  )
75
77
  await asyncio.sleep(final_delay)
76
-
78
+
77
79
  raise APIConnectionError(
78
- f"Failed after {self.MAX_RETRIES} retries. "
79
- f"Last error: {str(last_error)}"
80
+ f"Failed after {self.MAX_RETRIES} retries. " f"Last error: {str(last_error)}"
80
81
  )
81
82
 
83
+
82
84
  class AnthropicDirectClient(BaseAnthropicClient):
83
85
  """Direct Anthropic API client implementation."""
84
-
86
+
85
87
  def __init__(self, api_key: str, model: str):
86
88
  self.model = model
87
- self.client = Anthropic(
88
- api_key=api_key,
89
- http_client=self._create_http_client()
90
- )
91
-
89
+ self.client = Anthropic(api_key=api_key, http_client=self._create_http_client())
90
+
92
91
  def _create_http_client(self) -> httpx.Client:
93
92
  """Create an HTTP client with appropriate settings."""
94
93
  return httpx.Client(
95
94
  verify=True,
96
- timeout=httpx.Timeout(
97
- connect=30.0,
98
- read=300.0,
99
- write=30.0,
100
- pool=30.0
101
- ),
95
+ timeout=httpx.Timeout(connect=30.0, read=300.0, write=30.0, pool=30.0),
102
96
  transport=httpx.HTTPTransport(
103
97
  retries=3,
104
98
  verify=True,
105
- limits=httpx.Limits(
106
- max_keepalive_connections=5,
107
- max_connections=10
108
- )
109
- )
99
+ limits=httpx.Limits(max_keepalive_connections=5, max_connections=10),
100
+ ),
110
101
  )
111
-
102
+
112
103
  async def create_message(
113
104
  self,
114
105
  *,
@@ -119,6 +110,7 @@ class AnthropicDirectClient(BaseAnthropicClient):
119
110
  betas: list[str],
120
111
  ) -> BetaMessage:
121
112
  """Create a message using the direct Anthropic API with retry logic."""
113
+
122
114
  async def api_call():
123
115
  response = self.client.beta.messages.with_raw_response.create(
124
116
  max_tokens=max_tokens,
@@ -130,20 +122,21 @@ class AnthropicDirectClient(BaseAnthropicClient):
130
122
  )
131
123
  log_api_interaction(response.http_response.request, response.http_response, None)
132
124
  return response.parse()
133
-
125
+
134
126
  try:
135
127
  return await self._make_api_call_with_retries(api_call)
136
128
  except Exception as e:
137
129
  log_api_interaction(None, None, e)
138
130
  raise
139
131
 
132
+
140
133
  class AnthropicVertexClient(BaseAnthropicClient):
141
134
  """Google Cloud Vertex AI implementation of Anthropic client."""
142
-
135
+
143
136
  def __init__(self, model: str):
144
137
  self.model = model
145
138
  self.client = AnthropicVertex()
146
-
139
+
147
140
  async def create_message(
148
141
  self,
149
142
  *,
@@ -154,6 +147,7 @@ class AnthropicVertexClient(BaseAnthropicClient):
154
147
  betas: list[str],
155
148
  ) -> BetaMessage:
156
149
  """Create a message using Vertex AI with retry logic."""
150
+
157
151
  async def api_call():
158
152
  response = self.client.beta.messages.with_raw_response.create(
159
153
  max_tokens=max_tokens,
@@ -165,20 +159,21 @@ class AnthropicVertexClient(BaseAnthropicClient):
165
159
  )
166
160
  log_api_interaction(response.http_response.request, response.http_response, None)
167
161
  return response.parse()
168
-
162
+
169
163
  try:
170
164
  return await self._make_api_call_with_retries(api_call)
171
165
  except Exception as e:
172
166
  log_api_interaction(None, None, e)
173
167
  raise
174
168
 
169
+
175
170
  class AnthropicBedrockClient(BaseAnthropicClient):
176
171
  """AWS Bedrock implementation of Anthropic client."""
177
-
172
+
178
173
  def __init__(self, model: str):
179
174
  self.model = model
180
175
  self.client = AnthropicBedrock()
181
-
176
+
182
177
  async def create_message(
183
178
  self,
184
179
  *,
@@ -189,6 +184,7 @@ class AnthropicBedrockClient(BaseAnthropicClient):
189
184
  betas: list[str],
190
185
  ) -> BetaMessage:
191
186
  """Create a message using AWS Bedrock with retry logic."""
187
+
192
188
  async def api_call():
193
189
  response = self.client.beta.messages.with_raw_response.create(
194
190
  max_tokens=max_tokens,
@@ -200,23 +196,24 @@ class AnthropicBedrockClient(BaseAnthropicClient):
200
196
  )
201
197
  log_api_interaction(response.http_response.request, response.http_response, None)
202
198
  return response.parse()
203
-
199
+
204
200
  try:
205
201
  return await self._make_api_call_with_retries(api_call)
206
202
  except Exception as e:
207
203
  log_api_interaction(None, None, e)
208
204
  raise
209
205
 
206
+
210
207
  class AnthropicClientFactory:
211
208
  """Factory for creating appropriate Anthropic client implementations."""
212
-
209
+
213
210
  @staticmethod
214
- def create_client(provider: APIProvider, api_key: str, model: str) -> BaseAnthropicClient:
211
+ def create_client(provider: LLMProvider, api_key: str, model: str) -> BaseAnthropicClient:
215
212
  """Create an appropriate client based on the provider."""
216
- if provider == APIProvider.ANTHROPIC:
213
+ if provider == LLMProvider.ANTHROPIC:
217
214
  return AnthropicDirectClient(api_key, model)
218
- elif provider == APIProvider.VERTEX:
215
+ elif provider == LLMProvider.VERTEX:
219
216
  return AnthropicVertexClient(model)
220
- elif provider == APIProvider.BEDROCK:
217
+ elif provider == LLMProvider.BEDROCK:
221
218
  return AnthropicBedrockClient(model)
222
- raise ValueError(f"Unsupported provider: {provider}")
219
+ raise ValueError(f"Unsupported provider: {provider}")
@@ -32,7 +32,7 @@ from .tools.manager import ToolManager
32
32
  from .messages.manager import MessageManager
33
33
  from .callbacks.manager import CallbackManager
34
34
  from .prompts import SYSTEM_PROMPT
35
- from .types import APIProvider
35
+ from .types import LLMProvider
36
36
  from .tools import ToolResult
37
37
 
38
38
  # Constants
@@ -86,7 +86,7 @@ class AnthropicLoop(BaseLoop):
86
86
  self.model = "claude-3-7-sonnet-20250219"
87
87
 
88
88
  # Anthropic-specific attributes
89
- self.provider = APIProvider.ANTHROPIC
89
+ self.provider = LLMProvider.ANTHROPIC
90
90
  self.client = None
91
91
  self.retry_count = 0
92
92
  self.tool_manager = None
@@ -1,7 +1,7 @@
1
1
  from enum import StrEnum
2
2
 
3
3
 
4
- class APIProvider(StrEnum):
4
+ class LLMProvider(StrEnum):
5
5
  """Enum for supported API providers."""
6
6
 
7
7
  ANTHROPIC = "anthropic"
@@ -9,8 +9,8 @@ class APIProvider(StrEnum):
9
9
  VERTEX = "vertex"
10
10
 
11
11
 
12
- PROVIDER_TO_DEFAULT_MODEL_NAME: dict[APIProvider, str] = {
13
- APIProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
14
- APIProvider.BEDROCK: "anthropic.claude-3-7-sonnet-20250219-v2:0",
15
- APIProvider.VERTEX: "claude-3-5-sonnet-v2@20241022",
12
+ PROVIDER_TO_DEFAULT_MODEL_NAME: dict[LLMProvider, str] = {
13
+ LLMProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
14
+ LLMProvider.BEDROCK: "anthropic.claude-3-7-sonnet-20250219-v2:0",
15
+ LLMProvider.VERTEX: "claude-3-5-sonnet-v2@20241022",
16
16
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  # The OmniComputerAgent has been replaced by the unified ComputerAgent
4
4
  # which can be found in agent.core.agent
5
- from .types import APIProvider
5
+ from .types import LLMProvider
6
6
  from .experiment import ExperimentManager
7
7
  from .visualization import visualize_click, visualize_scroll, calculate_element_center
8
8
  from .image_utils import (
@@ -14,7 +14,7 @@ from .image_utils import (
14
14
  )
15
15
 
16
16
  __all__ = [
17
- "APIProvider",
17
+ "LLMProvider",
18
18
  "ExperimentManager",
19
19
  "visualize_click",
20
20
  "visualize_scroll",
@@ -17,7 +17,7 @@ import copy
17
17
  from .parser import OmniParser, ParseResult, ParserMetadata, UIElement
18
18
  from ...core.loop import BaseLoop
19
19
  from computer import Computer
20
- from .types import APIProvider
20
+ from .types import LLMProvider
21
21
  from .clients.base import BaseOmniClient
22
22
  from .clients.openai import OpenAIClient
23
23
  from .clients.groq import GroqClient
@@ -46,7 +46,7 @@ class OmniLoop(BaseLoop):
46
46
  def __init__(
47
47
  self,
48
48
  parser: OmniParser,
49
- provider: APIProvider,
49
+ provider: LLMProvider,
50
50
  api_key: str,
51
51
  model: str,
52
52
  computer: Computer,
@@ -180,11 +180,11 @@ class OmniLoop(BaseLoop):
180
180
  try:
181
181
  logger.info(f"Initializing {self.provider} client with model {self.model}...")
182
182
 
183
- if self.provider == APIProvider.OPENAI:
183
+ if self.provider == LLMProvider.OPENAI:
184
184
  self.client = OpenAIClient(api_key=self.api_key, model=self.model)
185
- elif self.provider == APIProvider.GROQ:
185
+ elif self.provider == LLMProvider.GROQ:
186
186
  self.client = GroqClient(api_key=self.api_key, model=self.model)
187
- elif self.provider == APIProvider.ANTHROPIC:
187
+ elif self.provider == LLMProvider.ANTHROPIC:
188
188
  self.client = AnthropicClient(
189
189
  api_key=self.api_key,
190
190
  model=self.model,
@@ -228,7 +228,7 @@ class OmniLoop(BaseLoop):
228
228
  prepared_messages = self.message_manager.get_formatted_messages(provider_name)
229
229
 
230
230
  # Filter out system messages for Anthropic
231
- if self.provider == APIProvider.ANTHROPIC:
231
+ if self.provider == LLMProvider.ANTHROPIC:
232
232
  filtered_messages = [
233
233
  msg for msg in prepared_messages if msg["role"] != "system"
234
234
  ]
@@ -238,7 +238,7 @@ class OmniLoop(BaseLoop):
238
238
  # Log request
239
239
  request_data = {"messages": filtered_messages, "max_tokens": self.max_tokens}
240
240
 
241
- if self.provider == APIProvider.ANTHROPIC:
241
+ if self.provider == LLMProvider.ANTHROPIC:
242
242
  request_data["system"] = self._get_system_prompt()
243
243
  else:
244
244
  request_data["system"] = system_prompt
@@ -255,7 +255,7 @@ class OmniLoop(BaseLoop):
255
255
 
256
256
  if is_async:
257
257
  # For async implementations (AnthropicClient)
258
- if self.provider == APIProvider.ANTHROPIC:
258
+ if self.provider == LLMProvider.ANTHROPIC:
259
259
  response = await run_method(
260
260
  messages=filtered_messages,
261
261
  system=self._get_system_prompt(),
@@ -269,7 +269,7 @@ class OmniLoop(BaseLoop):
269
269
  )
270
270
  else:
271
271
  # For non-async implementations (GroqClient, etc.)
272
- if self.provider == APIProvider.ANTHROPIC:
272
+ if self.provider == LLMProvider.ANTHROPIC:
273
273
  response = run_method(
274
274
  messages=filtered_messages,
275
275
  system=self._get_system_prompt(),
@@ -339,7 +339,7 @@ class OmniLoop(BaseLoop):
339
339
  action_screenshot_saved = False
340
340
  try:
341
341
  # Handle Anthropic response format
342
- if self.provider == APIProvider.ANTHROPIC:
342
+ if self.provider == LLMProvider.ANTHROPIC:
343
343
  if hasattr(response, "content") and isinstance(response.content, list):
344
344
  # Extract text from content blocks
345
345
  for block in response.content:
@@ -563,7 +563,7 @@ class OmniLoop(BaseLoop):
563
563
  """Process and add screen info to messages."""
564
564
  try:
565
565
  # Only add message if we have an image and provider supports it
566
- if self.provider in [APIProvider.OPENAI, APIProvider.ANTHROPIC]:
566
+ if self.provider in [LLMProvider.OPENAI, LLMProvider.ANTHROPIC]:
567
567
  image = parsed_screen.annotated_image_base64 or None
568
568
  if image:
569
569
  # Save screen info to current turn directory
@@ -577,7 +577,7 @@ class OmniLoop(BaseLoop):
577
577
  logger.info(f"Saved elements to {elements_path}")
578
578
 
579
579
  # Format the image content based on the provider
580
- if self.provider == APIProvider.ANTHROPIC:
580
+ if self.provider == LLMProvider.ANTHROPIC:
581
581
  # Compress the image before sending to Anthropic (5MB limit)
582
582
  image_size = len(image)
583
583
  logger.info(f"Image base64 is present, length: {image_size}")
@@ -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}")
@@ -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)}")
@@ -62,17 +62,3 @@ IMPORTANT NOTES:
62
62
  9. Reflect whether the element is clickable or not, for example reflect if it is an hyperlink or a button or a normal text.
63
63
  10. If you are prompted with login information page or captcha page, or you think it need user's permission to do the next action, you should say "Action": "None" in the json field.
64
64
  """
65
-
66
- # SYSTEM_PROMPT1 = """You are an AI assistant helping users interact with their computer.
67
- # Analyze the screen information and respond with JSON containing:
68
- # {
69
- # "Box ID": "Numeric ID of the relevant UI element",
70
- # "Action": "One of: left_click, right_click, double_click, move_cursor, drag_to, type_text, press_key, hotkey, scroll_down, scroll_up, wait",
71
- # "Value": "Text to type, key to press",
72
- # "Explanation": "Why this action was chosen"
73
- # }
74
-
75
- # Notes:
76
- # - For starting applications, use the "hotkey" action with command+space for starting a Spotlight search.
77
- # - Each UI element is highlighted with a colored bounding box, and its Box ID appears nearby in the same color for easy identification.
78
- # """
@@ -10,21 +10,18 @@ class LLMProvider(StrEnum):
10
10
 
11
11
  ANTHROPIC = "anthropic"
12
12
  OPENAI = "openai"
13
- GROQ = "groq"
14
- QWEN = "qwen"
15
13
 
16
14
 
17
- # For backward compatibility
18
- APIProvider = LLMProvider
15
+ LLMProvider
19
16
 
20
17
 
21
18
  @dataclass
22
19
  class LLM:
23
20
  """Configuration for LLM model and provider."""
24
-
21
+
25
22
  provider: LLMProvider
26
23
  name: Optional[str] = None
27
-
24
+
28
25
  def __post_init__(self):
29
26
  """Set default model name if not provided."""
30
27
  if self.name is None:
@@ -40,14 +37,10 @@ Model = LLM
40
37
  PROVIDER_TO_DEFAULT_MODEL: Dict[LLMProvider, str] = {
41
38
  LLMProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
42
39
  LLMProvider.OPENAI: "gpt-4o",
43
- LLMProvider.GROQ: "deepseek-r1-distill-llama-70b",
44
- LLMProvider.QWEN: "qwen2.5-vl-72b-instruct",
45
40
  }
46
41
 
47
42
  # Environment variable names for each provider
48
43
  PROVIDER_TO_ENV_VAR: Dict[LLMProvider, str] = {
49
44
  LLMProvider.ANTHROPIC: "ANTHROPIC_API_KEY",
50
45
  LLMProvider.OPENAI: "OPENAI_API_KEY",
51
- LLMProvider.GROQ: "GROQ_API_KEY",
52
- LLMProvider.QWEN: "QWEN_API_KEY",
53
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
+ }
agent/types/base.py CHANGED
@@ -44,9 +44,10 @@ class Annotation(BaseModel):
44
44
  vm_url: str
45
45
 
46
46
 
47
- class AgenticLoop(Enum):
47
+ class AgentLoop(Enum):
48
48
  """Enumeration of available loop types."""
49
49
 
50
50
  ANTHROPIC = auto() # Anthropic implementation
51
+ OPENAI = auto() # OpenAI implementation
51
52
  OMNI = auto() # OmniLoop implementation
52
53
  # Add more loop types as needed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cua-agent
3
- Version: 0.1.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=5IxjivBoXkpBQyPP3uwCrCoMx7gNZbM1rdVaIn2jxZ4,425
3
- agent/core/README.md,sha256=RY4kKEjm_-_Ul2xgY7ntzsXdPe0Tg1wvtOSZ4xp4DN0,3559
2
+ agent/__init__.py,sha256=iX9e3iSpdUtqZAK9smiqwo3OcipR2v5rlXQyDiXXVxQ,691
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=HSVNTiEhlDHMGJiA4CKi33TtwEdsMIvkgZ9nHfk2M8E,13730
6
- agent/core/base_agent.py,sha256=MgaMKTwgqNJ1-TgS_mxALoC9COzc7Acg9y7Q8HAFX2c,6266
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=AST1t83eqaGzjoW6KvrhfVIs3ELAR_I70VHq2NsMmNk,7446
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=E-0pz7MaguZQrHs5GP98Oc8C_Iz8ier0vXrD9Ny2HL8,8999
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
@@ -18,11 +19,11 @@ agent/core/tools/computer.py,sha256=lT_aW3huoYpcM8kffuokELupSz_WZG_qkaW1gITRC58,
18
19
  agent/core/tools/edit.py,sha256=kv4jTKCM0VXrnoNErf7mT-xlr81-7T8v49_VA9y_L4Y,2005
19
20
  agent/core/tools/manager.py,sha256=IRsCXjGc076nncQuyIjODoafnHTDhrf9sP5B4q5Pcdo,1742
20
21
  agent/providers/__init__.py,sha256=b4tIBAaIB1V7p8V0BWipHVnMhfHH_OuVgP4OWGSHdD8,194
21
- agent/providers/anthropic/__init__.py,sha256=vEqLDkYXZoXg9A64bOtWfv9hoJlJCXbTpQGcmQ9eec8,149
22
- agent/providers/anthropic/api/client.py,sha256=_DeCn6bYgVG0LcQYDO6VCjTPrt6U-PO5vr4GWmhCPH8,7404
22
+ agent/providers/anthropic/__init__.py,sha256=Mj11IZnVshZ2iHkvg4Z5-jrQIaD1WvzDz2Zk_pMwqIA,149
23
+ agent/providers/anthropic/api/client.py,sha256=Y_g4Xg8Ko4tCqjipVm0GBMw-86vw0KQVXS5aWzJinzw,7038
23
24
  agent/providers/anthropic/api/logging.py,sha256=vHpwkIyOZdkSTVIH4ycbBPd4a_rzhP7Osu1I-Ayouwc,5154
24
25
  agent/providers/anthropic/callbacks/manager.py,sha256=dRKN7MuBze2dLal0iHDxCKYqMdh_KShSphuwn7zC-c4,1878
25
- agent/providers/anthropic/loop.py,sha256=GfUU_0erZgaM8oENSbrKEepsYsYTfuOiygcjHK0pefY,17904
26
+ agent/providers/anthropic/loop.py,sha256=-g-OUpdVPSTO5kFJSZ5AmnjoWSEs2niHZFSR6B_KKvU,17904
26
27
  agent/providers/anthropic/messages/manager.py,sha256=atD41v6bjC1STxRB-jLBty9wHlMwacH9cwsL4tBz3uo,4891
27
28
  agent/providers/anthropic/prompts.py,sha256=nHFfgPrfvnWrEdVP7EUBGUHAI85D2X9HeZirk9EwncU,1941
28
29
  agent/providers/anthropic/tools/__init__.py,sha256=JyZwuVtPUnZwRSZBSCdQv9yxbLCsygm3l8Ywjjt9qTQ,661
@@ -33,8 +34,8 @@ agent/providers/anthropic/tools/computer.py,sha256=WnQS2rIIDz1juwoQMun2ODJjOV134
33
34
  agent/providers/anthropic/tools/edit.py,sha256=EGRP61MDA4Oue1D7Q-_vLpd6LdGbdBA1Z4HSZ66DbmI,13465
34
35
  agent/providers/anthropic/tools/manager.py,sha256=zW-biqO_MV3fb1nDEOl3EmCXD1leoglFj6LDRSM3djs,1982
35
36
  agent/providers/anthropic/tools/run.py,sha256=xhXdnBK1di9muaO44CEirL9hpGy3NmKbjfMpyeVmn8Y,1595
36
- agent/providers/anthropic/types.py,sha256=kKc4XvSuKfumv4KLpJOwyY4t5deBsLgZTSAP4raZGvg,421
37
- agent/providers/omni/__init__.py,sha256=wKOVVWHkD-p4QUz0TIEENkMb7Iq2LRSh88KUGBW1XQA,744
37
+ agent/providers/anthropic/types.py,sha256=SF00kOMC1ui8j9Ah56KaeiR2cL394qCHjFIsBpXxt5w,421
38
+ agent/providers/omni/__init__.py,sha256=eTUh4Pmh4zO-RLnP-wAFm8EkJBMImT-G2xnVIYWRti0,744
38
39
  agent/providers/omni/callbacks.py,sha256=ZG9NCgsHWt6y5jKsfcGLaoLxTpmKnIhCArDdeP4q9sA,2369
39
40
  agent/providers/omni/clients/anthropic.py,sha256=X_QRVxqwA_ExdUqgBEwo1aHOfZQxVIBDmDugNHF97OM,3554
40
41
  agent/providers/omni/clients/base.py,sha256=zAAgPi0jl3SWPC730R9l79E8bfYPSo39UtCSE-mrK6I,1076
@@ -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=U1R_ayfN4T25hvbLMp97qeqSrqVtSL-U03G8Sqf4AaM,43827
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=Iv-cXWG2qzdYjyZJH5pGUzfv6nOaiHQ2OXdQSe00Ydw,9151
49
- agent/providers/omni/prompts.py,sha256=29qy8ppbLOjLil3aiqryjaiBf8CQx-xXHN44O-85Q00,4503
49
+ agent/providers/omni/parser.py,sha256=lTAoSMSf2zpwqR_8W0SXG3cYIFeUiZa5vXdpjqZwEHY,9161
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=6x-n3MLvvKOFAdvzYDf6Zzw-i118kvWHXE37qxa_L4o,1284
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
- agent/types/base.py,sha256=rVb4mPWp1SOHfrzOCDqx0pfCV5bgIsdrIzgM_kX_xVs,1090
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.1.dist-info/METADATA,sha256=JdbHHQ7uBAlcnLZpZ1eWCiPCDODEOaB50j6XwtIs0Ss,1890
63
- cua_agent-0.1.1.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
64
- cua_agent-0.1.1.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
65
- cua_agent-0.1.1.dist-info/RECORD,,
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,,