Skip to main content

dynos-client

The HTTP client and command-line tool for talking to a DYNOS planning backend. You install this on your laptop or server, point it at a backend, and drive the planner from Python or the shell. The backend does the planning; this package gives you the network interface.

You usually combine it with a domain package (e.g. dynos-sentry-domain) that gives you the actual nouns and verbs your goals reference. dynos-client ships only the infrastructure: HTTP, CLI, mission builder, session-gateway authentication.

dynos-client depends on dynos-core, as do the domain files. The domain files also depend on this package. Specific robot missions or extensions, such as the adaptive sampling demo, depend on robot implementations.

Most users start here. pip install dynos-client plus a domain package and you can talk to a hosted backend immediately. You do not need to install the backend itself.

Install

pip install dynos-client

Pulls in dynos-core, requests, typer, and bcrypt. Registers a dynos console script.

Sessions and the backend

DYNOS backends run in two modes. Direct mode is a single process with no auth. This is fine for local development against a mock, or on-vehicle work. Session-gateway mode is multi-tenant, and most likely how you're be developing: a TLS endpoint accepts password logins, issues bearer tokens, and routes each user's traffic to a private subprocess (their session) with its own world model. The hosted backend at https://api.dynosplan.com is in this mode.

You authenticate once:

dynos login
# optionally, you can set the source. The default is the webpage, but this might be an on-ship server:
# dynos login --to https://api.dynosplan.com

That writes ~/.dynos/config.json (mode 0o600). Every subsequent dynos call, dynos session, and dynos connect reads the cached URL, token, and default session, so the --to flag is rarely needed after the first login.

A session is a private process that holds your world model. Create one before issuing goals:

dynos session create
dynos call health         # confirms the gateway is reachable
dynos session info        # confirms a session is selected

dynos session swap <name> switches the default; dynos session stop ends the current session. The CLI never silently picks a session for you. If your default session has expired or been stopped, commands error out with a hint to run swap or create.

CLI map

Command Purpose
dynos login / logout / whoami Authenticate against a session gateway and inspect the cache.
dynos call <subcmd> Talk to a running backend: health, goal, plan, execute, state, objects, trigger, reset, geojson, ...
dynos session <subcmd> Manage gateway sessions: create, list, info, swap, extend, reset, stop.
dynos connect <module>:<Class> Run as a servant; the backend dispatches actions over HTTP and your local code executes them.
dynos hash-password Produce a bcrypt hash for the gateway's admin user table. You can send this to the server admin (me) to change your pw

Run any of them with --help for full options.

60-second example

from dynos_client import RemoteOrchestrator

orch = RemoteOrchestrator.from_config()    # reads ~/.dynos/config.json
print(orch.health())

The shell equivalent:

dynos call health

Both go to the same endpoint. The Python form is the one you reach for when missions get longer than the CLI's 30-second HTTP timeout (see "Long-running plans" below).

Goal vs. trigger

There are two ways to make the backend do something, and they are not interchangeable.

dynos call execute (preferred). You set a goal, which is a desired symbolic state. The planner derives the prerequisite sequence. If something fails, the planner replans from the current state. This is what the system is designed for.

dynos call goal "full_coverage_of(site_alpha)"
dynos call plan        # optional: print the action sequence the planner picked
dynos call execute

dynos call trigger (debug only). Fires one transition directly, with no planner involvement and no precondition check. Convenient for confirming a custom @Action is wired up; not safe for anything that moves the vehicle. A careless trigger can leave the symbolic world in a state the planner doesn't trust (e.g. recording at(wp_end) without descent ever happening).

dynos call trigger "resample_zone(source_zone=site_alpha)"   # OK for debug

If you find yourself wanting to chain trigger calls, you actually want a goal. Let the planner sequence them.

Missions

A Mission chains plan blocks and action blocks. Each block runs to completion before the next starts; the planner replans at every block boundary using the state after the previous block.

from dynos_client import RemoteOrchestrator, Mission
from dynos_sentry.sentry import Zone, full_coverage_of

orch = RemoteOrchestrator.from_config(timeout_s=3600)

area_north = Zone("area_north", coordinate_frame="geographic",
    vertices=[[-70.67, 41.525], [-70.66, 41.525], [-70.66, 41.53], [-70.67, 41.53]],
    altitude=70.0, speed=0.8, coverage_width=170.0, robot_width=0.5)
area_south = Zone("area_south", coordinate_frame="geographic",
    vertices=[[-70.67, 41.52], [-70.66, 41.52], [-70.66, 41.525], [-70.67, 41.525]],
    altitude=70.0, speed=0.8, coverage_width=170.0, robot_width=0.5)
