orgo 0.0.11__py3-none-any.whl → 0.0.12__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.
orgo/prompt.py CHANGED
@@ -3,6 +3,7 @@ Prompt module for interacting with virtual computers using AI models.
3
3
  """
4
4
 
5
5
  import os
6
+ import base64
6
7
  from typing import Dict, List, Any, Optional, Callable, Union, Protocol
7
8
 
8
9
 
@@ -105,7 +106,14 @@ class AnthropicProvider:
105
106
  - File browser items: DOUBLE-CLICK to open folders and files
106
107
  - When submitting, use the 'Enter' key, not the 'Return' key.
107
108
  * If you see an icon on the desktop that you need to open, ALWAYS use the double_click action, never use left_click.
108
- </UBUNTU_DESKTOP_GUIDELINES>"""
109
+ </UBUNTU_DESKTOP_GUIDELINES>
110
+
111
+ <SCREENSHOT_GUIDELINES>
112
+ * Be mindful of how many screenshots you take - they consume significant memory.
113
+ * Only take screenshots when you need to see the current state of the screen.
114
+ * Try to batch multiple actions before taking another screenshot.
115
+ * For better performance, limit the number of screenshots you take.
116
+ </SCREENSHOT_GUIDELINES>"""
109
117
 
110
118
  try:
111
119
  # Define the computer tool per Anthropic's documentation
@@ -138,9 +146,9 @@ class AnthropicProvider:
138
146
  while iteration < max_iterations:
139
147
  iteration += 1
140
148
 
141
- # Prune old screenshots if we've exceeded our limit
149
+ # Filter to keep only the N most recent screenshots
142
150
  if screenshot_count > max_saved_screenshots:
143
- self._prune_old_screenshots(messages, screenshot_count - max_saved_screenshots)
151
+ self._filter_to_n_most_recent_images(messages, max_saved_screenshots)
144
152
  screenshot_count = max_saved_screenshots
145
153
 
146
154
  # Create the request parameters
@@ -161,7 +169,20 @@ class AnthropicProvider:
161
169
  }
162
170
 
163
171
  # Create message request to Claude
164
- response = client.beta.messages.create(**request_params)
172
+ try:
173
+ response = client.beta.messages.create(**request_params)
174
+ except Exception as e:
175
+ if "base64" in str(e).lower():
176
+ # If we get a base64 error, try again after more aggressively filtering images
177
+ if callback:
178
+ callback("error", f"Base64 error detected. Attempting recovery...")
179
+
180
+ # Remove all but the most recent image and try again
181
+ self._filter_to_n_most_recent_images(messages, 1)
182
+ response = client.beta.messages.create(**request_params)
183
+ else:
184
+ # Not a base64 error, re-raise
185
+ raise
165
186
 
166
187
  # Extract the content from the response
167
188
  response_content = response.content
@@ -226,49 +247,57 @@ class AnthropicProvider:
226
247
  if callback:
227
248
  callback("error", str(e))
228
249
  raise
229
-
230
- def _prune_old_screenshots(self, messages: List[Dict[str, Any]], num_to_prune: int):
250
+
251
+ def _filter_to_n_most_recent_images(self, messages: List[Dict[str, Any]], max_images: int):
231
252
  """
232
- Remove old screenshots from the conversation history.
253
+ Keep only the N most recent images in the conversation history.
233
254
 
234
255
  Args:
235
256
  messages: The conversation history
