cua-agent 0.3.2__py3-none-any.whl → 0.4.0b1__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.

Files changed (111) hide show
  1. agent/__init__.py +15 -51
  2. agent/__main__.py +21 -0
  3. agent/adapters/__init__.py +9 -0
  4. agent/adapters/huggingfacelocal_adapter.py +216 -0
  5. agent/agent.py +577 -0
  6. agent/callbacks/__init__.py +17 -0
  7. agent/callbacks/base.py +153 -0
  8. agent/callbacks/budget_manager.py +44 -0
  9. agent/callbacks/image_retention.py +139 -0
  10. agent/callbacks/logging.py +247 -0
  11. agent/callbacks/pii_anonymization.py +259 -0
  12. agent/callbacks/trajectory_saver.py +305 -0
  13. agent/cli.py +290 -0
  14. agent/computer_handler.py +107 -0
  15. agent/decorators.py +90 -0
  16. agent/loops/__init__.py +11 -0
  17. agent/loops/anthropic.py +728 -0
  18. agent/loops/omniparser.py +339 -0
  19. agent/loops/openai.py +95 -0
  20. agent/loops/uitars.py +688 -0
  21. agent/responses.py +207 -0
  22. agent/types.py +79 -0
  23. agent/ui/__init__.py +7 -1
  24. agent/ui/gradio/__init__.py +6 -19
  25. agent/ui/gradio/app.py +80 -1299
  26. agent/ui/gradio/ui_components.py +703 -0
  27. cua_agent-0.4.0b1.dist-info/METADATA +424 -0
  28. cua_agent-0.4.0b1.dist-info/RECORD +30 -0
  29. agent/core/__init__.py +0 -27
  30. agent/core/agent.py +0 -210
  31. agent/core/base.py +0 -217
  32. agent/core/callbacks.py +0 -200
  33. agent/core/experiment.py +0 -249
  34. agent/core/factory.py +0 -122
  35. agent/core/messages.py +0 -332
  36. agent/core/provider_config.py +0 -21
  37. agent/core/telemetry.py +0 -142
  38. agent/core/tools/__init__.py +0 -21
  39. agent/core/tools/base.py +0 -74
  40. agent/core/tools/bash.py +0 -52
  41. agent/core/tools/collection.py +0 -46
  42. agent/core/tools/computer.py +0 -113
  43. agent/core/tools/edit.py +0 -67
  44. agent/core/tools/manager.py +0 -56
  45. agent/core/tools.py +0 -32
  46. agent/core/types.py +0 -88
  47. agent/core/visualization.py +0 -197
  48. agent/providers/__init__.py +0 -4
  49. agent/providers/anthropic/__init__.py +0 -6
  50. agent/providers/anthropic/api/client.py +0 -360
  51. agent/providers/anthropic/api/logging.py +0 -150
  52. agent/providers/anthropic/api_handler.py +0 -140
  53. agent/providers/anthropic/callbacks/__init__.py +0 -5
  54. agent/providers/anthropic/callbacks/manager.py +0 -65
  55. agent/providers/anthropic/loop.py +0 -568
  56. agent/providers/anthropic/prompts.py +0 -23
  57. agent/providers/anthropic/response_handler.py +0 -226
  58. agent/providers/anthropic/tools/__init__.py +0 -33
  59. agent/providers/anthropic/tools/base.py +0 -88
  60. agent/providers/anthropic/tools/bash.py +0 -66
  61. agent/providers/anthropic/tools/collection.py +0 -34
  62. agent/providers/anthropic/tools/computer.py +0 -396
  63. agent/providers/anthropic/tools/edit.py +0 -326
  64. agent/providers/anthropic/tools/manager.py +0 -54
  65. agent/providers/anthropic/tools/run.py +0 -42
  66. agent/providers/anthropic/types.py +0 -16
  67. agent/providers/anthropic/utils.py +0 -381
  68. agent/providers/omni/__init__.py +0 -8
  69. agent/providers/omni/api_handler.py +0 -42
  70. agent/providers/omni/clients/anthropic.py +0 -103
  71. agent/providers/omni/clients/base.py +0 -35
  72. agent/providers/omni/clients/oaicompat.py +0 -195
  73. agent/providers/omni/clients/ollama.py +0 -122
  74. agent/providers/omni/clients/openai.py +0 -155
  75. agent/providers/omni/clients/utils.py +0 -25
  76. agent/providers/omni/image_utils.py +0 -34
  77. agent/providers/omni/loop.py +0 -990
  78. agent/providers/omni/parser.py +0 -307
  79. agent/providers/omni/prompts.py +0 -64
  80. agent/providers/omni/tools/__init__.py +0 -30
  81. agent/providers/omni/tools/base.py +0 -29
  82. agent/providers/omni/tools/bash.py +0 -74
  83. agent/providers/omni/tools/computer.py +0 -179
  84. agent/providers/omni/tools/manager.py +0 -61
  85. agent/providers/omni/utils.py +0 -236
  86. agent/providers/openai/__init__.py +0 -6
  87. agent/providers/openai/api_handler.py +0 -456
  88. agent/providers/openai/loop.py +0 -472
  89. agent/providers/openai/response_handler.py +0 -205
  90. agent/providers/openai/tools/__init__.py +0 -15
  91. agent/providers/openai/tools/base.py +0 -79
  92. agent/providers/openai/tools/computer.py +0 -326
  93. agent/providers/openai/tools/manager.py +0 -106
  94. agent/providers/openai/types.py +0 -36
  95. agent/providers/openai/utils.py +0 -98
  96. agent/providers/uitars/__init__.py +0 -1
  97. agent/providers/uitars/clients/base.py +0 -35
  98. agent/providers/uitars/clients/mlxvlm.py +0 -263
  99. agent/providers/uitars/clients/oaicompat.py +0 -214
  100. agent/providers/uitars/loop.py +0 -660
  101. agent/providers/uitars/prompts.py +0 -63
  102. agent/providers/uitars/tools/__init__.py +0 -1
  103. agent/providers/uitars/tools/computer.py +0 -283
  104. agent/providers/uitars/tools/manager.py +0 -60
  105. agent/providers/uitars/utils.py +0 -264
  106. agent/telemetry.py +0 -21
  107. agent/ui/__main__.py +0 -15
  108. cua_agent-0.3.2.dist-info/METADATA +0 -295
  109. cua_agent-0.3.2.dist-info/RECORD +0 -87
  110. {cua_agent-0.3.2.dist-info → cua_agent-0.4.0b1.dist-info}/WHEEL +0 -0
  111. {cua_agent-0.3.2.dist-info → cua_agent-0.4.0b1.dist-info}/entry_points.txt +0 -0
