cua-agent 0.1.41__tar.gz → 0.1.42__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.

Files changed (85) hide show
  1. {cua_agent-0.1.41 → cua_agent-0.1.42}/PKG-INFO +1 -1
  2. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/base.py +9 -0
  3. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/loop.py +28 -2
  4. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/loop.py +51 -2
  5. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/loop.py +28 -2
  6. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/loop.py +48 -0
  7. {cua_agent-0.1.41 → cua_agent-0.1.42}/pyproject.toml +3 -3
  8. {cua_agent-0.1.41 → cua_agent-0.1.42}/README.md +0 -0
  9. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/__init__.py +0 -0
  10. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/__init__.py +0 -0
  11. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/agent.py +0 -0
  12. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/callbacks.py +0 -0
  13. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/experiment.py +0 -0
  14. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/factory.py +0 -0
  15. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/messages.py +0 -0
  16. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/provider_config.py +0 -0
  17. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/telemetry.py +0 -0
  18. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/__init__.py +0 -0
  19. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/base.py +0 -0
  20. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/bash.py +0 -0
  21. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/collection.py +0 -0
  22. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/computer.py +0 -0
  23. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/edit.py +0 -0
  24. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools/manager.py +0 -0
  25. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/tools.py +0 -0
  26. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/types.py +0 -0
  27. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/core/visualization.py +0 -0
  28. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/__init__.py +0 -0
  29. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/__init__.py +0 -0
  30. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/api/client.py +0 -0
  31. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/api/logging.py +0 -0
  32. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/api_handler.py +0 -0
  33. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/callbacks/__init__.py +0 -0
  34. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/callbacks/manager.py +0 -0
  35. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/prompts.py +0 -0
  36. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/response_handler.py +0 -0
  37. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/__init__.py +0 -0
  38. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/base.py +0 -0
  39. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/bash.py +0 -0
  40. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/collection.py +0 -0
  41. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/computer.py +0 -0
  42. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/edit.py +0 -0
  43. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/manager.py +0 -0
  44. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/tools/run.py +0 -0
  45. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/types.py +0 -0
  46. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/anthropic/utils.py +0 -0
  47. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/__init__.py +0 -0
  48. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/api_handler.py +0 -0
  49. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/clients/anthropic.py +0 -0
  50. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/clients/base.py +0 -0
  51. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/clients/oaicompat.py +0 -0
  52. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/clients/ollama.py +0 -0
  53. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/clients/openai.py +0 -0
  54. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/clients/utils.py +0 -0
  55. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/image_utils.py +0 -0
  56. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/parser.py +0 -0
  57. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/prompts.py +0 -0
  58. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/tools/__init__.py +0 -0
  59. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/tools/base.py +0 -0
  60. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/tools/bash.py +0 -0
  61. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/tools/computer.py +0 -0
  62. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/tools/manager.py +0 -0
  63. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/omni/utils.py +0 -0
  64. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/__init__.py +0 -0
  65. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/api_handler.py +0 -0
  66. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/response_handler.py +0 -0
  67. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/tools/__init__.py +0 -0
  68. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/tools/base.py +0 -0
  69. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/tools/computer.py +0 -0
  70. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/tools/manager.py +0 -0
  71. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/types.py +0 -0
  72. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/openai/utils.py +0 -0
  73. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/__init__.py +0 -0
  74. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/clients/base.py +0 -0
  75. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/clients/mlxvlm.py +0 -0
  76. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/clients/oaicompat.py +0 -0
  77. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/prompts.py +0 -0
  78. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/tools/__init__.py +0 -0
  79. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/tools/computer.py +0 -0
  80. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/tools/manager.py +0 -0
  81. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/providers/uitars/utils.py +0 -0
  82. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/telemetry.py +0 -0
  83. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/ui/__init__.py +0 -0
  84. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/ui/gradio/__init__.py +0 -0
  85. {cua_agent-0.1.41 → cua_agent-0.1.42}/agent/ui/gradio/app.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cua-agent
3
- Version: 0.1.41
3
+ Version: 0.1.42
4
4
  Summary: CUA (Computer Use) Agent for AI-driven computer interaction
5
5
  Author-Email: TryCua <gh@trycua.com>
6
6
  Requires-Python: >=3.10
