Skip to content

feat(cli): add --segments to the commands that accept it - #17

Merged
spencer-zqian merged 2 commits into
mainfrom
feat/cli-segments
Jul 24, 2026
Merged

feat(cli): add --segments to the commands that accept it#17
spencer-zqian merged 2 commits into
mainfrom
feat/cli-segments

Conversation

@spencer-zqian

Copy link
Copy Markdown
Contributor

segments is the only structured parameter in the API — every other CLI flag is a scalar — so it was skipped when the CLI was first written. Both SDKs have accepted it all along; neither CLI exposed it. This adds it.

Surface

--segments '[{"start":0,"label":"intro","prompt":"airy pads"}]'   # inline JSON
--segments @segments.json                                          # read from a file
--segments @-                                                      # read from stdin

A value starting with @ is a source to read from; @- is stdin. Anything else is parsed as JSON directly. This follows the curl / gh / aws convention for structured flags.

Commands that gain the flag, matching what each endpoint actually accepts:

  • music segments {start, prompt, label?}text-to-music, video-to-music
  • SFX segments {start, end, prompt}video-to-sfx, video-to-sound, video-to-video-sound

video-to-video-music and text-to-sfx do not take segments. video-to-video-sfx does in the SDK, but has never been wired up as a CLI command — adding a command is a different change, so it is out of scope here.

Validation is shape-only, on purpose

The CLI checks that the value parses, is a non-empty array, and that each element has the required fields with the right types. It does not replicate the server's semantic rules — first segment at start: 0, minimum spacing, the label enum, distance from the end, item caps. Those live server-side, and a CLI copy would drift the moment the backend changes. The API's 422 stays the authority.

Unknown keys pass through untouched so a future API field needs no CLI release. The one exception is a key belonging to the other segment shape (end on music, label on SFX): that is the predictable mistake this design invites — pointing SFX-shaped segments at a music command or the reverse — so it is rejected with a message naming the expected shape:

sonilo: video-to-music segments take {start, prompt, label?} — got an object with keys start, end, prompt

Cross-CLI parity

The Node and Python CLIs were implemented against one shared contract and their user-visible behaviour was compared by running both. The shape-mismatch, wrong-type, and stdin-path errors are byte-identical, indices are 0-based in both. The only difference left is the wrapper wording on a JSON parse failure, where the tail is the runtime's own parser message (V8 vs CPython) and cannot be made identical anyway.

Tests

64 tests pass in sonilo-cli — covering inline JSON, @file, @-, malformed JSON, non-lists, empty lists, both cross-shape directions, unknown-key passthrough, and that omitting the flag sends no segments field at all. The index-naming tests put the fault at index 2 and index 1 of a multi-element array, so an implementation that hardcoded 0 would fail them. The core SDK suite was re-run as a regression check: 192 pass, untouched.

Release

sonilo-cli goes 0.3.0 → 0.4.0 in both pyproject.toml and __init__.py — the latter feeds the x-sonilo-client-version header, so the two have to move together. The core sonilo package is not bumped.

segments is the only structured parameter the API takes, so it was skipped
when the CLI was written. Both SDKs accept it; neither CLI exposed it.

The flag takes inline JSON, @file, or @- for stdin — the curl / gh / aws
convention. Validation is shape-only: parseable JSON, a non-empty array of
objects, required fields present with the right types. The server's semantic
rules (first segment at 0, minimum spacing, the label enum, item caps) are
left to the API's 422, since a copy of them here would drift.

Music commands (text-to-music, video-to-music) take {start, prompt, label?};
SFX commands (video-to-sfx, video-to-sound, video-to-video-sound) take
{start, end, prompt}. Passing one shape to a command that takes the other is
the predictable mistake, so errors name the expected shape and the keys that
were actually given. Keys belonging to neither shape are forwarded untouched,
so a field added to the API needs no CLI release.

video-to-video-sfx is the sixth segment-capable endpoint in the SDK but has
no CLI command yet, so only five commands gained the flag here.
Aligns the Python messages with the Node CLI, which already pointed at the
element that failed. A segments array is typically three or four items, so
naming the index saves the reader counting:

  video-to-sfx segments take {start, end, prompt} — element 2 is not an object
  video-to-sfx segments take {start, end, prompt} — "prompt" must be a string (element 1)

Indices are 0-based, matching Node. The shape mismatch message keeps its
no-index form ("got an object with keys ...") in both CLIs, since that is a
whole-payload mistake rather than one bad element.

Also renames the stdin source in parse errors from "standard input" to
"stdin", again matching Node.

The non-empty-array error keeps naming the expected shape, which Node's does
not — a deliberate difference, not an oversight.
@sapient-app

sapient-app Bot commented Jul 24, 2026

Copy link
Copy Markdown

Sapient docs evals

Waiting for the staging docs URL before running evals.

Sapient will start the selected PR evals automatically when GitHub reports a successful docs deployment for this PR. This usually happens within 15 minutes.

Commit: fb97bd1
Status: waiting for staging docs URL

@spencer-zqian
spencer-zqian merged commit cd85d96 into main Jul 24, 2026
2 checks passed
@spencer-zqian
spencer-zqian deleted the feat/cli-segments branch July 24, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant