fleet-python 0.2.66b2__py3-none-any.whl → 0.2.105__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.
Files changed (70) hide show
  1. examples/export_tasks.py +16 -5
  2. examples/export_tasks_filtered.py +245 -0
  3. examples/fetch_tasks.py +230 -0
  4. examples/import_tasks.py +140 -8
  5. examples/iterate_verifiers.py +725 -0
  6. fleet/__init__.py +128 -5
  7. fleet/_async/__init__.py +27 -3
  8. fleet/_async/base.py +24 -9
  9. fleet/_async/client.py +938 -41
  10. fleet/_async/env/client.py +60 -3
  11. fleet/_async/instance/client.py +52 -7
  12. fleet/_async/models.py +15 -0
  13. fleet/_async/resources/api.py +200 -0
  14. fleet/_async/resources/sqlite.py +1801 -46
  15. fleet/_async/tasks.py +122 -25
  16. fleet/_async/verifiers/bundler.py +22 -21
  17. fleet/_async/verifiers/verifier.py +25 -19
  18. fleet/agent/__init__.py +32 -0
  19. fleet/agent/gemini_cua/Dockerfile +45 -0
  20. fleet/agent/gemini_cua/__init__.py +10 -0
  21. fleet/agent/gemini_cua/agent.py +759 -0
  22. fleet/agent/gemini_cua/mcp/main.py +108 -0
  23. fleet/agent/gemini_cua/mcp_server/__init__.py +5 -0
  24. fleet/agent/gemini_cua/mcp_server/main.py +105 -0
  25. fleet/agent/gemini_cua/mcp_server/tools.py +178 -0
  26. fleet/agent/gemini_cua/requirements.txt +5 -0
  27. fleet/agent/gemini_cua/start.sh +30 -0
  28. fleet/agent/orchestrator.py +854 -0
  29. fleet/agent/types.py +49 -0
  30. fleet/agent/utils.py +34 -0
  31. fleet/base.py +34 -9
  32. fleet/cli.py +1061 -0
  33. fleet/client.py +1060 -48
  34. fleet/config.py +1 -1
  35. fleet/env/__init__.py +16 -0
  36. fleet/env/client.py +60 -3
  37. fleet/eval/__init__.py +15 -0
  38. fleet/eval/uploader.py +231 -0
  39. fleet/exceptions.py +8 -0
  40. fleet/instance/client.py +53 -8
  41. fleet/instance/models.py +1 -0
  42. fleet/models.py +303 -0
  43. fleet/proxy/__init__.py +25 -0
  44. fleet/proxy/proxy.py +453 -0
  45. fleet/proxy/whitelist.py +244 -0
  46. fleet/resources/api.py +200 -0
  47. fleet/resources/sqlite.py +1845 -46
  48. fleet/tasks.py +113 -20
  49. fleet/utils/__init__.py +7 -0
  50. fleet/utils/http_logging.py +178 -0
  51. fleet/utils/logging.py +13 -0
  52. fleet/utils/playwright.py +440 -0
  53. fleet/verifiers/bundler.py +22 -21
  54. fleet/verifiers/db.py +985 -1
  55. fleet/verifiers/decorator.py +1 -1
  56. fleet/verifiers/verifier.py +25 -19
  57. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/METADATA +28 -1
  58. fleet_python-0.2.105.dist-info/RECORD +115 -0
  59. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/WHEEL +1 -1
  60. fleet_python-0.2.105.dist-info/entry_points.txt +2 -0
  61. tests/test_app_method.py +85 -0
  62. tests/test_expect_exactly.py +4148 -0
  63. tests/test_expect_only.py +2593 -0
  64. tests/test_instance_dispatch.py +607 -0
  65. tests/test_sqlite_resource_dual_mode.py +263 -0
  66. tests/test_sqlite_shared_memory_behavior.py +117 -0
  67. fleet_python-0.2.66b2.dist-info/RECORD +0 -81
  68. tests/test_verifier_security.py +0 -427
  69. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/licenses/LICENSE +0 -0
  70. {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/top_level.txt +0 -0
fleet/agent/types.py ADDED
@@ -0,0 +1,49 @@
1
+ """Type definitions for Fleet Agent."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class AgentConfig(BaseModel):
8
+ """Configuration for running an agent."""
9
+
10
+ project_key: Optional[str] = None
11
+ task_keys: Optional[List[str]] = None
12
+ agent: str = "gemini_cua"
13
+ model: str = "gemini-2.5-pro"
14
+ max_concurrent: int = 4
15
+ max_steps: int = 200
16
+ timeout_seconds: int = 600
17
+ screen_width: int = 1366
18
+ screen_height: int = 768
19
+ port_range_start: int = 8800
20
+ vnc_port_start: int = 6080 # noVNC web port
21
+ headful: bool = False # Show browser via noVNC
22
+ verbose: bool = False # Enable verbose agent logging
23
+ api_keys: Dict[str, str] = Field(default_factory=dict)
24
+
25
+
26
+ class AgentResult(BaseModel):
27
+ """Result from agent execution on a single task."""
28
+
29
+ task_key: str
30
+ final_answer: Optional[str] = None
31
+ completed: bool = False
32
+ error: Optional[str] = None
33
+ steps_taken: int = 0
34
+ execution_time_ms: int = 0
35
+ transcript: List[Dict[str, Any]] = Field(default_factory=list)
36
+ session_id: Optional[str] = None # Fleet session ID for completion
37
+
38
+
39
+ class TaskResult(BaseModel):
40
+ """Full result including verification."""
41
+
42
+ task_key: str
43
+ task_prompt: str
44
+ agent_result: Optional[AgentResult] = None
45
+ verification_success: Optional[bool] = None
46
+ verification_score: Optional[float] = None
47
+ error: Optional[str] = None
48
+ execution_time_ms: int = 0
49
+
fleet/agent/utils.py ADDED
@@ -0,0 +1,34 @@
1
+ """Agent utilities."""
2
+
3
+ from pathlib import Path
4
+
5
+ AGENT_DIR = Path(__file__).parent
6
+
7
+
8
+ def get_agent_path(name_or_path: str) -> Path:
9
+ """Get path to an agent.
10
+
11
+ Args:
12
+ name_or_path: Either a built-in agent name (e.g., 'gemini_cua')
13
+ or a path to a custom agent directory
14
+
15
+ Returns:
16
+ Path to agent directory
17
+ """
18
+ # Check if it's a path (contains / or . or is absolute)
19
+ if "/" in name_or_path or name_or_path.startswith(".") or Path(name_or_path).is_absolute():
20
+ path = Path(name_or_path)
21
+ if not path.exists():
22
+ raise ValueError(f"Agent path not found: {name_or_path}")
23
+ if not (path / "Dockerfile").exists():
24
+ raise ValueError(f"Invalid agent directory (no Dockerfile): {name_or_path}")
25
+ return path
26
+
27
+ # Otherwise treat as built-in agent name
28
+ agent_path = AGENT_DIR / name_or_path
29
+ if not agent_path.exists():
30
+ available = [d.name for d in AGENT_DIR.iterdir()
31
+ if d.is_dir() and not d.name.startswith('_')]
32
+ raise ValueError(f"Agent '{name_or_path}' not found. Available: {available}")
33
+ return agent_path
34
+
fleet/base.py CHANGED
@@ -2,6 +2,8 @@ import httpx
2
2
  from typing import Dict, Any, Optional
3
3
  import json
4
4
  import logging
5
+ import time
6
+ import uuid
5
7
 
6
8
  from .models import InstanceResponse
7
9
  from .config import GLOBAL_BASE_URL
@@ -18,8 +20,15 @@ from .exceptions import (
18
20
  FleetVersionNotFoundError,
19
21
  FleetBadRequestError,
20
22
  FleetPermissionError,
23
+ FleetConflictError,
21
24
  )
22
25
 
26
+ # Import version
27
+ try:
28
+ from . import __version__
29
+ except ImportError:
30
+ __version__ = "0.2.103"
31
+
23
32
  logger = logging.getLogger(__name__)
24
33
 
25
34
 
@@ -38,17 +47,20 @@ class BaseWrapper:
38
47
  base_url = GLOBAL_BASE_URL
39
48
  self.base_url = base_url
40
49
 
41
- def get_headers(self) -> Dict[str, str]:
50
+ def get_headers(self, request_id: Optional[str] = None) -> Dict[str, str]:
42
51
  headers: Dict[str, str] = {
43
52
  "X-Fleet-SDK-Language": "Python",
44
- "X-Fleet-SDK-Version": "1.0.0",
53
+ "X-Fleet-SDK-Version": __version__,
45
54
  }
46
55
  headers["Authorization"] = f"Bearer {self.api_key}"
47
- # Debug log
48
- import logging
49
56
 
50
- logger = logging.getLogger(__name__)
51
- logger.debug(f"Headers being sent: {headers}")
57
+ # Add request ID for idempotency (persists across retries)
58
+ if request_id:
59
+ headers["X-Request-ID"] = request_id
60
+
61
+ # Add timestamp for all requests
62
+ headers["X-Request-Timestamp"] = str(int(time.time() * 1000))
63
+
52
64
  return headers
53
65
 
54
66
 
@@ -67,11 +79,14 @@ class SyncWrapper(BaseWrapper):
67
79
  **kwargs,
68
80
  ) -> httpx.Response:
69
81
  base_url = base_url or self.base_url
82
+ # Generate unique request ID that persists across retries
83
+ request_id = str(uuid.uuid4())
84
+
70
85
  try:
71
86
  response = self.httpx_client.request(
72
87
  method,
73
88
  f"{base_url}{url}",
74
- headers=self.get_headers(),
89
+ headers=self.get_headers(request_id=request_id),
75
90
  params=params,
76
91
  json=json,
77
92
  **kwargs,
@@ -93,8 +108,9 @@ class SyncWrapper(BaseWrapper):
93
108
 
94
109
  # Debug log 500 errors
95
110
  if status_code == 500:
96
- logger.error(f"Got 500 error from {response.url}")
97
- logger.error(f"Response text: {response.text}")
111
+ # logger.error(f"Got 500 error from {response.url}")
112
+ # logger.error(f"Response text: {response.text}")
113
+ pass
98
114
 
99
115
  # Try to parse error response as JSON
100
116
  try:
@@ -229,6 +245,15 @@ class SyncWrapper(BaseWrapper):
229
245
  elif status_code == 429:
230
246
  # Rate limit errors (not instance limit which is now 403)
231
247
  raise FleetRateLimitError(error_message)
248
+ elif status_code == 409:
249
+ # Conflict errors (resource already exists)
250
+ resource_name = None
251
+ # Try to extract resource name from error message
252
+ if "'" in error_message:
253
+ parts = error_message.split("'")
254
+ if len(parts) >= 2:
255
+ resource_name = parts[1]
256
+ raise FleetConflictError(error_message, resource_name=resource_name)
232
257
  else:
233
258
  raise FleetAPIError(
234
259
  error_message,