open-sse Architecture (中文 (繁體))
為什麼要使用獨立的工作區套件?
Section titled “為什麼要使用獨立的工作區套件?”基於以下幾個原因,open-sse/ 是 OmniRoute monorepo 中的一個獨立工作區:
- 可重複使用性 —
open-sse以@omniroute/open-sse的名稱發佈至 npm,因此其他專案可以獨立使用它 - 清晰的邊界 — 串流引擎與 OmniRoute 專屬的 UI/DB 層解耦
- 效能 — 此引擎沒有 Next.js 相依性,因而可在 CLI/無伺服器環境中更快地冷啟動
- 版本控制 —
open-sse可以按照自己的節奏發佈版本
"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)階段 1:路由(services/combo.ts)
Section titled “階段 1:路由(services/combo.ts)”進入點:services/combo.ts 中的 handleComboChat()
將請求解析為具體的 (provider, model, account, credentials) 元組:
- 依 ID 查詢組合(或為
auto/*模型建立虛擬組合) - 套用路由策略(優先順序、權重、輪詢等)
- 篩除狀態不健康的提供者(斷路器)
- 選擇下一個可用的目標
對於 auto/* 模型,此階段還會:
- 執行十六因素評分演算法(
services/autoCombo/) - 根據健康狀態、成本、延遲等因素選擇一組
provider+model
階段 2:轉譯(translator/)
Section titled “階段 2:轉譯(translator/)”如果來源格式(例如 OpenAI)與目標格式(例如 Claude)不同,請求將會被轉譯:
- 系統提示詞 → 系統訊息
- 工具定義 → 提供者專屬的工具格式
- 推理/思考參數 → 提供者專屬的對應參數
- 訊息角色正規化(對非 OpenAI 提供者將
developer→system)
translator/index.ts 公開以下項目:
translateRequest(body, sourceFormat, targetFormat): TranslatedRequestneedsTranslation(source, target): boolean階段 3:執行(executors/)
Section titled “階段 3:執行(executors/)”進入點:getExecutor(providerId).execute(request, options)
所有提供者都會透過 getExecutor() 工廠的後備機制使用 DefaultExecutor(executors/default.ts)。執行器會:
- 建構上游 URL(
buildUrl()) - 新增提供者專屬的標頭(
buildHeaders()) - 轉換請求主體(
transformRequest()) - 傳送 HTTP 請求,並支援重試及指數退避
- 必要時處理身分驗證重新整理(OAuth 提供者)
所有執行器都會擴充 BaseExecutor(executors/base.ts,1170 LOC),其提供:
- 通用重試邏輯
- Proxy 整合
- 斷路器整合
- 使用量記錄掛鉤
階段 4:串流(utils/stream.ts)
Section titled “階段 4:串流(utils/stream.ts)”對於串流回應,執行器會傳回一個 ReadableStream。處理常式會:
- 透過 SSE 轉換器進行管線傳輸(
createSSETransformStreamWithLogger) - 套用心跳 ping 以偵測失效的連線
- 妥善處理用戶端中斷連線(
pipeWithDisconnect) - 對非串流用戶端將 SSE → JSON
對於非串流回應,執行器會傳回已剖析的 JSON 物件,並將其原封不動地傳遞下去。
階段 5:記錄(services/usage.ts)
Section titled “階段 5:記錄(services/usage.ts)”回應後(無論成功或失敗),都會記錄使用情況:
- 回應中的
prompt_tokens、completion_tokens、cached_tokens - 根據定價資料計算的
cost_usd latency_ms、status,以及失敗時的error_class- 持久儲存至
usage_history資料表
呼叫日誌成品(若已啟用)會寫入 ${DATA_DIR}/call_logs/。
關鍵檔案深入解析
Section titled “關鍵檔案深入解析”chatCore.ts(5977 行)
Section titled “chatCore.ts(5977 行)”主要請求處理器。儘管檔案很大,但結構清晰:
// 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);}儘管它是一個巨型函式,但已整理為多個附有註解的區段,分別對應五階段管線。
combo.ts(4456 LOC)
Section titled “combo.ts(4456 LOC)”將組合解析為有序目標的路由引擎。
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) |
base.ts(1170 LOC)
Section titled “base.ts(1170 LOC)”所有 107 個執行器都會擴充的抽象執行器。其中包含:
buildUrl()— 預設 URL 建構方式(子類別可針對自訂需求覆寫)buildHeaders()— 預設標頭(驗證、內容類型)transformRequest()— 預設直接傳遞execute()— 具備重試、退避與斷路器機制的主要 HTTP 迴圈
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)`:
```tsimport { 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。
何時進行轉譯
Section titled “何時進行轉譯”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,以及systemInstructionOpenAI → Responses API:input陣列、previous_response_id狀態
已處理的邊緣情況
Section titled “已處理的邊緣情況”developer角色 → 對非 OpenAI 轉換為systemsystem角色 → 對 GLM/ERNIE 合併至第一則使用者訊息json_schema→ Gemini 的responseMimeType+responseSchematools→ 提供者特定的工具格式- 思考參數(o1、Claude)→ 提供者特定的對應項目
MCP 伺服器
Section titled “MCP 伺服器”open-sse/mcp-server/ 實作了 Model Context Protocol 伺服器:
- 110 項工具(提供者管理、組合、記憶體、快取、壓縮、代理、技能、遊戲化、外掛程式、Notion、Obsidian、本機語料庫)
- 3 種傳輸方式:stdio、SSE、Streamable HTTP
- 33 個範圍,用於細粒度授權
工具會以獨立檔案的形式註冊於 open-sse/mcp-server/tools/,每個檔案都會匯出名稱、結構描述、處理常式及範圍:
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 格式之間進行轉換。
為什麼需要獨立的轉換器?
Section titled “為什麼需要獨立的轉換器?”Responses API 是 OpenAI 的新格式,支援具狀態的對話(previous_response_id)。當用戶端傳送 Responses 請求時,OmniRoute 會:
- 在內部將 Responses → Chat Completions
- 傳送至提供者(任何支援 Chat Completions 的提供者)
- 將回應轉換回 Responses 格式
- 將轉換後的回應以串流方式傳送至用戶端
轉換器(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(位於服務中) |
依提供者移除欄位 |
提供者登錄檔結構描述
Section titled “提供者登錄檔結構描述”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 中定義
❌ 在並行請求之間變更狀態 — 僅使用請求範圍的上下文
- 建立職責明確的
open-sse/services/[serviceName].ts - 匯出主要處理函式及所有常數
- 在
tests/unit/services/[serviceName].test.mjs中新增單元測試 - 整合至
handlers/chatCore.ts中的請求管線(若與路由相關) - 若服務會影響目標選擇,請更新
combo.ts中的路由邏輯 - 在本檔案中加入文件說明
- 建立擴充
BaseExecutor的open-sse/executors/[provider].ts - 在
config/providerRegistry.ts中註冊 - 新增至
executors/index.ts工廠 - 為執行器新增單元測試
- 在
docs/architecture/ARCHITECTURE.md中加入文件說明
新增 MCP 工具
Section titled “新增 MCP 工具”- 建立或更新
open-sse/mcp-server/tools/[category]Tools.ts - 定義輸入的 Zod 結構描述
- 在
mcp-server/index.ts中註冊工具 - 新增至
mcp-server/auth/中的範圍矩陣 - 新增單元測試
- ARCHITECTURE.md — 高階架構
- CODEBASE_DOCUMENTATION.md — 工程參考資料
- REPOSITORY_MAP.md — 逐目錄說明
- AUTO-COMBO.md — 16 因子評分
- MCP-SERVER.md — MCP 伺服器
- A2A-SERVER.md — A2A 伺服器
- 原始碼:
open-sse/(400+ 個檔案,約 143K 行程式碼)
HagiCode
HagiCode 是智慧代理程式開發工作台,結合結構化工作流程、多代理程式執行與 Hero Dungeon 介面,將想法化為交付成果。
以更聰明、更快速且更有趣的智慧代理程式工作流程,打造實用的軟體。

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