Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions OPEN_SOURCE_COMPARISON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 同类开源项目横向对照与候选功能清单(COD-7)

调研日期:2026-11-12。仅调研与方案,不改生产代码、不加依赖。
参照项目版本取各仓库 `releases/latest` 与最近 push 时间。

## 1. 参照项目

| 项目 | 定位 | 版本 / 最近更新 | 来源 |
|---|---|---|---|
| LiteLLM | 100+ 供应商统一网关 + Python SDK,虚拟 Key、花费追踪、护栏、负载均衡、管理台 | v1.109.0(2026-11-11),push 2026-11-11 | https://github.com/BerriAI/litellm |
| One API | 多供应商统一 API 与 Key 二次分发,令牌额度/过期、渠道分组、兑换码、失败重试、单二进制 | v0.6.10(2025-02-02),push 2026-01-09 | https://github.com/songquanpeng/one-api |
| New API | 自托管 AI 网关,OpenAI/Claude/Gemini 三协议、配额与订阅、缓存计费、表达式定价、Redis 多节点、7 语言管理台 | v1.0.0-rc.49(2026-11-08),push 2026-11-11 | https://github.com/Calcium-Ion/new-api |
| Portkey Gateway | 高性能网关,重试/回退、负载均衡、条件路由、护栏、MCP Gateway | v1.15.2(2026-01-12),push 2026-05-25 | https://github.com/portkey-ai/gateway |
| Claude Code Router | 面向编码 Agent 的本地控制面,多 Agent 统一入口、供应商/模型路由、凭据池轮换、有序回退、工具增强 | v3.1.3(2026-10-09),push 2026-10-10 | https://github.com/musistudio/claude-code-router |

## 2. 逐项对照(现状 → 差距)

Coding2API 已有:OpenAI Chat/Responses + Anthropic Messages、十路逆向渠道、公共凭证池与健康调度、凭证权重、跨渠道 fallback 组、Key 级日配额(请求/token/成本 + RPM/TPM/并发)、到期额度优先、三角色与审计、脱敏统计/账单导出、出口 IP 粘性、上游状态与告警、热更配置、Playground、备份恢复。

| 能力 | 参照实现 | Coding2API 现状 | 差距 |
|---|---|---|---|
| 多协议入口 | New API 覆盖 OpenAI/Claude/Gemini;CCR 覆盖 Gemini Generate Content | 仅 Chat、Responses、Messages(`src/api/`) | 缺 Gemini 原生入口与 `count_tokens` 之外的生态端点 |
| Key 治理 | One API 令牌额度+过期+IP/模型白名单;LiteLLM 虚拟 Key 预算周期 | 日配额 + 瞬时限速;无月度/总量预算、无 Key 到期 | 预算周期与生命周期不完整 |
| 可靠性路由 | Portkey 条件路由/负载均衡;LiteLLM Router;CCR 有序回退 | 健康度+权重+到期额度+fallback 组 | 缺按成本/延迟/时段的条件策略 |
| 成本 | LiteLLM 花费追踪/预算;New API 表达式定价 | OpenRouter 刊例静态价 + 实测扣费校准 | 价格表需手工更新,缺自动同步 |
| 缓存 | LiteLLM/Portkey 网关缓存;New API 缓存计费 | 仅透传上游 `prompt_cache_key`/`cache_control` | 无网关侧响应缓存 |
| 安全护栏 | LiteLLM/Portkey 请求/响应护栏 | 登录限流、CSRF、Host 白名单、日志脱敏 | 无内容/PII 护栏钩子 |
| 可观测 | LiteLLM/Portkey 标准导出;New API 审计+日志 | 进程内统计/SSE/告警/审计 | 无 Prometheus/OTel 标准导出 |
| 多租户 | One API 用户分组;New API 面向团队共享的配额/订阅 | 全局三角色,无团队维度 | 无团队级 Key/配额/账单 |
| 多节点 | New API Redis 共享限流/多节点 | 单实例锁 + SQLite,限流进程内 | 架构上不支持横向扩容 |
| 国际化 | New API 7 语言 | 管理台中文 | 无 i18n |
| 其他 | One API 充值/兑换码;Portkey MCP Gateway;CCR 桌面控制面/工具增强 | 内部共享池;已有 MCP 无关 | 商业/生态类能力不匹配定位 |

## 3. 候选功能清单(按优先级)

排序依据:先看内部用户(个人/小团队)的直接价值,再看实现成本,最后看与「单实例 + SQLite + 公共凭证池」架构的契合度。若后续确认转向对外多租户服务,团队隔离与护栏两项上调一档。

