cua-agent 0.3.1__py3-none-any.whl → 0.4.0__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 (112) hide show
  1. agent/__init__.py +21 -12
  2. agent/__main__.py +21 -0
  3. agent/adapters/__init__.py +9 -0
  4. agent/adapters/huggingfacelocal_adapter.py +229 -0
  5. agent/agent.py +594 -0
  6. agent/callbacks/__init__.py +19 -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/telemetry.py +210 -0
  13. agent/callbacks/trajectory_saver.py +305 -0
  14. agent/cli.py +297 -0
  15. agent/computer_handler.py +107 -0
  16. agent/decorators.py +90 -0
  17. agent/loops/__init__.py +11 -0
  18. agent/loops/anthropic.py +728 -0
  19. agent/loops/omniparser.py +339 -0
  20. agent/loops/openai.py +95 -0
  21. agent/loops/uitars.py +688 -0
  22. agent/responses.py +207 -0
  23. agent/telemetry.py +135 -14
  24. agent/types.py +79 -0
  25. agent/ui/__init__.py +7 -1
  26. agent/ui/__main__.py +2 -13
  27. agent/ui/gradio/__init__.py +6 -19
  28. agent/ui/gradio/app.py +94 -1313
  29. agent/ui/gradio/ui_components.py +721 -0
  30. cua_agent-0.4.0.dist-info/METADATA +424 -0
  31. cua_agent-0.4.0.dist-info/RECORD +33 -0
  32. {cua_agent-0.3.1.dist-info → cua_agent-0.4.0.dist-info}/WHEEL +1 -1
  33. agent/core/__init__.py +0 -27
  34. agent/core/agent.py +0 -210
  35. agent/core/base.py +0 -217
  36. agent/core/callbacks.py +0 -200
  37. agent/core/experiment.py +0 -249
  38. agent/core/factory.py +0 -122
  39. agent/core/messages.py +0 -332
  40. agent/core/provider_config.py +0 -21
  41. agent/core/telemetry.py +0 -142
  42. agent/core/tools/__init__.py +0 -21
  43. agent/core/tools/base.py +0 -74
  44. agent/core/tools/bash.py +0 -52
  45. agent/core/tools/collection.py +0 -46
  46. agent/core/tools/computer.py +0 -113
  47. agent/core/tools/edit.py +0 -67
  48. agent/core/tools/manager.py +0 -56
  49. agent/core/tools.py +0 -32
  50. agent/core/types.py +0 -88
  51. agent/core/visualization.py +0 -197
  52. agent/providers/__init__.py +0 -4
  53. agent/providers/anthropic/__init__.py +0 -6
  54. agent/providers/anthropic/api/client.py +0 -360
  55. agent/providers/anthropic/api/logging.py +0 -150
  56. agent/providers/anthropic/api_handler.py +0 -140
  57. agent/providers/anthropic/callbacks/__init__.py +0 -5
  58. agent/providers/anthropic/callbacks/manager.py +0 -65
  59. agent/providers/anthropic/loop.py +0 -568
  60. agent/providers/anthropic/prompts.py +0 -23
  61. agent/providers/anthropic/response_handler.py +0 -226
  62. agent/providers/anthropic/tools/__init__.py +0 -33
  63. agent/providers/anthropic/tools/base.py +0 -88
  64. agent/providers/anthropic/tools/bash.py +0 -66
  65. agent/providers/anthropic/tools/collection.py +0 -34
  66. agent/providers/anthropic/tools/computer.py +0 -396
  67. agent/providers/anthropic/tools/edit.py +0 -326
  68. agent/providers/anthropic/tools/manager.py +0 -54
  69. agent/providers/anthropic/tools/run.py +0 -42
  70. agent/providers/anthropic/types.py +0 -16
  71. agent/providers/anthropic/utils.py +0 -367
  72. agent/providers/omni/__init__.py +0 -8
  73. agent/providers/omni/api_handler.py +0 -42
  74. agent/providers/omni/clients/anthropic.py +0 -103
  75. agent/providers/omni/clients/base.py +0 -35
  76. agent/providers/omni/clients/oaicompat.py +0 -195
  77. agent/providers/omni/clients/ollama.py +0 -122
  78. agent/providers/omni/clients/openai.py +0 -155
  79. agent/providers/omni/clients/utils.py +0 -25
  80. agent/providers/omni/image_utils.py +0 -34
  81. agent/providers/omni/loop.py +0 -990
  82. agent/providers/omni/parser.py +0 -307
  83. agent/providers/omni/prompts.py +0 -64
  84. agent/providers/omni/tools/__init__.py +0 -30
  85. agent/providers/omni/tools/base.py +0 -29
  86. agent/providers/omni/tools/bash.py +0 -74
  87. agent/providers/omni/tools/computer.py +0 -179
  88. agent/providers/omni/tools/manager.py +0 -61
  89. agent/providers/omni/utils.py +0 -236
  90. agent/providers/openai/__init__.py +0 -6
  91. agent/providers/openai/api_handler.py +0 -456
  92. agent/providers/openai/loop.py +0 -472
  93. agent/providers/openai/response_handler.py +0 -205
  94. agent/providers/openai/tools/__init__.py +0 -15
  95. agent/providers/openai/tools/base.py +0 -79
  96. agent/providers/openai/tools/computer.py +0 -326
  97. agent/providers/openai/tools/manager.py +0 -106
  98. agent/providers/openai/types.py +0 -36
  99. agent/providers/openai/utils.py +0 -98
  100. agent/providers/uitars/__init__.py +0 -1
  101. agent/providers/uitars/clients/base.py +0 -35
  102. agent/providers/uitars/clients/mlxvlm.py +0 -263
  103. agent/providers/uitars/clients/oaicompat.py +0 -214
  104. agent/providers/uitars/loop.py +0 -660
  105. agent/providers/uitars/prompts.py +0 -63
  106. agent/providers/uitars/tools/__init__.py +0 -1
  107. agent/providers/uitars/tools/computer.py +0 -283
  108. agent/providers/uitars/tools/manager.py +0 -60
  109. agent/providers/uitars/utils.py +0 -264
  110. cua_agent-0.3.1.dist-info/METADATA +0 -295
  111. cua_agent-0.3.1.dist-info/RECORD +0 -87
  112. {cua_agent-0.3.1.dist-info → cua_agent-0.4.0.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
@@ -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"])
@@ -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
- ]