cua-agent 0.3.1__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. {cua_agent-0.3.1.dist-info → cua_agent-0.4.0b1.dist-info}/WHEEL +1 -1
  30. agent/core/__init__.py +0 -27
  31. agent/core/agent.py +0 -210
  32. agent/core/base.py +0 -217
  33. agent/core/callbacks.py +0 -200
  34. agent/core/experiment.py +0 -249
  35. agent/core/factory.py +0 -122
  36. agent/core/messages.py +0 -332
  37. agent/core/provider_config.py +0 -21
  38. agent/core/telemetry.py +0 -142
  39. agent/core/tools/__init__.py +0 -21
  40. agent/core/tools/base.py +0 -74
  41. agent/core/tools/bash.py +0 -52
  42. agent/core/tools/collection.py +0 -46
  43. agent/core/tools/computer.py +0 -113
  44. agent/core/tools/edit.py +0 -67
  45. agent/core/tools/manager.py +0 -56
  46. agent/core/tools.py +0 -32
  47. agent/core/types.py +0 -88
  48. agent/core/visualization.py +0 -197
  49. agent/providers/__init__.py +0 -4
  50. agent/providers/anthropic/__init__.py +0 -6
  51. agent/providers/anthropic/api/client.py +0 -360
  52. agent/providers/anthropic/api/logging.py +0 -150
  53. agent/providers/anthropic/api_handler.py +0 -140
  54. agent/providers/anthropic/callbacks/__init__.py +0 -5
  55. agent/providers/anthropic/callbacks/manager.py +0 -65
  56. agent/providers/anthropic/loop.py +0 -568
  57. agent/providers/anthropic/prompts.py +0 -23
  58. agent/providers/anthropic/response_handler.py +0 -226
  59. agent/providers/anthropic/tools/__init__.py +0 -33
  60. agent/providers/anthropic/tools/base.py +0 -88
  61. agent/providers/anthropic/tools/bash.py +0 -66
  62. agent/providers/anthropic/tools/collection.py +0 -34
  63. agent/providers/anthropic/tools/computer.py +0 -396
  64. agent/providers/anthropic/tools/edit.py +0 -326
  65. agent/providers/anthropic/tools/manager.py +0 -54
  66. agent/providers/anthropic/tools/run.py +0 -42
  67. agent/providers/anthropic/types.py +0 -16
  68. agent/providers/anthropic/utils.py +0 -367
  69. agent/providers/omni/__init__.py +0 -8
  70. agent/providers/omni/api_handler.py +0 -42
  71. agent/providers/omni/clients/anthropic.py +0 -103
  72. agent/providers/omni/clients/base.py +0 -35
  73. agent/providers/omni/clients/oaicompat.py +0 -195
  74. agent/providers/omni/clients/ollama.py +0 -122
  75. agent/providers/omni/clients/openai.py +0 -155
  76. agent/providers/omni/clients/utils.py +0 -25
  77. agent/providers/omni/image_utils.py +0 -34
  78. agent/providers/omni/loop.py +0 -990
  79. agent/providers/omni/parser.py +0 -307
  80. agent/providers/omni/prompts.py +0 -64
  81. agent/providers/omni/tools/__init__.py +0 -30
  82. agent/providers/omni/tools/base.py +0 -29
  83. agent/providers/omni/tools/bash.py +0 -74
  84. agent/providers/omni/tools/computer.py +0 -179
  85. agent/providers/omni/tools/manager.py +0 -61
  86. agent/providers/omni/utils.py +0 -236
  87. agent/providers/openai/__init__.py +0 -6
  88. agent/providers/openai/api_handler.py +0 -456
  89. agent/providers/openai/loop.py +0 -472
  90. agent/providers/openai/response_handler.py +0 -205
  91. agent/providers/openai/tools/__init__.py +0 -15
  92. agent/providers/openai/tools/base.py +0 -79
  93. agent/providers/openai/tools/computer.py +0 -326
  94. agent/providers/openai/tools/manager.py +0 -106
  95. agent/providers/openai/types.py +0 -36
  96. agent/providers/openai/utils.py +0 -98
  97. agent/providers/uitars/__init__.py +0 -1
  98. agent/providers/uitars/clients/base.py +0 -35
  99. agent/providers/uitars/clients/mlxvlm.py +0 -263
  100. agent/providers/uitars/clients/oaicompat.py +0 -214
  101. agent/providers/uitars/loop.py +0 -660
  102. agent/providers/uitars/prompts.py +0 -63
  103. agent/providers/uitars/tools/__init__.py +0 -1
  104. agent/providers/uitars/tools/computer.py +0 -283
  105. agent/providers/uitars/tools/manager.py +0 -60
  106. agent/providers/uitars/utils.py +0 -264
  107. agent/telemetry.py +0 -21
  108. agent/ui/__main__.py +0 -15
  109. cua_agent-0.3.1.dist-info/METADATA +0 -295
  110. cua_agent-0.3.1.dist-info/RECORD +0 -87
  111. {cua_agent-0.3.1.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)