SYN-1365: SDK: Add LeverHandle + SensorFrame client APIs#191
SYN-1365: SDK: Add LeverHandle + SensorFrame client APIs#191JoshuaPurtell wants to merge 1 commit intomainfrom
Conversation
Greptile SummaryThis PR adds client SDK support for lever and sensor frame management in the optimization system. The implementation adds four new methods to
Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Client as LearningClient
participant API as Backend API
participant Storage as Data Store
Note over Client,Storage: Lever Operations
Client->>API: POST /api/v1/optimizers/{id}/levers
API->>Storage: Create/Update Lever
Storage-->>API: LeverHandle
API-->>Client: LeverHandle (with version)
Client->>API: GET /api/v1/optimizers/{id}/levers?lever_id&scope
API->>Storage: Resolve Lever Snapshot
Storage-->>API: LeverHandle
API-->>Client: LeverHandle | None
Note over Client,Storage: Sensor Operations
Client->>API: POST /api/v1/optimizers/{id}/sensors
API->>Storage: Store SensorFrame
Storage-->>API: SensorFrame (with frame_id)
API-->>Client: SensorFrame
Client->>API: GET /api/v1/optimizers/{id}/sensors?scope&limit
API->>Storage: Query SensorFrames
Storage-->>API: List[SensorFrame]
API-->>Client: List[SensorFrame]
Last reviewed commit: f0840a3 |
| kind = LeverKind(str(kind_raw)) if kind_raw is not None else LeverKind.CUSTOM | ||
| except ValueError: | ||
| kind = LeverKind.CUSTOM | ||
| version_raw = raw.get("version") or raw.get("lever_version") |
There was a problem hiding this comment.
🔴 or operator treats version 0 as falsy, skipping valid version values
In LeverHandle.from_dict, the expression raw.get("version") or raw.get("lever_version") uses Python's or operator to fall back between two keys. However, or treats 0 as falsy, so a legitimate version value of 0 from the "version" key would be skipped in favor of "lever_version".
Root Cause and Impact
When the API returns {"version": 0, "lever_version": 3}, the expression evaluates as:
raw.get("version")→0(falsy)0 or raw.get("lever_version")→3version_raw=3(wrong — should be0)
The correct approach is to use explicit is not None checks:
version_raw = raw.get("version")
if version_raw is None:
version_raw = raw.get("lever_version")Impact: Any lever with version 0 will have its version incorrectly read from the lever_version field (if present), or silently default to 0 only by coincidence if lever_version is also absent.
| version_raw = raw.get("version") or raw.get("lever_version") | |
| version_raw = raw.get("version") | |
| if version_raw is None: | |
| version_raw = raw.get("lever_version") |
Was this helpful? React with 👍 or 👎 to provide feedback.
| candidates = ( | ||
| js.get("items") | ||
| or js.get("sensor_frames") | ||
| or js.get("frames") | ||
| or js.get("data") | ||
| or [] | ||
| ) |
There was a problem hiding this comment.
🔴 or-chain in list_sensor_frames treats empty list [] as falsy, falling through to wrong response key
In list_sensor_frames, the or-chain js.get("items") or js.get("sensor_frames") or js.get("frames") or js.get("data") or [] uses Python's or operator to select the first truthy value. An empty list [] is falsy in Python, so if the correct response key (e.g. "items") contains an empty list, it will be skipped in favor of a later key.
Root Cause and Impact
Consider an API response like {"items": [], "data": [{...}]}. The intended key is "items" (empty — no frames), but:
js.get("items")→[](falsy)[] or js.get("sensor_frames")→None(falsy)None or js.get("frames")→None(falsy)None or js.get("data")→[{...}](truthy)candidates=[{...}](wrong — should be[])
This means the method would return phantom sensor frames from an unrelated key when the correct key has an empty list.
Impact: Callers could receive incorrect/unexpected sensor frames when the actual result set is empty but the response dict contains other list-valued keys.
| candidates = ( | |
| js.get("items") | |
| or js.get("sensor_frames") | |
| or js.get("frames") | |
| or js.get("data") | |
| or [] | |
| ) | |
| candidates = None | |
| for _key in ("items", "sensor_frames", "frames", "data"): | |
| _val = js.get(_key) | |
| if _val is not None: | |
| candidates = _val | |
| break | |
| if candidates is None: | |
| candidates = [] |
Was this helpful? React with 👍 or 👎 to provide feedback.
Resolves https://linear.app/synth-ai/issue/SYN-1365/sdk-add-leverhandle-sensorframe-client-apis