@@ -131,6 +131,15 @@ class BaseLoop(ABC):
131
131
  An async generator that yields agent responses
132
132
  """
133
133
  raise NotImplementedError
134
+
135
+ @abstractmethod
136
+ async def cancel(self) -> None:
137
+ """Cancel the currently running agent loop task.
138
+
139
+ This method should stop any ongoing processing in the agent loop
140
+ and clean up resources appropriately.
141
+ """
142
+ raise NotImplementedError
134
143
 
135
144
  ###########################################
136
145
  # EXPERIMENT AND TRAJECTORY MANAGEMENT
@@ -101,6 +101,7 @@ class AnthropicLoop(BaseLoop):
101
101
  self.tool_manager = None
102
102
  self.callback_manager = None
103
103
  self.queue = asyncio.Queue() # Initialize queue
104
+ self.loop_task = None # Store the loop task for cancellation
104
105
 
105
106
  # Initialize handlers
106
107
  self.api_handler = AnthropicAPIHandler(self)
@@ -169,7 +170,7 @@ class AnthropicLoop(BaseLoop):
169
170
  logger.info("Client initialized successfully")
170
171
 
171
172
  # Start loop in background task
172
- loop_task = asyncio.create_task(self._run_loop(queue, messages))
173
+ self.loop_task = asyncio.create_task(self._run_loop(queue, messages))
173
174
 
174
175
  # Process and yield messages as they arrive
175
176
  while True:
@@ -184,7 +185,7 @@ class AnthropicLoop(BaseLoop):
184
185
  continue
185
186
 
186
187
  # Wait for loop to complete
187
- await loop_task
188
+ await self.loop_task
188
189
 
189
190
  # Send completion message
190
191
  yield {
@@ -200,6 +201,31 @@ class AnthropicLoop(BaseLoop):
200
201
  "content": f"Error: {str(e)}",
201
202
  "metadata": {"title": "❌ Error"},
202
203
  }
204
+
205
+ async def cancel(self) -> None:
206
+ """Cancel the currently running agent loop task.
207
+
208
+ This method stops the ongoing processing in the agent loop
209
+ by cancelling the loop_task if it exists and is running.
210
+ """
211
+ if self.loop_task and not self.loop_task.done():
212
+ logger.info("Cancelling Anthropic loop task")
213
+ self.loop_task.cancel()
214
+ try:
215
+ # Wait for the task to be cancelled with a timeout
216
+ await asyncio.wait_for(self.loop_task, timeout=2.0)
217
+ except asyncio.TimeoutError:
218
+ logger.warning("Timeout while waiting for loop task to cancel")
219
+ except asyncio.CancelledError:
220
+ logger.info("Loop task cancelled successfully")
221
+ except Exception as e:
222
+ logger.error(f"Error while cancelling loop task: {str(e)}")
223
+ finally:
224
+ # Put None in the queue to signal any waiting consumers to stop
225
+ await self.queue.put(None)
226
+ logger.info("Anthropic loop task cancelled")
227
+ else:
228
+ logger.info("No active Anthropic loop task to cancel")
203
229
 
204
230
  ###########################################
205
231
  # AGENT LOOP IMPLEMENTATION
@@ -105,6 +105,7 @@ class OmniLoop(BaseLoop):
105
105
  # Set API client attributes
106
106
  self.client = None
107
107
  self.retry_count = 0
108
+ self.loop_task = None # Store the loop task for cancellation
108
109
 
109
110
  # Initialize handlers
110
111
  self.api_handler = OmniAPIHandler(loop=self)
@@ -583,7 +584,31 @@ class OmniLoop(BaseLoop):
583
584
  # Initialize the message manager with the provided messages
584
585
  self.message_manager.messages = messages.copy()
585
586
  logger.info(f"Starting OmniLoop run with {len(self.message_manager.messages)} messages")
587
+
588
+ # Create a task to run the loop
589
+ self.loop_task = asyncio.create_task(self._run_loop(messages))
586
590
 
591
+ # Yield from the loop task
592
+ try:
593
+ async for response in self.loop_task:
594
+ yield response
595
+ except Exception as e:
596
+ logger.error(f"Error in run method: {str(e)}")
597
+ yield {
598
+ "role": "assistant",
599
+ "content": f"Error: {str(e)}",
600
+ "metadata": {"title": "❌ Error"},
601
+ }
602
+
603
+ async def _run_loop(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[AgentResponse, None]:
604
+ """Internal method to run the agent loop with provided messages.
605
+
606
+ Args:
607
+ messages: List of messages in standard OpenAI format
608
+
609
+ Yields:
610
+ Agent response format
611
+ """
587
612
  # Continue running until explicitly told to stop
588
613
  running = True
589
614
  turn_created = False
@@ -688,7 +713,7 @@ class OmniLoop(BaseLoop):
688
713
 
689
714
  except Exception as e:
690
715
  attempt += 1
691
- error_msg = f"Error in run method (attempt {attempt}/{max_attempts}): {str(e)}"
716
+ error_msg = f"Error in _run_loop method (attempt {attempt}/{max_attempts}): {str(e)}"
692
717
  logger.error(error_msg)
693
718
 
694
719
  # If this is our last attempt, provide more info about the error
@@ -696,12 +721,36 @@ class OmniLoop(BaseLoop):
696
721
  logger.error(f"Maximum retry attempts reached. Last error was: {str(e)}")
697
722
 
698
723
  yield {
699
- "error": str(e),
724
+ "role": "assistant",
725
+ "content": f"Error: {str(e)}",
700
726
  "metadata": {"title": "❌ Error"},
701
727
  }
702
728
 
703
729
  # Create a brief delay before retrying
704
730
  await asyncio.sleep(1)
731
+
732
+ async def cancel(self) -> None:
733
+ """Cancel the currently running agent loop task.
734
+
735
+ This method stops the ongoing processing in the agent loop
736
+ by cancelling the loop_task if it exists and is running.
737
+ """
738
+ if self.loop_task and not self.loop_task.done():
739
+ logger.info("Cancelling Omni loop task")
740
+ self.loop_task.cancel()
741
+ try:
742
+ # Wait for the task to be cancelled with a timeout
743
+ await asyncio.wait_for(self.loop_task, timeout=2.0)
744
+ except asyncio.TimeoutError:
745
+ logger.warning("Timeout while waiting for loop task to cancel")
746
+ except asyncio.CancelledError:
747
+ logger.info("Loop task cancelled successfully")
748
+ except Exception as e:
749
+ logger.error(f"Error while cancelling loop task: {str(e)}")
750
+ finally:
751
+ logger.info("Omni loop task cancelled")
752
+ else:
753
+ logger.info("No active Omni loop task to cancel")
705
754
 
706
755
  async def process_model_response(self, response_text: str) -> Optional[Dict[str, Any]]:
707
756
  """Process model response to extract tool calls.
@@ -87,6 +87,7 @@ class OpenAILoop(BaseLoop):
87
87
  self.acknowledge_safety_check_callback = acknowledge_safety_check_callback
88
88
  self.queue = asyncio.Queue() # Initialize queue
89
89
  self.last_response_id = None # Store the last response ID across runs
90
+ self.loop_task = None # Store the loop task for cancellation
90
91
 
91
92
  # Initialize handlers
92
93
  self.api_handler = OpenAIAPIHandler(self)
@@ -138,7 +139,7 @@ class OpenAILoop(BaseLoop):
138
139
  await self.tool_manager.initialize()
139
140
 
140
141
  # Start loop in background task
141
- loop_task = asyncio.create_task(self._run_loop(queue, messages))
142
+ self.loop_task = asyncio.create_task(self._run_loop(queue, messages))
142
143
 
143
144
  # Process and yield messages as they arrive
144
145
  while True:
@@ -153,7 +154,7 @@ class OpenAILoop(BaseLoop):
153
154
  continue
154
155
 
155
156
  # Wait for loop to complete
156
- await loop_task
157
+ await self.loop_task
157
158
 
158
159
  # Send completion message
159
160
  yield {
@@ -169,6 +170,31 @@ class OpenAILoop(BaseLoop):
169
170
  "content": f"Error: {str(e)}",
170
171
  "metadata": {"title": "❌ Error"},
171
172
  }
