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.
- agent/__init__.py +15 -51
- agent/__main__.py +21 -0
- agent/adapters/__init__.py +9 -0
- agent/adapters/huggingfacelocal_adapter.py +216 -0
- agent/agent.py +577 -0
- agent/callbacks/__init__.py +17 -0
- agent/callbacks/base.py +153 -0
- agent/callbacks/budget_manager.py +44 -0
- agent/callbacks/image_retention.py +139 -0
- agent/callbacks/logging.py +247 -0
- agent/callbacks/pii_anonymization.py +259 -0
- agent/callbacks/trajectory_saver.py +305 -0
- agent/cli.py +290 -0
- agent/computer_handler.py +107 -0
- agent/decorators.py +90 -0
- agent/loops/__init__.py +11 -0
- agent/loops/anthropic.py +728 -0
- agent/loops/omniparser.py +339 -0
- agent/loops/openai.py +95 -0
- agent/loops/uitars.py +688 -0
- agent/responses.py +207 -0
- agent/types.py +79 -0
- agent/ui/__init__.py +7 -1
- agent/ui/gradio/__init__.py +6 -19
- agent/ui/gradio/app.py +80 -1299
- agent/ui/gradio/ui_components.py +703 -0
- cua_agent-0.4.0b1.dist-info/METADATA +424 -0
- cua_agent-0.4.0b1.dist-info/RECORD +30 -0
- agent/core/__init__.py +0 -27
- agent/core/agent.py +0 -210
- agent/core/base.py +0 -217
- agent/core/callbacks.py +0 -200
- agent/core/experiment.py +0 -249
- agent/core/factory.py +0 -122
- agent/core/messages.py +0 -332
- agent/core/provider_config.py +0 -21
- agent/core/telemetry.py +0 -142
- agent/core/tools/__init__.py +0 -21
- agent/core/tools/base.py +0 -74
- agent/core/tools/bash.py +0 -52
- agent/core/tools/collection.py +0 -46
- agent/core/tools/computer.py +0 -113
- agent/core/tools/edit.py +0 -67
- agent/core/tools/manager.py +0 -56
- agent/core/tools.py +0 -32
- agent/core/types.py +0 -88
- agent/core/visualization.py +0 -197
- agent/providers/__init__.py +0 -4
- agent/providers/anthropic/__init__.py +0 -6
- agent/providers/anthropic/api/client.py +0 -360
- agent/providers/anthropic/api/logging.py +0 -150
- agent/providers/anthropic/api_handler.py +0 -140
- agent/providers/anthropic/callbacks/__init__.py +0 -5
- agent/providers/anthropic/callbacks/manager.py +0 -65
- agent/providers/anthropic/loop.py +0 -568
- agent/providers/anthropic/prompts.py +0 -23
- agent/providers/anthropic/response_handler.py +0 -226
- agent/providers/anthropic/tools/__init__.py +0 -33
- agent/providers/anthropic/tools/base.py +0 -88
- agent/providers/anthropic/tools/bash.py +0 -66
- agent/providers/anthropic/tools/collection.py +0 -34
- agent/providers/anthropic/tools/computer.py +0 -396
- agent/providers/anthropic/tools/edit.py +0 -326
- agent/providers/anthropic/tools/manager.py +0 -54
- agent/providers/anthropic/tools/run.py +0 -42
- agent/providers/anthropic/types.py +0 -16
- agent/providers/anthropic/utils.py +0 -381
- agent/providers/omni/__init__.py +0 -8
- agent/providers/omni/api_handler.py +0 -42
- agent/providers/omni/clients/anthropic.py +0 -103
- agent/providers/omni/clients/base.py +0 -35
- agent/providers/omni/clients/oaicompat.py +0 -195
- agent/providers/omni/clients/ollama.py +0 -122
- agent/providers/omni/clients/openai.py +0 -155
- agent/providers/omni/clients/utils.py +0 -25
- agent/providers/omni/image_utils.py +0 -34
- agent/providers/omni/loop.py +0 -990
- agent/providers/omni/parser.py +0 -307
- agent/providers/omni/prompts.py +0 -64
- agent/providers/omni/tools/__init__.py +0 -30
- agent/providers/omni/tools/base.py +0 -29
- agent/providers/omni/tools/bash.py +0 -74
- agent/providers/omni/tools/computer.py +0 -179
- agent/providers/omni/tools/manager.py +0 -61
- agent/providers/omni/utils.py +0 -236
- agent/providers/openai/__init__.py +0 -6
- agent/providers/openai/api_handler.py +0 -456
- agent/providers/openai/loop.py +0 -472
- agent/providers/openai/response_handler.py +0 -205
- agent/providers/openai/tools/__init__.py +0 -15
- agent/providers/openai/tools/base.py +0 -79
- agent/providers/openai/tools/computer.py +0 -326
- agent/providers/openai/tools/manager.py +0 -106
- agent/providers/openai/types.py +0 -36
- agent/providers/openai/utils.py +0 -98
- agent/providers/uitars/__init__.py +0 -1
- agent/providers/uitars/clients/base.py +0 -35
- agent/providers/uitars/clients/mlxvlm.py +0 -263
- agent/providers/uitars/clients/oaicompat.py +0 -214
- agent/providers/uitars/loop.py +0 -660
- agent/providers/uitars/prompts.py +0 -63
- agent/providers/uitars/tools/__init__.py +0 -1
- agent/providers/uitars/tools/computer.py +0 -283
- agent/providers/uitars/tools/manager.py +0 -60
- agent/providers/uitars/utils.py +0 -264
- agent/telemetry.py +0 -21
- agent/ui/__main__.py +0 -15
- cua_agent-0.3.2.dist-info/METADATA +0 -295
- cua_agent-0.3.2.dist-info/RECORD +0 -87
- {cua_agent-0.3.2.dist-info → cua_agent-0.4.0b1.dist-info}/WHEEL +0 -0
- {cua_agent-0.3.2.dist-info → cua_agent-0.4.0b1.dist-info}/entry_points.txt +0 -0
agent/core/factory.py
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"""Base agent loop implementation."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import importlib.util
|
|
5
|
-
from typing import Dict, Optional, Type, TYPE_CHECKING, Any, cast, Callable, Awaitable
|
|
6
|
-
|
|
7
|
-
from computer import Computer
|
|
8
|
-
from .types import AgentLoop
|
|
9
|
-
from .base import BaseLoop
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class LoopFactory:
|
|
15
|
-
"""Factory class for creating agent loops."""
|
|
16
|
-
|
|
17
|
-
# Registry to store loop implementations
|
|
18
|
-
_loop_registry: Dict[AgentLoop, Type[BaseLoop]] = {}
|
|
19
|
-
|
|
20
|
-
@classmethod
|
|
21
|
-
def create_loop(
|
|
22
|
-
cls,
|
|
23
|
-
loop_type: AgentLoop,
|
|
24
|
-
api_key: str,
|
|
25
|
-
model_name: str,
|
|
26
|
-
computer: Computer,
|
|
27
|
-
provider: Any = None,
|
|
28
|
-
save_trajectory: bool = True,
|
|
29
|
-
trajectory_dir: str = "trajectories",
|
|
30
|
-
only_n_most_recent_images: Optional[int] = None,
|
|
31
|
-
acknowledge_safety_check_callback: Optional[Callable[[str], Awaitable[bool]]] = None,
|
|
32
|
-
provider_base_url: Optional[str] = None,
|
|
33
|
-
) -> BaseLoop:
|
|
34
|
-
"""Create and return an appropriate loop instance based on type."""
|
|
35
|
-
if loop_type == AgentLoop.ANTHROPIC:
|
|
36
|
-
# Lazy import AnthropicLoop only when needed
|
|
37
|
-
try:
|
|
38
|
-
from ..providers.anthropic.loop import AnthropicLoop
|
|
39
|
-
except ImportError:
|
|
40
|
-
raise ImportError(
|
|
41
|
-
"The 'anthropic' provider is not installed. "
|
|
42
|
-
"Install it with 'pip install cua-agent[anthropic]'"
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
return AnthropicLoop(
|
|
46
|
-
api_key=api_key,
|
|
47
|
-
model=model_name,
|
|
48
|
-
computer=computer,
|
|
49
|
-
save_trajectory=save_trajectory,
|
|
50
|
-
base_dir=trajectory_dir,
|
|
51
|
-
only_n_most_recent_images=only_n_most_recent_images,
|
|
52
|
-
)
|
|
53
|
-
elif loop_type == AgentLoop.OPENAI:
|
|
54
|
-
# Lazy import OpenAILoop only when needed
|
|
55
|
-
try:
|
|
56
|
-
from ..providers.openai.loop import OpenAILoop
|
|
57
|
-
except ImportError:
|
|
58
|
-
raise ImportError(
|
|
59
|
-
"The 'openai' provider is not installed. "
|
|
60
|
-
"Install it with 'pip install cua-agent[openai]'"
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
return OpenAILoop(
|
|
64
|
-
api_key=api_key,
|
|
65
|
-
model=model_name,
|
|
66
|
-
computer=computer,
|
|
67
|
-
save_trajectory=save_trajectory,
|
|
68
|
-
base_dir=trajectory_dir,
|
|
69
|
-
only_n_most_recent_images=only_n_most_recent_images,
|
|
70
|
-
acknowledge_safety_check_callback=acknowledge_safety_check_callback,
|
|
71
|
-
)
|
|
72
|
-
elif loop_type == AgentLoop.OMNI:
|
|
73
|
-
# Lazy import OmniLoop and related classes only when needed
|
|
74
|
-
try:
|
|
75
|
-
from ..providers.omni.loop import OmniLoop
|
|
76
|
-
from ..providers.omni.parser import OmniParser
|
|
77
|
-
from .types import LLMProvider
|
|
78
|
-
except ImportError:
|
|
79
|
-
raise ImportError(
|
|
80
|
-
"The 'omni' provider is not installed. "
|
|
81
|
-
"Install it with 'pip install cua-agent[all]'"
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
if provider is None:
|
|
85
|
-
raise ValueError("Provider is required for OMNI loop type")
|
|
86
|
-
|
|
87
|
-
# We know provider is the correct type at this point, so cast it
|
|
88
|
-
provider_instance = cast(LLMProvider, provider)
|
|
89
|
-
|
|
90
|
-
return OmniLoop(
|
|
91
|
-
provider=provider_instance,
|
|
92
|
-
api_key=api_key,
|
|
93
|
-
model=model_name,
|
|
94
|
-
computer=computer,
|
|
95
|
-
save_trajectory=save_trajectory,
|
|
96
|
-
base_dir=trajectory_dir,
|
|
97
|
-
only_n_most_recent_images=only_n_most_recent_images,
|
|
98
|
-
parser=OmniParser(),
|
|
99
|
-
provider_base_url=provider_base_url,
|
|
100
|
-
)
|
|
101
|
-
elif loop_type == AgentLoop.UITARS:
|
|
102
|
-
# Lazy import UITARSLoop only when needed
|
|
103
|
-
try:
|
|
104
|
-
from ..providers.uitars.loop import UITARSLoop
|
|
105
|
-
except ImportError:
|
|
106
|
-
raise ImportError(
|
|
107
|
-
"The 'uitars' provider is not installed. "
|
|
108
|
-
"Install it with 'pip install cua-agent[all]'"
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
return UITARSLoop(
|
|
112
|
-
api_key=api_key,
|
|
113
|
-
model=model_name,
|
|
114
|
-
computer=computer,
|
|
115
|
-
save_trajectory=save_trajectory,
|
|
116
|
-
base_dir=trajectory_dir,
|
|
117
|
-
only_n_most_recent_images=only_n_most_recent_images,
|
|
118
|
-
provider_base_url=provider_base_url,
|
|
119
|
-
provider=provider,
|
|
120
|
-
)
|
|
121
|
-
else:
|
|
122
|
-
raise ValueError(f"Unsupported loop type: {loop_type}")
|
agent/core/messages.py
DELETED
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
"""Message handling utilities for agent."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import json
|
|
5
|
-
from typing import Any, Dict, List, Optional, Union, Tuple
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
import re
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class ImageRetentionConfig:
|
|
14
|
-
"""Configuration for image retention in messages."""
|
|
15
|
-
|
|
16
|
-
num_images_to_keep: Optional[int] = None
|
|
17
|
-
min_removal_threshold: int = 1
|
|
18
|
-
enable_caching: bool = True
|
|
19
|
-
|
|
20
|
-
def should_retain_images(self) -> bool:
|
|
21
|
-
"""Check if image retention is enabled."""
|
|
22
|
-
return self.num_images_to_keep is not None and self.num_images_to_keep > 0
|
|
23
|
-
|
|
24
|
-
class StandardMessageManager:
|
|
25
|
-
"""Manages messages in a standardized OpenAI format across different providers."""
|
|
26
|
-
|
|
27
|
-
def __init__(self, config: Optional[ImageRetentionConfig] = None):
|
|
28
|
-
"""Initialize message manager.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
config: Configuration for image retention
|
|
32
|
-
"""
|
|
33
|
-
self.messages: List[Dict[str, Any]] = []
|
|
34
|
-
self.config = config or ImageRetentionConfig()
|
|
35
|
-
|
|
36
|
-
def add_user_message(self, content: Union[str, List[Dict[str, Any]]]) -> None:
|
|
37
|
-
"""Add a user message.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
content: Message content (text or multimodal content)
|
|
41
|
-
"""
|
|
42
|
-
self.messages.append({"role": "user", "content": content})
|
|
43
|
-
|
|
44
|
-
def add_assistant_message(self, content: Union[str, List[Dict[str, Any]]]) -> None:
|
|
45
|
-
"""Add an assistant message.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
content: Message content (text or multimodal content)
|
|
49
|
-
"""
|
|
50
|
-
self.messages.append({"role": "assistant", "content": content})
|
|
51
|
-
|
|
52
|
-
def add_system_message(self, content: str) -> None:
|
|
53
|
-
"""Add a system message.
|
|
54
|
-
|
|
55
|
-
Args:
|
|
56
|
-
content: System message content
|
|
57
|
-
"""
|
|
58
|
-
self.messages.append({"role": "system", "content": content})
|
|
59
|
-
|
|
60
|
-
def get_messages(self) -> List[Dict[str, Any]]:
|
|
61
|
-
"""Get all messages in standard format.
|
|
62
|
-
This method applies image retention policy if configured.
|
|
63
|
-
|
|
64
|
-
Returns:
|
|
65
|
-
List of messages
|
|
66
|
-
"""
|
|
67
|
-
# If image retention is configured, apply it
|
|
68
|
-
if self.config.num_images_to_keep is not None:
|
|
69
|
-
return self._apply_image_retention(self.messages)
|
|
70
|
-
return self.messages
|
|
71
|
-
|
|
72
|
-
def _apply_image_retention(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
73
|
-
"""Apply image retention policy to messages.
|
|
74
|
-
|
|
75
|
-
Args:
|
|
76
|
-
messages: List of messages
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
79
|
-
List of messages with image retention applied
|
|
80
|
-
"""
|
|
81
|
-
if not self.config.num_images_to_keep:
|
|
82
|
-
return messages
|
|
83
|
-
|
|
84
|
-
# Find messages with images (both user messages and tool call outputs)
|
|
85
|
-
image_messages = []
|
|
86
|
-
for msg in messages:
|
|
87
|
-
has_image = False
|
|
88
|
-
|
|
89
|
-
# Check user messages with images
|
|
90
|
-
if msg["role"] == "user" and isinstance(msg["content"], list):
|
|
91
|
-
has_image = any(
|
|
92
|
-
item.get("type") == "image_url" or item.get("type") == "image"
|
|
93
|
-
for item in msg["content"]
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
# Check assistant messages with tool calls that have images
|
|
97
|
-
elif msg["role"] == "assistant" and isinstance(msg["content"], list):
|
|
98
|
-
for item in msg["content"]:
|
|
99
|
-
if item.get("type") == "tool_result" and "base64_image" in item:
|
|
100
|
-
has_image = True
|
|
101
|
-
break
|
|
102
|
-
|
|
103
|
-
if has_image:
|
|
104
|
-
image_messages.append(msg)
|
|
105
|
-
|
|
106
|
-
# If we don't have more images than the limit, return all messages
|
|
107
|
-
if len(image_messages) <= self.config.num_images_to_keep:
|
|
108
|
-
return messages
|
|
109
|
-
|
|
110
|
-
# Get the most recent N images to keep
|
|
111
|
-
images_to_keep = image_messages[-self.config.num_images_to_keep :]
|
|
112
|
-
images_to_remove = image_messages[: -self.config.num_images_to_keep]
|
|
113
|
-
|
|
114
|
-
# Create a new message list, removing images from older messages
|
|
115
|
-
result = []
|
|
116
|
-
for msg in messages:
|
|
117
|
-
if msg in images_to_remove:
|
|
118
|
-
# Remove images from this message but keep the text content
|
|
119
|
-
if msg["role"] == "user" and isinstance(msg["content"], list):
|
|
120
|
-
# Keep only text content, remove images
|
|
121
|
-
new_content = [
|
|
122
|
-
item for item in msg["content"]
|
|
123
|
-
if item.get("type") not in ["image_url", "image"]
|
|
124
|
-
]
|
|
125
|
-
if new_content: # Only add if there's still content
|
|
126
|
-
result.append({"role": msg["role"], "content": new_content})
|
|
127
|
-
elif msg["role"] == "assistant" and isinstance(msg["content"], list):
|
|
128
|
-
# Remove base64_image from tool_result items
|
|
129
|
-
new_content = []
|
|
130
|
-
for item in msg["content"]:
|
|
131
|
-
if item.get("type") == "tool_result" and "base64_image" in item:
|
|
132
|
-
# Create a copy without the base64_image
|
|
133
|
-
new_item = {k: v for k, v in item.items() if k != "base64_image"}
|
|
134
|
-
new_content.append(new_item)
|
|
135
|
-
else:
|
|
136
|
-
new_content.append(item)
|
|
137
|
-
result.append({"role": msg["role"], "content": new_content})
|
|
138
|
-
else:
|
|
139
|
-
# For other message types, keep as is
|
|
140
|
-
result.append(msg)
|
|
141
|
-
else:
|
|
142
|
-
result.append(msg)
|
|
143
|
-
|
|
144
|
-
return result
|
|
145
|
-
|
|
146
|
-
def to_anthropic_format(
|
|
147
|
-
self, messages: List[Dict[str, Any]]
|
|
148
|
-
) -> Tuple[List[Dict[str, Any]], str]:
|
|
149
|
-
"""Convert standard OpenAI format messages to Anthropic format.
|
|
150
|
-
|
|
151
|
-
Args:
|
|
152
|
-
messages: List of messages in OpenAI format
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
Tuple containing (anthropic_messages, system_content)
|
|
156
|
-
"""
|
|
157
|
-
result = []
|
|
158
|
-
system_content = ""
|
|
159
|
-
|
|
160
|
-
# Process messages in order to maintain conversation flow
|
|
161
|
-
previous_assistant_tool_use_ids = (
|
|
162
|
-
set()
|
|
163
|
-
) # Track tool_use_ids in the previous assistant message
|
|
164
|
-
|
|
165
|
-
for i, msg in enumerate(messages):
|
|
166
|
-
role = msg.get("role", "")
|
|
167
|
-
content = msg.get("content", "")
|
|
168
|
-
|
|
169
|
-
if role == "system":
|
|
170
|
-
# Collect system messages for later use
|
|
171
|
-
system_content += content + "\n"
|
|
172
|
-
continue
|
|
173
|
-
|
|
174
|
-
if role == "assistant":
|
|
175
|
-
# Track tool_use_ids in this assistant message for the next user message
|
|
176
|
-
previous_assistant_tool_use_ids = set()
|
|
177
|
-
if isinstance(content, list):
|
|
178
|
-
for item in content:
|
|
179
|
-
if (
|
|
180
|
-
isinstance(item, dict)
|
|
181
|
-
and item.get("type") == "tool_use"
|
|
182
|
-
and "id" in item
|
|
183
|
-
):
|
|
184
|
-
previous_assistant_tool_use_ids.add(item["id"])
|
|
185
|
-
|
|
186
|
-
logger.info(
|
|
187
|
-
f"Tool use IDs in assistant message #{i}: {previous_assistant_tool_use_ids}"
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
if role in ["user", "assistant"]:
|
|
191
|
-
anthropic_msg = {"role": role}
|
|
192
|
-
|
|
193
|
-
# Convert content based on type
|
|
194
|
-
if isinstance(content, str):
|
|
195
|
-
# Simple text content
|
|
196
|
-
anthropic_msg["content"] = [{"type": "text", "text": content}]
|
|
197
|
-
elif isinstance(content, list):
|
|
198
|
-
# Convert complex content
|
|
199
|
-
anthropic_content = []
|
|
200
|
-
for item in content:
|
|
201
|
-
item_type = item.get("type", "")
|
|
202
|
-
|
|
203
|
-
if item_type == "text":
|
|
204
|
-
anthropic_content.append({"type": "text", "text": item.get("text", "")})
|
|
205
|
-
elif item_type == "image_url":
|
|
206
|
-
# Convert OpenAI image format to Anthropic
|
|
207
|
-
image_url = item.get("image_url", {}).get("url", "")
|
|
208
|
-
if image_url.startswith("data:"):
|
|
209
|
-
# Extract base64 data and media type
|
|
210
|
-
match = re.match(r"data:(.+);base64,(.+)", image_url)
|
|
211
|
-
if match:
|
|
212
|
-
media_type, data = match.groups()
|
|
213
|
-
anthropic_content.append(
|
|
214
|
-
{
|
|
215
|
-
"type": "image",
|
|
216
|
-
"source": {
|
|
217
|
-
"type": "base64",
|
|
218
|
-
"media_type": media_type,
|
|
219
|
-
"data": data,
|
|
220
|
-
},
|
|
221
|
-
}
|
|
222
|
-
)
|
|
223
|
-
else:
|
|
224
|
-
# Regular URL
|
|
225
|
-
anthropic_content.append(
|
|
226
|
-
{
|
|
227
|
-
"type": "image",
|
|
228
|
-
"source": {
|
|
229
|
-
"type": "url",
|
|
230
|
-
"url": image_url,
|
|
231
|
-
},
|
|
232
|
-
}
|
|
233
|
-
)
|
|
234
|
-
elif item_type == "tool_use":
|
|
235
|
-
# Always include tool_use blocks
|
|
236
|
-
anthropic_content.append(item)
|
|
237
|
-
elif item_type == "tool_result":
|
|
238
|
-
# Check if this is a user message AND if the tool_use_id exists in the previous assistant message
|
|
239
|
-
tool_use_id = item.get("tool_use_id")
|
|
240
|
-
|
|
241
|
-
# Only include tool_result if it references a tool_use from the immediately preceding assistant message
|
|
242
|
-
if (
|
|
243
|
-
role == "user"
|
|
244
|
-
and tool_use_id
|
|
245
|
-
and tool_use_id in previous_assistant_tool_use_ids
|
|
246
|
-
):
|
|
247
|
-
anthropic_content.append(item)
|
|
248
|
-
logger.info(
|
|
249
|
-
f"Including tool_result with tool_use_id: {tool_use_id}"
|
|
250
|
-
)
|
|
251
|
-
else:
|
|
252
|
-
# Convert to text to preserve information
|
|
253
|
-
logger.warning(
|
|
254
|
-
f"Converting tool_result to text. Tool use ID {tool_use_id} not found in previous assistant message"
|
|
255
|
-
)
|
|
256
|
-
content_text = "Tool Result: "
|
|
257
|
-
if "content" in item:
|
|
258
|
-
if isinstance(item["content"], list):
|
|
259
|
-
for content_item in item["content"]:
|
|
260
|
-
if (
|
|
261
|
-
isinstance(content_item, dict)
|
|
262
|
-
and content_item.get("type") == "text"
|
|
263
|
-
):
|
|
264
|
-
content_text += content_item.get("text", "")
|
|
265
|
-
elif isinstance(item["content"], str):
|
|
266
|
-
content_text += item["content"]
|
|
267
|
-
anthropic_content.append({"type": "text", "text": content_text})
|
|
268
|
-
|
|
269
|
-
anthropic_msg["content"] = anthropic_content
|
|
270
|
-
|
|
271
|
-
result.append(anthropic_msg)
|
|
272
|
-
|
|
273
|
-
return result, system_content
|
|
274
|
-
|
|
275
|
-
def from_anthropic_format(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
276
|
-
"""Convert Anthropic format messages to standard OpenAI format.
|
|
277
|
-
|
|
278
|
-
Args:
|
|
279
|
-
messages: List of messages in Anthropic format
|
|
280
|
-
|
|
281
|
-
Returns:
|
|
282
|
-
List of messages in OpenAI format
|
|
283
|
-
"""
|
|
284
|
-
result = []
|
|
285
|
-
|
|
286
|
-
for msg in messages:
|
|
287
|
-
role = msg.get("role", "")
|
|
288
|
-
content = msg.get("content", [])
|
|
289
|
-
|
|
290
|
-
if role in ["user", "assistant"]:
|
|
291
|
-
openai_msg = {"role": role}
|
|
292
|
-
|
|
293
|
-
# Simple case: single text block
|
|
294
|
-
if len(content) == 1 and content[0].get("type") == "text":
|
|
295
|
-
openai_msg["content"] = content[0].get("text", "")
|
|
296
|
-
else:
|
|
297
|
-
# Complex case: multiple blocks or non-text
|
|
298
|
-
openai_content = []
|
|
299
|
-
for item in content:
|
|
300
|
-
item_type = item.get("type", "")
|
|
301
|
-
|
|
302
|
-
if item_type == "text":
|
|
303
|
-
openai_content.append({"type": "text", "text": item.get("text", "")})
|
|
304
|
-
elif item_type == "image":
|
|
305
|
-
# Convert Anthropic image to OpenAI format
|
|
306
|
-
source = item.get("source", {})
|
|
307
|
-
if source.get("type") == "base64":
|
|
308
|
-
media_type = source.get("media_type", "image/png")
|
|
309
|
-
data = source.get("data", "")
|
|
310
|
-
openai_content.append(
|
|
311
|
-
{
|
|
312
|
-
"type": "image_url",
|
|
313
|
-
"image_url": {"url": f"data:{media_type};base64,{data}"},
|
|
314
|
-
}
|
|
315
|
-
)
|
|
316
|
-
else:
|
|
317
|
-
# URL
|
|
318
|
-
openai_content.append(
|
|
319
|
-
{
|
|
320
|
-
"type": "image_url",
|
|
321
|
-
"image_url": {"url": source.get("url", "")},
|
|
322
|
-
}
|
|
323
|
-
)
|
|
324
|
-
elif item_type in ["tool_use", "tool_result"]:
|
|
325
|
-
# Pass through tool-related content
|
|
326
|
-
openai_content.append(item)
|
|
327
|
-
|
|
328
|
-
openai_msg["content"] = openai_content
|
|
329
|
-
|
|
330
|
-
result.append(openai_msg)
|
|
331
|
-
|
|
332
|
-
return result
|
agent/core/provider_config.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""Provider-specific configurations and constants."""
|
|
2
|
-
|
|
3
|
-
from .types import LLMProvider
|
|
4
|
-
|
|
5
|
-
# Default models for different providers
|
|
6
|
-
DEFAULT_MODELS = {
|
|
7
|
-
LLMProvider.OPENAI: "gpt-4o",
|
|
8
|
-
LLMProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
|
|
9
|
-
LLMProvider.OLLAMA: "gemma3:4b-it-q4_K_M",
|
|
10
|
-
LLMProvider.OAICOMPAT: "Qwen2.5-VL-7B-Instruct",
|
|
11
|
-
LLMProvider.MLXVLM: "mlx-community/UI-TARS-1.5-7B-4bit",
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
# Map providers to their environment variable names
|
|
15
|
-
ENV_VARS = {
|
|
16
|
-
LLMProvider.OPENAI: "OPENAI_API_KEY",
|
|
17
|
-
LLMProvider.ANTHROPIC: "ANTHROPIC_API_KEY",
|
|
18
|
-
LLMProvider.OLLAMA: "none",
|
|
19
|
-
LLMProvider.OAICOMPAT: "none", # OpenAI-compatible API typically doesn't require an API key
|
|
20
|
-
LLMProvider.MLXVLM: "none", # MLX VLM typically doesn't require an API key
|
|
21
|
-
}
|
agent/core/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"])
|
agent/core/tools/__init__.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""Core tools package."""
|
|
2
|
-
|
|
3
|
-
from .base import BaseTool, ToolResult, ToolError, ToolFailure, CLIResult
|
|
4
|
-
from .bash import BaseBashTool
|
|
5
|
-
from .collection import ToolCollection
|
|
6
|
-
from .computer import BaseComputerTool
|
|
7
|
-
from .edit import BaseEditTool
|
|
8
|
-
from .manager import BaseToolManager
|
|
9
|
-
|
|
10
|
-
__all__ = [
|
|
11
|
-
"BaseTool",
|
|
12
|
-
"ToolResult",
|
|
13
|
-
"ToolError",
|
|
14
|
-
"ToolFailure",
|
|
15
|
-
"CLIResult",
|
|
16
|
-
"BaseBashTool",
|
|
17
|
-
"BaseComputerTool",
|
|
18
|
-
"BaseEditTool",
|
|
19
|
-
"ToolCollection",
|
|
20
|
-
"BaseToolManager",
|
|
21
|
-
]
|