cua-agent 0.1.40__py3-none-any.whl → 0.1.42__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/core/base.py CHANGED
@@ -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
  ###########################################
agent/ui/gradio/app.py CHANGED
@@ -1018,12 +1018,18 @@ if __name__ == "__main__":
1018
1018
  model_string_to_analyze = model_choice_value # Use the full UI string initially
1019
1019
 
1020
1020
  try:
1021
- # Special case for UITARS - use MLXVLM provider
1021
+ # Special case for UITARS - use MLXVLM provider or OAICOMPAT for custom
1022
1022
  if agent_loop_choice == "UITARS":
1023
- provider = LLMProvider.MLXVLM
1024
- cleaned_model_name_from_func = model_string_to_analyze
1025
- agent_loop_type = AgentLoop.UITARS
1026
- print(f"Using MLXVLM provider for UITARS model: {model_string_to_analyze}")
1023
+ if is_custom_openai_api:
1024
+ provider = LLMProvider.OAICOMPAT
1025
+ cleaned_model_name_from_func = custom_model_value
1026
+ agent_loop_type = AgentLoop.UITARS
1027
+ print(f"Using OAICOMPAT provider for custom UITARS model: {custom_model_value}")
1028
+ else:
1029
+ provider = LLMProvider.MLXVLM
1030
+ cleaned_model_name_from_func = model_string_to_analyze
1031
+ agent_loop_type = AgentLoop.UITARS
1032
+ print(f"Using MLXVLM provider for UITARS model: {model_string_to_analyze}")
1027
1033
  # Special case for Ollama custom model
1028
1034
  elif is_custom_ollama and agent_loop_choice == "OMNI":
1029
1035
  provider = LLMProvider.OLLAMA
@@ -1046,8 +1052,8 @@ if __name__ == "__main__":
1046
1052
  else cleaned_model_name_from_func
1047
1053
  )
1048
1054
 
1049
- # Determine if OAICOMPAT should be used (only for OpenAI compatible API custom model)
1050
- is_oaicompat = is_custom_openai_api and agent_loop_choice != "UITARS"
1055
+ # Determine if OAICOMPAT should be used (for OpenAI compatible API custom model)
1056
+ is_oaicompat = is_custom_openai_api
1051
1057
 
1052
1058
  # Get API key based on provider determined by get_provider_and_model
1053
1059
  if is_oaicompat and custom_api_key:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cua-agent
3
- Version: 0.1.40
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
@@ -1,7 +1,7 @@
1
1
  agent/__init__.py,sha256=guFGtorDBF6R5hVep0Bvci3_sUJfBlcsq9ss5Kwrej8,1484
2
2
  agent/core/__init__.py,sha256=7DhJ_6KKooM6uTmDIlumCnd7OFcU67BYIIR1dpIYUB0,506
3
3
  agent/core/agent.py,sha256=HUfBe7Uam3TObAmf6KH0GDKuNCNunNmmMcuxS7aZg0Q,8332
4
- agent/core/base.py,sha256=2sg8B2VqUKImRlkLTNj5lx-Oarlu7_GoMR6MbNzSY9Q,8078
4
+ agent/core/base.py,sha256=AiSjnBAcHhZIca4KWBP1vQRE3HyikAPkr4Ij9WDevZQ,8374
5
5
  agent/core/callbacks.py,sha256=FKAxyajJ-ZJ5SxNXoupNcrm0GYBgjOjJEsStqst0EAk,6453
6
6
  agent/core/experiment.py,sha256=Ywj6q3JZFDKicfPuQsDl0vSN55HS7-Cnk3u3EcUCKe8,8866
7
7
  agent/core/factory.py,sha256=zzlCdibctqhf8Uta-SrvE-G7h59wAw-7SGhHiGvS9GY,4608
@@ -25,7 +25,7 @@ agent/providers/anthropic/api/logging.py,sha256=vHpwkIyOZdkSTVIH4ycbBPd4a_rzhP7O
25
25
  agent/providers/anthropic/api_handler.py,sha256=pWXcqDs0ruviDhRNRrz5Ac9ZH4yDv6ZlwpeG3a42cDg,5206
