cua-agent 0.1.42__tar.gz → 0.1.43__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.42 → cua_agent-0.1.43}/PKG-INFO +1 -1
  2. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/loop.py +42 -18
  3. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/loop.py +4 -4
  4. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/loop.py +114 -87
  5. {cua_agent-0.1.42 → cua_agent-0.1.43}/pyproject.toml +3 -3
  6. {cua_agent-0.1.42 → cua_agent-0.1.43}/README.md +0 -0
  7. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/__init__.py +0 -0
  8. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/__init__.py +0 -0
  9. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/agent.py +0 -0
  10. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/base.py +0 -0
  11. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/callbacks.py +0 -0
  12. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/experiment.py +0 -0
  13. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/factory.py +0 -0
  14. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/messages.py +0 -0
  15. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/provider_config.py +0 -0
  16. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/telemetry.py +0 -0
  17. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/__init__.py +0 -0
  18. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/base.py +0 -0
  19. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/bash.py +0 -0
  20. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/collection.py +0 -0
  21. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/computer.py +0 -0
  22. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/edit.py +0 -0
  23. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools/manager.py +0 -0
  24. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/tools.py +0 -0
  25. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/types.py +0 -0
  26. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/core/visualization.py +0 -0
  27. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/__init__.py +0 -0
  28. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/__init__.py +0 -0
  29. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/api/client.py +0 -0
  30. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/api/logging.py +0 -0
  31. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/api_handler.py +0 -0
  32. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/callbacks/__init__.py +0 -0
  33. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/callbacks/manager.py +0 -0
  34. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/loop.py +0 -0
  35. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/prompts.py +0 -0
  36. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/response_handler.py +0 -0
  37. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/__init__.py +0 -0
  38. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/base.py +0 -0
  39. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/bash.py +0 -0
  40. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/collection.py +0 -0
  41. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/computer.py +0 -0
  42. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/edit.py +0 -0
  43. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/manager.py +0 -0
  44. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/tools/run.py +0 -0
  45. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/types.py +0 -0
  46. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/anthropic/utils.py +0 -0
  47. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/__init__.py +0 -0
  48. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/api_handler.py +0 -0
  49. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/clients/anthropic.py +0 -0
  50. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/clients/base.py +0 -0
  51. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/clients/oaicompat.py +0 -0
  52. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/clients/ollama.py +0 -0
  53. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/clients/openai.py +0 -0
  54. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/clients/utils.py +0 -0
  55. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/image_utils.py +0 -0
  56. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/parser.py +0 -0
  57. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/prompts.py +0 -0
  58. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/tools/__init__.py +0 -0
  59. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/tools/base.py +0 -0
  60. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/tools/bash.py +0 -0
  61. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/tools/computer.py +0 -0
  62. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/tools/manager.py +0 -0
  63. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/omni/utils.py +0 -0
  64. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/__init__.py +0 -0
  65. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/api_handler.py +0 -0
  66. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/response_handler.py +0 -0
  67. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/tools/__init__.py +0 -0
  68. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/tools/base.py +0 -0
  69. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/tools/computer.py +0 -0
  70. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/tools/manager.py +0 -0
  71. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/types.py +0 -0
  72. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/openai/utils.py +0 -0
  73. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/__init__.py +0 -0
  74. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/clients/base.py +0 -0
  75. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/clients/mlxvlm.py +0 -0
  76. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/clients/oaicompat.py +0 -0
  77. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/prompts.py +0 -0
  78. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/tools/__init__.py +0 -0
  79. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/tools/computer.py +0 -0
  80. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/tools/manager.py +0 -0
  81. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/providers/uitars/utils.py +0 -0
  82. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/telemetry.py +0 -0
  83. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/ui/__init__.py +0 -0
  84. {cua_agent-0.1.42 → cua_agent-0.1.43}/agent/ui/gradio/__init__.py +0 -0
  85. {cua_agent-0.1.42 → cua_agent-0.1.43}/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.42
3
+ Version: 0.1.43
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
@@ -581,17 +581,40 @@ class OmniLoop(BaseLoop):
581
581
  Yields:
582
582
  Agent response format
583
583
  """
584
- # Initialize the message manager with the provided messages
585
- self.message_manager.messages = messages.copy()
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))
590
-
591
- # Yield from the loop task
592
584
  try:
593
- async for response in self.loop_task:
594
- yield response
585
+ logger.info(f"Starting OmniLoop run with {len(messages)} messages")
586
+
587
+ # Initialize the message manager with the provided messages
588
+ self.message_manager.messages = messages.copy()
589
+
590
+ # Create queue for response streaming
591
+ queue = asyncio.Queue()
592
+
593
+ # Start loop in background task
594
+ self.loop_task = asyncio.create_task(self._run_loop(queue, messages))
595
+
596
+ # Process and yield messages as they arrive
597
+ while True:
598
+ try:
599
+ item = await queue.get()
600
+ if item is None: # Stop signal
601
+ break
602
+ yield item
603
+ queue.task_done()
604
+ except Exception as e:
605
+ logger.error(f"Error processing queue item: {str(e)}")
606
+ continue
607
+
608
+ # Wait for loop to complete
609
+ await self.loop_task
610
+
611
+ # Send completion message
612
+ yield {
613
+ "role": "assistant",
614
+ "content": "Task completed successfully.",
615
+ "metadata": {"title": "✅ Complete"},
616
+ }
617
+
595
618
  except Exception as e:
596
619
  logger.error(f"Error in run method: {str(e)}")
597
620
  yield {
@@ -600,14 +623,12 @@ class OmniLoop(BaseLoop):
600
623
  "metadata": {"title": "❌ Error"},
601
624
  }
602
625
 
603
- async def _run_loop(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[AgentResponse, None]:
626
+ async def _run_loop(self, queue: asyncio.Queue, messages: List[Dict[str, Any]]) -> None:
604
627
  """Internal method to run the agent loop with provided messages.
605
628
 
606
629
  Args:
630
+ queue: Queue to put responses into
607
631
  messages: List of messages in standard OpenAI format
608
-
609
- Yields:
610
- Agent response format
611
632
  """
612
633
  # Continue running until explicitly told to stop
613
634
  running = True
@@ -698,8 +719,8 @@ class OmniLoop(BaseLoop):
698
719
  # Log standardized response for ease of parsing
699
720
  self._log_api_call("agent_response", request=None, response=openai_compatible_response)
700
721
 
701
- # Yield the response to the caller
702
- yield openai_compatible_response
722
+ # Put the response in the queue
723
+ await queue.put(openai_compatible_response)
703
724
 
704
725
  # Check if we should continue this conversation
705
726
  running = should_continue
@@ -720,14 +741,17 @@ class OmniLoop(BaseLoop):
720
741
  if attempt >= max_attempts:
721
742
  logger.error(f"Maximum retry attempts reached. Last error was: {str(e)}")
722
743
 
723
- yield {
744
+ await queue.put({
724
745
  "role": "assistant",
725
746
  "content": f"Error: {str(e)}",
726
747
  "metadata": {"title": "❌ Error"},
727
- }
748
+ })
728
749
 
729
750
  # Create a brief delay before retrying
730
751
  await asyncio.sleep(1)
752
+ finally:
753
+ # Signal that we're done
754
+ await queue.put(None)
731
755
 
732
756
  async def cancel(self) -> None:
733
757
  """Cancel the currently running agent loop task.
@@ -133,22 +133,22 @@ class OpenAILoop(BaseLoop):
133
133
  logger.info("Starting OpenAI loop run")
134
134
 
135
135
  # Create queue for response streaming
136
- queue = asyncio.Queue()
136
+ self.queue = asyncio.Queue()
137
137
 
138
138
  # Ensure tool manager is initialized
139
139
  await self.tool_manager.initialize()
140
140
 
141
141
  # Start loop in background task
142
- self.loop_task = asyncio.create_task(self._run_loop(queue, messages))
142
+ self.loop_task = asyncio.create_task(self._run_loop(self.queue, messages))
143
143
 
144
144
  # Process and yield messages as they arrive
145
145
  while True:
146
146
  try:
147
- item = await queue.get()
147
+ item = await self.queue.get()
148
148
  if item is None: # Stop signal
149
149
  break
150
150
  yield item
151
- queue.task_done()
151
+ self.queue.task_done()
152
152
  except Exception as e:
153
153
  logger.error(f"Error processing queue item: {str(e)}")
154
154
  continue
@@ -463,17 +463,40 @@ class UITARSLoop(BaseLoop):
463
463
  Yields:
464
464
  Agent response format
465
465
  """
466
- # Initialize the message manager with the provided messages
467
- self.message_manager.messages = messages.copy()
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))
472
-
473
- # Yield from the loop task
474
466
  try:
475
- async for response in self.loop_task:
476
- yield response
467
+ logger.info(f"Starting UITARSLoop run with {len(messages)} messages")
468
+
469
+ # Initialize the message manager with the provided messages
470
+ self.message_manager.messages = messages.copy()
471
+
472
+ # Create queue for response streaming
473
+ queue = asyncio.Queue()
474
+
475
+ # Start loop in background task
476
+ self.loop_task = asyncio.create_task(self._run_loop(queue, messages))
477
+
478
+ # Process and yield messages as they arrive
479
+ while True:
480
+ try:
481
+ item = await queue.get()
482
+ if item is None: # Stop signal
483
+ break
484
+ yield item
485
+ queue.task_done()
486
+ except Exception as e:
487
+ logger.error(f"Error processing queue item: {str(e)}")
488
+ continue
489
+
490
+ # Wait for loop to complete
491
+ await self.loop_task
492
+
493
+ # Send completion message
494
+ yield {
495
+ "role": "assistant",
496
+ "content": "Task completed successfully.",
497
+ "metadata": {"title": "✅ Complete"},
498
+ }
499
+
477
500
  except Exception as e:
478
501
  logger.error(f"Error in run method: {str(e)}")
479
502
  yield {
@@ -482,14 +505,12 @@ class UITARSLoop(BaseLoop):
482
505
  "metadata": {"title": "❌ Error"},
483
506
  }
484
507
 
485
- async def _run_loop(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[AgentResponse, None]:
508
+ async def _run_loop(self, queue: asyncio.Queue, messages: List[Dict[str, Any]]) -> None:
486
509
  """Internal method to run the agent loop with provided messages.
487
510
 
488
511
  Args:
512
+ queue: Queue to put responses into
489
513
  messages: List of messages in standard OpenAI format
