Skip to content

AnlangA/glm-proxy

Repository files navigation

ai-proxy

单二进制 GLM API 透明转发代理。将 OpenAI / Anthropic 协议的请求按路径前缀路由到 Z.AI(智谱)GLM 上游,支持流式响应、客户端鉴权与模型校验。

特性

  • 多协议端点:通过 URL 路径前缀区分协议,同一代理同时服务 OpenAI 与 Anthropic 格式请求
  • 透明转发:原样转发请求体与响应体,完整支持 SSE 流式输出
  • 客户端鉴权:静态 API key 白名单,恒定时间比对防止时序攻击
  • 模型校验:精确匹配优先,支持前缀通配符(如 glm-*glm-z1-*
  • 结构化日志:支持 JSON 与人类可读两种格式,通过 RUST_LOG 控制级别
  • 优雅关闭:捕获 SIGTERM 与 Ctrl-C,确保连接正常结束
  • 零配置启动:开箱即用的示例配置,三个环境变量即可运行

架构概览

客户端请求
    │
    ▼
┌─────────────────────────────────────────────────┐
│  transport(axum)                                │
│  ├─ 提取路径前缀(openai / anthropic)             │
│  ├─ 提取 API key(Authorization / x-api-key)     │
│  ├─ 解析请求体中的 model 字段                      │
│  └─ 过滤逐跳头(hop-by-hop headers)               │
├─────────────────────────────────────────────────┤
│  application(ProxyService)                       │
│  └─ 鉴权 → 模型校验 + 端点选择 → 密钥映射         │
├─────────────────────────────────────────────────┤
│  security + routing                               │
│  ├─ ConfigKeyValidator:恒定时间 key 比对         │
│  └─ GlmRoutingEngine:精确匹配 + 通配符路由       │
└─────────────────────────────────────────────────┘
    │
    ▼
GLM 上游(按前缀选择 base_url)

URL 路由规则

客户端请求 URL 的第一段路径为协议前缀,代理剥离后转发到对应上游:

前缀 上游端点 示例
openai https://open.bigmodel.cn/api/coding/paas/v4 /openai/v1/chat/completions.../paas/v4/v1/chat/completions
anthropic https://api.z.ai/api/anthropic /anthropic/v1/messages.../anthropic/v1/messages

端点在配置文件 [[glm.endpoints]] 中定义,可自由扩展。

快速开始

1. 克隆并构建

git clone <repo-url> && cd ai-proxy
cargo build --release

2. 配置

复制示例配置并按需修改(文件名以 local. 开头会被 .gitignore 自动忽略):

cp configs/proxy.toml configs/local.proxy.toml

配置文件结构:

# configs/local.proxy.toml
[client_keys]
keys = [
    { key = "sk-your-client-key", client_id = "alice" },
]

[glm]
api_key_env = "GLM_API_KEY"          # GLM API key 对应的环境变量名
model_patterns = ["glm-*"]           # 允许的模型名规则

[[glm.endpoints]]
prefix = "openai"                    # URL 路径前缀
base_url = "https://open.bigmodel.cn/api/coding/paas/v4"

[[glm.endpoints]]
prefix = "anthropic"
base_url = "https://api.z.ai/api/anthropic"

[server]
body_limit_bytes = 33554432          # 请求体大小限制(默认 32 MB)

3. 设置环境变量

export GLM_API_KEY=<your_glm_api_key>

4. 启动

./target/release/ai-proxy

默认监听 0.0.0.0:8080,可通过环境变量覆盖:

AI_PROXY_ADDR=127.0.0.1:9000 ./target/release/ai-proxy

5. 验证

# 健康检查
curl http://localhost:8080/health
# → ok

# OpenAI 格式请求
curl http://localhost:8080/openai/v1/chat/completions \
  -H "Authorization: Bearer sk-your-client-key" \
  -H "Content-Type: application/json" \
  -d '{"model":"glm-4-flash","messages":[{"role":"user","content":"你好"}]}'

# Anthropic 格式请求
curl http://localhost:8080/anthropic/v1/messages \
  -H "Authorization: Bearer sk-your-client-key" \
  -H "Content-Type: application/json" \
  -H "anthropic-version: 2023-06-01" \
  -d '{"model":"glm-4-flash","max_tokens":256,"messages":[{"role":"user","content":"你好"}]}'

配置参考

完整配置项

字段 类型 必填 默认值 说明
client_keys.keys 数组 客户端 API key 白名单
client_keys.keys[].key 字符串 客户端 API key
client_keys.keys[].client_id 字符串 日志追踪用的客户端标识
glm.endpoints 数组 协议端点列表
glm.endpoints[].prefix 字符串 URL 路径前缀(不含斜杠)
glm.endpoints[].base_url 字符串 对应上游 base URL
glm.api_key_env 字符串 持有 GLM API key 的环境变量名
glm.model_patterns 数组 模型匹配规则
glm.timeout_secs 整数 300 上游请求总超时(秒)
glm.connect_timeout_secs 整数 10 上游连接超时(秒)
server.body_limit_bytes 整数 33554432 最大请求体大小(字节)
server.cors_allowed_origins 数组 CORS 允许的来源列表(未设置时允许所有来源)

模型匹配规则

model_patterns 支持两种格式,按声明顺序评估:

  • 精确匹配glm-4-flash — 仅匹配该模型名
  • 前缀通配符glm-* — 匹配所有以 glm- 开头的模型名

精确匹配优先于通配符。建议将高频精确名放在前面,通配符放在末尾兜底:

model_patterns = ["glm-4-flash", "glm-z1-air", "glm-z1-*", "glm-*"]

环境变量

变量 默认值 说明
GLM_API_KEY —(必须 GLM 上游 API key
AI_PROXY_CONFIG configs/local.proxy.toml(如存在),否则 configs/proxy.toml 配置文件路径,设置后忽略自动查找
AI_PROXY_ADDR 0.0.0.0:8080 监听地址与端口
RUST_LOG info 日志级别过滤(如 info,ai_proxy=debug
LOG_FORMAT 人类可读 设为 json 启用结构化 JSON 日志

模块结构

src/
├── main.rs           入口:加载配置、组装应用、启动服务器、优雅关闭
├── lib.rs            库入口:导出所有公共模块
├── domain.rs         强类型 ID(ModelId、ClientKeyId、RequestId)与请求上下文
├── config.rs         TOML 配置解析与一致性验证
├── security.rs       客户端 key 校验(KeyValidator trait + ConfigKeyValidator)
├── routing.rs        模型路由(RoutingEngine trait + GlmRoutingEngine)
├── error.rs          统一错误类型与 HTTP 状态码映射
├── observability.rs  链路追踪初始化(tracing-subscriber)
├── application.rs    代理业务编排(ProxyService)
└── transport.rs      HTTP 入口层(axum Router、proxy_handler、逐跳头过滤)

模块依赖关系

domain        (零外部依赖,仅 uuid 例外)
  ↓
config        (无外部依赖)
  ↓
error         (依赖 domain)
security      (依赖 config、domain)
routing       (依赖 config、domain)
  ↓
application   (依赖 security、routing、error、domain)
  ↓
transport     (依赖 application、domain、error、routing、security)
  ↓
main          (依赖全部,组装并启动)

规则:domain 零外部依赖;禁止下层模块反向依赖上层。

请求流程

  1. 客户端发送请求到 http://<addr>/<prefix>/<path>
  2. transport 层提取路径前缀、API key、请求体中的 model 字段
  3. application 层调用 security 校验客户端 key
  4. routing 层校验模型名是否匹配 model_patterns,并按前缀选择上游端点
  5. security 层提供 GLM 上游 API key
  6. transport 层过滤逐跳头、注入上游鉴权头、转发请求
  7. 上游响应(含 SSE 流)原样回传客户端
  8. 记录请求日志(request_id、client_id、model、状态码、耗时)

开发

前置要求

  • Rust 2024 edition(rustup 最新 stable)
  • 环境变量 GLM_API_KEY(集成测试需要,单元测试不需要)

常用命令

# 运行所有测试
cargo test

# 仅单元测试
cargo test --lib

# 仅集成测试
cargo test --test integration

# Clippy 检查(.cargo/config.toml 已开启 -D warnings)
cargo clippy

# 开发模式运行(自动加载 configs/local.proxy.toml,不存在则加载 configs/proxy.toml)
cargo run

# 发布构建(thin LTO + strip symbols)
cargo build --release

# 运行端到端演示(需要真实 GLM API key)
GLM_API_KEY=<key> cargo run --example proxy_demo

测试覆盖

  • 单元测试(32 个):各模块 #[cfg(test)] 内联测试,覆盖配置解析、key 校验、模型路由、错误映射
  • 集成测试(11 个):tests/integration.rs 使用 wiremock 模拟上游,覆盖健康检查、鉴权失败、模型不存在、请求转发、响应透传、上游错误透传等完整链路

代码风格

  • 模块顶部 //! 和公开 API /// 注释使用中文
  • 库边界错误使用 thiserror enum;业务逻辑使用 anyhow::Result
  • 日志使用 tracing 宏,禁止 println!
  • 不在日志或错误信息中暴露明文 API key
  • 领域类型使用 newtype 模式(Deref<Target=str> + Display + Hash

安全

  • GLM API key 通过环境变量注入,禁止写入配置文件或代码
  • 客户端 key 校验使用恒定时间字节比较,防止时序攻击
  • 本地配置文件(configs/local.*)、生产配置文件(configs/production.*)已加入 .gitignore
  • 请求头中的 Authorizationx-api-key 不会转发到上游,由代理统一注入上游鉴权头

部署

Docker(推荐)

FROM rust:1.85 AS builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/ai-proxy /usr/local/bin/
COPY configs/proxy.toml /etc/ai-proxy/proxy.toml
EXPOSE 8080
CMD ["ai-proxy"]
docker build -t ai-proxy .
docker run -e GLM_API_KEY=<key> -p 8080:8080 ai-proxy

直接部署

GLM_API_KEY=<key> AI_PROXY_CONFIG=/etc/ai-proxy/proxy.toml AI_PROXY_ADDR=0.0.0.0:8080 ./ai-proxy

配合 systemd 实现进程管理与自动重启。

License

MIT

About

GLM API 透明转发代理 — Rust + axum 0.8

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages