From 042188f954ce5d210a90805211f54071be2df4b1 Mon Sep 17 00:00:00 2001 From: kris Date: Sun, 22 Mar 2026 18:58:55 +0800 Subject: [PATCH] feat: restore local model gateway via cliproxy --- .env.example | 4 ++ README.md | 8 ++++ docker-compose.yml | 8 ++++ scripts/render_cliproxy_config.sh | 73 +++++++++++++++++++++++++++++++ scripts/start_business.sh | 11 ++++- scripts/status_business.sh | 1 + 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100755 scripts/render_cliproxy_config.sh diff --git a/.env.example b/.env.example index e362ca4..02b25c7 100644 --- a/.env.example +++ b/.env.example @@ -33,3 +33,7 @@ WEBHOOK_URL=http://127.0.0.1:5670/ GENERIC_TIMEZONE=Asia/Shanghai TZ=Asia/Shanghai CLIPROXY_IMAGE=storyforge/cli-proxy-api:patched +CLIPROXY_MANAGEMENT_SECRET=storyforge-local-management +CLIPROXY_DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1 +# Optional but recommended for local model gateway recovery. +# DASHSCOPE_API_KEY= diff --git a/README.md b/README.md index ac509ba..914c83a 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,14 @@ cp .env.example .env docker compose up -d --build ``` +如果要让本机模型网关 `cli-proxy-api` 自动提供 `GLM-5`,建议在启动前确保本机环境里存在: + +```bash +export DASHSCOPE_API_KEY=your_dashscope_key +``` + +或者把它写进本地 `.env`。`./scripts/start_business.sh` 会自动生成 `data/cliproxyapi/config.yaml` 并把 `glm-5 -> GLM-5` 映射到本机网关。 + 如果 `collector` 跑在 Docker 里,建议保留: ```bash diff --git a/docker-compose.yml b/docker-compose.yml index 72cf8b3..83f0332 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,6 +65,14 @@ services: image: ${CLIPROXY_IMAGE:-storyforge/cli-proxy-api:patched} container_name: storyforge-cliproxyapi restart: unless-stopped + command: + - ./CLIProxyAPI + - -config + - /CLIProxyAPI/config.yaml + volumes: + - ./data/cliproxyapi/config.yaml:/CLIProxyAPI/config.yaml:ro + - ./data/cliproxyapi/auths:/root/.cli-proxy-api + - ./data/cliproxyapi/logs:/CLIProxyAPI/logs ports: - "8317:8317" - "8085:8085" diff --git a/scripts/render_cliproxy_config.sh b/scripts/render_cliproxy_config.sh new file mode 100755 index 0000000..fffc4ff --- /dev/null +++ b/scripts/render_cliproxy_config.sh @@ -0,0 +1,73 @@ +#!/bin/sh +set -eu + +ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)" + +if [ -f "$ROOT/.env" ]; then + set -a + # shellcheck disable=SC1091 + . "$ROOT/.env" + set +a +fi + +DATA_DIR="$ROOT/data/cliproxyapi" +CONFIG_PATH="$DATA_DIR/config.yaml" +mkdir -p "$DATA_DIR/auths" "$DATA_DIR/logs" + +: "${CLIPROXY_MANAGEMENT_SECRET:=storyforge-local-management}" +: "${CLIPROXY_DASHSCOPE_BASE_URL:=https://dashscope.aliyuncs.com/compatible-mode/v1}" + +python3 - <<'PY' "$CONFIG_PATH" "$CLIPROXY_MANAGEMENT_SECRET" "$CLIPROXY_DASHSCOPE_BASE_URL" +import os +import sys +from pathlib import Path + +config_path = Path(sys.argv[1]) +management_secret = sys.argv[2] +base_url = sys.argv[3] +dashscope_api_key = os.environ.get("DASHSCOPE_API_KEY", "").strip() + +lines = [ + 'host: ""', + 'port: 8317', + 'tls:', + ' enable: false', + ' cert: ""', + ' key: ""', + 'remote-management:', + ' allow-remote: false', + f' secret-key: "{management_secret}"', + ' disable-control-panel: false', + 'auth-dir: "/root/.cli-proxy-api"', + 'debug: false', + 'logging-to-file: true', + 'logs-max-total-size-mb: 200', + 'usage-statistics-enabled: true', + 'request-retry: 2', +] + +if dashscope_api_key: + lines.extend( + [ + 'openai-compatibility:', + ' - name: "dashscope"', + f' base-url: "{base_url}"', + ' api-key-entries:', + f' - api-key: "{dashscope_api_key}"', + ' models:', + ' - name: "glm-5"', + ' alias: "GLM-5"', + ' - name: "glm-5"', + ' alias: "glm-5"', + ' - name: "qwen3.5-plus"', + ' alias: "qwen3.5-plus"', + ] + ) + +config_path.write_text("\n".join(lines) + "\n", encoding="utf-8") + +if dashscope_api_key: + print(f"rendered cliproxy config with DashScope upstream -> {config_path}") +else: + print(f"rendered cliproxy config without upstream credentials -> {config_path}") +PY diff --git a/scripts/start_business.sh b/scripts/start_business.sh index 6aa53d7..4be524f 100755 --- a/scripts/start_business.sh +++ b/scripts/start_business.sh @@ -5,7 +5,14 @@ ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)" COMPOSE_FILE="$ROOT/docker-compose.yml" cd "$ROOT" -docker compose -f "$COMPOSE_FILE" up -d --build collector n8n +"$ROOT/scripts/render_cliproxy_config.sh" + +OWNER="$(docker inspect storyforge-cliproxyapi --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}' 2>/dev/null || true)" +if [ -n "$OWNER" ] && [ "$OWNER" != "$ROOT" ]; then + docker rm -f storyforge-cliproxyapi >/dev/null 2>&1 || true +fi + +docker compose -f "$COMPOSE_FILE" up -d --build collector n8n cli-proxy-api python3 - <<'PY' import time @@ -14,6 +21,7 @@ import urllib.request checks = [ ("collector", "http://127.0.0.1:8081/healthz"), ("n8n", "http://127.0.0.1:5670/healthz"), + ("cli-proxy-api", "http://127.0.0.1:8317/v1/models"), ] deadline = time.time() + 45 @@ -37,3 +45,4 @@ PY echo "business started" echo "collector: http://127.0.0.1:8081/healthz" echo "n8n: http://127.0.0.1:5670/healthz" +echo "cli-proxy-api: http://127.0.0.1:8317/v1/models" diff --git a/scripts/status_business.sh b/scripts/status_business.sh index 137a3e6..d388595 100755 --- a/scripts/status_business.sh +++ b/scripts/status_business.sh @@ -13,6 +13,7 @@ import urllib.request for name, url in [ ("collector", "http://127.0.0.1:8081/healthz"), ("n8n", "http://127.0.0.1:5670/healthz"), + ("cli-proxy-api", "http://127.0.0.1:8317/v1/models"), ]: try: with urllib.request.urlopen(url, timeout=5) as resp: