From 6c728d4911d7ea0ac10b39c9f969a78a51fdb537 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 10 Apr 2026 17:14:38 +0800 Subject: [PATCH] feat(playbooks): add managed APISIX service deploy --- deploy_apisix_svc.plus.yaml | 7 + inventory.ini | 3 + roles/vhosts/apisix_service/defaults/main.yml | 27 ++ .../files/conf/api.svc.plus.caddy | 9 + .../apisix_service/files/conf/config.yaml | 16 + .../files/scripts/healthcheck.sh | 36 ++ .../apisix_service/files/scripts/reload.sh | 16 + .../apisix_service/files/scripts/validate.sh | 24 ++ roles/vhosts/apisix_service/handlers/main.yml | 9 + roles/vhosts/apisix_service/tasks/main.yml | 206 ++++++++++++ .../templates/conf/apisix.yaml.j2 | 312 ++++++++++++++++++ vars/cloudflare_svc_plus_dns.yml | 10 + 12 files changed, 675 insertions(+) create mode 100644 deploy_apisix_svc.plus.yaml create mode 100644 roles/vhosts/apisix_service/defaults/main.yml create mode 100644 roles/vhosts/apisix_service/files/conf/api.svc.plus.caddy create mode 100644 roles/vhosts/apisix_service/files/conf/config.yaml create mode 100644 roles/vhosts/apisix_service/files/scripts/healthcheck.sh create mode 100644 roles/vhosts/apisix_service/files/scripts/reload.sh create mode 100644 roles/vhosts/apisix_service/files/scripts/validate.sh create mode 100644 roles/vhosts/apisix_service/handlers/main.yml create mode 100644 roles/vhosts/apisix_service/tasks/main.yml create mode 100644 roles/vhosts/apisix_service/templates/conf/apisix.yaml.j2 diff --git a/deploy_apisix_svc.plus.yaml b/deploy_apisix_svc.plus.yaml new file mode 100644 index 0000000..1fb7e3d --- /dev/null +++ b/deploy_apisix_svc.plus.yaml @@ -0,0 +1,7 @@ +--- +- name: Deploy managed api.svc.plus service + hosts: "{{ apisix_service_hosts | default('apisix') }}" + gather_facts: false + become: true + roles: + - roles/vhosts/apisix_service diff --git a/inventory.ini b/inventory.ini index eb3a04c..57a2b6e 100644 --- a/inventory.ini +++ b/inventory.ini @@ -7,6 +7,9 @@ jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=roo [accounts] acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root +[apisix] +api.svc.plus ansible_host=46.250.251.132 ansible_user=root + [postgresql] acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root diff --git a/roles/vhosts/apisix_service/defaults/main.yml b/roles/vhosts/apisix_service/defaults/main.yml new file mode 100644 index 0000000..027d6b7 --- /dev/null +++ b/roles/vhosts/apisix_service/defaults/main.yml @@ -0,0 +1,27 @@ +--- +apisix_service_example_root: /Users/shenlan/workspaces/cloud-neutral-toolkit/openclaw-deploy-example/deploy/apisix/example/vps +apisix_service_root: /opt/svc-ai-gateway +apisix_service_conf_dir: "{{ apisix_service_root }}/conf" +apisix_service_docs_dir: "{{ apisix_service_root }}/docs" +apisix_service_scripts_dir: "{{ apisix_service_root }}/scripts" +apisix_service_caddy_conf_dir: /etc/caddy/conf.d +apisix_service_caddy_fragment_path: /etc/caddy/conf.d/api.svc.plus.caddy +apisix_service_env_file: "{{ apisix_service_root }}/.env" +apisix_service_compose_file: "{{ apisix_service_root }}/docker-compose.yml" +apisix_service_container_name: svc-ai-gateway +apisix_service_http_port: 9080 +apisix_service_public_host: api.svc.plus +apisix_service_validate_env: true +apisix_service_source_tree_files: + - docker-compose.yml + - Caddyfile +apisix_service_conf_files: + - conf/config.yaml +apisix_service_doc_files: + - docs/api.md + - docs/models.md + - docs/providers.md +apisix_service_script_files: + - scripts/validate.sh + - scripts/reload.sh + - scripts/healthcheck.sh diff --git a/roles/vhosts/apisix_service/files/conf/api.svc.plus.caddy b/roles/vhosts/apisix_service/files/conf/api.svc.plus.caddy new file mode 100644 index 0000000..f8eeb10 --- /dev/null +++ b/roles/vhosts/apisix_service/files/conf/api.svc.plus.caddy @@ -0,0 +1,9 @@ +api.svc.plus { + encode zstd gzip + reverse_proxy 127.0.0.1:9080 + header { + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + Referrer-Policy "no-referrer" + } +} diff --git a/roles/vhosts/apisix_service/files/conf/config.yaml b/roles/vhosts/apisix_service/files/conf/config.yaml new file mode 100644 index 0000000..4a1e8ae --- /dev/null +++ b/roles/vhosts/apisix_service/files/conf/config.yaml @@ -0,0 +1,16 @@ +apisix: + node_listen: + - 9080 + enable_admin: false + enable_control: false + router: + http: radixtree_host_uri + +deployment: + role: data_plane + role_data_plane: + config_provider: yaml + +nginx_config: + http: + access_log_format: '{"request_id":"$request_id","host":"$host","uri":"$uri","status":"$status","upstream_addr":"$upstream_addr","request_time":"$request_time","upstream_response_time":"$upstream_response_time"}' diff --git a/roles/vhosts/apisix_service/files/scripts/healthcheck.sh b/roles/vhosts/apisix_service/files/scripts/healthcheck.sh new file mode 100644 index 0000000..0887e60 --- /dev/null +++ b/roles/vhosts/apisix_service/files/scripts/healthcheck.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +ENV_FILE="${ROOT_DIR}/.env" +COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml" +CADDY_FRAGMENT="/etc/caddy/conf.d/api.svc.plus.caddy" + +if [[ -f "$ENV_FILE" ]]; then + set -a + # shellcheck disable=SC1090 + source "$ENV_FILE" + set +a +fi + +: "${API_PUBLIC_HOST:=api.svc.plus}" +: "${AI_GATEWAY_ACCESS_TOKEN:?missing AI_GATEWAY_ACCESS_TOKEN in ${ENV_FILE}}" + +for file in "$COMPOSE_FILE" "$CADDY_FRAGMENT"; do + [[ -f "$file" ]] || { + printf '[svc-ai-gateway] missing file: %s\n' "$file" >&2 + exit 1 + } +done + +caddy validate --adapter caddyfile --config "$CADDY_FRAGMENT" >/dev/null +systemctl is-active --quiet caddy +docker compose -f "$COMPOSE_FILE" ps --status running apisix >/dev/null + +curl --fail --silent --show-error \ + --resolve "${API_PUBLIC_HOST}:443:127.0.0.1" \ + -H "Authorization: Bearer ${AI_GATEWAY_ACCESS_TOKEN}" \ + "https://${API_PUBLIC_HOST}/v1/models" >/dev/null + +printf '[svc-ai-gateway] healthcheck passed\n' diff --git a/roles/vhosts/apisix_service/files/scripts/reload.sh b/roles/vhosts/apisix_service/files/scripts/reload.sh new file mode 100644 index 0000000..b989bd2 --- /dev/null +++ b/roles/vhosts/apisix_service/files/scripts/reload.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml" + +"${SCRIPT_DIR}/validate.sh" + +if docker compose -f "$COMPOSE_FILE" ps --status running apisix >/dev/null 2>&1; then + docker compose -f "$COMPOSE_FILE" restart apisix +else + docker compose -f "$COMPOSE_FILE" up -d apisix +fi + +printf '[svc-ai-gateway] reload finished\n' diff --git a/roles/vhosts/apisix_service/files/scripts/validate.sh b/roles/vhosts/apisix_service/files/scripts/validate.sh new file mode 100644 index 0000000..6161cb8 --- /dev/null +++ b/roles/vhosts/apisix_service/files/scripts/validate.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + +CONFIG_FILE="${ROOT_DIR}/conf/config.yaml" +RULES_FILE="${ROOT_DIR}/conf/apisix.yaml" +COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml" + +for file in "$CONFIG_FILE" "$RULES_FILE" "$COMPOSE_FILE"; do + [[ -f "$file" ]] || { + printf '[svc-ai-gateway] missing file: %s\n' "$file" >&2 + exit 1 + } +done + +tail -n 1 "$RULES_FILE" | grep -q '^#END$' || { + printf '[svc-ai-gateway] conf/apisix.yaml must end with #END for standalone reloads\n' >&2 + exit 1 +} + +docker compose -f "$COMPOSE_FILE" config >/dev/null +printf '[svc-ai-gateway] validation passed\n' diff --git a/roles/vhosts/apisix_service/handlers/main.yml b/roles/vhosts/apisix_service/handlers/main.yml new file mode 100644 index 0000000..aeab62a --- /dev/null +++ b/roles/vhosts/apisix_service/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: Reload caddy + ansible.builtin.command: systemctl reload caddy + changed_when: true + +- name: Restart APISIX + ansible.builtin.command: + cmd: docker compose -f "{{ apisix_service_compose_file }}" restart apisix + changed_when: true diff --git a/roles/vhosts/apisix_service/tasks/main.yml b/roles/vhosts/apisix_service/tasks/main.yml new file mode 100644 index 0000000..bc109d4 --- /dev/null +++ b/roles/vhosts/apisix_service/tasks/main.yml @@ -0,0 +1,206 @@ +--- +- name: Check docker CLI is present on the target node + ansible.builtin.command: docker --version + changed_when: false + +- name: Check docker compose plugin is present on the target node + ansible.builtin.command: docker compose version + changed_when: false + +- name: Check caddy CLI is present on the target node + ansible.builtin.command: caddy version + changed_when: false + +- name: Check managed APISIX source tree + ansible.builtin.stat: + path: "{{ apisix_service_root }}" + register: apisix_service_root_stat + +- name: Check managed APISIX conf directory + ansible.builtin.stat: + path: "{{ apisix_service_conf_dir }}" + register: apisix_service_conf_dir_stat + +- name: Check managed APISIX docs directory + ansible.builtin.stat: + path: "{{ apisix_service_docs_dir }}" + register: apisix_service_docs_dir_stat + +- name: Check managed APISIX scripts directory + ansible.builtin.stat: + path: "{{ apisix_service_scripts_dir }}" + register: apisix_service_scripts_dir_stat + +- name: Check managed APISIX Caddy fragment directory + ansible.builtin.stat: + path: "{{ apisix_service_caddy_conf_dir }}" + register: apisix_service_caddy_conf_dir_stat + +- name: Check managed APISIX env file + ansible.builtin.stat: + path: "{{ apisix_service_env_file }}" + register: apisix_service_env_file_stat + +- name: Validate APISIX live configuration prerequisites + ansible.builtin.assert: + that: + - apisix_service_env_file_stat.stat.exists + - apisix_service_public_host | length > 0 + - apisix_service_http_port | int > 0 + fail_msg: >- + api.svc.plus requires a preseeded .env plus a valid public host and HTTP port. + +- name: Determine whether managed APISIX files can be written in this run + ansible.builtin.set_fact: + apisix_service_source_tree_writable: "{{ (not ansible_check_mode) or apisix_service_root_stat.stat.exists }}" + apisix_service_conf_writable: "{{ (not ansible_check_mode) or apisix_service_conf_dir_stat.stat.exists }}" + apisix_service_docs_writable: "{{ (not ansible_check_mode) or apisix_service_docs_dir_stat.stat.exists }}" + apisix_service_scripts_writable: "{{ (not ansible_check_mode) or apisix_service_scripts_dir_stat.stat.exists }}" + apisix_service_caddy_writable: "{{ (not ansible_check_mode) or apisix_service_caddy_conf_dir_stat.stat.exists }}" + +- name: Ensure managed APISIX directories exist + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0755" + loop: + - "{{ apisix_service_root }}" + - "{{ apisix_service_conf_dir }}" + - "{{ apisix_service_docs_dir }}" + - "{{ apisix_service_scripts_dir }}" + - "{{ apisix_service_caddy_conf_dir }}" + +- name: Copy managed APISIX source tree files + ansible.builtin.copy: + src: "{{ apisix_service_example_root }}/{{ item }}" + dest: "{{ apisix_service_root }}/{{ item }}" + owner: root + group: root + mode: "0644" + loop: "{{ apisix_service_source_tree_files }}" + when: apisix_service_source_tree_writable + notify: Restart APISIX + +- name: Copy managed APISIX config files + ansible.builtin.copy: + src: "{{ role_path }}/files/{{ item }}" + dest: "{{ apisix_service_root }}/{{ item }}" + owner: root + group: root + mode: "0644" + loop: "{{ apisix_service_conf_files }}" + when: apisix_service_conf_writable + notify: Restart APISIX + +- name: Render managed APISIX route template + ansible.builtin.template: + src: "{{ role_path }}/templates/conf/apisix.yaml.j2" + dest: "{{ apisix_service_root }}/conf/apisix.yaml" + owner: root + group: root + mode: "0644" + variable_start_string: "[[" + variable_end_string: "]]" + when: apisix_service_conf_writable + notify: Restart APISIX + +- name: Copy managed APISIX docs + ansible.builtin.copy: + src: "{{ apisix_service_example_root }}/{{ item }}" + dest: "{{ apisix_service_root }}/{{ item }}" + owner: root + group: root + mode: "0644" + loop: "{{ apisix_service_doc_files }}" + when: apisix_service_docs_writable + +- name: Copy managed APISIX helper scripts + ansible.builtin.copy: + src: "{{ role_path }}/files/{{ item }}" + dest: "{{ apisix_service_root }}/{{ item }}" + owner: root + group: root + mode: "0755" + loop: "{{ apisix_service_script_files }}" + when: apisix_service_scripts_writable + +- name: Deploy managed APISIX Caddy fragment + ansible.builtin.copy: + src: "{{ role_path }}/files/conf/api.svc.plus.caddy" + dest: "{{ apisix_service_caddy_fragment_path }}" + owner: root + group: root + mode: "0644" + notify: Reload caddy + when: apisix_service_caddy_writable + +- name: Validate APISIX env contract + ansible.builtin.shell: | + set -euo pipefail + for key in \ + APISIX_HTTP_PORT \ + API_PUBLIC_HOST \ + OLLAMA_API_KEY \ + OLLAMA_CHAT_ENDPOINT \ + OLLAMA_CHAT_MODEL \ + OLLAMA_MINIMAX_MODEL \ + OLLAMA_KIMI_MODEL \ + NVIDIA_API_KEY \ + NVIDIA_CHAT_ENDPOINT \ + NVIDIA_CHAT_MODEL \ + KIMI_API_KEY \ + KIMI_CHAT_ENDPOINT \ + KIMI_CHAT_MODEL \ + MINIMAX_API_KEY \ + MINIMAX_CHAT_ENDPOINT \ + MINIMAX_CHAT_MODEL \ + EMBEDDINGS_API_KEY \ + EMBEDDINGS_ENDPOINT \ + EMBEDDINGS_MODEL \ + AI_GATEWAY_ACCESS_TOKEN + do + grep -q "^${key}=" "{{ apisix_service_env_file }}" + done + args: + executable: /bin/bash + changed_when: false + when: apisix_service_validate_env | bool + +- name: Validate Caddy configuration for APISIX + ansible.builtin.command: + cmd: caddy validate --adapter caddyfile --config "{{ apisix_service_caddy_fragment_path }}" + changed_when: false + when: not ansible_check_mode + +- name: Pull and start APISIX release + ansible.builtin.shell: | + set -euo pipefail + docker compose -f "{{ apisix_service_compose_file }}" pull + docker compose -f "{{ apisix_service_compose_file }}" up -d --remove-orphans + args: + executable: /bin/bash + changed_when: true + when: not ansible_check_mode + +- name: Flush APISIX restart handlers before validation + ansible.builtin.meta: flush_handlers + when: not ansible_check_mode + +- name: Wait for APISIX loopback port + ansible.builtin.wait_for: + host: 127.0.0.1 + port: "{{ apisix_service_http_port }}" + timeout: 60 + when: not ansible_check_mode + +- name: Reload Caddy after APISIX update + ansible.builtin.command: systemctl reload caddy + changed_when: true + when: not ansible_check_mode + +- name: Run APISIX health check + ansible.builtin.command: "{{ apisix_service_scripts_dir }}/healthcheck.sh" + changed_when: false + when: not ansible_check_mode diff --git a/roles/vhosts/apisix_service/templates/conf/apisix.yaml.j2 b/roles/vhosts/apisix_service/templates/conf/apisix.yaml.j2 new file mode 100644 index 0000000..d5ce13f --- /dev/null +++ b/roles/vhosts/apisix_service/templates/conf/apisix.yaml.j2 @@ -0,0 +1,312 @@ +consumers: + - username: ai-gateway-client + plugins: + key-auth: + key: "Bearer ${{AI_GATEWAY_ACCESS_TOKEN:=replace-me}}" + +routes: + - id: chat-z-ai-glm5 + name: chat-z-ai-glm5 + priority: 100 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/chat/completions + methods: + - POST + vars: + - - post_arg.model + - == + - z-ai/glm5 + plugins: + key-auth: + header: Authorization + hide_credentials: true + ai-proxy-multi: + fallback_strategy: + - http_429 + - http_5xx + - rate_limiting + instances: + - name: ollama-primary + provider: openai-compatible + weight: 0 + priority: 30 + auth: + header: + Authorization: "Bearer ${{OLLAMA_API_KEY}}" + options: + model: "${{OLLAMA_CHAT_MODEL:=glm-5:cloud}}" + override: + endpoint: "${{OLLAMA_CHAT_ENDPOINT}}" + - name: nvidia-fallback + provider: openai-compatible + weight: 0 + priority: 20 + auth: + header: + Authorization: "Bearer ${{NVIDIA_API_KEY}}" + options: + model: "${{NVIDIA_CHAT_MODEL:=z-ai/glm5}}" + override: + endpoint: "${{NVIDIA_CHAT_ENDPOINT}}" + logging: + summaries: true + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: chat-moonshotai-kimi-k2-5 + name: chat-moonshotai-kimi-k2-5 + priority: 100 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/chat/completions + methods: + - POST + vars: + - - post_arg.model + - == + - moonshotai/kimi-k2.5 + plugins: + key-auth: + header: Authorization + hide_credentials: true + ai-proxy: + provider: openai-compatible + auth: + header: + Authorization: "Bearer ${{OLLAMA_API_KEY}}" + options: + model: "${{OLLAMA_KIMI_MODEL:=kimi-k2.5:cloud}}" + override: + endpoint: "${{OLLAMA_CHAT_ENDPOINT}}" + logging: + summaries: true + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: chat-minimaxai-minimax-m2-5 + name: chat-minimaxai-minimax-m2-5 + priority: 100 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/chat/completions + methods: + - POST + vars: + - - post_arg.model + - == + - minimaxai/minimax-m2.5 + plugins: + key-auth: + header: Authorization + hide_credentials: true + ai-proxy: + provider: openai-compatible + auth: + header: + Authorization: "Bearer ${{OLLAMA_API_KEY}}" + options: + model: "${{OLLAMA_MINIMAX_MODEL:=minimax-m2.5:cloud}}" + override: + endpoint: "${{OLLAMA_CHAT_ENDPOINT}}" + logging: + summaries: true + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: chat-google-gemma4-31b + name: chat-google-gemma4-31b + priority: 100 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/chat/completions + methods: + - POST + vars: + - - post_arg.model + - == + - google/gemma4:31b + plugins: + key-auth: + header: Authorization + hide_credentials: true + ai-proxy: + provider: openai-compatible + auth: + header: + Authorization: "Bearer ${{OLLAMA_API_KEY}}" + options: + model: "${{OLLAMA_GEMMA4_MODEL:=gemma4:31b-cloud}}" + timeout: 60000 + override: + endpoint: "${{OLLAMA_CHAT_ENDPOINT}}" + logging: + summaries: true + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: chat-unsupported-model + name: chat-unsupported-model + priority: 1 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/chat/completions + methods: + - POST + plugins: + key-auth: + header: Authorization + hide_credentials: true + serverless-pre-function: + phase: access + functions: + - | + return function() + local core = require("apisix.core") + return core.response.exit(400, { + error = { + message = "Unsupported chat model. Use z-ai/glm5, moonshotai/kimi-k2.5, minimaxai/minimax-m2.5, or google/gemma4:31b.", + type = "invalid_request_error", + param = "model", + code = "model_not_found" + } + }) + end + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: embeddings-text-embedding-3-small + name: embeddings-text-embedding-3-small + priority: 100 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/embeddings + methods: + - POST + vars: + - - post_arg.model + - == + - text-embedding-3-small + plugins: + key-auth: + header: Authorization + hide_credentials: true + ai-proxy: + provider: openai-compatible + auth: + header: + Authorization: "Bearer ${{EMBEDDINGS_API_KEY}}" + options: + model: "${{EMBEDDINGS_MODEL:=text-embedding-3-small}}" + override: + endpoint: "${{EMBEDDINGS_ENDPOINT}}" + logging: + summaries: true + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: embeddings-unsupported-model + name: embeddings-unsupported-model + priority: 1 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/embeddings + methods: + - POST + plugins: + key-auth: + header: Authorization + hide_credentials: true + serverless-pre-function: + phase: access + functions: + - | + return function() + local core = require("apisix.core") + return core.response.exit(400, { + error = { + message = "Unsupported embedding model. Use text-embedding-3-small.", + type = "invalid_request_error", + param = "model", + code = "model_not_found" + } + }) + end + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 + + - id: models-catalog + name: models-catalog + priority: 100 + hosts: + - "${{API_PUBLIC_HOST:=api.svc.plus}}" + uri: /v1/models + methods: + - GET + plugins: + key-auth: + header: Authorization + hide_credentials: true + serverless-pre-function: + phase: access + functions: + - | + return function() + local core = require("apisix.core") + return core.response.exit(200, { + object = "list", + data = { + { + id = "z-ai/glm5", + object = "model", + owned_by = "svc-ai-gateway", + root = "z-ai/glm5" + }, + { + id = "moonshotai/kimi-k2.5", + object = "model", + owned_by = "svc-ai-gateway", + root = "moonshotai/kimi-k2.5" + }, + { + id = "minimaxai/minimax-m2.5", + object = "model", + owned_by = "svc-ai-gateway", + root = "minimaxai/minimax-m2.5" + }, + { + id = "google/gemma4:31b", + object = "model", + owned_by = "svc-ai-gateway", + root = "google/gemma4:31b" + }, + { + id = "text-embedding-3-small", + object = "model", + owned_by = "svc-ai-gateway", + root = "text-embedding-3-small" + } + } + }, { + ["Content-Type"] = "application/json" + }) + end + upstream: + type: roundrobin + nodes: + "127.0.0.1:1": 1 +#END diff --git a/vars/cloudflare_svc_plus_dns.yml b/vars/cloudflare_svc_plus_dns.yml index 3fa95e5..e2bb4fc 100644 --- a/vars/cloudflare_svc_plus_dns.yml +++ b/vars/cloudflare_svc_plus_dns.yml @@ -65,6 +65,16 @@ cloudflare_dns_records: content: vps-accounts.svc.plus ttl: 1 proxied: false + - type: A + name: vps-api.svc.plus + content: 46.250.251.132 + ttl: 1 + proxied: false + - type: CNAME + name: api.svc.plus + content: vps-api.svc.plus + ttl: 1 + proxied: false - type: CNAME name: preview-accounts.svc.plus content: vps-preview-accounts.svc.plus