--- # Windows-specific setup for llama_server role - name: Define Windows paths and versions ansible.builtin.set_fact: __llama_user: "{{ llama_server_windows_user }}" __llama_release_asset_name: "{{ 'llama-' ~ llama_server_windows_version ~ '-bin-win-' ~ llama_server_windows_backend ~ (llama_server_windows_backend == 'cuda') | ternary('-' ~ llama_server_cuda_version, '') ~ '-x64.zip' }}" __llama_release_zip_url: "https://github.com/ggml-org/llama.cpp/releases/download/{{ llama_server_windows_version }}/{{ 'llama-' ~ llama_server_windows_version ~ '-bin-win-' ~ llama_server_windows_backend ~ (llama_server_windows_backend == 'cuda') | ternary('-' ~ llama_server_cuda_version, '') ~ '-x64.zip' }}" __llama_release_zip_path: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\{{ 'llama-' ~ llama_server_windows_version ~ '-bin-win-' ~ llama_server_windows_backend ~ (llama_server_windows_backend == 'cuda') | ternary('-' ~ llama_server_cuda_version, '') ~ '-x64.zip' }}" __llama_release_marker: "{{ llama_server_windows_install_dir }}\\.release-{{ 'llama-' ~ llama_server_windows_version ~ '-bin-win-' ~ llama_server_windows_backend ~ (llama_server_windows_backend == 'cuda') | ternary('-' ~ llama_server_cuda_version, '') ~ '-x64.zip' }}" __llama_sync_script: "{{ llama_server_windows_working_dir }}\\.router\\llama_hf_sync.py" __llama_models_json: "{{ llama_server_windows_working_dir }}\\.router\\desired-models.json" __llama_preset_global_json: "{{ llama_server_windows_working_dir }}\\.router\\preset-global.json" __llama_managed_dir: "{{ llama_server_windows_working_dir }}\\models\\managed" __llama_links_dir: "{{ llama_server_windows_working_dir }}\\models\\managed-links" __llama_router_dir: "{{ llama_server_windows_working_dir }}\\.router" __llama_manifest: "{{ llama_server_windows_working_dir }}\\.router\\manifest.json" __llama_preset_file: "{{ llama_server_windows_working_dir }}\\.router\\models.ini" __llama_venv_path: "{{ llama_server_windows_working_dir }}\\.router\\.venv" __llama_venv_python: "{{ llama_server_windows_working_dir }}\\.router\\.venv\\Scripts\\python.exe" - name: Validate llama_server_models schema ansible.builtin.assert: that: - llama_server_models is iterable fail_msg: "llama_server_models must be a list" - name: Create llama server installation directories ansible.windows.win_file: path: "{{ item }}" state: directory loop: - "{{ llama_server_windows_install_dir }}" - "{{ llama_server_windows_working_dir }}" - "{{ llama_server_windows_working_dir }}\\models\\managed" - "{{ llama_server_windows_working_dir }}\\models\\managed-links" - "{{ llama_server_windows_working_dir }}\\.router" - name: Check if Python is available ansible.windows.win_stat: path: "{{ llama_server_windows_python_path }}" register: __llama_python_check - name: Download Python installer ansible.windows.win_get_url: url: "https://www.python.org/ftp/python/{{ llama_server_windows_python_version }}/python-{{ llama_server_windows_python_version }}-amd64.exe" dest: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\python-{{ llama_server_windows_python_version }}-amd64.exe" force: false when: not __llama_python_check.stat.exists - name: Install Python if not available ansible.windows.win_package: path: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\python-{{ llama_server_windows_python_version }}-amd64.exe" arguments: /quiet InstallAllUsers=1 PrependPath=0 Include_test=0 state: present when: not __llama_python_check.stat.exists - name: Check Microsoft VC++ runtime availability ansible.windows.win_stat: path: "C:\\Windows\\System32\\vcruntime140.dll" register: __llama_vcredist_check - name: Download Microsoft VC++ redistributable ansible.windows.win_get_url: url: "{{ llama_server_windows_vcredist_url }}" dest: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\vc_redist.x64.exe" force: false when: not __llama_vcredist_check.stat.exists - name: Install Microsoft VC++ redistributable ansible.windows.win_package: path: "{{ (ansible_env.TEMP | default('C:\\Windows\\Temp')) }}\\vc_redist.x64.exe" arguments: /install /quiet /norestart state: present when: not __llama_vcredist_check.stat.exists - name: Download llama.cpp Windows binary ansible.windows.win_get_url: url: "{{ __llama_release_zip_url }}" dest: "{{ __llama_release_zip_path }}" force: false - name: Check deployed llama.cpp release marker ansible.windows.win_stat: path: "{{ __llama_release_marker }}" register: __llama_release_marker_stat - name: Remove stale llama-server binary before release upgrade ansible.windows.win_file: path: "{{ llama_server_windows_install_dir }}\\{{ llama_server_windows_bin_name }}" state: absent when: not __llama_release_marker_stat.stat.exists - name: Extract llama.cpp binary community.windows.win_unzip: src: "{{ __llama_release_zip_path }}" dest: "{{ llama_server_windows_install_dir }}" delete_archive: false when: not __llama_release_marker_stat.stat.exists - name: Mark deployed llama.cpp release ansible.windows.win_copy: content: "{{ __llama_release_asset_name }}" dest: "{{ __llama_release_marker }}" when: not __llama_release_marker_stat.stat.exists - name: Verify llama-server binary is present ansible.windows.win_stat: path: "{{ llama_server_windows_install_dir }}\\{{ llama_server_windows_bin_name }}" register: __llama_bin_stat - name: Fail if llama-server binary is missing ansible.builtin.fail: msg: >- llama-server binary not found at {{ llama_server_windows_install_dir }}\{{ llama_server_windows_bin_name }}. Check download/unzip and llama_server_windows_backend settings. when: not __llama_bin_stat.stat.exists - name: Install NSSM ansible.builtin.include_role: name: nssm - name: Copy Windows-compatible Hugging Face sync script ansible.windows.win_copy: src: scripts/windows/llama_hf_sync.py dest: "{{ __llama_sync_script }}" mode: "0750" - name: Write desired llama server model manifest input ansible.windows.win_copy: content: "{{ llama_server_models | to_nice_json }}" dest: "{{ __llama_models_json }}" - name: Write global llama server preset options ansible.windows.win_copy: content: "{{ llama_server_preset_global | to_nice_json }}" dest: "{{ __llama_preset_global_json }}" - name: Check llama sync virtualenv ansible.windows.win_stat: path: "{{ __llama_venv_python }}" register: __llama_venv_python_check - name: Create llama sync virtualenv ansible.windows.win_command: argv: - "{{ llama_server_windows_python_path }}" - -m - venv - "{{ __llama_venv_path }}" changed_when: false when: not __llama_venv_python_check.stat.exists - name: Upgrade pip tooling in llama sync virtualenv ansible.windows.win_command: argv: - "{{ __llama_venv_python }}" - -m - pip - install - --upgrade - pip - setuptools - packaging changed_when: false - name: Install Hugging Face dependency in llama sync virtualenv ansible.windows.win_command: argv: - "{{ __llama_venv_python }}" - -m - pip - install - "huggingface_hub>=0.32.0" changed_when: false - name: Run llama server model sync from Hugging Face ansible.windows.win_command: cmd: >- "{{ __llama_venv_python }}" "{{ __llama_sync_script }}" --models-file "{{ __llama_models_json }}" --managed-dir "{{ __llama_managed_dir }}" --links-dir "{{ __llama_links_dir }}" --manifest-file "{{ __llama_manifest }}" --preset-file "{{ __llama_preset_file }}" --preset-global-file "{{ __llama_preset_global_json }}" --container-links-dir "{{ __llama_links_dir | replace('\\', '/') }}" {% if llama_server_models_prune_enabled | default(true) %}--prune{% endif %} {% if llama_server_models_dry_run | default(false) %}--dry-run{% endif %} register: __llama_sync_output__ changed_when: false environment: HF_TOKEN: "{{ lookup('env', 'HF_TOKEN') | default('', true) }}" - name: Parse llama server sync output ansible.builtin.set_fact: __llama_sync_result__: "{{ __llama_sync_output__.stdout | from_json }}" - name: Stat llama router preset file (when not in dry-run mode) ansible.windows.win_stat: path: "{{ __llama_preset_file }}" register: __llama_preset_stat__ when: not (llama_server_models_dry_run | default(false)) - name: Ensure router preset file exists when not in dry-run mode ansible.builtin.assert: that: - __llama_preset_stat__.stat.exists fail_msg: "llama router preset file was not created" when: not (llama_server_models_dry_run | default(false)) - name: Remove existing llama-server service if present ansible.windows.win_service: name: llama-server state: absent failed_when: false - name: Install llama-server service via NSSM ansible.windows.win_command: argv: >- {{ [ 'nssm.exe', 'install', 'llama-server', (llama_server_windows_install_dir ~ '\\' ~ llama_server_windows_bin_name), '--port', (llama_server_port | default(11434) | string), '--host', (llama_server_host | default('127.0.0.1') | string), '--metrics', '--models-preset', (__llama_preset_file | string), '--models-max', (llama_server_models_max | string) ] + (llama_server_argv_extra | default([])) }} args: chdir: "{{ llama_server_windows_install_dir }}" register: __llama_nssm_install changed_when: __llama_nssm_install.rc == 0 - name: Set llama-server AppDirectory via NSSM ansible.windows.win_command: argv: - nssm.exe - set - llama-server - AppDirectory - "{{ llama_server_windows_install_dir }}" - name: Configure llama-server stdout log via NSSM ansible.windows.win_command: argv: - nssm.exe - set - llama-server - AppStdout - "{{ llama_server_windows_install_dir }}\\llama-server.out.log" - name: Configure llama-server stderr log via NSSM ansible.windows.win_command: argv: - nssm.exe - set - llama-server - AppStderr - "{{ llama_server_windows_install_dir }}\\llama-server.err.log" - name: Set llama-server Start mode to AUTO via NSSM ansible.windows.win_command: argv: - nssm.exe - set - llama-server - Start - SERVICE_AUTO_START - name: Ensure llama-server service is running ansible.windows.win_service: name: llama-server start_mode: auto state: restarted when: not ansible_check_mode | default(false) - name: Wait for llama-server to listen on port ansible.windows.win_wait_for: port: "{{ llama_server_port | default(11434) }}" delay: 5 timeout: 60 when: not ansible_check_mode | default(false) - name: Query llama router model list ansible.windows.win_uri: url: "http://127.0.0.1:{{ llama_server_port | default(11434) }}/models?reload=1" method: GET status_code: 200 return_content: true register: __llama_router_models__ changed_when: false when: - llama_server_models_sync_enabled | default(true) - not (llama_server_models_dry_run | default(false)) - name: Parse llama router model list payload ansible.builtin.set_fact: __llama_router_models_payload__: "{{ __llama_router_models__.content | from_json }}" when: - llama_server_models_sync_enabled | default(true) - not (llama_server_models_dry_run | default(false)) - name: Validate expected model aliases are available in router ansible.builtin.assert: that: - item.name in (__llama_router_models_payload__.data | map(attribute='id') | list) fail_msg: "Model name '{{ item.name }}' not present in llama router /models output" loop: "{{ __llama_sync_result__.models | default([]) }}" loop_control: label: "{{ item.name }}" when: - llama_server_models_sync_enabled | default(true) - not (llama_server_models_dry_run | default(false)) - name: Set firewall remote IPs when string provided ansible.builtin.set_fact: llama_server_windows_remoteip: "{{ llama_server_windows_sources }}" when: - llama_server_windows_sources is defined - llama_server_windows_sources is string - name: Set firewall remote IPs when list provided ansible.builtin.set_fact: llama_server_windows_remoteip: "{{ llama_server_windows_sources | join(',') }}" when: - llama_server_windows_sources is defined - not (llama_server_windows_sources is string) - name: Allow incoming traffic to llama-server port through Windows Firewall community.windows.win_firewall_rule: name: Llama Server "{{ llama_server_port | default(11434) }}" localport: "{{ llama_server_port | default(11434) }}" protocol: tcp direction: in action: allow enabled: true # Provide one or more IPs/CIDRs in llama_server_windows_sources (e.g. ['203.0.113.10']) remoteip: "{{ llama_server_windows_remoteip | default(omit) }}" state: present # --- Windows Exporter tasks --- - name: Run Windows Exporter setup when: llama_server_windows_exporter_enabled | default(true) | bool ansible.builtin.include_tasks: file: llama_exporter.yml tags: - roles::llama_server::llama_exporter