| # | 优先级 | 模块 | 候选功能 | 现状差距 | 建议 | 档位 |
|---|---|---|---|---|---|---|
| 1 | P0 | API 兼容层 | Gemini 原生入口 | 仅三协议 | 新增 `/v1beta/models/{model}:generateContent` 与 `:streamGenerateContent`,复用现有调度与统计;请求/响应映射放 `src/compat/gemini/` | 2 |
| 2 | P0 | 鉴权与配额 | Key 预算周期扩展 | 仅自然日配额 | `api_keys.quota` JSON 增 `monthly_*`、`total_*`、`expires_at`;准入判定与明细聚合复用 `key_quota.py` | 2 |
| 3 | P0 | 部署运维 | 标准可观测导出 | 仅内部页面/SSE | 增 `/metrics`(请求、token、成本、在途、凭证健康)并预留 OTel trace 导出;指标从现有统计层读取 | 2 |
| 4 | P1 | 安全边界 | 请求/响应护栏钩子 | 无内容治理 | 可插拔 `guardrail` 接口(内置敏感词/PII 规则,失败策略可配),默认关闭;命中记审计 | 3 |
| 5 | P1 | 调度与路由 | 条件路由策略 | 仅健康+权重 | 在现有排序前加规则表(按模型/渠道/成本/延迟/时段),规则存 DB、热更;保留现逻辑为默认策略 | 3 |
| 6 | P1 | 成本 | 模型价格表自动同步 | 刊例价静态 | 定时拉 LiteLLM/OpenRouter 价目,按模型归一键合并,冲突以实测扣费为准;价目快照留版本 | 2 |
| 7 | P1 | 统计计费 | 网关侧精确缓存 | 仅透传上游缓存 | 非流式按 `model+messages+tools` 哈希缓存,TTL 可配,命中不计上游用量;注意与「到期额度优先」策略的冲突,默认关闭 | 3 |
| 8 | P2 | 鉴权与配额 | 团队/租户隔离 | 全局三角色 | `teams` 表 + Key/统计/账单挂团队维度;迁移与权限模型改动大 | 3 |
| 9 | P2 | 管理端 | 管理台 i18n | 中文硬编码 | 抽词条表,先中/英双语;与现有模板/组件解耦 | 2 |

## 4. 不纳入本次范围

| 功能 | 理由 |
|---|---|
| 充值、兑换码、订阅计费 | 定位是内部公共凭证池,无对外收款场景;引入会带来支付与合规边界 |
| MCP Gateway / ToolHub | 与「编码订阅转 API」核心链路无关,且上游渠道不提供对应能力 |
| 桌面客户端 / 本地代理控制面 | 服务端已是控制面,再套一层只增加部署复杂度 |
| 多节点 / Redis 共享限流 | 现有单实例锁 + SQLite 是明确设计取舍;改造成本高于当前收益 |
| Embeddings / 图像 / 音频端点 | 十个逆向渠道以编码对话为主,端点覆盖与上游能力不匹配 |
| 单二进制打包 | Python 技术栈,Docker 镜像已覆盖交付形态 |
| 语义缓存 / Prompt 模板管理 | 编码场景命中率低,且模板管理属产品层能力,非网关缺口 |

## 5. 验证

本次仅新增本文档,生产代码零改动:

```bash
uv run pytest -q --cov=src --cov-report=term --cov-fail-under=100
pnpm --dir web exec vitest run
```
59 changes: 59 additions & 0 deletions OSS-COMPARISON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# 同类开源项目功能对照与候选升级清单

调研时间:2026-11-12。对照对象为 5 个自托管 LLM 网关 / 多凭据聚合项目,版本与更新时间取自各仓库 GitHub Release 与仓库元数据。本文只做差距分析,不改动生产代码。

## 1. 对照对象

