cua-agent 0.1.5__py3-none-any.whl → 0.1.17__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 (52) hide show
  1. agent/__init__.py +3 -4
  2. agent/core/__init__.py +3 -10
  3. agent/core/computer_agent.py +207 -32
  4. agent/core/experiment.py +20 -3
  5. agent/core/loop.py +78 -120
  6. agent/core/messages.py +279 -125
  7. agent/core/telemetry.py +44 -32
  8. agent/core/types.py +35 -0
  9. agent/core/visualization.py +197 -0
  10. agent/providers/anthropic/api/client.py +142 -1
  11. agent/providers/anthropic/api_handler.py +140 -0
  12. agent/providers/anthropic/callbacks/__init__.py +5 -0
  13. agent/providers/anthropic/loop.py +224 -209
  14. agent/providers/anthropic/messages/manager.py +3 -1
  15. agent/providers/anthropic/response_handler.py +229 -0
  16. agent/providers/anthropic/tools/base.py +1 -1
  17. agent/providers/anthropic/tools/bash.py +0 -97
  18. agent/providers/anthropic/tools/collection.py +2 -2
  19. agent/providers/anthropic/tools/computer.py +34 -24
  20. agent/providers/anthropic/tools/manager.py +2 -2
  21. agent/providers/anthropic/utils.py +370 -0
  22. agent/providers/omni/__init__.py +1 -20
  23. agent/providers/omni/api_handler.py +42 -0
  24. agent/providers/omni/clients/anthropic.py +4 -0
  25. agent/providers/omni/image_utils.py +0 -72
  26. agent/providers/omni/loop.py +497 -607
  27. agent/providers/omni/parser.py +60 -5
  28. agent/providers/omni/tools/__init__.py +25 -8
  29. agent/providers/omni/tools/base.py +29 -0
  30. agent/providers/omni/tools/bash.py +43 -38
  31. agent/providers/omni/tools/computer.py +144 -181
  32. agent/providers/omni/tools/manager.py +26 -48
  33. agent/providers/omni/types.py +0 -4
  34. agent/providers/omni/utils.py +225 -144
  35. {cua_agent-0.1.5.dist-info → cua_agent-0.1.17.dist-info}/METADATA +6 -36
  36. cua_agent-0.1.17.dist-info/RECORD +63 -0
  37. agent/core/agent.py +0 -252
  38. agent/core/base_agent.py +0 -164
  39. agent/core/factory.py +0 -102
  40. agent/providers/omni/callbacks.py +0 -78
  41. agent/providers/omni/clients/groq.py +0 -101
  42. agent/providers/omni/experiment.py +0 -273
  43. agent/providers/omni/messages.py +0 -171
  44. agent/providers/omni/tool_manager.py +0 -91
  45. agent/providers/omni/visualization.py +0 -130
  46. agent/types/__init__.py +0 -26
  47. agent/types/base.py +0 -53
  48. agent/types/messages.py +0 -36
  49. cua_agent-0.1.5.dist-info/RECORD +0 -67
  50. /agent/{types → core}/tools.py +0 -0
  51. {cua_agent-0.1.5.dist-info → cua_agent-0.1.17.dist-info}/WHEEL +0 -0
  52. {cua_agent-0.1.5.dist-info → cua_agent-0.1.17.dist-info}/entry_points.txt +0 -0
