lab-sync-driver-protocol 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ """Shared wire contract for the driver protocol.
2
+
3
+ Hand-written newline-delimited JSON framing -- no codegen. Importable by the
4
+ supervisor AND by out-of-process drivers, so a driver speaks the protocol without
5
+ importing anything of the supervisor's. Transport is the child process's
6
+ stdin/stdout; see `wire` for the frame schema.
7
+ """
8
+
9
+ from lab_sync_driver_protocol.environment import CONFIGURATION_ENV
10
+ from lab_sync_driver_protocol.environment import DATA_DIR_ENV
11
+ from lab_sync_driver_protocol.wire import PROTOCOL_VERSION
12
+ from lab_sync_driver_protocol.wire import STREAM_LIMIT
13
+ from lab_sync_driver_protocol.wire import decode
14
+ from lab_sync_driver_protocol.wire import encode
15
+ from lab_sync_driver_protocol.wire import read_frame
16
+ from lab_sync_driver_protocol.wire import write_frame
17
+
18
+ __all__ = [
19
+ "CONFIGURATION_ENV",
20
+ "DATA_DIR_ENV",
21
+ "PROTOCOL_VERSION",
22
+ "STREAM_LIMIT",
23
+ "decode",
24
+ "encode",
25
+ "read_frame",
26
+ "write_frame",
27
+ ]
@@ -0,0 +1,17 @@
1
+ """Per-instance values the supervisor hands a driver process through its environment.
2
+
3
+ The launch command is a driver *type's* (shared by every instance), and stdin is the protocol channel, so anything the supervisor knows about
4
+ one *instance* before the process starts rides the environment. Both are set on every spawn; a driver that does not read them is unaffected.
5
+ """
6
+
7
+ #: The hardware variant this device was added as -- one of the names the driver
8
+ #: lists as its ``configurations`` in ``describe`` -- or unset for a driver that
9
+ #: declares none, or a device added before the driver declared any (the driver's
10
+ #: first configuration is then the one it runs as).
11
+ CONFIGURATION_ENV = "STUDIO_DEVICE_CONFIGURATION"
12
+
13
+ #: This instance's own writable directory (also the process's working
14
+ #: directory). The supervisor searches it when serving a relative ``model_path`` or
15
+ #: ``top_view_image_path``, so a driver that generates a file per instance --
16
+ #: a 3D model assembled for the configuration it runs as -- writes it here.
17
+ DATA_DIR_ENV = "STUDIO_DEVICE_DATA_DIR"
File without changes
@@ -0,0 +1,65 @@
1
+ """Newline-delimited JSON framing for the driver protocol.
2
+
3
+ One JSON object per line over the child process's stdin/stdout; stdout carries
4
+ protocol frames only, stderr is the driver's log channel. The frame schema is
5
+ transport-shaped -- a request `id` plus a `type` discriminator -- so unary
6
+ replies and the Execute progress stream share one duplex pipe, and a driver may
7
+ later push un-correlated frames (telemetry) without a pending request.
8
+
9
+ Request (supervisor -> driver): {"id": <int>, "method": <str>, "params": {...}}
10
+ Reply (driver -> supervisor): {"id": <int>, "type": "reply", "result": {...}}
11
+ Execute stream: {"id", "type": "progress", "fraction", "message"}
12
+ ... -> {"id", "type": "result", "output_json"}
13
+ or {"id", "type": "error", "code", "message"}
14
+ Telemetry (driver -> supervisor): {"type": "telemetry", "channel": <str>, "payload": {...}}
15
+
16
+ Telemetry is the un-correlated case this framing was shaped for: the frame
17
+ carries no `id` because nothing requested that particular reading, and a reader
18
+ must route on `type` before looking for one. It is **demand-gated rather than
19
+ free-running** -- a driver samples nothing until the supervisor says to:
20
+
21
+ supervisor -> driver: {"id", "method": "set_telemetry_demand",
22
+ "params": {"channels": [<str>, ...]}}
23
+ driver -> supervisor: {"id", "type": "reply", "result": {"channels": [...]}}
24
+
25
+ `channels` is the complete set that should be running, not a delta, so a lost
26
+ or reordered frame cannot leave a sampler stuck on. A driver that declares no
27
+ telemetry channels never sees the method, and one that receives a channel it
28
+ does not know ignores it rather than erroring -- both keep a supervisor and a driver
29
+ at different versions working together.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import json
35
+ from typing import TYPE_CHECKING
36
+ from typing import cast
37
+
38
+ if TYPE_CHECKING:
39
+ import asyncio
40
+
41
+ PROTOCOL_VERSION = "v0"
42
+
43
+ # Bump asyncio's 64 KiB line cap: a single Execute params/output frame may be
44
+ # larger than the default StreamReader limit, and readline() would raise.
45
+ STREAM_LIMIT = 2**24
46
+
47
+
48
+ def encode(frame: dict[str, object]) -> bytes:
49
+ return (json.dumps(frame) + "\n").encode("utf-8")
50
+
51
+
52
+ def decode(line: bytes) -> dict[str, object]:
53
+ return cast("dict[str, object]", json.loads(line))
54
+
55
+
56
+ async def read_frame(reader: asyncio.StreamReader) -> dict[str, object] | None:
57
+ line = await reader.readline()
58
+ if not line:
59
+ return None
60
+ return decode(line)
61
+
62
+
63
+ async def write_frame(writer: asyncio.StreamWriter, frame: dict[str, object]) -> None:
64
+ writer.write(encode(frame))
65
+ await writer.drain()
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: lab-sync-driver-protocol
3
+ Version: 0.3.0
4
+ Summary: Versioned stdio wire contract between the device engine and out-of-process device drivers
5
+ Author: Lab Sync
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Classifier: Topic :: Scientific/Engineering
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.14
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # lab-sync-driver-protocol
20
+
21
+ The versioned wire contract between device drivers and the supervisor that
22
+ launches them. A driver runs as a child process and the two talk
23
+ newline-delimited JSON over the child's stdin and stdout: stdout is the
24
+ protocol channel and nothing else may write to it, stderr is the driver's log
25
+ channel. The package is importable by both sides and depends on nothing, so a
26
+ driver speaks the protocol without importing anything of the supervisor's.
27
+
28
+ Most driver authors want [`lab-sync-driver-sdk`](https://pypi.org/project/lab-sync-driver-sdk/),
29
+ which implements the driver side of this protocol from a decorated Python
30
+ class. Depend on `lab-sync-driver-protocol` directly only to implement the protocol
31
+ yourself, in Python or as a reference for another language.
32
+
33
+ ```bash
34
+ pip install lab-sync-driver-protocol
35
+ ```
36
+
37
+ ## Frames
38
+
39
+ One JSON object per line. Every request carries an `id`, and every frame a
40
+ driver sends in answer carries the same `id` and a `type`:
41
+
42
+ ```text
43
+ Request (supervisor -> driver): {"id": <int>, "method": <str>, "params": {...}}
44
+ Reply (driver -> supervisor): {"id": <int>, "type": "reply", "result": {...}}
45
+ Execute stream: {"id", "type": "progress", "fraction", "message"}
46
+ ... -> {"id", "type": "result", "output_json"}
47
+ or {"id", "type": "error", "code", "message"}
48
+ Telemetry (driver -> supervisor): {"type": "telemetry", "channel": <str>, "payload": {...}}
49
+ ```
50
+
51
+ `lab_sync_driver_protocol.wire` holds the framing (`encode`, `decode`, `read_frame`,
52
+ `write_frame`), the protocol version (`PROTOCOL_VERSION`, currently `v0`) and
53
+ `STREAM_LIMIT`, the line length a reader should accept.
54
+
55
+ ## Methods
56
+
57
+ The request vocabulary is `handshake`, `describe`, `connect`,
58
+ `set_simulation`, `ping`, `execute` (streaming progress/result/error frames),
59
+ `abort`, `describe_options`, `estimate_time` and `set_telemetry_demand`.
60
+
61
+ `describe_options` is optional and additive (the protocol version stays `v0`):
62
+ request `{"command": name, "parameters": {…}}`, reply
63
+ `{"options": {param_name: [choice, …]}}` — the runtime choices for the
64
+ command's `dynamic_options` parameters, best-effort per parameter. A driver
65
+ predating it answers with the standard `unknown_method` error frame, which a
66
+ supervisor reads as "no dynamic options" rather than a fault.
67
+
68
+ Telemetry is demand-gated: a driver samples nothing until the supervisor sends
69
+ `set_telemetry_demand` with the complete set of channels that should be
70
+ running. See the `lab_sync_driver_protocol.wire` module docstring.
71
+
72
+ ## Environment
73
+
74
+ The supervisor passes per-instance values in the driver process's environment,
75
+ since the launch command is shared by every instance of a driver type:
76
+
77
+ - `STUDIO_DEVICE_CONFIGURATION` (`CONFIGURATION_ENV`): the hardware variant
78
+ the device was added as, one of the `configurations` the driver describes.
79
+ - `STUDIO_DEVICE_DATA_DIR` (`DATA_DIR_ENV`): the instance's own writable
80
+ directory, which is also the process's working directory.
81
+
82
+ ## License
83
+
84
+ MIT
@@ -0,0 +1,9 @@
1
+ lab_sync_driver_protocol/__init__.py,sha256=d9uHwgBQP1w0qVjcT2OJakxBr26-nZ0SQThqsPg7xbU,951
2
+ lab_sync_driver_protocol/environment.py,sha256=VfDHBV7lI-1cS4nKf8oSdaRDLTairYxqqMfopMuXsAI,1068
3
+ lab_sync_driver_protocol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ lab_sync_driver_protocol/wire.py,sha256=ovU-pRCx3JWwwYLf11VN8nrF7ajAvsTa2zSKtMNBISw,2692
5
+ lab_sync_driver_protocol-0.3.0.dist-info/licenses/LICENSE,sha256=jm-ZYaMIQMfZVTbmqsfK44orj0ZnKzzsKwwJwq21xOI,1065
6
+ lab_sync_driver_protocol-0.3.0.dist-info/METADATA,sha256=9rgBg9I57nkvtDKa-6vT9TuUYh-D5JPGZ4vI127EqX8,3675
7
+ lab_sync_driver_protocol-0.3.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
8
+ lab_sync_driver_protocol-0.3.0.dist-info/top_level.txt,sha256=8T2VVh7A2hbLgohJ50HpgInb31l3ZHK6fOL0_UzHook,25
9
+ lab_sync_driver_protocol-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lab Sync
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ lab_sync_driver_protocol