---
- 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