26
26
  agent/providers/anthropic/callbacks/__init__.py,sha256=PciBb6Z6MKSwfXqDjU3pV_0FS4MOn_Np_A7_skD-6dA,104
27
27
  agent/providers/anthropic/callbacks/manager.py,sha256=euIah5yiM8nhisN-RWXewo6v0WQr0c-FbMBO04r6dJk,1865
28
- agent/providers/anthropic/loop.py,sha256=A9ce3q5a4SQfOKVUk0En4x6fuc8a8s9wXFlQg4ypIkg,20394
28
+ agent/providers/anthropic/loop.py,sha256=Sepfo0b0oQT98xd3Sv2S7Xc81bfU7L4_Zv3VTiapKkg,21661
29
29
  agent/providers/anthropic/prompts.py,sha256=nHFfgPrfvnWrEdVP7EUBGUHAI85D2X9HeZirk9EwncU,1941
30
30
  agent/providers/anthropic/response_handler.py,sha256=ZTprV4NTP9Eb9jQ7QgEKZBX0L6rMj5nqBRiE3Zfws8I,8008
31
31
  agent/providers/anthropic/tools/__init__.py,sha256=JyZwuVtPUnZwRSZBSCdQv9yxbLCsygm3l8Ywjjt9qTQ,661
@@ -47,7 +47,7 @@ agent/providers/omni/clients/ollama.py,sha256=PmR5EhU9Mi43_o5mZN36XcpiGKp5HbQwlX
47
47
  agent/providers/omni/clients/openai.py,sha256=iTSYWEJEM8INFPGJMiUVs8rFn0781XF_ofRkd7NT3gk,5920
48
48
  agent/providers/omni/clients/utils.py,sha256=Ani9CVVBm_J2Dl51WG6p1GVuoI6cq8scISrG0pmQ37o,688
49
49
  agent/providers/omni/image_utils.py,sha256=wejhWb36yqedsPnLFTFwk2wth8a6txfVWSg4EaNrRdA,908
50
- agent/providers/omni/loop.py,sha256=3eL80w2btw7Gt9FpvJjZeHf97fnDSzYpTi1hBjzRjIk,40929
50
+ agent/providers/omni/loop.py,sha256=ecsDEUm9KPeBJVznXfWVyw4_YhbbslEjDkMMKDzUDwI,42973
51
51
  agent/providers/omni/parser.py,sha256=REpQwlwvY1z_N8wbMj6GhOeTiiWVWHhVja_LOxgzbks,11734
52
52
  agent/providers/omni/prompts.py,sha256=Mupjy0bUwBjcAeLXpE1r1jisYPSlhwsp-IXJKEKrEtw,3779
53
53
  agent/providers/omni/tools/__init__.py,sha256=IC1cMEDoR2ljGcNNthzBRF_VtnDbRL5qvHJWErtNp98,774
@@ -58,7 +58,7 @@ agent/providers/omni/tools/manager.py,sha256=UhtasaxGcmkxtz-bP1UJ1a4xdYnD3Cv8Pbt
58
58
  agent/providers/omni/utils.py,sha256=Ikp6ONL1HO637o3KDtv5yv6q-4uIWAzMSQDvGetWXC8,8724
59
59
  agent/providers/openai/__init__.py,sha256=8DS6YNZp42NLCacwXsfRaghyczaOCVovX8TgzXUZf_o,165
60
60
  agent/providers/openai/api_handler.py,sha256=L1K56dR1j4JsX1sX4OFYeKoCUMM25Fwj2y9nqv8oOhw,17736
61
- agent/providers/openai/loop.py,sha256=yJrFBy6uTlYf-MMcq_Q-EKQxbdBrEueJIeMhVo5OV1E,19344
61
+ agent/providers/openai/loop.py,sha256=y0oLPYMjdjm0g5sCg3zF28FnMLaO2JdN6FiDdVK_77Q,20602
62
62
  agent/providers/openai/response_handler.py,sha256=K8v_92uSr9R74Y5INY4naeEZZZm35CLIl4h74MBZhsw,7953
