Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
b301d67
Update agent.py
mikesklar Jan 13, 2026
32fa85f
update gemini cua agent with latest updates
c45 Jan 14, 2026
8852db9
update name
c45 Jan 14, 2026
0f52155
fix: allow verifier helper functions to be called from main verifier
Jan 16, 2026
5f89234
Merge pull request #38 from fleet-ai/fix/verifier-helper-functions-na…
ashishtanwer Jan 16, 2026
54feffd
Bump version to 0.2.104
Jan 16, 2026
f895cb6
add metadata to tasks
zach-fleet Jan 21, 2026
fef862d
fixes
zach-fleet Jan 21, 2026
9a0af14
fix: align InstanceRequest and TaskRequest with orchestrator API
Jan 22, 2026
9cdd7e6
Revert "fix: align InstanceRequest and TaskRequest with orchestrator …
Jan 22, 2026
acc58ed
Merge pull request #40 from fleet-ai/zz/add-metadata-0121
zach-fleet Jan 22, 2026
58f8faf
bump version
zach-fleet Jan 22, 2026
5dbb76e
Merge pull request #41 from fleet-ai/zz/2.105
zach-fleet Jan 22, 2026
ee6a8a5
consolidate
zach-fleet Jan 22, 2026
05112f7
Consolidate all metadata into "metadata" in TaskResponse
zach-fleet Jan 22, 2026
afb516c
README.md
ggelango Jan 28, 2026
c08b76b
README.md
ggelango Jan 28, 2026
e303638
Update README.md
ggelango Jan 28, 2026
0c4d149
Update README.md
ggelango Jan 28, 2026
f0737e5
Delete export_tasks_filtered.py
ggelango Jan 28, 2026
53609ae
Update README.md
ggelango Jan 29, 2026
8b92120
Update README.md
ggelango Jan 29, 2026
323f25e
feat: add factual_answer field to Task and TaskRequest/TaskResponse m…
andrew-stelmach-fleet Feb 3, 2026
33459b2
chore: bump version to 0.2.107
andrew-stelmach-fleet Feb 3, 2026
7dbbe39
chore: update lockfile for 0.2.107
andrew-stelmach-fleet Feb 3, 2026
1bcfb21
Merge pull request #47 from fleet-ai/add-factual-answer-support
andrew-stelmach-fleet Feb 3, 2026
b77cce1
feat: add task_modality support to SDK
andrew-stelmach-fleet Feb 5, 2026
a45a0ae
fix: add task_modality to load_task_from_json
andrew-stelmach-fleet Feb 5, 2026
875a297
chore: bump version to 0.2.108
andrew-stelmach-fleet Feb 5, 2026
aa03cd0
feat: add task_modality support to async SDK
andrew-stelmach-fleet Feb 5, 2026
0716db3
Merge pull request #48 from fleet-ai/feat/add-task-modality-support
andrew-stelmach-fleet Feb 5, 2026
5876366
fix: extract factual_answer in load_tasks and load_task_from_json
andrew-stelmach-fleet Feb 5, 2026
999ca5b
feat: add scenario support and fix factual_answer extraction
andrew-stelmach-fleet Feb 5, 2026
85ddff5
Merge pull request #50 from fleet-ai/feat/scenario-support
andrew-stelmach-fleet Feb 5, 2026
937ca43
fix: resolve merge conflict, bump version to 0.2.111
andrew-stelmach-fleet Feb 5, 2026
0d8eb78
fix: remove duplicate factual_answer keyword arguments
andrew-stelmach-fleet Feb 5, 2026
f08c733
Merge pull request #49 from fleet-ai/fix/factual-answer-extraction
andrew-stelmach-fleet Feb 5, 2026
90bea61
fix: use int for scenario IDs to match database schema
andrew-stelmach-fleet Feb 5, 2026
73219dc
Merge pull request #51 from fleet-ai/fix/scenario-id-type
andrew-stelmach-fleet Feb 5, 2026
a5eafaf
fix: handle environment_id in load_task_from_json
andrew-stelmach-fleet Feb 5, 2026
42bea78
Merge pull request #52 from fleet-ai/fix/load-task-from-json-env-id
andrew-stelmach-fleet Feb 5, 2026
18f3c4f
fix: propagate errors from import_single_task instead of swallowing
andrew-stelmach-fleet Feb 5, 2026
cc239a1
Merge pull request #53 from fleet-ai/fix/import-single-task-error-han…
andrew-stelmach-fleet Feb 5, 2026
9fea50b
feat: Add task_lifecycle_status field to Task model
andrew-stelmach-fleet Feb 5, 2026
44a5beb
refactor: Simplify env_id mapping in load_task_from_json
andrew-stelmach-fleet Feb 5, 2026
1ddacb9
refactor: Rename env_id to env_key in Task model
andrew-stelmach-fleet Feb 5, 2026
b8589f9
fix: Remove alias, map env_key -> env_id when sending to API
andrew-stelmach-fleet Feb 5, 2026
29f4c74
fix: Add task_lifecycle_status to TaskResponse and load_tasks
andrew-stelmach-fleet Feb 5, 2026
5393b18
fix: Rename env_key property to get_env_key() to avoid recursion
andrew-stelmach-fleet Feb 5, 2026
725d8ba
fix: Rename env_key property to get_env_key() to avoid recursion
andrew-stelmach-fleet Feb 5, 2026
f2d71bf
fix: Use get_env_key() in make() to include version suffix
andrew-stelmach-fleet Feb 5, 2026
747d945
fix: Add alias for TaskInfo env_key field to support env_id
andrew-stelmach-fleet Feb 5, 2026
81cd360
fix: Add env_key to JSON loading fallback chain for round-trip support
andrew-stelmach-fleet Feb 5, 2026
fae9bd7
refactor: Normalize env_key across all SDK models
andrew-stelmach-fleet Feb 5, 2026
115cffb
feat: Add computed properties to Task model
andrew-stelmach-fleet Feb 5, 2026
65d967a
fix: Add model_config to TaskInfo for populate_by_name support
andrew-stelmach-fleet Feb 5, 2026
2d438b3
fix: Update example files to use task.env_key instead of task.env_id
andrew-stelmach-fleet Feb 5, 2026
7ec526b
Merge pull request #55 from fleet-ai/feat/add-task-lifecycle-status
andrew-stelmach-fleet Feb 5, 2026
97cd5b2
fix: make TaskResponse.env_key optional to handle null API responses
andrew-stelmach-fleet Feb 6, 2026
382a289
fix: add null check in make_for_task before passing env_spec to make
andrew-stelmach-fleet Feb 6, 2026
3a4f711
Merge pull request #56 from fleet-ai/fix/task-response-optional-env-key
andrew-stelmach-fleet Feb 6, 2026
5715f73
Revert "Merge pull request #56 from fleet-ai/fix/task-response-option…
andrew-stelmach-fleet Feb 6, 2026
bb30e38
Merge pull request #57 from fleet-ai/revert/task-response-optional-en…
andrew-stelmach-fleet Feb 6, 2026
942a9af
export_tasks
ggelango Feb 9, 2026
9065029
fix: remove stale output_json_schema warning from import_tasks
andrew-stelmach-fleet Feb 19, 2026
1e6a928
Merge pull request #65 from fleet-ai/fix/remove-output-schema-warning
andrew-stelmach-fleet Feb 19, 2026
04eb18d
feat: add task bundle editing scripts (download, validate, upload)
mikesklar Feb 25, 2026
62d2251
Merge pull request #68 from fleet-ai/feat/task-bundle-editing
mikesklar Feb 25, 2026
3c340d4
fix(download): don't pass auto-resolved team_id to task GET (#69)
mikesklar Feb 25, 2026
e3e55c9
feat(upload): add job launching and auto-generated unique task keys (…
mikesklar Feb 26, 2026
dfb1f65
fix(download): use TASK_KEY env variable as file-set key (#72)
mikesklar Mar 2, 2026
8f22efb
fix(upload): extract job_id from API response and add workspace file …
mikesklar Mar 2, 2026
9612624
feat: add standalone launch_job script for existing tasks (#74)
mikesklar Mar 3, 2026
a110de8
fix(validate): error when verifier S3 solutions path doesn't match ta…
mikesklar Mar 3, 2026
6cd1311
fix: use env_variables.TASK_KEY exclusively for S3 path validation
mikesklar Mar 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 236 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,88 +8,278 @@ The Fleet Python SDK provides programmatic access to Fleet's environment infrast

## Installation

Install the Fleet SDK using pip:

```bash
pip install fleet-python
```

### Alpha/Pre-release Versions
## API Key Setup

To install the latest alpha or pre-release version:
Get your API key from the [Fleet Dashboard](https://fleetai.com/dashboard/api-keys), then set it as an environment variable:

```bash
pip install --pre fleet-python
export FLEET_API_KEY="sk_your_key_here"
```

To install a specific alpha version:
## Tasks

```bash
pip install fleet-python==0.2.64-alpha1
A **Task** represents a unit of work for an agent to complete within an environment. Each task combines:

- **Prompt**: Instructions describing what needs to be done
- **Environment**: The environment configuration (`env_key`, `data_key`, `env_variables`)
- **Verifier**: Code that validates task completion and returns a score (0.0 to 1.0)

Tasks provide all the configuration needed to spin up an environment and verify an agent's work.

### Task Properties

| Property | Description |
| --------------- | ----------------------------------------------------- |
| `key` | Unique task identifier |
| `prompt` | Instructions for the agent |
| `env_key` | Environment identifier (e.g., `"hubspot:v1.2"`) |
| `data_key` | Data configuration identifier |
| `env_variables` | Environment variables for the task |
| `metadata` | Additional info (e.g., `avg_steps` for task difficulty) |

## Quick Start

```python
import fleet

async def main():
# Load a task
tasks = await fleet.load_tasks_async(
keys=["task_abcdef"]
)
task = tasks[0]

# Create an environment from the task
env = await fleet.env.make_async(
env_key=task.env_key,
data_key=task.data_key,
env_variables=task.env_variables,
ttl_seconds=7200,
run_id="run-123",
)

# Access the environment URL
print(env.urls.app[0])

# ... interact with the environment ...

# Verify task completion
result = await task.verify_detailed_async(env.instance_id)
print(result)

# Clean up
await env.close()
```

## API Key Setup
## Loading Tasks

Fleet requires an API key for authentication. You can obtain one from the [Fleet Platform](https://fleetai.com/dashboard/api-keys).
### By Task Keys

Set your API key as an environment variable:
```python
tasks = await fleet.load_tasks_async(
keys=["task_abcdef"]
)
```

```bash
export FLEET_API_KEY="sk_your_key_here"
### By Project Key

```python
tasks = await fleet.load_tasks_async(project_key="my-project")
```

## Basic Usage
## Creating Environments

```python
import fleet
import datetime
env = await fleet.env.make_async(
env_key=task.env_key,
data_key=task.data_key,
env_variables=task.env_variables,
ttl_seconds=7200,
run_id="run-123",
)
```

# Create environment by key
env = fleet.env.make("fira")
### With Heartbeats (Optional)

# Reset environment with seed and options
env.reset(
seed=42,
timestamp=int(datetime.datetime.now().timestamp())
Optionally enable heartbeats to keep environments alive during long-running operations:

```python
env = await fleet.env.make_async(
env_key=task.env_key,
data_key=task.data_key,
env_variables=task.env_variables,
ttl_seconds=10800,
heartbeat_interval=30, # seconds
)
```

Send heartbeats to keep the environment alive:

```python
# Via the environment object
await env.heartbeat()

# Access environment state ('current' is the resource id for a sqlite database)
sql = env.state("sqlite://current")
sql.exec("UPDATE customers SET status = 'active' WHERE id = 123")
# Or via instance ID
await fleet.env.heartbeat_async(instance_id)
```

Heartbeats are optional. If `heartbeat_interval` is not set, the instance lifetime is controlled solely by `ttl_seconds`. If heartbeats are enabled and missed 3 consecutive times, the instance will be terminated. Heartbeats take precedence over the TTL.

## Instance Management

### List Instances

```python
# List all instances for a run
instances = await fleet.env.list_instances_async(run_id="run-123")

# List all instances for your profile
instances = await fleet.env.list_instances_async(profile_id="self")
```

### Close Instances

# Clean up
env.close()
```python
# Close all instances for a run
await fleet.env.close_all_async(run_id="run-123")

# Close all instances for your profile
await fleet.env.close_all_async(profile_id="self")

# Close a specific instance by ID
await fleet.env.close_async("bc8954c2")
```

## Environment Management
`"self"` is an alias for the profile associated with your `FLEET_API_KEY`.

### Creating Instances
## Account Information

View your current account details including team info, instance limits, and profile ID:

```python
# Create environment instance with explicit version
env = fleet.env.make("fira:v1.2.5")
account = await fleet.env.account_async()
```

# Create environment instance with default (latest) version
env = fleet.env.make("fira")
Returns:

```json
{
"team_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"team_name": "My Team",
"instance_limit": 32000,
"instance_count": 924,
"profile_id": "11111111-2222-3333-4444-555555555555",
"profile_name": "Jane Doe"
}
```

### Connecting to Existing Instances
## Run Tracking

Track active and past runs:

```python
# Connect to a running instance
env = fleet.env.get("env_instance_id")
# List active runs
runs = await fleet.env.list_runs_async()

# List all runs (active and inactive)
runs = await fleet.env.list_runs_async(status="all")

# Filter by profile
runs = await fleet.env.list_runs_async(profile_id="self")
```

# List all running instances
instances = fleet.env.list_instances()
for instance in instances:
print(f"Instance: {instance.instance_id}")
print(f"Type: {instance.environment_type}")
print(f"Status: {instance.status}")
Returns:

```json
[
{
"run_id": "run-123",
"running_count": 0,
"total_count": 4,
"first_created_at": "2025-10-24T09:48:47.152387",
"last_created_at": "2025-10-24T09:55:19.284294",
"profile_id": "11111111-2222-3333-4444-555555555555"
}
]
```

## Task Verification

Verify task completion and get detailed results:

```python
result = await task.verify_detailed_async(env.instance_id)
print(result)
```

Returns:

```json
{
"key": "task_abcdef",
"version": 4,
"success": true,
"result": 1.0,
"error": null,
"execution_time_ms": 2291,
"stdout": ""
}
```

On failure, `stdout` contains detailed verification errors:

```json
{
"key": "task_abcdef",
"version": 4,
"success": true,
"result": 0,
"error": null,
"execution_time_ms": 2291,
"stdout": "Verification errors: [\"Expected field to be 'value', got None\", \"Form not marked as complete\"]"
}
```

## Complete Example

```python
import fleet
import asyncio

# Filter instances by status (running, pending, stopped, error)
running_instances = fleet.env.list_instances(status_filter="running")
async def main():
# Load tasks from a project
tasks = await fleet.load_tasks_async(project_key="my-project")

for task in tasks:
# Create environment
env = await fleet.env.make_async(
env_key=task.env_key,
data_key=task.data_key,
env_variables=task.env_variables,
ttl_seconds=7200,
run_id="my-evaluation-run",
)

try:
# Access the environment URL
print(env.urls.app[0])

# ... run your agent ...

# Verify task completion
result = await task.verify_detailed_async(env.instance_id)
print(f"Task {task.key}: score={result['result']}")

finally:
await env.close()

# Clean up all instances from this run
await fleet.env.close_all_async(run_id="my-evaluation-run")

# List available environment types
available_envs = fleet.env.list_envs()
if __name__ == "__main__":
asyncio.run(main())
```
2 changes: 1 addition & 1 deletion examples/example_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async def main():
print(f"Task definition:")
print(f" Key: {task.key}")
print(f" Prompt: {task.prompt}")
print(f" Environment: {task.env_id}")
print(f" Environment: {task.env_key}")
print(
f" Verifier: {task.verifier.key if hasattr(task.verifier, 'key') else 'create_bug_issue'}"
)
Expand Down
42 changes: 32 additions & 10 deletions examples/export_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ def main():
help="Output JSON filename (defaults to {team_id}.json)",
default=None,
)
parser.add_argument(
"--skip-missing-verifiers",
"-s",
action="store_true",
help="Skip tasks missing verifier_func instead of erroring",
)

args = parser.parse_args()

Expand Down Expand Up @@ -91,16 +97,32 @@ def main():
missing_verifier.append(task.key)

if missing_verifier:
print(f"\n✗ Error: {len(missing_verifier)} task(s) missing verifier_func:")
for key in missing_verifier[:10]: # Show first 10
print(f" - {key}")
if len(missing_verifier) > 10:
print(f" ... and {len(missing_verifier) - 10} more")
raise ValueError(
"All tasks must have a verifier_func. Cannot export tasks without verifiers."
)

print("✓ All tasks have verifier_func")
if args.skip_missing_verifiers:
print(
f"\n⚠ Skipping {len(missing_verifier)} task(s) missing verifier_func:"
)
for key in missing_verifier[:10]: # Show first 10
print(f" - {key}")
if len(missing_verifier) > 10:
print(f" ... and {len(missing_verifier) - 10} more")
# Filter out tasks without verifiers
tasks = [task for task in tasks if task.verifier_func]
print(f"\n{len(tasks)} task(s) remaining after filtering")
if not tasks:
raise ValueError(
"No tasks remaining after filtering. Nothing to export."
)
else:
print(f"\n✗ Error: {len(missing_verifier)} task(s) missing verifier_func:")
for key in missing_verifier[:10]: # Show first 10
print(f" - {key}")
if len(missing_verifier) > 10:
print(f" ... and {len(missing_verifier) - 10} more")
raise ValueError(
"All tasks must have a verifier_func. Cannot export tasks without verifiers."
)
else:
print("✓ All tasks have verifier_func")

# Determine output filename
output_file = args.output or f"{account.team_id}.json"
Expand Down
Loading