pull.ymlΒΆ

---
- name: ⬇️ Pre-pull LM Studio models
  ansible.builtin.shell: > # nocheck: container-exec-resolver  runtime-resolved CID, delegated to the hosting node
    container exec -i {{ LMSTUDIO_LOCAL_CID | quote }} lms get --yes --gguf {{ (model.source ~ '@' ~ model.quant) | quote }}
  environment:
    CONTAINER_EXEC_TIMEOUT: >-
      {{ [ LMSTUDIO_PRELOAD_FLOOR_SECONDS | int,
           ((model.bytes | int) / (LMSTUDIO_PRELOAD_MIN_BYTES_PER_SECOND | int))
           | round(0, 'ceil') | int ] | max }}
  register: lmstudio_pull_result
  loop: "{{ LMSTUDIO_PRELOAD_MODELS }}"
  loop_control:
    loop_var: model
    label: "{{ model.alias }}"
  # rationale: async; the reap below reads every job back before the play continues, so no model is still downloading when playwright starts
  async: "{{ LMSTUDIO_PRELOAD_ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
  poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
  retries: "{{ 0 if ASYNC_ENABLED | bool else 1 }}"
  delay: 15
  until: >
    (lmstudio_pull_result.rc | default(0)) == 0 or
    ('You already have this model' in (lmstudio_pull_result.stderr | default('')))
  changed_when: false
  failed_when: >-
    (lmstudio_pull_result.failed | default(false))
    and 'You already have this model' not in ((lmstudio_pull_result.stdout | default('')) ~ (lmstudio_pull_result.stderr | default('')))
  delegate_to: "{{ LMSTUDIO_HOST_NODE }}"

- name: "⏳ Reap the LM Studio model pre-pulls"
  ansible.builtin.include_tasks:
    file: utils/async/reap.yml
    apply:
      delegate_to: "{{ LMSTUDIO_HOST_NODE }}"
  vars:
    async_reap_jobs: "{{ lmstudio_pull_result.results | default([]) }}"
    async_reap_label: "LM Studio model pre-pull"
    async_reap_tolerate:
      - "You already have this model"
    ASYNC_TIME: "{{ LMSTUDIO_PRELOAD_ASYNC_TIME }}"