# api-convert **Repository Path**: hjzly/api-convert ## Basic Information - **Project Name**: api-convert - **Description**: api-convert 是一个基于 Java 25 和 Spring Boot 4 的 AI API 网关,提供 OpenAI Chat Completions、OpenAI Responses API、Anthropic Messages 等兼容入口,支持多供应商路由、协议转换、流式 SSE 转换、API Key 鉴权、额度计费、请求日志和可视化管理端。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2026-09-03 - **Last Updated**: 2026-09-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # api-convert `api-convert` 是一个 Java 25 + Spring Boot 4 AI API 网关,用于把 OpenAI / Anthropic 风格客户端请求转发到管理端配置的上游渠道和模型。它提供统一入口、协议适配、模型路由、网关密钥、额度控制、请求日志和管理面板。 [English README](README_EN.md) | [开发文档](docs/DEVELOPMENT.md) | [Development Guide](docs/DEVELOPMENT_EN.md) ## 核心特性 - 兼容端点:`/v1/chat/completions`、`/v1/responses`、`/v1/videos`、`/v1/images/generations`、`/v1/messages`、`/v1/models` - 上游类型:OpenAI 兼容、Anthropic、OpenAI Responses、GPT_AUTH、CLAUDE_AUTH、DeepSeek Chat、DeepSeek Anthropic、Gemini - 协议适配:Chat Completions、Anthropic Messages、Responses API、DeepSeek、Gemini 之间按端点和上游类型转换 - 路由策略:随机、轮询、加权、会话粘性、工具请求优先、失败避让、灰度发布(canary pool + 比例分流) - 网关密钥:SHA-256 鉴权、渠道/模型授权、余额、按 token 计费、滑动窗口额度限制、滑动窗口请求数限制 - 可靠性:上游同渠道自动重试、熔断器(滑动窗口失败率,**Redis 跨实例共享**)、客户端断开取消上游 SSE、Idempotency-Key 跨实例回放 - 管理端:渠道、模型、网关密钥、请求日志、Dashboard、SLO 看板、熔断事件、审计日志、系统路由配置、AUTH 渠道授权管理 - 数据层:SQLite 默认开发库,支持 MySQL,启动时自动安装和迁移 schema > 代码中保留了 `LOCAL` Provider 枚举作为未来扩展占位;当前版本没有对应 Provider Client。 > Redis 是 **可选** 依赖。开启时为多实例部署提供熔断器状态共享、Idempotency-Key 跨实例回放、滑动窗口限流跨实例生效;不开启时这些能力自动降级为单实例语义,业务不受影响。详见 [Redis 部署与配置](#redis-部署与配置) 一节。 ## 技术栈 | 层 | 技术 | |---|---| | 后端 | Java 25、Spring Boot 4.0.6、Maven、MyBatis-Plus、Log4j2 | | 数据库 | SQLite / MySQL | | 跨实例共享存储 | Redis 6+(Lettuce 客户端,可选,自动降级) | | 前端 | Vue 3.5、Vite、TypeScript、Naive UI | | 管理端鉴权 | Sa-Token | ## 快速启动 前置要求: - Git - Node.js 与 npm - 仓库内 Maven Wrapper:`mvnw.cmd` / `mvnw`,由启动脚本内部调用 ### 下载 JDK 并启动 JDK 建议解压到仓库同级目录,不要放进 `api-convert` git 工作树。Maven 相关操作、后端启动和前端启动都由启动脚本内部封装。 Windows PowerShell: ```powershell mkdir api-convert-work cd api-convert-work git clone https://gitee.com/skwyl/api-convert.git Invoke-WebRequest ` -Uri "https://api.adoptium.net/v3/binary/latest/25/ga/windows/x64/jdk/hotspot/normal/eclipse" ` -OutFile "jdk-25.zip" mkdir jdk-25 tar -xf jdk-25.zip -C jdk-25 --strip-components=1 cd api-convert .\scripts\start.ps1 -JavaHome "..\jdk-25" ` -AdminUsername admin ` -AdminPassword "change-me" ``` Linux x64: ```bash mkdir -p api-convert-work cd api-convert-work git clone https://gitee.com/skwyl/api-convert.git curl -L "https://api.adoptium.net/v3/binary/latest/25/ga/linux/x64/jdk/hotspot/normal/eclipse" \ -o jdk-25.tar.gz mkdir -p jdk-25 tar -xzf jdk-25.tar.gz -C jdk-25 --strip-components=1 cd api-convert ./scripts/start.sh --java-home ../jdk-25 \ --admin-username admin \ --admin-password 'change-me' ``` macOS Apple Silicon: ```bash mkdir -p api-convert-work cd api-convert-work git clone https://gitee.com/skwyl/api-convert.git curl -L "https://api.adoptium.net/v3/binary/latest/25/ga/mac/aarch64/jdk/hotspot/normal/eclipse" \ -o jdk-25.tar.gz mkdir -p jdk-25 tar -xzf jdk-25.tar.gz -C jdk-25 --strip-components=3 cd api-convert ./scripts/start.sh --java-home ../jdk-25 \ --admin-username admin \ --admin-password 'change-me' ``` macOS Intel 把上面 URL 中的 `aarch64` 改成 `x64`。 国内镜像目录: | 系统 | 清华 TUNA 镜像目录 | |---|---| | Windows x64 | `https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jdk/x64/windows/` | | Linux x64 | `https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jdk/x64/linux/` | | macOS Intel | `https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jdk/x64/mac/` | | macOS Apple Silicon | `https://mirrors.tuna.tsinghua.edu.cn/Adoptium/25/jdk/aarch64/mac/` | 启动后: - 后端与内置管理端入口:`http://localhost:8080` - 前端开发服务:`http://localhost:5173` - 默认管理账号:`admin / admin123` 更多启动参数示例: ```powershell .\scripts\start.ps1 -JavaHome '..\jdk-25' ` -AdminUsername admin ` -AdminPassword 'change-me' ` -BackendPort 8080 ` -DbType mysql ` -DatasourceUrl 'jdbc:mysql://127.0.0.1:3306/api_convert?useSSL=false&serverTimezone=Asia/Shanghai' ` -DatasourceUsername root ` -DatasourcePassword 'mysql-password' ` -RedisHost 127.0.0.1 ` -RedisPort 6379 ` -RedisPassword 'change-me' ``` ```bash ./scripts/start.sh --java-home ../jdk-25 \ --admin-username admin \ --admin-password 'change-me' \ --backend-port 8080 \ --db-type mysql \ --datasource-url 'jdbc:mysql://127.0.0.1:3306/api_convert?useSSL=false&serverTimezone=Asia/Shanghai' \ --datasource-username root \ --datasource-password 'mysql-password' \ --redis-host 127.0.0.1 \ --redis-port 6379 \ --redis-password 'change-me' ``` 生产环境不要使用默认管理端密码。Redis 密码默认是 `123456`,**生产环境必须通过 `SPRING_DATA_REDIS_PASSWORD` env 覆盖**。更多环境变量见 [开发文档](docs/DEVELOPMENT.md#配置项)。 ## 常用接口 健康检查: ```bash curl http://localhost:8080/health ``` 模型列表: ```bash curl -H "Authorization: Bearer " \ http://localhost:8080/v1/models ``` OpenAI Chat Completions: ```bash curl -X POST http://localhost:8080/v1/chat/completions \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"example-chat","messages":[{"role":"user","content":"hello"}],"stream":false}' ``` Anthropic Messages: ```bash curl -X POST http://localhost:8080/v1/messages \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "anthropic-version: 2023-06-01" \ -d '{"model":"example-chat","messages":[{"role":"user","content":"hello"}],"stream":false}' ``` OpenAI Responses API: ```bash curl -X POST http://localhost:8080/v1/responses \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"example-chat","input":"hello","stream":false}' ``` OpenAI Videos API: ```bash curl -X POST http://localhost:8080/v1/videos \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"example-video","prompt":"A cinematic city skyline at sunset","seconds":4,"size":"1280x720"}' ``` OpenAI Images API: ```bash curl -X POST http://localhost:8080/v1/images/generations \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"model":"example-image","prompt":"A clean product render on a white background","size":"1024x1024","n":1}' ``` ## 管理端使用流程 1. 登录管理端。 2. 在“渠道管理”新增上游渠道并配置模型。 3. 在“模型管理”调整对外模型能力、启用状态和额度单价。 4. 在“网关密钥”创建调用方密钥,并按需配置余额、限制项、渠道授权和模型授权。 5. 在“系统配置”选择路由策略、失败避让和会话粘性参数。 6. 通过 Dashboard 和请求日志查看用量、错误和上游命中情况。 ## 文档 | 文档 | 说明 | |---|---| | [开发文档](docs/DEVELOPMENT.md) | 架构、目录、端点、Provider、适配器、数据库迁移、前端规范 | | [Development Guide](docs/DEVELOPMENT_EN.md) | English version of the development guide | | [英文 README](README_EN.md) | English project overview | ## Docker 构建本地镜像: ```bash docker build -t api-convert:local . ``` 运行 SQLite(单实例,无需 Redis): ```bash docker run --rm -p 8080:8080 \ -v api-convert-data:/app/data \ -e JAVA_OPTS='-XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders' \ -e LOG_PATH=/app/data/logs \ -e API_CONVERT_ADMIN_USERNAME=admin \ -e API_CONVERT_ADMIN_PASSWORD='change-me' \ api-convert:local ``` 多实例部署(连同 Redis + MySQL): ```bash docker network create api-convert-net # Redis 共享存储(熔断器状态 / Idempotency-Key / 滑动窗口限流) docker run -d --name api-convert-redis --network api-convert-net \ -p 6379:6379 \ redis:7-alpine \ redis-server --requirepass 'change-me' --appendonly yes # MySQL 业务库 docker run -d --name api-convert-mysql --network api-convert-net \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD='change-me' \ -e MYSQL_DATABASE=api_convert \ -v api-convert-mysql:/var/lib/mysql \ mysql:8.0 # 应用容器 docker run --rm -p 8080:8080 \ --network api-convert-net \ -v api-convert-data:/app/data \ -e JAVA_OPTS='-XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders' \ -e LOG_PATH=/app/data/logs \ -e API_CONVERT_ADMIN_USERNAME=admin \ -e API_CONVERT_ADMIN_PASSWORD='change-me' \ -e API_CONVERT_DB_TYPE=mysql \ -e SPRING_DATASOURCE_URL='jdbc:mysql://api-convert-mysql:3306/api_convert?useSSL=false&serverTimezone=Asia/Shanghai' \ -e SPRING_DATASOURCE_USERNAME=root \ -e SPRING_DATASOURCE_PASSWORD='change-me' \ -e SPRING_DATA_REDIS_HOST=api-convert-redis \ -e SPRING_DATA_REDIS_PORT=6379 \ -e SPRING_DATA_REDIS_PASSWORD='change-me' \ api-convert:local ``` 发布镜像示例: ```bash docker pull crpi-vqmjtaxg5bb83uba.cn-guangzhou.personal.cr.aliyuncs.com/aping/api-convert:v1.0.5 ``` ## Nginx 反向代理配置 通过 Nginx 暴露服务时,建议按下面的 `location` 配置透传 `Authorization`、禁用代理缓存、放宽大请求体限制,并关闭 SSE 缓冲以保证流式响应即时回传。将 `http://your_host:port` 替换为实际后端地址,例如 `http://127.0.0.1:8080`;如果上游启用了 HTTPS,则改为 `https://your_host:port`。 ```nginx location ^~ / { proxy_pass http://your_host:port; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; proxy_pass_request_headers on; proxy_set_header Authorization $http_authorization; proxy_http_version 1.1; add_header X-Cache $upstream_cache_status; proxy_ssl_server_name off; proxy_ssl_name $proxy_host; # 禁用缓存,避免 304 或缓存命中影响 API 响应。 proxy_no_cache 1; proxy_cache_bypass 1; add_header Cache-Control "no-cache, no-store, must-revalidate" always; expires off; # base64 大 payload 解码和上游处理可能需要较长时间。 proxy_read_timeout 300s; proxy_send_timeout 300s; proxy_connect_timeout 60s; # 支持图片、视频等 base64 大请求体。 client_max_body_size 50m; # 流式 SSE:关闭缓冲,让上游事件即时回传。 proxy_buffering off; proxy_request_buffering off; proxy_cache off; proxy_set_header Connection ''; chunked_transfer_encoding on; } ``` ## Redis 部署与配置 Redis 是 **可选** 依赖。开启时支撑三类跨实例能力: | 能力 | 关闭时的行为 | 开启后的差异 | |---|---|---| | 熔断器状态(CLOSE/OPEN/HALF_OPEN) | 仅本实例 | 多实例共享,任一实例 OPEN 后其他实例立即感知 | | Idempotency-Key 跨实例回放 | 仅本实例缓存命中 | 任意实例处理过的请求,其他实例也能 0 成本回放 | | 滑动窗口限流(QUOTA / REQUEST) | 每实例独立计数 | 全局 N 倍超额 → 严格按配额 | **Redis 不可达时**所有能力自动降级到进程内存,5s 退避期内不再尝试,业务不报错——生产建议始终部署 Redis,本地开发可省。 ### 1. 安装 Redis | 平台 | 方式 | |---|---| | Linux | `apt install redis-server` / `yum install redis` / 官方包 | | macOS | `brew install redis` | | Windows | WSL2 + Ubuntu,或用 Docker: `docker run -d -p 6379:6379 redis:7-alpine` | | 容器 | `docker run -d --name redis -p 6379:6379 redis:7-alpine --requirepass yourStrongPass` | ### 2. 启用密码 ```bash # 临时 redis-cli CONFIG SET requirepass 'yourStrongPass' # 永久: 编辑 /etc/redis/redis.conf requirepass yourStrongPass appendonly yes # 启用 AOF 持久化(熔断器状态不能丢) appendfsync everysec # 1s 同步一次,平衡性能与安全 ``` ### 3. 配置项 所有配置都通过 Spring Boot 标准 `spring.data.redis.*` 路径生效,可走 `application.yaml` / env / 启动脚本任一形式。下表统一用 **环境变量名**展示,对应 YAML 键为 `spring.data.redis.<环境变量去掉 `SPRING_DATA_REDIS_` 前缀并转小写>`。 | 环境变量 | 默认 | 说明 | |---|---|---| | `SPRING_DATA_REDIS_HOST` | `localhost` | Redis 地址 | | `SPRING_DATA_REDIS_PORT` | `6379` | Redis 端口 | | `SPRING_DATA_REDIS_DATABASE` | `0` | Redis 数据库索引(0-15)。多服务共享同一 Redis 集群时按数字隔离 keyspace | | `SPRING_DATA_REDIS_PASSWORD` | `123456` | **生产环境必须通过 env 覆盖**;本地未启用 AUTH 时显式传空 (`SPRING_DATA_REDIS_PASSWORD=`) 即可禁用 | | `SPRING_DATA_REDIS_TIMEOUT` | `2s` | 操作超时(读/写) | | `SPRING_DATA_REDIS_CONNECT_TIMEOUT` | `1s` | 建连超时 | | `SPRING_DATA_REDIS_POOL_ENABLED` | `true` | Lettuce 连接池开关。V1.0.6.3 起默认开启,Sa-Token / SharedStateClient 共享 Redis 时复用连接避免握手开销 | | `SPRING_DATA_REDIS_POOL_MAX_ACTIVE` | `32` | 最大活跃连接;超过后新请求按 `max-wait` 排队 | | `SPRING_DATA_REDIS_POOL_MAX_IDLE` | `16` | 空闲连接池上限 | | `SPRING_DATA_REDIS_POOL_MIN_IDLE` | `4` | 预热空闲连接数,热路径直接拿到连接无握手延迟 | | `SPRING_DATA_REDIS_POOL_MAX_WAIT` | `1s` | 连接池耗尽时上游最长等待时间;超时即抛 `RedisConnectionFailureException` | ### 4. 通过启动脚本配置 ```powershell .\scripts\start.ps1 -JavaHome '..\jdk-25' ` -AdminUsername admin -AdminPassword 'change-me' ` -RedisHost 127.0.0.1 -RedisPort 6379 -RedisPassword 'change-me' ``` ```bash ./scripts/start.sh --java-home ../jdk-25 \ --admin-username admin --admin-password 'change-me' \ --redis-host 127.0.0.1 --redis-port 6379 --redis-password 'change-me' ``` ### 5. 通过环境变量 ```bash export SPRING_DATA_REDIS_HOST=127.0.0.1 export SPRING_DATA_REDIS_PORT=6379 export SPRING_DATA_REDIS_DATABASE=0 export SPRING_DATA_REDIS_PASSWORD='change-me' ``` ### 6. 验证连接 启动日志看到 `Tomcat started on port 8080` + 无 `RedisConnectionFailureException` 即表示已连通。 若看到 WARN `Redis 共享状态不可用,5000ms 内降级到进程内存`,说明连不上,先按以下顺序排查: 1. `redis-cli -h 127.0.0.1 -p 6379 -a 'change-me' PING` → 应返回 `PONG` 2. 防火墙/安全组是否放行 6379 3. `bind 127.0.0.1` 限制是否过严(部署在远程需改为 `0.0.0.0` 或加 `protected-mode no` + 强密码) 4. 密码是否含特殊字符(URL 编码后再传 env) ### 7. 数据保留与清理 - 熔断器状态:`cb:{providerCode}:{providerModel}` Hash,TTL = `max(60, openDuration*4)` 秒 - Idempotency-Key L1:`idem:{apiKeyId}:{idempotencyKey}:{requestHash}`,TTL 60 秒 - 滑动窗口限流:`rl:{type}:{apiKeyId}:{windowValue}{unit}` ZSET,TTL = `windowMs * 1.5` - Sa-Token 管理端会话:`satoken:login:token:{tokenValue}` / `satoken:login:session:{loginId}`,TTL 跟随 `gateway_system_config.admin.token_timeout_seconds` 业务不依赖 Redis 长期保留共享状态;Sa-Token 会话丢失时管理员需重新登录。Redis 单独故障或重启时,共享状态降级业务**5s 内自动恢复到单实例语义**,但管理员登录态会丢失。 ## 构建与测试 本地启动和后端构建由启动脚本封装,直接通过脚本指定 JDK 路径: ```powershell .\scripts\start.ps1 -JavaHome "..\jdk-25" ``` ```bash ./scripts/start.sh --java-home ../jdk-25 ``` ## 开源协议 MIT License。 ## 界面预览 以下截图基于 1.0.6.1 版本,仅作演示展示。 ### 控制台 Dashboard 实时统计请求量、Token 消耗与成功率,按模型 / 渠道 / 密钥三个维度下钻;下方接口清单提供 Base URL 和端点元信息。 ![控制台 Dashboard](img/Snipaste_2026-06-17_20-38-01.png) ### 渠道管理 Channel Management 按供应商类型展示已配置渠道,端点能力以 Tag 形式高亮;行内“刷新”按钮可拉取上游真实额度(支持时)。 ![渠道管理](img/Snipaste_2026-06-17_20-38-40.png) ### 模型管理 Model Management 按对外模型名聚合渠道映射,统一管理每百万 token 的输入 / 输出 / 缓存读取额度单价,以及视觉 / 工具 / JSON 模式等能力开关。 ![模型管理](img/Snipaste_2026-06-17_20-38-55.png) ### 网关密钥 API Key Management 管理客户端调用网关时使用的 API Key,配置额度、限额(按窗口单位)和授权的渠道 / 模型范围;启用失败切换可在多渠道间自动重试。 ![网关密钥](img/Snipaste_2026-06-17_20-39-07.png) ### 系统配置 System Config 3 个 Tab 分组:路由策略(随机 / 轮询 / 加权 / 会话粘性)、失败避让(连续失败阈值 + 冷却分钟数)、会话粘性(保留分钟数)。每个字段有 tooltip 说明与输入校验。 ![系统配置](img/Snipaste_2026-06-17_20-39-37.png) ### 请求日志 Request Log 按请求编号 / 密钥 / 协议 / 接口类型 / 模型等维度检索历史请求;列表展示耗时、Token、状态码、错误码等关键信息。 ![请求日志](img/Snipaste_2026-06-17_20-40-17.png)