490
-
491
- Yields:
492
- Agent response format
493
514
  """
494
515
  # Continue running until explicitly told to stop
495
516
  running = True
@@ -500,88 +521,94 @@ class UITARSLoop(BaseLoop):
500
521
  attempt = 0
501
522
  max_attempts = 3
502
523
 
503
- while running and attempt < max_attempts:
504
- try:
505
- # Create a new turn directory if it's not already created
506
- if not turn_created:
507
- self._create_turn_dir()
508
- turn_created = True
524
+ try:
525
+ while running and attempt < max_attempts:
526
+ try:
527
+ # Create a new turn directory if it's not already created
528
+ if not turn_created:
529
+ self._create_turn_dir()
530
+ turn_created = True
509
531
 
510
- # Ensure client is initialized
511
- if self.client is None:
512
- logger.info("Initializing client...")
513
- await self.initialize_client()
532
+ # Ensure client is initialized
514
533
  if self.client is None:
515
- raise RuntimeError("Failed to initialize client")
516
- logger.info("Client initialized successfully")
517
-
518
- # Get current screen
519
- base64_screenshot = await self._get_current_screen()
520
-
521
- # Add screenshot to message history
522
- self.message_manager.add_user_message(
523
- [
524
- {
525
- "type": "image_url",
526
- "image_url": {"url": f"data:image/png;base64,{base64_screenshot}"},
527
- }
528
- ]
529
- )
530
- logger.info("Added screenshot to message history")
531
-
532
- # Get system prompt
533
- system_prompt = self._get_system_prompt()
534
+ logger.info("Initializing client...")
535
+ await self.initialize_client()
536
+ if self.client is None:
537
+ raise RuntimeError("Failed to initialize client")
538
+ logger.info("Client initialized successfully")
539
+
540
+ # Get current screen
541
+ base64_screenshot = await self._get_current_screen()
542
+
543
+ # Add screenshot to message history
544
+ self.message_manager.add_user_message(
545
+ [
546
+ {
547
+ "type": "image_url",
548
+ "image_url": {"url": f"data:image/png;base64,{base64_screenshot}"},
549
+ }
550
+ ]
551
+ )
552
+ logger.info("Added screenshot to message history")
534
553
 
535
- # Make API call with retries
536
- response = await self._make_api_call(
537
- self.message_manager.messages, system_prompt
538
- )
554
+ # Get system prompt
555
+ system_prompt = self._get_system_prompt()
539
556
 
540
- # Handle the response (may execute actions)
541
- # Returns: (should_continue, action_screenshot_saved)
542
- should_continue, new_screenshot_saved = await self._handle_response(
543
- response, self.message_manager.messages
544
- )
557
+ # Make API call with retries
558
+ response = await self._make_api_call(
559
+ self.message_manager.messages, system_prompt
560
+ )
545
561
 
546
- # Update whether an action screenshot was saved this turn
547
- action_screenshot_saved = action_screenshot_saved or new_screenshot_saved
548
-
549
- agent_response = await to_agent_response_format(
550
- response,
551
- messages,
552
- model=self.model,
553
- )
554
- # Log standardized response for ease of parsing
555
- self._log_api_call("agent_response", request=None, response=agent_response)
556
- yield agent_response
557
-
558
- # Check if we should continue this conversation
559
- running = should_continue
562
+ # Handle the response (may execute actions)
563
+ # Returns: (should_continue, action_screenshot_saved)
564
+ should_continue, new_screenshot_saved = await self._handle_response(
565
+ response, self.message_manager.messages
566
+ )
560
567
 
561
- # Create a new turn directory if we're continuing
562
- if running:
563
- turn_created = False
568
+ # Update whether an action screenshot was saved this turn
569
+ action_screenshot_saved = action_screenshot_saved or new_screenshot_saved
570
+
571
+ agent_response = await to_agent_response_format(
572
+ response,
573
+ messages,
574
+ model=self.model,
575
+ )
576
+ # Log standardized response for ease of parsing
577
+ self._log_api_call("agent_response", request=None, response=agent_response)
578
+
579
+ # Put the response in the queue
580
+ await queue.put(agent_response)
581
+
582
+ # Check if we should continue this conversation
583
+ running = should_continue
564
584
 
565
- # Reset attempt counter on success
566
- attempt = 0
585
+ # Create a new turn directory if we're continuing
586
+ if running:
587
+ turn_created = False
567
588
 
568
- except Exception as e:
569
- attempt += 1
570
- error_msg = f"Error in run method (attempt {attempt}/{max_attempts}): {str(e)}"
571
- logger.error(error_msg)
572
-
573
- # If this is our last attempt, provide more info about the error
574
- if attempt >= max_attempts:
575
- logger.error(f"Maximum retry attempts reached. Last error was: {str(e)}")
576
-
577
- yield {
578
- "role": "assistant",
579
- "content": f"Error: {str(e)}",
580
- "metadata": {"title": "❌ Error"},
581
- }
589
+ # Reset attempt counter on success
590
+ attempt = 0
582
591
 
583
- # Create a brief delay before retrying
584
- await asyncio.sleep(1)
592
+ except Exception as e:
593
+ attempt += 1
594
+ error_msg = f"Error in run method (attempt {attempt}/{max_attempts}): {str(e)}"
595
+ logger.error(error_msg)
596
+
597
+ # If this is our last attempt, provide more info about the error
598
+ if attempt >= max_attempts:
599
+ logger.error(f"Maximum retry attempts reached. Last error was: {str(e)}")
600
+
601
+ await queue.put({
602
+ "role": "assistant",
603
+ "content": f"Error: {str(e)}",
604
+ "metadata": {"title": "❌ Error"},
605
+ })
606
+
607
+ # Create a brief delay before retrying
608
+ await asyncio.sleep(1)
609
+ finally:
610
+ # Signal that we're done
611
+ await queue.put(None)
585
612
 
586
613
  async def cancel(self) -> None:
587
614
  """Cancel the currently running agent loop task.
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "cua-agent"
9
- version = "0.1.42"
9
+ version = "0.1.43"
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.42"
112
+ target-version = "0.1.43"
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.42"
126
+ python_version = "0.1.43"
127
127
  ignore_missing_imports = true
128
128
  disallow_untyped_defs = true
129
129
  check_untyped_defs = true
File without changes
File without changes