Add the remote-command translation backend #193

Closed
opened 2026-08-24 21:28:32 +01:00 by naps62-yolo · 1 comment
Owner

Implement the remote-command translation backend behind its own cargo feature,
against the trait from #190.

The backend runs a configured command template, writes the prompt and the cue
batch to its stdin, and reads the translated batch from its stdout. It makes no
assumption about ssh: ssh box claude -p is one instance of the template, a
local CLI or a container exec is another. That is what makes it useful — it
reaches an agent CLI on another host with no API key in arr's config at all.

  • The command template and a per-call timeout are settings (#198).
  • A non-zero exit, a timeout, or output that fails #190's cue validation is an
    error; a partially translated file is never written.
  • The command is spawned per batch and dies with it, so nothing is resident.

Tests drive a stub script rather than a real remote host.

Depends on: #190

Implement the remote-command translation backend behind its own cargo feature, against the trait from #190. The backend runs a configured command template, writes the prompt and the cue batch to its stdin, and reads the translated batch from its stdout. It makes no assumption about ssh: `ssh box claude -p` is one instance of the template, a local CLI or a container exec is another. That is what makes it useful — it reaches an agent CLI on another host with no API key in arr's config at all. - The command template and a per-call timeout are settings (#198). - A non-zero exit, a timeout, or output that fails #190's cue validation is an error; a partially translated file is never written. - The command is spawned per batch and dies with it, so nothing is resident. Tests drive a stub script rather than a real remote host. Depends on: #190
naps62-yolo added this to the Subtitles milestone 2026-08-24 21:28:32 +01:00
naps62-yolo added the area/subsdifficulty/moderatephase/9-subtitlestype/feature labels 2026-08-24 21:28:32 +01:00
naps62-yolo added a new dependency 2026-08-24 21:29:27 +01:00
Author
Owner

Implemented the remote-command translation backend behind translate-command (DESIGN.md §15). arr-subs::command runs a configured template per batch (ssh box claude -p is one instance; {source}/{target} placeholders get short tags like pt-PT), writes the shared system prompt plus the JSON cue batch to its stdin, and parses the same {number, text} reply shape the OpenAI backend speaks. Non-zero exit (with stderr detail), per-call timeout (child killed via kill_on_drop, nothing resident), non-UTF-8 and unparseable output all fail loudly; cue-count/numbering validation stays in translate, so a partial file can never be written. The LLM prompt helpers moved from openai.rs into translate.rs so both backends share them without a feature dependency. Tests drive stub shell scripts in a tempdir — round-trip, placeholder substitution, fenced replies, exit codes, garbage output, short batches, timeout kill.

Branch: subtitles/193-remote-command

just ci: passed (full gate, 646 tests green).

Findings

  • Tokio's ChildStdin::shutdown() does not reliably deliver EOF to the child; stdin must be explicitly dropped for cat-style consumers to finish. Worth knowing if another backend or the sync wrapper pipes to a child.
  • Spawning a freshly written executable intermittently fails with ETXTBSY on this machine (overlayfs); run retries briefly on that error kind. If other code spawns generated binaries it will need the same.
Implemented the remote-command translation backend behind `translate-command` (DESIGN.md §15). `arr-subs::command` runs a configured template per batch (`ssh box claude -p` is one instance; `{source}`/`{target}` placeholders get short tags like `pt-PT`), writes the shared system prompt plus the JSON cue batch to its stdin, and parses the same `{number, text}` reply shape the OpenAI backend speaks. Non-zero exit (with stderr detail), per-call timeout (child killed via `kill_on_drop`, nothing resident), non-UTF-8 and unparseable output all fail loudly; cue-count/numbering validation stays in `translate`, so a partial file can never be written. The LLM prompt helpers moved from `openai.rs` into `translate.rs` so both backends share them without a feature dependency. Tests drive stub shell scripts in a tempdir — round-trip, placeholder substitution, fenced replies, exit codes, garbage output, short batches, timeout kill. Branch: `subtitles/193-remote-command` `just ci`: passed (full gate, 646 tests green). ## Findings - Tokio's `ChildStdin::shutdown()` does not reliably deliver EOF to the child; stdin must be explicitly dropped for `cat`-style consumers to finish. Worth knowing if another backend or the sync wrapper pipes to a child. - Spawning a freshly written executable intermittently fails with `ETXTBSY` on this machine (overlayfs); `run` retries briefly on that error kind. If other code spawns generated binaries it will need the same.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yolo/arr#193