Files
ModelRouter/README.md
root f7f76e097d feat: ModelRouter — unified OpenAI-compatible multi-source LLM gateway
- Lua adapters per upstream (transform_request/response/stream_chunk, build_headers signing hooks)
- AUTO priority routing with per-model kind (chat/image), explicit source/model routing
- Per-source concurrency caps with queueing, exponential backoff, AUTO failover
- OpenAI-compatible API: chat completions, SSE streaming, image generations, models
- Gateway key auth, web UI for adapter/source management, runtime persistence
- e2e test running the real binary against mocked upstreams
2026-08-05 15:25:47 +08:00

139 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# llmsproxy
统一 OpenAI 兼容网关:把多个上游 LLM 源DeepSeek、OpenAI、Anthropic、Gemini、
Groq、Mistral、Ollama、KimiCode…通过 **Lua 适配器** 做协议转换,对内网暴露
一个标准 `OpenAI Chat Completions` 接口(`/v1/chat/completions` + `/v1/models`
支持单次结算与 SSE 流式。
从 [HomeAgent](https://gitcode.com/JianFeeeee/HomeAgent) 的多源 LLM 适配层
`internal/agent/api/provider.go` + `internal/lua/adapters/*`)抽离而来并独立演进。
## 特性
- **多源**:一个进程内配置任意多个上游源,按请求的 `model` 自动路由。
- **统一输出**:所有源对外都是 OpenAI 格式(含 `reasoning_content``tool_calls``usage`)。
- **Lua 适配协议**:每个源挂一个 `.lua` 适配器,完成 `transform_request` /
`transform_response` / `transform_stream_chunk` 双向转换,协议差异全在 Lua 层。
- **签名 / 请求头钩子**:适配器可定义 `build_headers(meta)`,在 Go 发 HTTP 前
动态注入/签名请求头——用于云端 API 校验调用方 app如 KimiCode 只放行特定
agent。提供 `hmac_sha256_hex` / `sha256_hex` / `base64_encode` 等签名辅助。
- **鉴权**:网关自身用 `gateway_keys` 校验客户端 Bearer key与上游各自的 key 相互独立。
- **流式**SSE `chat.completion.chunk`,含角色首包与 `[DONE]` 收尾。
## 快速开始
```bash
cp config.example.yaml config.yaml # 编辑你的源与 key
GOMODCACHE=... GOPROXY=off go build -o llmsproxy ./cmd/llmsproxy
./llmsproxy -config config.yaml
```
```bash
# 无 key -> 401
curl http://127.0.0.1:8080/v1/models
# 单次
curl -H "Authorization: Bearer sk-gw-local-0001" \
-d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hi"}]}' \
http://127.0.0.1:8080/v1/chat/completions
# 流式
curl -N -H "Authorization: Bearer sk-gw-local-0001" \
-d '{"model":"deepseek-v4-flash","stream":true,"messages":[{"role":"user","content":"hi"}]}' \
http://127.0.0.1:8080/v1/chat/completions
```
任何 OpenAI SDK 把 `base_url` 指到网关地址、`api_key``gateway_keys` 之一即可。
## 配置
见 [`config.example.yaml`](config.example.yaml)。核心字段:
```yaml
listen: 127.0.0.1:8080 # 网关监听地址(建议绑内网/回环)
gateway_keys: [sk-gw-0001] # 客户端访问网关的 key留空=不鉴权
default_source: deepseek # model 无法路由时回落的源
adapter_dir: adapters # Lua 适配目录,首启自动写入内置适配器
sources:
- name: deepseek
base_url: https://api.deepseek.com
api_key: sk-...
model: deepseek-v4-flash
adapter: deepseek
# 静态请求头(比适配器默认优先)
headers: { X-Tenant: prod }
# 传给 Lua build_headers 的透传元数据
meta: { app_id: x, app_secret: y }
temperature: 0.7
max_tokens: 4096
timeout: 120s # 请求超时,默认 120s
```
### 模型路由
`/v1/chat/completions``model` 解析顺序:
1. `source/model``source:model` 前缀 → 指定源;
2. 精确匹配某个源的 `model`
3. 回落到 `default_source`
任何 OpenAI 客户端,只要 `model` 设为某个源的 `name/任意名`,即可锁定走该源。
## Lua 适配器协议
每个适配器是一个返回 table 的 Lua 脚本(`internal/lua/adapters/<name>.lua`
加载时可被 `adapter_dir` 下的同名脚本覆盖。
```lua
return {
name = "mysrc",
version = "1.0.0",
endpoint = "/chat/completions", -- 上游路径(可被 source.endpoint 覆盖)
headers = { ["X-Static"] = "v" }, -- 静态默认请求头build_headers 缺省时使用)
-- 请求转换:把 OpenAI 格式 req 转成上游原生格式,返回字符串
transform_request = function(raw_json) ... end,
-- 响应转换:把上游原生响应转成统一格式字符串
-- { content, reasoning_content, finish_reason, token_usage{...}, tool_calls[{...}] }
transform_response = function(raw_json) ... end,
-- 流式分块转换:把上游 SSE data 转成 { content, done, ... },返回 "" 则跳过
transform_stream_chunk = function(raw_chunk) ... end,
-- [可选] 动态请求头/签名钩子
-- meta = { url, method, body, api_key, timestamp, source={ name, meta={...} } }
build_headers = function(meta) return { ["X-App-Sign"] = sign } end,
}
```
内置信号辅助:`hmac_sha256_hex(key, data)``sha256_hex(data)``base64_encode(s)`
`tohex(s)``json.encode/decode``log(level, msg)`
### 内置适配器
`openai` `deepseek` `anthropic` `gemini` `github` `groq` `mistral` `ollama` `kimicode`
**kimicode** 是展示 `build_headers` 的样例:云端校验调用方 app需要按
`meta.app_secret` 对时间戳+URL+请求体哈希做 HMAC 签名并附 `X-App-Sign` 等头。
配好 `sources[].meta.{app_id, app_secret, app_agent}` 即可。
## 目录
```
cmd/llmsproxy # 入口
internal/config # YAML 配置加载/校验
internal/lua # Lua VM + AdapterCache + 内置适配器 (embed)
internal/provider # Provider(HTTP) + Registry(路由)
internal/gateway # OpenAI 兼容 HTTP 服务 + 鉴权 + SDK/流式
internal/types # 统一格式 & OpenAI wire 类型
```
## 测试
```bash
go test ./...
```
覆盖:配置校验、适配器加载/变换、签名钩子、Gateway 鉴权、SDK 结算、SSE 流式、模型路由。