63
63
  agent/providers/openai/tools/__init__.py,sha256=-KbHMWcd2OVTk5RYQ3ACBEMygwbH-VW6n_98p0lwM4A,344
64
64
  agent/providers/openai/tools/base.py,sha256=Np_BC9Cm6TslK99etE9hVTtsBlcEaGhoNCK3NXdB_Lw,2474
@@ -70,7 +70,7 @@ agent/providers/uitars/__init__.py,sha256=sq5OMVJP9E_sok9tIiKJreGkjmNWXPMObjPTCl
70
70
  agent/providers/uitars/clients/base.py,sha256=5w8Ajmq1JiPyUQJUAq1lSkfpA8_Ts80NQiDxPMTtQrI,948
71
71
  agent/providers/uitars/clients/mlxvlm.py,sha256=lMnN6ecMmWHf_l7khJ2iJHHvT7PE4XagUjrWhB0zEhc,10893
72
72
  agent/providers/uitars/clients/oaicompat.py,sha256=uYjwrGCVpFi8wj4kcaJ905ABiY6ksJZXaLlM61B2DUA,8907
73
- agent/providers/uitars/loop.py,sha256=4-cgQteixPy03vp7xWezd6jWpuPkBmlLS3tizaOmd0U,23494
73
+ agent/providers/uitars/loop.py,sha256=cyzLGg5BEPOLHJk_gnoSLEy2BFnBXVgGry3TUvMX0_A,25467
74
74
  agent/providers/uitars/prompts.py,sha256=_pQNd438mFpZKZT0aMl6Bd0_GgQxuy9y08kQAMPi9UM,2536
75
75
  agent/providers/uitars/tools/__init__.py,sha256=0hc3W6u5TvcXYztYKIyve_C2G3XMfwt_y7grmH0ZHC0,29
76
76
  agent/providers/uitars/tools/computer.py,sha256=TeIg_aCtMroxWOBJEiYY_YI4krW_C3pYu51tgGsVUYU,11808
@@ -79,8 +79,8 @@ agent/providers/uitars/utils.py,sha256=493STTEEJcVhVbQgR0e8rNTI1DjkxUx8IgIv3wkJ1
79
79
  agent/telemetry.py,sha256=pVGxbj0ewnvq4EGj28CydN4a1iOfvZR_XKL3vIOqhOM,390
80
80
  agent/ui/__init__.py,sha256=ohhxJLBin6k1hl5sKcmBST8mgh23WXgAXz3pN4f470E,45
81
81
  agent/ui/gradio/__init__.py,sha256=ANKZhv1HqsLheWbLVBlyRQ7Q5qGeXuPi5jDs8vu-ZMo,579
82
- agent/ui/gradio/app.py,sha256=M_pqSiN40F1u-8luBdqvTJxQFGzqd0WPsSz8APLbPus,67826
83
- cua_agent-0.1.40.dist-info/METADATA,sha256=7ZNaoMyIfm5l_vnWUYNhcNxplhPj9_ocnfWF9sgN9yk,12689
84
- cua_agent-0.1.40.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
85
- cua_agent-0.1.40.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
86
- cua_agent-0.1.40.dist-info/RECORD,,
82
+ agent/ui/gradio/app.py,sha256=-ccsE6LrXFfxnPeMlEqm49QGvdjCgm-l6TudZZEM9r0,68241
83
+ cua_agent-0.1.42.dist-info/METADATA,sha256=rW2ijMboeUh6BKDZopZOuR01Fo2946tT0ftyDlwXL0k,12689
84
+ cua_agent-0.1.42.dist-info/WHEEL,sha256=tSfRZzRHthuv7vxpI4aehrdN9scLjk-dCJkPLzkHxGg,90
85
+ cua_agent-0.1.42.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
86
+ cua_agent-0.1.42.dist-info/RECORD,,