| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- ---
- # playbooks/04_models.yml
- # Pull and register Ollama models based on benchmark selection
- - name: "Models | Pull and register Ollama models"
- hosts: ai_server
- become: true
- gather_facts: true
- tags:
- - models
- vars:
- model_selection_file: "{{ playbook_dir }}/../benchmarks/results/model_selection.json"
- modelfiles_dir: /mnt/ai_data/ollama_models/modelfiles
- slot4_model: "" # legacy override kept for backwards compatibility
- slot5_model: "" # overrides slot5_general_rotate
- slot6_model: "" # overrides slot6_coding_rotate
- ollama_api_key: "{{ lookup('community.hashi_vault.hashi_vault', vault_secret_prefix ~ '/ollama:api_key token=' ~ lookup('ansible.builtin.file', vault_token_file) ~ ' url=' ~ vault_url) }}"
- tasks:
- # ── Load benchmark results ───────────────────────────────────────
- - name: "Models | Read model_selection.json from control node"
- ansible.builtin.slurp:
- src: "{{ model_selection_file }}"
- delegate_to: localhost
- become: false
- register: model_selection_raw
- tags:
- - models-load
- - name: "Models | Parse model selection data"
- ansible.builtin.set_fact:
- model_selection: "{{ model_selection_raw.content | b64decode | from_json }}"
- tags:
- - models-load
- - name: "Models | Apply slot4 override if provided"
- ansible.builtin.set_fact:
- model_selection: "{{ model_selection | combine({'slot4_coding': slot4_model}) }}"
- when: slot4_model | length > 0
- tags:
- - models-load
- - name: "Models | Apply slot5 override if provided"
- ansible.builtin.set_fact:
- model_selection: "{{ model_selection | combine({'slot5_general_rotate': slot5_model}) }}"
- when: slot5_model | length > 0
- tags:
- - models-load
- - name: "Models | Apply slot6 override if provided"
- ansible.builtin.set_fact:
- model_selection: "{{ model_selection | combine({'slot6_coding_rotate': slot6_model}) }}"
- when: slot6_model | length > 0
- tags:
- - models-load
- - name: "Models | Display selected models"
- ansible.builtin.debug:
- msg:
- - "=== Node 1 — General (port 11434) ==="
- - "Slot 1 (locked): {{ model_selection.slot1_general }}"
- - "Slot 2 (locked): {{ model_selection.slot2_general }}"
- - "Slot 5 (rotate): {{ model_selection.slot5_general_rotate | default('none') }}"
- - "=== Node 0 — Coding (port 11435) ==="
- - "Slot 3 (locked): {{ model_selection.slot3_coding }}"
- - "Slot 4 (locked): {{ model_selection.slot4_coding }}"
- - "Slot 6 (rotate): {{ model_selection.slot6_coding_rotate | default('none') }}"
- tags:
- - models-load
- # ── Pull models ──────────────────────────────────────────────────
- - name: "Models | Get currently installed models"
- ansible.builtin.command: ollama list
- changed_when: false
- register: current_models
- tags:
- - models-pull
- - name: "Models | Set installed models list"
- ansible.builtin.set_fact:
- installed_model_names: >-
- {{ current_models.stdout_lines[1:] |
- default([]) |
- map('split') |
- map('first') |
- list }}
- tags:
- - models-pull
- - name: "Models | Pull slot models if not already present"
- ansible.builtin.command: "ollama pull {{ item }}"
- loop:
- - "{{ model_selection.slot1_general }}"
- - "{{ model_selection.slot2_general }}"
- - "{{ model_selection.slot5_general_rotate | default('none') }}"
- - "{{ model_selection.slot3_coding }}"
- - "{{ model_selection.slot4_coding }}"
- - "{{ model_selection.slot6_coding_rotate | default('none') }}"
- when:
- - item | length > 0
- - item != 'none'
- - item not in installed_model_names
- changed_when: true
- loop_control:
- label: "Pulling {{ item }}"
- tags:
- - models-pull
- - name: "Models | Pull baseline models if not already present"
- ansible.builtin.command: "ollama pull {{ item }}"
- loop: "{{ baseline_models }}"
- when: item not in installed_model_names
- changed_when: true
- loop_control:
- label: "Pulling {{ item }}"
- tags:
- - models-pull
- # ── Create Modelfiles ────────────────────────────────────────────
- - name: "Models | Create modelfiles directory"
- ansible.builtin.file:
- path: "{{ modelfiles_dir }}"
- state: directory
- mode: "0755"
- owner: root
- group: root
- tags:
- - models-modelfile
- - name: "Models | Template coder-128k Modelfile"
- ansible.builtin.copy:
- content: |
- FROM {{ model_selection.slot3_coding }}
- PARAMETER num_ctx 32768
- SYSTEM You are an expert coding assistant. You write clean, efficient, well-documented code. Always include type hints and follow best practices.
- dest: "{{ modelfiles_dir }}/Modelfile.coder-128k"
- mode: "0644"
- tags:
- - models-modelfile
- - name: "Models | Template coder-32k Modelfile"
- ansible.builtin.copy:
- content: |
- FROM {{ model_selection.slot4_coding }}
- PARAMETER num_ctx 32768
- SYSTEM You are an expert coding assistant. You write clean, efficient, well-documented code. Always include type hints and follow best practices.
- dest: "{{ modelfiles_dir }}/Modelfile.coder-32k"
- mode: "0644"
- when:
- - model_selection.slot4_coding | length > 0
- - model_selection.slot4_coding != 'none'
- tags:
- - models-modelfile
- - name: "Models | Template coder-rotate Modelfile"
- ansible.builtin.copy:
- content: |
- FROM {{ model_selection.slot6_coding_rotate }}
- PARAMETER num_ctx 32768
- SYSTEM You are an expert coding assistant. You write clean, efficient, well-documented code. Always include type hints and follow best practices.
- dest: "{{ modelfiles_dir }}/Modelfile.coder-rotate"
- mode: "0644"
- when:
- - model_selection.slot6_coding_rotate | default('') | length > 0
- - model_selection.slot6_coding_rotate | default('none') != 'none'
- tags:
- - models-modelfile
- - name: "Models | Template llama-family Modelfile"
- ansible.builtin.copy:
- content: |
- FROM llama3.2:3b
- PARAMETER num_ctx 8192
- SYSTEM You are a helpful, friendly family assistant. Provide safe, age-appropriate responses suitable for all family members.
- dest: "{{ modelfiles_dir }}/Modelfile.llama-family"
- mode: "0644"
- tags:
- - models-modelfile
- - name: "Models | Template gemma-family Modelfile"
- ansible.builtin.copy:
- content: |
- FROM llama3.1:8b
- PARAMETER num_ctx 8192
- SYSTEM You are a helpful, friendly family assistant. Provide safe, age-appropriate responses suitable for all family members.
- dest: "{{ modelfiles_dir }}/Modelfile.gemma-family"
- mode: "0644"
- tags:
- - models-modelfile
- # ── Register models ──────────────────────────────────────────────
- - name: "Models | Register custom models with Ollama"
- ansible.builtin.command: "ollama create {{ item.name }} -f {{ modelfiles_dir }}/{{ item.file }}"
- loop:
- - { name: "coder-128k", file: "Modelfile.coder-128k" }
- - { name: "coder-32k", file: "Modelfile.coder-32k", slot: "{{ model_selection.slot4_coding }}" }
- - { name: "coder-rotate", file: "Modelfile.coder-rotate", slot: "{{ model_selection.slot6_coding_rotate | default('none') }}" }
- - { name: "llama-family", file: "Modelfile.llama-family" }
- - { name: "gemma-family", file: "Modelfile.gemma-family" }
- when: item.slot is not defined or (item.slot | length > 0 and item.slot != 'none')
- changed_when: true
- loop_control:
- label: "Creating {{ item.name }}"
- tags:
- - models-register
- # ── Warmup service ───────────────────────────────────────────────
- - name: "Models | Template warmup script"
- ansible.builtin.template:
- src: "{{ playbook_dir }}/../templates/ollama/warmup.sh.j2"
- dest: /usr/local/bin/ollama-warmup.sh
- mode: "0755"
- owner: root
- group: root
- tags:
- - models-warmup
- - name: "Models | Template warmup systemd service"
- ansible.builtin.template:
- src: "{{ playbook_dir }}/../templates/systemd/ollama-warmup.service.j2"
- dest: /etc/systemd/system/ollama-warmup.service
- mode: "0644"
- owner: root
- group: root
- tags:
- - models-warmup
- - name: "Models | Reload systemd daemon"
- ansible.builtin.systemd:
- daemon_reload: true
- tags:
- - models-warmup
- - name: "Models | Enable and start warmup service"
- ansible.builtin.systemd:
- name: ollama-warmup
- enabled: true
- state: started
- tags:
- - models-warmup
- # ── Node0 warmup service ─────────────────────────────────────────
- - name: "Models | Template node0 warmup script"
- ansible.builtin.template:
- src: "{{ playbook_dir }}/../templates/ollama/warmup-node0.sh.j2"
- dest: /usr/local/bin/ollama-warmup-node0.sh
- mode: "0755"
- owner: root
- group: root
- tags:
- - models-warmup
- - name: "Models | Template node0 warmup systemd service"
- ansible.builtin.template:
- src: "{{ playbook_dir }}/../templates/systemd/ollama-warmup-node0.service.j2"
- dest: /etc/systemd/system/ollama-warmup-node0.service
- mode: "0644"
- owner: root
- group: root
- tags:
- - models-warmup
- - name: "Models | Enable and start node0 warmup service"
- ansible.builtin.systemd:
- name: ollama-warmup-node0
- enabled: true
- state: started
- daemon_reload: true
- tags:
- - models-warmup
|