Agent¶
zelos_sdk.agent connects to a running Zelos agent.
It queries live signals, opens trace files, runs checks and actions, and manages extensions and layouts.
Most names here also import from the top level, for example zelos_sdk.connect(). Import validate_suite and SuiteValidationError from zelos_sdk.agent.checks.
Public Agent SDK exports and connect helper.
ActionFailed
¶
Bases: AgentError
An action ran and reported failure.
agent.actions.execute itself does not raise this — a failed run
reports through ActionResult.ok / ActionResult.status instead.
Raised only by callers that choose to escalate a failed result.
Examples:
>>> result = agent.actions.execute('battery_test', params={'target_soc': 80})
>>> if not result.ok:
... raise ActionFailed(result.status)
ActionInfo
¶
Identifier carried by every entry from agent.actions.list().
Currently exposes a single name field; kept as its own type (rather
than a plain string) so future fields (description, category, …) can ship
without breaking for info in agent.actions.list(): info.name notebooks.
Examples:
name
instance-attribute
¶
The action's registered name, the string execute() takes.
ActionResult
¶
Result of agent.actions.execute(...) — parsed value plus a non-optional status.
A missing status from the agent reads as ActionStatus.Done. Fail/error statuses come back
here rather than raising, so a caller branches on result.ok instead of try/except.
Examples:
ActionSchema
¶
Schema from agent.actions.schema(...) with pre-parsed JSON objects.
Schema blobs (action_schema, ui_schema) are pre-parsed dicts.
Examples:
action_schema
instance-attribute
¶
JSON Schema for the action's parameters, as a dict.
default_timeout_ms
instance-attribute
¶
Timeout the action declares for itself, in milliseconds; None when it
declares none.
read_only
instance-attribute
¶
Whether the action declares it changes nothing.
ui_schema
instance-attribute
¶
UI hints for rendering the parameter form, as a dict.
ActionStatus
¶
Typed action execution status: one of ActionStatus.Pass, .Fail, .Error, .Done.
Compares equal to its lowercase wire string ("pass" / "fail" / "error" / "done"),
like a str enum.
Examples:
Agent
¶
A handle to a Zelos agent — the entry point for signals, queries and checks.
Construction is lazy: no network call happens until the first RPC, and the
channel reconnects on its own across agent restarts. Use
zelos_sdk.connect() when you want the connection validated up front.
Examples:
>>> import zelos_sdk
>>> agent = zelos_sdk.Agent("http://localhost:2300")
>>> agent.signals()[0].path
'bus0/BMS_message/status.pack_current'
actions
instance-attribute
¶
The action registry and executor for this agent.
check
instance-attribute
¶
Typed predicate Check API. Returns a Checks proxy bound to this
agent: agent.check.that(signal, op, rhs, **kwargs) builds a spec
and runs it through _run_check. Results auto-post to any active
pytest Checker board via the ContextVar in
zelos_sdk.pytest.checker.
extensions
instance-attribute
¶
Extension management (list, info, start, stop, restart) for this agent.
layouts
instance-attribute
¶
Console-backed layout CRUD (Agent.layouts).
Returns an AgentLayouts handle sharing this agent's transport.
list() / show(id) / create(name, data) / update(id, ...) /
delete(id) all round-trip to the agent's layout service, which
requires console login.
target
instance-attribute
¶
The resolved agent endpoint this handle talks to.
url
instance-attribute
¶
Alias of [Self::target], matching the url= constructor keyword.
__new__(target=None, *, url=None)
¶
Create and return a new object. See help(type) for accurate signature.
at(paths, time, *, min_time=None, producers=None, lookback=None)
¶
Per-signal value at (or before) a specific timestamp.
Returns a Snapshot mapping each resolved signal's path to its most
recent LatestValue whose timestamp is <= time. Every dict spelling
works on it. time and min_time accept
datetime, ISO / relative string ("-1m", "now"), or raw int epoch
ns. Use min_time to floor the search. producers = None (default)
covers every connected producer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paths
|
PathsArg
|
A |
required |
time
|
datetime | str
|
Anchor timestamp (datetime, str, or epoch ns). |
required |
min_time
|
datetime | str | None
|
Optional lower bound; values strictly older than this are skipped. Also floors the window wildcards resolve against. |
None
|
producers
|
ProducersArg | None
|
Producer addresses; |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Snapshot |
Snapshot
|
One entry per matched signal, keyed by path. |
Raises:
| Type | Description |
|---|---|
SignalNotFound
|
a wildcard expanded to zero matches. |
ValueError
|
|
close()
¶
Close the transport and drop the catalog cache.
Idempotent. Subsequent RPCs on this Agent (or any Trace/TraceSet
derived from it) raise AgentCancelled. Prefer with Agent(...).
close_traces(paths=[])
¶
Release one or more open trace files in a single RPC. Empty (default) closes all.
connect(timeout=5.0)
¶
download(url, path=None, *, overwrite=False)
¶
Download a cloud trace to a local TRZ2 .trz file. Synchronous, on a
connection with no RPC deadline; Ctrl+C interrupts it. The AGENT
writes the file, so against a remote agent it lands on that host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The trace's console URL, |
required |
path
|
str | PathLike | Path | None
|
Destination |
None
|
overwrite
|
bool
|
Replace an existing destination. |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
FileExistsError
|
destination exists and |
export(path, *, start, end=None, signals=None, paths=None, producers=None, lookback=None, overwrite=False, format=None)
¶
Export signal data to a .trz file on disk.
signals selects the producers that own the matched signals, and each
of those producers writes its whole slice of the time window into the
same output file — the exported trace is wider than the filter. When
signals is None (default) every connected producer writes. The agent
does the actual writing;
on a localhost target with localhost producers an atomic tmp-rename
strategy guards against partial files when overwrite=True and the
destination already exists.
Cross-host targets reject overwrite=False outright (the gate
has no way to atomically replace a file on a remote host); pass
overwrite=True to opt in.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike | Path
|
Filesystem destination for the |
required |
start
|
datetime | str
|
Start of the time window. |
required |
end
|
datetime | str | None
|
End of the window; defaults to "now". |
None
|
signals
|
PathsArg | None
|
Optional |
None
|
producers
|
ProducersArg | None
|
Producer addresses; |
None
|
overwrite
|
bool
|
When |
False
|
format
|
str | None
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ExportResult |
ExportResult
|
On-disk path plus per-producer success bits.
|
Raises:
| Type | Description |
|---|---|
ValueError
|
|
FileExistsError
|
|
health()
¶
health_check()
¶
info()
¶
Composite identity + settings + memory + dirs.
Health-check gates; the four sub-RPCs (settings/memory/logs/configs) fan out
concurrently and partial failures land in info.failures rather than raising.
is_connected(*, timeout=1.0)
¶
Round-trip reachability probe — runs HealthCheck with a short
deadline and returns True iff the agent answered SERVING.
This costs a network round-trip (~1 ms typical, ~timeout on
failure) — it is a question, not a property. Ask it when you need the
answer; there is no cached "connected" flag to read, because the lazy
channel reconnects on its own and any such flag would lie.
Every failure reads as False. Call health() when you want the
typed error instead.
latest(paths, *, lookback=60.0, producers=None)
¶
Latest value(s): one literal path → LatestValue; a wildcard pattern or a
sequence → Snapshot, a read-only Mapping[str, LatestValue] (missing keys omitted).
A single path or pattern: no catalog match → SignalNotFound; a literal that matched but
has no rows in lookback → NoData; two producers owning that literal → AmbiguousSignal.
Sequence: misses omit keys.
producers = None (default) covers every connected producer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paths
|
PathsArg
|
A |
required |
lookback
|
float
|
How far back to look for a value (default 60 s). |
60.0
|
producers
|
ProducersArg | None
|
Producer addresses; |
None
|
query(paths, *, start, end=None, producers=None, lookback=None, max_rows=0, sort=None, sort_order=None, downsample=None)
¶
Time-bounded query → SignalFrame (Arrow IPC). Empty frame when the
signals resolved but no rows fall in the window.
A path that names no signal raises SignalNotFound rather than
returning a frame quietly missing that column — including a literal the
catalog lacks that no producer answers for.
paths accepts a str, Signal, or sequence. start/end accept
datetime, ISO 8601, or relative strings ("-1m", "now").
downsample = N runs the agent's M4 strategy with N buckets and is mutually
exclusive with max_rows / sort_order. max_rows = 0 means no cap.
producers = None (default) covers every connected producer. Wildcards
expand against the signals active inside [start, end]. Convert via
frame.to_pandas() or frame.series(name).
resolve_paths(paths, *, producers=None, lookback=60.0)
¶
segments(producers=None)
¶
Live data segments. producers = None (default) covers every connected
producer; pass a str or a sequence of addresses to narrow it.
Per-producer warnings (an unreachable producer, a stale segment) are
emitted as RuntimeWarning so partial results never silently hide
failures. Catch with warnings.catch_warnings() to inspect them.
signal(path)
¶
Path-only Signal reference. Shorthand for
Signal.from_path(path) — no catalog roundtrip, no
SignalNotFound. The path is verified server-side at the next
check / latest / query invocation, so the resolver picks
the freshest matching segment at evaluation time.
Use this in tests and extension consumers that need a typed signal handle before (or independent of) the catalog being populated.
signals(producers=None, lookback=60.0)
¶
Signal catalog. producers = None (default) covers every connected
producer; pass a str or a sequence of addresses to narrow it.
An explicit call always fetches — the short-lived cache behind
wildcard resolution would otherwise hand back a catalog from before the
producer you just started. Per-producer failures land in
catalog.warnings and are also emitted as RuntimeWarning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
producers
|
ProducersArg | None
|
Producer addresses; |
None
|
lookback
|
float
|
Seconds since a signal last produced a sample (default 60). |
60.0
|
trace(path)
¶
Open a trace. Returns a Trace handle sharing this agent's transport.
path is a local .trz file, or a cloud trace addressed by its console
URL — the link you copy from the trace page or its Copy-link button:
The zelos://open?org=<slug>&trace=<uuid> deep link is accepted too. It
is what the desktop app's protocol handler passes around; the URL is the
form a person actually has.
Raises TraceNotFound if the agent doesn't have the file. The
returned Trace is a context manager — prefer
with agent.trace(path) as trace: over manual trace.close().
traces(paths)
¶
Open multiple traces as one queryable set; routes per-signal to the owning file.
paths is a sequence of traces — the runs are then named by file stem —
or a {label: path} mapping when you want to name them yourself
(agent.traces({"baseline": "a.trz", "candidate": "b.trz"})). Each entry
is a local .trz file or a cloud trace's console URL, and the two can be
mixed, so a local run compares against a cloud one without downloading it
first. Label a cloud trace yourself: a URL has no useful file stem.
Raises ValueError on empty paths, TraceNotFound if any file is unknown.
TraceSet is a context manager — prefer
with agent.traces([...]) as traces: over manual traces.close().
upload(path=None, *, live=False, producers=None, organization=None, name=None, tags=[], wait=True, timeout=None, poll_interval=0.5)
¶
Upload a trace to Zelos Cloud through the agent, which holds the login token, seals the artifact and moves the bytes as a detached task.
Exactly one source: live=True uploads the agent's live session
(needs its disk store), or path names a sealed TRZ2 artifact. A
legacy DuckDB .trz is refused — make one with
agent.export(..., format="trz2").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike | Path | None
|
Sealed TRZ2 artifact, resolved against this process's cwd. |
None
|
live
|
bool
|
Upload the live session instead. |
False
|
producers
|
ProducersArg | None
|
With |
None
|
organization
|
str | None
|
Org slug; |
None
|
name
|
str | None
|
Human-facing label; defaults to |
None
|
tags
|
Sequence[str]
|
Existing org tag NAMES (case-insensitive), resolved before anything is allocated. Never creates a tag. |
[]
|
wait
|
bool
|
Block until the upload finishes (default); |
True
|
timeout
|
float | None
|
Seconds to wait when |
None
|
poll_interval
|
float
|
Seconds between status polls while waiting. |
0.5
|
Raises:
| Type | Description |
|---|---|
ValueError
|
no source, both sources, a path that is missing or a cloud URL, an unknown tag name. |
NoData
|
legacy artifact, or a live upload on a memory-store agent. |
QueryRangeTooLarge
|
the organization's storage quota is exhausted. |
UploadFailed
|
the agent reported the upload failed (when waiting). |
TimeoutError
|
|
watch(paths=None, *, interval=1.0, lookback=60.0, producers=None, until=None, on_error='raise', max_consecutive_errors=5)
¶
Yield a snapshot of the latest values every interval until until.
producers = None (default) covers every connected producer.
window(paths, *, start, duration, producers=None, lookback=None, display_fps=0)
¶
Replay snapshot + change-stream over a fixed time window.
Returns a ReplayWindow with two parts: the snapshot (latest
value per signal at start) and the changes (every subsequent
update inside the window). start accepts datetime, str, or
int ns; duration accepts int/float seconds, a
datetime.timedelta, or a string like "5s". producers = None
(default) covers every connected producer. display_fps = 0 means
no thinning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paths
|
PathsArg
|
A |
required |
start
|
datetime | str
|
Window start. |
required |
duration
|
float | int | str | timedelta
|
Window length. |
required |
producers
|
ProducersArg | None
|
Producer addresses; |
None
|
display_fps
|
int
|
Optional display-rate cap on the change stream. |
0
|
Returns:
| Name | Type | Description |
|---|---|---|
ReplayWindow |
ReplayWindow
|
Snapshot + changes for the window. |
Raises:
| Type | Description |
|---|---|
SignalNotFound
|
a wildcard expanded to zero matches. |
ValueError
|
|
AgentActions
¶
Agent action API (Agent.actions): list, schema, execute over gRPC (no caching).
Callers pass Python dicts; they are JSON-encoded at the wire boundary.
Examples:
>>> [info.name for info in agent.actions.list()]
['battery_test']
>>> result = agent.actions.execute("battery_test", params={"target_soc": 80})
>>> result.ok
True
execute(action, params=None, timeout=None)
¶
Run action with optional params. Status lives on ActionResult, not exceptions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float | None
|
Seconds. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ActionResult |
ActionResult
|
Always — a failed run reports through |
Raises:
| Type | Description |
|---|---|
Internal
|
the agent answered without a result payload. |
Examples:
list()
¶
Registered actions from actions_list (order as on the agent; not cached).
Returns:
| Type | Description |
|---|---|
list[ActionInfo]
|
list[ActionInfo]: One entry per action, with at least a |
Raises:
| Type | Description |
|---|---|
AgentUnavailable
|
agent is unreachable. |
schema(action, current_values=None)
¶
JSON-Schema and ui-schema for action, pre-parsed as dicts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current_values
|
dict | None
|
Partial form data for dynamic schemas; |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ActionSchema |
ActionSchema
|
The action's form schema. |
Raises:
| Type | Description |
|---|---|
Internal
|
the agent answered without a schema payload. |
Examples:
AgentCancelled
¶
Bases: AgentError
The RPC was cancelled or its deadline expired.
Examples:
>>> try:
... agent.query(['bus0/BMS_message/status.pack_current'], start='-5m', timeout=0.001)
... except AgentCancelled:
... pass
AgentError
¶
Bases: Exception
Base class for every Agent SDK wire or RPC failure.
Every subclass carries suggestions, matches, and cause
attributes (empty/None unless the specific subclass fills them in),
so a broad handler can inspect any of them without an AttributeError.
Examples:
AgentExtensions
¶
Sub-namespace exposing the agent's extension lifecycle API.
Reached via Agent.extensions. The handle holds a transport clone
plus a shared handle to the parent agent's signals catalog cache —
start(), stop(), and restart() invalidate that cache so the
next agent.signals() call sees any new producers an extension
brought online (or saw a producer go away).
list() enumerates installed extensions; info(id) and
readme(id) return descriptive metadata; config_schema(id) and
last_config(id) return the JSON-Schema and saved config (decoded
to dict). start(id, config={...}) and restart(id, config={...})
launch (or relaunch) the extension and return a typed
ExtensionStart carrying the resolved version + pid.
Examples:
>>> [e.id for e in agent.extensions.list()]
['zeloscloud.zelos-extension-can']
>>> agent.extensions.start(
... "zeloscloud.zelos-extension-can",
... config={"buses": [{"interface": "demo"}]},
... ).pid
4821
config_schema(id, version=None)
¶
Returns the extension's JSON-Schema config, decoded into a dict.
None means "no schema declared"; an empty dict means
"configured but with no fields" (proto schema_json = "{}").
info(id, version=None)
¶
Fetch metadata for a specific extension.
Returns the extension's manifest data: id, version, description,
any actions/signals it provides, and so on. Useful for building
catalog UIs or verifying an extension is installed before
start()-ing it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Extension identifier (e.g. |
required |
version
|
str | None
|
Specific version to query; |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ExtensionInfo |
ExtensionInfo
|
The extension's manifest. |
Raises:
| Type | Description |
|---|---|
ExtensionError
|
nothing matching |
Internal
|
the agent answered without a manifest. |
Examples:
last_config(id, version=None)
¶
Returns the last saved config, decoded into a dict. None means
"never configured"; an empty dict means "configured but with no
fields" (proto config_json = "{}").
list()
¶
List every extension installed in the agent.
The returned ExtensionEntry objects carry id, version, current
ExtensionState (Installed or Running), and pid (when running).
The list is not cached, so it always reflects the agent's
current view.
Returns:
| Type | Description |
|---|---|
list[ExtensionEntry]
|
list[ExtensionEntry]: One entry per installed extension. |
Raises:
| Type | Description |
|---|---|
AgentUnavailable
|
agent is unreachable. |
readme(id, version=None)
¶
Fetch the extension's README markdown.
Useful for building marketplace UIs or showing inline help in a notebook. Returns the raw markdown verbatim, or an empty string when the extension shipped no README — render it directly with whatever tooling fits the surface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Extension identifier. |
required |
version
|
str | None
|
Specific version; |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Markdown text, or |
restart(id, config=None)
¶
start(id, config=None)
¶
config = None means "no config" (transport sends nothing on
the wire). config = {} means "empty config" (transport sends
"{}" on the wire). The returned ExtensionStart already carries the
resolved version and pid, so no follow-up call is needed.
Examples:
stop(id)
¶
Stop a running extension.
Asks the agent to terminate the extension process; the
signals-catalog cache is invalidated so the next
agent.signals() call reflects the producer leaving. Idempotent
— stopping an already-stopped extension is a no-op.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
Extension identifier. |
required |
Raises:
| Type | Description |
|---|---|
ExtensionError
|
agent reported an error tearing down the extension process. |
AgentInfo
¶
Composite agent identity and runtime state.
Returned by Agent.info(). Bundles the connection target, latest
health-check result, and best-effort introspection: the agent's
AgentSettings, current memory usage, and the on-disk paths the
agent uses for logs and configs.
info() runs four optional sub-RPCs concurrently behind the scenes
(settings + memory + logs-dir + configs-dir); each of them may fail
independently of the others. Failed sub-RPC names appear in
failures (a stable list of strings — "SettingsGet",
"SystemGetMemory", "SystemGetDirLogs", "SystemGetDirConfigs" —
useful for grep-friendly notebook checks). Only the gating
health-check is fatal; everything else is fail-soft.
Examples:
>>> info = agent.info()
>>> info.target, info.health
('http://127.0.0.1:2300', 'OK')
>>> info.failures
[]
configs_dir
instance-attribute
¶
Directory the agent keeps its configs in; None when that sub-call failed.
failures
instance-attribute
¶
Names of the optional sub-calls that failed, e.g. ["SystemGetMemory"].
health
instance-attribute
¶
Result of the gating health check; "OK" when the agent answered.
logs_dir
instance-attribute
¶
Directory the agent writes its logs to; None when that sub-call failed.
memory_bytes
instance-attribute
¶
Memory the agent process is using, in bytes; None when that sub-call
failed.
settings
instance-attribute
¶
The agent's settings; None when that sub-call failed.
target
instance-attribute
¶
The agent endpoint this info was read from.
AgentLayouts
¶
Sub-namespace exposing the agent's layout CRUD API.
Reached via Agent.layouts. The handle holds a transport clone. Every
method round-trips to the agent's console-backed layout service, which
requires the agent to be logged in — unauthenticated calls surface as the
usual typed AgentError subclasses.
list() enumerates every layout visible to the logged-in user;
show(id) fetches one; create(name, data, *, is_personal=False) saves a
new layout and returns it; update(id, *, name=..., data=..., is_personal=...)
patches an existing layout (read-modify-write — omitted fields keep their
current values); delete(id) removes one.
Every update is also recorded in a Google Docs-style version history:
versions(id) lists a layout's versions (newest first, metadata only);
show_version(id, version) fetches one version with its data snapshot;
restore(id, version) restores the layout to a previous version
(appended to the history as a new version — no history is lost);
set_version_label(id, version, label) names a version (None clears —
labeled versions are exempt from retention pruning).
Examples:
>>> created = agent.layouts.create("My Dashboard", {"panels": []})
>>> agent.layouts.update(created.id, name="Renamed").name
'Renamed'
>>> agent.layouts.delete(created.id)
create(name, data, *, is_personal=False)
¶
Create a new layout and return it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Human-readable layout name. |
required |
data
|
dict
|
Arbitrary JSON-serializable dict (panels, config, …). |
required |
is_personal
|
bool
|
When |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
Layout |
Layout
|
The newly-created layout, with its server-assigned id. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the agent is not logged in, or returned malformed data. |
delete(id)
¶
Delete a layout by id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the layout does not exist, or the agent is not logged in. |
list()
¶
List every layout visible to the logged-in console user.
Returns:
| Type | Description |
|---|---|
list[Layout]
|
list[Layout]: One entry per saved layout (may be empty). |
Raises:
| Type | Description |
|---|---|
AgentUnavailable
|
agent is unreachable. |
AgentError
|
agent is not logged in, or returned malformed data. |
restore(id, version)
¶
Restore a layout to a previous version.
The restore is appended to the history as a new version (Google Docs-style — no history is lost), so it can itself be undone by restoring an earlier version.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
version
|
int
|
The version number to restore (versions start at 1). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Layout |
Layout
|
The layout after the restore. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the layout or version does not exist, or the agent is not logged in. |
set_version_label(id, version, label)
¶
Set or clear a layout version's label (name).
Labeled versions are kept forever — they are exempt from the
version-retention pruning. The label is trimmed before sending; pass
None (or a blank string) to clear it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
version
|
int
|
The version number to label (versions start at 1). |
required |
label
|
str | None
|
The label to set, or |
required |
Returns:
| Name | Type | Description |
|---|---|---|
LayoutVersion |
LayoutVersion
|
The updated version metadata. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the layout or version does not exist, or the agent is not logged in. |
show(id)
¶
Fetch a single layout by id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Layout |
Layout
|
The requested layout. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the layout does not exist, or the agent is not logged in. |
show_version(id, version)
¶
Fetch a single layout version, including its data snapshot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
version
|
int
|
The version number to fetch (versions start at 1). |
required |
Returns:
| Name | Type | Description |
|---|---|---|
LayoutVersion |
LayoutVersion
|
The requested version, with its |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
OverflowError
|
|
AgentError
|
the layout or version does not exist, or the agent is not logged in. |
update(id, *, name=None, data=None, is_personal=None)
¶
Update an existing layout and return the updated version.
Any kwarg left as None keeps its current value. The full
(name, data, is_personal) triple is always sent on the wire, so any
omitted field is filled from the current layout via a show() round-trip
(skipped only when all three are provided). Sending the full triple keeps
updates correct against older bundled agents, which predate partial
updates and would read a missing is_personal as False (silently
un-sharing a personal layout). Pass at least one kwarg — an all-None
call raises ValueError without touching the network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
name
|
str | None
|
New name, or |
None
|
data
|
dict | None
|
New payload dict, or |
None
|
is_personal
|
bool | None
|
New personal/team scope, or |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Layout |
Layout
|
The updated layout. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the layout does not exist, or the agent is not logged in. |
versions(id)
¶
List a layout's version history, newest first.
Version entries are metadata-only — their data snapshots come back
as None. Fetch a specific snapshot with show_version().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
id
|
str
|
The layout's UUID (as a string). |
required |
Returns:
| Type | Description |
|---|---|
list[LayoutVersion]
|
list[LayoutVersion]: One entry per saved version, newest first. |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
AgentError
|
the layout does not exist, or the agent is not logged in. |
AgentSettings
¶
Raw settings snapshot from SettingsGet. Wire fields cross verbatim;
the Python AgentSettings dataclass in agent/info.py re-exports
the same data (memory/disk strings stay strings — they are
human-readable like "25%" / "10GB" and are parsed against actual
host capacity at runtime).
Plot UX defaults and subscription / version-migration fields are omitted (not part of the SDK's public surface).
memory_limit / disk_limit are strings (e.g. "25%", "10GB"),
not u64 — the agent stores human-readable strings and parses them
against actual host capacity at runtime.
Examples:
>>> settings = agent.info().settings
>>> settings.store_type, settings.memory_limit
('memory', '25%')
data_retention
instance-attribute
¶
datetime.timedelta; None means time-based pruning is disabled (or
the wire value would overflow chrono::Duration — purely defensive,
realistic retention values stay far below i64::MAX seconds). Call
.total_seconds() for raw seconds.
dev_mode
instance-attribute
¶
True when the agent runs with developer features enabled.
disk_limit
instance-attribute
¶
e.g. "10GB", "25%"; None means unlimited.
log_retention
instance-attribute
¶
See [Self::data_retention]; None means no limit.
memory_limit
instance-attribute
¶
e.g. "25%", "10GB"; None means unlimited.
store_path
instance-attribute
¶
None means platform-default data directory. Only meaningful when
store_type == "disk".
store_type
instance-attribute
¶
Lowercase "memory" (ArrowStore, default), "disk" (ParquetStore), or
"metadata" (catalog only, no data — headless agents) — matches the
StoreType serde encoding so notebook code lines up with CLI / app
JSON debugging.
AgentUnavailable
¶
Bases: AgentError
No agent answered at the target.
Covers a dead connection, a closed port, and an agent that was killed or restarted mid-RPC.
Examples:
>>> try:
... agent.latest('bus0/BMS_message/status.pack_current')
... except AgentUnavailable:
... reconnect()
AmbiguousSignal
¶
Bases: AgentError
A path or pattern matched more than one signal.
Carries the matching catalog entries as matches: list[Signal]; pass
one of them directly to disambiguate.
Examples:
>>> try:
... agent.latest('*/BMS_message/status.pack_current')
... except AmbiguousSignal as exc:
... [s.path for s in exc.matches]
['bus0/BMS_message/status.pack_current', 'bus1/BMS_message/status.pack_current']
CheckEvidence
¶
The single sample that decided a CheckResult's verdict: which signal,
at what time, with what value. None on result.evidence when the
check has no one deciding sample (e.g. a passing count check).
Examples:
>>> result = agent.check.that(
... "bus0/BMS_message/status.pack_current", "<", 100, temporal="always", last="5m"
... )
>>> result.evidence.signal
'bus0/BMS_message/status.pack_current'
arrow_value
instance-attribute
¶
Raw 1-row, 1-column Arrow IPC RecordBatch bytes (same convention as
LatestSignalValue.arrow_value). None when the cell is NULL or the
column type is unsupported.
rhs_value
instance-attribute
¶
Typed value of the right-hand signal at the evidence row, or None when the check compared against a literal.
signal
instance-attribute
¶
Clean path of the signal the evidence sample came from.
time_ns
instance-attribute
¶
When the evidence sample was recorded, epoch nanoseconds.
value
instance-attribute
¶
Typed sample value at the evidence row. Decoded once from the
underlying 1-row, 1-column Arrow IPC payload — returns Python
bool / int / float / str / bytes matching the column
type, or None for NULL / unsupported types.
CheckResult
¶
Outcome of running a CheckSpec against the agent.
bool(result) is True iff status == "pass". result.failed is
True for both fail (predicate violated, evidence available) and
error (precondition failed, no evaluation possible).
Examples:
>>> result = agent.check.that("bus0/BMS_message/status.pack_current", "<", 100)
>>> result.passed
True
count
instance-attribute
¶
Populated for temporal="count"; otherwise None.
evidence
instance-attribute
¶
The sample that decided the verdict — the extreme on a pass, the first
violation on a fail. None when the check could not be evaluated.
failed
instance-attribute
¶
True for Status::Fail (predicate violated) or Status::Error
(precondition failed). The reason and evidence distinguish them.
fired_at_ns
instance-attribute
¶
Wall-clock UTC nanoseconds the agent captured the moment this check
finished evaluating. Same field the checkerboard's timestamp
column renders for live, trace, and literal-only checks alike.
message
instance-attribute
¶
Free-form human message for error/type_mismatch/signal_not_found.
name
instance-attribute
¶
Name of the spec this result came from.
passed
instance-attribute
¶
True iff the predicate held over the entire range (or for
count, always — count results carry the count, not a verdict).
query_ms
instance-attribute
¶
Time spent in the executor's query plan / SQL execution, in milliseconds.
reason
instance-attribute
¶
Why the check ended as it did ("ok", "signal_not_found",
"type_mismatch", ...).
status
instance-attribute
¶
The verdict as a wire string: "pass", "fail", or "error".
wall_ms
instance-attribute
¶
Total wall-clock time the agent spent on the check, in milliseconds.
CheckResults
¶
Bases: list
A run's results: a plain list that also knows its own tally.
bool(results) is "nothing failed and nothing errored" — not
"non-empty" — so a suite reads as one verdict.
Examples:
>>> results = agent.check.suite("checks/battery.json")
>>> len(results.failed)
0
>>> results.raise_if_failed()
errors
property
¶
The results that never got as far as a verdict.
failed
property
¶
The results whose predicate did not hold.
passed
property
¶
The results whose predicate held.
raise_if_failed()
¶
Raise AssertionError listing every check that did not pass —
each on its own line, with its predicate, window and evidence.
For a single check, assert agent.check.that(...): a result is
truthy when it passed.
CheckSpec
¶
A typed check specification — predicate + time range + temporal quantifier.
Constructed by the Python agent.check.that(...) / agent.check.count(...)
helpers. Field access via typed getters (signal, op, temporal,
start_ns, end_ns, rhs).
Examples:
>>> spec = CheckSpec(
... "pack_current_ok",
... signal="bus0/BMS_message/status.pack_current",
... op="<",
... rhs=100.0,
... )
>>> spec.op
'<'
duration_ns
instance-attribute
¶
How long the predicate must hold for the check to pass, in nanoseconds;
None means a single sample is enough.
end_ns
instance-attribute
¶
End of the window the check evaluates, epoch nanoseconds; None for a
latest check.
lhs
instance-attribute
¶
("literal", <python_value>) or ("signal", <signal_path>).
Symmetric with .rhs so Python wrappers render either side
uniformly on the rich-table board.
lookback
instance-attribute
¶
Seconds of history a latest check looks back over when it resolves its signal.
name
instance-attribute
¶
The check's name, echoed on every CheckResult it produces.
op
instance-attribute
¶
The comparison, in its canonical spelling ("<", "is_close", ...).
producers
instance-attribute
¶
Producer addresses the signal resolves against; empty means every connected producer.
rhs
instance-attribute
¶
("literal", <python_value>) or ("signal", <signal_path>) for
binary specs; None for unary specs (is_empty, is_positive,
...). The Python wrapper uses this to render the RHS on the
rich-table board — the None case renders as an empty cell.
start_ns
instance-attribute
¶
Start of the window the check evaluates, epoch nanoseconds; None for a
latest check.
strict_resolution
instance-attribute
¶
Whether the resolver should opt out of newest-segment-wins
disambiguation for this spec. See agent.check.that(..., strict=...).
temporal
instance-attribute
¶
How samples are quantified: "latest", "always", "ever", "never",
or "count".
tolerance
instance-attribute
¶
Tolerance for is_close / is_approximately; None for any
other op. Returned as a 3-tuple (rel_tol, abs_tol, nan_ok).
__new__(name, signal=None, op=None, *, lhs_value=None, lhs_is_null=False, rhs=None, rhs_signal=None, rhs_is_null=False, temporal='latest', start_ns=None, end_ns=None, lookback=60.0, tolerance=None, duration_ns=None, producers=[], strict=False)
¶
Create and return a new object. See help(type) for accurate signature.
from_json(s)
classmethod
¶
Parse and validate a CheckSpec from a JSON string.
The same loader the CLI uses for suite files, so a spec built as a dict
round-trips through CheckSpec.from_json(json.dumps(spec)) and is held
to exactly the same schema.
Checks(parent)
¶
Proxy returned by Agent.check and Trace.check: .that() / .count() / .run() / .suite().
Examples:
>>> result = agent.check.that("bus0/BMS_message/status.pack_current", "<", 100)
>>> result.passed
True
count(lhs, op='is_true', rhs=None, *, producers=None, last=None, start=None, end=None, name=None, lookback=DEFAULT_LOOKBACK_S, rel_tol=None, abs_tol=None, nan_ok=False, strict=False)
¶
Count the satisfying samples. Posts to any active Checker board.
Returns the CheckResult, not a bare number: int(result)
is the count, and it raises rather than reading 0 off a result
that never got as far as counting. Requires at least one signal
operand (a literal-only count is degenerate — there are no
samples to enumerate). Shape-changes on producers the same
way as that.
strict matches that — pass True to require a
unique segment per signal path (opt out of newest-segment-wins
disambiguation).
run(spec, *, producers=None, interval_s=DEFAULT_DURATION_INTERVAL_S, display_lhs=None, display_rhs=None, pure_literal=None)
¶
Run a pre-built CheckSpec and post the result(s).
Lower-level seam: bypasses the kwargs ergonomics. Useful for replaying specs loaded from JSON suites or constructed programmatically.
Same shape-change as that — scalar for a single
producer, dict for fan-out. Per-producer transport failures
synthesize CheckResult (status="error",
reason="internal_error") entries so the caller always sees
one entry per requested producer.
temporal="for_duration" / "within_duration" specs
dispatch into a client-side polling loop (cadence
interval_s) — the wire-level spec records the original
temporal so the artifact and any future streaming RPC stay
identical; only the execution path differs.
Routing: signal-bearing specs go through the agent's
gRPC executor (point-in-time or polling). Literal-only specs
(pure literals or TraceSourceCacheLastField operands whose
values were captured at spec-build time) are evaluated
in-process — no gRPC round-trip, no agent dependency. Same CheckResult
shape either way; same Checker board / artifact output.
Emission: while a Checker session is active, each
posted result also publishes a zelos.check.result.v1 event
through a shared TraceSource("checks"). Outside a session
there is no sink, no emission, and the literal-only path stays
pure in-process.
suite(path, *, producers=None, last=None, start=None, end=None, lookback=DEFAULT_LOOKBACK_S, strict=False)
¶
Run a JSON check suite and post each result to the Checker board.
path may be a single .json file, a directory (every
*.json inside, lex order), a glob pattern, or a list of any
of the above.
Each JSON file holds an array of spec dicts. An operand is
{"signal": "<path>"} for a catalog lookup, or a bare JSON scalar
for a literal. range is required for every temporal but
latest, and last= / start=+end= fill it in when the
file omits it. One entry reads:
[
{
"name": "pack current under load",
"lhs": {"signal": "bus0/BMS_message/status.pack_current"},
"op": "<",
"rhs": 120.0,
"temporal": "always",
"range": {"start_ns": 1000, "end_ns": 9000}
}
]
Element shape follows run: each spec yields a scalar
CheckResult for single-producer (the default), or a
{address: CheckResult} dict for fan-out.
last / start+end (mutually exclusive) inject a
runtime window into specs whose JSON omits range. Same JSON
suite ports between live (with e.g. last=30.0 or
start="-30s", end="now") and trace (TraceChecks.suite
fills the trace's full extent automatically). lookback
sets the resolution-freshness default that flows onto every
spec on the wire.
strict is the suite-level default for
strict_resolution — applied only to entries whose JSON
omits the field (entries that set it explicitly win). Mirrors
the lookback injection semantics.
The signal-bearing specs are batched into one LiveCheck
RPC against the agent (per-agent customization: each producer
in producers gets the same spec list, sent in one call).
Literal-only specs (pure literals or captured
TraceSourceCacheLastField references) evaluate in-process,
with no RPC. Each result posts to the active Checker board in
input order regardless of which path it took.
that(lhs, op='is_true', rhs=None, *, producers=None, last=None, start=None, end=None, temporal=None, name=None, lookback=DEFAULT_LOOKBACK_S, rel_tol=None, abs_tol=None, nan_ok=False, duration_s=None, interval_s=DEFAULT_DURATION_INTERVAL_S, nonblocking=False, strict=False)
¶
Assert a typed predicate lhs op rhs (or unary lhs op).
Both lhs and rhs accept the same shapes:
bool/int/float/str→ literal.Signalhandle (fromagent.signals()[path]ortrace.signals()[path]) → signal reference; the agent fetches the underlying samples (full range foralways/ever/never/count, latest forlatest) and evaluates server-side.TraceSourceCacheLastField→ the current cached value is captured via.get()at spec build time and sent on the wire as a literal — the agent stores the check as an event with the source path preserved for naming / replay.SignalSeries(anlhscolumn of a queried frame) → check the thing you just queried. A signal-backed series resolves to its signal and to its own time extent, so the window is the one you queried; passinglast=/start=/end=alongside it is an error. A derived series (cell_a - cell_b) is evaluated in-process over its own samples.
op defaults to "is_true" so any Python predicate
result becomes a checked assertion routed through the agent
— check.that(isinstance(foo, int)),
check.that(x is None), check.that(0 < y < 10) all
work without needing an explicit op. Encourages the rich
check artifact over plain assert.
rhs is omitted for unary ops (is_empty,
is_positive / is_not_positive, is_negative /
is_not_negative, is_true, is_false).
rel_tol / abs_tol / nan_ok set the tolerance for
the tolerance ops (is_close / is_around,
is_approximately / ~=). Each defaults to None /
False — left unset, the op's stdlib default applies
(is_close → math.isclose: rel_tol=1e-9,
abs_tol=0; is_approximately → pytest.approx:
rel_tol=1e-6, abs_tol=1e-12). Override any subset.
All combinations route through the agent CheckMulti RPC —
the agent is the single eval/storage point.
Shape-changes by the producers argument:
producers=None(default, the local store) or a single address → oneCheckResult.producers=()or a multi-address list → fan-out; returns{address: CheckResult}.
None is the local store, not every connected producer as it
is on Agent.query.
With no last / start / end, defaults to
temporal="latest" (most recent sample). When a range is
given, defaults to temporal="always" (every sample must
satisfy). last= reads the duration grammar — 120,
"2m", timedelta(minutes=2).
Mistakes raise here rather than becoming an error row:
comparing two incomparable literals is a TypeError, and a
window on a check with no samples to quantify over is a
ValueError.
temporal="for_duration" + duration_s=... asserts the
predicate holds continuously from now until the duration
elapses; temporal="within_duration" asserts the predicate
becomes true at least once before the duration elapses. Both
run as a client-side polling loop over temporal="latest"
with cadence interval_s (default
DEFAULT_DURATION_INTERVAL_S). On TraceChecks
the same temporals desugar to Always / Ever over
[trace.end_ns - duration_s*1e9, trace.end_ns], the last
duration_s of the recording — the wire spec is identical,
the executor differs.
ColumnMetadata
¶
Per-column metadata attached to a query result: which signal a column holds, its producer, segment, and the time range it covers.
signal keeps its wire name (not name) because name collides with
__name__-style lookups when used as a pandas/pyarrow column header.
Examples:
>>> frame = agent.query(["bus0/BMS_message/status.pack_current"], start="-5m")
>>> frame.meta[0].signal
'pack_current'
data_segment_id
instance-attribute
¶
The data segment this column belongs to, None when segmentation is
not in play.
end_time_s
instance-attribute
¶
End of the covered time range, in seconds, None when unknown.
message
instance-attribute
¶
The column's message ({source}/{message}.{signal}).
path
instance-attribute
¶
Clean signal path: "{source}/{message}.{signal}". Producer / segment
disambiguation lives on the matching column name and on the
producer / data_segment_id fields; this getter is just the
human-readable label for display and notebook lookups.
producer
instance-attribute
¶
The producer this column came from, None when the query carries no
producer metadata.
signal
instance-attribute
¶
The column's wire signal name (see the class docstring for why this
isn't called name).
source
instance-attribute
¶
The column's source ({source}/{message}.{signal}).
start_time_s
instance-attribute
¶
Start of the covered time range, in seconds, None when unknown.
trace_path
instance-attribute
¶
The trace file this column was read from, None for a live query.
ConnectionTargetError
¶
Bases: AgentError, ValueError
The agent target string could not be parsed into a URL.
Also a ValueError, so except ValueError: catches it too.
Examples:
>>> try:
... zelos_sdk.connect('not a url')
... except ConnectionTargetError as exc:
... str(exc)
"could not parse target 'not a url'"
DownloadResult
¶
ExitInfo
¶
Exit information from a terminated extension process.
Both fields are optional because some platforms only report one (POSIX signal vs. exit code).
Examples:
ExportProducerResult
¶
One producer's outcome from Agent.export(...) / Trace.export(...), keyed
by producer address in the parent ExportResult.results dict.
Examples:
ExportResult
¶
Result of Agent.export(...) / Trace.export(...).
Bundles the on-disk path the agent (or local tmp-rename strategy)
wrote to with a results dict keyed by producer address. Each entry
is an ExportProducerResult carrying that producer's per-shard
success bit and any error message.
ok is True only when every producer shard succeeded — partial
failures surface as ok=False with per-producer detail in results
rather than raising. This lets callers branch on partial-success
outcomes without a try/except.
Examples:
format
instance-attribute
¶
The trace format the agent wrote ("trz2" / "legacy"), None when
it did not report one.
ok
instance-attribute
¶
True only when every producer's shard succeeded.
path
instance-attribute
¶
Path of the trace file the export wrote.
results
instance-attribute
¶
Per-producer results keyed by producer address. Builds the dict
directly from &self.results — cloning the inner HashMap first
would double the allocation work for no benefit.
open()
¶
Open the file this export wrote as a Trace, through the same agent.
Examples:
with agent.export("run.trz", start="-5m").open() as trace: frame = trace.query("bus0/BMS.voltage")
ExtensionEntry
¶
Catalog row returned by agent.extensions.list().
host_type and app_contribution_kind are exposed as their canonical
lowercase wire strings (e.g. "agent", "web_app").
Examples:
>>> entry = agent.extensions.list()[0]
>>> entry.id, entry.version, entry.state.value
('zeloscloud.zelos-extension-can', '0.1.10', 'running')
app_contribution_kind
instance-attribute
¶
e.g. "web_app"; None for non-app extensions.
author
instance-attribute
¶
Manifest author; None when the manifest omits it.
categories
instance-attribute
¶
Manifest categories, used for grouping in the app.
description
instance-attribute
¶
One-line summary from the manifest.
dev_mode
instance-attribute
¶
True when the extension is loaded from a local development checkout.
entry
instance-attribute
¶
Entry point the host runs; None for an extension with nothing to run.
homepage
instance-attribute
¶
Project homepage URL; None when the manifest omits it.
host_type
instance-attribute
¶
"agent" or "app".
icon_path
instance-attribute
¶
Absolute path to the extension's icon file; None when it ships none.
id
instance-attribute
¶
Fully qualified extension id, e.g. "zeloscloud.zelos-extension-can".
keywords
instance-attribute
¶
Manifest keywords, used for search.
last_exit
instance-attribute
¶
How the extension process last terminated; None when it has never run.
name
instance-attribute
¶
Display name from the extension's manifest.
pid
instance-attribute
¶
Process id while the extension runs; None when it is not running.
repository
instance-attribute
¶
Source repository URL; None when the manifest omits it.
state
instance-attribute
¶
Whether the extension is installed or currently running.
version
instance-attribute
¶
Installed version.
zelos_version
instance-attribute
¶
Zelos version range the extension declares it works with.
ExtensionError
¶
ExtensionInfo
¶
Rich info returned by agent.extensions.info(id).
Compared to ExtensionEntry, this carries install_path, readme_path,
etc. but not author or last_exit (those are list-time concerns).
Examples:
>>> agent.extensions.info("zeloscloud.zelos-extension-can").install_path
'/opt/zelos/extensions/zeloscloud.zelos-extension-can/0.1.10'
app_contribution_kind
instance-attribute
¶
What an app extension contributes, e.g. "web_app"; None for an agent
extension.
categories
instance-attribute
¶
Manifest categories, used for grouping in the app.
description
instance-attribute
¶
One-line summary from the manifest.
dev_mode
instance-attribute
¶
True when the extension is loaded from a local development checkout.
entry
instance-attribute
¶
Entry point the host runs; None for an extension with nothing to run.
homepage
instance-attribute
¶
Project homepage URL; None when the manifest omits it.
host_type
instance-attribute
¶
Which host runs the extension: "agent" or "app".
icon_path
instance-attribute
¶
Absolute path to the extension's icon file; None when it ships none.
id
instance-attribute
¶
Fully qualified extension id, e.g. "zeloscloud.zelos-extension-can".
install_path
instance-attribute
¶
Directory the extension is installed in.
keywords
instance-attribute
¶
Manifest keywords, used for search.
name
instance-attribute
¶
Display name from the extension's manifest.
pid
instance-attribute
¶
Process id while the extension runs; None when it is not running.
readme_path
instance-attribute
¶
Absolute path to the extension's README; None when it ships none.
repository
instance-attribute
¶
Source repository URL; None when the manifest omits it.
state
instance-attribute
¶
Whether the extension is installed or currently running.
version
instance-attribute
¶
Installed version.
zelos_version
instance-attribute
¶
Zelos version range the extension declares it works with.
ExtensionStart
¶
Result of agent.extensions.start(id, ...) / restart(id, ...).
Carries the caller-known id alongside the resolved pid and
version the agent actually launched — no follow-up
agent.extensions.list() call is needed to learn either.
Examples:
ExtensionState
¶
Runtime state of an extension.
Two-member enum-like class: ExtensionState.Installed and
ExtensionState.Running. Wire form is the lowercase string
("installed" / "running"); access via state.value, or compare
directly to the member or its wire string.
Examples:
>>> entry = agent.extensions.list()[0]
>>> entry.state == ExtensionState.Running
True
>>> entry.state.value
'running'
Installed
instance-attribute
¶
Runtime state of an extension.
Two-member enum-like class: ExtensionState.Installed and
ExtensionState.Running. Wire form is the lowercase string
("installed" / "running"); access via state.value, or compare
directly to the member or its wire string.
Examples:
Running
instance-attribute
¶
Runtime state of an extension.
Two-member enum-like class: ExtensionState.Installed and
ExtensionState.Running. Wire form is the lowercase string
("installed" / "running"); access via state.value, or compare
directly to the member or its wire string.
Examples:
value
instance-attribute
¶
The state as its wire string: "installed" or "running".
Internal
¶
Bases: RuntimeError
An SDK bug marker.
Deliberately outside AgentError so a broad except AgentError
handler cannot swallow it — the agent answered, but the SDK could not
make sense of the reply. If you hit this, it is a bug in the SDK, not
a caller mistake.
Examples:
LatestValue
¶
One row from latest() / at().
Optional Arrow bytes plus a typed scalar; decoding uses catalog metadata when signal is known.
Clone only bumps refcounts / copies small buffers (no GIL).
Examples:
arrow_array
instance-attribute
¶
Lazily decode arrow_value into a 1-element pyarrow.Array.
arrow_value
instance-attribute
¶
Raw 1-row Arrow IPC bytes when the agent provided a typed
payload, else None.
data_segment_id
instance-attribute
¶
Id of the data segment the sample came from; None when unknown.
display_value
instance-attribute
¶
Agent-provided display string. Always populated; the typed value
getter is preferred for arithmetic / formatting.
label
instance-attribute
¶
signal.value_table[int(value)] lookup. Returns None when no
value_table is present or the value isn't int-coercible.
message
instance-attribute
¶
Message the signal belongs to.
name
instance-attribute
¶
Checkable contract — alias for [Self::path].
path
instance-attribute
¶
Fully-qualified path: "{source}/{message}.{signal}".
producer
instance-attribute
¶
Address of the producer that reported the sample; None for trace data.
raw
instance-attribute
¶
Alias for display_value (matches pre-rewrite SDK).
signal
instance-attribute
¶
Resolved catalog Signal, when known. Populated by
PyLatestRow::with_signal.
signal_name
instance-attribute
¶
Signal name within the message — the part after the final ..
source
instance-attribute
¶
Source name — the first segment of the signal's path.
time
instance-attribute
¶
time_ns as a UTC datetime, or None when the agent did not
timestamp this row. chrono::DateTime::from_timestamp_nanos is
total over i64 ns (every ns since the Unix epoch is in range).
time_ns
instance-attribute
¶
When the sample was recorded, epoch nanoseconds; None when the agent
reported no timestamp.
trace_path
instance-attribute
¶
Trace file the sample came from; None for live data.
value
instance-attribute
¶
Typed Python value (bool / int / float / str / bytes /
None) decoded once at construction. Falls back to the
agent-provided display_value string when no typed payload was
decodable.
formatted(digits=2)
¶
Display string: enum label, numeric+unit, bool, hex bytes, em-dash for null, etc.
get()
¶
Checkable contract — typed value, mirrors the value property.
Layout
¶
A single saved layout, as returned by agent.layouts.list() / show() /
create() / update().
Mirrors the console API LayoutData envelope. Fields are parsed once from
the wire JSON; data is the arbitrary panel/config payload decoded to a
Python dict on access. team_slug, user_id, email, and
updated_by_email are optional and come back as None when the wire
omits them.
Examples:
created_at
instance-attribute
¶
When the layout was created, as an RFC 3339 string.
data
instance-attribute
¶
The layout's stored panel/config payload, decoded into a dict.
The wire data is validated to be a JSON object when the layout is
parsed, so this getter never fails on a well-formed layout.
email
instance-attribute
¶
Email of the layout's owner; None when the wire omits it.
id
instance-attribute
¶
Layout id — the handle every other agent.layouts call takes.
is_personal
instance-attribute
¶
True for a private layout, False for one shared with the team.
name
instance-attribute
¶
Display name.
team_id
instance-attribute
¶
Id of the team the layout belongs to.
team_slug
instance-attribute
¶
URL slug of that team; None when the wire omits it.
updated_at
instance-attribute
¶
When the layout was last saved, as an RFC 3339 string.
updated_by_email
instance-attribute
¶
Email of whoever saved the layout last; None when the wire omits it.
user_id
instance-attribute
¶
Id of the owning user; None for a team layout.
LayoutVersion
¶
A single entry in a layout's version history, as returned by
agent.layouts.versions() / show_version().
Mirrors the console API LayoutVersionData envelope. Fields are parsed
once from the wire JSON; data is the layout payload snapshot at this
version — present on show_version(), None on versions() list
entries (the list is metadata-only). label is the user-given version
name set via set_version_label() (None when unlabeled),
restored_from_version is set when the version was created by a
restore(), and created_by_email comes back as None when the wire
omits it.
Examples:
created_at
instance-attribute
¶
When the version was saved, as an RFC 3339 string.
created_by_email
instance-attribute
¶
Email of whoever saved the version; None when the wire omits it.
data
instance-attribute
¶
The layout payload snapshot at this version, decoded into a dict.
None for entries from versions() (the list is metadata-only);
always populated on show_version().
id
instance-attribute
¶
Version id.
label
instance-attribute
¶
User-given version name; labeled versions are never pruned.
layout_id
instance-attribute
¶
Id of the layout this version belongs to.
name
instance-attribute
¶
The layout's display name when this version was saved.
restored_from_version
instance-attribute
¶
Version this one was restored from; None for an ordinary save.
version
instance-attribute
¶
Version number, counting up from 1.
NamedScalar
¶
Bases: float
float carrying the aggregation's name and unit (Checkable contract).
str and repr round to four significant digits and keep the unit;
float(scalar) is the exact value.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
How the value was produced, e.g. |
|
unit |
The unit it inherits from the series, |
Examples:
NoData
¶
Bases: AgentError
The signal exists but produced no value inside the window.
Examples:
>>> try:
... agent.latest('bus0/BMS_message/status.pack_current', lookback=0.001)
... except NoData:
... pass
QueryRangeTooLarge
¶
Bases: AgentError
The requested range exceeds what the agent will serve at once.
No agent build caps a query range today, so nothing raises this; it is reserved
for the gRPC ResourceExhausted status, which a storage quota or a rate limit
on a console-backed call can also carry.
Examples:
>>> try:
... agent.query(['bus0/BMS_message/status.pack_current'], start='-30d', end='now')
... except QueryRangeTooLarge:
... pass
QueryType
¶
Raw / M4 — selects the agent-side query strategy. Wire encoding
mirrors the proto's QueryType enum (Raw=0, M4=1); the proto's
MinMax=2 is internal-only and not exposed through the SDK surface.
Examples:
ReplayWindow
¶
Replay-window response: I-frame snapshot + P-frame changes + window bounds.
Rows use the same typed LatestValue shape as latest() and at().
Examples:
>>> window = agent.window(["bus0/BMS_message/status.pack_current"], start="-10s", duration=5)
>>> [row.value for row in window.changes]
changes
instance-attribute
¶
duration
instance-attribute
¶
How long the window spans, as a timedelta.
end
instance-attribute
¶
Window end as a datetime.
end_ns
instance-attribute
¶
Alias for [Self::window_end_ns] (matches pre-rewrite SDK).
snapshot
property
¶
Opening values at start, one entry per signal — the same Snapshot
at() returns.
start
instance-attribute
¶
Window start as a datetime.
start_ns
instance-attribute
¶
Alias for [Self::window_start_ns] (matches pre-rewrite SDK).
window_end_ns
instance-attribute
¶
Authoritative window end, epoch nanoseconds (clamped like the start).
window_start_ns
instance-attribute
¶
Authoritative window start (may differ from request if clamped).
Segment
¶
Metadata for a single data segment.
Both bounds are optional because a segment that is still recording does
not have a final end time. connection is set for live segments, trace_path
for trace-file segments — they're mutually exclusive in practice.
Examples:
>>> segments = agent.segments()
>>> segments[0].producer, segments[0].sources
('bus0', ['bus0/BMS_message', 'bus0/inverter_status'])
connection
instance-attribute
¶
Live-connection address (None for trace segments).
duration
instance-attribute
¶
None if the segment is still recording (no end bound).
end
instance-attribute
¶
Last sample time in the segment; None while it is still recording.
id
instance-attribute
¶
Data segment id.
producer
instance-attribute
¶
Producer that owns the segment.
sources
instance-attribute
¶
Source names that appear in the segment.
start
instance-attribute
¶
First sample time in the segment; None when the agent reported none.
trace_path
instance-attribute
¶
Trace file path (None for live segments).
SeriesByRun(runs, frame=None)
¶
Signal
¶
Selector + metadata for a single signal: source, message, name, data type, unit, and (for enum-like signals) the value table.
The wire field signal is exposed as the Pythonic name (signal.signal
is awkward in Python); data_segment_id is exposed as a string.
Examples:
>>> sig = agent.signals()["bus0/BMS_message/status.pack_current"]
>>> sig.unit
'A'
>>> sig.path
'bus0/BMS_message/status.pack_current'
data_segment_id
instance-attribute
¶
The data segment this signal belongs to, "" when segmentation is
not in play.
data_type
instance-attribute
¶
None on a path-only handle — no catalog entry has described it yet.
message
instance-attribute
¶
The signal's message ({source}/{message}.{name}).
name
instance-attribute
¶
The signal's own name — the trailing component of its path
({source}/{message}.{name}).
path
instance-attribute
¶
Fully-qualified path: "{source}/{message}.{name}". A column a frame
computed under a plain label, such as "power", is that label.
producer
instance-attribute
¶
The producer this signal came from, None when the frame carries no
producer metadata.
source
instance-attribute
¶
The signal's source ({source}/{message}.{name}).
trace_path
instance-attribute
¶
The trace file this signal was read from, None for a live signal.
unit
instance-attribute
¶
The signal's engineering unit (e.g. "V", "A"), None when unknown.
value_table
instance-attribute
¶
{raw value: label} for an enum-like signal, None otherwise.
__new__(source, message, name, data_type, data_segment_id=None, producer=None, trace_path=None, unit=None, value_table=None)
¶
Create and return a new object. See help(type) for accurate signature.
from_path(path, data_type=None)
classmethod
¶
Construct a path-only Signal handle. No catalog roundtrip —
the path is verified server-side at the next check / latest
/ query invocation.
Use this when the producer hasn't emitted yet (cold-start tests, fixture order races, extension consumers) or when you want a long-lived handle that re-resolves to the freshest segment on every call rather than binding to a specific one.
data_type is informational only — only the path crosses the
wire (SignalOperand(path)); the agent uses its own resolved
schema for evaluation. Left unset, data_type and unit read
back as None: nothing has verified them yet.
Accepts bare paths ("src/foo.bar"), wildcard-segment paths
("*/src/foo.bar"), and fully-qualified segment paths
("{uuid}/src/foo.bar") — the last pins resolution to a
specific segment.
SignalCatalog
¶
Immutable signal catalog with fast search and exact-path lookup, returned
by agent.signals().
Examples:
paths
instance-attribute
¶
Every canonical path in catalog order.
warnings
instance-attribute
¶
Per-producer / per-trace warnings collected while building the catalog.
__contains__(key)
¶
Return bool(key in self).
by_path(path)
¶
Deprecated alias for catalog[path].
get(path, default=None)
¶
Exact-path lookup that returns default instead of raising when the
catalog has no such signal. Ambiguity still raises AmbiguousSignal.
items()
¶
(path, signal) pairs in catalog order.
keys()
¶
Every path in catalog order, the same list as paths. SignalFrame
and Snapshot answer keys(), values() and items() too.
match(pattern)
¶
Filter the catalog by a glob over the full canonical path.
* and ? span / and ., so "bus0/*", "*.cell_*" and
"bus0/BMS_message/*.cell_0" all work. The same grammar every
paths= argument uses. No match returns an empty catalog.
search(query)
¶
Filter the catalog by case-insensitive substring match.
Each signal is checked across five fields: full path, source, message, signal name, and unit. Matching is case-insensitive; the empty query returns the catalog unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
query
|
str
|
Substring to search for. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
SignalCatalog |
SignalCatalog
|
A new catalog containing only the matched signals; warnings are inherited from the parent. |
to_list()
¶
Deprecated alias for list(catalog).
to_pandas()
¶
Render the catalog as a pandas DataFrame (one row per signal).
Columns: path, source, message, name, data_type, unit,
producer, trace_path, data_segment_id. Raises ImportError
when pandas is not installed.
values()
¶
Every signal in catalog order: list(catalog), by its mapping name.
SignalFrame
¶
A query's rows: one per timestamp, one column per signal, null where a signal had no sample.
It reads like a DataFrame: frame[path] is a SignalSeries,
frame[[a, b]] a sub-frame, frame[mask] the rows a boolean series
keeps, and frame["+10s":"+20s"] a time window.
Examples:
>>> frame = agent.query(["bus0/BMS_message/status.pack_current"], start="-5m")
>>> frame["bus0/BMS_message/status.pack_current"].mean()
arrow_ipc_data
instance-attribute
¶
Decode with pyarrow.ipc.open_stream. A fresh bytes is allocated
per access; users typically call to_arrow() once per
response so the cost is negligible.
column_names
instance-attribute
¶
Deprecated alias of columns.
columns
instance-attribute
¶
Every column label except the time column, as pandas' df.columns
reads: the same labels as keys() and the columns of to_pandas().
downsampled
instance-attribute
¶
True when the agent ran an M4 / downsampled query rather than raw.
dtypes
instance-attribute
¶
Each data column's pandas dtype, indexed by label: the dtypes of
to_pandas(), read without converting the rows.
empty
instance-attribute
¶
Whether the frame has no rows or no data columns, as in pandas.
iloc
instance-attribute
¶
Rows by position, as pandas' iloc: frame.iloc[10:20], and
frame.iloc[10:20, [0, 2]] for some columns, by position or label.
One row, frame.iloc[-1], is a pandas Series; frame.iloc[0, 2]
is one value.
index
instance-attribute
¶
The time axis as pandas reads it: the UTC DatetimeIndex named time
that to_pandas() indexes by, or a TimedeltaIndex of offsets on a
run-aligned frame.
loc
instance-attribute
¶
Rows by time or by a mask, as pandas' loc:
frame.loc["+10s":"+20s"], frame.loc[mask], and
frame.loc[mask, ["bus0/BMS.voltage"]] for some columns. One
timestamp is one row, a pandas Series, as pandas reads it.
meta
instance-attribute
¶
Wire metadata for the data columns, in Arrow order. The time column is not a signal and has no metadata worth reporting, so it is not here.
query_duration_s
instance-attribute
¶
How long the agent took to answer the query, in seconds.
requested_range
instance-attribute
¶
The range the caller ASKED for. time_range reports the data's own
extent; this is what the request said.
shape
instance-attribute
¶
(rows, columns), the time column not counted, as pandas reports it.
signals
instance-attribute
¶
Resolved catalog signals in column order (excludes the time column),
as the SignalCatalog agent.signals() returns:
frame.signals["bus0/BMS.voltage"].unit reads the same way.
time_origin
instance-attribute
¶
Where the frame's time axis starts: "epoch" for wall-clock time,
"run_start" for a TraceSet frame aligned at each run's own start,
None for a frame built locally with no wire metadata.
time_range
instance-attribute
¶
The extent of the DATA: (first, last) timestamp in the frame, None
when it has no rows. The window the caller asked for is
requested_range.
truncated
instance-attribute
¶
True when max_rows > 0 and the result hit the cap.
warnings
instance-attribute
¶
Per-producer / per-trace warnings collected during the query.
__array__(dtype=None, copy=None)
¶
numpy's array protocol: np.asarray(frame) is frame.to_numpy().
pandas.DataFrame(frame) would read that array without the time axis
or the labels, so it raises and names to_pandas().
__arrow_c_stream__(requested_schema=None)
¶
The Arrow PyCapsule stream of to_arrow(): time first, then one
column per label. pandas.DataFrame.from_arrow(frame),
pyarrow.table(frame) and polars.DataFrame(frame) read it.
__contains__(name)
¶
Return bool(key in self).
__iter__()
¶
Iterating a SignalFrame yields its column labels, as a pandas
DataFrame and a dict do: for label in frame: frame[label]. The
catalog Signals are frame.signals.
__setitem__(key, value)
¶
frame[label] = value sets a column in place, as pandas does:
value replaces the column label names, or is appended last under a
new label. frame.assign(label=value) does the same on a copy, and
takes the same values.
Examples:
frame["bus0/BMS.power"] = frame["bus0/BMS.voltage"] * frame["bus0/BMS.current"]
assign(**columns)
¶
A copy of the frame with each keyword set as a column, as pandas'
assign does: a label already in the frame is replaced, a new one is
appended last. A value is a SignalSeries on this frame's time axis,
which brings its unit; a scalar, which fills every row; or one value
per row as a list, tuple, numpy array or pyarrow array. A callable is
called with the frame built so far and its result assigned.
Any assigned column makes the frame derived: its columns stand for the samples held here, and a check evaluates those rather than re-querying.
Examples:
frame.assign(power=lambda f: f["bus0/BMS.voltage"] * f["bus0/BMS.current"])
between(start=None, end=None)
¶
Rows inside [start, end], both anchored on the frame's own extent:
"+10s" counts from the first sample, "-10s" back from the last, and
"start" / "end" are the bounds themselves. An omitted bound is the
matching end of the frame.
Examples:
frame.between("+30s", "+40s") # the second half-minute of the run
corr(**kwargs)
¶
to_pandas().corr(**kwargs): the pandas DataFrame of pairwise
correlations between the data columns, as describe() returns a
pandas one. method= takes "pearson", "kendall" or "spearman".
Examples:
frame.ffill().corr()["bus0/BMS.current"]
describe()
¶
pandas.DataFrame.describe() over the data columns, with each column's
unit as the first row.
Examples:
frame.describe() # unit / count / mean / std / min / quartiles / max
dropna(how='any', subset=None)
¶
Drop rows with nulls: how="any" (default) keeps only rows where every
column has a sample, how="all" drops only the empty rows. subset
names the columns to look at, as a label or a list of them, as in
pandas; the others keep whatever nulls they have.
Examples:
frame.dropna() # rows both messages landed on frame.dropna(subset=["bus0/BMS.voltage"]) # rows with a voltage sample
ffill(limit=None)
¶
Sample-and-hold every column: each null takes the last value before it.
limit fills at most that many nulls in a row, as in pandas.
One row per timestamp means a two-message frame is mostly nulls; this is how you get a dense frame to do arithmetic on.
Examples:
power = frame.ffill()["bus0/BMS.voltage"] * frame.ffill()["bus0/BMS.current"]
fillna(value)
¶
Every null takes value, per column, as pandas' fillna; a column
whose type cannot hold value is left as it is.
Examples:
frame.fillna(0.0)
from_arrow_ipc(data, columns=[], signals=[])
staticmethod
¶
Build a frame from an Arrow IPC stream — the same bytes the wire carries
in arrow_ipc_data — with columns positionally aligned to its schema
(entry 0 describes the time column). Lets local Arrow data and tests use
the frame API without an agent.
signals is the catalog view of the data columns, parallel to
columns[1:]. Pass it and the frame carries units and value tables the
way a queried one does: frame["path"].unit, frame.signals, plots.
from_pandas(df, *, units=None)
staticmethod
¶
Build a frame from a pandas DataFrame: its DatetimeIndex (naive
reads as UTC), or else its time column, is the time axis, and every
other column is a data column labeled by its name. A TimedeltaIndex
is a run-aligned axis of offsets, as to_pandas() writes for one.
Each column's unit comes from units. pandas carries df.attrs
through arithmetic unchanged, so a unit left there is not trusted.
Examples:
df = frame.to_pandas() df["bus0/BMS.power"] = df["bus0/BMS.voltage"] * df["bus0/BMS.current"] frame = SignalFrame.from_pandas(df, units={"bus0/BMS.power": "W"})
groupby(by, **kwargs)
¶
to_pandas().groupby(by, **kwargs), the pandas GroupBy it returns.
by takes column labels, and a SignalSeries on this frame's time
axis groups the rows it lines up with.
Examples:
frame.groupby(frame["bus1/inverter.state"])["bus0/BMS.current"].mean()
head(n=5)
¶
First n rows as a new SignalFrame (default 5).
items()
¶
(label, series) tuples for every non-time column.
keys()
¶
Column labels excluding the leading time column. Pandas-style
iteration, and the same labels to_pandas() puts on its columns.
plot(*columns, signals=None, **kwargs)
¶
Render with zelos_sdk.agent.plot.plot_frame: every column, or only
the ones named — frame.plot("bus0/BMS.voltage") — by the keys
frame[[...]] takes, one per argument or in one list, or as
signals=[...]. Keyword arguments (title=, height=, zero=)
forward verbatim.
The legend names each line by its column label, so
frame.short_names().plot() and frame.rename({...}).plot() choose it.
rename(mapping=None, *, columns=None)
¶
Relabel columns from a {old: new} mapping or a callable over the
current labels, passed alone or as pandas' columns=. Unknown keys
keep their label; two columns landing on one label raise ValueError.
Examples:
frame.rename({"bus0/BMS_message/status.pack_current": "current"}) frame.rename(columns=str.upper)
resample(every, how='mean')
¶
Aggregate onto a fixed time grid: one row per every, combined with
how ("mean", "min", "max", "median", "sum", "std",
"count", "first", "last"), as pandas' resample(every).<how>().
A bucket with no samples is missing, for "sum" too; "count" counts
it as 0. A state (a signal with a value table, a boolean, or text)
takes its last value in each bucket under "mean", "median",
"sum" and "std", because averaging codes invents codes. Units
follow the numbers: "count" is unitless, and the "std" of a
temperature reading is a difference (ΔdegC), as SignalSeries.std()
reports it.
This is the analysis verb pandas users reach for; downsample= on
query() is a chart thinning by bucket count, not a period.
Examples:
frame.resample("1s").to_pandas()
series(key)
¶
Project one column as a Python SignalSeries (pandas/pyarrow ergonomics).
key accepts a str (a column label, a clean catalog path, or the raw
wire column name) or a Signal from the frame's catalog. Ties across
producers / segments raise [errors::AmbiguousSignal] with a hint to
pass a Signal, except on a TraceSet frame, where one path really
does name one column per run: that returns a SeriesByRun mapping.
short_names()
¶
Label each column by its signal name alone, falling back to
message.name where a bare name repeats in this frame. A run frame
shortens within each run and keeps the run: baseline · cell_0.
Examples:
frame.short_names().to_pandas() # columns "cell_0", "pack_current"
tail(n=5)
¶
Last n rows as a new SignalFrame (default 5).
to_arrow()
¶
The frame's rows as a pyarrow.Table, decoded and normalized.
The returned object is a pyarrow.Table with a canonical
time: timestamp[ns, tz=UTC] column and signal columns labeled with
the catalog path source/message.signal. The name mirrors
SignalSeries.to_arrow() (a column accessor) so notebook users have one
symbol to reach for at both the frame and column level.
Cached after the first call; every caller gets a handle to the same immutable Table.
to_numpy(dtype=None)
¶
The data columns as one 2-D numpy array, a row per timestamp and a
column per signal, as pandas' DataFrame.to_numpy(). Nulls read as
NaN; dtype casts, and an integer or boolean dtype refuses a frame
with nulls rather than inventing values. np.asarray(frame) reads
the same array.
to_pandas(*, index=True)
¶
Notebook one-liner: frame.to_pandas(), indexed by a UTC
DatetimeIndex named time. A run-aligned TraceSet frame
(time_origin == "run_start") is indexed by a TimedeltaIndex of
offsets instead. Pass index=False for the older shape, where time
is an ordinary column.
The index is what df.resample("1s"), df.loc["...":] and df.plot()
all read, and it matches SignalSeries.to_pandas(). Columns are labeled
the way the catalog labels signals — source/message.signal — taking a
producer:: prefix only when two columns in one result share a path.
Examples:
frame.to_pandas().resample("1s").mean()
Raises ImportError with an actionable hint if pandas is not
installed; we deliberately do not list it in the SDK's hard
requirements so notebook users opt-in via zelos-sdk[notebook].
values()
¶
One SignalSeries per non-time column (parallel to keys()).
SignalNotFound
¶
Bases: AgentError, KeyError
No signal matched the requested path or pattern.
Carries a computed suggestions: list[str] of the closest catalog
paths, so a caller can surface a "did you mean" hint.
Examples:
>>> try:
... agent.latest('bus0/BMS_message/status.pack_currnt')
... except SignalNotFound as exc:
... exc.suggestions
['bus0/BMS_message/status.pack_current']
SignalSeries(name, values, *, signal=None, unit=None, time=None)
¶
One Arrow column with pyarrow/pandas helpers and a unit-aware math surface.
Unit composes through * and /; + and - require matching
units. Scalars, Arrow arrays, numpy arrays and plain sequences are
unit-blind operands paired by position; a pandas object is refused unless
it sits on the series' own time axis. Values are always a
pa.ChunkedArray; time is the frame's shared time column, so two
series from one frame align by identity.
What the series does natively answers to its pandas name (gt(),
fillna(), shift(), rolling(), index, &); a pandas name it does not have
raises an AttributeError naming the .to_pandas() call that does.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
The signal's path, a caller's |
|
values |
The samples as a |
|
signal |
The catalog |
|
time |
The shared time axis, |
Examples:
>>> frame = agent.query(["bus0/BMS_message/status.pack_current"], start="-5m")
>>> series = frame["bus0/BMS_message/status.pack_current"]
>>> series.mean().unit
'A'
dtype
property
¶
The numpy dtype pandas would give these samples, as Series.dtype.
empty
property
¶
Whether the series has no samples at all.
iloc
property
¶
Position-based access by its pandas name: series.iloc[0],
series.iloc[-1] and series.iloc[2:5] are series[0],
series[-1] and series[2:5].
index
property
¶
The time axis as pandas reads it: the UTC DatetimeIndex named
time that to_pandas() carries, or a RangeIndex without one.
legend
property
¶
Display label as "name (unit)", falling back to whichever exists.
loc
property
¶
Time-based access by its pandas name: series.loc["+10s":"+20s"]
keeps a window, both ends included, in SignalFrame.between's
grammar; series.loc[mask] keeps the samples a boolean series is
true on; series.loc[timestamp] is the sample at that instant.
null_count
property
¶
Number of null samples in the series.
shape
property
¶
(len(series),), as pandas reports it.
size
property
¶
Number of samples, nulls included: len(series).
unit
property
¶
The series' unit as a display string, None when unknown.
abs()
¶
Magnitude of every sample, unit kept; the magnitude of a temperature reading, a distance from an arbitrary zero, has none.
add(other, fill_value=None)
¶
series + other, by its pandas name.
all()
¶
Whether every present sample is true (nonzero); True when none is present.
any()
¶
Whether any present sample is true (nonzero); False when none is present.
asof(where)
¶
The last present sample at or before where, as pandas' asof;
nan before the first sample. where reads the between()
grammar: an absolute time, "+10s", "-10s".
astype(dtype)
¶
Cast every sample to dtype (a numpy dtype or its name, a Python
type, or a pa.DataType), keeping the unit. A float truncates toward
zero on its way to an integer, as in pandas; nulls stay nulls.
between(left, right, inclusive='both')
¶
Boolean series, True where the sample lies between left and
right, as pandas' Series.between: inclusive is "both",
"neither", "left" or "right". For rows inside a time
window, slice by time: series["+10s":"+20s"].
clip(lower=None, upper=None, **kwargs)
¶
Clamp values to [lower, upper]. At least one bound is required.
A bound is a number, or a series aligned by time as pandas aligns it
and converted to this series' unit; where it is missing, that side
is unbounded. numpy's own keywords are accepted at their defaults,
so np.clip(series, 0, 30) stays a series.
corr(other, method='pearson')
¶
Correlation with other, aligned by time as pandas aligns it:
"pearson", "spearman" or "kendall".
count()
¶
Number of non-null values, as an int — not a NamedScalar.
cumsum(axis=None, skipna=True, **kwargs)
¶
Running total over the samples, unit kept, skipping a missing one
as pandas does; np.cumsum(series) too. A boolean series counts
its true samples, so s.ne(s.shift(fill_value=False)).cumsum()
numbers the runs of s.
derivative()
¶
Numerical derivative over time: centered differences
(np.gradient), which average each sample's two neighbors. For the
rate between consecutive samples, divide diff() by the step.
derive(values=None, *, label=None, unit=None)
¶
Deprecated alias of from_arrow on this series' time axis.
describe()
¶
pandas' summary (count, mean, std, min, quartiles, max) with the
unit as its first row, as frame.describe() gives each column.
diff()
¶
Delta to the previous sample; the first value is null. Two
temperature readings differ by a ΔdegC, not a degC.
div(other, fill_value=None)
¶
series / other, by its pandas name.
dropna()
¶
Drop null samples, keeping time aligned with the values that remain.
eq(other)
¶
series == other, by its pandas name.
ffill(limit=None)
¶
the first limit nulls of each gap, as in pandas.
fillna(value)
¶
Each null takes value, unit kept: (speed == 0).fillna(False).
A series fills each gap with its own sample at that time, as pandas
aligns it, converted to this series' unit: speed.fillna(gps_speed).
from_arrow(values, *, name=None, unit=None, time=None)
classmethod
¶
Series from Arrow-compatible values, checked against time.
from_pandas(series, *, unit=None, name=None)
classmethod
¶
Series from pandas; a DatetimeIndex becomes the UTC time axis.
The unit is unit: pandas carries attrs through arithmetic
unchanged, so a unit found there may no longer be true.
ge(other)
¶
series >= other, by its pandas name.
get()
¶
Deprecated alias of to_pandas.
groupby(by, **kwargs)
¶
pandas' groupby on this series, returning the pandas groupby:
power.groupby(state).mean(). by is a series on the same time
axis (a state, run numbers from cumsum()), or anything pandas takes.
gt(other)
¶
series > other, by its pandas name.
head(n=5)
¶
The first n samples, time kept alongside.
idxmax()
¶
Where the largest sample is: its timestamp from index, or its
position when the series has no time axis.
idxmin()
¶
Where the smallest sample is: its timestamp from index, or its
position when the series has no time axis.
integrate()
¶
Cumulative trapezoidal integration over time; first sample is 0.
Neighboring samples are joined by a straight line, so a series cut
down by a mask integrates across the gaps the mask left. To integrate
only while a condition holds, zero the rest instead:
current.where(current > 100).fillna(0.0).integrate().
isin(values)
¶
Boolean series, True where the sample is one of values:
fault.isin([3, 7]). A missing sample matches nothing.
isna()
¶
Boolean series, True wherever the sample is missing.
isnull()
¶
pandas' other name for isna.
le(other)
¶
series <= other, by its pandas name.
lt(other)
¶
series < other, by its pandas name.
max(axis=None, skipna=True, **kwargs)
¶
Largest present sample; nan when there is none.
mean(axis=None, skipna=True, **kwargs)
¶
Arithmetic mean over the present samples.
median(axis=None, skipna=True, **kwargs)
¶
Exact median of the present samples.
min(axis=None, skipna=True, **kwargs)
¶
Smallest present sample; nan when there is none.
mul(other, fill_value=None)
¶
series * other, by its pandas name.
ne(other)
¶
series != other, by its pandas name.
notna()
¶
Boolean series, True wherever a sample is present.
notnull()
¶
pandas' other name for notna.
nunique(dropna=True)
¶
Number of distinct values; dropna=False counts a missing sample
as one more, as in pandas.
plot(**kwargs)
¶
Render with zelos_sdk.agent.plot.plot_series. Keyword
arguments (title=, height=, zero=) forward verbatim.
pow(other, fill_value=None)
¶
series ** other, by its pandas name.
quantile(q=0.5)
¶
The q quantile of the present samples, interpolated linearly as
in pandas and named by percentile, as in p95(...). A list of
quantiles returns a pandas.Series indexed by q, as pandas does.
radd(other, fill_value=None)
¶
other + series, by its pandas name.
rate()
¶
Change per second from each sample to the next: diff() over the
time between the two, in unit/s; the first value is null. Unlike
derivative(), nothing is averaged, so a step shows at full height.
rdiv(other, fill_value=None)
¶
other / series, by its pandas name.
reindex(index, method=None, tolerance=None)
¶
This series on another time axis, as pandas' reindex: a
timestamp it lacks is missing, or with method="ffill" takes the
sample before it. Put one signal on another's axis to combine them:
speed * gps.reindex(speed.index, method="ffill"). index is a
pandas index or a series, whose timestamps are used.
rename(name)
¶
Same samples under a new name — what a derived series wants.
replace(to_replace=None, value=_KEEP, regex=False)
¶
Values swapped as pandas' replace swaps them, time and unit
kept: series.replace([np.inf, -np.inf], np.nan). A NaN is a
missing sample, as everywhere in a series.
resample(every, how=None)
¶
This series on a fixed time grid of every, one bucket per step,
as in pandas: speed.resample("1s").mean(), or as a frame takes
it, speed.resample("1s", how="mean"). An empty bucket is missing,
and count() counts it as 0. A boolean's mean is the fraction
of samples it was true; a text series takes each bucket's last value
under mean, median, sum and std.
rmul(other, fill_value=None)
¶
other * series, by its pandas name.
rolling(window, min_periods=None, center=False)
¶
A moving window to aggregate: window samples, or a duration
("1s", a timedelta) over time, as in
speed.rolling("1s").mean(). As in pandas, a sample window needs
window present samples unless min_periods says fewer, a
duration window needs one, and center=True labels each window by
its middle sample rather than its last.
round(decimals=0)
¶
Each sample rounded to decimals places, unit kept; round(series) too.
rsub(other, fill_value=None)
¶
other - series, by its pandas name.
runs(min_samples=None, min_duration=None)
¶
Each stretch of true samples in a boolean series, one row per run:
its start, end, duration and number of samples.
min_samples and min_duration keep only the runs that long:
(speed == 0).runs(min_duration="2s").
A run lasts until the sample that ends it, so one true sample at 4 Hz
lasts 250 ms. A run cut off by either end of the capture, whose true
start or end is unseen, has complete False: its duration is only
what was recorded. A missing sample ends a run, since a comparison
reads it as False, so on a sparse signal compare the sample-and-held
series: (temp.ffill() > 20).runs().
shift(periods=1, freq=None, fill_value=None)
¶
Each sample moved periods rows later on the same time axis
(earlier when negative); the rows it leaves hold fill_value, a
null by default. With freq, as in pandas, the samples stay and
their times move periods times freq: speed.shift(freq="1s").
std(axis=None, skipna=True, ddof=1, **kwargs)
¶
Sample standard deviation (ddof=1, as in pandas; np.std
passes 0). A spread of temperature readings is a difference:
ΔdegC.
sub(other, fill_value=None)
¶
series - other, by its pandas name.
sum(axis=None, skipna=True, **kwargs)
¶
Total of the present samples. A boolean series counts its true
samples and returns an int, as count() does.
tail(n=5)
¶
The last n samples, time kept alongside.
time_arrow()
¶
Deprecated alias of the time attribute.
to(unit)
¶
The same quantity in unit: power.to("kW"),
energy.to("Wh"), speed.to("rad/s"). SI prefixes and the common
engineering units convert; a degC reading converts to degF with
the offset, and a ΔdegC difference by scale alone.
to_arrow()
¶
The samples as a pa.ChunkedArray, without copying.
to_list()
¶
The samples as a Python list, a missing one as None.
to_numpy(dtype=None)
¶
float64 numpy array; nulls become NaN. The fast path for bulk math.
dtype casts the samples first, as pandas does; a dtype that cannot
hold NaN refuses a series with nulls rather than inventing values.
to_pandas()
¶
pandas.Series (UTC DatetimeIndex when timed, unit in attrs).
tolist()
¶
pandas' other name for to_list.
truediv(other, fill_value=None)
¶
pandas' other name for div.
unique()
¶
The distinct values in order of first appearance, as a numpy array;
a missing sample is nan, as in pandas.
value_counts()
¶
How often each present value occurs, most frequent first, as a
pandas.Series indexed by value: a state or fault code's histogram.
var(axis=None, skipna=True, ddof=1, **kwargs)
¶
Sample variance (ddof=1, as in pandas), in the square of the
unit a spread has: V², ΔdegC².
where(mask, other=None)
¶
Keep values where mask is True; the rest become other,
missing by default, as pandas' where. other is a number, or a
series aligned by time as pandas aligns it and converted to this
series' unit.
with_unit(unit)
¶
Assert a unit (no conversion).
Snapshot
¶
The values of several signals as of one moment: what latest(paths),
at(paths, time) and each watch() tick hand back.
A read-only Mapping[str, LatestValue] keyed by clean path, so every
spelling that worked against the plain dict still works — snapshot[path],
in, len, .get, .keys(), .values(), .items(), iteration, and
dict(snapshot). time is the newest sample time in the set.
Examples:
SortOrder
¶
Asc / Desc — drives row ordering for Agent.query() /
Trace.query(). Wire encoding mirrors the proto's SortOrder enum
(Asc=0, Desc=1).
Examples:
>>> agent.query(["bus0/BMS_message/status.pack_current"], start="-5m", sort_order=SortOrder.Desc)
>>> SortOrder.Asc.value
'asc'
SuiteValidationError(source, index, message)
¶
One problem validate_suite found in a check suite file.
Attributes:
| Name | Type | Description |
|---|---|---|
source |
Path of the suite file. |
|
index |
0-based index of the failing entry, or |
|
message |
What is wrong, naming the entry's |
Examples:
>>> for error in validate_suite("checks/live.json"):
... print(error.source, error.index, error.message)
TimeMode
¶
TimeRange
¶
Inclusive datetime interval [start, end].
Examples:
>>> trace = agent.trace("run.trz")
>>> trace.time_range.start, trace.time_range.end
(datetime.datetime(2026, 8, 1, 12, 0, tzinfo=datetime.timezone.utc), datetime.datetime(2026, 8, 1, 12, 5, tzinfo=datetime.timezone.utc))
duration
instance-attribute
¶
end - start, as a timedelta.
end
instance-attribute
¶
Last instant in the interval.
end_ns
instance-attribute
¶
end as epoch nanoseconds, the precision a datetime cannot hold.
start
instance-attribute
¶
First instant in the interval.
start_ns
instance-attribute
¶
Epoch nanoseconds. Prefer this over .start / .end when precision
matters: a Python datetime only holds microseconds.
TimeRangeMulti
¶
Multi-trace time range response (overlay / absolute mode): the union
extent across every run, plus each run's own TraceTiming.
Examples:
>>> timing = TraceTiming("a.trz", start=trace.time_range.start, duration=trace.time_range.duration)
>>> multi = TimeRangeMulti(trace.time_range.start, trace.time_range.end, max_duration=trace.time_range.duration, traces=[timing])
>>> multi.max_duration, len(multi.traces)
(datetime.timedelta(seconds=300), 1)
end
instance-attribute
¶
Latest end across every run.
max_duration
instance-attribute
¶
The longest run's duration, as a timedelta.
max_duration_s
instance-attribute
¶
The longest run's duration, in seconds.
start
instance-attribute
¶
Earliest start across every run.
traces
instance-attribute
¶
One entry per run, in the order the set was opened.
Trace
¶
Handle to a trace file opened through Agent.trace.
Owns an Arc<AgentTransport> cloned from its parent Agent, so the
trace shares the agent's connection lifecycle: calling agent.close()
invalidates this handle too, and any subsequent method on it raises
AgentCancelled.
Time arguments anchor on this file, not on wall-clock now: start="-30s"
is the last 30 seconds OF THE FILE. The frame's time axis is always epoch.
Examples:
>>> trace = agent.trace("run.trz")
>>> df = trace.query(["bus0/BMS_message/status.pack_current"], start="-30s").to_pandas()
>>> trace.close()
check
instance-attribute
¶
Typed predicate Check API bound to this trace file.
Returns a TraceChecks proxy whose default is temporal="always"
over the trace's full [start_ns, end_ns] — matches the natural
reading of trace.check.that('voltage', '<', 42) ("is voltage < 42
across this recording?"). Override with last= (anchored at the
trace's end_ns, not wall-clock), start_ns=+end_ns=, or
temporal="latest".
path
instance-attribute
¶
How this trace was addressed: its path on disk, or a cloud trace's
zelos://open?org=…&trace=… deep link, which a console URL becomes.
time_range
instance-attribute
¶
The trace file's own time extent.
__enter__()
¶
Enable with agent.trace(path) as trace: — returns self.
__exit__(_exc_type=None, _exc_value=None, _traceback=None)
¶
Close the trace handle on context exit. Mirrors the try/finally
trace.close() pattern; idempotent on multiple closes.
at(paths, time=None, *, min_time=None)
¶
Per-signal value at (or before) a moment inside the trace.
time and min_time use the same source-anchored grammar as query
and default to the file's end. One literal path returns a LatestValue;
a wildcard or a sequence returns a Snapshot keyed by signal path.
Raises SignalNotFound on catalog miss; ValueError if min_time > time.
close()
¶
Release the agent's hold on this trace file.
A hint, not a shutdown: it tells the agent it may drop the catalog and
indices it loaded, and the agent reopens the file on demand the next
time this handle is used. Idempotent; the handle stays usable, and only
a file that has since left the disk raises TraceNotFound. The
per-handle catalog cache is kept — a trace file cannot change.
export(path, *, start=None, end=None, overwrite=False, format=None, relative_start=None, relative_end=None)
¶
Export this trace (or a slice of it) to a new .trz file.
start / end use the same source-anchored grammar as query; omitting
both exports the full trace. format defaults to the agent's release
default (legacy DuckDB), except over a TRZ2 input, which re-exports as
TRZ2.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Destination path for the new |
required |
start
|
datetime | str | None
|
Slice start; defaults to the file's start. |
None
|
end
|
datetime | str | None
|
Slice end; defaults to the file's end. |
None
|
overwrite
|
bool
|
Replace |
False
|
format
|
str | None
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
ExportResult |
ExportResult
|
On-disk path plus per-producer success bits. |
info()
¶
Aggregate trace inspection — one round-trip for file size, time
range, segments, per-table row counts, and total data rows.
Same shape as zelos trace info.
latest(paths)
¶
The file's last value for each path — at(paths, "end").
One literal path returns a LatestValue, anything else a Snapshot.
query(paths, *, start=None, end=None, downsample=None, max_rows=0, sort=None, sort_order=None, relative_start=None, relative_end=None)
¶
Time-bounded query against this trace file.
Resolves paths against the trace's catalog (trace mode is
authoritative — literal-path misses raise SignalNotFound).
start / end anchor on THIS FILE: "-30s" is 30 s before its end,
"+30s" is 30 s after its start, "start" / "end" / "now" are its
bounds, and a datetime, ISO 8601 string or epoch-ns int is taken
literally. An omitted bound is the matching end of the file. A window
entirely outside the file raises ValueError naming the file's extent;
one that only overhangs is clamped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paths
|
PathsArg
|
A |
required |
start
|
datetime | str | None
|
Window start; defaults to the file's start. |
None
|
end
|
datetime | str | None
|
Window end; defaults to the file's end. |
None
|
downsample
|
int | None
|
Optional M4 bucket count — same semantics as
|
None
|
max_rows
|
int
|
Cap on returned rows; |
0
|
sort
|
SortArg | None
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
SignalFrame |
SignalFrame
|
Arrow-backed frame on an epoch time axis. |
Raises:
| Type | Description |
|---|---|
SignalNotFound
|
catalog miss for a literal path. |
ValueError
|
an unparsable or out-of-range window. |
segments()
¶
List the trace file's data segments.
A segment is a contiguous chunk inside the trace bounded by a
TraceSegmentStart / TraceSegmentEnd pair. Per-trace read
warnings are emitted as RuntimeWarning, matching Agent.segments;
use segments_with_warnings() to receive them as a list instead.
segments_with_warnings()
¶
Same as segments() but returns (segments, warnings).
signals()
¶
Fetch the trace's signal catalog.
Cached per-handle: the first call hits the wire, every subsequent
call returns the same SignalCatalog (trace files are immutable
— the catalog cannot drift). Supports search(), catalog[path],
and slicing — same shape as the live agent catalog.
Returns:
| Name | Type | Description |
|---|---|---|
SignalCatalog |
SignalCatalog
|
All signals present in this trace. |
window(paths, *, start, duration, display_fps=0)
¶
Replay snapshot + change-stream over a window inside this trace.
Same semantics as Agent.window() scoped to this trace. start uses the
source-anchored grammar; duration accepts int/float seconds, a
datetime.timedelta, or a string like "30s". display_fps=0 means
"no thinning". Raises ValueError on duration <= 0 or display_fps < 0.
TraceNotFound
¶
TraceSet
¶
Handle to a set of trace files opened together for multi-trace queries.
Returned by Agent.traces([...]). Like Trace, holds an
Arc<AgentTransport> cloned from its parent Agent and shares the
agent's connection lifecycle.
Each member file is a RUN, named by runs (the file stems, deduplicated).
len(traces), traces[label], and iteration over (label, Trace) address
them individually.
Bounds given as source-anchored offsets (or omitted) align the runs at
t = 0 and the frame's axis is seconds from each run's start; bounds given
as absolute instants apply once and the axis is epoch.
Examples:
>>> ts = agent.traces(["a.trz", "b.trz"])
>>> df = ts.query(["bus1/inverter_status.rpm"], end="+30s").to_pandas()
>>> ts.close()
check
instance-attribute
¶
Typed predicate Check API bound to this set. Each assertion returns a
dict[run label, CheckResult].
max_duration
instance-attribute
¶
The longest run's duration — what time_range.max_duration returned
when time_range was a TimeRangeMulti.
paths
instance-attribute
¶
How each trace was addressed (path on disk, or a cloud trace's
zelos:// deep link), in the order the set was opened.
runs
instance-attribute
¶
The run labels, in the order the set was opened.
time_range
instance-attribute
¶
The union extent across every run.
traces
instance-attribute
¶
Per-run timing entries, one per run — what time_range.traces
returned when time_range was a TimeRangeMulti.
__enter__()
¶
Enable with agent.traces(paths) as traces: — returns self.
__exit__(_exc_type=None, _exc_value=None, _traceback=None)
¶
Close every trace in the set on context exit. Mirrors the
try/finally traces.close() pattern; idempotent on multiple closes.
at(paths, time=None, *, min_time=None)
¶
Per-signal value at (or before) a moment, across all member runs.
time and min_time resolve against the set's union extent and default
to its end. One literal path returns a LatestValue; a wildcard or a
sequence returns a Snapshot keyed by signal path.
close()
¶
Release the agent's hold on every trace in the set.
Idempotent. Same semantics as Trace.close() but issued for
every member trace in one RPC.
export(path, *, start=None, end=None, overwrite=False, format=None, time_mode=None, relative_start=None, relative_end=None)
¶
Export the union of all member runs to a single new .trz file.
Same bounds contract as TraceSet.query(): offset bounds slice each run
from its own start, absolute bounds slice once on wall-clock.
latest(paths)
¶
The last value for each path across the set — at(paths, "end").
One literal path returns a LatestValue, anything else a Snapshot.
query(paths, *, start=None, end=None, downsample=None, max_rows=0, sort=None, sort_order=None, time_mode=None, relative_start=None, relative_end=None)
¶
Time-bounded query joining results across every member run.
start / end take the same grammar as Trace.query. Offset bounds
("+30s", "-30s", "start", "end") — or no bounds at all — align
the runs at t = 0 and the frame's axis is seconds from each run's own
start; a datetime or ISO instant selects the epoch axis instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paths
|
PathsArg
|
A |
required |
start
|
datetime | str | None
|
Window start; defaults to each run's start. |
None
|
end
|
datetime | str | None
|
Window end; defaults to the longest run's end. |
None
|
downsample
|
int | None
|
Optional M4 bucket count; mutually exclusive with
|
None
|
max_rows
|
int
|
Cap on returned rows; |
0
|
sort
|
SortArg | None
|
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
SignalFrame |
SignalFrame
|
Arrow-backed frame; one column per |
segments()
¶
List per-trace segments across every member trace.
Segment entries carry their owning trace_path so callers can
group them. Read warnings surface as RuntimeWarning; use
segments_with_warnings() to receive them as a list instead.
segments_with_warnings()
¶
Same as segments() but returns (segments, warnings).
series(frame, path)
¶
One SignalSeries per run for path, keyed by run label.
A set frame lays out one column per (run, signal); this is the run
dimension a caller can name, resolved through each column's trace_path.
frame["path"] cannot do it on its own — the frame does not know the
set's labels.
signals()
¶
Fetch the union signal catalog across every member trace.
Cached per handle. Each signal carries its source file in
signal.trace_path, so a later query routes to the right one.
Returns:
| Name | Type | Description |
|---|---|---|
SignalCatalog |
SignalCatalog
|
Union of all member-trace catalogs. |
window(paths, *, start, duration, display_fps=0)
¶
Replay snapshot + change-stream over a window across all member runs.
start resolves against the set's union extent; duration accepts
int/float seconds, a datetime.timedelta, or a string like "30s".
TraceTiming
¶
One run's timing entry inside a TimeRangeMulti (per-trace start and duration).
Examples:
>>> timing = TraceTiming("a.trz", start=trace.time_range.start, duration=trace.time_range.duration)
>>> timing.path, timing.start, timing.duration
('a.trz', datetime.datetime(2026, 8, 1, 12, 0, tzinfo=datetime.timezone.utc), datetime.timedelta(seconds=300))
duration
instance-attribute
¶
How long the run spans, as a timedelta.
duration_s
instance-attribute
¶
How long the run spans, in seconds.
path
instance-attribute
¶
Path of the trace file this timing describes.
start
instance-attribute
¶
When the run starts, as a datetime.
start_s
instance-attribute
¶
When the run starts, as epoch seconds.
UnsupportedAgentService
¶
Bases: AgentError
The agent is reachable but does not implement the RPC.
Usually means the SDK is newer than the running agent build.
Examples:
Upload
¶
A cloud upload in progress, returned by Agent.upload(...).
The upload runs on the agent; this handle polls it. It stays valid for the life of the agent connection.
Examples:
>>> upload = agent.upload(live=True, wait=False)
>>> while not (status := upload.status()).done:
... print(status.phase, status.bytes_done, status.bytes_total)
>>> agent.download(upload.url)
organization
instance-attribute
¶
The organization the trace landed in (the slug given, or the active organization the agent resolved).
trace_id
instance-attribute
¶
The cloud trace's id.
url
instance-attribute
¶
The trace's page in the console, https://<console>/<org>/traces/<id>:
the link to share, and what Agent.download and Agent.trace take.
None from an agent too old to report it.
cancel()
¶
Request cancellation; the agent's detached task owns the teardown.
status()
¶
Poll the upload once.
Raises:
| Type | Description |
|---|---|
AgentError
|
the agent no longer knows this upload (finished uploads are evicted from its registry under pressure). |
wait(timeout=None, poll_interval=0.5)
¶
Block until the upload reaches a terminal phase.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float | None
|
Seconds before raising |
None
|
poll_interval
|
float
|
Seconds between status polls. |
0.5
|
Raises:
| Type | Description |
|---|---|
UploadFailed
|
the agent reported the upload failed. |
TimeoutError
|
|
KeyboardInterrupt
|
|
UploadFailed
¶
Bases: AgentError
A cloud upload the agent reported as failed.
The upload runs detached on the agent, so the failure arrives as the
terminal failed phase of Upload.wait() rather than as an error on
any one call; str(exc) carries the agent's (credential-redacted) reason.
Examples:
UploadStatus
¶
One progress sample of a cloud upload.
phase is one of exporting, sealing, uploading, finalizing,
ready, failed; bytes_total is 0 until the agent knows the size.
Examples:
>>> status = upload.status()
>>> status.phase, status.bytes_done, status.bytes_total
('uploading', 1048576, 8388608)
bytes_done
instance-attribute
¶
Bytes moved so far.
bytes_total
instance-attribute
¶
Bytes the upload will move; 0 until the agent knows.
done
instance-attribute
¶
True once the upload reached ready or failed.
error
instance-attribute
¶
The agent's failure reason once phase == "failed", else None.
phase
instance-attribute
¶
The upload's phase, as a lower-case name.
connect(target=None, *, timeout=5.0)
¶
Connect to an agent, checking it answers before returning.
The canonical entry point: from zelos_sdk import connect. Prefer the lazy
Agent(target) for long-running scripts and pytest fixtures — its channel
reconnects on its own and RPC errors surface at the call site.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
str | None
|
Agent endpoint; |
None
|
timeout
|
float
|
Seconds to wait for the health check. |
5.0
|
Returns:
| Name | Type | Description |
|---|---|---|
Agent |
Agent
|
A handle whose agent answered. |
Raises:
| Type | Description |
|---|---|
AgentUnavailable
|
nothing answered before |
UnsupportedAgentService
|
something answered but is not a Zelos agent. |
Examples:
parse_connect_target(target=None)
¶
Normalize a connect-target string into the URL connect() would use,
without actually connecting — for pre-flight config validation in CI.
None falls back to ZELOS_AGENT_URL, then http://127.0.0.1:2300.
host:port and bare host get an http:// scheme and, for a bare
host, the default port; grpc:// normalizes to http://.
Raises:
| Type | Description |
|---|---|
ConnectionTargetError
|
the string has whitespace, more than one
|
Examples:
parse_duration(value, *, allow_zero=True)
¶
Non-negative seconds.
Accepts int/float seconds, timedelta, or the unsigned
duration-string grammar ("30s", "2m" or "2min", "1.5h", "500ms").
Rejects bool, negative values, NaN/inf, and (optionally) zero. A
signed string ("-30s") is a time offset, not a duration — rejected
with a hint to use parse_time instead.
Examples:
parse_time(value)
¶
Normalize to a UTC-aware datetime.
Accepts a datetime (naive → assumed UTC), "now", ISO 8601, or a
relative offset ("-30s", "-2m", "-1.5h", "-1d", "+30s").
Relative forms resolve against the wall clock.
Examples:
parse_time_ns(value)
¶
Coerce a flexible time argument to epoch nanoseconds.
Same surface as parse_time plus integer / float / None
pass-through:
None→None(caller decides what unset means).int→ epoch ns, but only if unambiguous (>= 1e15, i.e. after 1970-01-12); a smaller int is almost certainly seconds by mistake and raises with a hint.float→ epoch seconds, unless it is already too large to be seconds (>= 1e15, year 31 million), in which case it is read as epoch ns.datetime/str→parse_timethen converted.
Used at any agent SDK boundary that takes start_ns / end_ns
on the wire — the Check API, custom replay helpers, suite runners.
Examples:
parse_until(value)
¶
Absolute deadline from datetime/ISO-string, or now + N seconds.
A float means "this many real seconds from now". A string in the
unsigned duration grammar ("30s", "1.5h") means the same thing.
Other string forms go through parse_time and resolve the same
way, against the wall clock.
Examples:
validate_suite(path)
¶
Validate a JSON check suite without running any checks.
Checks every entry against the suite rules Checks.suite enforces,
without contacting an agent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | PathLike[str] | Iterable[Any]
|
A suite file, a directory (every |
required |
Returns:
| Type | Description |
|---|---|
list[SuiteValidationError]
|
One |
list[SuiteValidationError]
|
empty list means the suite is valid. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
|
Examples: