开发指南
环境要求
- Rust 1.85+(
rust-toolchain.toml未固定,跟workspace.package.rust-version对齐即可)。 - 测试用 Postgres + Redis 时,Docker 即可。
- 跑 mock 上游需要 Python 3.10+(
scripts/mock-openai.py)。
项目结构
crates/
├── gateway-core/ # 配置、proxy engine、provider 适配、cache、pricing、安全
├── gateway-storage/ # store trait + sqlite / postgres / redis / memory 实现
├── gateway-api/ # axum 路由、admin、auth、ratelimit、budget、reload
├── gateway-bin/ # 二进制入口
└── gateway-ui/ # 内嵌管理 UI(打包后的静态 index.html)
migrations/
├── sqlite/ # sqlx migrate 文件
└── postgres/
config/ # 示例 + 测试用 YAML
docs/ # mdBook 源(本目录)
scripts/mock-openai.py # 集成测试用的 mock 上游
pricing-catalog.json # 默认定价表(模型 → 单价,启动时通过 --pricing-catalog 指定路径)
编译与运行
# Debug 编译
cargo build
# Release 编译
cargo build --release --bin gateway
# 用纯内存后端起一个 dev 实例(不需要 DB)
cargo run --bin gateway -- --config config/test.memory.yaml
# Lite + 自带 mock 上游
python3 scripts/mock-openai.py & # 监听 :18181
cargo run --bin gateway -- --config config/test.mock.yaml
测试
# 单元 + 集成测试
cargo test --workspace
# 只跑某个 crate
cargo test -p gateway-core
# 关掉日志输出 noise
RUST_LOG=warn cargo test --workspace
集成测试中:
config/test.standard.yaml假定 httpbin.org 可达。config/test.cost.yaml/test.mock.yaml用本地mock-openai.py(端口 18181)。config/test.limits.yaml用 httpbin.org 的/anything跑通限流。
数据库迁移
# SQLite (Lite)
sqlx migrate run --source migrations/sqlite --database-url sqlite://./data/gateway.db
# Postgres (Standard)
sqlx migrate run --source migrations/postgres --database-url postgres://gateway:gatewaypass@127.0.0.1:54329/gateway
启动时 SqliteBackend::open / PostgresBackend::open 会自动跑一次 migrate,所以平时不用手动跑;手动跑的场景是新增 migration 后调试。
代码风格
cargo fmt --all提交前过一遍。cargo clippy --workspace --all-targets -- -D warnings当 CI 门槛。- 错误传播用
thiserror::Error自定义 +anyhow::Result在 binary 边界。 - HTTP handler 返回
Result<Response, ApiError>,ApiError知道如何映射到 HTTP code。 - 时间戳一律 unix ms / unix sec(看上下文),不用 chrono 类型穿过 trait。
加新的供应商
绝大多数情况下你不需要写代码 —— 如果新供应商的 API 兼容 OpenAI 协议(豆包、DeepSeek、Groq、Together、Mistral、Azure OpenAI、vLLM、Ollama、LM Studio 等都属于这类),直接在 YAML 里加一条 providers.<name>: { kind: openai, base_url: ..., credential: ... } 即可。具体写法见 配置参考 > providers。
只有当上游使用完全不同的认证协议时(既不是 OpenAI 也不是 Anthropic),才需要新建一个 auth adapter:
- 在
crates/gateway-core/src/providers/加一个<name>.rs,实现AuthInjectortrait(看openai.rs和anthropic.rs作模板)。adapter 主要做两件事:- 改写 outgoing request 的 auth header(替换客户端送来的 Authorization)。
- 注入该 provider 要求的固定 header(如 anthropic 的
anthropic-version)。
- 在
providers/mod.rs:pub mod <name>;- 在
build_auth_injector的 match 里加一个kind分支 - 在
is_known_provider_kind里加同一个kind—— 让启动期validate认识它
- 在
pricing-catalog.json加该供应商的模型 → 单价(如果你想跟踪成本)。 - 写测试(参考
crates/gateway-core/src/providers/openai.rs末尾的单元测试)。 - 配置文件里用
providers.<name>: { kind: <kind>, ... }引用。
至于 token usage 抽取(用于成本核算和 TPM 限流):目前在 crates/gateway-api/src/tokens.rs::extract_token_usage 集中处理,假设上游响应体里有标准的 usage 字段(OpenAI / Anthropic 都满足)。完全不同形态的响应需要在那里加一条解析分支。
加新的 Admin 端点
- 在
crates/gateway-api/src/routes/admin/加一个<resource>.rs,handler 签名:#![allow(unused)] fn main() { pub async fn handler( State(state): State<AppState>, principal: AdminPrincipal, /* extractors */ ) -> Result<Json<Response>, ApiError> } - 在
routes/admin/mod.rs暴露,然后在server.rs::build_router的adminRouter 里.route(...)。 - 给 store trait 加方法(若需要新数据);分别在 SQLite / Postgres / Memory 实现。
- 写集成测试(参考
crates/gateway-api/tests/模板)。 - 更新 Admin API 文档。
配置字段加 / 改
crates/gateway-core/src/config.rs加字段,记得加#[serde(default)]和默认值函数,保持向后兼容。- 在
AppConfig::validate里加校验(若需要)。 - 跑
cargo test -p gateway-core config验证序列化/反序列化。 - 更新 配置参考。
- 改动若涉及行为,加到 架构概览 中对应小节。
文档
文档源在 docs/src/,mdBook 项目根在 docs/。
cargo install mdbook
cd docs
mdbook serve --open # 本地预览,改文件自动刷新
mdbook build # 输出到 docs/book/
每次 PR 改了代码行为,记得同步改文档。GitHub Actions 会在 push 到 main 时自动构建并发布到 GitHub Pages,见 .github/workflows/docs.yml。