feat: support macos runtime deployment
This commit is contained in:
parent
0e1f8ab7cf
commit
dbbce5ff49
@ -4,6 +4,7 @@
|
||||
daemon_reload: true
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Restart openclaw gateway
|
||||
ansible.builtin.shell: |
|
||||
@ -24,6 +25,18 @@
|
||||
become: true
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Restart openclaw on macOS
|
||||
ansible.builtin.command: "launchctl stop plus.svc.xworkspace.openclaw"
|
||||
register: launchctl_stop
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
notify: Start openclaw on macOS
|
||||
|
||||
- name: Start openclaw on macOS
|
||||
ansible.builtin.command: "launchctl start plus.svc.xworkspace.openclaw"
|
||||
changed_when: false
|
||||
|
||||
- name: Reload caddy
|
||||
ansible.builtin.systemd:
|
||||
@ -31,3 +44,4 @@
|
||||
state: reloaded
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
13
roles/vhosts/gateway_openclaw/tasks/macos.yml
Normal file
13
roles/vhosts/gateway_openclaw/tasks/macos.yml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Create launchd plist template for OpenClaw Gateway
|
||||
ansible.builtin.template:
|
||||
src: openclaw.plist.j2
|
||||
dest: "{{ ansible_env.HOME }}/Library/LaunchAgents/plus.svc.xworkspace.openclaw.plist"
|
||||
mode: "0644"
|
||||
notify: Restart openclaw on macOS
|
||||
|
||||
- name: Reload launchd agent for OpenClaw
|
||||
ansible.builtin.command: "launchctl load -w {{ ansible_env.HOME }}/Library/LaunchAgents/plus.svc.xworkspace.openclaw.plist"
|
||||
register: launchctl_result
|
||||
changed_when: false
|
||||
failed_when: launchctl_result.rc != 0 and 'already loaded' not in launchctl_result.stderr
|
||||
@ -219,11 +219,13 @@
|
||||
register: gateway_openclaw_config_attrs
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Remove immutable flag from OpenClaw gateway JSON config when present
|
||||
ansible.builtin.command:
|
||||
cmd: chattr -i "{{ gateway_openclaw_config_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_config_attrs.stdout | default(''))"
|
||||
changed_when: true
|
||||
|
||||
@ -301,6 +303,7 @@
|
||||
ansible.builtin.command:
|
||||
cmd: chattr +i "{{ gateway_openclaw_config_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_config_attrs.stdout | default(''))"
|
||||
- not ansible_check_mode
|
||||
changed_when: true
|
||||
@ -398,6 +401,7 @@
|
||||
owner: "{{ gateway_openclaw_service_user }}"
|
||||
group: "{{ gateway_openclaw_service_group }}"
|
||||
mode: "0755"
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Deploy OpenClaw user systemd unit
|
||||
ansible.builtin.template:
|
||||
@ -407,6 +411,7 @@
|
||||
group: "{{ gateway_openclaw_service_group }}"
|
||||
mode: "0644"
|
||||
register: gateway_openclaw_user_service_unit
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Deploy OpenClaw user systemd shell environment
|
||||
ansible.builtin.template:
|
||||
@ -415,6 +420,7 @@
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Enable OpenClaw service user linger
|
||||
ansible.builtin.command:
|
||||
@ -422,6 +428,7 @@
|
||||
creates: "/var/lib/systemd/linger/{{ gateway_openclaw_service_user }}"
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Resolve OpenClaw service user uid
|
||||
ansible.builtin.command:
|
||||
@ -429,6 +436,7 @@
|
||||
register: gateway_openclaw_resolved_service_uid
|
||||
changed_when: false
|
||||
check_mode: false
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Set OpenClaw effective service uid
|
||||
ansible.builtin.set_fact:
|
||||
@ -436,8 +444,9 @@
|
||||
{{
|
||||
gateway_openclaw_service_uid
|
||||
if (gateway_openclaw_service_uid | string | trim | length > 0)
|
||||
else gateway_openclaw_resolved_service_uid.stdout
|
||||
else (gateway_openclaw_resolved_service_uid.stdout | default(''))
|
||||
}}
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Ensure OpenClaw service user manager is running
|
||||
ansible.builtin.systemd:
|
||||
@ -445,6 +454,7 @@
|
||||
state: started
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Stop and disable stale root-managed OpenClaw gateway service
|
||||
ansible.builtin.systemd:
|
||||
@ -454,6 +464,7 @@
|
||||
failed_when: false
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Inspect stale OpenClaw gateway root systemd unit attributes
|
||||
ansible.builtin.command:
|
||||
@ -461,11 +472,13 @@
|
||||
register: gateway_openclaw_unit_attrs
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Remove immutable flag from stale OpenClaw gateway root systemd unit when present
|
||||
ansible.builtin.command:
|
||||
cmd: chattr -i "{{ gateway_openclaw_service_unit_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_unit_attrs.stdout | default(''))"
|
||||
changed_when: true
|
||||
|
||||
@ -474,6 +487,7 @@
|
||||
path: "{{ gateway_openclaw_service_unit_path }}"
|
||||
state: absent
|
||||
register: gateway_openclaw_removed_root_service_unit
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Reload root systemd after removing stale OpenClaw gateway unit
|
||||
ansible.builtin.systemd:
|
||||
@ -481,6 +495,7 @@
|
||||
when:
|
||||
- gateway_openclaw_removed_root_service_unit.changed | default(false)
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Reload OpenClaw user systemd manager
|
||||
ansible.builtin.shell: |
|
||||
@ -498,6 +513,7 @@
|
||||
changed_when: false
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Ensure OpenClaw user gateway service is enabled and running
|
||||
ansible.builtin.shell: |
|
||||
@ -518,6 +534,7 @@
|
||||
'Created symlink' in (gateway_openclaw_user_service_enable.stderr | default(''))
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Restart OpenClaw user gateway service after unit changes
|
||||
ansible.builtin.shell: |
|
||||
@ -535,6 +552,11 @@
|
||||
when:
|
||||
- gateway_openclaw_user_service_unit.changed | default(false)
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Import macOS specific service tasks
|
||||
ansible.builtin.import_tasks: macos.yml
|
||||
when: ansible_os_family == 'Darwin'
|
||||
|
||||
- name: Ensure Caddy fragment directory exists
|
||||
ansible.builtin.file:
|
||||
@ -550,11 +572,13 @@
|
||||
register: gateway_openclaw_caddyfile_attrs
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Remove immutable flag from Caddy main file when present
|
||||
ansible.builtin.command:
|
||||
cmd: chattr -i "{{ gateway_openclaw_caddyfile_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_caddyfile_attrs.stdout | default(''))"
|
||||
changed_when: true
|
||||
|
||||
@ -574,6 +598,7 @@
|
||||
ansible.builtin.command:
|
||||
cmd: chattr +i "{{ gateway_openclaw_caddyfile_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_caddyfile_attrs.stdout | default(''))"
|
||||
- not ansible_check_mode
|
||||
changed_when: true
|
||||
@ -584,11 +609,13 @@
|
||||
register: gateway_openclaw_caddy_fragment_attrs
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Remove immutable flag from OpenClaw Caddy fragment when present
|
||||
ansible.builtin.command:
|
||||
cmd: chattr -i "{{ gateway_openclaw_caddy_fragment_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_caddy_fragment_attrs.stdout | default(''))"
|
||||
changed_when: true
|
||||
|
||||
@ -613,6 +640,7 @@
|
||||
ansible.builtin.command:
|
||||
cmd: chattr +i "{{ gateway_openclaw_caddy_fragment_path }}"
|
||||
when:
|
||||
- ansible_os_family != 'Darwin'
|
||||
- "'i' in (gateway_openclaw_caddy_fragment_attrs.stdout | default(''))"
|
||||
- not ansible_check_mode
|
||||
changed_when: true
|
||||
@ -624,3 +652,4 @@
|
||||
state: started
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
35
roles/vhosts/gateway_openclaw/templates/openclaw.plist.j2
Normal file
35
roles/vhosts/gateway_openclaw/templates/openclaw.plist.j2
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>plus.svc.xworkspace.openclaw</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>-c</string>
|
||||
<string>
|
||||
export PATH="{{ gateway_openclaw_service_path }}"
|
||||
export NODE_COMPILE_CACHE="{{ gateway_openclaw_compile_cache_dir }}"
|
||||
export OPENCLAW_NO_RESPAWN=1
|
||||
|
||||
exec "{{ gateway_openclaw_binary_path }}" gateway --json-config "{{ gateway_openclaw_config_path }}"
|
||||
</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>{{ gateway_openclaw_home }}</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>{{ gateway_openclaw_home }}/.local/state/xworkspace/openclaw.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>{{ gateway_openclaw_home }}/.local/state/xworkspace/openclaw.err.log</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>HOME</key>
|
||||
<string>{{ gateway_openclaw_home }}</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -34,6 +34,10 @@ litellm_ui_password: "{{ litellm_master_key }}"
|
||||
|
||||
litellm_deepseek_api_key: "{{ lookup('ansible.builtin.env', 'DEEPSEEK_API_KEY') | default('', true) }}"
|
||||
litellm_openai_api_key: "{{ lookup('ansible.builtin.env', 'OPENAI_API_KEY') | default('', true) }}"
|
||||
litellm_nvidia_api_key: "{{ lookup('ansible.builtin.env', 'NVIDIA_API_KEY') | default('', true) }}"
|
||||
litellm_gemini_api_key: "{{ lookup('ansible.builtin.env', 'GEMINI_API_KEY') | default('', true) }}"
|
||||
litellm_anthropic_api_key: "{{ lookup('ansible.builtin.env', 'ANTHROPIC_API_KEY') | default('', true) }}"
|
||||
litellm_ollama_api_key: "{{ lookup('ansible.builtin.env', 'OLLAMA_API_KEY') | default('', true) }}"
|
||||
|
||||
litellm_caddyfile_path: /etc/caddy/Caddyfile
|
||||
litellm_caddy_conf_dir: /etc/caddy/conf.d
|
||||
|
||||
130
roles/vhosts/litellm/files/register_mainstream_models.sh
Normal file
130
roles/vhosts/litellm/files/register_mainstream_models.sh
Normal file
@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Configurable endpoints and tokens
|
||||
LITELLM_URL="${LITELLM_URL:-http://127.0.0.1:4000}"
|
||||
LITELLM_TOKEN="${LITELLM_TOKEN:-}"
|
||||
if [ -z "$LITELLM_TOKEN" ] && [ -f "$HOME/.ai_workspace_auth_token" ]; then
|
||||
LITELLM_TOKEN="$(cat "$HOME/.ai_workspace_auth_token")"
|
||||
fi
|
||||
|
||||
if [ -z "$LITELLM_TOKEN" ]; then
|
||||
echo "Error: LITELLM_TOKEN is not set and could not be read from ~/.ai_workspace_auth_token."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INFO] Using LiteLLM URL: $LITELLM_URL"
|
||||
|
||||
# Function to add a model
|
||||
add_model() {
|
||||
local alias_name="$1"
|
||||
local litellm_provider_model="$2"
|
||||
local api_key_env_var="$3"
|
||||
local api_base="${4:-}"
|
||||
|
||||
echo "Adding model: $alias_name -> $litellm_provider_model"
|
||||
|
||||
local payload
|
||||
if [ -n "$api_base" ]; then
|
||||
payload=$(cat <<EOF
|
||||
{
|
||||
"model_name": "$alias_name",
|
||||
"litellm_params": {
|
||||
"model": "$litellm_provider_model",
|
||||
"api_key": "os.environ/$api_key_env_var",
|
||||
"api_base": "$api_base"
|
||||
},
|
||||
"model_info": {
|
||||
"id": "$alias_name",
|
||||
"mode": "chat"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
else
|
||||
payload=$(cat <<EOF
|
||||
{
|
||||
"model_name": "$alias_name",
|
||||
"litellm_params": {
|
||||
"model": "$litellm_provider_model",
|
||||
"api_key": "os.environ/$api_key_env_var"
|
||||
},
|
||||
"model_info": {
|
||||
"id": "$alias_name",
|
||||
"mode": "chat"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
fi
|
||||
|
||||
local response
|
||||
local http_code
|
||||
response=$(curl -s -w "\nHTTP_CODE:%{http_code}" -X POST "$LITELLM_URL/model/new" \
|
||||
-H "Authorization: Bearer $LITELLM_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$payload")
|
||||
|
||||
http_code=$(echo "$response" | grep -Eo 'HTTP_CODE:[0-9]{3}' | cut -d':' -f2 || echo "000")
|
||||
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
||||
echo "[SUCCESS] Model $alias_name added."
|
||||
else
|
||||
echo "[ERROR] Failed to add model $alias_name. HTTP Code: $http_code"
|
||||
echo "Response: $response"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering DeepSeek Models..."
|
||||
echo "========================================="
|
||||
add_model "deepseek-chat" "deepseek/deepseek-chat" "DEEPSEEK_API_KEY"
|
||||
add_model "deepseek-reasoner" "deepseek/deepseek-reasoner" "DEEPSEEK_API_KEY"
|
||||
add_model "deepseek-v4-flash" "deepseek/deepseek-v4-flash" "DEEPSEEK_API_KEY"
|
||||
add_model "deepseek-v4-pro" "deepseek/deepseek-v4-pro" "DEEPSEEK_API_KEY"
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering NVIDIA Build Models..."
|
||||
echo "========================================="
|
||||
# For NVIDIA NIM models, you can use openai format with custom base, or nvidia_nim/ provider
|
||||
add_model "nvidia/deepseek-r1" "openai/deepseek-ai/deepseek-r1" "NVIDIA_API_KEY" "https://integrate.api.nvidia.com/v1"
|
||||
add_model "nvidia/minimax-text-01" "openai/minimax/minimax-text-01" "NVIDIA_API_KEY" "https://integrate.api.nvidia.com/v1"
|
||||
add_model "nvidia/glm-4" "openai/thudm/glm-4-9b-chat" "NVIDIA_API_KEY" "https://integrate.api.nvidia.com/v1"
|
||||
add_model "nvidia/glm-5" "openai/thudm/glm-5" "NVIDIA_API_KEY" "https://integrate.api.nvidia.com/v1"
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering Gemini Models..."
|
||||
echo "========================================="
|
||||
add_model "gemini-2.5-pro" "gemini/gemini-2.5-pro" "GEMINI_API_KEY"
|
||||
add_model "gemini-2.5-flash" "gemini/gemini-2.5-flash" "GEMINI_API_KEY"
|
||||
add_model "gemini-1.5-pro" "gemini/gemini-1.5-pro" "GEMINI_API_KEY"
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering GPT Models..."
|
||||
echo "========================================="
|
||||
add_model "gpt-5.5" "openai/gpt-5.5" "OPENAI_API_KEY"
|
||||
add_model "gpt-5.4" "openai/gpt-5.4" "OPENAI_API_KEY"
|
||||
add_model "gpt-5.4-mini" "openai/gpt-5.4-mini" "OPENAI_API_KEY"
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering Claude Models..."
|
||||
echo "========================================="
|
||||
add_model "claude-3.5-sonnet" "anthropic/claude-3-5-sonnet-20241022" "ANTHROPIC_API_KEY"
|
||||
add_model "claude-3.5-haiku" "anthropic/claude-3-5-haiku-20241022" "ANTHROPIC_API_KEY"
|
||||
add_model "claude-3-opus" "anthropic/claude-3-opus-20240229" "ANTHROPIC_API_KEY"
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering Zhipu (GLM) using OLLAMA_API_KEY..."
|
||||
echo "========================================="
|
||||
add_model "glm-4" "openai/glm-4" "OLLAMA_API_KEY" "https://open.bigmodel.cn/api/paas/v4"
|
||||
add_model "glm-5" "openai/glm-5" "OLLAMA_API_KEY" "https://open.bigmodel.cn/api/paas/v4"
|
||||
|
||||
echo "========================================="
|
||||
echo "Registering OLLAMA Cloud Models..."
|
||||
echo "========================================="
|
||||
# Assuming OLLAMA API is exposed via a cloud endpoint or an OpenAI proxy
|
||||
OLLAMA_API_BASE="${OLLAMA_API_BASE:-https://api.ollama.cloud/v1}"
|
||||
add_model "ollama-llama3" "openai/llama3" "OLLAMA_API_KEY" "$OLLAMA_API_BASE"
|
||||
add_model "ollama-qwen" "openai/qwen" "OLLAMA_API_KEY" "$OLLAMA_API_BASE"
|
||||
|
||||
echo "All models requested have been registered."
|
||||
echo "You can check them at $LITELLM_URL/ui/?page=models"
|
||||
@ -2,8 +2,21 @@
|
||||
ansible.builtin.service:
|
||||
name: caddy
|
||||
state: reloaded
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Restart litellm
|
||||
ansible.builtin.service:
|
||||
name: "{{ litellm_service_name }}"
|
||||
state: restarted
|
||||
state: restarted
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Restart litellm on macOS
|
||||
ansible.builtin.command: "launchctl stop plus.svc.xworkspace.litellm"
|
||||
register: launchctl_stop
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
notify: Start litellm on macOS
|
||||
|
||||
- name: Start litellm on macOS
|
||||
ansible.builtin.command: "launchctl start plus.svc.xworkspace.litellm"
|
||||
changed_when: false
|
||||
13
roles/vhosts/litellm/tasks/macos.yml
Normal file
13
roles/vhosts/litellm/tasks/macos.yml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
- name: Create launchd plist template for LiteLLM
|
||||
ansible.builtin.template:
|
||||
src: litellm.plist.j2
|
||||
dest: "{{ ansible_env.HOME }}/Library/LaunchAgents/plus.svc.xworkspace.litellm.plist"
|
||||
mode: "0644"
|
||||
notify: Restart litellm on macOS
|
||||
|
||||
- name: Reload launchd agent for LiteLLM
|
||||
ansible.builtin.command: "launchctl load -w {{ ansible_env.HOME }}/Library/LaunchAgents/plus.svc.xworkspace.litellm.plist"
|
||||
register: launchctl_result
|
||||
changed_when: false
|
||||
failed_when: launchctl_result.rc != 0 and 'already loaded' not in launchctl_result.stderr
|
||||
@ -1,7 +1,7 @@
|
||||
---
|
||||
# Provision the litellm database and user BEFORE litellm starts.
|
||||
# Only runs when litellm_database_host is set.
|
||||
- name: Install LiteLLM prerequisites
|
||||
- name: Install LiteLLM prerequisites (Linux)
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- python3
|
||||
@ -9,6 +9,13 @@
|
||||
- python3-venv
|
||||
- python3-psycopg2
|
||||
state: present
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Install LiteLLM prerequisites (macOS)
|
||||
community.general.homebrew:
|
||||
name: python@3.13
|
||||
state: present
|
||||
when: ansible_os_family == 'Darwin'
|
||||
|
||||
- name: Materialize persisted LiteLLM secrets
|
||||
ansible.builtin.assert:
|
||||
@ -42,7 +49,9 @@
|
||||
ansible.builtin.group:
|
||||
name: "{{ litellm_service_group }}"
|
||||
state: present
|
||||
when: litellm_service_group != 'root'
|
||||
when:
|
||||
- litellm_service_group != 'root'
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Ensure litellm service user exists
|
||||
ansible.builtin.user:
|
||||
@ -51,7 +60,9 @@
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
state: present
|
||||
when: litellm_service_user != 'root'
|
||||
when:
|
||||
- litellm_service_user != 'root'
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Ensure litellm config directory exists
|
||||
ansible.builtin.file:
|
||||
@ -133,11 +144,14 @@
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart litellm
|
||||
when: ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Reload systemd after unit changes
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
when: not ansible_check_mode
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Ensure Caddy fragment directory exists
|
||||
ansible.builtin.file:
|
||||
@ -209,7 +223,13 @@
|
||||
name: "{{ litellm_service_name }}"
|
||||
enabled: true
|
||||
state: started
|
||||
when: not ansible_check_mode
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Import macOS specific service tasks
|
||||
ansible.builtin.import_tasks: macos.yml
|
||||
when: ansible_os_family == 'Darwin'
|
||||
|
||||
- name: Ensure Caddy is enabled and running
|
||||
ansible.builtin.systemd:
|
||||
@ -219,6 +239,7 @@
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- litellm_caddy_config_enabled | bool
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Apply litellm and Caddy changes before validation
|
||||
ansible.builtin.meta: flush_handlers
|
||||
@ -243,3 +264,24 @@
|
||||
owner: "{{ litellm_service_user }}"
|
||||
group: "{{ litellm_service_group }}"
|
||||
mode: "0600"
|
||||
|
||||
- name: Register mainstream models to LiteLLM
|
||||
ansible.builtin.script: files/register_mainstream_models.sh
|
||||
environment:
|
||||
LITELLM_URL: "http://{{ litellm_listen_host }}:{{ litellm_listen_port }}"
|
||||
LITELLM_TOKEN: "{{ litellm_master_key }}"
|
||||
DEEPSEEK_API_KEY: "{{ litellm_deepseek_api_key }}"
|
||||
OPENAI_API_KEY: "{{ litellm_openai_api_key }}"
|
||||
NVIDIA_API_KEY: "{{ litellm_nvidia_api_key }}"
|
||||
GEMINI_API_KEY: "{{ litellm_gemini_api_key }}"
|
||||
ANTHROPIC_API_KEY: "{{ litellm_anthropic_api_key }}"
|
||||
OLLAMA_API_KEY: "{{ litellm_ollama_api_key }}"
|
||||
when: >
|
||||
not ansible_check_mode and
|
||||
((litellm_deepseek_api_key | length > 0) or
|
||||
(litellm_openai_api_key | length > 0) or
|
||||
(litellm_nvidia_api_key | length > 0) or
|
||||
(litellm_gemini_api_key | length > 0) or
|
||||
(litellm_anthropic_api_key | length > 0) or
|
||||
(litellm_ollama_api_key | length > 0))
|
||||
changed_when: false
|
||||
|
||||
@ -5,6 +5,10 @@ UI_PASSWORD={{ litellm_ui_password }}
|
||||
LITELLM_DB_PASSWORD={{ litellm_database_password }}
|
||||
DEEPSEEK_API_KEY={{ litellm_deepseek_api_key }}
|
||||
OPENAI_API_KEY={{ litellm_openai_api_key }}
|
||||
NVIDIA_API_KEY={{ litellm_nvidia_api_key }}
|
||||
GEMINI_API_KEY={{ litellm_gemini_api_key }}
|
||||
ANTHROPIC_API_KEY={{ litellm_anthropic_api_key }}
|
||||
OLLAMA_API_KEY={{ litellm_ollama_api_key }}
|
||||
{% if litellm_database_url | trim | length > 0 %}
|
||||
DATABASE_URL={{ litellm_database_url }}
|
||||
{% endif %}
|
||||
|
||||
39
roles/vhosts/litellm/templates/litellm.plist.j2
Normal file
39
roles/vhosts/litellm/templates/litellm.plist.j2
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>plus.svc.xworkspace.litellm</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>-c</string>
|
||||
<string>
|
||||
export PATH="/opt/homebrew/bin:/usr/local/bin:{{ litellm_service_home }}/.local/bin:$PATH"
|
||||
export DATABASE_URL="{{ litellm_database_url }}"
|
||||
export LITELLM_MASTER_KEY="{{ litellm_master_key }}"
|
||||
export LITELLM_SALT_KEY="{{ litellm_salt_key }}"
|
||||
export UI_USERNAME="{{ litellm_ui_username }}"
|
||||
export UI_PASSWORD="{{ litellm_ui_password }}"
|
||||
export DEEPSEEK_API_KEY="{{ litellm_deepseek_api_key }}"
|
||||
export OPENAI_API_KEY="{{ litellm_openai_api_key }}"
|
||||
export NVIDIA_API_KEY="{{ litellm_nvidia_api_key }}"
|
||||
export OLLAMA_API_KEY="{{ litellm_ollama_api_key }}"
|
||||
export GEMINI_API_KEY="{{ litellm_gemini_api_key }}"
|
||||
export ANTHROPIC_API_KEY="{{ litellm_anthropic_api_key }}"
|
||||
|
||||
exec "{{ litellm_proxy_dir | default(litellm_service_home ~ '/.local/lib/python3.13/site-packages/litellm/proxy') }}/../../../../bin/litellm" --host {{ litellm_listen_host }} --port {{ litellm_listen_port }} --config "{{ litellm_config_file }}" --use_prisma_db_push
|
||||
</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>{{ litellm_service_home }}</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>{{ litellm_service_home }}/.local/state/xworkspace/litellm.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>{{ litellm_service_home }}/.local/state/xworkspace/litellm.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
57
roles/vhosts/postgres/tasks/macos.yml
Normal file
57
roles/vhosts/postgres/tasks/macos.yml
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
- name: Ensure PostgreSQL 16 is installed via Homebrew
|
||||
community.general.homebrew:
|
||||
name: postgresql@16
|
||||
state: present
|
||||
|
||||
- name: Start PostgreSQL via Homebrew Services
|
||||
ansible.builtin.command: brew services start postgresql@16
|
||||
register: brew_services_output
|
||||
changed_when: "'Successfully started' in brew_services_output.stdout or 'started' in brew_services_output.stdout"
|
||||
failed_when: brew_services_output.rc != 0 and 'already started' not in brew_services_output.stderr and 'already started' not in brew_services_output.stdout
|
||||
|
||||
- name: Wait for PostgreSQL to become ready
|
||||
ansible.builtin.wait_for:
|
||||
host: "{{ postgresql_listen_addresses }}"
|
||||
port: "{{ postgresql_port }}"
|
||||
timeout: 60
|
||||
|
||||
- name: Ensure the database user exists
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
# Run the SQL via psql as the current user (which Homebrew configures as superuser)
|
||||
psql -h "{{ postgresql_listen_addresses }}" -p "{{ postgresql_port }}" -d postgres -v ON_ERROR_STOP=1 <<SQL
|
||||
DO \$\$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '{{ postgresql_admin_user }}') THEN
|
||||
CREATE ROLE "{{ postgresql_admin_user }}" LOGIN PASSWORD '{{ postgresql_admin_password }}';
|
||||
ELSE
|
||||
ALTER ROLE "{{ postgresql_admin_user }}" LOGIN PASSWORD '{{ postgresql_admin_password }}';
|
||||
END IF;
|
||||
END
|
||||
\$\$;
|
||||
SQL
|
||||
environment:
|
||||
PATH: "/opt/homebrew/opt/postgresql@16/bin:/usr/local/opt/postgresql@16/bin:{{ ansible_env.PATH }}"
|
||||
no_log: true
|
||||
changed_when: true # Idempotent SQL
|
||||
|
||||
- name: Ensure the database exists and belongs to the user
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
psql -h "{{ postgresql_listen_addresses }}" -p "{{ postgresql_port }}" -d postgres -v ON_ERROR_STOP=1 <<SQL
|
||||
SELECT format('CREATE DATABASE %I OWNER %I', '{{ postgresql_database }}', '{{ postgresql_admin_user }}')
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = '{{ postgresql_database }}') \gexec
|
||||
ALTER DATABASE "{{ postgresql_database }}" OWNER TO "{{ postgresql_admin_user }}";
|
||||
SQL
|
||||
environment:
|
||||
PATH: "/opt/homebrew/opt/postgresql@16/bin:/usr/local/opt/postgresql@16/bin:{{ ansible_env.PATH }}"
|
||||
changed_when: true # Idempotent SQL
|
||||
|
||||
- name: Verify PostgreSQL connection
|
||||
ansible.builtin.shell: |
|
||||
PGPASSWORD="{{ postgresql_admin_password }}" psql -h "{{ postgresql_listen_addresses }}" -p "{{ postgresql_port }}" -U "{{ postgresql_admin_user }}" -d "{{ postgresql_database }}" -v ON_ERROR_STOP=1 -Atc 'select 1'
|
||||
environment:
|
||||
PATH: "/opt/homebrew/opt/postgresql@16/bin:/usr/local/opt/postgresql@16/bin:{{ ansible_env.PATH }}"
|
||||
no_log: true
|
||||
changed_when: false
|
||||
@ -9,6 +9,14 @@
|
||||
ansible.builtin.import_tasks: compose.yml
|
||||
when: postgresql_deploy_mode == 'compose'
|
||||
|
||||
- name: Deploy PostgreSQL natively
|
||||
- name: Deploy PostgreSQL natively (Linux)
|
||||
ansible.builtin.import_tasks: native.yml
|
||||
when: postgresql_deploy_mode == 'native'
|
||||
when:
|
||||
- postgresql_deploy_mode == 'native'
|
||||
- ansible_os_family != 'Darwin'
|
||||
|
||||
- name: Deploy PostgreSQL natively (macOS)
|
||||
ansible.builtin.import_tasks: macos.yml
|
||||
when:
|
||||
- postgresql_deploy_mode == 'native'
|
||||
- ansible_os_family == 'Darwin'
|
||||
|
||||
@ -4,7 +4,7 @@ xworkmate_bridge_service_user: ubuntu
|
||||
xworkmate_bridge_service_group: ubuntu
|
||||
xworkmate_bridge_service_home: "/home/{{ xworkmate_bridge_service_user }}"
|
||||
ai_workspace_auth_token: "{{ lookup('ansible.builtin.env', 'AI_WORKSPACE_AUTH_TOKEN') | default('', true) }}"
|
||||
xworkmate_bridge_auth_token: "{{ lookup('ansible.builtin.env', 'BRIDGE_AUTH_TOKEN') | default(lookup('ansible.builtin.env', 'XWORKMATE_BRIDGE_AUTH_TOKEN') | default(lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default(ai_workspace_auth_token, true), true), true) }}"
|
||||
xworkmate_bridge_auth_token: "{{ lookup('ansible.builtin.env', 'BRIDGE_AUTH_TOKEN') | default(lookup('ansible.builtin.env', 'XWORKMATE_BRIDGE_AUTH_TOKEN') | default(ai_workspace_auth_token, true), true) }}"
|
||||
xworkmate_bridge_review_auth_token: "{{ lookup('ansible.builtin.env', 'BRIDGE_REVIEW_AUTH_TOKEN') | default('', true) }}"
|
||||
xworkmate_bridge_listen_host: 127.0.0.1
|
||||
xworkmate_bridge_listen_port: 8787
|
||||
@ -19,6 +19,7 @@ xworkmate_bridge_deprecated_compose_file: "{{ xworkmate_bridge_base_dir }}/docke
|
||||
xworkmate_bridge_obsolete_systemd_dropin_paths:
|
||||
- "/etc/systemd/system/{{ xworkmate_bridge_service_name }}.service.d/20-distributed-forward.conf"
|
||||
xworkmate_bridge_service_environment:
|
||||
AI_WORKSPACE_AUTH_TOKEN: "{{ ai_workspace_auth_token }}"
|
||||
BRIDGE_AUTH_TOKEN: "{{ xworkmate_bridge_effective_auth_token | default(xworkmate_bridge_auth_token) }}"
|
||||
BRIDGE_REVIEW_AUTH_TOKEN: "{{ xworkmate_bridge_effective_review_auth_token | default(xworkmate_bridge_review_auth_token) }}"
|
||||
BRIDGE_CONFIG_PATH: "{{ xworkmate_bridge_config_file }}"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user