Skip to content

Add device management SDK and CLI#469

Open
rvirani1 wants to merge 3 commits intomainfrom
sdk-device-mgmt
Open

Add device management SDK and CLI#469
rvirani1 wants to merge 3 commits intomainfrom
sdk-device-mgmt

Conversation

@rvirani1
Copy link
Copy Markdown
Contributor

@rvirani1 rvirani1 commented May 1, 2026

Description

Adds Python SDK + CLI bindings for the new external Deployments / Device Management API (added to the platform in roboflow/roboflow#11350 and hardened in #11441). Closes ENT-1179.

The SDK wraps every route documented in docs/api/deployments/overview.md on roboflow/roboflow master — 1:1, no invented surface. PATCH /config, stream commands, device delete, and fleet-groups CRUD are explicitly out of scope per the doc's "Versioning and scope" section.

New surface:

  • Workspace.devices(), Workspace.device(id), Workspace.create_device(...)
  • Device class with config(), config_history(), streams(), stream(id), logs(...), telemetry(...), events(...), refresh()
  • roboflow device {list, get, create, config, config-history, streams, stream, logs, telemetry, events} with --json, exit codes 0/1/2/3, rate-limit hints
  • Typed exceptions in roboflow/adapters/devicesapi.py: DeviceBadRequestError (400), DeviceAuthError (401/403), DeviceNotFoundError (404), DeviceRateLimitedError (429), DeviceApiError (5xx)

Files:

  • roboflow/adapters/devicesapi.py (new) — HTTP layer for all 10 routes
  • roboflow/core/device.py (new) — Device model
  • roboflow/core/workspace.py — wired in devices() / device(id) / create_device(...)
  • roboflow/cli/handlers/device.py (new) + registration in roboflow/cli/__init__.py
  • tests/test_device.py (new), tests/cli/test_device_handler.py (new)
  • CLI-COMMANDS.md — quickstart entries

Dependencies: none new — uses requests and typer already required by the SDK.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

How has this change been tested, please provide a testcase or example of how you tested the change?

Unit tests — 33 new tests pass (python -m unittest tests.test_device tests.cli.test_device_handler). Full repo suite: 545/545 pass. Coverage:

  • URL building for every route; CSV serialization for service/severity; opaque cursor round-trip for events
  • Error mapping for 400/401/403/404/404+GraphMethodException/429/500 → typed exceptions
  • Device method dispatch; Workspace.devices()/device(id)/create_device integration
  • CLI: subcommand registration, --json and text output, exit-code mapping (404→3, 401→2, 429→1)

Lint / type: ruff check roboflow tests clean, ruff format --check roboflow clean, mypy roboflow clean (no new issues).

Live staging validation against api.roboflow.one workspace device-manager-demo-workspace

# Endpoint Result
1 GET /:ws/devices/v2 OK — 96 devices, schema matches mappers.js
2 POST /:ws/devices/v2 Not exercised (avoid mutating shared workspace); covered by unit tests
3 GET /:ws/devices/v2/:id OK — schema match
4 GET .../config OK — 16 keys incl. environment_variables, services
5 GET .../config/history OK — pagination roundtrip works
6 GET .../streams OK — schema match
7 GET .../streams/:sid OK — schema match
8 GET .../logs OK — schema match
9 GET .../telemetry?time_period=1h OK — 31 buckets, schema match
10 GET .../events Server-side 500 when no filter; works with entity_type=stream. Not an SDK bug — flagged separately.

Negative cases against staging — every one mapped to the correct typed exception with the correct status code: bad time_period (400), bad events cursor (400), bad logs start_time (400), unknown deviceId (404), invalid api_key (401).

CLI parity verified end-to-end: roboflow device list --json → 96 entries; roboflow device get <id> → human-readable text; roboflow device telemetry <id> --time-period 1h --json → 31 buckets; 404 → exit 3; 401 → exit 2 with the device:read scope hint.

Will the change affect Universe? If so was this change tested in universe?

No. Pure-Python SDK and CLI changes only; no Universe surface touched.

Any specific deployment considerations

Nope

Infrastructure impact

  • This change affects infrastructure needs (GPUs, Cloud Function sizing, storage etc.)

Docs

  • Docs updated? What were the changes:
    • CLI-COMMANDS.md — added device commands to the quickstart and the command-groups table
    • Inline docstrings on the Device class call out that config() is sensitive (may include environment_variables and integration credentials)
    • Follow-up doc PR for the full reference will land in roboflow-product-docs per the repo's CLI documentation policy

Copy link
Copy Markdown

@NVergunst-ROBO NVergunst-ROBO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leaving a couple notes

NVergunst-ROBO
NVergunst-ROBO previously approved these changes May 2, 2026
Copy link
Copy Markdown

@NVergunst-ROBO NVergunst-ROBO left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple nits

Comment thread roboflow/adapters/devicesapi.py Outdated

def list_devices(api_key: str, workspace: str) -> Dict[str, Any]:
"""``GET /:workspace/devices/v2`` — returns the parsed JSON response."""
response = requests.get(_build_url(workspace, "", api_key))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These requests calls do not pass timeout=, so a slow or stuck API can hang the CLI and any SDK caller forever. A default connect/read timeout would make behaviour better

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7ce03eb by adding a shared DEFAULT_TIMEOUT=(10, 60) to every devicesapi requests.get/post call, plus a unit test that exercises all adapter request paths and asserts the timeout is passed.

Comment thread roboflow/adapters/devicesapi.py
Comment thread roboflow/cli/handlers/device.py
Comment thread roboflow/cli/handlers/device.py
…ice config

Address @NVergunst-ROBO's review on PR #469.

1. Tighten `_sanitize_credentials` regex in roboflow/cli/_output.py to match
   `api_key=` values containing dashes / non-word URL-safe chars. The old
   pattern `[A-Za-z0-9_]+` would leak keys like `my-key-123`. Stops `requests`
   exceptions whose `str(exc)` echoes the request URL from leaking the api_key
   to stderr.

2. Cap raw response bodies at 1024 chars in `_raise_for_status` so a multi-KB
   HTML 500 stack trace doesn't land in `str(DeviceApiError)`. The Express
   default 500 handler we hit on `/events` returned ~1KB; without the cap a
   real backend trace could be much larger.

3. `roboflow device config` now writes a stderr advisory in interactive (text,
   non-quiet) mode reminding the user the config can contain env vars and
   integration credentials. JSON mode is intentionally untouched: the API
   contract is a passthrough of the Firestore config doc, and silently
   redacting in the SDK would corrupt round-trip use cases (backup/restore,
   diff, migration). Server-side parity (sanitize uniformly across UI / SDK /
   raw curl) is the right place for any actual redaction.

Also adds three regression tests covering the redaction, the truncation, and
the warning behavior in both text and JSON modes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants