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.
- examples/export_tasks.py +16 -5
- examples/export_tasks_filtered.py +245 -0
- examples/fetch_tasks.py +230 -0
- examples/import_tasks.py +140 -8
- examples/iterate_verifiers.py +725 -0
- fleet/__init__.py +128 -5
- fleet/_async/__init__.py +27 -3
- fleet/_async/base.py +24 -9
- fleet/_async/client.py +938 -41
- fleet/_async/env/client.py +60 -3
- fleet/_async/instance/client.py +52 -7
- fleet/_async/models.py +15 -0
- fleet/_async/resources/api.py +200 -0
- fleet/_async/resources/sqlite.py +1801 -46
- fleet/_async/tasks.py +122 -25
- fleet/_async/verifiers/bundler.py +22 -21
- fleet/_async/verifiers/verifier.py +25 -19
- fleet/agent/__init__.py +32 -0
- fleet/agent/gemini_cua/Dockerfile +45 -0
- fleet/agent/gemini_cua/__init__.py +10 -0
- fleet/agent/gemini_cua/agent.py +759 -0
- fleet/agent/gemini_cua/mcp/main.py +108 -0
- fleet/agent/gemini_cua/mcp_server/__init__.py +5 -0
- fleet/agent/gemini_cua/mcp_server/main.py +105 -0
- fleet/agent/gemini_cua/mcp_server/tools.py +178 -0
- fleet/agent/gemini_cua/requirements.txt +5 -0
- fleet/agent/gemini_cua/start.sh +30 -0
- fleet/agent/orchestrator.py +854 -0
- fleet/agent/types.py +49 -0
- fleet/agent/utils.py +34 -0
- fleet/base.py +34 -9
- fleet/cli.py +1061 -0
- fleet/client.py +1060 -48
- fleet/config.py +1 -1
- fleet/env/__init__.py +16 -0
- fleet/env/client.py +60 -3
- fleet/eval/__init__.py +15 -0
- fleet/eval/uploader.py +231 -0
- fleet/exceptions.py +8 -0
- fleet/instance/client.py +53 -8
- fleet/instance/models.py +1 -0
- fleet/models.py +303 -0
- fleet/proxy/__init__.py +25 -0
- fleet/proxy/proxy.py +453 -0
- fleet/proxy/whitelist.py +244 -0
- fleet/resources/api.py +200 -0
- fleet/resources/sqlite.py +1845 -46
- fleet/tasks.py +113 -20
- fleet/utils/__init__.py +7 -0
- fleet/utils/http_logging.py +178 -0
- fleet/utils/logging.py +13 -0
- fleet/utils/playwright.py +440 -0
- fleet/verifiers/bundler.py +22 -21
- fleet/verifiers/db.py +985 -1
- fleet/verifiers/decorator.py +1 -1
- fleet/verifiers/verifier.py +25 -19
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/METADATA +28 -1
- fleet_python-0.2.105.dist-info/RECORD +115 -0
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/WHEEL +1 -1
- fleet_python-0.2.105.dist-info/entry_points.txt +2 -0
- tests/test_app_method.py +85 -0
- tests/test_expect_exactly.py +4148 -0
- tests/test_expect_only.py +2593 -0
- tests/test_instance_dispatch.py +607 -0
- tests/test_sqlite_resource_dual_mode.py +263 -0
- tests/test_sqlite_shared_memory_behavior.py +117 -0
- fleet_python-0.2.66b2.dist-info/RECORD +0 -81
- tests/test_verifier_security.py +0 -427
- {fleet_python-0.2.66b2.dist-info → fleet_python-0.2.105.dist-info}/licenses/LICENSE +0 -0
- {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":
|
|
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
|
-
|
|
51
|
-
|
|
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,
|