236
- num_to_prune: Number of screenshots to remove
257
+ max_images: Maximum number of images to keep
237
258
  """
238
- screenshots_pruned = 0
259
+ # Find all the image blocks in the conversation history
260
+ image_blocks = []
239
261
 
240
- # Start from the beginning of the messages (excluding the first user message)
241
- for i in range(1, len(messages)):
242
- if messages[i]["role"] != "user":
262
+ for msg_idx, msg in enumerate(messages):
263
+ if msg["role"] != "user":
243
264
  continue
244
265
 
245
- content = messages[i]["content"]
266
+ content = msg.get("content", [])
246
267
  if not isinstance(content, list):
247
268
  continue
248
269
 
249
- # Look for tool_result blocks in the content
250
- for j, block in enumerate(content):
270
+ for content_idx, block in enumerate(content):
251
271
  if not isinstance(block, dict):
252
272
  continue
253
273
 
254
274
  if block.get("type") != "tool_result":
255
275
  continue
256
-
257
- # Check if this tool_result contains an image
276
+
258
277
  block_content = block.get("content", [])
259
- for k, content_item in enumerate(block_content):
278
+ for content_item_idx, content_item in enumerate(block_content):
260
279
  if not isinstance(content_item, dict):
261
280
  continue
262
281
 
263
- if content_item.get("type") == "image":
264
- # This is a screenshot, remove it
265
- if "source" in content_item and "data" in content_item["source"]:
266
- # Replace the base64 data with a placeholder to save space
267
- content_item["source"]["data"] = "[IMAGE DATA REMOVED]"
268
- screenshots_pruned += 1
269
-
270
- if screenshots_pruned >= num_to_prune:
271
- return
282
+ if content_item.get("type") == "image" and "source" in content_item:
283
+ image_blocks.append({
284
+ "msg_idx": msg_idx,
285
+ "content_idx": content_idx,
286
+ "block": block,
287
+ "content_item_idx": content_item_idx,
288
+ "content_item": content_item
289
+ })
290
+
291
+ # If we have more images than our limit, remove the oldest ones
292
+ if len(image_blocks) > max_images:
293
+ # Keep only the most recent ones (which are at the end of the list)
294
+ images_to_remove = image_blocks[:-max_images]
295
+
296
+ for img_block in images_to_remove:
297
+ content_item = img_block["content_item"]
298
+ if "source" in content_item and "data" in content_item["source"]:
299
+ # Replace the base64 data with a placeholder
300
+ content_item["source"]["data"] = "[IMAGE DATA REMOVED]"
272
301
 
273
302
  def _execute_tool(self,
274
303
  computer_id: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orgo
3
- Version: 0.0.11
3
+ Version: 0.0.12
4
4
  Summary: Computers for AI agents
5
5
  Author: Orgo Team
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  orgo/__init__.py,sha256=SjO41JOwyLmX8K6fKu7LAIOyBk8sAtQKRaNVu2wZVBE,108
2
2
  orgo/computer.py,sha256=3S-LoxhgDFiQRajOqeGZkzL93a0l09BEmLO3ADphFFI,7140
3
- orgo/prompt.py,sha256=XCB54dCAwYsQLTB3cTFoatTm8a5mDOwDlZEoyZGEMhA,17490
3
+ orgo/prompt.py,sha256=WQfde1DXO-W_E59yvSFXK6ct0WOyoy16duSjczm8QQQ,18907
4
4
  orgo/adapters/__init__.py,sha256=LMpWJGVHvvSrLPhCJrMSENYSJ8ifWIpTfi88L6aN65I,219
5
5
  orgo/adapters/anthropic.py,sha256=KfPD5YWbwECZkpj421rSLCQf9SMr-EJ4nePe2EMXDWA,2342
6
6
  orgo/adapters/base.py,sha256=W1dNArKn1ri-X_36KuN-h5yxh1LmjIYCZMkKi9dm020,559
@@ -9,7 +9,7 @@ orgo/api/__init__.py,sha256=nE9fyIZw2q4mGqy063-1RAbBgy_Qhx11gN_swkhmbX8,86
9
9
  orgo/api/client.py,sha256=xMTE6kBN9y9ceiTwxkZOg8pi15a2mYk_-AfSBKV6wUg,4264
10
10
  orgo/utils/__init__.py,sha256=XSx9W-IPAc7yDnkwgED4v5eBUIQCxmokr_VQzF6LOZs,94
11
11
  orgo/utils/auth.py,sha256=mpnaOvM3BGIdZ_j9cTw8K34gPpCeLRrG0rZfoojzONc,484
12
- orgo-0.0.11.dist-info/METADATA,sha256=FlzfZeC5pIiF3gEFDSpVEUMpBp4hZ5c8S_Q908drSq0,738
13
- orgo-0.0.11.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
14
- orgo-0.0.11.dist-info/top_level.txt,sha256=q0rYtFji8GbYuhFW8A5Ab9e0j27761IKPhnL0E9xow4,5
15
- orgo-0.0.11.dist-info/RECORD,,
12
+ orgo-0.0.12.dist-info/METADATA,sha256=ZrleYHDguovTMCfi36X_tyrAJMS-nlaLpFaGE9tl9zM,738
13
+ orgo-0.0.12.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
14
+ orgo-0.0.12.dist-info/top_level.txt,sha256=q0rYtFji8GbYuhFW8A5Ab9e0j27761IKPhnL0E9xow4,5
15
+ orgo-0.0.12.dist-info/RECORD,,
File without changes