@@ -1,273 +0,0 @@
1
- """Experiment management for the Cua provider."""
2
-
3
- import os
4
- import logging
5
- import copy
6
- import base64
7
- from io import BytesIO
8
- from datetime import datetime
9
- from typing import Any, Dict, List, Optional
10
- from PIL import Image
11
- import json
12
- import time
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- class ExperimentManager:
18
- """Manages experiment directories and logging for the agent."""
19
-
20
- def __init__(
21
- self,
22
- base_dir: Optional[str] = None,
23
- only_n_most_recent_images: Optional[int] = None,
24
- ):
25
- """Initialize the experiment manager.
26
-
27
- Args:
28
- base_dir: Base directory for saving experiment data
29
- only_n_most_recent_images: Maximum number of recent screenshots to include in API requests
30
- """
31
- self.base_dir = base_dir
32
- self.only_n_most_recent_images = only_n_most_recent_images
33
- self.run_dir = None
34
- self.current_turn_dir = None
35
- self.turn_count = 0
36
- self.screenshot_count = 0
37
- # Track all screenshots for potential API request inclusion
38
- self.screenshot_paths = []
39
-
40
- # Set up experiment directories if base_dir is provided
41
- if self.base_dir:
42
- self.setup_experiment_dirs()
43
-
44
- def setup_experiment_dirs(self) -> None:
45
- """Setup the experiment directory structure."""
46
- if not self.base_dir:
47
- return
48
-
49
- # Create base experiments directory if it doesn't exist
50
- os.makedirs(self.base_dir, exist_ok=True)
51
-
52
- # Use the base_dir directly as the run_dir
53
- self.run_dir = self.base_dir
54
- logger.info(f"Using directory for experiment: {self.run_dir}")
55
-
56
- # Create first turn directory
57
- self.create_turn_dir()
58
-
59
- def create_turn_dir(self) -> None:
60
- """Create a new directory for the current turn."""
61
- if not self.run_dir:
62
- return
63
-
64
- self.turn_count += 1
65
- self.current_turn_dir = os.path.join(self.run_dir, f"turn_{self.turn_count:03d}")
66
- os.makedirs(self.current_turn_dir, exist_ok=True)
67
- logger.info(f"Created turn directory: {self.current_turn_dir}")
68
-
69
- def sanitize_log_data(self, data: Any) -> Any:
70
- """Sanitize data for logging by removing large base64 strings.
71
-
72
- Args:
73
- data: Data to sanitize (dict, list, or primitive)
74
-
75
- Returns:
76
- Sanitized copy of the data
77
- """
78
- if isinstance(data, dict):
79
- result = copy.deepcopy(data)
80
-
81
- # Handle nested dictionaries and lists
82
- for key, value in result.items():
83
- # Process content arrays that contain image data
84
- if key == "content" and isinstance(value, list):
85
- for i, item in enumerate(value):
86
- if isinstance(item, dict):
87
- # Handle Anthropic format
88
- if item.get("type") == "image" and isinstance(item.get("source"), dict):
89
- source = item["source"]
90
- if "data" in source and isinstance(source["data"], str):
91
- # Replace base64 data with a placeholder and length info
92
- data_len = len(source["data"])
93
- source["data"] = f"[BASE64_IMAGE_DATA_LENGTH_{data_len}]"
94
-
95
- # Handle OpenAI format
96
- elif item.get("type") == "image_url" and isinstance(
97
- item.get("image_url"), dict
98
- ):
99
- url_dict = item["image_url"]
100
- if "url" in url_dict and isinstance(url_dict["url"], str):
101
- url = url_dict["url"]
102
- if url.startswith("data:"):
103
- # Replace base64 data with placeholder
104
- data_len = len(url)
105
- url_dict["url"] = f"[BASE64_IMAGE_URL_LENGTH_{data_len}]"
106
-
107
- # Handle other nested structures recursively
108
- if isinstance(value, dict):
109
- result[key] = self.sanitize_log_data(value)
110
- elif isinstance(value, list):
111
- result[key] = [self.sanitize_log_data(item) for item in value]
112
-
113
- return result
114
- elif isinstance(data, list):
115
- return [self.sanitize_log_data(item) for item in data]
116
- else:
117
- return data
118
-
119
- def save_debug_image(self, image_data: str, filename: str) -> None:
120
- """Save a debug image to the experiment directory.
121
-
122
- Args:
123
- image_data: Base64 encoded image data
124
- filename: Filename to save the image as
125
- """
126
- # Since we no longer want to use the images/ folder, we'll skip this functionality
127
- return
128
-
129
- def save_screenshot(self, img_base64: str, action_type: str = "") -> None:
130
- """Save a screenshot to the experiment directory.
131
-
132
- Args:
133
- img_base64: Base64 encoded screenshot
134
- action_type: Type of action that triggered the screenshot
135
- """
136
- if not self.current_turn_dir:
137
- return
138
-
139
- try:
140
- # Increment screenshot counter
141
- self.screenshot_count += 1
142
-
143
- # Create a descriptive filename
144
- timestamp = int(time.time() * 1000)
145
- action_suffix = f"_{action_type}" if action_type else ""
146
- filename = f"screenshot_{self.screenshot_count:03d}{action_suffix}_{timestamp}.png"
147
-
148
- # Save directly to the turn directory (no screenshots subdirectory)
149
- filepath = os.path.join(self.current_turn_dir, filename)
150
-
151
- # Save the screenshot
152
- img_data = base64.b64decode(img_base64)
153
- with open(filepath, "wb") as f:
154
- f.write(img_data)
155
-
156
- # Keep track of the file path for reference
157
- self.screenshot_paths.append(filepath)
158
-
159
- return filepath
160
- except Exception as e:
161
- logger.error(f"Error saving screenshot: {str(e)}")
162
- return None
163
-
164
- def should_save_debug_image(self) -> bool:
165
- """Determine if debug images should be saved.
166
-
167
- Returns:
168
- Boolean indicating if debug images should be saved
169
- """
170
- # We no longer need to save debug images, so always return False
171
- return False
172
-
173
- def save_action_visualization(
174
- self, img: Image.Image, action_name: str, details: str = ""
175
- ) -> str:
176
- """Save a visualization of an action.
177
-
178
- Args:
179
- img: Image to save
180
- action_name: Name of the action
181
- details: Additional details about the action
182
-
183
- Returns:
184
- Path to the saved image
185
- """
186
- if not self.current_turn_dir:
187
- return ""
188
-
189
- try:
190
- # Create a descriptive filename
191
- timestamp = int(time.time() * 1000)
192
- details_suffix = f"_{details}" if details else ""
193
- filename = f"vis_{action_name}{details_suffix}_{timestamp}.png"
194
-
195
- # Save directly to the turn directory (no visualizations subdirectory)
196
- filepath = os.path.join(self.current_turn_dir, filename)
197
-
198
- # Save the image
199
- img.save(filepath)
200
-
201
- # Keep track of the file path for cleanup
202
- self.screenshot_paths.append(filepath)
203
-
204
- return filepath
205
- except Exception as e:
206
- logger.error(f"Error saving action visualization: {str(e)}")
207
- return ""
208
-
209
- def extract_and_save_images(self, data: Any, prefix: str) -> None:
210
- """Extract and save images from response data.
211
-
212
- Args:
213
- data: Response data to extract images from
214
- prefix: Prefix for saved image filenames
215
- """
216
- # Since we no longer want to save extracted images separately,
217
- # we'll skip this functionality entirely
218
- return
219
-
220
- def log_api_call(
221
- self,
222
- call_type: str,
223
- request: Any,
224
- provider: str,
225
- model: str,
226
- response: Any = None,
227
- error: Optional[Exception] = None,
228
- ) -> None:
229
- """Log API call details to file.
230
-
231
- Args:
232
- call_type: Type of API call (e.g., 'request', 'response', 'error')
233
- request: The API request data
234
- provider: The AI provider used
235
- model: The AI model used
236
- response: Optional API response data
237
- error: Optional error information
238
- """
239
- if not self.current_turn_dir:
240
- return
241
-
242
- try:
243
- # Create a unique filename with timestamp
244
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
245
- filename = f"api_call_{timestamp}_{call_type}.json"
246
- filepath = os.path.join(self.current_turn_dir, filename)
247
-
248
- # Sanitize data to remove large base64 strings
249
- sanitized_request = self.sanitize_log_data(request)
250
- sanitized_response = self.sanitize_log_data(response) if response is not None else None
251
-
252
- # Prepare log data
253
- log_data = {
254
- "timestamp": timestamp,
255
- "provider": provider,
256
- "model": model,
257
- "type": call_type,
258
- "request": sanitized_request,
259
- }
260
-
261
- if sanitized_response is not None:
262
- log_data["response"] = sanitized_response
263
- if error is not None:
264
- log_data["error"] = str(error)
265
-
266
- # Write to file
267
- with open(filepath, "w") as f:
268
- json.dump(log_data, f, indent=2, default=str)
269
-
270
- logger.info(f"Logged API {call_type} to {filepath}")
271
-
272
- except Exception as e:
273
- logger.error(f"Error logging API call: {str(e)}")
@@ -1,171 +0,0 @@
1
- """Omni message manager implementation."""
2
-
3
- import base64
4
- from typing import Any, Dict, List, Optional
5
- from io import BytesIO
6
- from PIL import Image
7
-
8
- from ...core.messages import BaseMessageManager, ImageRetentionConfig
9
-
10
-
11
- class OmniMessageManager(BaseMessageManager):
12
- """Message manager for multi-provider support."""
13
-
14
- def __init__(self, config: Optional[ImageRetentionConfig] = None):
15
- """Initialize the message manager.
16
-
17
- Args:
18
- config: Optional configuration for image retention
19
- """
20
- super().__init__(config)
21
- self.messages: List[Dict[str, Any]] = []
22
- self.config = config
23
-
24
- def add_user_message(self, content: str, images: Optional[List[bytes]] = None) -> None:
25
- """Add a user message to the history.
26
-
27
- Args:
28
- content: Message content
29
- images: Optional list of image data
30
- """
31
- # Add images if present
32
- if images:
33
- # Initialize with proper typing for mixed content
34
- message_content: List[Dict[str, Any]] = [{"type": "text", "text": content}]
35
-
36
- # Add each image
37
- for img in images:
38
- message_content.append(
39
- {
40
- "type": "image_url",
41
- "image_url": {
42
- "url": f"data:image/png;base64,{base64.b64encode(img).decode()}"
43
- },
44
- }
45
- )
46
-
47
- message = {"role": "user", "content": message_content}
48
- else:
49
- # Simple text message
50
- message = {"role": "user", "content": content}
51
-
52
- self.messages.append(message)
53
-
54
- # Apply retention policy
55
- if self.config and self.config.num_images_to_keep:
56
- self._apply_image_retention_policy()
57
-
58
- def add_assistant_message(self, content: str) -> None:
59
- """Add an assistant message to the history.
60
-
61
- Args:
62
- content: Message content
63
- """
64
- self.messages.append({"role": "assistant", "content": content})
65
-
66
- def add_system_message(self, content: str) -> None:
67
- """Add a system message to the history.
68
-
69
- Args:
70
- content: Message content
71
- """
72
- self.messages.append({"role": "system", "content": content})
73
-
74
- def _apply_image_retention_policy(self) -> None:
75
- """Apply image retention policy to message history."""
76
- if not self.config or not self.config.num_images_to_keep:
77
- return
78
-
79
- # Count images from newest to oldest
80
- image_count = 0
81
- for message in reversed(self.messages):
82
- if message["role"] != "user":
83
- continue
84
-
85
- # Handle multimodal messages
86
- if isinstance(message["content"], list):
87
- new_content = []
88
- for item in message["content"]:
89
- if item["type"] == "text":
90
- new_content.append(item)
91
- elif item["type"] == "image_url":
92
- if image_count < self.config.num_images_to_keep:
93
- new_content.append(item)
94
- image_count += 1
95
- message["content"] = new_content
96
-
97
- def get_formatted_messages(self, provider: str) -> List[Dict[str, Any]]:
98
- """Get messages formatted for specific provider.
99
-
100
- Args:
101
- provider: Provider name to format messages for
102
-
103
- Returns:
104
- List of formatted messages
105
- """
106
- # Set the provider for message formatting
107
- self.set_provider(provider)
108
-
109
- if provider == "anthropic":
110
- return self._format_for_anthropic()
111
- elif provider == "openai":
112
- return self._format_for_openai()
113
- elif provider == "groq":
114
- return self._format_for_groq()
115
- elif provider == "qwen":
116
- return self._format_for_qwen()
117
- else:
118
- raise ValueError(f"Unsupported provider: {provider}")
119
-
120
- def _format_for_anthropic(self) -> List[Dict[str, Any]]:
121
- """Format messages for Anthropic API."""
122
- formatted = []
123
- for msg in self.messages:
124
- formatted_msg = {"role": msg["role"]}
125
-
126
- # Handle multimodal content
127
- if isinstance(msg["content"], list):
128
- formatted_msg["content"] = []
129
- for item in msg["content"]:
130
- if item["type"] == "text":
131
- formatted_msg["content"].append({"type": "text", "text": item["text"]})
132
- elif item["type"] == "image_url":
133
- formatted_msg["content"].append(
134
- {
135
- "type": "image",
136
- "source": {
137
- "type": "base64",
138
- "media_type": "image/png",
139
- "data": item["image_url"]["url"].split(",")[1],
140
- },
141
- }
142
- )
143
- else:
144
- formatted_msg["content"] = msg["content"]
145
-
146
- formatted.append(formatted_msg)
147
- return formatted
148
-
149
- def _format_for_openai(self) -> List[Dict[str, Any]]:
150
- """Format messages for OpenAI API."""
151
- # OpenAI already uses the same format
152
- return self.messages
153
-
154
- def _format_for_groq(self) -> List[Dict[str, Any]]:
155
- """Format messages for Groq API."""
156
- # Groq uses OpenAI-compatible format
157
- return self.messages
158
-
159
- def _format_for_qwen(self) -> List[Dict[str, Any]]:
160
- """Format messages for Qwen API."""
161
- formatted = []
162
- for msg in self.messages:
163
- if isinstance(msg["content"], list):
164
- # Convert multimodal content to text-only
165
- text_content = next(
166
- (item["text"] for item in msg["content"] if item["type"] == "text"), ""
167
- )
168
- formatted.append({"role": msg["role"], "content": text_content})
169
- else:
170
- formatted.append(msg)
171
- return formatted
@@ -1,91 +0,0 @@
1
- # """Omni tool manager implementation."""
2
-
3
- # from typing import Dict, List, Type, Any
4
-
5
- # from computer import Computer
6
- # from ...core.tools import BaseToolManager, BashTool, EditTool
7
-
8
- # class OmniToolManager(BaseToolManager):
9
- # """Tool manager for multi-provider support."""
10
-
11
- # def __init__(self, computer: Computer):
12
- # """Initialize Omni tool manager.
13
-
14
- # Args:
15
- # computer: Computer instance for tools
16
- # """
17
- # super().__init__(computer)
18
-
19
- # def get_anthropic_tools(self) -> List[Dict[str, Any]]:
20
- # """Get tools formatted for Anthropic API.
21
-
22
- # Returns:
23
- # List of tool parameters in Anthropic format
24
- # """
25
- # tools: List[Dict[str, Any]] = []
26
-
27
- # # Map base tools to Anthropic format
28
- # for tool in self.tools.values():
29
- # if isinstance(tool, BashTool):
30
- # tools.append({
31
- # "type": "bash_20241022",
32
- # "name": tool.name
33
- # })
34
- # elif isinstance(tool, EditTool):
35
- # tools.append({
36
- # "type": "text_editor_20241022",
37
- # "name": "str_replace_editor"
38
- # })
39
-
40
- # return tools
41
-
42
- # def get_openai_tools(self) -> List[Dict]:
43
- # """Get tools formatted for OpenAI API.
44
-
45
- # Returns:
46
- # List of tool parameters in OpenAI format
47
- # """
48
- # tools = []
49
-
50
- # # Map base tools to OpenAI format
51
- # for tool in self.tools.values():
52
- # tools.append({
53
- # "type": "function",
54
- # "function": tool.get_schema()
55
- # })
56
-
57
- # return tools
58
-
59
- # def get_groq_tools(self) -> List[Dict]:
60
- # """Get tools formatted for Groq API.
61
-
62
- # Returns:
63
- # List of tool parameters in Groq format
64
- # """
65
- # tools = []
66
-
67
- # # Map base tools to Groq format
68
- # for tool in self.tools.values():
69
- # tools.append({
70
- # "type": "function",
71
- # "function": tool.get_schema()
72
- # })
73
-
74
- # return tools
75
-
76
- # def get_qwen_tools(self) -> List[Dict]:
77
- # """Get tools formatted for Qwen API.
78
-
79
- # Returns:
80
- # List of tool parameters in Qwen format
81
- # """
82
- # tools = []
83
-
84
- # # Map base tools to Qwen format
85
- # for tool in self.tools.values():
86
- # tools.append({
87
- # "type": "function",
88
- # "function": tool.get_schema()
89
- # })
90
-
91
- # return tools
@@ -1,130 +0,0 @@
1
- """Visualization utilities for the Cua provider."""
2
-
3
- import base64
4
- import logging
5
- from io import BytesIO
6
- from typing import Tuple
7
- from PIL import Image, ImageDraw
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- def visualize_click(x: int, y: int, img_base64: str) -> Image.Image:
13
- """Visualize a click action by drawing on the screenshot.
14
-
15
- Args:
16
- x: X coordinate of the click
17
- y: Y coordinate of the click
18
- img_base64: Base64 encoded image to draw on
19
-
20
- Returns:
21
- PIL Image with visualization
22
- """
23
- try:
24
- # Decode the base64 image
25
- img_data = base64.b64decode(img_base64)
26
- img = Image.open(BytesIO(img_data))
27
-
28
- # Create a drawing context
29
- draw = ImageDraw.Draw(img)
30
-
31
- # Draw concentric circles at the click position
32
- small_radius = 10
33
- large_radius = 30
34
-
35
- # Draw filled inner circle
36
- draw.ellipse(
37
- [(x - small_radius, y - small_radius), (x + small_radius, y + small_radius)],
38
- fill="red",
39
- )
40
-
41
- # Draw outlined outer circle
42
- draw.ellipse(
43
- [(x - large_radius, y - large_radius), (x + large_radius, y + large_radius)],
44
- outline="red",
45
- width=3,
46
- )
47
-
48
- return img
49
-
50
- except Exception as e:
51
- logger.error(f"Error visualizing click: {str(e)}")
52
- # Return a blank image in case of error
53
- return Image.new("RGB", (800, 600), color="white")
54
-
55
-
56
- def visualize_scroll(direction: str, clicks: int, img_base64: str) -> Image.Image:
57
- """Visualize a scroll action by drawing arrows on the screenshot.
58
-
59
- Args:
60
- direction: 'up' or 'down'
61
- clicks: Number of scroll clicks
62
- img_base64: Base64 encoded image to draw on
63
-
64
- Returns:
65
- PIL Image with visualization
66
- """
67
- try:
68
- # Decode the base64 image
69
- img_data = base64.b64decode(img_base64)
70
- img = Image.open(BytesIO(img_data))
71
-
72
- # Get image dimensions
73
- width, height = img.size
74
-
75
- # Create a drawing context
76
- draw = ImageDraw.Draw(img)
77
-
78
- # Determine arrow direction and positions
79
- center_x = width // 2
80
- arrow_width = 100
81
-
82
- if direction.lower() == "up":
83
- # Draw up arrow in the middle of the screen
84
- arrow_y = height // 2
85
- # Arrow points
86
- points = [
87
- (center_x, arrow_y - 50), # Top point
88
- (center_x - arrow_width // 2, arrow_y + 50), # Bottom left
89
- (center_x + arrow_width // 2, arrow_y + 50), # Bottom right
90
- ]
91
- color = "blue"
92
- else: # down
93
- # Draw down arrow in the middle of the screen
94
- arrow_y = height // 2
95
- # Arrow points
96
- points = [
97
- (center_x, arrow_y + 50), # Bottom point
98
- (center_x - arrow_width // 2, arrow_y - 50), # Top left
99
- (center_x + arrow_width // 2, arrow_y - 50), # Top right
100
- ]
101
- color = "green"
102
-
103
- # Draw filled arrow
104
- draw.polygon(points, fill=color)
105
-
106
- # Add text showing number of clicks
107
- text_y = arrow_y + 70 if direction.lower() == "down" else arrow_y - 70
108
- draw.text((center_x - 40, text_y), f"{clicks} clicks", fill="black")
109
-
110
- return img
111
-
112
- except Exception as e:
113
- logger.error(f"Error visualizing scroll: {str(e)}")
114
- # Return a blank image in case of error
115
- return Image.new("RGB", (800, 600), color="white")
116
-
117
-
118
- def calculate_element_center(box: Tuple[int, int, int, int]) -> Tuple[int, int]:
119
- """Calculate the center coordinates of a bounding box.
120
-
121
- Args:
122
- box: Tuple of (left, top, right, bottom) coordinates
123
-
124
- Returns:
125
- Tuple of (center_x, center_y) coordinates
126
- """
127
- left, top, right, bottom = box
128
- center_x = (left + right) // 2
129
- center_y = (top + bottom) // 2
130
- return center_x, center_y
agent/types/__init__.py DELETED
@@ -1,26 +0,0 @@
1
- """Type definitions for the agent package."""
2
-
3
- from .base import Provider, HostConfig, TaskResult, Annotation
4
- from .messages import Message, Request, Response, StepMessage, DisengageMessage
5
- from .tools import ToolInvocation, ToolInvocationState, ClientAttachment, ToolResult
6
-
7
- __all__ = [
8
- # Base types
9
- "Provider",
10
- "HostConfig",
11
- "TaskResult",
12
- "Annotation",
13
-
14
- # Message types
15
- "Message",
16
- "Request",
17
- "Response",
18
- "StepMessage",
19
- "DisengageMessage",
20
-
21
- # Tool types
22
- "ToolInvocation",
23
- "ToolInvocationState",
24
- "ClientAttachment",
25
- "ToolResult",
26
- ]