跳到內容
OmniRoute source

open-sse Architecture (中文 (繁體))

為什麼要使用獨立的工作區套件?

Section titled “為什麼要使用獨立的工作區套件?”

基於以下幾個原因,open-sse/ 是 OmniRoute monorepo 中的一個獨立工作區:

  1. 可重複使用性 — open-sse 以 @omniroute/open-sse 的名稱發佈至 npm,因此其他專案可以獨立使用它
  2. 清晰的邊界 — 串流引擎與 OmniRoute 專屬的 UI/DB 層解耦
  3. 效能 — 此引擎沒有 Next.js 相依性,因而可在 CLI/無伺服器環境中更快地冷啟動
  4. 版本控制 — open-sse 可以按照自己的節奏發佈版本
package.json
"workspaces": ["open-sse"]

open-sse/
├── index.ts # 公開進入點
├── types.d.ts # 公開類型匯出
├── package.json # @omniroute/open-sse
├── config/ # 提供者設定、常數、登錄檔
├── executors/ # 各提供者的 HTTP 執行器(67 個,另加 base.ts/index.ts)
├── handlers/ # 請求處理常式(chatCore、responses 等)
├── lib/ # 內部工具
├── mcp-server/ # Model Context Protocol 伺服器
├── services/ # 約 298 個服務模組
├── transformer/ # Responses API 格式轉換器
├── translator/ # 格式轉譯(OpenAI ↔ Claude ↔ Gemini)
└── utils/ # 共用工具(記錄、錯誤、串流等)

| 目錄 | 檔案數 | 用途 | | executors/ | 167 | 各提供者的 HTTP 執行器(透過 DefaultExecutor 工廠統一) | | handlers/ | 157 | 請求進入點(chatCore、responses、embeddings) | | services/ | ~536 | 路由、快取、速率限制、重新整理等 | | translator/ | 56 | 格式轉換(OpenAI ↔ Claude ↔ Gemini) | | mcp-server/ | 44 | MCP 工具與傳輸方式 | | utils/ | ~108 | 跨領域工具(記錄、錯誤、串流) | | config/ | ~339 | 提供者設定、常數、登錄檔 |


每個 LLM 請求都會流經一個五階段管線:

┌──────────────┐
HTTP 請求 │ 1. 路由 │ 組合解析、模型選擇
(Next.js 路由) └──────┬───────┘
│
▼
┌──────────────┐
│ 2. 轉譯 │ 格式轉換(OpenAI ↔ Claude ↔ Gemini)
└──────┬───────┘
│
▼
┌──────────────┐
│ 3. 執行 │ 提供者執行器、HTTP、重試、斷路器
└──────┬───────┘
│
▼
┌──────────────┐
│ 4. 串流 │ SSE 轉換、背壓
└──────┬───────┘
│
▼
┌──────────────┐
│ 5. 記錄 │ 使用量追蹤、呼叫日誌、錯誤分類
└──────┬───────┘
│
▼
HTTP 回應(SSE 或 JSON)

進入點:services/combo.ts 中的 handleComboChat()

