Files
computer-playbook/roles/svc-ai-lmstudio/meta/services.yml
kevinveenbirkenbachandClaude Opus 5 eef3c3be71 fix(lmstudio): bound the model fetch by size, disk and a fallback source
A wedged LM Studio daemon cost CI job 105558224377 three hours: three kills at
the global 60-minute `CONTAINER_EXEC_TIMEOUT`, for a 531 MB model that a healthy
node pulls in 27 seconds. Nothing in the path was sized against the thing being
fetched, and nothing checked whether it could land at all.

Size. Each model now declares `bytes`, taken from the value the deploy already
printed as "OK (531068224 bytes)", and the pre-pull derives its own timeout per
model: max(preload_floor_seconds, bytes / preload_min_bytes_per_second). The
floor of 600s protects a small model from a brief stall; the rate of 1 MB/s sits
twenty times below the 21.98 MB/s a healthy pull reached and seven thousand
times above the 142 B/s the wedged one managed, so it separates the two without
guessing. Evaluated against ansible-core's filters: 531 MB and 10 MB both land
on the floor, 4 GB gives 4000s.

Disk. The seed measures free space with `df -P -B1` before fetching and refuses
with a message naming both figures in GiB, the factor and the file to edit. The
factor is three because the node cache, the staging copy inside the container
and the models volume all exist at once during the import; that is measured, not
padding.

Fallback. A model may list `mirrors`, extra bases tried after the source. The
mirror task is a no-op once the source delivered, because get_url skips a dest
whose checksum already matches: measured at 145.24s for the first pass against
0.64s for the second. The list ships empty, because the obvious candidate is
not one: hf-mirror.com answers 206 but resolves to us.aws.cdn.hf.co, so it is a
front end for HuggingFace and adds no redundancy against a HuggingFace outage.
The mechanism is here; an independent copy is the operator's to name.

The external check grew with the declaration: `bytes` is compared against the
size the API publishes, and every declared mirror must still serve the pinned
file, or it is redundancy on paper only.

01_manager_ops.yml's rescue rationale claimed that restarting the container
clears a wedged transfer. Job 105558224377 shows the restart returning rc 0
without clearing it, so the claim now says what was measured instead.

Residual: the mirror path has never run in a deploy, since no mirror is
declared. Its loop expression is verified against ansible-core's subelements
over four shapes, including the zero-iteration case when nothing is mirrored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 06:31:48 +02:00

37 lines
850 B
YAML

---
lmstudio:
modes:
compose:
enabled: true
swarm:
enabled: true
bond: 1
enabled: true
shared: true
backup:
no_stop_required: true
image: lmstudio/llmster-preview
# nocheck: docker-version -- the repository publishes 2 tags and neither is a version; 'cpu' selects the backend, not a release
version: cpu
name: lmstudio
platform_arch: x86_64
min_storage: 588MB
cpus: "4.0"
mem_reservation: 6g
mem_limit: 8g
pids_limit: 2048
ports:
internal:
http: 1234
preload_models: "{{ AI_LOCAL_MODELS }}"
preload_async_time: 3600
preload_floor_seconds: 600
preload_min_bytes_per_second: 1000000
lifecycle: beta
# nocheck: playwright-service-flag, dynamic-flag
container_backup: # nocheck: bond-baseline
bond: 1
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
shared: true