cua-agent 0.4.14__py3-none-any.whl → 0.4.16__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
@@ -28,13 +28,9 @@ try:
28
28
  # Import from core telemetry for basic functions
29
29
  from core.telemetry import (
30
30
  is_telemetry_enabled,
31
- flush,
32
31
  record_event,
33
32
  )
34
33
 
35
- # Import set_dimension from our own telemetry module
36
- from .telemetry import set_dimension
37
-
38
34
  # Check if telemetry is enabled
39
35
  if is_telemetry_enabled():
40
36
  logger.info("Telemetry is enabled")
@@ -49,11 +45,6 @@ try:
49
45
  },
50
46
  )
51
47
 
52
- # Set the package version as a dimension
53
- set_dimension("agent_version", __version__)
54
-
55
- # Flush events to ensure they're sent
56
- flush()
57
48
  else:
58
49
  logger.info("Telemetry is disabled")
59
50
  except ImportError as e:
agent/agent.py CHANGED
@@ -7,7 +7,13 @@ from typing import Dict, List, Any, Optional, AsyncGenerator, Union, cast, Calla
7
7
 
8
8
  from litellm.responses.utils import Usage
9
9
 
10
- from .types import Messages, AgentCapability
10
+ from .types import (
11
+ Messages,
12
+ AgentCapability,
13
+ ToolError,
14
+ IllegalArgumentError
15
+ )
16
+ from .responses import make_tool_error_item, replace_failed_computer_calls_with_function_calls
11
17
  from .decorators import find_agent_config
12
18
  import json
13
19
  import litellm
@@ -30,6 +36,15 @@ from .computers import (
30
36
  make_computer_handler
31
37
  )
32
38
 
39
+ def assert_callable_with(f, *args, **kwargs):
40
+ """Check if function can be called with given arguments."""
41
+ try:
42
+ inspect.signature(f).bind(*args, **kwargs)
43
+ return True
44
+ except TypeError as e:
45
+ sig = inspect.signature(f)
46
+ raise IllegalArgumentError(f"Expected {sig}, got args={args} kwargs={kwargs}") from e
47
+
33
48
  def get_json(obj: Any, max_depth: int = 10) -> Any:
34
49
  def custom_serializer(o: Any, depth: int = 0, seen: Optional[Set[int]] = None) -> Any:
35
50
  if seen is None:
@@ -405,7 +420,8 @@ class ComputerAgent:
405
420
 
406
421
  async def _handle_item(self, item: Any, computer: Optional[AsyncComputerHandler] = None, ignore_call_ids: Optional[List[str]] = None) -> List[Dict[str, Any]]:
407
422
  """Handle each item; may cause a computer action + screenshot."""
408
- if ignore_call_ids and item.get("call_id") and item.get("call_id") in ignore_call_ids:
423
+ call_id = item.get("call_id")
424
+ if ignore_call_ids and call_id and call_id in ignore_call_ids:
409
425
  return []
410
426
 
411
427
  item_type = item.get("type", None)
@@ -419,96 +435,103 @@ class ComputerAgent:
419
435
  # print(content_item.get("text"))
420
436
  return []
421
437
 
422
- if item_type == "computer_call":
423
- await self._on_computer_call_start(item)
424
- if not computer:
425
- raise ValueError("Computer handler is required for computer calls")
426
-
427
- # Perform computer actions
428
- action = item.get("action")
429
- action_type = action.get("type")
430
- if action_type is None:
431
- print(f"Action type cannot be `None`: action={action}, action_type={action_type}")
432
- return []
433
-
434
- # Extract action arguments (all fields except 'type')
435
- action_args = {k: v for k, v in action.items() if k != "type"}
436
-
437
- # print(f"{action_type}({action_args})")
438
-
439
- # Execute the computer action
440
- computer_method = getattr(computer, action_type, None)
441
- if computer_method:
442
- await computer_method(**action_args)
443
- else:
444
- print(f"Unknown computer action: {action_type}")
445
- return []
446
-
447
- # Take screenshot after action
448
- if self.screenshot_delay and self.screenshot_delay > 0:
449
- await asyncio.sleep(self.screenshot_delay)
450
- screenshot_base64 = await computer.screenshot()
451
- await self._on_screenshot(screenshot_base64, "screenshot_after")
438
+ try:
439
+ if item_type == "computer_call":
440
+ await self._on_computer_call_start(item)
441
+ if not computer:
442
+ raise ValueError("Computer handler is required for computer calls")
443
+
444
+ # Perform computer actions
445
+ action = item.get("action")
446
+ action_type = action.get("type")
447
+ if action_type is None:
448
+ print(f"Action type cannot be `None`: action={action}, action_type={action_type}")
449
+ return []
450
+
451
+ # Extract action arguments (all fields except 'type')
452
+ action_args = {k: v for k, v in action.items() if k != "type"}
453
+
454
+ # print(f"{action_type}({action_args})")
455
+
456
+ # Execute the computer action
457
+ computer_method = getattr(computer, action_type, None)
458
+ if computer_method:
459
+ assert_callable_with(computer_method, **action_args)
460
+ await computer_method(**action_args)
461
+ else:
462
+ print(f"Unknown computer action: {action_type}")
463
+ return []
464
+
465
+ # Take screenshot after action
466
+ if self.screenshot_delay and self.screenshot_delay > 0:
467
+ await asyncio.sleep(self.screenshot_delay)
468
+ screenshot_base64 = await computer.screenshot()
469
+ await self._on_screenshot(screenshot_base64, "screenshot_after")
470
+
471
+ # Handle safety checks
472
+ pending_checks = item.get("pending_safety_checks", [])
473
+ acknowledged_checks = []
474
+ for check in pending_checks:
475
+ check_message = check.get("message", str(check))
476
+ acknowledged_checks.append(check)
477
+ # TODO: implement a callback for safety checks
478
+ # if acknowledge_safety_check_callback(check_message, allow_always=True):
479
+ # acknowledged_checks.append(check)
480
+ # else:
481
+ # raise ValueError(f"Safety check failed: {check_message}")
482
+
483
+ # Create call output
484
+ call_output = {
485
+ "type": "computer_call_output",
486
+ "call_id": item.get("call_id"),
487
+ "acknowledged_safety_checks": acknowledged_checks,
488
+ "output": {
489
+ "type": "input_image",
490
+ "image_url": f"data:image/png;base64,{screenshot_base64}",
491
+ },
492
+ }
493
+
494
+ # # Additional URL safety checks for browser environments
495
+ # if await computer.get_environment() == "browser":
496
+ # current_url = await computer.get_current_url()
497
+ # call_output["output"]["current_url"] = current_url
498
+ # # TODO: implement a callback for URL safety checks
499
+ # # check_blocklisted_url(current_url)
500
+
501
+ result = [call_output]
502
+ await self._on_computer_call_end(item, result)
503
+ return result
452
504
 
453
- # Handle safety checks
454
- pending_checks = item.get("pending_safety_checks", [])
455
- acknowledged_checks = []
456
- for check in pending_checks:
457
- check_message = check.get("message", str(check))
458
- acknowledged_checks.append(check)
459
- # TODO: implement a callback for safety checks
460
- # if acknowledge_safety_check_callback(check_message, allow_always=True):
461
- # acknowledged_checks.append(check)
462
- # else:
463
- # raise ValueError(f"Safety check failed: {check_message}")
505
+ if item_type == "function_call":
506
+ await self._on_function_call_start(item)
507
+ # Perform function call
508
+ function = self._get_tool(item.get("name"))
509
+ if not function:
510
+ raise ValueError(f"Function {item.get("name")} not found")
464
511
 