orch.create_object(area_north)
orch.create_object(area_south)

mission = Mission(name="two_surveys")
mission.add_plan([full_coverage_of(area_north)])
mission.add_plan([full_coverage_of(area_south)])
results = orch.execute_blocks(mission)

Mix action blocks (run a single transition immediately, no planning) with plan blocks (set a goal, planner derives the sequence):

from dynos_sentry.sentry import full_coverage_of

mission = Mission(name="custom_sequence")
mission.add_plan([full_coverage_of(area_north)])    # planned
mission.add_plan([full_coverage_of(area_south)])
results = orch.execute_blocks(mission)

A single plan block can carry multiple goals; the planner finds one plan that achieves all of them. Note what is not here: no takeover, descent, or abort steps. Those bookends (and the vehicle's abort/recovery) are added by the mission framework and the on-vehicle safety system, not by you -- a client never assembles lifecycle or abort steps, and never puts a phase in a goal. A plan block is the normal building block; an action block (a single transition, no planning) strips out the replanning context and is reserved for genuine imperative primitives.

Servants and dynos connect

When the action's implementation lives on your machine; your ML model, a sensor handler, a custom controller; you run a servant. The backend keeps planning; your code executes the assignments it dispatches. See dynos-adaptive-resampling's README for a full worked example.

dynos connect my_package.runnable:MyNode

A Runnable is a small class with three static methods (register, get_start_state, get_start_goal); the servant loop polls the backend for assignments and posts results back. dynos connect reads the same cached login as dynos call.

Custom actions in a client-only env

@Action and @Script (the decorators that bind a Python method to a Transition) are available directly from dynos_client.action, so an extension package imports them with no backend installed:

from dynos_client.action import Action, Script

In a client-only environment the decorator records the binding; the action fires when the class is registered against a backend that dispatches it (this is exactly how dynos-adaptive-resampling defines its resample_zone action).

Long-running plans

dynos call execute is synchronous: the backend holds the HTTP response open until every step in the plan has finished. The CLI defaults to a 30-second client-side timeout, so for any plan that takes longer your terminal will print Read timed out even though the backend is still happily executing. This times out the client, not the plan.

Two workarounds:

  1. Use Python with a longer timeout. RemoteOrchestrator.from_config(timeout_s=3600).
  2. Don't watch, open a second terminal and poll. dynos call state, dynos call goal, dynos call live-trace, dynos call live-log --wall.

Configuration

~/.dynos/config.json (mode 0o600) stores:

Key Source
backend_url dynos login --to ...
token dynos login (gateway-issued bearer)
username dynos login
role dynos login (user, admin)
default_session dynos session create / swap

Set DYNOS_CONFIG_PATH=/some/other/file.json to override the location (handy for tests; the public CLI honours the env var).

Public API

Symbol Purpose
RemoteOrchestrator The HTTP client; mirrors the backend's Orchestrator Python API.
RemoteAuthExpired Raised when the cached token is rejected; catch and reauthenticate.
Mission Builder for compound goal sequences.
PlanBlock Plan-derived block within a Mission (one or more goals -> planner).
ActionBlock / AnyActionBlock Single-action block within a Mission (no planning).

Next

Install dynos-sentry-domain to get Zone, Coordinate, full_coverage_of, and the rest of the goal vocabulary. The walkthrough at user_guide.md ties everything together end-to-end. A simple toy domain (warehouse) is also available.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dynos_client-0.2.0.tar.gz (53.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dynos_client-0.2.0-py3-none-any.whl (57.3 kB view details)

Uploaded Python 3

File details

Details for the file dynos_client-0.2.0.tar.gz.

File metadata

  • Download URL: dynos_client-0.2.0.tar.gz
  • Upload date:
  • Size: 53.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.10

File hashes

Hashes for dynos_client-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c066aba1e71643f8b314075f9b40981c30b9f132fa03eae94fb342d4b909bb5f
MD5 9002d6b635e415f6ae17b437fd056dab
BLAKE2b-256 d412d49afb0c4d092e0bb1532572254e9b5c7120d2b72027c4e02dd3f4e9a026

See more details on using hashes here.

File details

Details for the file dynos_client-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: dynos_client-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 57.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.10

File hashes

Hashes for dynos_client-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6bf2b0bda30fa1bd10321410072c1c0ed8e6297db266c7c1a15b617e71097c8a
MD5 ec13e439aecba2112aee12fbb8cff4a5
BLAKE2b-256 ae99f9f6c294c0ac8eae59505da89df2f485e5f060832b8c3ceda5a130b5b016

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.2

2 files

This release

0.2.0 This release

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page