將請求解析為具體的 (provider, model, account, credentials) 元組:

  • 依 ID 查詢組合(或為 auto/* 模型建立虛擬組合)
  • 套用路由策略(優先順序、權重、輪詢等)
  • 篩除狀態不健康的提供者(斷路器)
  • 選擇下一個可用的目標

對於 auto/* 模型,此階段還會:

  • 執行十六因素評分演算法(services/autoCombo/)
  • 根據健康狀態、成本、延遲等因素選擇一組 provider+model

如果來源格式(例如 OpenAI)與目標格式(例如 Claude)不同,請求將會被轉譯:

  • 系統提示詞 → 系統訊息
  • 工具定義 → 提供者專屬的工具格式
  • 推理/思考參數 → 提供者專屬的對應參數
  • 訊息角色正規化(對非 OpenAI 提供者將 developer → system)

translator/index.ts 公開以下項目:

translateRequest(body, sourceFormat, targetFormat): TranslatedRequest
needsTranslation(source, target): boolean

進入點:getExecutor(providerId).execute(request, options)

所有提供者都會透過 getExecutor() 工廠的後備機制使用 DefaultExecutor(executors/default.ts)。執行器會:

  • 建構上游 URL(buildUrl())
  • 新增提供者專屬的標頭(buildHeaders())
  • 轉換請求主體(transformRequest())
  • 傳送 HTTP 請求,並支援重試及指數退避
  • 必要時處理身分驗證重新整理(OAuth 提供者)

所有執行器都會擴充 BaseExecutor(executors/base.ts,1170 LOC),其提供:

  • 通用重試邏輯
  • Proxy 整合
  • 斷路器整合
  • 使用量記錄掛鉤

對於串流回應,執行器會傳回一個 ReadableStream。處理常式會:

  • 透過 SSE 轉換器進行管線傳輸(createSSETransformStreamWithLogger)
  • 套用心跳 ping 以偵測失效的連線
  • 妥善處理用戶端中斷連線(pipeWithDisconnect)
  • 對非串流用戶端將 SSE → JSON

對於非串流回應,執行器會傳回已剖析的 JSON 物件,並將其原封不動地傳遞下去。

回應後(無論成功或失敗),都會記錄使用情況:

  • 回應中的 prompt_tokens、completion_tokens、cached_tokens
  • 根據定價資料計算的 cost_usd
  • latency_ms、status,以及失敗時的 error_class
  • 持久儲存至 usage_history 資料表

呼叫日誌成品(若已啟用)會寫入 ${DATA_DIR}/call_logs/。


主要請求處理器。儘管檔案很大,但結構清晰:

// chatCore.ts 的虛擬結構
export async function handleChat(request: NextRequest) {
// 1. 驗證 + CORS
await authenticateRequest(request);
applyCorsHeaders(response);
// 2. 請求主體驗證
const body = await parseRequestBody(request);
// 3. 格式偵測 + 轉換
const sourceFormat = detectFormat(request);
const targetFormat = getTargetFormat(providerId);
if (needsTranslation(sourceFormat, targetFormat)) {
body = translateRequest(body, sourceFormat, targetFormat);
}
// 4. 組合路由
const targets = await resolveComboTargets(comboId, body);
for (const target of targets) {
try {
const result = await executeOnTarget(target, body);
await recordUsage(result);
return result;
} catch (err) {
// 繼續嘗試下一個目標
}
}
// 5. 緊急備援
return await emergencyFallback(body);
}

儘管它是一個巨型函式,但已整理為多個附有註解的區段,分別對應五階段管線。

將組合解析為有序目標的路由引擎。

services/combo.ts
export async function handleComboChat(body, comboId): Promise<ChatResult> {
const targets = await resolveComboTargets(comboId, body);
for (const target of targets) {
try {
return await handleSingleModel(target, body);
} catch (err) {
log.warn("target failed, trying next", { target, err });
}
}
throw new ComboExhaustedError("All targets failed");
}

支援 19 種路由策略(請參閱 src/shared/constants/routingStrategies.ts):

策略 行為
priority 優先選擇第一個目標的有序清單
weighted 依各目標權重進行機率式選擇
round-robin 依序循環選擇目標
context-relay 在各目標之間交接上下文
fill-first 用滿配額後再移至下一個目標
p2c 兩個選擇的冪次法
random 均勻隨機選擇
least-used 選擇近期使用次數最少的目標
cost-optimized 優先選擇成本最低且健康的目標
reset-aware 考量提供者的重設時間窗
reset-window 基於重設時間窗的路由
headroom 優先選擇剩餘配額餘裕最多的目標
strict-random 真正均勻的隨機選擇(不套用品質權重)
auto 使用 16 因子評分(autoCombo/)
lkgp 優先選擇最近已知運作正常的提供者
context-optimized 最適合長上下文請求
fusion 同時平行分派給一組目標,再透過裁判進行綜合(fusion.ts)

所有 107 個執行器都會擴充的抽象執行器。其中包含:

  • buildUrl() — 預設 URL 建構方式(子類別可針對自訂需求覆寫)
  • buildHeaders() — 預設標頭(驗證、內容類型)
  • transformRequest() — 預設直接傳遞
  • execute() — 具備重試、退避與斷路器機制的主要 HTTP 迴圈
open-sse/executors/default.ts
export class DefaultExecutor extends BaseExecutor {
// 處理所有與 OpenAI/Anthropic 相容的提供者
// 提供者會註冊組態(URL、驗證、標頭),但共用執行器邏輯
}

提供者特定的行為(驗證標頭、基礎 URL、版本標頭)是透過提供者登錄檔設定,而非使用個別的執行器類別。

---
## 服務(117 個模組)
服務是處理器所組合的**專注、單一用途模組**。主要類別如下:
### 路由與組合
- `combo.ts` — 組合路由請求的進入點
- `services/autoCombo/` — 16 因子評分、8 種自動路由策略
- `wildcardRouter.ts` — 比對萬用字元路由(`gpt-*`)
- `modelFamilyFallback.ts` — T5 系列內部備援
### 速率限制與配額
- `rateLimitManager.ts` — 每個金鑰與提供者組合的權杖桶
- `usage.ts` — 使用量記錄
- `quotaCache.ts` — 記憶體內配額快照
### 帳戶與權杖
- `tokenRefresh.ts` — 發生 401 時重新整理 OAuth
- `accountFallback.ts` — 切換至替代帳戶
- `sessionManager.ts` — 多輪工作階段狀態
### 智慧功能
- `intentClassifier.ts` — 分類請求意圖
- `taskAwareRouter.ts` — 依工作類型進行路由
- `thinkingBudget.ts` — 配置思考權杖
- `contextManager.ts` — 注入路由情境
### 韌性
- `resilience.ts` — 重試、退避與斷路器協調
- `emergencyFallback.ts` — 最後手段的備援
- `modelDeprecation.ts` — 自動路由至後繼模型
### 狀態
- `signatureCache.ts` — 依請求簽章去除重複項目
- `volumeDetector.ts` — 負載卸除
- `contextHandoff.ts` — 工作階段序列化
### 壓縮
- `compression/`(子目錄)— 完整的壓縮管線
- 39 個檔案,涵蓋引擎、規則套件與配接器
### 技能
- (請參閱 [SKILLS.md](./SKILLS.md))
### 記憶體
- (請參閱 [MEMORY.md](./MEMORY.md))
---
## 執行器(75+ 個檔案)
每個提供者各有一個檔案。它們全都擴充 `BaseExecutor`,並覆寫不同之處。
### 常見模式
提供者透過 `getExecutor(providerId)` 解析,該函式會傳回已設定的執行器。與 OpenAI/Anthropic 相容的提供者使用 `DefaultExecutor`(`executors/default.ts`)。提供者特定行為(基礎 URL、驗證標頭、API 版本)於 `open-sse/config/providers/` 中設定,而請求主體轉換則於 `open-sse/translator/` 中處理。
**自訂 URL** 透過提供者設定指定:
```ts
// open-sse/config/providers/ 中的提供者設定
export default {
id: "together",
baseURL: "https://api.together.xyz/v1/chat/completions",
}

自訂驗證 透過提供者登錄檔的驗證設定(API 金鑰、OAuth、標頭設定檔)處理。

自訂請求主體轉換(例如 Anthropic 將 system 與 messages 分開)會依提供者註冊於 open-sse/translator/ 中。

### 執行器工廠
`executors/index.ts` 會匯出 `getExecutor(providerId)`:
```ts
import { getExecutor } from "@omniroute/open-sse/executors";
const executor = getExecutor("anthropic");
const result = await executor.execute({
model: "claude-sonnet-4-5",
messages: [...],
});

解析會經由 ExecutorRegistry(executors/registry.ts)進行:每個專用執行器都宣告於 executors/index.ts 的內建表格中,並在模組載入時透過 registerExecutor(alias, instance) 註冊;getExecutor() 會查詢登錄檔,對於任何沒有專用項目的提供者,則退回使用已記憶化的 DefaultExecutor。完整的別名 → 執行器對應關係由黃金測試 tests/unit/executor-map-golden.test.ts 描述。


在 3 種格式之間進行轉譯:OpenAI、Anthropic、Gemini,以及新的 Responses API。

import { needsTranslation, translateRequest } from "@omniroute/open-sse/translator";
if (needsTranslation(sourceFormat, targetFormat)) {
body = translateRequest(body, sourceFormat, targetFormat);
}

常見轉譯:

  • OpenAI → Anthropic:獨立的 system 欄位、x-api-key 標頭
  • OpenAI → Gemini:使用 contents 取代 messages,以及 systemInstruction
  • OpenAI → Responses API:input 陣列、previous_response_id 狀態
  • developer 角色 → 對非 OpenAI 轉換為 system
  • system 角色 → 對 GLM/ERNIE 合併至第一則使用者訊息
  • json_schema → Gemini 的 responseMimeType + responseSchema
  • tools → 提供者特定的工具格式
  • 思考參數(o1、Claude)→ 提供者特定的對應項目

open-sse/mcp-server/ 實作了 Model Context Protocol 伺服器:

  • 110 項工具(提供者管理、組合、記憶體、快取、壓縮、代理、技能、遊戲化、外掛程式、Notion、Obsidian、本機語料庫)
  • 3 種傳輸方式:stdio、SSE、Streamable HTTP
  • 33 個範圍,用於細粒度授權

工具會以獨立檔案的形式註冊於 open-sse/mcp-server/tools/,每個檔案都會匯出名稱、結構描述、處理常式及範圍:

open-sse/mcp-server/tools/getHealth.ts
import { z } from "zod";
export default {
name: "omniroute_get_health",
description: "Get system health snapshot",
scope: "read:health",
inputSchema: z.object({}),
handler: async (_args, ctx) => {
return await getSystemHealth();
},
};
// stdio(CLI 用法)
startMcpStdio(server);
// SSE(以 HTTP 為基礎的串流)
startMcpSse(server, port);
// Streamable HTTP(現代 MCP)
startMcpStreamable(server, port);

每次工具呼叫都會經過範圍檢查(open-sse/mcp-server/auth/):

if (!hasScope(apiKey, "providers:read")) {
throw new Error("Insufficient scope");
}

open-sse/transformer/ 會在 Chat Completions 與 Responses API 格式之間進行轉換。

Responses API 是 OpenAI 的新格式,支援具狀態的對話(previous_response_id)。當用戶端傳送 Responses 請求時,OmniRoute 會:

  1. 在內部將 Responses → Chat Completions
  2. 傳送至提供者(任何支援 Chat Completions 的提供者)
  3. 將回應轉換回 Responses 格式
  4. 將轉換後的回應以串流方式傳送至用戶端

轉換器(transformer/responsesTransformer.ts)提供:

createResponsesApiTransformStream(): TransformStream

這會處理:

  • response.output_item.added 事件
  • response.output_text.delta 事件
  • response.completed 事件
  • 工具呼叫對應(function_call ↔ tool_calls)

open-sse/config/ 包含設定層:

檔案 用途
providerRegistry.ts 建構於 352 個提供者目錄之上的聊天模型登錄檔
providerModels.ts 模型別名、格式對應
constants.ts 逾時、限制、狀態碼
defaultThinkingSignature.ts 預設 Claude 思考簽章
modelStrip.ts(位於服務中) 依提供者移除欄位
interface ProviderConfig {
id: string;
name: string;
baseUrl: string;
authType: "bearer" | "api-key" | "oauth" | "cookie";
executorClass: string;
defaultModel: string;
capabilities: ProviderCapabilities;
models: ModelDefinition[];
}

模組載入時的 Zod 驗證可確保所有提供者設定均有效。


路由引擎有嚴格的效能預算:

操作 目標 測量條件
組合解析 <10ms 針對 50 個目標
速率限制檢查 <1ms 記憶體內權杖桶
模型系列備援 <5ms 已快取的系列定義
請求路由分派 <2ms 熱路徑
路由熱路徑中不得有阻塞式 I/O — 全部採用非同步處理

❌ 在 combo.ts 中進行同步資料庫呼叫 — 預先計算並快取 ❌ 在處理常式中實作重試邏輯 — 使用韌性服務提供的 retry() ❌ 直接存取提供者設定 — 使用 providerRegistry 的 getter ❌ 硬編碼備援鏈 — 在 modelFamilyFallback.ts 中定義 ❌ 在並行請求之間變更狀態 — 僅使用請求範圍的上下文


  1. 建立職責明確的 open-sse/services/[serviceName].ts
  2. 匯出主要處理函式及所有常數
  3. 在 tests/unit/services/[serviceName].test.mjs 中新增單元測試
  4. 整合至 handlers/chatCore.ts 中的請求管線(若與路由相關)
  5. 若服務會影響目標選擇,請更新 combo.ts 中的路由邏輯
  6. 在本檔案中加入文件說明
  1. 建立擴充 BaseExecutor 的 open-sse/executors/[provider].ts
  2. 在 config/providerRegistry.ts 中註冊
  3. 新增至 executors/index.ts 工廠
  4. 為執行器新增單元測試
  5. 在 docs/architecture/ARCHITECTURE.md 中加入文件說明
  1. 建立或更新 open-sse/mcp-server/tools/[category]Tools.ts
  2. 定義輸入的 Zod 結構描述
  3. 在 mcp-server/index.ts 中註冊工具
  4. 新增至 mcp-server/auth/ 中的範圍矩陣
  5. 新增單元測試


OmniRoute 原始碼 (a58000c7685f)

HagiCode

HagiCode 是智慧代理程式開發工作台,結合結構化工作流程、多代理程式執行與 Hero Dungeon 介面,將想法化為交付成果。

以更聰明、更快速且更有趣的智慧代理程式工作流程,打造實用的軟體。

HagiCode 淺色主題介面畫面
  • Smart結構化流程將意圖轉化為從構想到交付的可執行步驟。
  • Efficient多代理程式工作流程讓研究、實作與審查並行進行。
  • FunHero Dungeon 讓長時間的程式協作更直覺、更有參與感。
造訪 HagiCode