| 项目 | 版本 / 更新时间 | 定位 | 与本项目重叠 |
|---|---|---|---|
| [songquanpeng/one-api](https://github.com/songquanpeng/one-api) | v0.6.10 / 2025-02-02 | 多供应商 Key 管理与二次分发,单二进制 | 渠道池、Key 配额、模型重定向、兑换码、OAuth 登录 |
| [Calcium-Ion/new-api](https://github.com/Calcium-Ion/new-api) | v1.0.0-rc.49 / 2026-11-08 | 商业向 AI 网关,OpenAI / Claude / Gemini 三协议 | 渠道分组、模型映射、计费、看板、OAuth、订阅 |
| [BerriAI/litellm](https://github.com/BerriAI/litellm) | v1.109.0 / 2026-11-11 | 100+ 供应商 SDK + 网关 | 路由回退、虚拟 Key 预算、缓存、护栏、可观测性 |
| [yym68686/uni-api](https://github.com/yym68686/uni-api) | v1.7.276 / 2026-09-12 | YAML 配置的轻量多供应商聚合 | 统一 OpenAI 出口、负载均衡、重试、限流 |
| [tbphp/gpt-load](https://github.com/tbphp/gpt-load) | v2.0.0-rc.63 / 2026-11-08 | 多渠道多凭据自托管网关 | 密钥/订阅账号池、加权调度、故障转移、请求日志与用量 |

## 2. 逐项对照

| 能力 | 本项目现状 | 同类项目做法 | 结论 |
|---|---|---|---|
| 供应商接入 | 硬编码 10 个订阅渠道(`KNOWN_PROVIDERS`) | one-api / new-api / uni-api / LiteLLM 均允许任意 OpenAI 兼容 `base_url` | **缺口**,见 C1 |
| 模型映射 | 全局 `MODEL_ALIASES`,渠道内模型名由代码归一 | one-api / new-api 支持渠道级「模型重定向」 | **部分缺口**,见 C3 |
| 调度与容错 | 健康度、权重、冷却、到期额度、粘性会话、回退组 | LiteLLM Router、new-api 渠道分组、gpt-load 加权轮询 | 本项目不弱,保持 |
| 配额治理 | Key / 用户双层的日请求、日 token、日成本、RPM、TPM、并发;凭证护栏 | 竞品多为 Key / 用户 / 团队预算,部分支持 Redis 跨副本 | 本项目够用;团队层级见第 4 节 |
| 鉴权 | 本地账号、一次性激活、三角色、CSRF、IP / 模型白名单 | one-api / new-api 支持 GitHub / OIDC 等 OAuth | **缺口**,见 C4 |
| 可观测性 | Prometheus `/metrics`、环形日志、审计、九类告警 + Webhook | LiteLLM 接 OTel / Langfuse;new-api 数据看板 | **部分缺口**,见 C5 |
| 成本核算 | OpenRouter 刊例价 + 24h 实测扣费校准 | LiteLLM 维护价格表;new-api 支持自定义倍率 | **部分缺口**,见 C6 |
| 协议兼容 | OpenAI Chat / Responses、Claude Messages、模型列表 | new-api 另有原生 Gemini;LiteLLM 全协议 | **低优先缺口**,见 C7 |
| 运维动作 | 批量探活/签到/预刷新、备份、配置热更 85 项 | gpt-load / one-api 批量导入导出、渠道测试 | 本项目覆盖较全 |
| 计费与充值 | 不做按 token 计费(订阅制上游,见 PROPOSAL 非目标) | one-api / new-api 完整计费、兑换码、充值 | 不纳入,见第 4 节 |
| 对话留存 | 明确不存对话,只留用量明细 | 多数竞品记录请求/响应全文 | 不纳入,见第 4 节 |

## 3. 候选功能清单(按优先级)

优先级依据:用户价值(能否直接解决渠道可用性 / 运维成本 / 账单准确性)> 与现有架构契合度 > 实现成本。工作量档位:1 = ≤2 人天,2 = 3–7 人天,3 = ≥8 人天或跨模块改造。

| 优先级 | 编号 | 候选功能 | 所属模块 | 现状差距 | 升级 / 新增建议 | 档位 |
|---|---|---|---|---|---|---|
| P0 | C1 | 自定义 OpenAI 兼容渠道 | provider / credentials / 调度 | 新增供应商必须改代码与发版 | 新增 `generic_openai` 适配器:admin 配 `base_url`、密钥、模型列表;复用现有 executor、统计、冷却与配额 | 3 |
| P0 | C2 | 标签参与调度与限额 | engine / db / 管理台 | 标签仅展示,不参与选号或限额 | 候选过滤支持按标签(如仅 `prod`);凭证限额支持按标签聚合日用量 | 2 |
| P1 | C3 | 渠道级模型 ID 覆盖 | provider / model_resolver | 上游改模型 ID 需改代码;全局别名无法按渠道区分 | 增加 `provider → {统一名: 上游 ID}` 配置,解析优先级:渠道覆盖 > 全局别名 > 原值 | 1 |
| P1 | C4 | OIDC / SSO 登录 | auth / 管理台 | 只有本地账号与激活链接 | OIDC 授权码登录,claim 映射到现有三角色;保留本地账号兜底 | 2 |
| P1 | C5 | OpenTelemetry 链路导出 | observability | 有 request_id 与 Prometheus,但无 trace 导出 | 可选 OTLP exporter,复用 request_id 贯穿上游调用,支持采样与关闭 | 2 |
| P2 | C6 | 模型单价手工覆盖 | 统计 / 成本 | 成本依赖 OpenRouter 刊例价与实测校准,无法按合同价修正 | admin 可编辑每模型 USD/1M 单价,未配置时回落现有校准逻辑 | 1 |
| P2 | C7 | 原生 Gemini 入口 | api / compat | 仅 OpenAI 与 Claude 入站 | `/v1beta/models/*:generateContent` 入站翻译,复用现有三出口骨架 | 2 |

## 4. 不纳入本次范围

| 功能 | 理由 |
|---|---|
| 按 token 计费、充值、兑换码、套餐订阅 | PROPOSAL 明确非目标:上游订阅制,成本不随 token 线性增长 |
| 多租户组织 / 团队层级 | 目标用户尚未定为团队或多租户(PO 待确认);现有 users + 三角色已覆盖单团队 |
| 请求 / 响应全文留存与回放 | 现有隐私设计明确不存对话;竞品做法不适用于本定位 |
| 语义缓存 / Prompt 缓存 | 会破坏用量与成本口径,订阅制下收益不明确 |
| Embeddings / Rerank / 图片 / 音频端点 | 十个上游以编码类 chat 为主,短期无真实需求 |
| 分布式限流与多副本 | 现架构为单实例锁 + SQLite;需先重构存储层,成本高于收益 |
| MCP 网关 / Agent 托管 | 本项目定位是 API 出口,不是 Agent 平台 |

## 5. 建议排期

先做 C2(成本低、直接改善多号运维),再评估 C1 与 C4;C3、C6 可作为顺手项并入相邻迭代;C5、C7 等真实需求出现再排。
4 changes: 2 additions & 2 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Ten channels — **CodeBuddy** (CN + international, two separate channels), **TR
## Features

- **OpenAI-compatible surface**: `/v1/chat/completions` (streaming / non-streaming), `/v1/responses` (Codex CLI), `/v1/messages` (Claude Code), `/v1/models` (incl. single-model lookup), `/v1/user/balance`.
- **Unified scheduling**: bare model names route by health; `model@provider` pins upstream. Free channels first → least in-flight → expiring credits → health with tiered cooldowns → largest balance. Catalog x0 rates are calibrated against actual charges in the last 24h, so an upstream that changed billing without updating its catalog can't hold the free tier forever. Model-scoped limit sidelines only that model; multi-turn sticks to one credential, rotates only on error.
- **Unified scheduling**: bare model names route by health; `model@provider` pins upstream. Free channels first → least in-flight → expiring credits → health with tiered cooldowns → largest balance. Admins can set a per-credential **weight** (`-1000..1000`): it only reorders within the same health tier (positive first, negative as standby) and never promotes cooling or exhausted credentials. Catalog x0 rates are calibrated against actual charges in the last 24h, so an upstream that changed billing without updating its catalog can't hold the free tier forever. Model-scoped limit sidelines only that model; multi-turn sticks to one credential, rotates only on error.
- **Expiry-aware burn**: credits expiring inside main window (default 36h) burn first; ties fall to 7-day window. Units unify to credits.
- **Credential-gated model list**: only channels with usable credential appear; no phantom models.
- **Shared credential pool**: admins maintain, everyone shares, encrypted at rest (`APP_SECRET`); device-code login, account switching, quota probing, daily check-in, token pre-refresh.
Expand Down Expand Up @@ -123,7 +123,7 @@ echo "$PASSWORD" | python3 scripts/create_user.py alice --role admin --password-
- This process only; it never tails file. Under multiple processes each sees its own slice.
- **Redaction is hard precondition**: lines matching `token` / `secret` / `authorization` / `cookie` / `password` / `api_key`, an `sk-…` shape or `Bearer <value>` dropped whole.
- Buffer `LOG_BUFFER_LINES` (default 500, hot), oldest overwritten first; `0` disables page (both endpoints 404).
- 4000 chars per line; each carries `seq`, timestamp, level, logger, `request_id`. Subscriber limit 4, beyond → 429 + `Retry-After`.
- 4000 chars per line; each carries `seq`, timestamp, level, logger, `request_id`. **Click the `#first8` chip** to filter by that request id and collapse one request's whole chain (every rotation / cooldown / downgrade in the executor shares it); pasting the `X-Request-ID` response header into the keyword box does the same. stdout / journald / launchd files carry `[request_id]` too (background tasks show `-`). Subscriber limit 4, beyond → 429 + `Retry-After`.

## Usage

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
## 特性

- **OpenAI 兼容出口**:`/v1/chat/completions`(流式 / 非流式)、`/v1/responses`(Codex CLI)、`/v1/messages`(Claude Code)、`/v1/models`(含单模型查询)、`/v1/user/balance`。
- **统一调度**:扁平名按健康度选号;`模型@渠道` 强制。优先级:免费渠道 → 在途最少 → 到期额度 → 健康度 + 分级冷却 → 余额多者。目录里的 x0 会被最近 24h 的实测扣费校准(上游改计费不改目录时不至于永远占住免费档)。模型级限流只避让该模型;多轮对话粘同一凭证,出错才轮换。
- **统一调度**:扁平名按健康度选号;`模型@渠道` 强制。优先级:免费渠道 → 在途最少 → 到期额度 → 健康度 + 分级冷却 → 余额多者。管理员可给凭证设**权重**(`-1000..1000`):只在同一健康档内重排(正数优先、负数当备用),不会把冷却/耗尽的号顶上来。目录里的 x0 会被最近 24h 的实测扣费校准(上游改计费不改目录时不至于永远占住免费档)。模型级限流只避让该模型;多轮对话粘同一凭证,出错才轮换。
- **到期额度优先**:主窗口(默认 36h)先烧将过期额度;打平再比 7 天窗口。单位统一积分。
- **模型列表按凭证加载**:只列有可用凭证的渠道 → 无幽灵模型。
- **公共凭证池**:admin 维护、全员共享、加密入库(`APP_SECRET`);设备码登录、多账号切换、额度探测、签到、token 预刷新。
Expand Down Expand Up @@ -123,7 +123,7 @@ echo "$PASSWORD" | python3 scripts/create_user.py alice --role admin --password-
- 只收本进程 `logging`;不读文件、不 tail。多进程各看各的。
- **脱敏是硬前提**:每行发出前过白名单。命中 `token` / `secret` / `authorization` / `cookie` / `password` / `api_key`、`sk-…` 形状或 `Bearer <值>` → 整行丢弃。
- 缓冲 `LOG_BUFFER_LINES`(默认 500,可热更),环形覆盖最旧。`0` 关整页(两端点 404)。
- 单行上限 4000 字符;每行带 `seq`、时间、级别、logger、`request_id`。订阅者上限 4,超限 429 + `Retry-After`。
- 单行上限 4000 字符;每行带 `seq`、时间、级别、logger、`request_id`。**点行尾的 `#前8位`** 即按该 request_id 过滤,收起一次请求的全部日志(executor 每次换号 / 冷却 / 降级都带同一个 ID);也可以直接把响应头 `X-Request-ID` 粘进关键字框。stdout / journald / launchd 文件同样带 `[request_id]`(后台任务为 `-`)。订阅者上限 4,超限 429 + `Retry-After`。

## 使用

Expand Down
2 changes: 2 additions & 0 deletions TECHNICAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ class Scheduler:
4. **账户剩余积分**多者优先——只作健康度**打平键**。
5. 仍同分按 `credential_id` 稳定。

**管理员权重**(`credentials.routing_weight`,`-1000..1000`,NULL/0 = 默认)不参与上表的键比较,而是在排完序后再做一次**同健康档内的稳定重排**:取当前第一名所在的 `_rank` 档,档内按 `-weight` 重排,再接回其余档。因此权重能压过免费优先 / 在途 / 到期额度,但永远不能把 exhausted 或 unknown 的号越过 known 档(权重是偏好,不是准入条件);全 0 时排序与补列前逐字节一致。

**手动 pin 优先**:有 `pinned` → 只在 pinned 间排序。到期积分只算一处 `expiring_credits()`:选号走 `Candidate.expiry_credits()`;列表走 `GET /api/credentials` 的 `quota_expiring_credits` / `_secondary`;共用 `expiry_windows()`。无到期信息 → `null`;窗口关闭或确无临近过期 → `0`(均不显示)。

> **TRAE 也落 `quota_expiry_ladder`**(权益包各自独立到期),两列同时填,口径「未过期 + 有余额」。`quota_cycle_end` 仍 `NULL`(各包未必共享重置点)。
Expand Down
Loading
Loading