NAMEFRAME Frame the world. Name the frames.

Python SDK and API v1

The SDK is a small synchronous wrapper around the local API v1 server. Use it when you want a stable Python surface for automation without hand-rolling HTTP.

Start the API bridge#

nameframe api-serve --scene-manifest _local/scene_manifest.json --port 8010

The server binds to localhost. Produce the scene manifest first with nameframe scene-unreal against an open editor, or point it at a manifest you already have.

Using the client#

from nameframe import NameFrameApiError, NameFrameClient

client = NameFrameClient("http://127.0.0.1:8010", timeout_s=10)

print(client.capabilities())
print(client.classes())

Call capabilities() first. It reports the plugin, API and schema versions and which features are supported, so your code can adapt instead of failing on a missing endpoint.

Scenarios#

scenario = {
    "schema_version": 1,
    "scenario_id": "scenario_demo",
    "scenario_version": 1,
    "master_seed": 123,
    "scene": {"map": "Testing"},
    "spawn": {}, "camera": {}, "environment": {},
    "capture": {}, "annotations": {},
    "output": {"root": "D:/tmp/nameframe_sdk"},
}

validation = client.validate_scenario(scenario)
if not validation["ok"]:
    raise RuntimeError(validation["errors"])

client.apply_scenario(scenario)
current = client.current_scenario()

Controlled updates

Updates are protected by optimistic concurrency. You pass the config hash you believe is current, and a stale writer gets CONFIG_CONFLICT instead of silently overwriting newer state. Updates are dry runs by default.

# preview only, nothing changes
planned = client.update_scenario(
    {"spawn": {"count": 25}},
    expected_config_hash=current["configuration_hash"],
)

# persistent write: needs dry_run=False and an idempotency key
updated = client.update_scenario(
    {"camera": {"fov_deg": 72}},
    expected_config_hash=current["configuration_hash"],
    dry_run=False,
    idempotency_key="sdk-camera-update-001",
)
Idempotency keys

Retrying a request with the same key is safe: it returns the original result instead of applying the change twice. Generate one per logical operation, not per HTTP attempt.

Durable tasks#

Generation does not block. You start a task and poll it.

import time

task = client.create_generation_task(
    plan,
    expected_config_hash=plan["configuration_hash"],
    approved=True,
    idempotency_key="sdk-generation-001",
)

while task["status"] not in {"completed", "failed", "cancelled"}:
    time.sleep(0.5)
    task = client.task(task["task_id"])

Tasks can be cancelled, retried and resumed with cancel_task, retry_task and resume_task. Cancelling preserves completed frames.

Errors#

try:
    client.job("missing")
except NameFrameApiError as exc:
    print(exc.status, exc.code, exc.recoverable, exc.details)

recoverable tells you whether retrying could plausibly work. Use it instead of retrying blindly on every failure.

Supported calls#

Discovery

capabilities(), scene(), classes(), zones(), cameras()

Scenario

validate_scenario(scenario), apply_scenario(scenario), current_scenario(), update_scenario(patch, expected_config_hash=..., dry_run=True, idempotency_key=None), create_scene_helper(helper, expected_config_hash=..., dry_run=True, idempotency_key=None)

Tasks and jobs

create_generation_task(plan, ...), create_validation_task(scenario=None, idempotency_key=...), tasks(limit=50, status=""), task(task_id), cancel_task(task_id), retry_task(task_id), resume_task(task_id), create_job(target_samples=..., output_root=..., scenario=None, job_id=None), jobs(), job(job_id), pause_job(job_id), resume_job(job_id), cancel_job(job_id)

Preview and analytics

preview(scenario=None), get_preview(preview_id), analytics(), search_frames(filters=None, limit=50, offset=0), annotation_issues(run_id="", limit=100), compare_runs(baseline_run_id, candidate_run_id, representative_limit=12), coverage_correction_plan(matrix, limit=100, max_additional_frames=100000), events()

API v1 endpoints#

If you would rather speak HTTP directly:

AreaEndpoints
DiscoveryGET /v1/capabilities, /v1/scene, /v1/classes, /v1/zones, /v1/cameras
ScenarioPOST /v1/scenario/apply, /v1/scenario/validate, /v1/scenario/update; GET /v1/scenario/current
Scene helpersPOST /v1/scene/helpers
PreviewPOST /v1/preview, GET /v1/preview/{id}
JobsPOST /v1/jobs, GET /v1/jobs, GET /v1/jobs/{id}, POST /v1/jobs/{id}/pause|resume|cancel
TasksPOST /v1/tasks/generate, /v1/tasks/validate; GET /v1/tasks, /v1/tasks/{id}; POST /v1/tasks/{id}/cancel
EventsGET /v1/events, a finite Server-Sent Events snapshot

Scene helper creation only accepts NameFrame spawn, target, camera and exclusion zones and flight paths. It never accepts arbitrary class or asset paths, and the native operation runs as an Unreal transaction. The full contract, including schema versions and job status transitions, is in docs/API_V1.md.