Repository navigation
serve: the image encoder can run on a second PC over ssh (#1404) - #1942
Closed
TraceRecursion wants to merge 1 commit into
Closed
TraceRecursion wants to merge 1 commit into
TraceRecursion wants to merge 1 commit into
Conversation
Contributor
|
oh i need to study this. |
TraceRecursion
force-pushed
the
remote-vision
branch
from
October 13, 2026 13:09
5f34bfd to
c983b53
Compare
The image encoder is already a separate process behind a line protocol, and it is not tied to the
server's machine, so it can run on a second PC over ssh. That takes its VRAM off the card running
the model: 1383 MiB measured, and the expert cache goes from 3112 to 3641 experts (+1.00 GiB),
because the engine sizes it from the VRAM that is free at startup.
Measured on the reference machine (Ryzen 7 9700X, RTX 5080; vision host i7-11800H + RTX 3060
Laptop, 10 GbE; IQ3_S, 524288 context, --kv int8, engine 0.1.40.2, 2026-10-07/08). The three
configs differ ONLY in the encoder, and every run used a distinct picture, because embeddings are
cached by the image's hash:
one 1024x1024 picture, as the client sees it (1088 prompt tokens), median of 5
this GPU 1.63 s 1383 MiB VRAM 3112 experts, 5.96 GiB
another PC 2.83 s 0 3641 experts, 6.96 GiB
this CPU 15.27 s 0 3641 experts, 6.96 GiB (8 threads)
5.4x faster than the CPU encoder; 1.2 s a picture slower than a local GPU one, which is the price
of not spending the VRAM. It does NOT make text faster: the cache does grow, but long-context text
is the same in all three modes (3.27/3.30/3.31 K prompt tok/s at 257 K tokens, 3.03/3.05/3.04 K at
463 K) - ~1 GiB against a ~47 GiB expert set is ~2% coverage. A picture-latency and VRAM trade,
not a text-speed optimisation.
Two server-side changes, both narrow, both under "remote": true:
- Service.ensure_vision: the server starts and serves text when the encoder cannot, and the first
image asks for it again. A vision host can be off at boot, and failing the whole server for
that is the wrong trade. Its failure is a ValueError, which the request path turns into a 400
carrying the reason; anything else escapes prepare() to socketserver, which logs a traceback and
drops the connection, so the client is told nothing at all (measured as "status 0", then fixed).
Gated on the same key as the rest: a LOCAL encoder that fails to start stays fatal, with the
cause named (Niko1221#1445) - that is a broken install, not an absent host.
- "remote": true makes the encode skip the request FIFO. A local encoder shares the GPU with the
engine and must be serialised with the requests; a remote one shares nothing, and holding the
FIFO across the upload to a starved host froze the whole API, text requests included.
tools/remote-vision/ holds the wrapper (a man in the middle for a protocol that passes paths, not
data, so the picture and the embeddings cross over one ssh connection), the vocabulary-stub builder
that spares the vision host the 51 GB text model (strata-vision opens it with vocab_only = true),
a remote build script, a watchdog that reaps the encoder when the server's port goes away, and a
test. The server starts the wrapper through a .cmd shim, so nothing has to be packed into an exe.
The docs are the page this issue asked for (docs/REMOTE_VISION.md), the one-line pointer in the
README, and the "remote" paragraph in DETAILS.md's list of vision settings.
Opt-in and off by default: no behaviour changes without "remote": true.
694 tests pass in serve/ (11 skipped, 4 new), and the wrapper's own check passes against the
vision host: READY 2560, a 640x480 picture OK 300 in 0.77 s.
TraceRecursion
force-pushed
the
remote-vision
branch
from
October 17, 2026 16:28
c983b53 to
0b54d82
Compare
Owner
|
Thanks for the pull request. We're resetting the tracker for 0.2.0-beta. There are more than 1,300 open issues and pull requests, too many for us to review properly, so we're closing everything that isn't being worked on right now. Closed here does not mean rejected. To reopen it takes about two minutes:
The rules are in CONTRIBUTING.md. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue: Resolves #1404
Summary
The image encoder is already a separate process behind a line protocol (
ENC <image> <out>), and it is not tiedto the server's machine, so it can run on a second PC over ssh. That takes its VRAM off the card running the
model: 1383 MiB measured, and the engine's own startup line goes 3112 -> 3641 experts (+1.00 GiB of expert
cache). 2.8 s per 1024-token picture against 15.27 s on this CPU (5.4x), and 1.2 s a picture slower than a
local GPU encoder. It does not make text faster: long-context text is the same in all three modes, because
+1 GiB against a ~47 GiB expert set is ~2% coverage. A picture-latency and VRAM trade.
What changed
docs/REMOTE_VISION.md(new): the page asked for in [Feature]: run the image encoder on a second PC #1404 - the exact command, the config line, what happenswhen the second PC is down, and the measurements with what they were measured on.
README.md: one line under "Pictures" pointing at it.docs/DETAILS.md: the"remote": trueparagraph in "Images (vision)", next to the other vision settings.tools/remote-vision/(new):strata_vision_remote.py(the wrapper - the protocol passes paths, not data, sothe picture and the embeddings cross one ssh connection),
strata-vision-remote.cmd(thevision.exeshim;Popencannot start a.pydirectly),make_vocab_stub.py(an 11 MB vocabulary-only stub -strata-visionopens the text model with
vocab_only = true, so the vision host does not need the 51 GB shard),remote-build.sh(builds the encoder on a Linux host),remote-vision-watchdog.sh(reaps the encoder when theserver's port stops answering), and
test-wrapper.ps1(the check in the notes below).serve/server.py: the two changes I asked about in [Feature]: run the image encoder on a second PC #1404."remote": truekeeps the encode out of the requestFIFO: a local encoder shares this machine's GPU with the engine and must stay serialised, a remote one shares
nothing, and holding the FIFO across the upload to a starved host freezes the API, text requests included - a
text request sent during one encode waited 13.30 s with the turn held against 0.56 s or less with
"remote": true(measured below).Service.ensure_visionlets the server start and serve text when theencoder cannot, answering
400 the image encoder is not available: ...on the first image and asking again onthe next one. Without it, a boot-order race dropped the connection and told the client nothing at all
(measured:
status 0).serve/test_server.py: 4 tests (RemoteVision). The FIFO one counts the engine's turn, so it fails if theremote path ever takes it (0 entries, against 1 for a local encoder), and it asserts that the client's cancel
event reaches the encoder (serve: a client that leaves during an image encode cannot be cancelled — prepare() runs before the cancel event, and the encode holds the single request turn #1999).
.gitignore:tools/remote-vision/*.gguf, so the generated stub cannot be committed by accident.Extra Notes
Rebased on the current
main(477ac13c). The conflicts were with #1999 (the engine's turn is a cancellableService._turn(cancel)now), with the"elastic"encoder'sService._vision_up/last_image_atin thepicture path, and with the
"elastic"paragraph that now sits where the vision settings are documented. Theresolution keeps all of it and only skips the turn for a remote encoder:
encode_all(images, cancel)for a localone, the same call without the turn for a remote one, and the paragraph follows the
"elastic"one.It composes with the neighbouring vision options.
"elastic": true(#2189) starts the GPU encoder on thefirst picture and gives its VRAM back when it sits idle;
"remote": truemoves the encoder off this cardentirely, so there is no local VRAM to make room for and the encode skips the turn. Upstream now points
pictures at the CPU encoder on NVIDIA cards under 24 GB (#2406, measured there at 15.27 s for a 1024-token
picture against 2.7 s here), so the two answer different points of the same trade: that one keeps the card's
VRAM and pays in seconds per picture, this one keeps the seconds and pays a second PC over ssh.
Default path.
"remote": trueis the opt-in and nothing without it behaves differently - including thestartup tolerance, which is gated on the same key: a LOCAL encoder that fails to start is still fatal, and still
names the cause (#1445), because that is a broken install and not an absent host. Only a remote one starts
text-only and retries.
What I checked.
python -m unittest discover -s serve -t . -p "test_*.py": 852 tests, OK (15 skipped) onthis branch, rebased on
477ac13c. Real-machine checks on engine 0.1.45 (server from this branch; an RTX 5080plus an RTX 3060 over ssh, the encoder rebuilt from this branch's
tools/vision):[vision-remote] encoder up: READY 2560; a text request answered in 0.87 s; onefresh 1600x1600 picture (1024 image tokens, none of it cached) answered in 5.23 s with the right answer, and
the wrapper logged
...img: OK 1024 in 2.71s (10.00 MB back);STRATA_VISION_SSHat a host that does not resolve): the server stillstarted and served text (0.70 s), and the first image answered
400withthe image encoder is not available: the vision encoder did not start: ERR the remote vision encoder could not be started- a readable errorinstead of a dropped connection - and so did the next one (7.97 s and 7.95 s, the ssh attempt's cost);
the window is unambiguous) - with the turn held, a text request sent while the encode ran waited 13.30 s (the
image request itself 20.9 s); with
"remote": true, nine text requests spread over the same encode all cameback in 0.17-0.56 s against a 0.17-0.19 s baseline (the image request 19.3 s).
Only Python and
tools/are touched, no C++/CUDA/HIP/SYCL file, so no backend rebuild is involved.What I did not test. The server side was only exercised on Windows; the wrapper's Linux side
(
remote-build.sh, the watchdog) was built and smoke-tested on the vision host but not against a Linux Strataserver.
STRATA_VISION_MUXis off by default because ssh multiplexing fails on Windows (getsockname failed: Not a socket). The translated READMEs are untouched. The FIFO A/B above uses a local CPU encoder to make theencode long enough to measure; the turn it takes is the same object a remote encoder would take. The encode
above was the first picture after a fresh encoder start (2.71 s); a warm encoder measured 1.71 s for the same
1024 tokens.
The table is from engine 0.1.40.2 (the release current on 2026-10-07/08, when it was taken): the three modes
were measured the same day on one build, which is what the deltas need. The encoder binary and its mtmd path are
unchanged since, and the config gives
--vram-reserve-mib 700explicitly, so 0.1.42's Windows auto-reserve floor(#1376) does not apply to these numbers either. The branch itself is rebased on 0.1.45, which is what the checks
above ran on.
Left out on purpose: my own deploy helper (a PowerShell script that pushes the sources to my vision host and
rebuilds there - it hardcodes my two machines and my model paths). Happy to add a generalized version if you
want one.
Happy to split the
serve/server.pybehaviour and its tests into their own PR if that is easier to review;the page and
tools/remote-vision/need each other to be usable.