465
- # Create call output
466
- call_output = {
467
- "type": "computer_call_output",
468
- "call_id": item.get("call_id"),
469
- "acknowledged_safety_checks": acknowledged_checks,
470
- "output": {
471
- "type": "input_image",
472
- "image_url": f"data:image/png;base64,{screenshot_base64}",
473
- },
474
- }
512
+ args = json.loads(item.get("arguments"))
513
+
514
+ # Validate arguments before execution
515
+ assert_callable_with(function, **args)
516
+
517
+ # Execute function - use asyncio.to_thread for non-async functions
518
+ if inspect.iscoroutinefunction(function):
519
+ result = await function(**args)
520
+ else:
521
+ result = await asyncio.to_thread(function, **args)
475
522
 
476
- # # Additional URL safety checks for browser environments
477
- # if await computer.get_environment() == "browser":
478
- # current_url = await computer.get_current_url()
479
- # call_output["output"]["current_url"] = current_url
480
- # # TODO: implement a callback for URL safety checks
481
- # # check_blocklisted_url(current_url)
523
+ # Create function call output
524
+ call_output = {
525
+ "type": "function_call_output",
526
+ "call_id": item.get("call_id"),
527
+ "output": str(result),
528
+ }
482
529
 
483
- result = [call_output]
484
- await self._on_computer_call_end(item, result)
485
- return result
486
-
487
- if item_type == "function_call":
488
- await self._on_function_call_start(item)
489
- # Perform function call
490
- function = self._get_tool(item.get("name"))
491
- if not function:
492
- raise ValueError(f"Function {item.get("name")} not found")
493
-
494
- args = json.loads(item.get("arguments"))
495
-
496
- # Execute function - use asyncio.to_thread for non-async functions
497
- if inspect.iscoroutinefunction(function):
498
- result = await function(**args)
499
- else:
500
- result = await asyncio.to_thread(function, **args)
501
-
502
- # Create function call output
503
- call_output = {
504
- "type": "function_call_output",
505
- "call_id": item.get("call_id"),
506
- "output": str(result),
507
- }
508
-
509
- result = [call_output]
510
- await self._on_function_call_end(item, result)
511
- return result
530
+ result = [call_output]
531
+ await self._on_function_call_end(item, result)
532
+ return result
533
+ except ToolError as e:
534
+ return [make_tool_error_item(repr(e), call_id)]
512
535
 
513
536
  return []
514
537
 
@@ -569,6 +592,7 @@ class ComputerAgent:
569
592
  # - PII anonymization
570
593
  # - Image retention policy
571
594
  combined_messages = old_items + new_items
595
+ combined_messages = replace_failed_computer_calls_with_function_calls(combined_messages)
572
596
  preprocessed_messages = await self._on_llm_start(combined_messages)
573
597
 
