00_core.ymlยถ

---
- include_tasks: utils/once/flag.yml

- include_tasks: utils/load/handlers.yml
  vars:
    handler_role_name: "sys-svc-compose"

- name: "๐Ÿงฉ For '{{ application_id }}': Load central RDBMS"
  include_role:
    name: sys-svc-rdbms
    public: false
  vars:
    database_consumer_id: "{{ application_id }}"
  when: lookup('database', application_id, 'enabled') | bool

- name: "๐Ÿ›‘ Refuse a remote model whose provider has no key slot"
  ansible.builtin.assert:
    that:
      - LITELLM_REMOTE_MODELS | map(attribute='provider') | difference((LITELLM_PROVIDER_KEYS | list) + [AI_MOCK_PROVIDER]) | length == 0
    fail_msg: >-
      services.litellm.remote_models names provider(s)
      {{ LITELLM_REMOTE_MODELS | map(attribute='provider') | difference((LITELLM_PROVIDER_KEYS | list) + [AI_MOCK_PROVIDER]) | unique | join(', ') }};
      known providers are {{ ((LITELLM_PROVIDER_KEYS | list) + [AI_MOCK_PROVIDER]) | join(', ') }}. A model served by
      '{{ AI_MOCK_PROVIDER }}' answers from its own declared response and needs no key.
    quiet: true

- name: "๐Ÿ” Look for the rates the last deploy measured"
  ansible.builtin.stat:
    path: "{{ LITELLM_SPEED_PATH_HOST }}"
  delegate_to: "{{ STACK_HOST }}"
  run_once: true
  register: litellm_speed_file

- name: "๐Ÿ“ฅ Read the rates the last deploy measured"
  ansible.builtin.slurp:
    src: "{{ LITELLM_SPEED_PATH_HOST }}"
  delegate_to: "{{ STACK_HOST }}"
  run_once: true
  register: litellm_speed_stored
  when: litellm_speed_file.stat.exists

- name: "๐Ÿงฎ Rank this render on those rates"
  ansible.builtin.set_fact:
    LITELLM_MEASURED_SPEED: "{{ (litellm_speed_stored.content | b64decode | from_json) if litellm_speed_file.stat.exists else {} }}"

- name: "๐Ÿ“ Create the decider package directory"
  ansible.builtin.file:
    path: "{{ LITELLM_DECIDER_DIR_HOST }}"
    state: directory
    mode: "0755"
  delegate_to: "{{ STACK_HOST }}"
  run_once: true

- name: "๐Ÿงฉ Stage the decider package beside the router hook"
  stack_host_copy:
    src: "{{ [ LITELLM_DECIDER_SOURCE_DIR, item ] | path_join }}"
    dest: "{{ [ LITELLM_DECIDER_DIR_HOST, item ] | path_join }}"
    mode: "0444"
  loop: "{{ LITELLM_DECIDER_MODULES }}"
  notify:
    - compose-up
    - container-recreate

- name: "๐Ÿ“„ Render LiteLLM gateway config (local + swarm peers)"
  ansible.builtin.include_tasks:
    file: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/render_replicated_templates.yml') }}"
  vars:
    role_templates:
      - src: "router_hook.py.j2"
        dest: "{{ LITELLM_ROUTER_HOOK_PATH_HOST }}"
      - src: "config.yaml.j2"
        dest: "{{ LITELLM_CONFIG_PATH_HOST }}"

- name: "๐Ÿ”ง Setup container network for {{ application_id }}"
  include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/network/routine.yml') }}"
  vars:
    compose_handlers_flush: true

- name: "๐Ÿ” Build consumer key list"
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"
  ansible.builtin.set_fact:
    LITELLM_CONSUMER_KEYS: >-
      {%- set out = [] -%}
      {%- for consumer in lookup('roles_with_service', 'litellm', scope='all', include_headless=true) -%}
        {%- set key = lookup('config', consumer.id, 'secrets.credentials.litellm_api_key', '') -%}
        {%- if key -%}
          {%- set _ = out.append({'alias': consumer.id, 'key': key}) -%}
        {%- endif -%}
      {%- endfor -%}
      {{ out }}

- name: "๐Ÿ”‘ Provision per-consumer virtual keys"
  ansible.builtin.include_tasks: 01_keys.yml
  when:
    - IS_STACK_HOST | bool
    - LITELLM_CONSUMER_KEYS | length > 0

- name: "๐Ÿ” Publish the current gateway models in Open WebUI"
  ansible.builtin.include_role:
    name: web-app-openwebui
    tasks_from: utils/refresh_model_grants.yml
  vars:
    application_id: web-app-openwebui
  when:
    - "'web-app-openwebui' in group_names"
    - lookup('config', 'web-app-openwebui', 'services.agent-broker.enabled') | bool