cua-agent 0.4.34__py3-none-any.whl → 0.4.36__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 (61) hide show
  1. agent/__init__.py +4 -10
  2. agent/__main__.py +2 -1
  3. agent/adapters/huggingfacelocal_adapter.py +54 -61
  4. agent/adapters/human_adapter.py +116 -114
  5. agent/adapters/mlxvlm_adapter.py +110 -99
  6. agent/adapters/models/__init__.py +14 -6
  7. agent/adapters/models/generic.py +7 -4
  8. agent/adapters/models/internvl.py +66 -30
  9. agent/adapters/models/opencua.py +23 -8
  10. agent/adapters/models/qwen2_5_vl.py +7 -4
  11. agent/agent.py +184 -158
  12. agent/callbacks/__init__.py +4 -4
  13. agent/callbacks/base.py +45 -31
  14. agent/callbacks/budget_manager.py +22 -10
  15. agent/callbacks/image_retention.py +18 -13
  16. agent/callbacks/logging.py +55 -42
  17. agent/callbacks/operator_validator.py +3 -1
  18. agent/callbacks/pii_anonymization.py +19 -16
  19. agent/callbacks/telemetry.py +67 -61
  20. agent/callbacks/trajectory_saver.py +90 -70
  21. agent/cli.py +115 -110
  22. agent/computers/__init__.py +13 -8
  23. agent/computers/base.py +32 -19
  24. agent/computers/cua.py +33 -25
  25. agent/computers/custom.py +78 -71
  26. agent/decorators.py +23 -14
  27. agent/human_tool/__init__.py +2 -7
  28. agent/human_tool/__main__.py +6 -2
  29. agent/human_tool/server.py +48 -37
  30. agent/human_tool/ui.py +235 -185
  31. agent/integrations/hud/__init__.py +15 -21
  32. agent/integrations/hud/agent.py +101 -83
  33. agent/integrations/hud/proxy.py +90 -57
  34. agent/loops/__init__.py +25 -21
  35. agent/loops/anthropic.py +537 -483
  36. agent/loops/base.py +13 -14
  37. agent/loops/composed_grounded.py +135 -149
  38. agent/loops/gemini.py +31 -12
  39. agent/loops/glm45v.py +135 -133
  40. agent/loops/gta1.py +47 -50
  41. agent/loops/holo.py +4 -2
  42. agent/loops/internvl.py +6 -11
  43. agent/loops/moondream3.py +36 -12
  44. agent/loops/omniparser.py +215 -210
  45. agent/loops/openai.py +49 -50
  46. agent/loops/opencua.py +29 -41
  47. agent/loops/qwen.py +510 -0
  48. agent/loops/uitars.py +237 -202
  49. agent/proxy/examples.py +54 -50
  50. agent/proxy/handlers.py +27 -34
  51. agent/responses.py +330 -330
  52. agent/types.py +11 -5
  53. agent/ui/__init__.py +1 -1
  54. agent/ui/__main__.py +1 -1
  55. agent/ui/gradio/app.py +23 -18
  56. agent/ui/gradio/ui_components.py +310 -161
  57. {cua_agent-0.4.34.dist-info → cua_agent-0.4.36.dist-info}/METADATA +18 -10
  58. cua_agent-0.4.36.dist-info/RECORD +64 -0
  59. cua_agent-0.4.34.dist-info/RECORD +0 -63
  60. {cua_agent-0.4.34.dist-info → cua_agent-0.4.36.dist-info}/WHEEL +0 -0
  61. {cua_agent-0.4.34.dist-info → cua_agent-0.4.36.dist-info}/entry_points.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  import asyncio
2
2
  import uuid
3
+ from dataclasses import asdict, dataclass
3
4
  from datetime import datetime
4
- from typing import Dict, List, Any, Optional
5
- from dataclasses import dataclass, asdict
6
5
  from enum import Enum
6
+ from typing import Any, Dict, List, Optional
7
7
 
8
8
  from fastapi import FastAPI, HTTPException
9
9
  from pydantic import BaseModel
@@ -49,7 +49,7 @@ class CompletionQueue:
49
49
  self._queue: Dict[str, CompletionCall] = {}
50
50
  self._pending_order: List[str] = []
51
51
  self._lock = asyncio.Lock()
52
-
52
+
53
53
  async def add_completion(self, messages: List[Dict[str, Any]], model: str) -> str:
54
54
  """Add a completion call to the queue."""
55
55
  async with self._lock:
@@ -59,42 +59,47 @@ class CompletionQueue:
59
59
  messages=messages,
60
60
  model=model,
61
61
  status=CompletionStatus.PENDING,
62
- created_at=datetime.now()
62
+ created_at=datetime.now(),
63
63
  )
64
64
  self._queue[call_id] = completion_call
65
65
  self._pending_order.append(call_id)
66
66
  return call_id
67
-
67
+
68
68
  async def get_pending_calls(self) -> List[Dict[str, Any]]:
69
69
  """Get all pending completion calls."""
70
70
  async with self._lock:
71
71
  pending_calls = []
72
72
  for call_id in self._pending_order:
73
- if call_id in self._queue and self._queue[call_id].status == CompletionStatus.PENDING:
73
+ if (
74
+ call_id in self._queue
75
+ and self._queue[call_id].status == CompletionStatus.PENDING
76
+ ):
74
77
  call = self._queue[call_id]
