feat(playbooks): add managed APISIX service deploy
This commit is contained in:
parent
557272bf88
commit
6c728d4911
7
deploy_apisix_svc.plus.yaml
Normal file
7
deploy_apisix_svc.plus.yaml
Normal file
@ -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
|
||||||
@ -7,6 +7,9 @@ jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=roo
|
|||||||
[accounts]
|
[accounts]
|
||||||
acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root
|
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]
|
[postgresql]
|
||||||
acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root
|
acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root
|
||||||
|
|
||||||
|
|||||||
27
roles/vhosts/apisix_service/defaults/main.yml
Normal file
27
roles/vhosts/apisix_service/defaults/main.yml
Normal file
@ -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
|
||||||
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
roles/vhosts/apisix_service/files/conf/config.yaml
Normal file
16
roles/vhosts/apisix_service/files/conf/config.yaml
Normal file
@ -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"}'
|
||||||
36
roles/vhosts/apisix_service/files/scripts/healthcheck.sh
Normal file
36
roles/vhosts/apisix_service/files/scripts/healthcheck.sh
Normal file
@ -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'
|
||||||
16
roles/vhosts/apisix_service/files/scripts/reload.sh
Normal file
16
roles/vhosts/apisix_service/files/scripts/reload.sh
Normal file
@ -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'
|
||||||
24
roles/vhosts/apisix_service/files/scripts/validate.sh
Normal file
24
roles/vhosts/apisix_service/files/scripts/validate.sh
Normal file
@ -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'
|
||||||
9
roles/vhosts/apisix_service/handlers/main.yml
Normal file
9
roles/vhosts/apisix_service/handlers/main.yml
Normal file
@ -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
|
||||||
206
roles/vhosts/apisix_service/tasks/main.yml
Normal file
206
roles/vhosts/apisix_service/tasks/main.yml
Normal file
@ -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
|
||||||
312
roles/vhosts/apisix_service/templates/conf/apisix.yaml.j2
Normal file
312
roles/vhosts/apisix_service/templates/conf/apisix.yaml.j2
Normal file
@ -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
|
||||||
@ -65,6 +65,16 @@ cloudflare_dns_records:
|
|||||||
content: vps-accounts.svc.plus
|
content: vps-accounts.svc.plus
|
||||||
ttl: 1
|
ttl: 1
|
||||||
proxied: false
|
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
|
- type: CNAME
|
||||||
name: preview-accounts.svc.plus
|
name: preview-accounts.svc.plus
|
||||||
content: vps-preview-accounts.svc.plus
|
content: vps-preview-accounts.svc.plus
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user