173
+
174
+ async def cancel(self) -> None:
175
+ """Cancel the currently running agent loop task.
176
+
177
+ This method stops the ongoing processing in the agent loop
178
+ by cancelling the loop_task if it exists and is running.
179
+ """
180
+ if self.loop_task and not self.loop_task.done():
181
+ logger.info("Cancelling OpenAI loop task")
182
+ self.loop_task.cancel()
183
+ try:
184
+ # Wait for the task to be cancelled with a timeout
185
+ await asyncio.wait_for(self.loop_task, timeout=2.0)
186
+ except asyncio.TimeoutError:
187
+ logger.warning("Timeout while waiting for loop task to cancel")
188
+ except asyncio.CancelledError:
189
+ logger.info("Loop task cancelled successfully")
190
+ except Exception as e:
191
+ logger.error(f"Error while cancelling loop task: {str(e)}")
192
+ finally:
193
+ # Put None in the queue to signal any waiting consumers to stop
194
+ await self.queue.put(None)
195
+ logger.info("OpenAI loop task cancelled")
196
+ else:
197
+ logger.info("No active OpenAI loop task to cancel")
172
198
 
173
199
  ###########################################
174
200
  # AGENT LOOP IMPLEMENTATION
@@ -93,6 +93,7 @@ class UITARSLoop(BaseLoop):
93
93
  # Set API client attributes
94
94
  self.client = None
95
95
  self.retry_count = 0
96
+ self.loop_task = None # Store the loop task for cancellation
96
97
 
97
98
  # Initialize visualization helper
98
99
  self.viz_helper = VisualizationHelper(agent=self)
@@ -465,7 +466,31 @@ class UITARSLoop(BaseLoop):
465
466
  # Initialize the message manager with the provided messages
466
467
  self.message_manager.messages = messages.copy()
467
468
  logger.info(f"Starting UITARSLoop run with {len(self.message_manager.messages)} messages")
469
+
470
+ # Create a task to run the loop
471
+ self.loop_task = asyncio.create_task(self._run_loop(messages))
468
472
 
473
+ # Yield from the loop task
474
+ try:
475
+ async for response in self.loop_task:
476
+ yield response
477
+ except Exception as e:
478
+ logger.error(f"Error in run method: {str(e)}")
479
+ yield {
480
+ "role": "assistant",
481
+ "content": f"Error: {str(e)}",
482
+ "metadata": {"title": "❌ Error"},
483
+ }
484
+
485
+ async def _run_loop(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[AgentResponse, None]:
486
+ """Internal method to run the agent loop with provided messages.
487
+
488
+ Args:
489
+ messages: List of messages in standard OpenAI format
490
+
491
+ Yields:
492
+ Agent response format
493
+ """
469
494
  # Continue running until explicitly told to stop
470
495
  running = True
471
496
  turn_created = False
@@ -558,6 +583,29 @@ class UITARSLoop(BaseLoop):
558
583
  # Create a brief delay before retrying
559
584
  await asyncio.sleep(1)
560
585
 
586
+ async def cancel(self) -> None:
587
+ """Cancel the currently running agent loop task.
588
+
589
+ This method stops the ongoing processing in the agent loop
590
+ by cancelling the loop_task if it exists and is running.
591
+ """
592
+ if self.loop_task and not self.loop_task.done():
593
+ logger.info("Cancelling UITARS loop task")
594
+ self.loop_task.cancel()
595
+ try:
596
+ # Wait for the task to be cancelled with a timeout
597
+ await asyncio.wait_for(self.loop_task, timeout=2.0)
598
+ except asyncio.TimeoutError:
599
+ logger.warning("Timeout while waiting for loop task to cancel")
600
+ except asyncio.CancelledError:
601
+ logger.info("Loop task cancelled successfully")
602
+ except Exception as e:
603
+ logger.error(f"Error while cancelling loop task: {str(e)}")
604
+ finally:
605
+ logger.info("UITARS loop task cancelled")
606
+ else:
607
+ logger.info("No active UITARS loop task to cancel")
608
+
561
609
  ###########################################
562
610
  # UTILITY METHODS
563
611
  ###########################################
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "cua-agent"
9
- version = "0.1.41"
9
+ version = "0.1.42"
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.1.41"
112
+ target-version = "0.1.42"
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.1.41"
126
+ python_version = "0.1.42"
127
127
  ignore_missing_imports = true
128
128
  disallow_untyped_defs = true
129
129
  check_untyped_defs = true
File without changes
File without changes