75
- pending_calls.append({
76
- "id": call.id,
77
- "model": call.model,
78
- "created_at": call.created_at.isoformat(),
79
- "messages": call.messages
80
- })
78
+ pending_calls.append(
79
+ {
80
+ "id": call.id,
81
+ "model": call.model,
82
+ "created_at": call.created_at.isoformat(),
83
+ "messages": call.messages,
84
+ }
85
+ )
81
86
  return pending_calls
82
-
87
+
83
88
  async def get_call_status(self, call_id: str) -> Optional[Dict[str, Any]]:
84
89
  """Get the status of a specific completion call."""
85
90
  async with self._lock:
86
91
  if call_id not in self._queue:
87
92
  return None
88
-
93
+
89
94
  call = self._queue[call_id]
90
95
  result = {
91
96
  "id": call.id,
92
97
  "status": call.status.value,
93
98
  "created_at": call.created_at.isoformat(),
94
99
  "model": call.model,
95
- "messages": call.messages
100
+ "messages": call.messages,
96
101
  }
97
-
102
+
98
103
  if call.completed_at:
99
104
  result["completed_at"] = call.completed_at.isoformat()
100
105
  if call.response:
@@ -103,69 +108,74 @@ class CompletionQueue:
103
108
  result["tool_calls"] = call.tool_calls
104
109
  if call.error:
105
110
  result["error"] = call.error
106
-
111
+
107
112
  return result
108
-
109
- async def complete_call(self, call_id: str, response: Optional[str] = None, tool_calls: Optional[List[Dict[str, Any]]] = None) -> bool:
113
+
114
+ async def complete_call(
115
+ self,
116
+ call_id: str,
117
+ response: Optional[str] = None,
118
+ tool_calls: Optional[List[Dict[str, Any]]] = None,
119
+ ) -> bool:
110
120
  """Mark a completion call as completed with a response or tool calls."""
111
121
  async with self._lock:
112
122
  if call_id not in self._queue:
113
123
  return False
114
-
124
+
115
125
  call = self._queue[call_id]
116
126
  if call.status != CompletionStatus.PENDING:
117
127
  return False
118
-
128
+
119
129
  call.status = CompletionStatus.COMPLETED
120
130
  call.completed_at = datetime.now()
121
131
  call.response = response
122
132
  call.tool_calls = tool_calls
123
-
133
+
124
134
  # Remove from pending order
125
135
  if call_id in self._pending_order:
126
136
  self._pending_order.remove(call_id)
127
-
137
+
128
138
  return True
129
-
139
+
130
140
  async def fail_call(self, call_id: str, error: str) -> bool:
131
141
  """Mark a completion call as failed with an error."""
132
142
  async with self._lock:
133
143
  if call_id not in self._queue:
134
144
  return False
135
-
145
+
136
146
  call = self._queue[call_id]
137
147
  if call.status != CompletionStatus.PENDING:
138
148
  return False
139
-
149
+
140
150
  call.status = CompletionStatus.FAILED
141
151
  call.completed_at = datetime.now()
142
152
  call.error = error
143
-
153
+
144
154
  # Remove from pending order
145
155
  if call_id in self._pending_order:
146
156
  self._pending_order.remove(call_id)
147
-
157
+
148
158
  return True
149
-
159
+
150
160
  async def wait_for_completion(self, call_id: str, timeout: float = 300.0) -> Optional[str]:
151
161
  """Wait for a completion call to be completed and return the response."""
152
162
  start_time = asyncio.get_event_loop().time()
153
-
163
+
154
164
  while True:
155
165
  status = await self.get_call_status(call_id)
156
166
  if not status:
157
167
  return None
158
-
168
+
159
169
  if status["status"] == CompletionStatus.COMPLETED.value:
160
170
  return status.get("response")
161
171
  elif status["status"] == CompletionStatus.FAILED.value:
162
172
  raise Exception(f"Completion failed: {status.get('error', 'Unknown error')}")
163
-
173
+
164
174
  # Check timeout
165
175
  if asyncio.get_event_loop().time() - start_time > timeout:
166
176
  await self.fail_call(call_id, "Timeout waiting for human response")
167
177
  raise TimeoutError("Timeout waiting for human response")
168
-
178
+
169
179
  # Wait a bit before checking again
170
180
  await asyncio.sleep(0.5)
171
181
 
@@ -204,9 +214,7 @@ async def get_status(call_id: str):
204
214
  async def complete_call(call_id: str, response: CompletionResponse):
205
215
  """Complete a call with a human response."""
206
216
  success = await completion_queue.complete_call(
207
- call_id,
208
- response=response.response,
209
- tool_calls=response.tool_calls
217
+ call_id, response=response.response, tool_calls=response.tool_calls
210
218
  )
211
219
  if success:
212
220
  return {"status": "success", "message": "Call completed"}
@@ -219,7 +227,9 @@ async def fail_call(call_id: str, error: Dict[str, str]):
219
227
  """Mark a call as failed."""
220
228
  success = await completion_queue.fail_call(call_id, error.get("error", "Unknown error"))
221
229
  if not success:
222
- raise HTTPException(status_code=404, detail="Completion call not found or already completed")
230
+ raise HTTPException(
231
+ status_code=404, detail="Completion call not found or already completed"
232
+ )
223
233
  return {"status": "failed"}
224
234
 
225
235
 
@@ -231,4 +241,5 @@ async def root():
231
241
 
232
242
  if __name__ == "__main__":
233
243
  import uvicorn
244
+
234
245
  uvicorn.run(app, host="0.0.0.0", port=8002)