574
598
  loop_kwargs = {
@@ -7,13 +7,18 @@ import uuid
7
7
  from typing import List, Dict, Any, Optional, Union
8
8
 
9
9
  from .base import AsyncCallbackHandler
10
- from ..telemetry import (
10
+ from core.telemetry import (
11
11
  record_event,
12
12
  is_telemetry_enabled,
13
- set_dimension,
14
- SYSTEM_INFO,
15
13
  )
16
14
 
15
+ import platform
16
+
17
+ SYSTEM_INFO = {
18
+ "os": platform.system().lower(),
19
+ "os_version": platform.release(),
20
+ "python_version": platform.python_version(),
21
+ }
17
22
 
18
23
  class TelemetryCallback(AsyncCallbackHandler):
19
24
  """
@@ -65,11 +70,6 @@ class TelemetryCallback(AsyncCallbackHandler):
65
70
  **SYSTEM_INFO
66
71
  }
67
72
 
68
- # Set session-level dimensions
69
- set_dimension("session_id", self.session_id)
70
- set_dimension("agent_type", agent_info["agent_type"])
71
- set_dimension("model", agent_info["model"])
72
-
73
73
  record_event("agent_session_start", agent_info)
74
74
 
75
75
  async def on_run_start(self, kwargs: Dict[str, Any], old_items: List[Dict[str, Any]]) -> None:
@@ -98,7 +98,6 @@ class TelemetryCallback(AsyncCallbackHandler):
98
98
  if trajectory:
99
99
  run_data["uploaded_trajectory"] = trajectory
100
100
 
101
- set_dimension("run_id", self.run_id)
102
101
  record_event("agent_run_start", run_data)
103
102
 
104
103
  async def on_run_end(self, kwargs: Dict[str, Any], old_items: List[Dict[str, Any]], new_items: List[Dict[str, Any]]) -> None:
agent/responses.py CHANGED
@@ -252,6 +252,53 @@ def make_failed_tool_call_items(tool_name: str, tool_kwargs: Dict[str, Any], err
252
252
  }
253
253
  ]
254
254
 
255
+ def make_tool_error_item(error_message: str, call_id: Optional[str] = None) -> Dict[str, Any]:
256
+ call_id = call_id if call_id else random_id()
257
+ return {
258
+ "type": "function_call_output",
259
+ "call_id": call_id,
260
+ "output": json.dumps({"error": error_message}),
261
+ }
262
+
263
+ def replace_failed_computer_calls_with_function_calls(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
264
+ """
265
+ Replace computer_call items with function_call items if they share a call_id with a function_call_output.
266
+ This indicates the computer call failed and should be treated as a function call instead.
267
+ We do this because the computer_call_output items do not support text output.
268
+
269
+ Args:
270
+ messages: List of message items to process
271
+ """
272
+ messages = messages.copy()
273
+
274
+ # Find all call_ids that have function_call_output items
275
+ failed_call_ids = set()
276
+ for msg in messages:
277
+ if msg.get("type") == "function_call_output":
278
+ call_id = msg.get("call_id")
279
+ if call_id:
280
+ failed_call_ids.add(call_id)
281
+
282
+ # Replace computer_call items that have matching call_ids
283
+ for i, msg in enumerate(messages):
284
+ if (msg.get("type") == "computer_call" and
285
+ msg.get("call_id") in failed_call_ids):
286
+
287
+ # Extract action from computer_call
288
+ action = msg.get("action", {})
289
+ call_id = msg.get("call_id")
290
+
291
+ # Create function_call replacement
292
+ messages[i] = {
293
+ "type": "function_call",
294
+ "id": msg.get("id", random_id()),
295
+ "call_id": call_id,
296
+ "name": "computer",
297
+ "arguments": json.dumps(action),
298
+ }
299
+
300
+ return messages
301
+
255
302
  # Conversion functions between element descriptions and coordinates
256
303
  def convert_computer_calls_desc2xy(responses_items: List[Dict[str, Any]], desc2xy: Dict[str, tuple]) -> List[Dict[str, Any]]:
257
304
  """
agent/types.py CHANGED
@@ -16,6 +16,15 @@ Tools = Optional[Iterable[ToolParam]]
16
16
  AgentResponse = ResponsesAPIResponse
17
17
  AgentCapability = Literal["step", "click"]
18
18
 
19
+ # Exception types
20
+ class ToolError(RuntimeError):
21
+ """Base exception for tool-related errors"""
22
+ pass
23
+
24
+ class IllegalArgumentError(ToolError):
25
+ """Exception raised when function arguments are invalid"""
26
+ pass
27
+
19
28
 
20
29
  # Agent config registration
21
30
  class AgentConfigInfo(BaseModel):
agent/ui/gradio/app.py CHANGED
@@ -114,14 +114,14 @@ MODEL_MAPPINGS = {
114
114
  "Anthropic: Claude 4 Opus (20250514)": "anthropic/claude-opus-4-20250514",
115
115
  "Anthropic: Claude 4 Sonnet (20250514)": "anthropic/claude-sonnet-4-20250514",
116
116
  "Anthropic: Claude 3.7 Sonnet (20250219)": "anthropic/claude-3-7-sonnet-20250219",
117
- "Anthropic: Claude 3.5 Sonnet (20240620)": "anthropic/claude-3-5-sonnet-20240620",
117
+ "Anthropic: Claude 3.5 Sonnet (20241022)": "anthropic/claude-3-5-sonnet-20241022",
118
118
  },
119
119
  "omni": {
120
120
  "default": "omniparser+openai/gpt-4o",
121
121
  "OMNI: OpenAI GPT-4o": "omniparser+openai/gpt-4o",
122
122
  "OMNI: OpenAI GPT-4o mini": "omniparser+openai/gpt-4o-mini",
123
123
  "OMNI: Claude 3.7 Sonnet (20250219)": "omniparser+anthropic/claude-3-7-sonnet-20250219",
124
- "OMNI: Claude 3.5 Sonnet (20240620)": "omniparser+anthropic/claude-3-5-sonnet-20240620",
124
+ "OMNI: Claude 3.5 Sonnet (20241022)": "omniparser+anthropic/claude-3-5-sonnet-20241022",
125
125
  },
126
126
  "uitars": {
127
127
  "default": "huggingface-local/ByteDance-Seed/UI-TARS-1.5-7B" if is_mac else "ui-tars",
@@ -38,13 +38,13 @@ def create_gradio_ui() -> gr.Blocks:
38
38
  "Anthropic: Claude 4 Opus (20250514)",
39
39
  "Anthropic: Claude 4 Sonnet (20250514)",
40
40
  "Anthropic: Claude 3.7 Sonnet (20250219)",
41
- "Anthropic: Claude 3.5 Sonnet (20240620)",
41
+ "Anthropic: Claude 3.5 Sonnet (20241022)",
42
42
  ]
43
43
  omni_models = [
44
44
  "OMNI: OpenAI GPT-4o",
45
45
  "OMNI: OpenAI GPT-4o mini",
46
46
  "OMNI: Claude 3.7 Sonnet (20250219)",
47
- "OMNI: Claude 3.5 Sonnet (20240620)"
47
+ "OMNI: Claude 3.5 Sonnet (20241022)"
48
48
  ]
49
49
 
50
50
  # Check if API keys are available
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cua-agent
3
- Version: 0.4.14
3
+ Version: 0.4.16
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.12
@@ -56,8 +56,8 @@ Description-Content-Type: text/markdown
56
56
  <h1>
57
57
  <div class="image-wrapper" style="display: inline-block;">
58
58
  <picture>
59
- <source media="(prefers-color-scheme: dark)" alt="logo" height="150" srcset="../../../img/logo_white.png" style="display: block; margin: auto;">
60
- <source media="(prefers-color-scheme: light)" alt="logo" height="150" srcset="../../../img/logo_black.png" style="display: block; margin: auto;">
59
+ <source media="(prefers-color-scheme: dark)" alt="logo" height="150" srcset="https://raw.githubusercontent.com/trycua/cua/main/img/logo_white.png" style="display: block; margin: auto;">
60
+ <source media="(prefers-color-scheme: light)" alt="logo" height="150" srcset="https://raw.githubusercontent.com/trycua/cua/main/img/logo_black.png" style="display: block; margin: auto;">
61
61
  <img alt="Shows my svg">
62
62
  </picture>
63
63
  </div>
@@ -138,7 +138,7 @@ if __name__ == "__main__":
138
138
  ### Anthropic Claude (Computer Use API)
139
139
  ```python
140
140
  model="anthropic/claude-3-5-sonnet-20241022"
141
- model="anthropic/claude-3-5-sonnet-20240620"
141
+ model="anthropic/claude-3-7-sonnet-20250219"
142
142
  model="anthropic/claude-opus-4-20250514"
143
143
  model="anthropic/claude-sonnet-4-20250514"
144
144
  ```
@@ -1,16 +1,16 @@
1
- agent/__init__.py,sha256=vWbQYgjkzIso7zILSm4OAbNU_vrmN4HyYkfX8vC-Yi0,1547
1
+ agent/__init__.py,sha256=MaW-BczJ-lCACPYH39DvFhE7ZWiSo7sBO6pBfyO7Nxc,1269
2
2
  agent/__main__.py,sha256=lBUe8Niqa5XoCjwFfXyX7GtnUwjjZXC1-j4V9mvUYSc,538
3
3
  agent/adapters/__init__.py,sha256=lNH6srgIMmZOI7dgicJs3LCk_1MeqLF0lou9n7b23Ts,238
4
4
  agent/adapters/huggingfacelocal_adapter.py,sha256=Uqjtcohhzd33VFh38Ra2y4Uv_lTghMswoqS1t-KKFkw,8480
5
5
  agent/adapters/human_adapter.py,sha256=xT4nnfNXb1z-vnGFlLmFEZN7TMcoMBGS40MtR1Zwv4o,13079
6
- agent/agent.py,sha256=mEbmN5G6y8jZ0FrlUnHfJQFE7r_GlXrHxqC93twv54k,27881
6
+ agent/agent.py,sha256=Zo7K7FacdCwGUDW3cuMCpJ1h9tkULH3hrgx94tdjCsk,29123
7
7
  agent/callbacks/__init__.py,sha256=yxxBXUqpXQ-jRi_ixJMtmQPxoNRy5Vz1PUBzNNa1Dwg,538
8
8
  agent/callbacks/base.py,sha256=UnnnYlh6XCm6HKZZsAPaT_Eyo9LUYLyjyNwF-QRm6Ns,4691
9
9
  agent/callbacks/budget_manager.py,sha256=RyKM-7iXQcDotYvrw3eURzeEHEXvQjID-NobtvQWE7k,1832
10
10
  agent/callbacks/image_retention.py,sha256=tiuRT5ke9xXTb2eP8Gz-2ITyAMY29LURUH6AbjX3RP8,6165
11
11
  agent/callbacks/logging.py,sha256=OOxU97EzrxlnUAtiEnvy9FB7SwCUK90-rdpDFA2Ae4E,10921
12
12
  agent/callbacks/pii_anonymization.py,sha256=NEkUTUjQBi82nqus7kT-1E4RaeQ2hQrY7YCnKndLhP8,3272
13
- agent/callbacks/telemetry.py,sha256=PU7pkK7W1v1xjDN-9gA30lGvn4-WhqK3BPHGW3HpTOc,7497
13
+ agent/callbacks/telemetry.py,sha256=RbUDhE41mTi8g9hNre0EpltK_NUZkLj8buJLWBzs0Ek,7363
14
14
  agent/callbacks/trajectory_saver.py,sha256=VHbiDQzI_XludkWhZIVqIMrsxgwKfFWwVtqaRot_D4U,12231
15
15
  agent/cli.py,sha256=AgaXwywHd3nGQWuqMRj6SbPyFaCPjfo5980Y1ApQOTQ,12413
16
16
  agent/computers/__init__.py,sha256=39ISJsaREaQIZckpzxSuLhuR763wUU3TxUux78EKjAg,1477
@@ -36,15 +36,14 @@ agent/loops/model_types.csv,sha256=GmFn4x80yoUpQZuQ-GXtJkPVlOLYWZ5u_5A73HRyeNE,1
36
36
  agent/loops/omniparser.py,sha256=-db8JUL2Orn47ERIaLbuNShAXn4LeIgYzRWphn_9Dg4,15071
37
37
  agent/loops/openai.py,sha256=8Ad_XufpENmLq1nEnhzF3oswPrPK1EPz-C5NU8UOEs0,8035
38
38
  agent/loops/uitars.py,sha256=PVNOdwcn2K6RgaxoU-9I4HjBTsEH073M11LTqTrN7C4,31849
39
- agent/responses.py,sha256=TTJ3wXN_eb0J26GKhO3cVQngOiZ1AgUPIUadozLUQyE,28991
40
- agent/telemetry.py,sha256=87ZTyBaT0wEPQn4v76II3g0V3GERuIVbypoX-Ug6FKQ,4786
41
- agent/types.py,sha256=ZoWY8a3GZtB8V0SnOzoI7DQy4nP_GRubxJKbuLPOc8c,840
39
+ agent/responses.py,sha256=_SoN4BkaTxMHMB21EOtDc_aDBIJlfDwsCzszMBnIkH0,30764
40
+ agent/types.py,sha256=h6SnmTAEAaryVCjwVZFAuCbio9UW13OqgQEV7HKmZVM,1060
42
41
  agent/ui/__init__.py,sha256=DTZpK85QXscXK2nM9HtpAhVBF13yAamUrtwrQSuV-kM,126
43
42
  agent/ui/__main__.py,sha256=vudWXYvGM0aNT5aZ94HPtGW8YXOZ4cLXepHyhUM_k1g,73
44
43
  agent/ui/gradio/__init__.py,sha256=yv4Mrfo-Sj2U5sVn_UJHAuwYCezo-5O4ItR2C9jzNko,145
45
- agent/ui/gradio/app.py,sha256=m2yDd6Tua_lMMZT1zCzOty2meYEy756d8OlFF7lpdeU,9117
46
- agent/ui/gradio/ui_components.py,sha256=vfsqVo_COsFfw11ouMHClib9fdBf3q52G-qbuo0RyOY,36068
47
- cua_agent-0.4.14.dist-info/METADATA,sha256=uLZvYOCvm5B-sX4z-WNZ1CYLmpZYQd31Rk-33MwzOMM,12616
48
- cua_agent-0.4.14.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
49
- cua_agent-0.4.14.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
50
- cua_agent-0.4.14.dist-info/RECORD,,
44
+ agent/ui/gradio/app.py,sha256=Ol97YEbwREZZQ9_PMjVHlfOcu9BGsawxgAGAm79hT80,9117
45
+ agent/ui/gradio/ui_components.py,sha256=dJUvKDmc1oSejtoR_gU_oWWYwxaOOQyPloSYRGMrUCQ,36068
46
+ cua_agent-0.4.16.dist-info/METADATA,sha256=iKOsC1Me0Rq_jQrhfgLN-uY1KR5OtKIQXpRGXpnH_LE,12698
47
+ cua_agent-0.4.16.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
48
+ cua_agent-0.4.16.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
49
+ cua_agent-0.4.16.dist-info/RECORD,,
agent/telemetry.py DELETED
@@ -1,142 +0,0 @@
1
- """Agent telemetry for tracking anonymous usage and feature usage."""
2
-
3
- import logging
4
- import os
5
- import platform
6
- import sys
7
- from typing import Dict, Any, Callable
8
-
9
- # Import the core telemetry module
10
- TELEMETRY_AVAILABLE = False
11
-
12
-
13
- # Local fallbacks in case core telemetry isn't available
14
- def _noop(*args: Any, **kwargs: Any) -> None:
15
- """No-op function for when telemetry is not available."""
16
- pass
17
-
18
-
19
- # Define default functions with unique names to avoid shadowing
20
- _default_record_event = _noop
21
- _default_increment_counter = _noop
22
- _default_set_dimension = _noop
23
- _default_get_telemetry_client = lambda: None
24
- _default_flush = _noop
25
- _default_is_telemetry_enabled = lambda: False
26
- _default_is_telemetry_globally_disabled = lambda: True
27
-
28
- # Set the actual functions to the defaults initially
29
- record_event = _default_record_event
30
- increment_counter = _default_increment_counter
31
- set_dimension = _default_set_dimension
32
- get_telemetry_client = _default_get_telemetry_client
33
- flush = _default_flush
34
- is_telemetry_enabled = _default_is_telemetry_enabled
35
- is_telemetry_globally_disabled = _default_is_telemetry_globally_disabled
36
-
37
- logger = logging.getLogger("agent.telemetry")
38
-
39
- try:
40
- # Import from core telemetry
41
- from core.telemetry import (
42
- record_event as core_record_event,
43
- increment as core_increment,
44
- get_telemetry_client as core_get_telemetry_client,
45
- flush as core_flush,
46
- is_telemetry_enabled as core_is_telemetry_enabled,
47
- is_telemetry_globally_disabled as core_is_telemetry_globally_disabled,
48
- )
49
-
50
- # Override the default functions with actual implementations
51
- record_event = core_record_event
52
- get_telemetry_client = core_get_telemetry_client
53
- flush = core_flush
54
- is_telemetry_enabled = core_is_telemetry_enabled
55
- is_telemetry_globally_disabled = core_is_telemetry_globally_disabled
56
-
57
- def increment_counter(counter_name: str, value: int = 1) -> None:
58
- """Wrapper for increment to maintain backward compatibility."""
59
- if is_telemetry_enabled():
60
- core_increment(counter_name, value)
61
-
62
- def set_dimension(name: str, value: Any) -> None:
63
- """Set a dimension that will be attached to all events."""
64
- logger.debug(f"Setting dimension {name}={value}")
65
-
66
- TELEMETRY_AVAILABLE = True
67
- logger.info("Successfully imported telemetry")
68
- except ImportError as e:
69
- logger.warning(f"Could not import telemetry: {e}")
70
- logger.debug("Telemetry not available, using no-op functions")
71
-
72
- # Get system info once to use in telemetry
73
- SYSTEM_INFO = {
74
- "os": platform.system().lower(),
75
- "os_version": platform.release(),
76
- "python_version": platform.python_version(),
77
- }
78
-
79
-
80
- def enable_telemetry() -> bool:
81
- """Enable telemetry if available.
82
-
83
- Returns:
84
- bool: True if telemetry was successfully enabled, False otherwise
85
- """
86
- global TELEMETRY_AVAILABLE, record_event, increment_counter, get_telemetry_client, flush, is_telemetry_enabled, is_telemetry_globally_disabled
87
-
88
- # Check if globally disabled using core function
89
- if TELEMETRY_AVAILABLE and is_telemetry_globally_disabled():
90
- logger.info("Telemetry is globally disabled via environment variable - cannot enable")
91
- return False
92
-
93
- # Already enabled
94
- if TELEMETRY_AVAILABLE:
95
- return True
96
-
97
- # Try to import and enable
98
- try:
99
- from core.telemetry import (
100
- record_event,
101
- increment,
102
- get_telemetry_client,
103
- flush,
104
- is_telemetry_globally_disabled,
105
- )
106
-
107
- # Check again after import
108
- if is_telemetry_globally_disabled():
109
- logger.info("Telemetry is globally disabled via environment variable - cannot enable")
110
- return False
111
-
112
- TELEMETRY_AVAILABLE = True
113
- logger.info("Telemetry successfully enabled")
114
- return True
115
- except ImportError as e:
116
- logger.warning(f"Could not enable telemetry: {e}")
117
- return False
118
-
119
-
120
- def is_telemetry_enabled() -> bool:
121
- """Check if telemetry is enabled.
122
-
123
- Returns:
124
- bool: True if telemetry is enabled, False otherwise
125
- """
126
- # Use the core function if available, otherwise use our local flag
127
- if TELEMETRY_AVAILABLE:
128
- from core.telemetry import is_telemetry_enabled as core_is_enabled
129
-
130
- return core_is_enabled()
131
- return False
132
-
133
-
134
- def record_agent_initialization() -> None:
135
- """Record when an agent instance is initialized."""
136
- if TELEMETRY_AVAILABLE and is_telemetry_enabled():
137
- record_event("agent_initialized", SYSTEM_INFO)
138
-
139
- # Set dimensions that will be attached to all events
140
- set_dimension("os", SYSTEM_INFO["os"])
141
- set_dimension("os_version", SYSTEM_INFO["os_version"])
142
- set_dimension("python_version", SYSTEM_INFO["python_version"])