---
- name: "π Create the shared model store before the daemons start"
ansible.builtin.file:
path: "{{ LMSTUDIO_SHARED_MODELS_PATH }}/{{ model.repo }}"
state: directory
mode: "0755"
loop: "{{ LMSTUDIO_PRELOAD_MODELS }}"
loop_control:
loop_var: model
label: "{{ model.file }}"
- name: "π Create the staging directory beside the model store"
ansible.builtin.file:
path: "{{ LMSTUDIO_SHARED_STAGING_PATH }}"
state: directory
mode: "0755"
- name: "π Read what the shared model store already holds"
ansible.builtin.stat:
path: "{{ LMSTUDIO_SHARED_MODELS_PATH }}/{{ model.repo }}/{{ model.file }}"
checksum_algorithm: sha256
register: lmstudio_present
changed_when: false
loop: "{{ LMSTUDIO_PRELOAD_MODELS }}"
loop_control:
loop_var: model
label: "{{ model.file }}"
- name: "ποΈ Measure the free space the shared model store offers"
ansible.builtin.command: "df -P -B1 {{ LMSTUDIO_SHARED_MODELS_PATH | quote }}"
register: lmstudio_export_space
changed_when: false
- name: "π Refuse to fetch models the shared store cannot hold"
vars:
_free: "{{ lmstudio_export_space.stdout_lines[1].split()[3] | int }}"
_absent: >-
{{ lmstudio_present.results
| rejectattr('stat.exists')
| map(attribute='model.bytes') | map('int') | sum }}
_required: "{{ (_absent | int) + (LMSTUDIO_PRELOAD_LARGEST_BYTES | int) }}"
ansible.builtin.fail:
msg: >-
The shared model store on {{ LMSTUDIO_NFS_SERVER }} has
{{ (_free | int / 1024 / 1024 / 1024) | round(1) }} GiB free but needs
{{ (_required | int / 1024 / 1024 / 1024) | round(1) }} GiB: it is missing
{{ (_absent | int / 1024 / 1024 / 1024) | round(1) }} GiB of declared models,
plus {{ (LMSTUDIO_PRELOAD_LARGEST_BYTES | int / 1024 / 1024 / 1024) | round(1) }}
GiB for the largest one while it stages. Shrink AI_LOCAL_MODELS in
group_vars/all/16_ai.yml or grow the export.
when: _free | int < _required | int
- name: "β¬οΈ Fetch the models into the shared store"
get_url_retry:
url: "{{ model.url }}"
dest: "{{ LMSTUDIO_SHARED_MODELS_PATH }}/{{ model.repo }}/{{ model.file }}"
tmp_dest: "{{ LMSTUDIO_SHARED_STAGING_PATH }}"
checksum: "sha256:{{ model.sha256 }}"
mode: "0644"
loop: "{{ LMSTUDIO_PRELOAD_MODELS }}"
loop_control:
loop_var: model
label: "{{ model.file }}"
register: lmstudio_fetch
changed_when: false
failed_when: false
- name: "πͺ Fetch from a mirror when the source did not deliver"
get_url_retry:
url: "{{ item.1 }}"
dest: "{{ LMSTUDIO_SHARED_MODELS_PATH }}/{{ item.0.repo }}/{{ item.0.file }}"
tmp_dest: "{{ LMSTUDIO_SHARED_STAGING_PATH }}"
checksum: "sha256:{{ item.0.sha256 }}"
mode: "0644"
loop: "{{ LMSTUDIO_PRELOAD_MODELS | subelements('mirrors', skip_missing=True) }}"
loop_control:
label: "{{ item.0.file }} from {{ item.1 }}"
register: lmstudio_fetch_mirror
changed_when: false
failed_when: false
- name: "π Read back what the shared model store now holds"
ansible.builtin.stat:
path: "{{ LMSTUDIO_SHARED_MODELS_PATH }}/{{ model.repo }}/{{ model.file }}"
checksum_algorithm: sha256
register: lmstudio_seeded
changed_when: false
loop: "{{ LMSTUDIO_PRELOAD_MODELS }}"
loop_control:
loop_var: model
label: "{{ model.file }}"
- name: "π Fail when no source delivered a declared model"
vars:
_missing: >-
{{ lmstudio_seeded.results
| rejectattr('stat.exists')
| map(attribute='model.file') | list }}
_said: >-
{{ (lmstudio_fetch.results | map(attribute='msg') | select | list)
+ (lmstudio_fetch_mirror.results | default([]) | map(attribute='msg') | select | list) }}
ansible.builtin.fail:
msg: >-
The shared model store on {{ LMSTUDIO_NFS_SERVER }} is missing
{{ _missing | join(', ') }}. What the sources answered:
{{ _said | join(' | ') }}
when: _missing | length > 0
- name: "π Fail when a seeded model does not match its checksum"
ansible.builtin.fail:
msg: >-
{{ item.model.file }} in the shared model store on {{ LMSTUDIO_NFS_SERVER }}
hashes to {{ item.stat.checksum }}, but AI_LOCAL_MODELS declares
{{ item.model.sha256 }}.
loop: "{{ lmstudio_seeded.results | selectattr('stat.exists') | list }}"
loop_control:
label: "{{ item.model.file }}"
when: item.stat.checksum != item.model.sha256