@@ -1,150 +0,0 @@
1
- """API logging functionality."""
2
-
3
- import json
4
- import logging
5
- from datetime import datetime
6
- from pathlib import Path
7
- import httpx
8
- from typing import Any
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
- def _filter_base64_images(content: Any) -> Any:
13
- """Filter out base64 image data from content.
14
-
15
- Args:
16
- content: Content to filter
17
-
18
- Returns:
19
- Filtered content with base64 data replaced by placeholder
20
- """
21
- if isinstance(content, dict):
22
- filtered = {}
23
- for key, value in content.items():
24
- if (
25
- isinstance(value, dict)
26
- and value.get("type") == "image"
27
- and value.get("source", {}).get("type") == "base64"
28
- ):
29
- # Replace base64 data with placeholder
30
- filtered[key] = {
31
- **value,
32
- "source": {
33
- **value["source"],
34
- "data": "<base64_image_data>"
35
- }
36
- }
37
- else:
38
- filtered[key] = _filter_base64_images(value)
39
- return filtered
40
- elif isinstance(content, list):
41
- return [_filter_base64_images(item) for item in content]
42
- return content
43
-
44
- def log_api_interaction(
45
- request: httpx.Request | None,
46
- response: httpx.Response | object | None,
47
- error: Exception | None,
48
- log_dir: Path = Path("/tmp/claude_logs")
49
- ) -> None:
50
- """Log API request, response, and any errors in a structured way.
51
-
52
- Args:
53
- request: The HTTP request if available
54
- response: The HTTP response or response object
55
- error: Any error that occurred
56
- log_dir: Directory to store log files
57
- """
58
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
59
-
60
- # Helper function to safely decode JSON content
61
- def safe_json_decode(content):
62
- if not content:
63
- return None
64
- try:
65
- if isinstance(content, bytes):
66
- return json.loads(content.decode())
67
- elif isinstance(content, str):
68
- return json.loads(content)
69
- elif isinstance(content, dict):
70
- return content
71
- return None
72
- except json.JSONDecodeError:
73
- return {"error": "Could not decode JSON", "raw": str(content)}
74
-
75
- # Process request content
76
- request_content = None
77
- if request and request.content:
78
- request_content = safe_json_decode(request.content)
79
- request_content = _filter_base64_images(request_content)
80
-
81
- # Process response content
82
- response_content = None
83
- if response:
84
- if isinstance(response, httpx.Response):
85
- try:
86
- response_content = response.json()
87
- except json.JSONDecodeError:
88
- response_content = {"error": "Could not decode JSON", "raw": response.text}
89
- else:
90
- response_content = safe_json_decode(response)
91
- response_content = _filter_base64_images(response_content)
92
-
93
- log_entry = {
94
- "timestamp": timestamp,
95
- "request": {
96
- "method": request.method if request else None,
97
- "url": str(request.url) if request else None,
98
- "headers": dict(request.headers) if request else None,
99
- "content": request_content,
100
- } if request else None,
101
- "response": {
102
- "status_code": response.status_code if isinstance(response, httpx.Response) else None,
103
- "headers": dict(response.headers) if isinstance(response, httpx.Response) else None,
104
- "content": response_content,
105
- } if response else None,
106
- "error": {
107
- "type": type(error).__name__ if error else None,
108
- "message": str(error) if error else None,
109
- } if error else None
110
- }
111
-
112
- # Log to file with timestamp in filename
113
- log_dir.mkdir(exist_ok=True)
114
- log_file = log_dir / f"claude_api_{timestamp.replace(' ', '_').replace(':', '-')}.json"
115
-
116
- with open(log_file, 'w') as f:
117
- json.dump(log_entry, f, indent=2)
118
-
119
- # Also log a summary to the console
120
- if error:
121
- logger.error(f"API Error at {timestamp}: {error}")
122
- else:
123
- logger.info(
124
- f"API Call at {timestamp}: "
125
- f"{request.method if request else 'No request'} -> "
126
- f"{response.status_code if isinstance(response, httpx.Response) else 'No response'}"
127
- )
128
-
129
- # Log if there are any images in the content
130
- if response_content:
131
- image_count = count_images(response_content)
132
- if image_count > 0:
133
- logger.info(f"Response contains {image_count} images")
134
-
135
- def count_images(content: dict | list | Any) -> int:
136
- """Count the number of images in the content.
137
-
138
- Args:
139
- content: Content to search for images
140
-
141
- Returns:
142
- Number of images found
143
- """
144
- if isinstance(content, dict):
145
- if content.get("type") == "image":
146
- return 1
147
- return sum(count_images(v) for v in content.values())
148
- elif isinstance(content, list):
149
- return sum(count_images(item) for item in content)
150
- return 0
@@ -1,140 +0,0 @@
1
- """API call handling for Anthropic provider."""
2
-
3
- import logging
4
- import asyncio
5
- from typing import List
6
-
7
- from anthropic.types.beta import (
8
- BetaMessage,
9
- BetaMessageParam,
10
- BetaTextBlockParam,
11
- )
12
-
13
- from .types import LLMProvider
14
- from .prompts import SYSTEM_PROMPT
15
-
16
- # Constants
17
- COMPUTER_USE_BETA_FLAG = "computer-use-2025-01-24"
18
- PROMPT_CACHING_BETA_FLAG = "prompt-caching-2024-07-31"
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class AnthropicAPIHandler:
24
- """Handles API calls to Anthropic's API with structured error handling and retries."""
25
-
26
- def __init__(self, loop):
27
- """Initialize the API handler.
28
-
29
- Args:
30
- loop: Reference to the parent loop instance that provides context
31
- """
32
- self.loop = loop
33
-
34
- async def make_api_call(
35
- self, messages: List[BetaMessageParam], system_prompt: str = SYSTEM_PROMPT
36
- ) -> BetaMessage:
37
- """Make API call to Anthropic with retry logic.
38
-
39
- Args:
40
- messages: List of messages to send to the API
41
- system_prompt: System prompt to use (default: SYSTEM_PROMPT)
42
-
43
- Returns:
44
- API response
45
-
46
- Raises:
47
- RuntimeError: If API call fails after all retries
48
- """
49
- if self.loop.client is None:
50
- raise RuntimeError("Client not initialized. Call initialize_client() first.")
51
- if self.loop.tool_manager is None:
52
- raise RuntimeError("Tool manager not initialized. Call initialize_client() first.")
53
-
54
- last_error = None
55
-
56
- # Add detailed debug logging to examine messages
57
- logger.info(f"Sending {len(messages)} messages to Anthropic API")
58
-
59
- # Log tool use IDs and tool result IDs for debugging
60
- tool_use_ids = set()
61
- tool_result_ids = set()
62
-
63
- for i, msg in enumerate(messages):
64
- logger.info(f"Message {i}: role={msg.get('role')}")
65
- if isinstance(msg.get("content"), list):
66
- for content_block in msg.get("content", []):
67
- if isinstance(content_block, dict):
68
- block_type = content_block.get("type")
69
- if block_type == "tool_use" and "id" in content_block:
70
- tool_id = content_block.get("id")
71
- tool_use_ids.add(tool_id)
72
- logger.info(f" - Found tool_use with ID: {tool_id}")
73
- elif block_type == "tool_result" and "tool_use_id" in content_block:
74
- result_id = content_block.get("tool_use_id")
75
- tool_result_ids.add(result_id)
76
- logger.info(f" - Found tool_result referencing ID: {result_id}")
77
-
78
- # Check for mismatches
79
- missing_tool_uses = tool_result_ids - tool_use_ids
80
- if missing_tool_uses:
81
- logger.warning(
82
- f"Found tool_result IDs without matching tool_use IDs: {missing_tool_uses}"
83
- )
84
-
85
- for attempt in range(self.loop.max_retries):
86
- try:
87
- # Log request
88
- request_data = {
89
- "messages": messages,
90
- "max_tokens": self.loop.max_tokens,
91
- "system": system_prompt,
92
- }
93
- # Let ExperimentManager handle sanitization
94
- self.loop._log_api_call("request", request_data)
95
-
96
- # Setup betas and system
97
- system = BetaTextBlockParam(
98
- type="text",
99
- text=system_prompt,
100
- )
101
-
102
- betas = [COMPUTER_USE_BETA_FLAG]
103
- # Add prompt caching if enabled in the message manager's config
104
- if self.loop.message_manager.config.enable_caching:
105
- betas.append(PROMPT_CACHING_BETA_FLAG)
106
- system["cache_control"] = {"type": "ephemeral"}
107
-
108
- # Make API call
109
- response = await self.loop.client.create_message(
110
- messages=messages,
111
- system=[system],
112
- tools=self.loop.tool_manager.get_tool_params(),
113
- max_tokens=self.loop.max_tokens,
114
- betas=betas,
115
- )
116
-
117
- # Let ExperimentManager handle sanitization
118
- self.loop._log_api_call("response", request_data, response)
119
-
120
- return response
121
- except Exception as e:
122
- last_error = e
123
- logger.error(
124
- f"Error in API call (attempt {attempt + 1}/{self.loop.max_retries}): {str(e)}"
125
- )
126
- self.loop._log_api_call("error", {"messages": messages}, error=e)
127
-
128
- if attempt < self.loop.max_retries - 1:
129
- await asyncio.sleep(
130
- self.loop.retry_delay * (attempt + 1)
131
- ) # Exponential backoff
132
- continue
133
-
134
- # If we get here, all retries failed
135
- error_message = f"API call failed after {self.loop.max_retries} attempts"
136
- if last_error:
137
- error_message += f": {str(last_error)}"
138
-
139
- logger.error(error_message)
140
- raise RuntimeError(error_message)
@@ -1,5 +0,0 @@
1
- """Anthropic callbacks package."""
2
-
3
- from .manager import CallbackManager
4
-
5
- __all__ = ["CallbackManager"]
@@ -1,65 +0,0 @@
1
- from typing import Callable, Protocol
2
- import httpx
3
- from anthropic.types.beta import BetaContentBlockParam
4
- from ..tools import ToolResult
5
-
6
-
7
- class APICallback(Protocol):
8
- """Protocol for API callbacks."""
9
-
10
- def __call__(
11
- self,
12
- request: httpx.Request | None,
13
- response: httpx.Response | object | None,
14
- error: Exception | None,
15
- ) -> None: ...
16
-
17
-
18
- class ContentCallback(Protocol):
19
- """Protocol for content callbacks."""
20
-
21
- def __call__(self, content: BetaContentBlockParam) -> None: ...
22
-
23
-
24
- class ToolCallback(Protocol):
25
- """Protocol for tool callbacks."""
26
-
27
- def __call__(self, result: ToolResult, tool_id: str) -> None: ...
28
-
29
-
30
- class CallbackManager:
31
- """Manages various callbacks for the agent system."""
32
-
33
- def __init__(
34
- self,
35
- content_callback: ContentCallback,
36
- tool_callback: ToolCallback,
37
- api_callback: APICallback,
38
- ):
39
- """Initialize the callback manager.
40
-
41
- Args:
42
- content_callback: Callback for content updates
43
- tool_callback: Callback for tool execution results
44
- api_callback: Callback for API interactions
45
- """
46
- self.content_callback = content_callback
47
- self.tool_callback = tool_callback
48
- self.api_callback = api_callback
49
-
50
- def on_content(self, content: BetaContentBlockParam) -> None:
51
- """Handle content updates."""
52
- self.content_callback(content)
53
-
54
- def on_tool_result(self, result: ToolResult, tool_id: str) -> None:
55
- """Handle tool execution results."""
56
- self.tool_callback(result, tool_id)
57
-
58
- def on_api_interaction(
59
- self,
60
- request: httpx.Request | None,
61
- response: httpx.Response | object | None,
62
- error: Exception | None,
63
- ) -> None:
64
- """Handle API interactions."""
65
- self.api_callback(request, response, error)