cua-agent 0.2.6__tar.gz → 0.2.9__tar.gz
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.
- {cua_agent-0.2.6 → cua_agent-0.2.9}/PKG-INFO +1 -1
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/__init__.py +2 -2
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/messages.py +1 -100
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/loop.py +5 -1
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/computer.py +2 -8
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/loop.py +2 -2
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/clients/oaicompat.py +4 -2
- {cua_agent-0.2.6 → cua_agent-0.2.9}/pyproject.toml +3 -3
- {cua_agent-0.2.6 → cua_agent-0.2.9}/README.md +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/agent.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/callbacks.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/experiment.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/factory.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/provider_config.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/telemetry.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/bash.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/collection.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/computer.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/edit.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools/manager.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/tools.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/types.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/core/visualization.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/api/client.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/api/logging.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/api_handler.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/callbacks/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/callbacks/manager.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/prompts.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/response_handler.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/bash.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/collection.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/edit.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/manager.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/tools/run.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/types.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/anthropic/utils.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/api_handler.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/clients/anthropic.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/clients/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/clients/oaicompat.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/clients/ollama.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/clients/openai.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/clients/utils.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/image_utils.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/loop.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/parser.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/prompts.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/tools/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/tools/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/tools/bash.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/tools/computer.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/tools/manager.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/omni/utils.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/api_handler.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/response_handler.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/tools/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/tools/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/tools/computer.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/tools/manager.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/types.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/openai/utils.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/clients/base.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/clients/mlxvlm.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/loop.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/prompts.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/tools/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/tools/computer.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/tools/manager.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/providers/uitars/utils.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/telemetry.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/ui/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/ui/gradio/__init__.py +0 -0
- {cua_agent-0.2.6 → cua_agent-0.2.9}/agent/ui/gradio/app.py +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from .factory import BaseLoop
|
|
4
4
|
from .messages import (
|
|
5
|
-
|
|
5
|
+
StandardMessageManager,
|
|
6
6
|
ImageRetentionConfig,
|
|
7
7
|
)
|
|
8
8
|
from .callbacks import (
|
|
@@ -18,7 +18,7 @@ __all__ = [
|
|
|
18
18
|
"BaseLoop",
|
|
19
19
|
"CallbackManager",
|
|
20
20
|
"CallbackHandler",
|
|
21
|
-
"
|
|
21
|
+
"StandardMessageManager",
|
|
22
22
|
"ImageRetentionConfig",
|
|
23
23
|
"BaseCallbackManager",
|
|
24
24
|
"ContentCallback",
|
|
@@ -22,106 +22,6 @@ class ImageRetentionConfig:
|
|
|
22
22
|
"""Check if image retention is enabled."""
|
|
23
23
|
return self.num_images_to_keep is not None and self.num_images_to_keep > 0
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
class BaseMessageManager:
|
|
27
|
-
"""Base class for message preparation and management."""
|
|
28
|
-
|
|
29
|
-
def __init__(self, image_retention_config: Optional[ImageRetentionConfig] = None):
|
|
30
|
-
"""Initialize the message manager.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
image_retention_config: Configuration for image retention
|
|
34
|
-
"""
|
|
35
|
-
self.image_retention_config = image_retention_config or ImageRetentionConfig()
|
|
36
|
-
if self.image_retention_config.min_removal_threshold < 1:
|
|
37
|
-
raise ValueError("min_removal_threshold must be at least 1")
|
|
38
|
-
|
|
39
|
-
# Track provider for message formatting
|
|
40
|
-
self.provider = "openai" # Default provider
|
|
41
|
-
|
|
42
|
-
def set_provider(self, provider: str) -> None:
|
|
43
|
-
"""Set the current provider to format messages for.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
provider: Provider name (e.g., 'openai', 'anthropic')
|
|
47
|
-
"""
|
|
48
|
-
self.provider = provider.lower()
|
|
49
|
-
|
|
50
|
-
def prepare_messages(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
51
|
-
"""Prepare messages by applying image retention and caching as configured.
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
messages: List of messages to prepare
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
Prepared messages
|
|
58
|
-
"""
|
|
59
|
-
if self.image_retention_config.should_retain_images():
|
|
60
|
-
self._filter_images(messages)
|
|
61
|
-
if self.image_retention_config.enable_caching:
|
|
62
|
-
self._inject_caching(messages)
|
|
63
|
-
return messages
|
|
64
|
-
|
|
65
|
-
def _filter_images(self, messages: List[Dict[str, Any]]) -> None:
|
|
66
|
-
"""Filter messages to retain only the specified number of most recent images.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
messages: Messages to filter
|
|
70
|
-
"""
|
|
71
|
-
# Find all tool result blocks that contain images
|
|
72
|
-
tool_results = [
|
|
73
|
-
item
|
|
74
|
-
for message in messages
|
|
75
|
-
for item in (message["content"] if isinstance(message["content"], list) else [])
|
|
76
|
-
if isinstance(item, dict) and item.get("type") == "tool_result"
|
|
77
|
-
]
|
|
78
|
-
|
|
79
|
-
# Count total images
|
|
80
|
-
total_images = sum(
|
|
81
|
-
1
|
|
82
|
-
for result in tool_results
|
|
83
|
-
for content in result.get("content", [])
|
|
84
|
-
if isinstance(content, dict) and content.get("type") == "image"
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
# Calculate how many images to remove
|
|
88
|
-
images_to_remove = total_images - (self.image_retention_config.num_images_to_keep or 0)
|
|
89
|
-
images_to_remove -= images_to_remove % self.image_retention_config.min_removal_threshold
|
|
90
|
-
|
|
91
|
-
# Remove oldest images first
|
|
92
|
-
for result in tool_results:
|
|
93
|
-
if isinstance(result.get("content"), list):
|
|
94
|
-
new_content = []
|
|
95
|
-
for content in result["content"]:
|
|
96
|
-
if isinstance(content, dict) and content.get("type") == "image":
|
|
97
|
-
if images_to_remove > 0:
|
|
98
|
-
images_to_remove -= 1
|
|
99
|
-
continue
|
|
100
|
-
new_content.append(content)
|
|
101
|
-
result["content"] = new_content
|
|
102
|
-
|
|
103
|
-
def _inject_caching(self, messages: List[Dict[str, Any]]) -> None:
|
|
104
|
-
"""Inject caching control for recent message turns.
|
|
105
|
-
|
|
106
|
-
Args:
|
|
107
|
-
messages: Messages to inject caching into
|
|
108
|
-
"""
|
|
109
|
-
# Only apply cache_control for Anthropic API, not OpenAI
|
|
110
|
-
if self.provider != "anthropic":
|
|
111
|
-
return
|
|
112
|
-
|
|
113
|
-
# Default to caching last 3 turns
|
|
114
|
-
turns_to_cache = 3
|
|
115
|
-
for message in reversed(messages):
|
|
116
|
-
if message["role"] == "user" and isinstance(content := message["content"], list):
|
|
117
|
-
if turns_to_cache:
|
|
118
|
-
turns_to_cache -= 1
|
|
119
|
-
content[-1]["cache_control"] = {"type": "ephemeral"}
|
|
120
|
-
else:
|
|
121
|
-
content[-1].pop("cache_control", None)
|
|
122
|
-
break
|
|
123
|
-
|
|
124
|
-
|
|
125
25
|
class StandardMessageManager:
|
|
126
26
|
"""Manages messages in a standardized OpenAI format across different providers."""
|
|
127
27
|
|
|
@@ -160,6 +60,7 @@ class StandardMessageManager:
|
|
|
160
60
|
|
|
161
61
|
def get_messages(self) -> List[Dict[str, Any]]:
|
|
162
62
|
"""Get all messages in standard format.
|
|
63
|
+
This method applies image retention policy if configured.
|
|
163
64
|
|
|
164
65
|
Returns:
|
|
165
66
|
List of messages
|
|
@@ -283,8 +283,12 @@ class AnthropicLoop(BaseLoop):
|
|
|
283
283
|
# Create new turn directory for this API call
|
|
284
284
|
self._create_turn_dir()
|
|
285
285
|
|
|
286
|
+
|
|
287
|
+
# Apply image retention policy
|
|
288
|
+
self.message_manager.messages = messages.copy()
|
|
289
|
+
prepared_messages = self.message_manager.get_messages()
|
|
286
290
|
# Convert standard messages to Anthropic format using utility function
|
|
287
|
-
anthropic_messages, system_content = to_anthropic_format(
|
|
291
|
+
anthropic_messages, system_content = to_anthropic_format(prepared_messages)
|
|
288
292
|
|
|
289
293
|
# Use API handler to make API call with Anthropic format
|
|
290
294
|
response = await self.api_handler.make_api_call(
|
|
@@ -478,17 +478,11 @@ class ComputerTool(BaseComputerTool, BaseAnthropicTool):
|
|
|
478
478
|
if direction == "down":
|
|
479
479
|
# Scroll down (Page Down on macOS)
|
|
480
480
|
self.logger.info(f"Scrolling down, amount: {amount}")
|
|
481
|
-
|
|
482
|
-
for _ in range(amount):
|
|
483
|
-
await self.computer.interface.hotkey("fn", "down")
|
|
484
|
-
await asyncio.sleep(0.1)
|
|
481
|
+
await self.computer.interface.scroll_down(amount)
|
|
485
482
|
else:
|
|
486
483
|
# Scroll up (Page Up on macOS)
|
|
487
484
|
self.logger.info(f"Scrolling up, amount: {amount}")
|
|
488
|
-
|
|
489
|
-
for _ in range(amount):
|
|
490
|
-
await self.computer.interface.hotkey("fn", "up")
|
|
491
|
-
await asyncio.sleep(0.1)
|
|
485
|
+
await self.computer.interface.scroll_up(amount)
|
|
492
486
|
|
|
493
487
|
# Wait briefly for UI changes
|
|
494
488
|
await asyncio.sleep(0.5)
|
|
@@ -276,7 +276,7 @@ class OpenAILoop(BaseLoop):
|
|
|
276
276
|
# Call API
|
|
277
277
|
screen_size = await self.computer.interface.get_screen_size()
|
|
278
278
|
response = await self.api_handler.send_initial_request(
|
|
279
|
-
messages=
|
|
279
|
+
messages=self.message_manager.get_messages(), # Apply image retention policy
|
|
280
280
|
display_width=str(screen_size["width"]),
|
|
281
281
|
display_height=str(screen_size["height"]),
|
|
282
282
|
previous_response_id=self.last_response_id,
|
|
@@ -397,7 +397,7 @@ class OpenAILoop(BaseLoop):
|
|
|
397
397
|
# The API handler will extract this from the message history
|
|
398
398
|
if isinstance(self.last_response_id, str):
|
|
399
399
|
response = await self.api_handler.send_computer_call_request(
|
|
400
|
-
messages=self.message_manager.
|
|
400
|
+
messages=self.message_manager.get_messages(), # Apply image retention policy
|
|
401
401
|
display_width=str(screen_size["width"]),
|
|
402
402
|
display_height=str(screen_size["height"]),
|
|
403
403
|
previous_response_id=self.last_response_id, # Use instance variable
|
|
@@ -6,6 +6,7 @@ from typing import Dict, List, Optional, Any
|
|
|
6
6
|
import aiohttp
|
|
7
7
|
import re
|
|
8
8
|
from .base import BaseUITarsClient
|
|
9
|
+
import asyncio
|
|
9
10
|
|
|
10
11
|
logger = logging.getLogger(__name__)
|
|
11
12
|
|
|
@@ -144,7 +145,7 @@ class OAICompatClient(BaseUITarsClient):
|
|
|
144
145
|
else:
|
|
145
146
|
message = {"role": "user", "content": [{"type": "text", "text": item}]}
|
|
146
147
|
final_messages.append(message)
|
|
147
|
-
|
|
148
|
+
|
|
148
149
|
payload = {
|
|
149
150
|
"model": self.model,
|
|
150
151
|
"messages": final_messages,
|
|
@@ -192,7 +193,8 @@ class OAICompatClient(BaseUITarsClient):
|
|
|
192
193
|
|
|
193
194
|
# if 503, then the endpoint is still warming up
|
|
194
195
|
if response.status == 503:
|
|
195
|
-
logger.error(f"Endpoint is still warming up,
|
|
196
|
+
logger.error(f"Endpoint is still warming up, trying again in 30 seconds...")
|
|
197
|
+
await asyncio.sleep(30)
|
|
196
198
|
raise Exception(f"Endpoint is still warming up: {response_text}")
|
|
197
199
|
|
|
198
200
|
# Try to parse as JSON if the content type is appropriate
|
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "cua-agent"
|
|
9
|
-
version = "0.2.
|
|
9
|
+
version = "0.2.9"
|
|
10
10
|
description = "CUA (Computer Use) Agent for AI-driven computer interaction"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [
|
|
@@ -109,7 +109,7 @@ target-version = [
|
|
|
109
109
|
|
|
110
110
|
[tool.ruff]
|
|
111
111
|
line-length = 100
|
|
112
|
-
target-version = "0.2.
|
|
112
|
+
target-version = "0.2.9"
|
|
113
113
|
select = [
|
|
114
114
|
"E",
|
|
115
115
|
"F",
|
|
@@ -123,7 +123,7 @@ docstring-code-format = true
|
|
|
123
123
|
|
|
124
124
|
[tool.mypy]
|
|
125
125
|
strict = true
|
|
126
|
-
python_version = "0.2.
|
|
126
|
+
python_version = "0.2.9"
|
|
127
127
|
ignore_missing_imports = true
|
|
128
128
|
disallow_untyped_defs = true
|
|
129
129
|
check_untyped_defs = true
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|