OmniRoute 仓库开发指南:面向 Claude Code 的代码库架构、弹性机制与硬性规则解析
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
导读
本文基于仓库内的瑞典语版 CLAUDE.md 展开,系统讲解 OmniRoute(统一 AI 网关/路由器)仓库的结构、请求处理流水线、三层运行时弹性机制(供应商熔断器、连接冷却、模型锁定问题),以及面向 Claude Code 等 AI 编程助手与人类开发者的编码规范、扩展场景与硬性规则。读完本文,你将掌握在 OmniRoute 仓库中快速定位模块、正确添加供应商/路由/数据库模块/MCP 工具、排查路由故障,并遵守其测试、安全与 Git 工作流约束的完整实操路径。
项目全景:一个端点的多供应商 AI 网关
OmniRoute是一个统一的 AI 代理/路由器:对外只暴露一个 OpenAI 兼容端点,对内聚合 329+ LLM 供应商,支持自动回退、负载均衡与格式翻译。仓库为 monorepo 结构,核心工作区与职责划分如下:
| 层级 | 位置 | 职责 |
|---|---|---|
| API 路由 | src/app/api/v1/ | Next.js App Router 入口点 |
| Handlers | open-sse/handlers/ | 请求处理(chat、embeddings 等) |
| Executors | open-sse/executors/ | 供应商特定的 HTTP 分发 |
| Translators | open-sse/translator/ | 格式转换(OpenAI↔Claude↔Gemini) |
| Transformer | open-sse/transformer/ | 响应 API ↔ 聊天补全转换 |
| 服务 | open-sse/services/ | 组合路由、速率限制、缓存等 |
| 数据库 | src/lib/db/ | SQLite 领域模块与迁移 |
| 域/策略 | src/domain/ | 策略引擎、成本规则、回退逻辑 |
| MCP 服务器 | open-sse/mcp-server/ | 多个工具、三种传输(stdio/SSE/Streamable HTTP)、多级作用域 |
| A2A 服务器 | src/lib/a2a/ | JSON-RPC 2.0 智能体协议 |
| 技能 | src/lib/skills/ | 可扩展技能框架 |
| 记忆 | src/lib/memory/ | 持久化对话记忆 |
仓库顶层同时包含src/(Next.js 应用)、open-sse/(流式引擎工作区)、electron/(桌面应用)、tests/(测试套件)以及bin/(CLI 入口)。项目级总规则统一收敛在 AGENTS.md,而 CLAUDE.md 只保留 Claude Code 特有的运行细节;本瑞典语文档则是面向国际贡献者的一站式操作指南。
快速上手与开发环境
常用命令
npm install # 安装依赖(自动从 .env.example 生成 .env) npm run dev # 开发服务器,http://localhost:20128 npm run build # 生产构建(Next.js 独立产物) npm run lint # ESLint(期望 0 错误;警告为既有存量) npm run typecheck:core # TypeScript 类型检查(应为干净) npm run typecheck:noimplicit:core # 严格检查(不允许隐式 any) npm run test:coverage # 单元测试 + 覆盖率门槛(语句/行/函数/分支 = 75/75/75/70) npm run check # lint + test 组合 npm run check:cycles # 检测循环依赖运行测试
# 单个测试文件(Node.js 内置测试运行器——大多数测试) node --import tsx/esm --test tests/unit/your-file.test.ts # Vitest(MCP-server、autoCombo、cache) npm run test:vitest # 全部测试套件 npm run test:all完整测试矩阵参见 CONTRIBUTING.md 中的"运行测试"一节;深入架构参见 AGENTS.md。
运行环境要求
- 运行时:Node.js
>=20.20.2 <21 | >=22.22.2 <23 | >=24 <25,ES Modules。 - TypeScript:5.9+,target ES2022,module esnext,resolution bundler。
- 路径别名:
@/*→src/,@omniroute/open-sse→open-sse/,@omniroute/open-sse/*→open-sse/*。 - 默认端口:20128(API 与 dashboard 同端口)。
- 数据目录:由
DATA_DIR环境变量指定,默认~/.omniroute/。 - 关键环境变量:
PORT、JWT_SECRET、API_KEY_SECRET、INITIAL_PASSWORD、REQUIRE_API_KEY、APP_LOG_LEVEL。 - 初始化:
cp .env.example .env,然后用openssl rand -base64 48生成JWT_SECRET、用openssl rand -hex 32生成API_KEY_SECRET。
请求处理流水线:从客户端到上游再到响应
OmniRoute 的请求路径遵循清晰的分层结构,瑞典语文档给出了完整链路:
客户端 → /v1/chat/completions (Next.js 路由) → CORS → Zod 校验 → 认证? → 策略检查 → 提示注入防护 → handleChatCore() [open-sse/handlers/chatCore.ts] → 缓存检查 → 速率限制 → 组合路由? → resolveComboTargets() → 每个目标执行 handleSingleModel() → translateRequest() → getExecutor() → executor.execute() → fetch() 上游 → 带退避的重试 → 响应翻译 → SSE 流或 JSON → 若为 Responses API: responsesTransformer.ts TransformStream从源码看,chatCore.ts 中handleChatCore()正是请求处理的中枢:它在 translator/index.ts 中通过translateRequest()将请求转换为目标供应商的格式(needsTranslation()判定是否真的需要转换),再经由 executor 分发到上游。每条 API 路由遵循一致模式:路由 → CORS preflight → Zod 体校验 → 可选认证(extractApiKey/isValidApiKey)→ API 密钥策略应用 → handler 分发(open-sse)。值得注意,项目没有全局 Next.js 中间件——所有拦截都是路由级(route-specific)的。
组合路由(Combo routing)位于 open-sse/services/combo.ts,提供 19 种公开策略:priority、weighted、fill-first、round-robin、p2c、random、least-used、cost-optimized、reset-aware、reset-window、headroom、strict-random、auto、lkgp、context-optimized、cache-optimized、context-relay、fusion、pipeline。每个目标调用handleSingleModel(),后者用handleChatCore()包装并叠加每目标错误处理与熔断器检查。策略详细评分可参阅 AUTO-COMBO.md(13 因子 Auto-Combo 评分)与 RESILIENCE_GUIDE.md(3 层弹性)。
三层运行时弹性机制:熔断、冷却与模型锁定
OmniRoute 针对瞬时故障设计了三个相关但彼此独立的机制。调试路由行为时务必区分它们的作用域,否则很容易误判"供应商坏了"。总览图见 resilience-3layers.svg(源文件 resilience-3layers.mmd)。
第一层:供应商级熔断器(Provider Circuit Breaker)
- 作用域:整个供应商,例如
glm、openai、anthropic。 - 目的:当某供应商在上游/服务层面反复失败时停止向其发送流量,避免不健康的供应商拖慢每一个请求。
实现位置:
- 核心类:circuitBreaker.ts(
CircuitBreaker类、getCircuitBreaker(name, options)工厂、getAllCircuitBreakerStatuses()状态汇总)。 - Chat 端口/接线:src/sse/handlers/chatHelpers.ts、src/sse/handlers/chat.ts。
- 运行时状态 API:src/app/api/monitoring/health/route.ts。
- 共享封装:open-sse/services/accountFallback.ts。
- 持久状态表:
domain_circuit_breakers。
状态机:
CLOSED:正常流量放行。OPEN:供应商被临时阻断;调用方收到 provider-circuit-open 响应,或组合路由跳到另一目标。HALF_OPEN:重置超时已到期,放行一次试探请求。成功则关闭熔断器,失败则再次打开。
从源码看,circuitBreaker.ts 还定义了DEGRADED状态(故障计数达到降级阈值后先进入降级态,再升级为 OPEN),并支持按故障类型(FailureKind)分别设定阈值与冷却,实现"每种错误单独计数"的精细控制(kindFailureCounts)。
配置阈值(定义于 open-sse/config/constants.ts 的PROVIDER_PROFILES,可通过OMNIROUTE_CIRCUIT_BREAKER_*环境变量覆盖):
| 供应商类型 | 熔断阈值(默认) | 重置窗口(默认) |
|---|---|---|
| OAuth 供应商 | 8(OMNIROUTE_CIRCUIT_BREAKER_OAUTH_THRESHOLD) | 60s(..._OAUTH_RESET_MS) |
| API 密钥供应商 | 12(OMNIROUTE_CIRCUIT_BREAKER_API_KEY_THRESHOLD) | 30s(..._API_KEY_RESET_MS) |
| 本地供应商 | 2(OMNIROUTE_CIRCUIT_BREAKER_LOCAL_THRESHOLD) | 15s(..._LOCAL_RESET_MS) |
注:仓库当前版本(2026-09)已将 OAuth 默认阈值从文档时代的 3 上调到 8、API 密钥从 5 上调到 12(适应 500+ 连接的规模),并新增
providerFailureThreshold(整供应商冷却)、providerCooldownMs、degradationThreshold、maxBackoffMultiplier等参数。文档与源码存在演进差异,以 constants.ts 为最终事实。
触发规则:只有供应商级错误状态才应触发供应商熔断器:
(408, 500, 502, 503, 504);不要为常规账户/密钥/模型错误(如大多数401、403、429)触发整供应商熔断——这些通常属于连接冷却或模型锁定问题。一个常规 API 密钥供应商的403若未被分类为终结性供应商/账户错误,应可恢复。
惰性恢复:熔断器使用惰性恢复而非后台定时器。当OPEN到期后,getStatus()、canExecute()、getRetryAfterMs()等读取路径会将状态更新为HALF_OPEN,从而保证仪表盘与组合候选构建器不会永远排除一个已过期的供应商。
第二层:连接冷却(Connection Cooldown)
- 作用域:某个供应商连接/账户/密钥(单条凭据)。
- 目的:临时跳过一条坏密钥/账户,同时同一供应商的其他连接继续服务请求。
实现位置:
- 写入/更新路径:
src/sse/services/auth.ts::markAccountUnavailable() - 账户遍历/过滤:
src/sse/services/auth.ts::getProviderCredentials... - 冷却计算:
open-sse/services/accountFallback.ts::checkFallbackError() - 设置项:
src/lib/resilience/settings.ts
供应商连接上的关键字段:
rateLimitedUntil; testStatus: "unavailable"; lastError; lastErrorType; errorCode; backoffLevel;账户选择期间,满足以下条件时跳过该连接:
new Date(rateLimitedUntil).getTime() > Date.now();冷却同样是惰性的:当rateLimitedUntil已落在过去,连接重新具备资格。成功使用后,clearAccountError()会清除testStatus、rateLimitedUntil、错误字段与backoffLevel。
默认冷却行为:
- OAuth 基础冷却:
5s。 - API 密钥基础冷却:
3s。 - API 密钥
429应优先采用上游重试提示(Retry-After、重置响应头或可解析的重置文本)。 - 反复的可恢复错误使用指数退避:
baseCooldownMs * 2 ** failureIndex;防惊群(anti-thundering-herd)保护用于防止同一连接上的并发失败反复延长冷却或对backoffLevel重复加一。
终结性状态不是冷却:banned、expired、credits_exhausted应保持不可用,直到凭据/设置变更或操作员重置。不得用临时冷却状态覆盖终结性状态。
第三层:模型锁定问题(Model Lockout)
- 作用域:供应商 + 连接 + 模型(最精细粒度)。
- 目的:当仅某个模型对该连接不可用或配额受限时,避免停用整条连接。
典型场景:
- 按模型配额计费的供应商返回
429。 - 本地供应商对缺失模型返回
404。 - 供应商特定的模式/模型权限错误(如选定的 Grok 模式)。
模型锁定问题实现在 open-sse/services/accountFallback.ts,允许同一条连接继续服务其他模型。
故障排查指引
- 若某供应商的所有密钥都被跳过,请同时检查供应商熔断器状态与每条连接的
rateLimitedUntil/testStatus。 - 若某供应商在重置窗口后仍被"永久"排除,检查代码是否读取原始
state而非使用getStatus()/canExecute()。 - 若某供应商密钥失败但其他密钥正常,应优先连接冷却而非供应商熔断器。
- 若只有某个模型失败,应优先模型锁定问题而非连接冷却。
- 若某状态应自愈,它应当带未来时间戳/重置超时,并有读取路径去更新过期状态;永久状态需要人工修改凭据或配置。
编码规范与数据库约束
代码风格
- 2 空格缩进、分号、双引号、100 字符宽度、ES5 尾逗号(由 lint-staged 经 Prettier 强制)。
- 导入顺序:外部 → 内部(
@/、@omniroute/open-sse)→ 相对。 - 命名:文件 = camelCase/kebab,组件 = PascalCase,常量 = UPPER_SNAKE。
- ESLint:
no-eval、no-implied-eval、no-new-func全局为错误;no-explicit-any在open-sse/与tests/中为警告。 - TypeScript:
strict: false,target ES2022,module esnext,resolution bundler;优先显式类型。
数据库约定
- 始终经由
src/lib/db/领域模块访问数据库——绝不在路由或 handler 中写裸 SQL。 - 绝不向
src/lib/localDb.ts添加逻辑(它只是 re-export 层)。 - 绝不从
localDb.ts做 barrel 导入——应导入具体的db/模块。 - DB 单例:
getDbInstance(),来自src/lib/db/core.ts(WAL journaling)。 - 迁移:
src/lib/db/migrations/—— 版本化 SQL 文件、幂等、在事务中执行。
错误处理
- try/catch 使用具体错误类型,用 pino 上下文记录日志。
- 绝不在 SSE 流中吞掉错误——用中止信号做清理。
- 返回正确的 HTTP 状态码(4xx/5xx)。
安全红线
瑞典语文档中针对安全列出了明确的强制要求,均可在源码与文档中印证:
- 绝不使用
eval()、new Function()或隐式 eval。 - 所有输入用 Zod schema 校验。
- 凭据静态加密(AES-256-GCM)。
- 上游 header denylist 位于
src/shared/constants/upstreamHeaders.ts——编辑时保持净化、Zod schema 与单元测试同步。 - 公开上游凭据(Gemini/Antigravity/Windsurf 风格 OAuth client_id/secret + 从公开 CLI 提取的 Firebase Web 密钥)必须通过
open-sse/utils/publicCreds.ts的resolvePublicCred()内嵌,绝不允许写成字符串字面量。参见 PUBLIC_CREDS.md。 - 错误响应(HTTP / SSE / executor / MCP handler)必须经由
open-sse/utils/error.ts的buildErrorBody()或sanitizeErrorMessage()路由——绝不允许把裸err.stack或err.message放进响应体。参见 ERROR_SANITIZATION.md。 - 基于变量拼接的 Shell 命令:调用
exec()/spawn()时如需传入运行时值,请通过env选项传递(自动做 shell 转义)——绝不在脚本体中字符串插值不可信/外部路径。参考src/mitm/cert/install.ts::updateNssDatabases。 - 新增安全敏感面时,优先采用安全默认库(如 Helmet.js、DOMPurify、ssrf-req-filter、safe-regex、Google Tink),而不是自研实现。
常见扩展场景:五条实战操作路径
1. 添加新供应商
- 在
src/shared/constants/providers.ts注册(加载时 Zod 校验)。 - 若需自定义逻辑,在
open-sse/executors/添加 executor(继承BaseExecutor)。 - 非 OpenAI 格式则在
open-sse/translator/添加 translator。 - 若基于 OAuth,在
src/lib/oauth/constants/oauth.ts添加 OAuth 配置——若上游 CLI 下发公开 client_id/secret,通过resolvePublicCred()内嵌(见 PUBLIC_CREDS.md),绝不用字面量。 - 在
open-sse/config/providerRegistry.ts注册模型。 - 在
tests/unit/编写测试(若添加了新的内嵌默认值,务必包含 publicCreds 形式)。
2. 添加新 API 路由
- 在
src/app/api/v1/your-route/下建目录。 - 创建带
GET/POSThandler 的route.ts。 - 遵循模式:CORS → Zod 体校验 → 可选认证 → handler 分发。
- handler 放在
open-sse/handlers/(从那里导入,不要内联)。 - 错误响应使用
open-sse/utils/error.ts的buildErrorBody()/errorResponse()(自动净化——绝不要把err.stack或err.message裸放进响应体)。参见 ERROR_SANITIZATION.md。 - 添加测试——至少包含一条确认错误响应不泄漏堆栈的断言(
!body.error.message.includes("at /"))。
3. 添加新 DB 模块
- 创建
src/lib/db/yourModule.ts—— 从./core.ts导入getDbInstance。 - 为你的领域表导出 CRUD 函数。
- 若需新表,在
src/lib/db/migrations/添加迁移。 - 从
src/lib/localDb.tsre-export(仅加入 re-export 列表)。 - 编写测试。
4. 添加新 MCP 工具
- 在
open-sse/mcp-server/tools/添加工具定义:Zod 输入 schema + 异步 handler。 - 注册进工具集(由
createMcpServer()接线)。 - 分配到合适的作用域。
- 编写测试(工具调用会记录到
mcp_audit表)。
5. 添加新 A2A 技能 / 云智能体 / 护栏等
- A2A 技能:在
src/lib/a2a/skills/创建(已有 5 个:smart-routing、quota-management、provider-discovery、cost-analysis、health-report)→ 在src/lib/a2a/taskExecution.ts的A2A_SKILL_HANDLERS注册 → 在src/app/.well-known/agent.json/route.ts(Agent Card)暴露 → 在tests/unit/写测试 → 在 A2A-SERVER.md 的技能表补充文档。 - 云智能体:在
src/lib/cloudAgent/agents/创建继承CloudAgentBase的类(已有 3 个:codex-cloud、devin、jules),实现createTask、getStatus、approvePlan、sendMessage、listSources→ 注册到src/lib/cloudAgent/registry.ts→ 必要时加 OAuth/认证处理(src/lib/oauth/providers/)→ 测试并文档化到 CLOUD_AGENT.md。 - 护栏/评测/技能/Webhook 事件:guardrail →
src/lib/guardrails/→ GUARDRAILS.md;eval 套件 →src/lib/evals/→ EVALS.md;沙箱技能 →src/lib/skills/→ SKILLS.md;webhook 事件 →src/lib/webhookDispatcher.ts→ WEBHOOKS.md。
测试矩阵与 PR 规则
| 内容 | 命令 |
|---|---|
| 单元测试 | npm run test:unit |
| 单个文件 | node --import tsx/esm --test tests/unit/file.test.ts |
| Vitest(MCP、autoCombo) | npm run test:vitest |
| E2E(Playwright) | npm run test:e2e |
| 协议 E2E(MCP+A2A) | npm run test:protocols:e2e |
| 生态 | npm run test:ecosystem |
| 覆盖率门槛 | npm run test:coverage(语句/行/函数/分支 = 75/75/75/70) |
| 覆盖率报告 | npm run coverage:report |
PR 规则:若修改src/、open-sse/、electron/或bin/中的生产代码,必须在同一 PR 中包含或更新测试。
测试层级偏好:单元优先 → 集成(多模块或 DB 状态)→ e2e(仅 UI/工作流)。Bug 复现应编码为自动化测试,与修复同步提交。
Copilot 覆盖率策略:当 PR 改动生产代码且覆盖率低于 75%(语句/行/函数)或 70%(分支)时,不仅要报告——还要补充或更新测试、重新运行覆盖率门槛,然后请求确认。PR 报告中应包含执行的命令、修改的测试文件与最终覆盖率结果。
Git 工作流与质量门禁
# 绝不直接提交到 main git checkout -b feat/your-feature git commit -m "feat: beskriv din ändring" git push -u origin feat/your-feature- 分支前缀:
feat/、fix/、refactor/、docs/、test/、chore/。 - 提交格式(Conventional Commits):
feat(db): lägg till kretsbrytare—— 领域范围包括db、sse、oauth、dashboard、api、cli、docker、ci、mcp、a2a、memory、skills。 - Husky hooks:
- pre-commit:lint-staged +
check-docs-sync+check:any-budget:t11。 - pre-push:
npm run test:unit。
- pre-commit:lint-staged +
任何非平凡的改动,应先阅读对应主题的深度文档(详见 docs/architecture/REPOSITORY_MAP.md 的索引):架构总览 ARCHITECTURE.md、工程参考 CODEBASE_DOCUMENTATION.md、Auto-Combo 13 因子评分 AUTO-COMBO.md、3 层弹性 RESILIENCE_GUIDE.md、推理重放 REASONING_REPLAY.md、记忆系统(FTS5 + Qdrant)MEMORY.md、授权管道 AUTHZ_GUIDE.md、MCP 服务器 MCP-SERVER.md、A2A 服务器 A2A-SERVER.md、API 参考 API_REFERENCE.md 与 openapi.yaml、发布流程 RELEASE_CHECKLIST.md 等。
十六条硬性规则(Hard Rules)速查
- 绝不提交机密或凭据。
- 绝不在
localDb.ts添加逻辑。 - 绝不使用
eval()/new Function()/ 隐式 eval。 - 绝不直接提交到
main。 - 绝不在路由中写裸 SQL——使用
src/lib/db/模块。 - 绝不在 SSE 流中吞掉错误。
- 始终用 Zod schema 校验输入。
- 改动生产代码时始终包含测试。
- 覆盖率必须保持 ≥75%(语句、行、函数)/ ≥70%(分支),当前实测约 82%。
- 未经操作员明确批准,绝不绕过 Husky hooks(
--no-verify、--no-gpg-sign)。 - 绝不把公开上游 OAuth client_id/secret 或 Firebase Web 密钥作为字符串字面量——始终经由
resolvePublicCred()(open-sse/utils/publicCreds.ts),参见 PUBLIC_CREDS.md。 - 绝不在 HTTP / SSE / executor 响应中返回裸
err.stack/err.message——始终经由buildErrorBody()或sanitizeErrorMessage()(open-sse/utils/error.ts),参见 ERROR_SANITIZATION.md。 - 绝不在传给
exec()/spawn()的 shell 脚本中字符串插值外部路径或运行时值——改经env选项传递,参考src/mitm/cert/install.ts::updateNssDatabases。 - 驳回 CodeQL / Secret-Scanning 告警时,须先核对上述模式文档是否适用,并在驳回注释中记录技术理由;
js/stack-trace-exposure在已路由sanitizeErrorMessage()的调用点属已知 CodeQL 局限,可标注false positive并引用 ERROR_SANITIZATION.md。 - 绝不暴露会创建子进程的路由(
/api/mcp/、/api/cli-tools/runtime/),除非在src/server/authz/routeGuard.ts中完成isLocalOnlyPath()分类;loopback 强制在任何认证之前无条件执行——经隧道泄漏的 JWT 也无法触发进程创建。参见 ROUTE_GUARD_TIERS.md。 - 绝不添加将提交归属到 AI 助手、LLM 或自动化账户的
Co-Authored-By尾注(如含 "Claude"、"GPT"、"Copilot"、"Bot" 的名字;anthropic.com/openai.com邮箱或 bot 拥有的noreply.github.com地址),否则会掩盖真实作者;人类贡献者——包括移植到 OmniRoute 的上游 PR 作者与 issue 报告者——可以且应该使用标准Co-authored-by: Name <email>尾注,上游移植工作流(/port-upstream-features、/port-upstream-issues)依赖这一点。
结语
OmniRoute 仓库为 AI 助手与人类开发者同时准备了一套可操作、可验证的协作契约:请求流水线把路由、翻译、执行与弹性机制分层解耦;三层运行时弹性(供应商熔断器、连接冷却、模型锁定问题)覆盖了从"整个供应商宕机"到"单条密钥失效"再到"单个模型配额耗尽"的全部故障粒度;而数据库、安全与 Git 规则则保证了 500+ 贡献者在同一仓库上的长期可维护性。以本文为地图,配合 AGENTS.md、REPOSITORY_MAP.md 与 RESILIENCE_GUIDE.md 深度阅读,即可安全高效地在这个规模庞大的 monorepo 中开展开发与排障工作。
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考