@yzj01/llm-router 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +14 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +14 -7
- package/dist/index.js.map +1 -1
- package/dist/{proxy-Co87mKak.d.ts → proxy-B7j2DcEK.d.ts} +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/config-loader.ts","../src/proxy-config-resolver.ts","../src/proxy.ts","../src/router/rules.ts","../src/router/selector.ts","../src/router/strategy.ts","../src/router/config.ts","../src/router/trace.ts","../src/router/index.ts","../src/config.ts","../src/model-registry.ts","../src/public-model-resolver.ts","../src/session.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\n\nimport { loadConfig } from \"./config-loader.js\";\nimport { resolveProxyConfig } from \"./proxy-config-resolver.js\";\nimport { startProxy as startProxyImpl, VERSION } from \"./proxy.js\";\nimport type { ProxyHandle, ProxyOptions } from \"./proxy.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CliRuntime = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n exit: (code: number) => void;\n onSignal: (signal: string, handler: () => void) => void;\n startProxy: (options: ProxyOptions) => Promise<ProxyHandle>;\n};\n\nexport type ParsedArgs = {\n help: boolean;\n version: boolean;\n config?: string;\n port?: number;\n baseUrl?: string;\n apiKey?: string;\n unknown: string[];\n};\n\n// ---------------------------------------------------------------------------\n// Argument parsing\n// ---------------------------------------------------------------------------\n\nexport function parseArgs(rawArgs: string[]): ParsedArgs {\n const result: ParsedArgs = { help: false, version: false, unknown: [] };\n\n for (let i = 0; i < rawArgs.length; i++) {\n const arg = rawArgs[i]!;\n\n switch (arg) {\n case \"--help\":\n case \"-h\":\n result.help = true;\n break;\n\n case \"--version\":\n case \"-v\":\n result.version = true;\n break;\n\n case \"--port\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n break;\n }\n const port = Number.parseInt(val, 10);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n result.unknown.push(arg, val);\n } else {\n result.port = port;\n }\n break;\n }\n\n case \"--config\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n } else {\n result.config = val;\n }\n break;\n }\n\n case \"--api-key\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n } else {\n result.apiKey = val;\n }\n break;\n }\n\n case \"--base-url\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n } else {\n result.baseUrl = val;\n }\n break;\n }\n\n default:\n result.unknown.push(arg);\n break;\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Help text\n// ---------------------------------------------------------------------------\n\nfunction helpText(): string {\n return `llm-router v${VERSION}\n\nUsage:\n llm-router --config <path> Start the local proxy with config\n llm-router --config <path> --port 9000 Listen on a custom port\n\nOptions:\n --help, -h Show help\n --version, -v Show version\n --config <path> Configuration file path (required)\n --port <number> Override proxy port\n --api-key <key> Override API key\n --base-url <url> Override upstream API base URL`;\n}\n\n// ---------------------------------------------------------------------------\n// Default runtime\n// ---------------------------------------------------------------------------\n\nconst defaultRuntime: CliRuntime = {\n log: console.log,\n error: console.error,\n exit: (code) => process.exit(code),\n onSignal: (signal, handler) => process.on(signal, handler),\n startProxy: startProxyImpl,\n};\n\n// ---------------------------------------------------------------------------\n// CLI execution\n// ---------------------------------------------------------------------------\n\nexport async function runCli(rawArgs: string[], runtime: Partial<CliRuntime> = {}): Promise<void> {\n const rt: CliRuntime = { ...defaultRuntime, ...runtime };\n const args = parseArgs(rawArgs);\n\n // --help\n if (args.help) {\n rt.log(helpText());\n rt.exit(0);\n return;\n }\n\n // --version\n if (args.version) {\n rt.log(`v${VERSION}`);\n rt.exit(0);\n return;\n }\n\n // Unknown commands\n if (args.unknown.length > 0) {\n for (const cmd of args.unknown) {\n rt.error(`Unsupported command: ${cmd}`);\n }\n rt.exit(1);\n return;\n }\n\n // --config is required\n if (!args.config) {\n rt.error(\"Missing required argument: --config <path>\");\n rt.exit(1);\n return;\n }\n\n // Load config from file\n const rawConfig = loadConfig({ kind: \"file\", path: args.config });\n\n // Resolve proxy config with CLI overrides\n const proxyConfig = resolveProxyConfig(rawConfig.proxy, {\n port: args.port,\n upstreamUrl: args.baseUrl,\n apiKey: args.apiKey,\n });\n const runtimeConfig = {\n ...rawConfig,\n proxy: proxyConfig,\n };\n\n // Start proxy\n const handle = await rt.startProxy({\n config: runtimeConfig,\n });\n rt.log(`llm-router listening on http://127.0.0.1:${handle.port}`);\n\n // Graceful shutdown\n const shutdown = async () => {\n rt.log(\"\\nShutting down...\");\n await handle.close();\n rt.exit(0);\n };\n\n rt.onSignal(\"SIGINT\", () => {\n void shutdown();\n });\n rt.onSignal(\"SIGTERM\", () => {\n void shutdown();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Main detection\n// ---------------------------------------------------------------------------\n\nexport function isMain(): boolean {\n return process.argv[1] === fileURLToPath(import.meta.url);\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\nif (isMain()) {\n void runCli(process.argv.slice(2));\n}\n","import { readFileSync } from \"node:fs\";\nimport type { ConfigSource, RawConfig } from \"./config-schema.js\";\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction assertFiniteNumber(value: unknown, path: string): asserts value is number {\n if (!isFiniteNumber(value)) {\n throw new Error(`${path} must be a finite number`);\n }\n}\n\nfunction assertPublicModelMetadata(value: unknown, path: string): void {\n if (!value || typeof value !== \"object\") {\n throw new Error(`${path}.metadata is required`);\n }\n\n const metadata = value as Record<string, unknown>;\n const metadataPath = `${path}.metadata`;\n\n for (const key of [\"name\", \"reasoning\", \"contextWindow\", \"maxTokens\", \"cost\"]) {\n if (!hasOwn(metadata, key)) {\n throw new Error(`${metadataPath}.${key} is required`);\n }\n }\n\n if (typeof metadata.name !== \"string\") {\n throw new Error(`${metadataPath}.name must be a string`);\n }\n\n if (typeof metadata.reasoning !== \"boolean\") {\n throw new Error(`${metadataPath}.reasoning must be a boolean`);\n }\n\n if (typeof metadata.contextWindow !== \"number\") {\n throw new Error(`${metadataPath}.contextWindow must be a number`);\n }\n\n if (typeof metadata.maxTokens !== \"number\") {\n throw new Error(`${metadataPath}.maxTokens must be a number`);\n }\n\n if (!metadata.cost || typeof metadata.cost !== \"object\") {\n throw new Error(`${metadataPath}.cost must be an object`);\n }\n\n const cost = metadata.cost as Record<string, unknown>;\n const costPath = `${metadataPath}.cost`;\n\n for (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\"]) {\n if (!hasOwn(cost, key)) {\n throw new Error(`${costPath}.${key} is required`);\n }\n }\n\n for (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\"] as const) {\n if (typeof cost[key] !== \"number\") {\n throw new Error(`${costPath}.${key} must be a number`);\n }\n }\n}\n\nfunction assertAliasPublicModel(publicModels: RawConfig[\"publicModels\"], id: string, path: string): void {\n const publicModel = publicModels[id];\n\n if (!publicModel) {\n throw new Error(`${path} references unknown publicModel: ${id}`);\n }\n\n if (publicModel.kind !== \"alias\") {\n throw new Error(`${path} must reference a publicModel with kind: \"alias\"`);\n }\n}\n\n/**\n * 加载并校验配置文件\n * @param source 配置源:内联对象或文件路径\n * @returns 校验通过的配置对象\n * @throws {Error} 如果配置不合法(重复 ID、引用不存在、缺少 auto 等)\n */\nexport function loadConfig(source: ConfigSource): RawConfig {\n const raw = source.kind === \"inline\" ? source.config : JSON.parse(readFileSync(source.path, \"utf-8\"));\n validateConfig(raw);\n return raw;\n}\n\n/**\n * 校验配置完整性和引用关系\n *\n * 校验项:\n * - models[].id 唯一性\n * - publicModels.auto 必须是 router 且 metadata 完整\n * - publicModels[*].candidates[] 引用必须在 models 中存在\n * - publicModels[*].candidates[] 非空(仅 alias)\n * - routing.tiers[*].publicModel / fallback[] 只能引用 alias publicModel\n * - 四个 tier 必须完整声明\n * - proxy.port 是 1-65535 整数\n * - proxy.headers 值都是字符串\n *\n * @param config 待校验的配置对象\n * @throws {Error} 如果校验失败,错误信息包含具体字段和期望值\n */\nfunction validateConfig(config: RawConfig): void {\n const modelIds = new Set<string>();\n\n for (const model of config.models) {\n if (modelIds.has(model.id)) {\n throw new Error(`Duplicate model ID: ${model.id}`);\n }\n\n modelIds.add(model.id);\n }\n\n const auto = config.publicModels.auto;\n if (!auto || auto.kind !== \"router\") {\n throw new Error('publicModels must contain \"auto\" with kind: \"router\"');\n }\n assertPublicModelMetadata(auto.metadata, \"publicModels.auto\");\n\n for (const [publicModelId, publicModel] of Object.entries(config.publicModels)) {\n if (publicModel.kind === \"router\") {\n if (publicModelId !== \"auto\") {\n throw new Error(`publicModels.${publicModelId}: only auto may use kind: \"router\"`);\n }\n assertPublicModelMetadata(publicModel.metadata, `publicModels.${publicModelId}`);\n continue;\n }\n\n if (!Array.isArray(publicModel.candidates) || publicModel.candidates.length === 0) {\n throw new Error(`publicModels.${publicModelId}.candidates must not be empty`);\n }\n\n for (const candidate of publicModel.candidates) {\n if (!modelIds.has(candidate)) {\n throw new Error(`Unknown candidate '${candidate}' in publicModels.${publicModelId}`);\n }\n }\n\n if (publicModel.metadata != null) {\n assertPublicModelMetadata(publicModel.metadata, `publicModels.${publicModelId}`);\n }\n }\n\n for (const tier of [\"SIMPLE\", \"MEDIUM\", \"COMPLEX\", \"REASONING\"] as const) {\n if (!config.routing.tiers[tier]) {\n throw new Error(`routing.tiers.${tier} is required`);\n }\n }\n\n for (const [tier, tierConfig] of Object.entries(config.routing.tiers)) {\n assertAliasPublicModel(config.publicModels, tierConfig.publicModel, `routing.tiers.${tier}.publicModel`);\n\n for (const fallbackId of tierConfig.fallback ?? []) {\n assertAliasPublicModel(config.publicModels, fallbackId, `routing.tiers.${tier}.fallback`);\n }\n }\n\n if (hasOwn(config.routing as object, \"tierBoundaries\")) {\n const tierBoundaries = config.routing.tierBoundaries;\n\n if (!tierBoundaries || typeof tierBoundaries !== \"object\" || Array.isArray(tierBoundaries)) {\n throw new Error(\"routing.tierBoundaries must be an object\");\n }\n\n const { simpleMedium, mediumComplex, complexReasoning } = tierBoundaries;\n\n assertFiniteNumber(simpleMedium, \"routing.tierBoundaries.simpleMedium\");\n assertFiniteNumber(mediumComplex, \"routing.tierBoundaries.mediumComplex\");\n assertFiniteNumber(complexReasoning, \"routing.tierBoundaries.complexReasoning\");\n\n if (!(simpleMedium <= mediumComplex && mediumComplex <= complexReasoning)) {\n throw new Error(\n \"routing.tierBoundaries must satisfy simpleMedium <= mediumComplex <= complexReasoning\"\n );\n }\n }\n\n if (hasOwn(config.routing as object, \"confidenceThreshold\")) {\n assertFiniteNumber(config.routing.confidenceThreshold, \"routing.confidenceThreshold\");\n\n if (config.routing.confidenceThreshold < 0 || config.routing.confidenceThreshold > 1) {\n throw new Error(\"routing.confidenceThreshold must be between 0 and 1\");\n }\n }\n\n if (!Number.isInteger(config.proxy.port) || config.proxy.port < 1 || config.proxy.port > 65535) {\n throw new Error(`proxy.port must be an integer between 1-65535, got: ${config.proxy.port}`);\n }\n\n if (config.proxy.headers) {\n for (const [key, value] of Object.entries(config.proxy.headers)) {\n if (typeof value !== \"string\") {\n throw new Error(`proxy.headers['${key}'] must be a string, got: ${typeof value}`);\n }\n }\n }\n}\n","import type { ProxyConfig } from \"./config-schema.js\";\n\n/**\n * 覆盖配置(来自 CLI flag 或插件 pluginConfig),所有字段可选\n *\n * 优先级规则:\n * - CLI flag 优先级最高(--port, --api-key, --headers 等)\n * - 插件 pluginConfig 次之(pluginConfig.port, pluginConfig.upstreamUrl 等)\n * - 配置文件最低\n *\n * 调用方负责按优先级合并多个覆盖源后再传入此函数。\n */\nexport interface ProxyConfigOverrides {\n port?: number;\n upstreamUrl?: string;\n apiKey?: string;\n headers?: Record<string, string>;\n trace?: \"off\" | \"summary\" | \"debug\";\n}\n\nexport type { ProxyConfig };\n\n/**\n * 将基础配置与覆盖配置合并,返回最终的 ProxyConfig。\n *\n * 覆盖优先级:overrides > baseConfig(逐字段覆盖)。\n *\n * headers 合并规则:\n * - 结果为 `{ ...baseConfig.headers, ...overrides.headers }`\n * - overrides.headers 中的 key 优先覆盖 baseConfig.headers 中相同的 key\n * - baseConfig.headers 中未被覆盖的 key 原样保留\n * - 若两者均无 headers,结果为 undefined\n *\n * @param baseConfig 来自配置文件的基础 proxy 配置\n * @param overrides 来自 CLI flag 或插件 pluginConfig 的覆盖配置(可选)\n * @returns 合并后的 ProxyConfig\n */\nexport function resolveProxyConfig(\n baseConfig: ProxyConfig,\n overrides: ProxyConfigOverrides = {},\n): ProxyConfig {\n const mergedHeaders = mergeHeaders(baseConfig.headers, overrides.headers);\n\n return {\n port: overrides.port ?? baseConfig.port,\n upstreamUrl: overrides.upstreamUrl ?? baseConfig.upstreamUrl,\n apiKey: overrides.apiKey ?? baseConfig.apiKey,\n headers: mergedHeaders,\n trace: overrides.trace ?? baseConfig.trace,\n };\n}\n\n/**\n * 合并两个 headers 对象,override 优先。\n *\n * @param base 基础 headers(来自配置文件)\n * @param override 覆盖 headers(来自 CLI flag 或 pluginConfig)\n * @returns 合并后的 headers,若两者均为 undefined 则返回 undefined\n */\nfunction mergeHeaders(\n base: Record<string, string> | undefined,\n override: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n if (base === undefined && override === undefined) {\n return undefined;\n }\n return { ...base, ...override };\n}\n","import http from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport type { RouterConfig } from \"./config.js\";\nimport { resolveConfig } from \"./config.js\";\nimport type { PublicModelConfig, RawConfig } from \"./config-schema.js\";\nimport type { TierEntry } from \"./config-schema.js\";\nimport { createModelRegistry } from \"./model-registry.js\";\nimport type { ModelRegistry } from \"./model-registry.js\";\nimport { resolvePublicModelCandidate } from \"./public-model-resolver.js\";\nimport {\n DEFAULT_ROUTING_CONFIG,\n buildTraceSummary,\n emitRouteTrace,\n getPromptPreview,\n route,\n resolveTraceWriter,\n} from \"./router/index.js\";\nimport type {\n ModelPricing,\n RouteTraceLog,\n RoutingConfig,\n RoutingDecision,\n TraceAttempt,\n TraceLogger,\n TraceReason,\n TraceSessionAction,\n Tier,\n TierConfig,\n} from \"./router/index.js\";\nimport type { RouterOptions } from \"./router/types.js\";\nimport type { SessionConfig } from \"./session.js\";\nimport { deriveSessionId, SessionStore } from \"./session.js\";\n\nexport const VERSION = \"1.0.1\";\n\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n \"host\",\n \"content-length\",\n]);\n\nconst RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);\n/**\n * 运行时韧性保护默认值。\n *\n * 这里统一集中定义,方便 CLI / 测试 / 未来配置入口共享同一组基线。\n */\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024;\nconst DEFAULT_BODY_READ_TIMEOUT_MS = 30_000;\nconst DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS = 300_000;\n\n/**\n * `startProxy()` 实际运行时使用的完整限制集合。\n *\n * 这里把每个限制都解析成必填字段,避免请求处理路径再去分支判断默认值。\n */\nexport type ProxyRuntimeLimits = {\n /**\n * 允许单个客户端请求体占用的最大 UTF-8 字节数。\n */\n maxBodyBytes: number;\n /**\n * 允许客户端在请求体读取阶段占用连接的最长时间。\n */\n bodyReadTimeoutMs: number;\n /**\n * 单次上游请求允许占用的最长时间。\n *\n * 超时后 proxy 会主动 abort 当前 fetch,并把它转换成面向客户端的 504。\n */\n upstreamRequestTimeoutMs: number;\n};\n\n/**\n * 调用方可按需覆盖的运行时限制输入。\n */\nexport type ProxyRuntimeLimitInput = Partial<ProxyRuntimeLimits>;\n\n/**\n * Proxy 在未显式传参时采用的默认运行时限制。\n */\nexport const DEFAULT_RUNTIME_LIMITS: ProxyRuntimeLimits = {\n maxBodyBytes: DEFAULT_MAX_BODY_BYTES,\n bodyReadTimeoutMs: DEFAULT_BODY_READ_TIMEOUT_MS,\n upstreamRequestTimeoutMs: DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS,\n};\n\n/**\n * 归一化运行时限制输入,确保请求处理路径拿到的是完整、稳定的配置对象。\n */\nexport function resolveRuntimeLimits(\n input?: ProxyRuntimeLimitInput,\n): ProxyRuntimeLimits {\n return {\n maxBodyBytes: input?.maxBodyBytes ?? DEFAULT_RUNTIME_LIMITS.maxBodyBytes,\n bodyReadTimeoutMs:\n input?.bodyReadTimeoutMs ?? DEFAULT_RUNTIME_LIMITS.bodyReadTimeoutMs,\n upstreamRequestTimeoutMs:\n input?.upstreamRequestTimeoutMs ??\n DEFAULT_RUNTIME_LIMITS.upstreamRequestTimeoutMs,\n };\n}\n\n/**\n * 当前版本对外暴露的可请求 model 白名单。\n *\n * `publicModels` 仍然可以包含 `flash` / `pro` / `lite` / `think` 等 alias,\n * 但它们只作为 Router 内部语义输出,不接受客户端显式请求。\n */\nconst REQUESTABLE_PUBLIC_MODELS = new Set([\"auto\"]);\nconst PUBLIC_HEADER_PREFIXES = [\"x-xy-router-\"] as const;\n\n/**\n * 启动本地 HTTP proxy 所需的全部输入。\n */\nexport type ProxyOptions = {\n config: RawConfig;\n traceLogger?: TraceLogger;\n session?: Partial<SessionConfig>;\n runtimeLimits?: ProxyRuntimeLimitInput;\n};\n\n/**\n * 已启动 proxy 的可关闭句柄。\n */\nexport type ProxyHandle = {\n port: number;\n baseUrl: string;\n close: () => Promise<void>;\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 请求体读取阶段使用的结构化错误。\n *\n * 它把“HTTP 应返回什么状态码”与“内部异常控制流”绑定在一起,方便边界层统一\n * 转换成 OpenAI-compatible 错误响应。\n */\nclass BodyReadError extends Error {\n readonly statusCode: number;\n\n constructor(statusCode: number, message: string) {\n super(message);\n this.name = \"BodyReadError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * 判断未知异常是否来自请求体读取阶段,避免把 413 一类客户端错误误报成 502。\n */\nfunction isBodyReadError(error: unknown): error is BodyReadError {\n return error instanceof BodyReadError;\n}\n\n/**\n * 对请求体读取失败返回结构化客户端错误,并在响应刷出后关闭当前连接。\n *\n * 这里不能在 `readBody()` 的超时/超限分支里立刻 `req.destroy()`,否则客户端往往只会\n * 看到底层 `ECONNRESET`,拿不到 OpenAI-compatible JSON 错误。正确顺序是:\n * 1. 先把 408/413 之类的结构化错误写回客户端;\n * 2. 再把这个“请求体尚未完整消费”的连接标记为不可复用;\n * 3. 等响应完成后销毁 request/socket,避免半读状态的连接进入 keep-alive 池。\n */\nfunction writeBodyReadErrorAndCloseRequest(\n req: IncomingMessage,\n res: ServerResponse,\n error: BodyReadError,\n): void {\n res.shouldKeepAlive = false;\n res.setHeader(\"connection\", \"close\");\n res.once(\"finish\", () => {\n if (!req.destroyed) {\n req.destroy();\n }\n });\n writeOpenAiError(res, error.statusCode, error.message);\n}\n\n/**\n * 读取完整请求体文本。\n *\n * 同时负责:\n * - `maxBodyBytes`:限制单个请求可读入的总字节数;\n * - `bodyReadTimeoutMs`:限制客户端在“持续占着连接但迟迟不发完 body”时可占用的时间。\n *\n * 超时分支只 reject,不在这里直接 `req.destroy()`。这样外层 `proxyChat()` 仍有机会\n * 返回结构化 408 JSON,而不是让客户端只看到 `ECONNRESET`。\n */\nfunction readBody(\n req: IncomingMessage,\n limits: ProxyRuntimeLimits,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n let bodyBytes = 0;\n let settled = false;\n req.setEncoding(\"utf8\");\n const timeout = setTimeout(() => {\n rejectOnce(new BodyReadError(408, \"Request body read timeout\"));\n }, limits.bodyReadTimeoutMs);\n\n const cleanup = (): void => {\n clearTimeout(timeout);\n req.off(\"data\", onData);\n req.off(\"end\", onEnd);\n req.off(\"error\", onError);\n };\n\n const rejectOnce = (error: unknown): void => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(error);\n };\n\n const resolveOnce = (value: string): void => {\n if (settled) return;\n settled = true;\n cleanup();\n resolve(value);\n };\n\n const onData = (chunk: string): void => {\n if (settled) return;\n\n bodyBytes += Buffer.byteLength(chunk, \"utf8\");\n if (bodyBytes > limits.maxBodyBytes) {\n rejectOnce(new BodyReadError(413, \"Payload Too Large\"));\n return;\n }\n\n body += chunk;\n };\n\n const onEnd = (): void => resolveOnce(body);\n const onError = (error: Error): void => rejectOnce(error);\n\n req.on(\"data\", onData);\n req.on(\"end\", onEnd);\n req.on(\"error\", onError);\n });\n}\n\n/**\n * 从 OpenAI / OpenClaw 风格消息数组里提取路由真正关心的文本视图。\n *\n * - `text`:非 system 消息拼接后的总文本\n * - `routeText`:优先取最后一段 user 指令,避免 OpenClaw CLI transcript 干扰\n * - `system`:system prompt 聚合结果\n * - `openingText`:第一段非空文本,便于未来扩展首轮特征\n */\ntype ExtractedPrompt = {\n text: string;\n routeText: string;\n system?: string;\n openingText: string;\n};\n\nconst OPENCLAW_CLI_TURN_PATTERN =\n /(?:^|\\n)\\[[^\\]\\n]+?\\]\\s+([\\s\\S]*?)(?=(?:\\n\\[[^\\]\\n]+?\\]\\s+)|$)/g;\n\n/**\n * 从 OpenClaw CLI transcript 中截取最后一轮真实 user 文本,减少历史回显内容\n * 对路由打分的污染。\n */\nfunction extractRouteTextFromUserMessage(text: string): string {\n const matches = [...text.matchAll(OPENCLAW_CLI_TURN_PATTERN)];\n const last = matches.at(-1)?.[1]?.trim();\n return last || text;\n}\n\n/**\n * 从 messages 中抽取 Router 需要的 prompt 视图。\n */\nfunction extractPrompt(messages: unknown[]): ExtractedPrompt {\n const parts: string[] = [];\n let system: string | undefined;\n let openingText = \"\";\n let lastUserText = \"\";\n\n for (const msg of messages) {\n if (!msg || typeof msg !== \"object\") continue;\n\n const role: unknown = (msg as Record<string, unknown>).role;\n const content: unknown = (msg as Record<string, unknown>).content;\n\n let text = \"\";\n if (typeof content === \"string\") {\n text = content;\n } else if (Array.isArray(content)) {\n text = content\n .filter(\n (p): p is { type: string; text?: string } =>\n typeof p === \"object\" &&\n p !== null &&\n (p as Record<string, unknown>).type === \"text\",\n )\n .map((p) => (typeof p.text === \"string\" ? p.text : \"\"))\n .join(\" \");\n }\n\n if (role === \"system\") {\n system = system ? `${system}\\n${text}` : text;\n } else {\n parts.push(text);\n if (role === \"user\" && text.trim()) {\n lastUserText = extractRouteTextFromUserMessage(text);\n }\n if (!openingText && text.trim()) {\n openingText = text;\n }\n }\n }\n\n const text = parts.join(\" \");\n return { text, routeText: lastUserText || text, system, openingText };\n}\n\n// ---------------------------------------------------------------------------\n// Upstream headers\n// ---------------------------------------------------------------------------\n\n/**\n * 生成转发到上游的请求头。\n *\n * 优先级:\n * 1. 来自原始请求的非 hop-by-hop header\n * 2. `config.proxy.headers` 覆盖同名 header\n * 3. 若仍无 Authorization,则补上 `config.proxy.apiKey`\n */\nfunction buildUpstreamHeaders(\n req: IncomingMessage,\n cfg: RouterConfig,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n\n const setHeader = (key: string, value: string): void => {\n const existingKey = Object.keys(headers).find(\n (candidate) => candidate.toLowerCase() === key.toLowerCase(),\n );\n if (existingKey) {\n delete headers[existingKey];\n }\n headers[key] = value;\n };\n\n for (const [key, value] of Object.entries(req.headers)) {\n if (HOP_BY_HOP.has(key.toLowerCase())) continue;\n if (value !== undefined) {\n setHeader(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n }\n\n // Custom headers override request headers\n for (const [key, value] of Object.entries(cfg.headers)) {\n setHeader(key, value);\n }\n\n // Add auth if missing (header names are case-insensitive per HTTP spec)\n const hasAuth = Object.keys(headers).some(\n (k) => k.toLowerCase() === \"authorization\",\n );\n if (!hasAuth && cfg.apiKey) {\n headers.authorization = `Bearer ${cfg.apiKey}`;\n }\n\n // Ensure content-type is set\n if (!Object.keys(headers).some((k) => k.toLowerCase() === \"content-type\")) {\n setHeader(\"content-type\", \"application/json\");\n }\n\n return headers;\n}\n\n// ---------------------------------------------------------------------------\n// Response helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 统一写 JSON 响应,且只在 headers 尚未发送时生效。\n */\nfunction writeJson(res: ServerResponse, status: number, body: unknown): void {\n if (!res.headersSent) {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n }\n}\n\n/**\n * 复制上游响应头,并剔除不应继续透传到客户端的 header。\n *\n * 这里会主动移除任何上游已带的公开路由头,确保最终对外头部完全由当前\n * Router 实例生成。\n */\nfunction copyResponseHeaders(\n response: Response,\n extraHeaders: Record<string, string>,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n const lower = key.toLowerCase();\n if (HOP_BY_HOP.has(lower)) continue;\n if (PUBLIC_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix)))\n continue;\n headers[key] = value;\n }\n Object.assign(headers, extraHeaders);\n return headers;\n}\n\n/**\n * 透明转发上游响应体(支持流式 body)。\n */\nasync function streamResponse(\n response: Response,\n res: ServerResponse,\n): Promise<void> {\n if (response.body) {\n for await (const chunk of response.body as AsyncIterable<Uint8Array>) {\n res.write(chunk);\n }\n }\n res.end();\n}\n\n/**\n * 输出 OpenAI-compatible 错误结构,可选附带路由诊断头。\n */\nfunction writeOpenAiError(\n res: ServerResponse,\n status: number,\n message: string,\n type: string = \"invalid_request_error\",\n code: string | null = null,\n headers?: Record<string, string>,\n): void {\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n res.setHeader(key, value);\n }\n }\n\n writeJson(res, status, {\n error: {\n message,\n type,\n param: null,\n code,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Upstream fetch with retryable detection\n// ---------------------------------------------------------------------------\n\ntype LinkedAbortSignal = {\n signal: AbortSignal;\n cleanup: () => void;\n};\n\n/**\n * 把多个 abort signal 聚合成一个 signal,供单个 `fetch()` 统一消费。\n *\n * 这里显式返回 `cleanup()`,因为上游流式响应在 `fetch()` resolve 之后仍可能继续\n * 读取 body;只有在 proxy 完全结束这次转发后,才能安全移除监听器,避免:\n * 1. 客户端中途断开后无法继续取消上游;\n * 2. 长连接场景中事件监听器残留造成泄漏。\n */\nfunction anySignal(signals: AbortSignal[]): LinkedAbortSignal {\n const controller = new AbortController();\n const listeners: Array<{ signal: AbortSignal; listener: () => void }> = [];\n let cleaned = false;\n\n const cleanup = (): void => {\n if (cleaned) return;\n cleaned = true;\n for (const { signal, listener } of listeners) {\n signal.removeEventListener(\"abort\", listener);\n }\n };\n\n const abortFrom = (signal: AbortSignal): void => {\n if (!controller.signal.aborted) {\n controller.abort(signal.reason);\n }\n cleanup();\n };\n\n for (const signal of signals) {\n if (signal.aborted) {\n abortFrom(signal);\n return { signal: controller.signal, cleanup };\n }\n }\n\n for (const signal of signals) {\n const listener = (): void => {\n abortFrom(signal);\n };\n listeners.push({ signal, listener });\n signal.addEventListener(\"abort\", listener, { once: true });\n }\n\n return { signal: controller.signal, cleanup };\n}\n\n/**\n * 上游请求结果:\n * - `ok: true`:拿到可直接返回的响应\n * - `retryable`:拿到了响应,但状态码属于可重试类\n * - `timeout`:请求已发出,但在限定时间内没等到可用响应\n * - `aborted`:客户端已断开,本次 proxy 不应再继续消耗上游资源或写回响应\n * - `network_error`:压根没拿到 HTTP 响应\n */\ntype AttemptResult = {\n cleanup: () => void;\n} & (\n | { ok: true; response: Response }\n | { ok: false; reason: \"retryable\"; response: Response }\n | { ok: false; reason: \"timeout\"; error: unknown }\n | { ok: false; reason: \"aborted\"; error: unknown }\n | { ok: false; reason: \"network_error\"; error: unknown }\n);\n\n/**\n * 执行一次上游 Chat Completions 调用,并把 public alias 改写成真实 physical\n * model。\n *\n * 这里把“客户端断连”和“上游首包超时”合并到同一个 `fetch()` signal:\n * - 客户端断开:立刻取消上游,避免继续计费/占用流式连接;\n * - 超时:只覆盖等待上游响应头这一段,首包到达后即清除计时器。\n */\nasync function fetchUpstream(\n cfg: ReturnType<typeof resolveConfig>,\n req: IncomingMessage,\n body: Record<string, unknown>,\n actualModel: string,\n runtimeLimits: ProxyRuntimeLimits,\n requestSignal: AbortSignal,\n): Promise<AttemptResult> {\n const timeoutController = new AbortController();\n const timeout = setTimeout(() => {\n timeoutController.abort();\n }, runtimeLimits.upstreamRequestTimeoutMs);\n const linkedSignal = anySignal([requestSignal, timeoutController.signal]);\n const cleanup = (): void => {\n clearTimeout(timeout);\n linkedSignal.cleanup();\n };\n\n try {\n const response = await fetch(`${cfg.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: buildUpstreamHeaders(req, cfg),\n body: JSON.stringify({ ...body, model: actualModel }),\n signal: linkedSignal.signal,\n });\n clearTimeout(timeout);\n\n if (RETRYABLE_STATUS.has(response.status)) {\n return { ok: false, reason: \"retryable\", response, cleanup };\n }\n\n return { ok: true, response, cleanup };\n } catch (error) {\n // 若客户端断连与超时几乎同时发生,优先保留 timeout,避免把真实的上游慢响应\n // 误归类成客户端主动取消。\n if (timeoutController.signal.aborted) {\n return { ok: false, reason: \"timeout\", error, cleanup };\n }\n\n if (requestSignal.aborted) {\n return { ok: false, reason: \"aborted\", error, cleanup };\n }\n\n return { ok: false, reason: \"network_error\", error, cleanup };\n }\n}\n\n/**\n * `chooseModel()` 产出的编排结果。\n *\n * 这份结构专门服务于 `proxyChat()`:它既包含“语义层 alias 决策”,也包含\n * “实际上游 model 选择”和 trace / session 所需的附加上下文。\n */\ntype SelectedModel = {\n routedModel: string;\n actualModel: string;\n tier: Tier;\n decision?: RoutingDecision;\n routeText: string;\n requestedModel: string;\n sessionId?: string;\n routed: boolean;\n explicit: boolean;\n sessionAction: TraceSessionAction;\n};\n\n/**\n * 兼容 OpenAI 常见的两个最大输出 token 字段名。\n */\nfunction getMaxOutputTokens(body: Record<string, unknown>): number {\n const maxTokens = body.max_tokens;\n if (\n typeof maxTokens === \"number\" &&\n Number.isFinite(maxTokens) &&\n maxTokens > 0\n ) {\n return Math.ceil(maxTokens);\n }\n\n const maxCompletionTokens = body.max_completion_tokens;\n if (\n typeof maxCompletionTokens === \"number\" &&\n Number.isFinite(maxCompletionTokens) &&\n maxCompletionTokens > 0\n ) {\n return Math.ceil(maxCompletionTokens);\n }\n\n return 1024;\n}\n\n/**\n * 组装 Router 内核所需的运行时 options。\n */\nfunction buildRouterOptions(options: {\n routingConfig: RoutingConfig;\n modelPricing: Map<string, ModelPricing>;\n}): RouterOptions {\n return {\n config: options.routingConfig,\n modelPricing: options.modelPricing,\n };\n}\n\n/**\n * 把持久化 RawConfig 映射为 Router 运行时配置。\n */\nfunction buildRoutingConfigFromRawConfig(rawConfig: RawConfig): RoutingConfig {\n const scoring = {\n ...DEFAULT_ROUTING_CONFIG.scoring,\n tierBoundaries: {\n ...DEFAULT_ROUTING_CONFIG.scoring.tierBoundaries,\n ...rawConfig.routing.tierBoundaries,\n },\n confidenceThreshold:\n rawConfig.routing.confidenceThreshold ?? DEFAULT_ROUTING_CONFIG.scoring.confidenceThreshold,\n };\n\n return {\n ...DEFAULT_ROUTING_CONFIG,\n scoring,\n tiers: mapRawTierEntries(rawConfig.routing.tiers),\n overrides: {\n structuredOutputMinTier:\n rawConfig.routing.structuredOutputMinTier ?? \"MEDIUM\",\n ambiguousDefaultTier: rawConfig.routing.ambiguousDefaultTier ?? \"MEDIUM\",\n },\n };\n}\n\n/**\n * 将配置文件里的 tier 结构转换成 Router 核心使用的 primary / fallback 结构。\n */\nfunction mapRawTierEntries(\n entries: Record<Tier, TierEntry>,\n): Record<Tier, TierConfig> {\n return Object.fromEntries(\n Object.entries(entries).map(([tier, entry]) => [\n tier,\n {\n primary: entry.publicModel,\n fallback: entry.fallback ?? [],\n },\n ]),\n ) as Record<Tier, TierConfig>;\n}\n\n/**\n * 为 alias 层构造成本表。\n *\n * 注意这里的 key 是 public alias:`auto` 使用自身 metadata 成本,普通 alias\n * 则解析到实际 physical model 后继承其单价。\n */\nfunction buildModelPricingFromPublicModels(\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n): Map<string, ModelPricing> {\n return new Map(\n Object.entries(publicModels).map(([publicModelId, config]) => {\n if (config.kind === \"router\") {\n return [\n publicModelId,\n {\n inputPrice: config.metadata.cost.input,\n outputPrice: config.metadata.cost.output,\n },\n ];\n }\n\n const physicalModel = resolvePublicModelCandidate(\n publicModelId,\n publicModels,\n registry,\n );\n return [\n publicModelId,\n {\n inputPrice: physicalModel.inputPrice,\n outputPrice: physicalModel.outputPrice,\n },\n ];\n }),\n );\n}\n\nconst TIER_ORDER: Record<Tier, number> = {\n SIMPLE: 0,\n MEDIUM: 1,\n COMPLEX: 2,\n REASONING: 3,\n};\n\n/**\n * 会话 pinning 只允许“维持当前 tier 或升级”,不允许降级。\n */\nfunction isLowerTier(nextTier: Tier, pinnedTier: Tier): boolean {\n return TIER_ORDER[nextTier] < TIER_ORDER[pinnedTier];\n}\n\n/**\n * 根据 alias 出现在 tier 配置中的位置,反推出一个“显式请求时应显示的 tier”。\n *\n * 虽然当前请求边界只接受 `auto`,但保留这段逻辑能让代码在将来重新开放 alias\n * 请求时仍然自洽,也便于测试 / 内部调用复用。\n */\nfunction getExplicitTier(\n publicModelId: string,\n entries: Record<Tier, TierEntry>,\n): Tier {\n const matches = (Object.entries(entries) as Array<[Tier, TierEntry]>)\n .filter(([, entry]) => entry.publicModel === publicModelId)\n .map(([tier]) => tier);\n\n if (matches.length === 0) {\n return \"MEDIUM\";\n }\n\n return matches.reduce((highest, tier) =>\n TIER_ORDER[tier] > TIER_ORDER[highest] ? tier : highest,\n );\n}\n\n/**\n * 决定 trace reason 字段,供 summary / debug trace 共用。\n */\nfunction getTraceReason(selected: SelectedModel, failed: boolean): TraceReason {\n if (selected.explicit) return \"user\";\n if (selected.decision?.tier === \"REASONING\") return \"reasoning\";\n if (failed) return \"error\";\n return \"first-pass\";\n}\n\n/**\n * 构造对客户端公开的路由响应头。\n */\nfunction buildPublicHeaders(\n cfg: RouterConfig,\n selected: SelectedModel,\n finalTier: Tier,\n trace: string,\n): Record<string, string> {\n return {\n \"x-xy-router-model\": selected.routedModel,\n \"x-xy-router-actual-model\": selected.actualModel,\n \"x-xy-router-tier\": finalTier,\n \"x-xy-router-trace\": trace,\n \"x-xy-router-routed\": String(selected.routed),\n \"x-xy-router-fallback\": \"false\",\n \"x-xy-router-upstream\": cfg.baseUrl,\n };\n}\n\n/**\n * 发送一条结构化路由 trace,并返回紧凑 trace 字符串。\n */\nfunction emitProxyTrace(\n cfg: RouterConfig,\n selected: SelectedModel,\n finalTier: Tier,\n attempts: TraceAttempt[],\n sessionAction: TraceSessionAction,\n failed: boolean,\n): string {\n const writer = resolveTraceWriter(cfg.traceLogger);\n const reason = getTraceReason(selected, failed);\n const trace = buildTraceSummary({\n requestedModel: selected.requestedModel,\n routedModel: selected.routedModel,\n actualModel: selected.actualModel,\n tier: finalTier,\n profile: selected.decision?.profile ?? \"default\",\n reason,\n routed: selected.routed,\n explicit: selected.explicit,\n fallback: false,\n });\n const detail: RouteTraceLog = {\n trace,\n requestedModel: selected.requestedModel,\n routedModel: selected.routedModel,\n actualModel: selected.actualModel,\n tier: finalTier,\n profile: selected.decision?.profile ?? \"default\",\n reason,\n explicit: selected.explicit,\n routed: selected.routed,\n fallback: false,\n attempts,\n sessionAction,\n ...(selected.decision && {\n method: selected.decision.method,\n confidence: selected.decision.confidence,\n ...(selected.decision.score !== undefined && {\n score: selected.decision.score,\n }),\n ...(selected.decision.agenticScore !== undefined && {\n agenticScore: selected.decision.agenticScore,\n }),\n }),\n ...(cfg.traceMode === \"debug\" && {\n promptPreview: getPromptPreview(selected.routeText),\n }),\n };\n\n emitRouteTrace(cfg.traceMode, detail, writer);\n return trace;\n}\n\n/**\n * 选择本次请求最终应使用的 alias / physical model。\n *\n * 当前外部请求边界已经在 `proxyChat()` 收紧到只允许 `auto`,但这里仍保留了\n * 显式 alias 分支,使该函数在测试、未来协议调整或内部复用时更容易扩展。\n */\nfunction chooseModel(\n requestedModel: string,\n body: { messages?: unknown[]; tools?: unknown[] },\n headers: IncomingMessage[\"headers\"],\n sessionStore: SessionStore,\n cfg: RouterConfig,\n tierEntries: Record<Tier, TierEntry>,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n routerOptions: RouterOptions,\n): SelectedModel {\n const prompt = extractPrompt(body.messages ?? []);\n\n if (requestedModel !== \"auto\") {\n // 兼容式分支:如果未来重新允许显式 alias,请求会在这里直接解析到\n // physical model,而不会经过 auto 路由。\n const physicalModel = resolvePublicModelCandidate(\n requestedModel,\n publicModels,\n registry,\n );\n return {\n routedModel: requestedModel,\n actualModel: physicalModel.id,\n tier: getExplicitTier(requestedModel, tierEntries),\n routeText: prompt.routeText,\n requestedModel,\n routed: false,\n explicit: true,\n sessionAction: \"none\",\n };\n }\n\n const sessionId = deriveSessionId(headers, body.messages ?? []);\n const existing = cfg.sessionPinning\n ? sessionStore.getSession(sessionId)\n : undefined;\n const decision = route(\n prompt.routeText,\n prompt.system,\n getMaxOutputTokens(body as Record<string, unknown>),\n routerOptions,\n );\n const routedModel = decision.publicModel;\n const physicalModel = resolvePublicModelCandidate(\n routedModel,\n publicModels,\n registry,\n );\n\n // 会话已 pin 到更高 tier 时,不允许新的简单请求把它降回来。\n if (existing && isLowerTier(decision.tier, existing.pinnedTier)) {\n sessionStore.touchSession(sessionId);\n return {\n routedModel: existing.routedPublicModel,\n actualModel: existing.physicalModelId,\n tier: existing.pinnedTier,\n routeText: prompt.routeText,\n requestedModel,\n sessionId,\n routed: true,\n explicit: false,\n sessionAction: \"reuse\",\n };\n }\n\n return {\n routedModel,\n actualModel: physicalModel.id,\n tier: decision.tier,\n decision,\n routeText: prompt.routeText,\n requestedModel,\n sessionId,\n routed: true,\n explicit: false,\n sessionAction: \"none\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// Chat completion proxy\n// ---------------------------------------------------------------------------\n\n/**\n * 处理 `POST /v1/chat/completions`。\n *\n * 这是整个仓库最重要的边界函数:它负责请求合法性校验、auto 路由、physical\n * model 解析、上游转发,以及所有对外响应头 / 错误格式的一致性。\n */\nasync function proxyChat(\n req: IncomingMessage,\n res: ServerResponse,\n cfg: RouterConfig,\n runtimeLimits: ProxyRuntimeLimits,\n sessionStore: SessionStore,\n tierEntries: Record<Tier, TierEntry>,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n routerOptions: RouterOptions,\n): Promise<void> {\n // Read body\n let rawBody: string;\n try {\n rawBody = await readBody(req, runtimeLimits);\n } catch (error) {\n if (isBodyReadError(error)) {\n writeBodyReadErrorAndCloseRequest(req, res, error);\n return;\n }\n throw error;\n }\n\n // Parse JSON\n let body: unknown;\n try {\n body = JSON.parse(rawBody);\n } catch {\n writeOpenAiError(res, 400, \"Invalid JSON body\");\n return;\n }\n\n if (!body || typeof body !== \"object\") {\n writeOpenAiError(res, 400, \"Body must be a JSON object\");\n return;\n }\n\n const bodyObj = body as Record<string, unknown>;\n\n const requestedModelId = bodyObj.model;\n const supportedModels = [...REQUESTABLE_PUBLIC_MODELS]\n .filter((modelId) => publicModels[modelId])\n .sort()\n .join(\", \");\n if (\n typeof requestedModelId !== \"string\" ||\n !publicModels[requestedModelId] ||\n !REQUESTABLE_PUBLIC_MODELS.has(requestedModelId)\n ) {\n // 这里故意同时检查:\n // 1. 配置里是否存在该 public model\n // 2. 它是否属于当前“允许被客户端显式请求”的极小白名单\n // 从而把 alias 从“配置驱动内部语义”与“外部请求合同”严格分开。\n writeOpenAiError(\n res,\n 400,\n `Unknown model \"${String(requestedModelId)}\". Supported models: ${supportedModels}`,\n \"invalid_request_error\",\n \"model_not_found\",\n );\n return;\n }\n\n // 先完成 alias 级别决策,再解析实际发往上游的 physical model。\n const selected = chooseModel(\n requestedModelId,\n bodyObj as { messages?: unknown[]; tools?: unknown[] },\n req.headers,\n sessionStore,\n cfg,\n tierEntries,\n publicModels,\n registry,\n routerOptions,\n );\n\n const physicalModel = registry.get(selected.actualModel);\n if (!physicalModel) {\n writeOpenAiError(\n res,\n 500,\n `Physical model not found in registry: ${selected.actualModel}`,\n );\n return;\n }\n\n const requestController = new AbortController();\n let responseFinished = false;\n /**\n * 客户端一旦在 proxy 等待/转发上游期间断开,就立刻取消上游请求,避免继续\n * 持有流式连接或产生无意义的上游计费。\n */\n const abortUpstreamRequest = (): void => {\n if (responseFinished || requestController.signal.aborted) return;\n requestController.abort();\n };\n const onRequestAborted = (): void => {\n abortUpstreamRequest();\n };\n const onResponseClose = (): void => {\n abortUpstreamRequest();\n };\n const onResponseFinish = (): void => {\n responseFinished = true;\n };\n const cleanupRequestAbortListeners = (): void => {\n req.off(\"aborted\", onRequestAborted);\n res.off(\"close\", onResponseClose);\n res.off(\"finish\", onResponseFinish);\n };\n\n req.on(\"aborted\", onRequestAborted);\n res.on(\"close\", onResponseClose);\n res.on(\"finish\", onResponseFinish);\n\n let attempt: AttemptResult | undefined;\n try {\n attempt = await fetchUpstream(\n cfg,\n req,\n bodyObj,\n physicalModel.id,\n runtimeLimits,\n requestController.signal,\n );\n const attempts: TraceAttempt[] = [\n attempt.ok\n ? { model: selected.actualModel, status: \"success\" }\n : {\n model: selected.actualModel,\n status: \"error\",\n error:\n attempt.reason === \"timeout\"\n ? \"upstream_timeout\"\n : attempt.reason === \"network_error\"\n ? \"network_error\"\n : attempt.reason === \"aborted\"\n ? \"client_aborted\"\n : `upstream_http_${attempt.response.status}`,\n },\n ];\n const finalTier = selected.tier;\n let sessionAction = selected.sessionAction;\n\n if (!attempt.ok && attempt.reason === \"aborted\") {\n emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n true,\n );\n return;\n }\n\n if (!attempt.ok && attempt.reason === \"timeout\") {\n const trace = emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n true,\n );\n const headers = buildPublicHeaders(cfg, selected, finalTier, trace);\n writeOpenAiError(\n res,\n 504,\n \"Upstream request timed out\",\n \"invalid_request_error\",\n null,\n headers,\n );\n return;\n }\n\n if (!attempt.ok && attempt.reason === \"network_error\") {\n const trace = emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n true,\n );\n const headers = buildPublicHeaders(cfg, selected, finalTier, trace);\n writeOpenAiError(\n res,\n 502,\n attempt.error instanceof Error\n ? attempt.error.message\n : \"Upstream request failed\",\n \"invalid_request_error\",\n null,\n headers,\n );\n return;\n }\n\n // 只有真正拿到 HTTP 响应后才更新 session pinning,避免把纯网络错误写成成功 pin。\n if (attempt.ok && selected.sessionId && !selected.explicit) {\n sessionStore.setSession(selected.sessionId, {\n physicalModelId: selected.actualModel,\n routedPublicModel: selected.routedModel,\n pinnedTier: finalTier,\n });\n if (sessionAction === \"none\") {\n sessionAction = \"set\";\n }\n }\n\n const trace = emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n !attempt.ok,\n );\n const headers = buildPublicHeaders(cfg, selected, finalTier, trace);\n const responseHeaders = copyResponseHeaders(attempt.response, headers);\n res.statusCode = attempt.response.status;\n for (const [k, v] of Object.entries(responseHeaders)) {\n res.setHeader(k, v);\n }\n await streamResponse(attempt.response, res);\n responseFinished = true;\n } finally {\n cleanupRequestAbortListeners();\n attempt?.cleanup();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Proxy server\n// ---------------------------------------------------------------------------\n\n/**\n * 启动本地 Router HTTP 服务。\n */\nexport async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {\n const cfg = resolveConfig({\n baseUrl: options.config.proxy.upstreamUrl,\n apiKey: options.config.proxy.apiKey,\n headers: options.config.proxy.headers,\n port: options.config.proxy.port,\n traceMode: options.config.proxy.trace,\n traceLogger: options.traceLogger,\n sessionPinning: options.session?.enabled,\n });\n const runtimeLimits = resolveRuntimeLimits(options.runtimeLimits);\n const sessionStore = new SessionStore(options.session);\n const publicModels = options.config.publicModels;\n const tierEntries = options.config.routing.tiers;\n const registry = createModelRegistry(options.config.models);\n const routerOptions = buildRouterOptions({\n routingConfig: buildRoutingConfigFromRawConfig(options.config),\n modelPricing: buildModelPricingFromPublicModels(publicModels, registry),\n });\n\n const server = http.createServer((req, res) => {\n void (async () => {\n try {\n const url = req.url ?? \"/\";\n\n if (req.method === \"GET\" && url === \"/health\") {\n res.setHeader(\"content-type\", \"application/json\");\n res.end(\n JSON.stringify({\n status: \"ok\",\n baseUrl: cfg.baseUrl,\n version: VERSION,\n }),\n );\n return;\n }\n\n // 当前版本只暴露一个业务入口,其他路径统一保持 OpenAI-compatible 404。\n if (req.method === \"POST\" && url === \"/v1/chat/completions\") {\n await proxyChat(\n req,\n res,\n cfg,\n runtimeLimits,\n sessionStore,\n tierEntries,\n publicModels,\n registry,\n routerOptions,\n );\n return;\n }\n\n // Everything else → 404\n writeOpenAiError(res, 404, \"Not Found\");\n } catch (error) {\n if (!res.headersSent) {\n writeOpenAiError(res, 502, String(error));\n } else {\n res.destroy();\n }\n }\n })();\n });\n\n const port = await new Promise<number>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(cfg.port, \"127.0.0.1\", () => {\n const address = server.address();\n if (!address || typeof address === \"string\") {\n reject(new Error(\"Could not determine server port\"));\n return;\n }\n resolve(address.port);\n });\n });\n\n return {\n port,\n baseUrl: cfg.baseUrl,\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((err) => {\n sessionStore.close();\n if (err) reject(err);\n else resolve();\n });\n }),\n };\n}\n","import type { ScoringConfig, ScoringResult } from \"./types.js\";\n\nexport const SIMPLE_PATTERNS = [\n /\\btranslate\\b/i,\n /\\bsummarize\\b/i,\n /\\bformat\\b/i,\n /\\bexplain briefly\\b/i,\n /翻译/,\n /总结/,\n /格式化/,\n];\n\nexport const CODE_PATTERNS = [\n /\\bapply_patch\\b/i,\n /\\btypescript\\b/i,\n /\\bjavascript\\b/i,\n /\\bfunction\\b/i,\n /\\bclass\\b/i,\n /\\brename\\b/i,\n /\\bwrite\\s+(some\\s+)?code\\b/i,\n /\\bgenerate\\s+code\\b/i,\n /\\bedit\\s+(the\\s+)?file\\b/i,\n /\\bimplement\\b/i,\n /\\b[a-z0-9_-]+\\.(ts|tsx|js|jsx|mjs|cjs|json|md|py|rs|go|java|kt|swift|rb|php|css|scss|html|yml|yaml)\\b/i,\n /代码/,\n /函数/,\n];\n\nexport const COMPLEX_PATTERNS = [\n /\\bdebug\\b/i,\n /\\bfailing tests?\\b/i,\n /\\barchitecture\\b/i,\n /\\brefactor\\b/i,\n /\\bmultiple files?\\b/i,\n /\\broot cause\\b/i,\n /调试/,\n /测试失败/,\n /架构/,\n /重构/,\n /多文件/,\n];\n\ntype DimensionScore = { name: string; score: number; signal: string | null };\n\nfunction scoreTokenCount(\n estimatedTokens: number,\n thresholds: { simple: number; complex: number },\n): DimensionScore {\n if (estimatedTokens < thresholds.simple) {\n return { name: \"tokenCount\", score: -1, signal: `short (${estimatedTokens} tokens)` };\n }\n if (estimatedTokens > thresholds.complex) {\n return { name: \"tokenCount\", score: 1, signal: `long (${estimatedTokens} tokens)` };\n }\n return { name: \"tokenCount\", score: 0, signal: null };\n}\n\nfunction scoreKeywordMatch(\n text: string,\n keywords: string[],\n name: string,\n signalLabel: string,\n thresholds: { low: number; high: number },\n scores: { none: number; low: number; high: number },\n): DimensionScore {\n const matches = keywords.filter((keyword) => text.includes(keyword.toLowerCase()));\n if (matches.length >= thresholds.high) {\n return { name, score: scores.high, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n if (matches.length >= thresholds.low) {\n return { name, score: scores.low, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n return { name, score: scores.none, signal: null };\n}\n\nfunction scoreMultiStep(text: string): DimensionScore {\n const patterns = [/first.*then/i, /step \\d/i, /\\d\\.\\s/];\n return patterns.some((pattern) => pattern.test(text))\n ? { name: \"multiStepPatterns\", score: 0.5, signal: \"multi-step\" }\n : { name: \"multiStepPatterns\", score: 0, signal: null };\n}\n\nfunction scoreQuestionComplexity(prompt: string): DimensionScore {\n const count = (prompt.match(/\\?/g) ?? []).length;\n return count > 3\n ? { name: \"questionComplexity\", score: 0.5, signal: `${count} questions` }\n : { name: \"questionComplexity\", score: 0, signal: null };\n}\n\nfunction scoreAgenticTask(\n text: string,\n keywords: string[],\n): { dimensionScore: DimensionScore; agenticScore: number } {\n let matchCount = 0;\n const signals: string[] = [];\n\n for (const keyword of keywords) {\n if (text.includes(keyword.toLowerCase())) {\n matchCount++;\n if (signals.length < 3) signals.push(keyword);\n }\n }\n\n if (matchCount >= 4) {\n return {\n dimensionScore: { name: \"agenticTask\", score: 1, signal: `agentic (${signals.join(\", \")})` },\n agenticScore: 1,\n };\n }\n if (matchCount >= 3) {\n return {\n dimensionScore: { name: \"agenticTask\", score: 0.6, signal: `agentic (${signals.join(\", \")})` },\n agenticScore: 0.6,\n };\n }\n if (matchCount >= 1) {\n return {\n dimensionScore: { name: \"agenticTask\", score: 0.2, signal: `agentic-light (${signals.join(\", \")})` },\n agenticScore: 0.2,\n };\n }\n\n return {\n dimensionScore: { name: \"agenticTask\", score: 0, signal: null },\n agenticScore: 0,\n };\n}\n\nexport function classifyByRules(\n prompt: string,\n _systemPrompt: string | undefined,\n estimatedTokens: number,\n config: ScoringConfig,\n): ScoringResult {\n const userText = prompt.toLowerCase();\n const dimensions: DimensionScore[] = [\n scoreTokenCount(estimatedTokens, config.tokenCountThresholds),\n scoreKeywordMatch(userText, config.codeKeywords, \"codePresence\", \"code\", { low: 1, high: 2 }, { none: 0, low: 0.5, high: 1 }),\n scoreKeywordMatch(userText, config.reasoningKeywords, \"reasoningMarkers\", \"reasoning\", { low: 1, high: 2 }, { none: 0, low: 0.7, high: 1 }),\n scoreKeywordMatch(userText, config.technicalKeywords, \"technicalTerms\", \"technical\", { low: 2, high: 4 }, { none: 0, low: 0.5, high: 1 }),\n scoreKeywordMatch(userText, config.creativeKeywords, \"creativeMarkers\", \"creative\", { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.7 }),\n scoreKeywordMatch(userText, config.simpleKeywords, \"simpleIndicators\", \"simple\", { low: 1, high: 2 }, { none: 0, low: -1, high: -1 }),\n scoreMultiStep(userText),\n scoreQuestionComplexity(prompt),\n scoreKeywordMatch(userText, config.imperativeVerbs, \"imperativeVerbs\", \"imperative\", { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(userText, config.constraintIndicators, \"constraintCount\", \"constraints\", { low: 1, high: 3 }, { none: 0, low: 0.3, high: 0.7 }),\n scoreKeywordMatch(userText, config.outputFormatKeywords, \"outputFormat\", \"format\", { low: 1, high: 2 }, { none: 0, low: 0.4, high: 0.7 }),\n scoreKeywordMatch(userText, config.referenceKeywords, \"referenceComplexity\", \"references\", { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(userText, config.negationKeywords, \"negationComplexity\", \"negation\", { low: 2, high: 3 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(userText, config.domainSpecificKeywords, \"domainSpecificity\", \"domain-specific\", { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.8 }),\n ];\n\n const agenticResult = scoreAgenticTask(userText, config.agenticTaskKeywords);\n dimensions.push(agenticResult.dimensionScore);\n\n const signals = dimensions.filter((dimension) => dimension.signal !== null).map((dimension) => dimension.signal!);\n const weightedScore = dimensions.reduce(\n (score, dimension) => score + dimension.score * (config.dimensionWeights[dimension.name] ?? 0),\n 0,\n );\n\n const reasoningMatches = config.reasoningKeywords.filter((keyword) =>\n userText.includes(keyword.toLowerCase()),\n );\n if (reasoningMatches.length >= 2) {\n return {\n score: weightedScore,\n tier: \"REASONING\",\n confidence: Math.max(calibrateConfidence(Math.max(weightedScore, 0.3), config.confidenceSteepness), 0.85),\n signals,\n agenticScore: agenticResult.agenticScore,\n dimensions,\n };\n }\n\n const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;\n let tier: ScoringResult[\"tier\"];\n let distanceFromBoundary: number;\n\n if (weightedScore < simpleMedium) {\n tier = \"SIMPLE\";\n distanceFromBoundary = simpleMedium - weightedScore;\n } else if (weightedScore < mediumComplex) {\n tier = \"MEDIUM\";\n distanceFromBoundary = Math.min(weightedScore - simpleMedium, mediumComplex - weightedScore);\n } else if (weightedScore < complexReasoning) {\n tier = \"COMPLEX\";\n distanceFromBoundary = Math.min(weightedScore - mediumComplex, complexReasoning - weightedScore);\n } else {\n tier = \"REASONING\";\n distanceFromBoundary = weightedScore - complexReasoning;\n }\n\n const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);\n if (confidence < config.confidenceThreshold) {\n return {\n score: weightedScore,\n tier: null,\n confidence,\n signals,\n agenticScore: agenticResult.agenticScore,\n dimensions,\n };\n }\n\n return {\n score: weightedScore,\n tier,\n confidence,\n signals,\n agenticScore: agenticResult.agenticScore,\n dimensions,\n };\n}\n\nfunction calibrateConfidence(distance: number, steepness: number): number {\n return 1 / (1 + Math.exp(-steepness * distance));\n}\n","import type { RoutingDecision, Tier, TierConfig } from \"./types.js\";\n\n/**\n * 路由层只关心 alias 的成本画像,因此这里的 key 是 public alias,而不是\n * physical model ID。\n */\nexport type ModelPricing = {\n inputPrice: number;\n outputPrice: number;\n};\n\nconst DEFAULT_BASELINE_INPUT_PRICE = 0.56;\nconst DEFAULT_BASELINE_OUTPUT_PRICE = 1.68;\n\n/**\n * 把 tier 选择结果转换成统一的 RoutingDecision,并补齐成本估算字段。\n */\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n agenticScore?: number,\n score?: number,\n): RoutingDecision;\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n agenticScore?: number,\n score?: number,\n): RoutingDecision {\n const config = tierConfigs[tier];\n if (!config) {\n throw new Error(`Missing tier config for ${tier}`);\n }\n\n // `primary` 在当前阶段表示“语义层 alias 选择结果”,不是 physical model。\n const model = config.primary;\n const baselineModel = tierConfigs[\"COMPLEX\"].primary;\n const costs = calculateModelCost(\n model,\n modelPricing,\n estimatedInputTokens,\n maxOutputTokens,\n baselineModel,\n );\n\n return {\n publicModel: model,\n tier,\n confidence,\n method,\n reasoning,\n ...costs,\n ...(agenticScore !== undefined && { agenticScore }),\n ...(score !== undefined && { score }),\n };\n}\n\n/**\n * 返回某个 tier 的完整尝试链:先 primary,再按顺序追加 fallback。\n */\nexport function getFallbackChain(\n tier: Tier,\n tierConfigs: Record<Tier, TierConfig>,\n): string[] {\n const config = tierConfigs[tier];\n return [config.primary, ...config.fallback];\n}\n\n/**\n * 按 1M token 单价估算当前 alias 的输入 / 输出成本,并和 baseline 进行对比。\n */\nexport function calculateModelCost(\n model: string,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n baselineModelId?: string,\n): { costEstimate: number; baselineCost: number; savings: number } {\n const pricing = modelPricing.get(model);\n const inputCost =\n (estimatedInputTokens / 1_000_000) * (pricing?.inputPrice ?? 0);\n const outputCost =\n (maxOutputTokens / 1_000_000) * (pricing?.outputPrice ?? 0);\n const costEstimate = inputCost + outputCost;\n\n const baselinePricing = baselineModelId\n ? modelPricing.get(baselineModelId)\n : undefined;\n const baselineInput =\n (estimatedInputTokens / 1_000_000) *\n (baselinePricing?.inputPrice ?? DEFAULT_BASELINE_INPUT_PRICE);\n const baselineOutput =\n (maxOutputTokens / 1_000_000) *\n (baselinePricing?.outputPrice ?? DEFAULT_BASELINE_OUTPUT_PRICE);\n const baselineCost = baselineInput + baselineOutput;\n const savings =\n baselineCost > 0\n ? Math.max(0, (baselineCost - costEstimate) / baselineCost)\n : 0;\n\n return { costEstimate, baselineCost, savings };\n}\n\n/**\n * 从候选链里剔除不允许继续尝试的模型;如果剔除后为空,则保底返回原链,避免\n * 调用方拿到“无路可走”的空数组。\n */\nexport function filterByExcludeList(\n models: string[],\n excludeList: Set<string>,\n): string[] {\n if (excludeList.size === 0) return models;\n const filtered = models.filter((model) => !excludeList.has(model));\n return filtered.length > 0 ? filtered : models;\n}\n","import { classifyByRules } from \"./rules.js\";\nimport { selectModel } from \"./selector.js\";\nimport type {\n RouterOptions,\n RouterStrategy,\n RoutingDecision,\n Tier,\n TierConfig,\n} from \"./types.js\";\n\n/**\n * 基于规则的默认路由策略。\n *\n * 它只负责把 prompt 映射到 tier / alias 决策,不参与 physical model 解析,\n * 因此可以长期保持纯函数式、可测试的语义层逻辑。\n */\nexport class RulesStrategy implements RouterStrategy {\n readonly name = \"rules\";\n\n route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n ): RoutingDecision {\n const { config, modelPricing } = options;\n if (!config) {\n throw new Error(\"Routing config is required at runtime\");\n }\n if (!modelPricing) {\n throw new Error(\"Model pricing is required at runtime\");\n }\n\n // 使用一个非常粗粒度的字符数近似,避免把 tokenizer 引进 Router 内核。\n const fullText = `${systemPrompt ?? \"\"} ${prompt}`;\n const estimatedTokens = Math.ceil(fullText.length / 4);\n const ruleResult = classifyByRules(\n prompt,\n systemPrompt,\n estimatedTokens,\n config.scoring,\n );\n\n const { tierConfigs, profile, profileSuffix } = chooseTierConfigs(options);\n\n const hasStructuredOutput = systemPrompt\n ? /json|structured|schema/i.test(systemPrompt)\n : false;\n let tier: Tier;\n let confidence: number;\n let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(\", \")}`;\n\n // 命中明确规则时直接采用;否则落到配置里的 ambiguous 默认 tier。\n if (ruleResult.tier !== null) {\n tier = ruleResult.tier;\n confidence = ruleResult.confidence;\n } else {\n tier = config.overrides?.ambiguousDefaultTier ?? \"MEDIUM\";\n confidence = 0.5;\n reasoning += ` | ambiguous -> default: ${tier}`;\n }\n\n // structured output 请求倾向提升到至少 MEDIUM,避免过度保守地走 SIMPLE。\n if (hasStructuredOutput) {\n const tierRank: Record<Tier, number> = {\n SIMPLE: 0,\n MEDIUM: 1,\n COMPLEX: 2,\n REASONING: 3,\n };\n const minTier = config.overrides?.structuredOutputMinTier ?? \"MEDIUM\";\n if (tierRank[tier] < tierRank[minTier]) {\n reasoning += ` | upgraded to ${minTier} (structured output)`;\n tier = minTier;\n } else {\n reasoning += \" | structured output\";\n }\n }\n\n reasoning += profileSuffix;\n\n const decision = selectModel(\n tier,\n confidence,\n \"rules\",\n reasoning,\n tierConfigs,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n ruleResult.agenticScore,\n ruleResult.score,\n );\n return { ...decision, tierConfigs, profile };\n }\n}\n\n/**\n * 预留 profile 扩展点。\n *\n * 当前仓库只使用 default profile,但把结构保留下来,能避免未来新增 profile\n * 时改动 `RulesStrategy.route()` 的主流程。\n */\nfunction chooseTierConfigs(options: RouterOptions | undefined): {\n tierConfigs: Record<Tier, TierConfig>;\n profile: RoutingDecision[\"profile\"];\n profileSuffix: string;\n} {\n const config = options?.config;\n if (!config?.tiers) {\n throw new Error(\"Routing tiers are required at runtime\");\n }\n\n return {\n tierConfigs: config.tiers,\n profile: \"default\",\n profileSuffix: \"\",\n };\n}\n\nconst registry = new Map<string, RouterStrategy>();\nregistry.set(\"rules\", new RulesStrategy());\n\n/**\n * 从策略注册表中读取命名路由策略。\n */\nexport function getStrategy(name: string): RouterStrategy {\n const strategy = registry.get(name);\n if (!strategy) {\n throw new Error(`Unknown routing strategy: ${name}`);\n }\n return strategy;\n}\n\n/**\n * 允许外部测试或扩展模块注册新的路由策略实现。\n */\nexport function registerStrategy(strategy: RouterStrategy): void {\n registry.set(strategy.name, strategy);\n}\n","import type { RoutingConfig } from \"./types.js\";\n\nexport const DEFAULT_ROUTING_CONFIG = {\n version: \"2.0\",\n\n scoring: {\n tokenCountThresholds: { simple: 50, complex: 500 },\n\n // Multilingual keywords: EN + ZH + JA + RU + DE + ES + PT + KO + AR\n codeKeywords: [\n // English\n \"function\",\n \"class\",\n \"import\",\n \"def\",\n \"SELECT\",\n \"async\",\n \"await\",\n \"const\",\n \"let\",\n \"var\",\n \"return\",\n \"```\",\n // Chinese\n \"函数\",\n \"类\",\n \"导入\",\n \"定义\",\n \"查询\",\n \"异步\",\n \"等待\",\n \"常量\",\n \"变量\",\n \"返回\",\n // Japanese\n \"関数\",\n \"クラス\",\n \"インポート\",\n \"非同期\",\n \"定数\",\n \"変数\",\n // Russian\n \"функция\",\n \"класс\",\n \"импорт\",\n \"определ\",\n \"запрос\",\n \"асинхронный\",\n \"ожидать\",\n \"константа\",\n \"переменная\",\n \"вернуть\",\n // German\n \"funktion\",\n \"klasse\",\n \"importieren\",\n \"definieren\",\n \"abfrage\",\n \"asynchron\",\n \"erwarten\",\n \"konstante\",\n \"variable\",\n \"zurückgeben\",\n // Spanish\n \"función\",\n \"clase\",\n \"importar\",\n \"definir\",\n \"consulta\",\n \"asíncrono\",\n \"esperar\",\n \"constante\",\n \"variable\",\n \"retornar\",\n // Portuguese\n \"função\",\n \"classe\",\n \"importar\",\n \"definir\",\n \"consulta\",\n \"assíncrono\",\n \"aguardar\",\n \"constante\",\n \"variável\",\n \"retornar\",\n // Korean\n \"함수\",\n \"클래스\",\n \"가져오기\",\n \"정의\",\n \"쿼리\",\n \"비동기\",\n \"대기\",\n \"상수\",\n \"변수\",\n \"반환\",\n // Arabic\n \"دالة\",\n \"فئة\",\n \"استيراد\",\n \"تعريف\",\n \"استعلام\",\n \"غير متزامن\",\n \"انتظار\",\n \"ثابت\",\n \"متغير\",\n \"إرجاع\",\n ],\n reasoningKeywords: [\n // English\n \"prove\",\n \"theorem\",\n \"derive\",\n \"step by step\",\n \"chain of thought\",\n \"formally\",\n \"mathematical\",\n \"proof\",\n \"logically\",\n // Chinese\n \"证明\",\n \"定理\",\n \"推导\",\n \"逐步\",\n \"思维链\",\n \"形式化\",\n \"数学\",\n \"逻辑\",\n // Japanese\n \"証明\",\n \"定理\",\n \"導出\",\n \"ステップバイステップ\",\n \"論理的\",\n // Russian\n \"доказать\",\n \"докажи\",\n \"доказательств\",\n \"теорема\",\n \"вывести\",\n \"шаг за шагом\",\n \"пошагово\",\n \"поэтапно\",\n \"цепочка рассуждений\",\n \"рассуждени\",\n \"формально\",\n \"математически\",\n \"логически\",\n // German\n \"beweisen\",\n \"beweis\",\n \"theorem\",\n \"ableiten\",\n \"schritt für schritt\",\n \"gedankenkette\",\n \"formal\",\n \"mathematisch\",\n \"logisch\",\n // Spanish\n \"demostrar\",\n \"teorema\",\n \"derivar\",\n \"paso a paso\",\n \"cadena de pensamiento\",\n \"formalmente\",\n \"matemático\",\n \"prueba\",\n \"lógicamente\",\n // Portuguese\n \"provar\",\n \"teorema\",\n \"derivar\",\n \"passo a passo\",\n \"cadeia de pensamento\",\n \"formalmente\",\n \"matemático\",\n \"prova\",\n \"logicamente\",\n // Korean\n \"증명\",\n \"정리\",\n \"도출\",\n \"단계별\",\n \"사고의 연쇄\",\n \"형식적\",\n \"수학적\",\n \"논리적\",\n // Arabic\n \"إثبات\",\n \"نظرية\",\n \"اشتقاق\",\n \"خطوة بخطوة\",\n \"سلسلة التفكير\",\n \"رسمياً\",\n \"رياضي\",\n \"برهان\",\n \"منطقياً\",\n ],\n simpleKeywords: [\n // English\n \"what is\",\n \"define\",\n \"translate\",\n \"hello\",\n \"yes or no\",\n \"capital of\",\n \"how old\",\n \"who is\",\n \"when was\",\n // Chinese\n \"什么是\",\n \"定义\",\n \"翻译\",\n \"你好\",\n \"是否\",\n \"首都\",\n \"多大\",\n \"谁是\",\n \"何时\",\n // Japanese\n \"とは\",\n \"定義\",\n \"翻訳\",\n \"こんにちは\",\n \"はいかいいえ\",\n \"首都\",\n \"誰\",\n // Russian\n \"что такое\",\n \"определение\",\n \"перевести\",\n \"переведи\",\n \"привет\",\n \"да или нет\",\n \"столица\",\n \"сколько лет\",\n \"кто такой\",\n \"когда\",\n \"объясни\",\n // German\n \"was ist\",\n \"definiere\",\n \"übersetze\",\n \"hallo\",\n \"ja oder nein\",\n \"hauptstadt\",\n \"wie alt\",\n \"wer ist\",\n \"wann\",\n \"erkläre\",\n // Spanish\n \"qué es\",\n \"definir\",\n \"traducir\",\n \"hola\",\n \"sí o no\",\n \"capital de\",\n \"cuántos años\",\n \"quién es\",\n \"cuándo\",\n // Portuguese\n \"o que é\",\n \"definir\",\n \"traduzir\",\n \"olá\",\n \"sim ou não\",\n \"capital de\",\n \"quantos anos\",\n \"quem é\",\n \"quando\",\n // Korean\n \"무엇\",\n \"정의\",\n \"번역\",\n \"안녕하세요\",\n \"예 또는 아니오\",\n \"수도\",\n \"누구\",\n \"언제\",\n // Arabic\n \"ما هو\",\n \"تعريف\",\n \"ترجم\",\n \"مرحبا\",\n \"نعم أو لا\",\n \"عاصمة\",\n \"من هو\",\n \"متى\",\n ],\n technicalKeywords: [\n // English\n \"algorithm\",\n \"optimize\",\n \"architecture\",\n \"distributed\",\n \"kubernetes\",\n \"microservice\",\n \"database\",\n \"infrastructure\",\n // Chinese\n \"算法\",\n \"优化\",\n \"架构\",\n \"分布式\",\n \"微服务\",\n \"数据库\",\n \"基础设施\",\n // Japanese\n \"アルゴリズム\",\n \"最適化\",\n \"アーキテクチャ\",\n \"分散\",\n \"マイクロサービス\",\n \"データベース\",\n // Russian\n \"алгоритм\",\n \"оптимизировать\",\n \"оптимизаци\",\n \"оптимизируй\",\n \"архитектура\",\n \"распределённый\",\n \"микросервис\",\n \"база данных\",\n \"инфраструктура\",\n // German\n \"algorithmus\",\n \"optimieren\",\n \"architektur\",\n \"verteilt\",\n \"kubernetes\",\n \"mikroservice\",\n \"datenbank\",\n \"infrastruktur\",\n // Spanish\n \"algoritmo\",\n \"optimizar\",\n \"arquitectura\",\n \"distribuido\",\n \"microservicio\",\n \"base de datos\",\n \"infraestructura\",\n // Portuguese\n \"algoritmo\",\n \"otimizar\",\n \"arquitetura\",\n \"distribuído\",\n \"microsserviço\",\n \"banco de dados\",\n \"infraestrutura\",\n // Korean\n \"알고리즘\",\n \"최적화\",\n \"아키텍처\",\n \"분산\",\n \"마이크로서비스\",\n \"데이터베이스\",\n \"인프라\",\n // Arabic\n \"خوارزمية\",\n \"تحسين\",\n \"بنية\",\n \"موزع\",\n \"خدمة مصغرة\",\n \"قاعدة بيانات\",\n \"بنية تحتية\",\n ],\n creativeKeywords: [\n // English\n \"story\",\n \"poem\",\n \"compose\",\n \"brainstorm\",\n \"creative\",\n \"imagine\",\n \"write a\",\n // Chinese\n \"故事\",\n \"诗\",\n \"创作\",\n \"头脑风暴\",\n \"创意\",\n \"想象\",\n \"写一个\",\n // Japanese\n \"物語\",\n \"詩\",\n \"作曲\",\n \"ブレインストーム\",\n \"創造的\",\n \"想像\",\n // Russian\n \"история\",\n \"рассказ\",\n \"стихотворение\",\n \"сочинить\",\n \"сочини\",\n \"мозговой штурм\",\n \"творческий\",\n \"представить\",\n \"придумай\",\n \"напиши\",\n // German\n \"geschichte\",\n \"gedicht\",\n \"komponieren\",\n \"brainstorming\",\n \"kreativ\",\n \"vorstellen\",\n \"schreibe\",\n \"erzählung\",\n // Spanish\n \"historia\",\n \"poema\",\n \"componer\",\n \"lluvia de ideas\",\n \"creativo\",\n \"imaginar\",\n \"escribe\",\n // Portuguese\n \"história\",\n \"poema\",\n \"compor\",\n \"criativo\",\n \"imaginar\",\n \"escreva\",\n // Korean\n \"이야기\",\n \"시\",\n \"작곡\",\n \"브레인스토밍\",\n \"창의적\",\n \"상상\",\n \"작성\",\n // Arabic\n \"قصة\",\n \"قصيدة\",\n \"تأليف\",\n \"عصف ذهني\",\n \"إبداعي\",\n \"تخيل\",\n \"اكتب\",\n ],\n imperativeVerbs: [\n // English\n \"build\",\n \"create\",\n \"implement\",\n \"design\",\n \"develop\",\n \"construct\",\n \"generate\",\n \"deploy\",\n \"configure\",\n \"set up\",\n // Chinese\n \"构建\",\n \"创建\",\n \"实现\",\n \"设计\",\n \"开发\",\n \"生成\",\n \"部署\",\n \"配置\",\n \"设置\",\n // Japanese\n \"構築\",\n \"作成\",\n \"実装\",\n \"設計\",\n \"開発\",\n \"生成\",\n \"デプロイ\",\n \"設定\",\n // Russian\n \"построить\",\n \"построй\",\n \"создать\",\n \"создай\",\n \"реализовать\",\n \"реализуй\",\n \"спроектировать\",\n \"разработать\",\n \"разработай\",\n \"сконструировать\",\n \"сгенерировать\",\n \"сгенерируй\",\n \"развернуть\",\n \"разверни\",\n \"настроить\",\n \"настрой\",\n // German\n \"erstellen\",\n \"bauen\",\n \"implementieren\",\n \"entwerfen\",\n \"entwickeln\",\n \"konstruieren\",\n \"generieren\",\n \"bereitstellen\",\n \"konfigurieren\",\n \"einrichten\",\n // Spanish\n \"construir\",\n \"crear\",\n \"implementar\",\n \"diseñar\",\n \"desarrollar\",\n \"generar\",\n \"desplegar\",\n \"configurar\",\n // Portuguese\n \"construir\",\n \"criar\",\n \"implementar\",\n \"projetar\",\n \"desenvolver\",\n \"gerar\",\n \"implantar\",\n \"configurar\",\n // Korean\n \"구축\",\n \"생성\",\n \"구현\",\n \"설계\",\n \"개발\",\n \"배포\",\n \"설정\",\n // Arabic\n \"بناء\",\n \"إنشاء\",\n \"تنفيذ\",\n \"تصميم\",\n \"تطوير\",\n \"توليد\",\n \"نشر\",\n \"إعداد\",\n ],\n constraintIndicators: [\n // English\n \"under\",\n \"at most\",\n \"at least\",\n \"within\",\n \"no more than\",\n \"o(\",\n \"maximum\",\n \"minimum\",\n \"limit\",\n \"budget\",\n // Chinese\n \"不超过\",\n \"至少\",\n \"最多\",\n \"在内\",\n \"最大\",\n \"最小\",\n \"限制\",\n \"预算\",\n // Japanese\n \"以下\",\n \"最大\",\n \"最小\",\n \"制限\",\n \"予算\",\n // Russian\n \"не более\",\n \"не менее\",\n \"как минимум\",\n \"в пределах\",\n \"максимум\",\n \"минимум\",\n \"ограничение\",\n \"бюджет\",\n // German\n \"höchstens\",\n \"mindestens\",\n \"innerhalb\",\n \"nicht mehr als\",\n \"maximal\",\n \"minimal\",\n \"grenze\",\n \"budget\",\n // Spanish\n \"como máximo\",\n \"al menos\",\n \"dentro de\",\n \"no más de\",\n \"máximo\",\n \"mínimo\",\n \"límite\",\n \"presupuesto\",\n // Portuguese\n \"no máximo\",\n \"pelo menos\",\n \"dentro de\",\n \"não mais que\",\n \"máximo\",\n \"mínimo\",\n \"limite\",\n \"orçamento\",\n // Korean\n \"이하\",\n \"이상\",\n \"최대\",\n \"최소\",\n \"제한\",\n \"예산\",\n // Arabic\n \"على الأكثر\",\n \"على الأقل\",\n \"ضمن\",\n \"لا يزيد عن\",\n \"أقصى\",\n \"أدنى\",\n \"حد\",\n \"ميزانية\",\n ],\n outputFormatKeywords: [\n // English\n \"json\",\n \"yaml\",\n \"xml\",\n \"table\",\n \"csv\",\n \"markdown\",\n \"schema\",\n \"format as\",\n \"structured\",\n // Chinese\n \"表格\",\n \"格式化为\",\n \"结构化\",\n // Japanese\n \"テーブル\",\n \"フォーマット\",\n \"構造化\",\n // Russian\n \"таблица\",\n \"форматировать как\",\n \"структурированный\",\n // German\n \"tabelle\",\n \"formatieren als\",\n \"strukturiert\",\n // Spanish\n \"tabla\",\n \"formatear como\",\n \"estructurado\",\n // Portuguese\n \"tabela\",\n \"formatar como\",\n \"estruturado\",\n // Korean\n \"테이블\",\n \"형식\",\n \"구조화\",\n // Arabic\n \"جدول\",\n \"تنسيق\",\n \"منظم\",\n ],\n referenceKeywords: [\n // English\n \"above\",\n \"below\",\n \"previous\",\n \"following\",\n \"the docs\",\n \"the api\",\n \"the code\",\n \"earlier\",\n \"attached\",\n // Chinese\n \"上面\",\n \"下面\",\n \"之前\",\n \"接下来\",\n \"文档\",\n \"代码\",\n \"附件\",\n // Japanese\n \"上記\",\n \"下記\",\n \"前の\",\n \"次の\",\n \"ドキュメント\",\n \"コード\",\n // Russian\n \"выше\",\n \"ниже\",\n \"предыдущий\",\n \"следующий\",\n \"документация\",\n \"код\",\n \"ранее\",\n \"вложение\",\n // German\n \"oben\",\n \"unten\",\n \"vorherige\",\n \"folgende\",\n \"dokumentation\",\n \"der code\",\n \"früher\",\n \"anhang\",\n // Spanish\n \"arriba\",\n \"abajo\",\n \"anterior\",\n \"siguiente\",\n \"documentación\",\n \"el código\",\n \"adjunto\",\n // Portuguese\n \"acima\",\n \"abaixo\",\n \"anterior\",\n \"seguinte\",\n \"documentação\",\n \"o código\",\n \"anexo\",\n // Korean\n \"위\",\n \"아래\",\n \"이전\",\n \"다음\",\n \"문서\",\n \"코드\",\n \"첨부\",\n // Arabic\n \"أعلاه\",\n \"أدناه\",\n \"السابق\",\n \"التالي\",\n \"الوثائق\",\n \"الكود\",\n \"مرفق\",\n ],\n negationKeywords: [\n // English\n \"don't\",\n \"do not\",\n \"avoid\",\n \"never\",\n \"without\",\n \"except\",\n \"exclude\",\n \"no longer\",\n // Chinese\n \"不要\",\n \"避免\",\n \"从不\",\n \"没有\",\n \"除了\",\n \"排除\",\n // Japanese\n \"しないで\",\n \"避ける\",\n \"決して\",\n \"なしで\",\n \"除く\",\n // Russian\n \"не делай\",\n \"не надо\",\n \"нельзя\",\n \"избегать\",\n \"никогда\",\n \"без\",\n \"кроме\",\n \"исключить\",\n \"больше не\",\n // German\n \"nicht\",\n \"vermeide\",\n \"niemals\",\n \"ohne\",\n \"außer\",\n \"ausschließen\",\n \"nicht mehr\",\n // Spanish\n \"no hagas\",\n \"evitar\",\n \"nunca\",\n \"sin\",\n \"excepto\",\n \"excluir\",\n // Portuguese\n \"não faça\",\n \"evitar\",\n \"nunca\",\n \"sem\",\n \"exceto\",\n \"excluir\",\n // Korean\n \"하지 마\",\n \"피하다\",\n \"절대\",\n \"없이\",\n \"제외\",\n // Arabic\n \"لا تفعل\",\n \"تجنب\",\n \"أبداً\",\n \"بدون\",\n \"باستثناء\",\n \"استبعاد\",\n ],\n domainSpecificKeywords: [\n // English\n \"quantum\",\n \"fpga\",\n \"vlsi\",\n \"risc-v\",\n \"asic\",\n \"photonics\",\n \"genomics\",\n \"proteomics\",\n \"topological\",\n \"homomorphic\",\n \"zero-knowledge\",\n \"lattice-based\",\n // Chinese\n \"量子\",\n \"光子学\",\n \"基因组学\",\n \"蛋白质组学\",\n \"拓扑\",\n \"同态\",\n \"零知识\",\n \"格密码\",\n // Japanese\n \"量子\",\n \"フォトニクス\",\n \"ゲノミクス\",\n \"トポロジカル\",\n // Russian\n \"квантовый\",\n \"фотоника\",\n \"геномика\",\n \"протеомика\",\n \"топологический\",\n \"гомоморфный\",\n \"с нулевым разглашением\",\n \"на основе решёток\",\n // German\n \"quanten\",\n \"photonik\",\n \"genomik\",\n \"proteomik\",\n \"topologisch\",\n \"homomorph\",\n \"zero-knowledge\",\n \"gitterbasiert\",\n // Spanish\n \"cuántico\",\n \"fotónica\",\n \"genómica\",\n \"proteómica\",\n \"topológico\",\n \"homomórfico\",\n // Portuguese\n \"quântico\",\n \"fotônica\",\n \"genômica\",\n \"proteômica\",\n \"topológico\",\n \"homomórfico\",\n // Korean\n \"양자\",\n \"포토닉스\",\n \"유전체학\",\n \"위상\",\n \"동형\",\n // Arabic\n \"كمي\",\n \"ضوئيات\",\n \"جينوميات\",\n \"طوبولوجي\",\n \"تماثلي\",\n ],\n // Agentic task keywords - file ops, execution, multi-step, iterative work\n // Pruned: removed overly common words like \"then\", \"first\", \"run\", \"test\", \"build\"\n agenticTaskKeywords: [\n // English - File operations (clearly agentic)\n \"read file\",\n \"read the file\",\n \"look at\",\n \"check the\",\n \"open the\",\n \"edit\",\n \"modify\",\n \"update the\",\n \"change the\",\n \"write to\",\n \"create file\",\n // English - Execution (specific commands only)\n \"execute\",\n \"deploy\",\n \"install\",\n \"npm\",\n \"pip\",\n \"compile\",\n // English - Multi-step patterns (specific only)\n \"after that\",\n \"and also\",\n \"once done\",\n \"step 1\",\n \"step 2\",\n // English - Iterative work\n \"fix\",\n \"debug\",\n \"until it works\",\n \"keep trying\",\n \"iterate\",\n \"make sure\",\n \"verify\",\n \"confirm\",\n // Chinese (keep specific ones)\n \"读取文件\",\n \"查看\",\n \"打开\",\n \"编辑\",\n \"修改\",\n \"更新\",\n \"创建\",\n \"执行\",\n \"部署\",\n \"安装\",\n \"第一步\",\n \"第二步\",\n \"修复\",\n \"调试\",\n \"直到\",\n \"确认\",\n \"验证\",\n // Spanish\n \"leer archivo\",\n \"editar\",\n \"modificar\",\n \"actualizar\",\n \"ejecutar\",\n \"desplegar\",\n \"instalar\",\n \"paso 1\",\n \"paso 2\",\n \"arreglar\",\n \"depurar\",\n \"verificar\",\n // Portuguese\n \"ler arquivo\",\n \"editar\",\n \"modificar\",\n \"atualizar\",\n \"executar\",\n \"implantar\",\n \"instalar\",\n \"passo 1\",\n \"passo 2\",\n \"corrigir\",\n \"depurar\",\n \"verificar\",\n // Korean\n \"파일 읽기\",\n \"편집\",\n \"수정\",\n \"업데이트\",\n \"실행\",\n \"배포\",\n \"설치\",\n \"단계 1\",\n \"단계 2\",\n \"디버그\",\n \"확인\",\n // Arabic\n \"قراءة ملف\",\n \"تحرير\",\n \"تعديل\",\n \"تحديث\",\n \"تنفيذ\",\n \"نشر\",\n \"تثبيت\",\n \"الخطوة 1\",\n \"الخطوة 2\",\n \"إصلاح\",\n \"تصحيح\",\n \"تحقق\",\n ],\n\n // Dimension weights (sum to 1.0)\n dimensionWeights: {\n tokenCount: 0.08,\n codePresence: 0.15,\n reasoningMarkers: 0.18,\n technicalTerms: 0.1,\n creativeMarkers: 0.05,\n simpleIndicators: 0.02, // Reduced from 0.12 to make room for agenticTask\n multiStepPatterns: 0.12,\n questionComplexity: 0.05,\n imperativeVerbs: 0.03,\n constraintCount: 0.04,\n outputFormat: 0.03,\n referenceComplexity: 0.02,\n negationComplexity: 0.01,\n domainSpecificity: 0.02,\n agenticTask: 0.04, // Reduced - agentic signals influence tier selection, not dominate it\n },\n // Tier boundaries on weighted score axis\n tierBoundaries: {\n simpleMedium: 0.0,\n mediumComplex: 0.3, // Raised from 0.18 - prevent simple tasks from reaching expensive COMPLEX tier\n complexReasoning: 0.5, // Raised from 0.4 - reserve for true reasoning tasks\n },\n\n // Sigmoid steepness for confidence calibration\n confidenceSteepness: 12,\n // Below this confidence → ambiguous (null tier)\n confidenceThreshold: 0.7,\n },\n\n overrides: {\n structuredOutputMinTier: \"MEDIUM\",\n ambiguousDefaultTier: \"MEDIUM\",\n },\n} satisfies RoutingConfig;\n","import type { Tier } from \"./types.js\";\n\nexport type TraceMode = \"off\" | \"summary\" | \"debug\";\nexport type TraceReason =\n | \"first-pass\"\n | \"user\"\n | \"reasoning\"\n | \"error\";\nexport type TraceSessionAction =\n | \"none\"\n | \"set\"\n | \"reuse\";\n\nexport type TraceAttempt = {\n model: string;\n status: \"success\" | \"error\";\n error?: string;\n};\n\nexport type TraceSummaryInput = {\n requestedModel: string;\n routedModel: string;\n actualModel: string;\n tier: Tier;\n profile: string;\n reason: TraceReason;\n routed: boolean;\n explicit: boolean;\n fallback: boolean;\n};\n\nexport type RouteTraceLog = TraceSummaryInput & {\n trace: string;\n method?: string;\n confidence?: number;\n score?: number;\n agenticScore?: number;\n attempts: TraceAttempt[];\n sessionAction: TraceSessionAction;\n promptPreview?: string;\n};\n\nexport type TraceWriter = (message: string) => void;\nexport type TraceLogger = { debug?: TraceWriter; info?: TraceWriter };\n\nfunction defaultTraceWriter(): TraceWriter {\n return console.debug.bind(console);\n}\n\nexport function resolveTraceWriter(logger?: TraceLogger): TraceWriter {\n return logger?.debug ?? logger?.info ?? defaultTraceWriter();\n}\n\nexport function normalizeTraceMode(mode: unknown): TraceMode {\n if (mode === \"summary\" || mode === \"debug\") {\n return mode;\n }\n\n return \"off\";\n}\n\nexport function getPromptPreview(prompt: string): string {\n const chars = Array.from(prompt);\n\n if (chars.length <= 24) {\n return prompt;\n }\n\n return `${chars.slice(0, 10).join(\"\")}...${chars.slice(-10).join(\"\")}`;\n}\n\nexport function buildTraceSummary(input: TraceSummaryInput): string {\n const requestCode = getRequestCode(input);\n const profileCode = getProfileOrTierCode(input.profile, input.tier);\n return [\n requestCode,\n profileCode,\n input.routedModel,\n input.reason,\n ].join(\":\");\n}\n\nexport function emitRouteTrace(\n mode: TraceMode,\n detail: RouteTraceLog,\n writer: TraceWriter = defaultTraceWriter(),\n): void {\n if (mode === \"off\") {\n return;\n }\n\n if (mode === \"summary\") {\n try {\n writer(\n `[llm-router] ${detail.trace} model=${detail.actualModel} fallback=${detail.fallback}`,\n );\n } catch {\n // Tracing is diagnostic only; logging failures must not affect routing.\n }\n return;\n }\n\n try {\n writer(JSON.stringify(detail));\n } catch {\n // Tracing is diagnostic only; logging failures must not affect routing.\n }\n}\n\nfunction getRequestCode(input: TraceSummaryInput): string {\n if (input.explicit || !input.routed) {\n return \"explicit\";\n }\n\n if (input.requestedModel !== \"auto\") {\n return input.requestedModel;\n }\n\n return \"auto\";\n}\n\nfunction getProfileOrTierCode(profile: string, tier: Tier): string {\n if (profile === \"agentic\") {\n return \"agentic\";\n }\n\n return tier.toLowerCase();\n}\n","import { getStrategy } from \"./strategy.js\";\nimport type { RouterOptions, RoutingDecision } from \"./types.js\";\n\n/**\n * Router 统一入口。\n *\n * 当前总是走规则路由(`rules` strategy),并返回 alias / tier 级别的决策;\n * 它不会解析 physical model,也不会直接接触 HTTP 请求。\n */\nexport function route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n): RoutingDecision {\n return getStrategy(\"rules\").route(\n prompt,\n systemPrompt,\n maxOutputTokens,\n options,\n );\n}\n\nexport { DEFAULT_ROUTING_CONFIG } from \"./config.js\";\nexport { getStrategy, registerStrategy, RulesStrategy } from \"./strategy.js\";\nexport {\n calculateModelCost,\n filterByExcludeList,\n getFallbackChain,\n selectModel,\n} from \"./selector.js\";\nexport {\n buildTraceSummary,\n emitRouteTrace,\n getPromptPreview,\n normalizeTraceMode,\n resolveTraceWriter,\n} from \"./trace.js\";\nexport type {\n OverridesConfig,\n RouterOptions,\n RouterStrategy,\n RoutingConfig,\n RoutingDecision,\n ScoringConfig,\n ScoringResult,\n Tier,\n TierConfig,\n} from \"./types.js\";\nexport type { ModelPricing } from \"./selector.js\";\nexport type {\n RouteTraceLog,\n TraceAttempt,\n TraceLogger,\n TraceMode,\n TraceReason,\n TraceSessionAction,\n TraceSummaryInput,\n TraceWriter,\n} from \"./trace.js\";\n","import { normalizeTraceMode } from \"./router/index.js\";\nimport type { TraceLogger, TraceMode } from \"./router/index.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nexport const DEFAULT_PORT = 8402;\n\nexport type RouterConfig = {\n baseUrl: string;\n apiKey?: string;\n headers: Record<string, string>;\n port: number;\n sessionPinning: boolean;\n traceMode: TraceMode;\n traceLogger?: TraceLogger;\n};\n\nexport type RouterConfigInput = Partial<RouterConfig>;\n\nexport function normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\n/**\n * Pure config resolver — all values come from the caller, no process.env.\n */\nexport function resolveConfig(input: RouterConfigInput = {}): RouterConfig {\n return {\n baseUrl: normalizeBaseUrl(input.baseUrl ?? DEFAULT_BASE_URL),\n apiKey: input.apiKey,\n headers: input.headers ?? {},\n port: input.port ?? DEFAULT_PORT,\n sessionPinning: input.sessionPinning ?? true,\n traceMode: normalizeTraceMode(input.traceMode),\n traceLogger: input.traceLogger,\n };\n}\n","import type { PhysicalModel } from \"./config-schema.js\";\n\n/**\n * 运行时模型注册表,提供 O(1) 模型查询 API\n *\n * 不变性保证:所有返回值均为内部数据的浅拷贝,外部修改不会污染注册表。\n */\nexport type ModelRegistry = {\n /**\n * 根据 ID 获取物理模型,不存在返回 undefined。\n * 返回的对象是副本,修改不会影响注册表内部状态。\n */\n get(id: string): PhysicalModel | undefined;\n /** 检查模型 ID 是否存在 */\n has(id: string): boolean;\n /**\n * 返回所有注册的物理模型。\n * 返回的数组及其元素均为副本,修改不会影响注册表内部状态。\n */\n all(): readonly PhysicalModel[];\n};\n\n/**\n * 从物理模型列表创建运行时注册表\n *\n * 注册表持有传入模型的浅拷贝,调用方后续修改原始数组或对象不会影响注册表。\n *\n * @param models 物理模型列表\n * @returns 模型注册表实例\n * @throws {Error} 如果存在重复的模型 ID\n */\nexport function createModelRegistry(models: PhysicalModel[]): ModelRegistry {\n const map = new Map<string, PhysicalModel>();\n\n for (const model of models) {\n if (map.has(model.id)) {\n throw new Error(`Duplicate model ID: ${model.id}`);\n }\n // 存入浅拷贝,隔离外部对原对象的后续修改\n map.set(model.id, { ...model });\n }\n\n return {\n get(id: string) {\n const model = map.get(id);\n // 返回浅拷贝,防止外部修改污染内部状态\n return model ? { ...model } : undefined;\n },\n has(id: string) {\n return map.has(id);\n },\n all() {\n return Array.from(map.values(), (model) => ({ ...model }));\n },\n };\n}\n","import type { PublicModelConfig } from \"./config-schema.js\";\nimport type { PhysicalModel } from \"./config-schema.js\";\nimport type { ModelRegistry } from \"./model-registry.js\";\n\n/**\n * 将公开模型 ID 解析为物理模型 ID\n * @param publicModelId 公开模型 ID(如 \"flash\", \"pro\")\n * @param publicModels 公开模型配置映射\n * @param registry 物理模型注册表\n * @returns 物理模型 ID\n * @throws {Error} 如果公开模型不存在或 kind 为 \"router\"\n */\nexport function resolvePublicModel(\n publicModelId: string,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n): string {\n return resolvePublicModelCandidate(publicModelId, publicModels, registry).id;\n}\n\n/**\n * 将公开模型 ID 解析为物理模型\n * @param publicModelId 公开模型 ID(如 \"flash\", \"pro\")\n * @param publicModels 公开模型配置映射\n * @param registry 物理模型注册表\n * @returns 选中的物理模型\n * @throws {Error} 如果公开模型不存在、kind 为 \"router\" 或候选模型不可用\n */\nexport function resolvePublicModelCandidate(\n publicModelId: string,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n): PhysicalModel {\n const pub = publicModels[publicModelId];\n if (!pub) {\n throw new Error(`Unknown public model: ${publicModelId}`);\n }\n if (pub.kind === \"router\") {\n throw new Error(\"Cannot resolve router model directly\");\n }\n\n const candidateId = selectCandidateId(pub.candidates, registry, pub.selection ?? \"cheapest\");\n const candidate = registry.get(candidateId);\n if (!candidate) {\n throw new Error(`Candidate model not found in registry: ${candidateId}`);\n }\n return candidate;\n}\n\nfunction selectCandidateId(\n candidates: string[],\n registry: ModelRegistry,\n selection: \"cheapest\" | \"first\",\n): string {\n if (candidates.length === 0) {\n throw new Error(\"No candidates available for public model\");\n }\n if (selection === \"first\") {\n const first = candidates[0]!;\n if (!registry.has(first)) {\n throw new Error(`Candidate model not found in registry: ${first}`);\n }\n return first;\n }\n\n let selectedId: string | undefined;\n let selectedCost = Number.POSITIVE_INFINITY;\n\n for (const candidateId of candidates) {\n const model = registry.get(candidateId);\n if (!model) {\n throw new Error(`Candidate model not found in registry: ${candidateId}`);\n }\n\n const cost = model.inputPrice + model.outputPrice;\n if (cost < selectedCost) {\n selectedId = candidateId;\n selectedCost = cost;\n }\n }\n\n if (!selectedId) {\n throw new Error(\"No candidates available for public model\");\n }\n\n return selectedId;\n}\n","import { createHash } from \"node:crypto\";\n\nimport type { Tier } from \"./router/types.js\";\n\n/**\n * 自动路由请求的 session pinning 状态。\n *\n * 这里缓存的是最近一次稳定命中的 alias / physical model 组合,用来减少同一\n * 会话内的 tier 抖动。\n */\nexport type SessionEntry = {\n sessionId: string;\n physicalModelId: string;\n routedPublicModel: string;\n pinnedTier: Tier;\n createdAt: number;\n updatedAt: number;\n expiresAt: number;\n inputTokens: number;\n outputTokens: number;\n costEstimate: number;\n};\n\nexport type SessionConfig = {\n enabled: boolean;\n ttlMs: number;\n cleanupIntervalMs: number;\n};\n\nexport const DEFAULT_SESSION_CONFIG: SessionConfig = {\n enabled: true,\n ttlMs: 30 * 60 * 1000,\n cleanupIntervalMs: 5 * 60 * 1000,\n};\n\nexport type SessionStats = {\n enabled: boolean;\n size: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCostEstimate: number;\n};\n\n/**\n * 仅服务于 `auto` 路由的轻量内存 session store。\n *\n * 它不是通用会话数据库;这里只关心 pinning、TTL 和少量成本统计。\n */\nexport class SessionStore {\n private readonly config: SessionConfig;\n private readonly sessions = new Map<string, SessionEntry>();\n private readonly cleanupTimer?: NodeJS.Timeout;\n\n constructor(config: Partial<SessionConfig> = {}) {\n this.config = { ...DEFAULT_SESSION_CONFIG, ...config };\n\n // 后台被动清理过期 session,避免长期运行时内存无限增长。\n if (this.config.enabled && this.config.cleanupIntervalMs > 0) {\n this.cleanupTimer = setInterval(() => {\n this.cleanupExpired();\n }, this.config.cleanupIntervalMs);\n this.cleanupTimer.unref?.();\n }\n }\n\n /**\n * 读取一个未过期 session;如果已经过期,会顺手删除并返回 undefined。\n */\n getSession(sessionId: string | undefined): SessionEntry | undefined {\n if (!this.config.enabled || !sessionId) return undefined;\n\n const entry = this.sessions.get(sessionId);\n if (!entry) return undefined;\n if (this.isExpired(entry)) {\n this.sessions.delete(sessionId);\n return undefined;\n }\n\n return entry;\n }\n\n /**\n * 创建或更新某个 session 的 pinning 结果,同时保留历史用量累计值。\n */\n setSession(\n sessionId: string | undefined,\n input: {\n physicalModelId: string;\n routedPublicModel: string;\n pinnedTier: Tier;\n },\n ): SessionEntry | undefined {\n if (!this.config.enabled || !sessionId) return undefined;\n\n const now = Date.now();\n const existing = this.getSession(sessionId);\n const entry: SessionEntry = {\n sessionId,\n physicalModelId: input.physicalModelId,\n routedPublicModel: input.routedPublicModel,\n pinnedTier: input.pinnedTier,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n expiresAt: now + this.config.ttlMs,\n inputTokens: existing?.inputTokens ?? 0,\n outputTokens: existing?.outputTokens ?? 0,\n costEstimate: existing?.costEstimate ?? 0,\n };\n\n this.sessions.set(sessionId, entry);\n return entry;\n }\n\n /**\n * 只刷新 TTL,不改动当前 alias / physical model 选择结果。\n */\n touchSession(sessionId: string | undefined): boolean {\n const entry = this.getSession(sessionId);\n if (!entry) return false;\n\n const now = Date.now();\n entry.updatedAt = now;\n entry.expiresAt = now + this.config.ttlMs;\n return true;\n }\n\n clearSession(sessionId: string | undefined): boolean {\n if (!sessionId) return false;\n return this.sessions.delete(sessionId);\n }\n\n clearAll(): void {\n this.sessions.clear();\n }\n\n /**\n * 返回清理过期项后的聚合统计。\n */\n getStats(): SessionStats {\n this.cleanupExpired();\n\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalCostEstimate = 0;\n\n for (const entry of this.sessions.values()) {\n totalInputTokens += entry.inputTokens;\n totalOutputTokens += entry.outputTokens;\n totalCostEstimate += entry.costEstimate;\n }\n\n return {\n enabled: this.config.enabled,\n size: this.sessions.size,\n totalInputTokens,\n totalOutputTokens,\n totalCostEstimate,\n };\n }\n\n /**\n * 把一次上游调用的 token / cost 增量累计到 session 上。\n */\n recordUsage(\n sessionId: string | undefined,\n usage: {\n inputTokens?: number;\n outputTokens?: number;\n costEstimate?: number;\n },\n ): void {\n const entry = this.getSession(sessionId);\n if (!entry) return;\n\n entry.inputTokens += usage.inputTokens ?? 0;\n entry.outputTokens += usage.outputTokens ?? 0;\n entry.costEstimate += usage.costEstimate ?? 0;\n entry.updatedAt = Date.now();\n }\n\n /**\n * 停止后台清理定时器。\n */\n close(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n }\n }\n\n private cleanupExpired(): void {\n if (!this.config.enabled) return;\n\n for (const [sessionId, entry] of this.sessions) {\n if (this.isExpired(entry)) {\n this.sessions.delete(sessionId);\n }\n }\n }\n\n private isExpired(entry: SessionEntry): boolean {\n return Date.now() >= entry.expiresAt;\n }\n}\n\n/**\n * 把请求文本和工具集合归一化后压缩成稳定短哈希,作为隐式 session key。\n */\nexport function hashRequestContent(\n content: string,\n toolNames: string[] = [],\n): string {\n const normalizedContent = content.trim().replace(/\\s+/g, \" \");\n const normalizedTools = [...toolNames]\n .map((tool) => tool.trim())\n .filter(Boolean)\n .sort()\n .join(\",\");\n return hashHex(`${normalizedContent}\\n${normalizedTools}`, 8);\n}\n\n/**\n * 优先读取显式 `x-session-id`,否则退化为首条 user 消息内容哈希。\n */\nexport function deriveSessionId(\n headers: Record<string, string | string[] | undefined>,\n messages: unknown[],\n): string | undefined {\n const explicit = headers[\"x-session-id\"];\n const explicitId = pickHeaderValue(explicit);\n if (explicitId) return explicitId;\n\n const firstUserContent = findFirstUserContent(messages);\n if (!firstUserContent) return undefined;\n\n return hashRequestContent(firstUserContent);\n}\n\nfunction pickHeaderValue(\n value: string | string[] | undefined,\n): string | undefined {\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed || undefined;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n const trimmed = item.trim();\n if (trimmed) return trimmed;\n }\n }\n\n return undefined;\n}\n\n/**\n * 提取第一条 user 消息文本,用作默认 session key 来源。\n */\nfunction findFirstUserContent(messages: unknown[]): string | undefined {\n for (const message of messages) {\n if (!message || typeof message !== \"object\") continue;\n\n const record = message as Record<string, unknown>;\n if (record.role !== \"user\") continue;\n\n const text = contentToText(record.content);\n if (text.trim()) return text;\n }\n\n return undefined;\n}\n\n/**\n * 同时兼容 `content: string` 和 OpenAI 风格的多 part 文本数组。\n */\nfunction contentToText(content: unknown): string {\n if (typeof content === \"string\") return content;\n\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (!part || typeof part !== \"object\") return \"\";\n const record = part as Record<string, unknown>;\n return record.type === \"text\" && typeof record.text === \"string\"\n ? record.text\n : \"\";\n })\n .filter(Boolean)\n .join(\" \");\n }\n\n return \"\";\n}\n\nfunction hashHex(value: string, length: number): string {\n return createHash(\"sha256\").update(value).digest(\"hex\").slice(0, length);\n}\n"],"mappings":"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AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA;AAAA,MAEd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA;AAAA,MAEf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,wBAAwB;AAAA;AAAA,MAEtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA;AAAA,IAGA,qBAAqB;AAAA;AAAA,MAEnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA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","names":["registry","text","registry","physicalModel","trace","headers"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/config-loader.ts","../src/proxy-config-resolver.ts","../src/proxy.ts","../src/router/rules.ts","../src/router/selector.ts","../src/router/strategy.ts","../src/router/config.ts","../src/router/trace.ts","../src/router/index.ts","../src/config.ts","../src/model-registry.ts","../src/public-model-resolver.ts","../src/session.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\n\nimport { loadConfig } from \"./config-loader.js\";\nimport { resolveProxyConfig } from \"./proxy-config-resolver.js\";\nimport { startProxy as startProxyImpl, VERSION } from \"./proxy.js\";\nimport type { ProxyHandle, ProxyOptions } from \"./proxy.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CliRuntime = {\n log: (msg: string) => void;\n error: (msg: string) => void;\n exit: (code: number) => void;\n onSignal: (signal: string, handler: () => void) => void;\n startProxy: (options: ProxyOptions) => Promise<ProxyHandle>;\n};\n\nexport type ParsedArgs = {\n help: boolean;\n version: boolean;\n config?: string;\n port?: number;\n baseUrl?: string;\n apiKey?: string;\n unknown: string[];\n};\n\n// ---------------------------------------------------------------------------\n// Argument parsing\n// ---------------------------------------------------------------------------\n\nexport function parseArgs(rawArgs: string[]): ParsedArgs {\n const result: ParsedArgs = { help: false, version: false, unknown: [] };\n\n for (let i = 0; i < rawArgs.length; i++) {\n const arg = rawArgs[i]!;\n\n switch (arg) {\n case \"--help\":\n case \"-h\":\n result.help = true;\n break;\n\n case \"--version\":\n case \"-v\":\n result.version = true;\n break;\n\n case \"--port\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n break;\n }\n const port = Number.parseInt(val, 10);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n result.unknown.push(arg, val);\n } else {\n result.port = port;\n }\n break;\n }\n\n case \"--config\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n } else {\n result.config = val;\n }\n break;\n }\n\n case \"--api-key\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n } else {\n result.apiKey = val;\n }\n break;\n }\n\n case \"--base-url\": {\n const val = rawArgs[++i];\n if (val === undefined) {\n result.unknown.push(arg);\n } else {\n result.baseUrl = val;\n }\n break;\n }\n\n default:\n result.unknown.push(arg);\n break;\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Help text\n// ---------------------------------------------------------------------------\n\nfunction helpText(): string {\n return `llm-router v${VERSION}\n\nUsage:\n llm-router --config <path> Start the local proxy with config\n llm-router --config <path> --port 9000 Listen on a custom port\n\nOptions:\n --help, -h Show help\n --version, -v Show version\n --config <path> Configuration file path (required)\n --port <number> Override proxy port\n --api-key <key> Override API key\n --base-url <url> Override upstream API base URL`;\n}\n\n// ---------------------------------------------------------------------------\n// Default runtime\n// ---------------------------------------------------------------------------\n\nconst defaultRuntime: CliRuntime = {\n log: console.log,\n error: console.error,\n exit: (code) => process.exit(code),\n onSignal: (signal, handler) => process.on(signal, handler),\n startProxy: startProxyImpl,\n};\n\n// ---------------------------------------------------------------------------\n// CLI execution\n// ---------------------------------------------------------------------------\n\nexport async function runCli(rawArgs: string[], runtime: Partial<CliRuntime> = {}): Promise<void> {\n const rt: CliRuntime = { ...defaultRuntime, ...runtime };\n const args = parseArgs(rawArgs);\n\n // --help\n if (args.help) {\n rt.log(helpText());\n rt.exit(0);\n return;\n }\n\n // --version\n if (args.version) {\n rt.log(`v${VERSION}`);\n rt.exit(0);\n return;\n }\n\n // Unknown commands\n if (args.unknown.length > 0) {\n for (const cmd of args.unknown) {\n rt.error(`Unsupported command: ${cmd}`);\n }\n rt.exit(1);\n return;\n }\n\n // --config is required\n if (!args.config) {\n rt.error(\"Missing required argument: --config <path>\");\n rt.exit(1);\n return;\n }\n\n // Load config from file\n const rawConfig = loadConfig({ kind: \"file\", path: args.config });\n\n // Resolve proxy config with CLI overrides\n const proxyConfig = resolveProxyConfig(rawConfig.proxy, {\n port: args.port,\n upstreamUrl: args.baseUrl,\n apiKey: args.apiKey,\n });\n const runtimeConfig = {\n ...rawConfig,\n proxy: proxyConfig,\n };\n\n // Start proxy\n const handle = await rt.startProxy({\n config: runtimeConfig,\n });\n rt.log(`llm-router listening on http://127.0.0.1:${handle.port}`);\n\n // Graceful shutdown\n const shutdown = async () => {\n rt.log(\"\\nShutting down...\");\n await handle.close();\n rt.exit(0);\n };\n\n rt.onSignal(\"SIGINT\", () => {\n void shutdown();\n });\n rt.onSignal(\"SIGTERM\", () => {\n void shutdown();\n });\n}\n\n// ---------------------------------------------------------------------------\n// Main detection\n// ---------------------------------------------------------------------------\n\nexport function isMain(): boolean {\n return process.argv[1] === fileURLToPath(import.meta.url);\n}\n\n// ---------------------------------------------------------------------------\n// Entry point\n// ---------------------------------------------------------------------------\n\nif (isMain()) {\n void runCli(process.argv.slice(2));\n}\n","import { readFileSync } from \"node:fs\";\nimport type { ConfigSource, RawConfig } from \"./config-schema.js\";\n\nfunction hasOwn(value: object, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction assertFiniteNumber(value: unknown, path: string): asserts value is number {\n if (!isFiniteNumber(value)) {\n throw new Error(`${path} must be a finite number`);\n }\n}\n\nfunction assertPublicModelMetadata(value: unknown, path: string): void {\n if (!value || typeof value !== \"object\") {\n throw new Error(`${path}.metadata is required`);\n }\n\n const metadata = value as Record<string, unknown>;\n const metadataPath = `${path}.metadata`;\n\n for (const key of [\"name\", \"reasoning\", \"contextWindow\", \"maxTokens\", \"cost\"]) {\n if (!hasOwn(metadata, key)) {\n throw new Error(`${metadataPath}.${key} is required`);\n }\n }\n\n if (typeof metadata.name !== \"string\") {\n throw new Error(`${metadataPath}.name must be a string`);\n }\n\n if (typeof metadata.reasoning !== \"boolean\") {\n throw new Error(`${metadataPath}.reasoning must be a boolean`);\n }\n\n if (typeof metadata.contextWindow !== \"number\") {\n throw new Error(`${metadataPath}.contextWindow must be a number`);\n }\n\n if (typeof metadata.maxTokens !== \"number\") {\n throw new Error(`${metadataPath}.maxTokens must be a number`);\n }\n\n if (!metadata.cost || typeof metadata.cost !== \"object\") {\n throw new Error(`${metadataPath}.cost must be an object`);\n }\n\n const cost = metadata.cost as Record<string, unknown>;\n const costPath = `${metadataPath}.cost`;\n\n for (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\"]) {\n if (!hasOwn(cost, key)) {\n throw new Error(`${costPath}.${key} is required`);\n }\n }\n\n for (const key of [\"input\", \"output\", \"cacheRead\", \"cacheWrite\"] as const) {\n if (typeof cost[key] !== \"number\") {\n throw new Error(`${costPath}.${key} must be a number`);\n }\n }\n}\n\nfunction assertAliasPublicModel(publicModels: RawConfig[\"publicModels\"], id: string, path: string): void {\n const publicModel = publicModels[id];\n\n if (!publicModel) {\n throw new Error(`${path} references unknown publicModel: ${id}`);\n }\n\n if (publicModel.kind !== \"alias\") {\n throw new Error(`${path} must reference a publicModel with kind: \"alias\"`);\n }\n}\n\n/**\n * 加载并校验配置文件\n * @param source 配置源:内联对象或文件路径\n * @returns 校验通过的配置对象\n * @throws {Error} 如果配置不合法(重复 ID、引用不存在、缺少 auto 等)\n */\nexport function loadConfig(source: ConfigSource): RawConfig {\n const raw = source.kind === \"inline\" ? source.config : JSON.parse(readFileSync(source.path, \"utf-8\"));\n validateConfig(raw);\n return raw;\n}\n\n/**\n * 校验配置完整性和引用关系\n *\n * 校验项:\n * - models[].id 唯一性\n * - publicModels.auto 必须是 router 且 metadata 完整\n * - publicModels[*].candidates[] 引用必须在 models 中存在\n * - publicModels[*].candidates[] 非空(仅 alias)\n * - routing.tiers[*].publicModel / fallback[] 只能引用 alias publicModel\n * - 四个 tier 必须完整声明\n * - proxy.port 是 1-65535 整数\n * - proxy.headers 值都是字符串\n *\n * @param config 待校验的配置对象\n * @throws {Error} 如果校验失败,错误信息包含具体字段和期望值\n */\nfunction validateConfig(config: RawConfig): void {\n const modelIds = new Set<string>();\n\n for (const model of config.models) {\n if (modelIds.has(model.id)) {\n throw new Error(`Duplicate model ID: ${model.id}`);\n }\n\n modelIds.add(model.id);\n }\n\n const auto = config.publicModels.auto;\n if (!auto || auto.kind !== \"router\") {\n throw new Error('publicModels must contain \"auto\" with kind: \"router\"');\n }\n assertPublicModelMetadata(auto.metadata, \"publicModels.auto\");\n\n for (const [publicModelId, publicModel] of Object.entries(config.publicModels)) {\n if (publicModel.kind === \"router\") {\n if (publicModelId !== \"auto\") {\n throw new Error(`publicModels.${publicModelId}: only auto may use kind: \"router\"`);\n }\n assertPublicModelMetadata(publicModel.metadata, `publicModels.${publicModelId}`);\n continue;\n }\n\n if (!Array.isArray(publicModel.candidates) || publicModel.candidates.length === 0) {\n throw new Error(`publicModels.${publicModelId}.candidates must not be empty`);\n }\n\n for (const candidate of publicModel.candidates) {\n if (!modelIds.has(candidate)) {\n throw new Error(`Unknown candidate '${candidate}' in publicModels.${publicModelId}`);\n }\n }\n\n if (publicModel.metadata != null) {\n assertPublicModelMetadata(publicModel.metadata, `publicModels.${publicModelId}`);\n }\n }\n\n for (const tier of [\"SIMPLE\", \"MEDIUM\", \"COMPLEX\", \"REASONING\"] as const) {\n if (!config.routing.tiers[tier]) {\n throw new Error(`routing.tiers.${tier} is required`);\n }\n }\n\n for (const [tier, tierConfig] of Object.entries(config.routing.tiers)) {\n assertAliasPublicModel(config.publicModels, tierConfig.publicModel, `routing.tiers.${tier}.publicModel`);\n\n for (const fallbackId of tierConfig.fallback ?? []) {\n assertAliasPublicModel(config.publicModels, fallbackId, `routing.tiers.${tier}.fallback`);\n }\n }\n\n if (hasOwn(config.routing as object, \"tierBoundaries\")) {\n const tierBoundaries = config.routing.tierBoundaries;\n\n if (!tierBoundaries || typeof tierBoundaries !== \"object\" || Array.isArray(tierBoundaries)) {\n throw new Error(\"routing.tierBoundaries must be an object\");\n }\n\n const { simpleMedium, mediumComplex, complexReasoning } = tierBoundaries;\n\n assertFiniteNumber(simpleMedium, \"routing.tierBoundaries.simpleMedium\");\n assertFiniteNumber(mediumComplex, \"routing.tierBoundaries.mediumComplex\");\n assertFiniteNumber(complexReasoning, \"routing.tierBoundaries.complexReasoning\");\n\n if (!(simpleMedium <= mediumComplex && mediumComplex <= complexReasoning)) {\n throw new Error(\n \"routing.tierBoundaries must satisfy simpleMedium <= mediumComplex <= complexReasoning\"\n );\n }\n }\n\n if (hasOwn(config.routing as object, \"confidenceThreshold\")) {\n assertFiniteNumber(config.routing.confidenceThreshold, \"routing.confidenceThreshold\");\n\n if (config.routing.confidenceThreshold < 0 || config.routing.confidenceThreshold > 1) {\n throw new Error(\"routing.confidenceThreshold must be between 0 and 1\");\n }\n }\n\n if (!Number.isInteger(config.proxy.port) || config.proxy.port < 1 || config.proxy.port > 65535) {\n throw new Error(`proxy.port must be an integer between 1-65535, got: ${config.proxy.port}`);\n }\n\n if (config.proxy.headers) {\n for (const [key, value] of Object.entries(config.proxy.headers)) {\n if (typeof value !== \"string\") {\n throw new Error(`proxy.headers['${key}'] must be a string, got: ${typeof value}`);\n }\n }\n }\n}\n","import type { ProxyConfig } from \"./config-schema.js\";\n\n/**\n * 覆盖配置(来自 CLI flag 或插件 pluginConfig),所有字段可选\n *\n * 优先级规则:\n * - CLI flag 优先级最高(--port, --api-key, --headers 等)\n * - 插件 pluginConfig 次之(pluginConfig.port, pluginConfig.upstreamUrl 等)\n * - 配置文件最低\n *\n * 调用方负责按优先级合并多个覆盖源后再传入此函数。\n */\nexport interface ProxyConfigOverrides {\n port?: number;\n upstreamUrl?: string;\n apiKey?: string;\n headers?: Record<string, string>;\n trace?: \"off\" | \"summary\" | \"debug\";\n}\n\nexport type { ProxyConfig };\n\n/**\n * 将基础配置与覆盖配置合并,返回最终的 ProxyConfig。\n *\n * 覆盖优先级:overrides > baseConfig(逐字段覆盖)。\n *\n * headers 合并规则:\n * - 结果为 `{ ...baseConfig.headers, ...overrides.headers }`\n * - overrides.headers 中的 key 优先覆盖 baseConfig.headers 中相同的 key\n * - baseConfig.headers 中未被覆盖的 key 原样保留\n * - 若两者均无 headers,结果为 undefined\n *\n * @param baseConfig 来自配置文件的基础 proxy 配置\n * @param overrides 来自 CLI flag 或插件 pluginConfig 的覆盖配置(可选)\n * @returns 合并后的 ProxyConfig\n */\nexport function resolveProxyConfig(\n baseConfig: ProxyConfig,\n overrides: ProxyConfigOverrides = {},\n): ProxyConfig {\n const mergedHeaders = mergeHeaders(baseConfig.headers, overrides.headers);\n\n return {\n port: overrides.port ?? baseConfig.port,\n upstreamUrl: overrides.upstreamUrl ?? baseConfig.upstreamUrl,\n apiKey: overrides.apiKey ?? baseConfig.apiKey,\n headers: mergedHeaders,\n trace: overrides.trace ?? baseConfig.trace,\n };\n}\n\n/**\n * 合并两个 headers 对象,override 优先。\n *\n * @param base 基础 headers(来自配置文件)\n * @param override 覆盖 headers(来自 CLI flag 或 pluginConfig)\n * @returns 合并后的 headers,若两者均为 undefined 则返回 undefined\n */\nfunction mergeHeaders(\n base: Record<string, string> | undefined,\n override: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n if (base === undefined && override === undefined) {\n return undefined;\n }\n return { ...base, ...override };\n}\n","import http from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport type { RouterConfig } from \"./config.js\";\nimport { resolveConfig } from \"./config.js\";\nimport type { PublicModelConfig, RawConfig } from \"./config-schema.js\";\nimport type { TierEntry } from \"./config-schema.js\";\nimport { createModelRegistry } from \"./model-registry.js\";\nimport type { ModelRegistry } from \"./model-registry.js\";\nimport { resolvePublicModelCandidate } from \"./public-model-resolver.js\";\nimport {\n DEFAULT_ROUTING_CONFIG,\n buildTraceSummary,\n emitRouteTrace,\n getPromptPreview,\n route,\n resolveTraceWriter,\n} from \"./router/index.js\";\nimport type {\n ModelPricing,\n RouteTraceLog,\n RoutingConfig,\n RoutingDecision,\n TraceAttempt,\n TraceLogger,\n TraceReason,\n TraceSessionAction,\n Tier,\n TierConfig,\n} from \"./router/index.js\";\nimport type { RouterOptions } from \"./router/types.js\";\nimport type { SessionConfig } from \"./session.js\";\nimport { deriveSessionId, SessionStore } from \"./session.js\";\n\nexport const VERSION = \"1.0.3\";\n\nconst HOP_BY_HOP = new Set([\n \"connection\",\n \"keep-alive\",\n \"proxy-authenticate\",\n \"proxy-authorization\",\n \"te\",\n \"trailer\",\n \"transfer-encoding\",\n \"upgrade\",\n \"host\",\n \"content-length\",\n]);\n\nconst RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);\n/**\n * 运行时韧性保护默认值。\n *\n * 这里统一集中定义,方便 CLI / 测试 / 未来配置入口共享同一组基线。\n */\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024;\nconst DEFAULT_BODY_READ_TIMEOUT_MS = 30_000;\nconst DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS = 300_000;\n\n/**\n * `startProxy()` 实际运行时使用的完整限制集合。\n *\n * 这里把每个限制都解析成必填字段,避免请求处理路径再去分支判断默认值。\n */\nexport type ProxyRuntimeLimits = {\n /**\n * 允许单个客户端请求体占用的最大 UTF-8 字节数。\n */\n maxBodyBytes: number;\n /**\n * 允许客户端在请求体读取阶段占用连接的最长时间。\n */\n bodyReadTimeoutMs: number;\n /**\n * 单次上游请求允许占用的最长时间。\n *\n * 超时后 proxy 会主动 abort 当前 fetch,并把它转换成面向客户端的 504。\n */\n upstreamRequestTimeoutMs: number;\n};\n\n/**\n * 调用方可按需覆盖的运行时限制输入。\n */\nexport type ProxyRuntimeLimitInput = Partial<ProxyRuntimeLimits>;\n\n/**\n * Proxy 在未显式传参时采用的默认运行时限制。\n */\nexport const DEFAULT_RUNTIME_LIMITS: ProxyRuntimeLimits = {\n maxBodyBytes: DEFAULT_MAX_BODY_BYTES,\n bodyReadTimeoutMs: DEFAULT_BODY_READ_TIMEOUT_MS,\n upstreamRequestTimeoutMs: DEFAULT_UPSTREAM_REQUEST_TIMEOUT_MS,\n};\n\n/**\n * 归一化运行时限制输入,确保请求处理路径拿到的是完整、稳定的配置对象。\n */\nexport function resolveRuntimeLimits(\n input?: ProxyRuntimeLimitInput,\n): ProxyRuntimeLimits {\n return {\n maxBodyBytes: input?.maxBodyBytes ?? DEFAULT_RUNTIME_LIMITS.maxBodyBytes,\n bodyReadTimeoutMs:\n input?.bodyReadTimeoutMs ?? DEFAULT_RUNTIME_LIMITS.bodyReadTimeoutMs,\n upstreamRequestTimeoutMs:\n input?.upstreamRequestTimeoutMs ??\n DEFAULT_RUNTIME_LIMITS.upstreamRequestTimeoutMs,\n };\n}\n\n/**\n * 当前版本对外暴露的可请求 model 白名单。\n *\n * `publicModels` 仍然可以包含 `flash` / `pro` / `lite` / `think` 等 alias,\n * 但它们只作为 Router 内部语义输出,不接受客户端显式请求。\n */\nconst REQUESTABLE_PUBLIC_MODELS = new Set([\"auto\"]);\nconst PUBLIC_HEADER_PREFIXES = [\"x-xy-router-\"] as const;\n\n/**\n * 启动本地 HTTP proxy 所需的全部输入。\n */\nexport type ProxyOptions = {\n config: RawConfig;\n traceLogger?: TraceLogger;\n session?: Partial<SessionConfig>;\n runtimeLimits?: ProxyRuntimeLimitInput;\n};\n\n/**\n * 已启动 proxy 的可关闭句柄。\n */\nexport type ProxyHandle = {\n port: number;\n baseUrl: string;\n close: () => Promise<void>;\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 请求体读取阶段使用的结构化错误。\n *\n * 它把“HTTP 应返回什么状态码”与“内部异常控制流”绑定在一起,方便边界层统一\n * 转换成 OpenAI-compatible 错误响应。\n */\nclass BodyReadError extends Error {\n readonly statusCode: number;\n\n constructor(statusCode: number, message: string) {\n super(message);\n this.name = \"BodyReadError\";\n this.statusCode = statusCode;\n }\n}\n\n/**\n * 判断未知异常是否来自请求体读取阶段,避免把 413 一类客户端错误误报成 502。\n */\nfunction isBodyReadError(error: unknown): error is BodyReadError {\n return error instanceof BodyReadError;\n}\n\n/**\n * 对请求体读取失败返回结构化客户端错误,并在响应刷出后关闭当前连接。\n *\n * 这里不能在 `readBody()` 的超时/超限分支里立刻 `req.destroy()`,否则客户端往往只会\n * 看到底层 `ECONNRESET`,拿不到 OpenAI-compatible JSON 错误。正确顺序是:\n * 1. 先把 408/413 之类的结构化错误写回客户端;\n * 2. 再把这个“请求体尚未完整消费”的连接标记为不可复用;\n * 3. 等响应完成后销毁 request/socket,避免半读状态的连接进入 keep-alive 池。\n */\nfunction writeBodyReadErrorAndCloseRequest(\n req: IncomingMessage,\n res: ServerResponse,\n error: BodyReadError,\n): void {\n res.shouldKeepAlive = false;\n res.setHeader(\"connection\", \"close\");\n res.once(\"finish\", () => {\n if (!req.destroyed) {\n req.destroy();\n }\n });\n writeOpenAiError(res, error.statusCode, error.message);\n}\n\n/**\n * 读取完整请求体文本。\n *\n * 同时负责:\n * - `maxBodyBytes`:限制单个请求可读入的总字节数;\n * - `bodyReadTimeoutMs`:限制客户端在“持续占着连接但迟迟不发完 body”时可占用的时间。\n *\n * 超时分支只 reject,不在这里直接 `req.destroy()`。这样外层 `proxyChat()` 仍有机会\n * 返回结构化 408 JSON,而不是让客户端只看到 `ECONNRESET`。\n */\nfunction readBody(\n req: IncomingMessage,\n limits: ProxyRuntimeLimits,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n let bodyBytes = 0;\n let settled = false;\n req.setEncoding(\"utf8\");\n const timeout = setTimeout(() => {\n rejectOnce(new BodyReadError(408, \"Request body read timeout\"));\n }, limits.bodyReadTimeoutMs);\n\n const cleanup = (): void => {\n clearTimeout(timeout);\n req.off(\"data\", onData);\n req.off(\"end\", onEnd);\n req.off(\"error\", onError);\n };\n\n const rejectOnce = (error: unknown): void => {\n if (settled) return;\n settled = true;\n cleanup();\n reject(error);\n };\n\n const resolveOnce = (value: string): void => {\n if (settled) return;\n settled = true;\n cleanup();\n resolve(value);\n };\n\n const onData = (chunk: string): void => {\n if (settled) return;\n\n bodyBytes += Buffer.byteLength(chunk, \"utf8\");\n if (bodyBytes > limits.maxBodyBytes) {\n rejectOnce(new BodyReadError(413, \"Payload Too Large\"));\n return;\n }\n\n body += chunk;\n };\n\n const onEnd = (): void => resolveOnce(body);\n const onError = (error: Error): void => rejectOnce(error);\n\n req.on(\"data\", onData);\n req.on(\"end\", onEnd);\n req.on(\"error\", onError);\n });\n}\n\n/**\n * 从 OpenAI / OpenClaw 风格消息数组里提取路由真正关心的文本视图。\n *\n * - `text`:非 system 消息拼接后的总文本\n * - `routeText`:用于路由判定的 user 文本;OpenClaw 新格式会在尾部放置\n * 两条连续 user 消息,此时前一条承载真实 query\n * - `system`:system prompt 聚合结果\n * - `openingText`:第一段非空文本,便于未来扩展首轮特征\n */\ntype ExtractedPrompt = {\n text: string;\n routeText: string;\n system?: string;\n openingText: string;\n};\n\nconst OPENCLAW_CLI_TURN_PATTERN =\n /(?:^|\\n)\\[[^\\]\\n]+?\\]\\s+([\\s\\S]*?)(?=(?:\\n\\[[^\\]\\n]+?\\]\\s+)|$)/g;\n\n/**\n * 保留 user 文本原样参与路由。\n *\n * 曾经这里会用 `OPENCLAW_CLI_TURN_PATTERN` 从 OpenClaw CLI transcript 中截取\n * 某一轮文本;该逻辑先保留但停用,便于后续确认是否需要恢复。\n */\nfunction extractRouteTextFromUserMessage(text: string): string {\n // const matches = [...text.matchAll(OPENCLAW_CLI_TURN_PATTERN)];\n // const last = matches.at(-1)?.[1]?.trim();\n // return last || text;\n void OPENCLAW_CLI_TURN_PATTERN;\n return text;\n}\n\n/**\n * 从 messages 中抽取 Router 需要的 prompt 视图。\n */\nfunction extractPrompt(messages: unknown[]): ExtractedPrompt {\n const parts: string[] = [];\n let system: string | undefined;\n let openingText = \"\";\n let routeUserText = \"\";\n let previousRole: unknown;\n let previousUserText = \"\";\n\n for (const msg of messages) {\n if (!msg || typeof msg !== \"object\") continue;\n\n const role: unknown = (msg as Record<string, unknown>).role;\n const content: unknown = (msg as Record<string, unknown>).content;\n\n let text = \"\";\n if (typeof content === \"string\") {\n text = content;\n } else if (Array.isArray(content)) {\n text = content\n .filter(\n (p): p is { type: string; text?: string } =>\n typeof p === \"object\" &&\n p !== null &&\n (p as Record<string, unknown>).type === \"text\",\n )\n .map((p) => (typeof p.text === \"string\" ? p.text : \"\"))\n .join(\" \");\n }\n\n if (role === \"system\") {\n system = system ? `${system}\\n${text}` : text;\n } else {\n parts.push(text);\n if (role === \"user\" && text.trim()) {\n if (previousRole === \"user\" && previousUserText.trim()) {\n routeUserText = previousUserText;\n } else {\n routeUserText = extractRouteTextFromUserMessage(text);\n }\n }\n if (!openingText && text.trim()) {\n openingText = text;\n }\n }\n\n previousRole = role;\n previousUserText =\n role === \"user\" ? extractRouteTextFromUserMessage(text) : \"\";\n }\n\n const text = parts.join(\" \");\n return { text, routeText: routeUserText || text, system, openingText };\n}\n\n// ---------------------------------------------------------------------------\n// Upstream headers\n// ---------------------------------------------------------------------------\n\n/**\n * 生成转发到上游的请求头。\n *\n * 优先级:\n * 1. 来自原始请求的非 hop-by-hop header\n * 2. `config.proxy.headers` 覆盖同名 header\n * 3. 若仍无 Authorization,则补上 `config.proxy.apiKey`\n */\nfunction buildUpstreamHeaders(\n req: IncomingMessage,\n cfg: RouterConfig,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n\n const setHeader = (key: string, value: string): void => {\n const existingKey = Object.keys(headers).find(\n (candidate) => candidate.toLowerCase() === key.toLowerCase(),\n );\n if (existingKey) {\n delete headers[existingKey];\n }\n headers[key] = value;\n };\n\n for (const [key, value] of Object.entries(req.headers)) {\n if (HOP_BY_HOP.has(key.toLowerCase())) continue;\n if (value !== undefined) {\n setHeader(key, Array.isArray(value) ? value.join(\", \") : value);\n }\n }\n\n // Custom headers override request headers\n for (const [key, value] of Object.entries(cfg.headers)) {\n setHeader(key, value);\n }\n\n // Add auth if missing (header names are case-insensitive per HTTP spec)\n const hasAuth = Object.keys(headers).some(\n (k) => k.toLowerCase() === \"authorization\",\n );\n if (!hasAuth && cfg.apiKey) {\n headers.authorization = `Bearer ${cfg.apiKey}`;\n }\n\n // Ensure content-type is set\n if (!Object.keys(headers).some((k) => k.toLowerCase() === \"content-type\")) {\n setHeader(\"content-type\", \"application/json\");\n }\n\n return headers;\n}\n\n// ---------------------------------------------------------------------------\n// Response helpers\n// ---------------------------------------------------------------------------\n\n/**\n * 统一写 JSON 响应,且只在 headers 尚未发送时生效。\n */\nfunction writeJson(res: ServerResponse, status: number, body: unknown): void {\n if (!res.headersSent) {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n }\n}\n\n/**\n * 复制上游响应头,并剔除不应继续透传到客户端的 header。\n *\n * 这里会主动移除任何上游已带的公开路由头,确保最终对外头部完全由当前\n * Router 实例生成。\n */\nfunction copyResponseHeaders(\n response: Response,\n extraHeaders: Record<string, string>,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n const lower = key.toLowerCase();\n if (HOP_BY_HOP.has(lower)) continue;\n if (PUBLIC_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix)))\n continue;\n headers[key] = value;\n }\n Object.assign(headers, extraHeaders);\n return headers;\n}\n\n/**\n * 透明转发上游响应体(支持流式 body)。\n */\nasync function streamResponse(\n response: Response,\n res: ServerResponse,\n): Promise<void> {\n if (response.body) {\n for await (const chunk of response.body as AsyncIterable<Uint8Array>) {\n res.write(chunk);\n }\n }\n res.end();\n}\n\n/**\n * 输出 OpenAI-compatible 错误结构,可选附带路由诊断头。\n */\nfunction writeOpenAiError(\n res: ServerResponse,\n status: number,\n message: string,\n type: string = \"invalid_request_error\",\n code: string | null = null,\n headers?: Record<string, string>,\n): void {\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n res.setHeader(key, value);\n }\n }\n\n writeJson(res, status, {\n error: {\n message,\n type,\n param: null,\n code,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Upstream fetch with retryable detection\n// ---------------------------------------------------------------------------\n\ntype LinkedAbortSignal = {\n signal: AbortSignal;\n cleanup: () => void;\n};\n\n/**\n * 把多个 abort signal 聚合成一个 signal,供单个 `fetch()` 统一消费。\n *\n * 这里显式返回 `cleanup()`,因为上游流式响应在 `fetch()` resolve 之后仍可能继续\n * 读取 body;只有在 proxy 完全结束这次转发后,才能安全移除监听器,避免:\n * 1. 客户端中途断开后无法继续取消上游;\n * 2. 长连接场景中事件监听器残留造成泄漏。\n */\nfunction anySignal(signals: AbortSignal[]): LinkedAbortSignal {\n const controller = new AbortController();\n const listeners: Array<{ signal: AbortSignal; listener: () => void }> = [];\n let cleaned = false;\n\n const cleanup = (): void => {\n if (cleaned) return;\n cleaned = true;\n for (const { signal, listener } of listeners) {\n signal.removeEventListener(\"abort\", listener);\n }\n };\n\n const abortFrom = (signal: AbortSignal): void => {\n if (!controller.signal.aborted) {\n controller.abort(signal.reason);\n }\n cleanup();\n };\n\n for (const signal of signals) {\n if (signal.aborted) {\n abortFrom(signal);\n return { signal: controller.signal, cleanup };\n }\n }\n\n for (const signal of signals) {\n const listener = (): void => {\n abortFrom(signal);\n };\n listeners.push({ signal, listener });\n signal.addEventListener(\"abort\", listener, { once: true });\n }\n\n return { signal: controller.signal, cleanup };\n}\n\n/**\n * 上游请求结果:\n * - `ok: true`:拿到可直接返回的响应\n * - `retryable`:拿到了响应,但状态码属于可重试类\n * - `timeout`:请求已发出,但在限定时间内没等到可用响应\n * - `aborted`:客户端已断开,本次 proxy 不应再继续消耗上游资源或写回响应\n * - `network_error`:压根没拿到 HTTP 响应\n */\ntype AttemptResult = {\n cleanup: () => void;\n} & (\n | { ok: true; response: Response }\n | { ok: false; reason: \"retryable\"; response: Response }\n | { ok: false; reason: \"timeout\"; error: unknown }\n | { ok: false; reason: \"aborted\"; error: unknown }\n | { ok: false; reason: \"network_error\"; error: unknown }\n);\n\n/**\n * 执行一次上游 Chat Completions 调用,并把 public alias 改写成真实 physical\n * model。\n *\n * 这里把“客户端断连”和“上游首包超时”合并到同一个 `fetch()` signal:\n * - 客户端断开:立刻取消上游,避免继续计费/占用流式连接;\n * - 超时:只覆盖等待上游响应头这一段,首包到达后即清除计时器。\n */\nasync function fetchUpstream(\n cfg: ReturnType<typeof resolveConfig>,\n req: IncomingMessage,\n body: Record<string, unknown>,\n actualModel: string,\n runtimeLimits: ProxyRuntimeLimits,\n requestSignal: AbortSignal,\n): Promise<AttemptResult> {\n const timeoutController = new AbortController();\n const timeout = setTimeout(() => {\n timeoutController.abort();\n }, runtimeLimits.upstreamRequestTimeoutMs);\n const linkedSignal = anySignal([requestSignal, timeoutController.signal]);\n const cleanup = (): void => {\n clearTimeout(timeout);\n linkedSignal.cleanup();\n };\n\n try {\n const response = await fetch(`${cfg.baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: buildUpstreamHeaders(req, cfg),\n body: JSON.stringify({ ...body, model: actualModel }),\n signal: linkedSignal.signal,\n });\n clearTimeout(timeout);\n\n if (RETRYABLE_STATUS.has(response.status)) {\n return { ok: false, reason: \"retryable\", response, cleanup };\n }\n\n return { ok: true, response, cleanup };\n } catch (error) {\n // 若客户端断连与超时几乎同时发生,优先保留 timeout,避免把真实的上游慢响应\n // 误归类成客户端主动取消。\n if (timeoutController.signal.aborted) {\n return { ok: false, reason: \"timeout\", error, cleanup };\n }\n\n if (requestSignal.aborted) {\n return { ok: false, reason: \"aborted\", error, cleanup };\n }\n\n return { ok: false, reason: \"network_error\", error, cleanup };\n }\n}\n\n/**\n * `chooseModel()` 产出的编排结果。\n *\n * 这份结构专门服务于 `proxyChat()`:它既包含“语义层 alias 决策”,也包含\n * “实际上游 model 选择”和 trace / session 所需的附加上下文。\n */\ntype SelectedModel = {\n routedModel: string;\n actualModel: string;\n tier: Tier;\n decision?: RoutingDecision;\n routeText: string;\n requestedModel: string;\n sessionId?: string;\n routed: boolean;\n explicit: boolean;\n sessionAction: TraceSessionAction;\n};\n\n/**\n * 兼容 OpenAI 常见的两个最大输出 token 字段名。\n */\nfunction getMaxOutputTokens(body: Record<string, unknown>): number {\n const maxTokens = body.max_tokens;\n if (\n typeof maxTokens === \"number\" &&\n Number.isFinite(maxTokens) &&\n maxTokens > 0\n ) {\n return Math.ceil(maxTokens);\n }\n\n const maxCompletionTokens = body.max_completion_tokens;\n if (\n typeof maxCompletionTokens === \"number\" &&\n Number.isFinite(maxCompletionTokens) &&\n maxCompletionTokens > 0\n ) {\n return Math.ceil(maxCompletionTokens);\n }\n\n return 1024;\n}\n\n/**\n * 组装 Router 内核所需的运行时 options。\n */\nfunction buildRouterOptions(options: {\n routingConfig: RoutingConfig;\n modelPricing: Map<string, ModelPricing>;\n}): RouterOptions {\n return {\n config: options.routingConfig,\n modelPricing: options.modelPricing,\n };\n}\n\n/**\n * 把持久化 RawConfig 映射为 Router 运行时配置。\n */\nfunction buildRoutingConfigFromRawConfig(rawConfig: RawConfig): RoutingConfig {\n const scoring = {\n ...DEFAULT_ROUTING_CONFIG.scoring,\n tierBoundaries: {\n ...DEFAULT_ROUTING_CONFIG.scoring.tierBoundaries,\n ...rawConfig.routing.tierBoundaries,\n },\n confidenceThreshold:\n rawConfig.routing.confidenceThreshold ?? DEFAULT_ROUTING_CONFIG.scoring.confidenceThreshold,\n };\n\n return {\n ...DEFAULT_ROUTING_CONFIG,\n scoring,\n tiers: mapRawTierEntries(rawConfig.routing.tiers),\n overrides: {\n structuredOutputMinTier:\n rawConfig.routing.structuredOutputMinTier ?? \"MEDIUM\",\n ambiguousDefaultTier: rawConfig.routing.ambiguousDefaultTier ?? \"MEDIUM\",\n },\n };\n}\n\n/**\n * 将配置文件里的 tier 结构转换成 Router 核心使用的 primary / fallback 结构。\n */\nfunction mapRawTierEntries(\n entries: Record<Tier, TierEntry>,\n): Record<Tier, TierConfig> {\n return Object.fromEntries(\n Object.entries(entries).map(([tier, entry]) => [\n tier,\n {\n primary: entry.publicModel,\n fallback: entry.fallback ?? [],\n },\n ]),\n ) as Record<Tier, TierConfig>;\n}\n\n/**\n * 为 alias 层构造成本表。\n *\n * 注意这里的 key 是 public alias:`auto` 使用自身 metadata 成本,普通 alias\n * 则解析到实际 physical model 后继承其单价。\n */\nfunction buildModelPricingFromPublicModels(\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n): Map<string, ModelPricing> {\n return new Map(\n Object.entries(publicModels).map(([publicModelId, config]) => {\n if (config.kind === \"router\") {\n return [\n publicModelId,\n {\n inputPrice: config.metadata.cost.input,\n outputPrice: config.metadata.cost.output,\n },\n ];\n }\n\n const physicalModel = resolvePublicModelCandidate(\n publicModelId,\n publicModels,\n registry,\n );\n return [\n publicModelId,\n {\n inputPrice: physicalModel.inputPrice,\n outputPrice: physicalModel.outputPrice,\n },\n ];\n }),\n );\n}\n\nconst TIER_ORDER: Record<Tier, number> = {\n SIMPLE: 0,\n MEDIUM: 1,\n COMPLEX: 2,\n REASONING: 3,\n};\n\n/**\n * 会话 pinning 只允许“维持当前 tier 或升级”,不允许降级。\n */\nfunction isLowerTier(nextTier: Tier, pinnedTier: Tier): boolean {\n return TIER_ORDER[nextTier] < TIER_ORDER[pinnedTier];\n}\n\n/**\n * 根据 alias 出现在 tier 配置中的位置,反推出一个“显式请求时应显示的 tier”。\n *\n * 虽然当前请求边界只接受 `auto`,但保留这段逻辑能让代码在将来重新开放 alias\n * 请求时仍然自洽,也便于测试 / 内部调用复用。\n */\nfunction getExplicitTier(\n publicModelId: string,\n entries: Record<Tier, TierEntry>,\n): Tier {\n const matches = (Object.entries(entries) as Array<[Tier, TierEntry]>)\n .filter(([, entry]) => entry.publicModel === publicModelId)\n .map(([tier]) => tier);\n\n if (matches.length === 0) {\n return \"MEDIUM\";\n }\n\n return matches.reduce((highest, tier) =>\n TIER_ORDER[tier] > TIER_ORDER[highest] ? tier : highest,\n );\n}\n\n/**\n * 决定 trace reason 字段,供 summary / debug trace 共用。\n */\nfunction getTraceReason(selected: SelectedModel, failed: boolean): TraceReason {\n if (selected.explicit) return \"user\";\n if (selected.decision?.tier === \"REASONING\") return \"reasoning\";\n if (failed) return \"error\";\n return \"first-pass\";\n}\n\n/**\n * 构造对客户端公开的路由响应头。\n */\nfunction buildPublicHeaders(\n cfg: RouterConfig,\n selected: SelectedModel,\n finalTier: Tier,\n trace: string,\n): Record<string, string> {\n return {\n \"x-xy-router-model\": selected.routedModel,\n \"x-xy-router-actual-model\": selected.actualModel,\n \"x-xy-router-tier\": finalTier,\n \"x-xy-router-trace\": trace,\n \"x-xy-router-routed\": String(selected.routed),\n \"x-xy-router-fallback\": \"false\",\n \"x-xy-router-upstream\": cfg.baseUrl,\n };\n}\n\n/**\n * 发送一条结构化路由 trace,并返回紧凑 trace 字符串。\n */\nfunction emitProxyTrace(\n cfg: RouterConfig,\n selected: SelectedModel,\n finalTier: Tier,\n attempts: TraceAttempt[],\n sessionAction: TraceSessionAction,\n failed: boolean,\n): string {\n const writer = resolveTraceWriter(cfg.traceLogger);\n const reason = getTraceReason(selected, failed);\n const trace = buildTraceSummary({\n requestedModel: selected.requestedModel,\n routedModel: selected.routedModel,\n actualModel: selected.actualModel,\n tier: finalTier,\n profile: selected.decision?.profile ?? \"default\",\n reason,\n routed: selected.routed,\n explicit: selected.explicit,\n fallback: false,\n });\n const detail: RouteTraceLog = {\n trace,\n requestedModel: selected.requestedModel,\n routedModel: selected.routedModel,\n actualModel: selected.actualModel,\n tier: finalTier,\n profile: selected.decision?.profile ?? \"default\",\n reason,\n explicit: selected.explicit,\n routed: selected.routed,\n fallback: false,\n attempts,\n sessionAction,\n ...(selected.decision && {\n method: selected.decision.method,\n confidence: selected.decision.confidence,\n ...(selected.decision.score !== undefined && {\n score: selected.decision.score,\n }),\n ...(selected.decision.agenticScore !== undefined && {\n agenticScore: selected.decision.agenticScore,\n }),\n }),\n ...(cfg.traceMode === \"debug\" && {\n promptPreview: getPromptPreview(selected.routeText),\n }),\n };\n\n emitRouteTrace(cfg.traceMode, detail, writer);\n return trace;\n}\n\n/**\n * 选择本次请求最终应使用的 alias / physical model。\n *\n * 当前外部请求边界已经在 `proxyChat()` 收紧到只允许 `auto`,但这里仍保留了\n * 显式 alias 分支,使该函数在测试、未来协议调整或内部复用时更容易扩展。\n */\nfunction chooseModel(\n requestedModel: string,\n body: { messages?: unknown[]; tools?: unknown[] },\n headers: IncomingMessage[\"headers\"],\n sessionStore: SessionStore,\n cfg: RouterConfig,\n tierEntries: Record<Tier, TierEntry>,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n routerOptions: RouterOptions,\n): SelectedModel {\n const prompt = extractPrompt(body.messages ?? []);\n\n if (requestedModel !== \"auto\") {\n // 兼容式分支:如果未来重新允许显式 alias,请求会在这里直接解析到\n // physical model,而不会经过 auto 路由。\n const physicalModel = resolvePublicModelCandidate(\n requestedModel,\n publicModels,\n registry,\n );\n return {\n routedModel: requestedModel,\n actualModel: physicalModel.id,\n tier: getExplicitTier(requestedModel, tierEntries),\n routeText: prompt.routeText,\n requestedModel,\n routed: false,\n explicit: true,\n sessionAction: \"none\",\n };\n }\n\n const sessionId = deriveSessionId(headers, body.messages ?? []);\n const existing = cfg.sessionPinning\n ? sessionStore.getSession(sessionId)\n : undefined;\n const decision = route(\n prompt.routeText,\n prompt.system,\n getMaxOutputTokens(body as Record<string, unknown>),\n routerOptions,\n );\n const routedModel = decision.publicModel;\n const physicalModel = resolvePublicModelCandidate(\n routedModel,\n publicModels,\n registry,\n );\n\n // 会话已 pin 到更高 tier 时,不允许新的简单请求把它降回来。\n if (existing && isLowerTier(decision.tier, existing.pinnedTier)) {\n sessionStore.touchSession(sessionId);\n return {\n routedModel: existing.routedPublicModel,\n actualModel: existing.physicalModelId,\n tier: existing.pinnedTier,\n routeText: prompt.routeText,\n requestedModel,\n sessionId,\n routed: true,\n explicit: false,\n sessionAction: \"reuse\",\n };\n }\n\n return {\n routedModel,\n actualModel: physicalModel.id,\n tier: decision.tier,\n decision,\n routeText: prompt.routeText,\n requestedModel,\n sessionId,\n routed: true,\n explicit: false,\n sessionAction: \"none\",\n };\n}\n\n// ---------------------------------------------------------------------------\n// Chat completion proxy\n// ---------------------------------------------------------------------------\n\n/**\n * 处理 `POST /v1/chat/completions`。\n *\n * 这是整个仓库最重要的边界函数:它负责请求合法性校验、auto 路由、physical\n * model 解析、上游转发,以及所有对外响应头 / 错误格式的一致性。\n */\nasync function proxyChat(\n req: IncomingMessage,\n res: ServerResponse,\n cfg: RouterConfig,\n runtimeLimits: ProxyRuntimeLimits,\n sessionStore: SessionStore,\n tierEntries: Record<Tier, TierEntry>,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n routerOptions: RouterOptions,\n): Promise<void> {\n // Read body\n let rawBody: string;\n try {\n rawBody = await readBody(req, runtimeLimits);\n } catch (error) {\n if (isBodyReadError(error)) {\n writeBodyReadErrorAndCloseRequest(req, res, error);\n return;\n }\n throw error;\n }\n\n // Parse JSON\n let body: unknown;\n try {\n body = JSON.parse(rawBody);\n } catch {\n writeOpenAiError(res, 400, \"Invalid JSON body\");\n return;\n }\n\n if (!body || typeof body !== \"object\") {\n writeOpenAiError(res, 400, \"Body must be a JSON object\");\n return;\n }\n\n const bodyObj = body as Record<string, unknown>;\n\n const requestedModelId = bodyObj.model;\n const supportedModels = [...REQUESTABLE_PUBLIC_MODELS]\n .filter((modelId) => publicModels[modelId])\n .sort()\n .join(\", \");\n if (\n typeof requestedModelId !== \"string\" ||\n !publicModels[requestedModelId] ||\n !REQUESTABLE_PUBLIC_MODELS.has(requestedModelId)\n ) {\n // 这里故意同时检查:\n // 1. 配置里是否存在该 public model\n // 2. 它是否属于当前“允许被客户端显式请求”的极小白名单\n // 从而把 alias 从“配置驱动内部语义”与“外部请求合同”严格分开。\n writeOpenAiError(\n res,\n 400,\n `Unknown model \"${String(requestedModelId)}\". Supported models: ${supportedModels}`,\n \"invalid_request_error\",\n \"model_not_found\",\n );\n return;\n }\n\n // 先完成 alias 级别决策,再解析实际发往上游的 physical model。\n const selected = chooseModel(\n requestedModelId,\n bodyObj as { messages?: unknown[]; tools?: unknown[] },\n req.headers,\n sessionStore,\n cfg,\n tierEntries,\n publicModels,\n registry,\n routerOptions,\n );\n\n const physicalModel = registry.get(selected.actualModel);\n if (!physicalModel) {\n writeOpenAiError(\n res,\n 500,\n `Physical model not found in registry: ${selected.actualModel}`,\n );\n return;\n }\n\n const requestController = new AbortController();\n let responseFinished = false;\n /**\n * 客户端一旦在 proxy 等待/转发上游期间断开,就立刻取消上游请求,避免继续\n * 持有流式连接或产生无意义的上游计费。\n */\n const abortUpstreamRequest = (): void => {\n if (responseFinished || requestController.signal.aborted) return;\n requestController.abort();\n };\n const onRequestAborted = (): void => {\n abortUpstreamRequest();\n };\n const onResponseClose = (): void => {\n abortUpstreamRequest();\n };\n const onResponseFinish = (): void => {\n responseFinished = true;\n };\n const cleanupRequestAbortListeners = (): void => {\n req.off(\"aborted\", onRequestAborted);\n res.off(\"close\", onResponseClose);\n res.off(\"finish\", onResponseFinish);\n };\n\n req.on(\"aborted\", onRequestAborted);\n res.on(\"close\", onResponseClose);\n res.on(\"finish\", onResponseFinish);\n\n let attempt: AttemptResult | undefined;\n try {\n attempt = await fetchUpstream(\n cfg,\n req,\n bodyObj,\n physicalModel.id,\n runtimeLimits,\n requestController.signal,\n );\n const attempts: TraceAttempt[] = [\n attempt.ok\n ? { model: selected.actualModel, status: \"success\" }\n : {\n model: selected.actualModel,\n status: \"error\",\n error:\n attempt.reason === \"timeout\"\n ? \"upstream_timeout\"\n : attempt.reason === \"network_error\"\n ? \"network_error\"\n : attempt.reason === \"aborted\"\n ? \"client_aborted\"\n : `upstream_http_${attempt.response.status}`,\n },\n ];\n const finalTier = selected.tier;\n let sessionAction = selected.sessionAction;\n\n if (!attempt.ok && attempt.reason === \"aborted\") {\n emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n true,\n );\n return;\n }\n\n if (!attempt.ok && attempt.reason === \"timeout\") {\n const trace = emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n true,\n );\n const headers = buildPublicHeaders(cfg, selected, finalTier, trace);\n writeOpenAiError(\n res,\n 504,\n \"Upstream request timed out\",\n \"invalid_request_error\",\n null,\n headers,\n );\n return;\n }\n\n if (!attempt.ok && attempt.reason === \"network_error\") {\n const trace = emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n true,\n );\n const headers = buildPublicHeaders(cfg, selected, finalTier, trace);\n writeOpenAiError(\n res,\n 502,\n attempt.error instanceof Error\n ? attempt.error.message\n : \"Upstream request failed\",\n \"invalid_request_error\",\n null,\n headers,\n );\n return;\n }\n\n // 只有真正拿到 HTTP 响应后才更新 session pinning,避免把纯网络错误写成成功 pin。\n if (attempt.ok && selected.sessionId && !selected.explicit) {\n sessionStore.setSession(selected.sessionId, {\n physicalModelId: selected.actualModel,\n routedPublicModel: selected.routedModel,\n pinnedTier: finalTier,\n });\n if (sessionAction === \"none\") {\n sessionAction = \"set\";\n }\n }\n\n const trace = emitProxyTrace(\n cfg,\n selected,\n finalTier,\n attempts,\n sessionAction,\n !attempt.ok,\n );\n const headers = buildPublicHeaders(cfg, selected, finalTier, trace);\n const responseHeaders = copyResponseHeaders(attempt.response, headers);\n res.statusCode = attempt.response.status;\n for (const [k, v] of Object.entries(responseHeaders)) {\n res.setHeader(k, v);\n }\n await streamResponse(attempt.response, res);\n responseFinished = true;\n } finally {\n cleanupRequestAbortListeners();\n attempt?.cleanup();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Proxy server\n// ---------------------------------------------------------------------------\n\n/**\n * 启动本地 Router HTTP 服务。\n */\nexport async function startProxy(options: ProxyOptions): Promise<ProxyHandle> {\n const cfg = resolveConfig({\n baseUrl: options.config.proxy.upstreamUrl,\n apiKey: options.config.proxy.apiKey,\n headers: options.config.proxy.headers,\n port: options.config.proxy.port,\n traceMode: options.config.proxy.trace,\n traceLogger: options.traceLogger,\n sessionPinning: options.session?.enabled,\n });\n const runtimeLimits = resolveRuntimeLimits(options.runtimeLimits);\n const sessionStore = new SessionStore(options.session);\n const publicModels = options.config.publicModels;\n const tierEntries = options.config.routing.tiers;\n const registry = createModelRegistry(options.config.models);\n const routerOptions = buildRouterOptions({\n routingConfig: buildRoutingConfigFromRawConfig(options.config),\n modelPricing: buildModelPricingFromPublicModels(publicModels, registry),\n });\n\n const server = http.createServer((req, res) => {\n void (async () => {\n try {\n const url = req.url ?? \"/\";\n\n if (req.method === \"GET\" && url === \"/health\") {\n res.setHeader(\"content-type\", \"application/json\");\n res.end(\n JSON.stringify({\n status: \"ok\",\n baseUrl: cfg.baseUrl,\n version: VERSION,\n }),\n );\n return;\n }\n\n // 当前版本只暴露一个业务入口,其他路径统一保持 OpenAI-compatible 404。\n if (req.method === \"POST\" && url === \"/v1/chat/completions\") {\n await proxyChat(\n req,\n res,\n cfg,\n runtimeLimits,\n sessionStore,\n tierEntries,\n publicModels,\n registry,\n routerOptions,\n );\n return;\n }\n\n // Everything else → 404\n writeOpenAiError(res, 404, \"Not Found\");\n } catch (error) {\n if (!res.headersSent) {\n writeOpenAiError(res, 502, String(error));\n } else {\n res.destroy();\n }\n }\n })();\n });\n\n const port = await new Promise<number>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(cfg.port, \"127.0.0.1\", () => {\n const address = server.address();\n if (!address || typeof address === \"string\") {\n reject(new Error(\"Could not determine server port\"));\n return;\n }\n resolve(address.port);\n });\n });\n\n return {\n port,\n baseUrl: cfg.baseUrl,\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((err) => {\n sessionStore.close();\n if (err) reject(err);\n else resolve();\n });\n }),\n };\n}\n","import type { ScoringConfig, ScoringResult } from \"./types.js\";\n\nexport const SIMPLE_PATTERNS = [\n /\\btranslate\\b/i,\n /\\bsummarize\\b/i,\n /\\bformat\\b/i,\n /\\bexplain briefly\\b/i,\n /翻译/,\n /总结/,\n /格式化/,\n];\n\nexport const CODE_PATTERNS = [\n /\\bapply_patch\\b/i,\n /\\btypescript\\b/i,\n /\\bjavascript\\b/i,\n /\\bfunction\\b/i,\n /\\bclass\\b/i,\n /\\brename\\b/i,\n /\\bwrite\\s+(some\\s+)?code\\b/i,\n /\\bgenerate\\s+code\\b/i,\n /\\bedit\\s+(the\\s+)?file\\b/i,\n /\\bimplement\\b/i,\n /\\b[a-z0-9_-]+\\.(ts|tsx|js|jsx|mjs|cjs|json|md|py|rs|go|java|kt|swift|rb|php|css|scss|html|yml|yaml)\\b/i,\n /代码/,\n /函数/,\n];\n\nexport const COMPLEX_PATTERNS = [\n /\\bdebug\\b/i,\n /\\bfailing tests?\\b/i,\n /\\barchitecture\\b/i,\n /\\brefactor\\b/i,\n /\\bmultiple files?\\b/i,\n /\\broot cause\\b/i,\n /调试/,\n /测试失败/,\n /架构/,\n /重构/,\n /多文件/,\n];\n\ntype DimensionScore = { name: string; score: number; signal: string | null };\n\nfunction scoreTokenCount(\n estimatedTokens: number,\n thresholds: { simple: number; complex: number },\n): DimensionScore {\n if (estimatedTokens < thresholds.simple) {\n return { name: \"tokenCount\", score: -1, signal: `short (${estimatedTokens} tokens)` };\n }\n if (estimatedTokens > thresholds.complex) {\n return { name: \"tokenCount\", score: 1, signal: `long (${estimatedTokens} tokens)` };\n }\n return { name: \"tokenCount\", score: 0, signal: null };\n}\n\nfunction scoreKeywordMatch(\n text: string,\n keywords: string[],\n name: string,\n signalLabel: string,\n thresholds: { low: number; high: number },\n scores: { none: number; low: number; high: number },\n): DimensionScore {\n const matches = keywords.filter((keyword) => text.includes(keyword.toLowerCase()));\n if (matches.length >= thresholds.high) {\n return { name, score: scores.high, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n if (matches.length >= thresholds.low) {\n return { name, score: scores.low, signal: `${signalLabel} (${matches.slice(0, 3).join(\", \")})` };\n }\n return { name, score: scores.none, signal: null };\n}\n\nfunction scoreMultiStep(text: string): DimensionScore {\n const patterns = [/first.*then/i, /step \\d/i, /\\d\\.\\s/];\n return patterns.some((pattern) => pattern.test(text))\n ? { name: \"multiStepPatterns\", score: 0.5, signal: \"multi-step\" }\n : { name: \"multiStepPatterns\", score: 0, signal: null };\n}\n\nfunction scoreQuestionComplexity(prompt: string): DimensionScore {\n const count = (prompt.match(/\\?/g) ?? []).length;\n return count > 3\n ? { name: \"questionComplexity\", score: 0.5, signal: `${count} questions` }\n : { name: \"questionComplexity\", score: 0, signal: null };\n}\n\nfunction scoreAgenticTask(\n text: string,\n keywords: string[],\n): { dimensionScore: DimensionScore; agenticScore: number } {\n let matchCount = 0;\n const signals: string[] = [];\n\n for (const keyword of keywords) {\n if (text.includes(keyword.toLowerCase())) {\n matchCount++;\n if (signals.length < 3) signals.push(keyword);\n }\n }\n\n if (matchCount >= 4) {\n return {\n dimensionScore: { name: \"agenticTask\", score: 1, signal: `agentic (${signals.join(\", \")})` },\n agenticScore: 1,\n };\n }\n if (matchCount >= 3) {\n return {\n dimensionScore: { name: \"agenticTask\", score: 0.6, signal: `agentic (${signals.join(\", \")})` },\n agenticScore: 0.6,\n };\n }\n if (matchCount >= 1) {\n return {\n dimensionScore: { name: \"agenticTask\", score: 0.2, signal: `agentic-light (${signals.join(\", \")})` },\n agenticScore: 0.2,\n };\n }\n\n return {\n dimensionScore: { name: \"agenticTask\", score: 0, signal: null },\n agenticScore: 0,\n };\n}\n\nexport function classifyByRules(\n prompt: string,\n _systemPrompt: string | undefined,\n estimatedTokens: number,\n config: ScoringConfig,\n): ScoringResult {\n const userText = prompt.toLowerCase();\n const dimensions: DimensionScore[] = [\n scoreTokenCount(estimatedTokens, config.tokenCountThresholds),\n scoreKeywordMatch(userText, config.codeKeywords, \"codePresence\", \"code\", { low: 1, high: 2 }, { none: 0, low: 0.5, high: 1 }),\n scoreKeywordMatch(userText, config.reasoningKeywords, \"reasoningMarkers\", \"reasoning\", { low: 1, high: 2 }, { none: 0, low: 0.7, high: 1 }),\n scoreKeywordMatch(userText, config.technicalKeywords, \"technicalTerms\", \"technical\", { low: 2, high: 4 }, { none: 0, low: 0.5, high: 1 }),\n scoreKeywordMatch(userText, config.creativeKeywords, \"creativeMarkers\", \"creative\", { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.7 }),\n scoreKeywordMatch(userText, config.simpleKeywords, \"simpleIndicators\", \"simple\", { low: 1, high: 2 }, { none: 0, low: -1, high: -1 }),\n scoreMultiStep(userText),\n scoreQuestionComplexity(prompt),\n scoreKeywordMatch(userText, config.imperativeVerbs, \"imperativeVerbs\", \"imperative\", { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(userText, config.constraintIndicators, \"constraintCount\", \"constraints\", { low: 1, high: 3 }, { none: 0, low: 0.3, high: 0.7 }),\n scoreKeywordMatch(userText, config.outputFormatKeywords, \"outputFormat\", \"format\", { low: 1, high: 2 }, { none: 0, low: 0.4, high: 0.7 }),\n scoreKeywordMatch(userText, config.referenceKeywords, \"referenceComplexity\", \"references\", { low: 1, high: 2 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(userText, config.negationKeywords, \"negationComplexity\", \"negation\", { low: 2, high: 3 }, { none: 0, low: 0.3, high: 0.5 }),\n scoreKeywordMatch(userText, config.domainSpecificKeywords, \"domainSpecificity\", \"domain-specific\", { low: 1, high: 2 }, { none: 0, low: 0.5, high: 0.8 }),\n ];\n\n const agenticResult = scoreAgenticTask(userText, config.agenticTaskKeywords);\n dimensions.push(agenticResult.dimensionScore);\n\n const signals = dimensions.filter((dimension) => dimension.signal !== null).map((dimension) => dimension.signal!);\n const weightedScore = dimensions.reduce(\n (score, dimension) => score + dimension.score * (config.dimensionWeights[dimension.name] ?? 0),\n 0,\n );\n\n const reasoningMatches = config.reasoningKeywords.filter((keyword) =>\n userText.includes(keyword.toLowerCase()),\n );\n if (reasoningMatches.length >= 2) {\n return {\n score: weightedScore,\n tier: \"REASONING\",\n confidence: Math.max(calibrateConfidence(Math.max(weightedScore, 0.3), config.confidenceSteepness), 0.85),\n signals,\n agenticScore: agenticResult.agenticScore,\n dimensions,\n };\n }\n\n const { simpleMedium, mediumComplex, complexReasoning } = config.tierBoundaries;\n let tier: ScoringResult[\"tier\"];\n let distanceFromBoundary: number;\n\n if (weightedScore < simpleMedium) {\n tier = \"SIMPLE\";\n distanceFromBoundary = simpleMedium - weightedScore;\n } else if (weightedScore < mediumComplex) {\n tier = \"MEDIUM\";\n distanceFromBoundary = Math.min(weightedScore - simpleMedium, mediumComplex - weightedScore);\n } else if (weightedScore < complexReasoning) {\n tier = \"COMPLEX\";\n distanceFromBoundary = Math.min(weightedScore - mediumComplex, complexReasoning - weightedScore);\n } else {\n tier = \"REASONING\";\n distanceFromBoundary = weightedScore - complexReasoning;\n }\n\n const confidence = calibrateConfidence(distanceFromBoundary, config.confidenceSteepness);\n if (confidence < config.confidenceThreshold) {\n return {\n score: weightedScore,\n tier: null,\n confidence,\n signals,\n agenticScore: agenticResult.agenticScore,\n dimensions,\n };\n }\n\n return {\n score: weightedScore,\n tier,\n confidence,\n signals,\n agenticScore: agenticResult.agenticScore,\n dimensions,\n };\n}\n\nfunction calibrateConfidence(distance: number, steepness: number): number {\n return 1 / (1 + Math.exp(-steepness * distance));\n}\n","import type { RoutingDecision, Tier, TierConfig } from \"./types.js\";\n\n/**\n * 路由层只关心 alias 的成本画像,因此这里的 key 是 public alias,而不是\n * physical model ID。\n */\nexport type ModelPricing = {\n inputPrice: number;\n outputPrice: number;\n};\n\nconst DEFAULT_BASELINE_INPUT_PRICE = 0.56;\nconst DEFAULT_BASELINE_OUTPUT_PRICE = 1.68;\n\n/**\n * 把 tier 选择结果转换成统一的 RoutingDecision,并补齐成本估算字段。\n */\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n agenticScore?: number,\n score?: number,\n): RoutingDecision;\nexport function selectModel(\n tier: Tier,\n confidence: number,\n method: \"rules\" | \"llm\",\n reasoning: string,\n tierConfigs: Record<Tier, TierConfig>,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n agenticScore?: number,\n score?: number,\n): RoutingDecision {\n const config = tierConfigs[tier];\n if (!config) {\n throw new Error(`Missing tier config for ${tier}`);\n }\n\n // `primary` 在当前阶段表示“语义层 alias 选择结果”,不是 physical model。\n const model = config.primary;\n const baselineModel = tierConfigs[\"COMPLEX\"].primary;\n const costs = calculateModelCost(\n model,\n modelPricing,\n estimatedInputTokens,\n maxOutputTokens,\n baselineModel,\n );\n\n return {\n publicModel: model,\n tier,\n confidence,\n method,\n reasoning,\n ...costs,\n ...(agenticScore !== undefined && { agenticScore }),\n ...(score !== undefined && { score }),\n };\n}\n\n/**\n * 返回某个 tier 的完整尝试链:先 primary,再按顺序追加 fallback。\n */\nexport function getFallbackChain(\n tier: Tier,\n tierConfigs: Record<Tier, TierConfig>,\n): string[] {\n const config = tierConfigs[tier];\n return [config.primary, ...config.fallback];\n}\n\n/**\n * 按 1M token 单价估算当前 alias 的输入 / 输出成本,并和 baseline 进行对比。\n */\nexport function calculateModelCost(\n model: string,\n modelPricing: Map<string, ModelPricing>,\n estimatedInputTokens: number,\n maxOutputTokens: number,\n baselineModelId?: string,\n): { costEstimate: number; baselineCost: number; savings: number } {\n const pricing = modelPricing.get(model);\n const inputCost =\n (estimatedInputTokens / 1_000_000) * (pricing?.inputPrice ?? 0);\n const outputCost =\n (maxOutputTokens / 1_000_000) * (pricing?.outputPrice ?? 0);\n const costEstimate = inputCost + outputCost;\n\n const baselinePricing = baselineModelId\n ? modelPricing.get(baselineModelId)\n : undefined;\n const baselineInput =\n (estimatedInputTokens / 1_000_000) *\n (baselinePricing?.inputPrice ?? DEFAULT_BASELINE_INPUT_PRICE);\n const baselineOutput =\n (maxOutputTokens / 1_000_000) *\n (baselinePricing?.outputPrice ?? DEFAULT_BASELINE_OUTPUT_PRICE);\n const baselineCost = baselineInput + baselineOutput;\n const savings =\n baselineCost > 0\n ? Math.max(0, (baselineCost - costEstimate) / baselineCost)\n : 0;\n\n return { costEstimate, baselineCost, savings };\n}\n\n/**\n * 从候选链里剔除不允许继续尝试的模型;如果剔除后为空,则保底返回原链,避免\n * 调用方拿到“无路可走”的空数组。\n */\nexport function filterByExcludeList(\n models: string[],\n excludeList: Set<string>,\n): string[] {\n if (excludeList.size === 0) return models;\n const filtered = models.filter((model) => !excludeList.has(model));\n return filtered.length > 0 ? filtered : models;\n}\n","import { classifyByRules } from \"./rules.js\";\nimport { selectModel } from \"./selector.js\";\nimport type {\n RouterOptions,\n RouterStrategy,\n RoutingDecision,\n Tier,\n TierConfig,\n} from \"./types.js\";\n\n/**\n * 基于规则的默认路由策略。\n *\n * 它只负责把 prompt 映射到 tier / alias 决策,不参与 physical model 解析,\n * 因此可以长期保持纯函数式、可测试的语义层逻辑。\n */\nexport class RulesStrategy implements RouterStrategy {\n readonly name = \"rules\";\n\n route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n ): RoutingDecision {\n const { config, modelPricing } = options;\n if (!config) {\n throw new Error(\"Routing config is required at runtime\");\n }\n if (!modelPricing) {\n throw new Error(\"Model pricing is required at runtime\");\n }\n\n // 使用一个非常粗粒度的字符数近似,避免把 tokenizer 引进 Router 内核。\n const fullText = `${systemPrompt ?? \"\"} ${prompt}`;\n const estimatedTokens = Math.ceil(fullText.length / 4);\n const ruleResult = classifyByRules(\n prompt,\n systemPrompt,\n estimatedTokens,\n config.scoring,\n );\n\n const { tierConfigs, profile, profileSuffix } = chooseTierConfigs(options);\n\n const hasStructuredOutput = systemPrompt\n ? /json|structured|schema/i.test(systemPrompt)\n : false;\n let tier: Tier;\n let confidence: number;\n let reasoning = `score=${ruleResult.score.toFixed(2)} | ${ruleResult.signals.join(\", \")}`;\n\n // 命中明确规则时直接采用;否则落到配置里的 ambiguous 默认 tier。\n if (ruleResult.tier !== null) {\n tier = ruleResult.tier;\n confidence = ruleResult.confidence;\n } else {\n tier = config.overrides?.ambiguousDefaultTier ?? \"MEDIUM\";\n confidence = 0.5;\n reasoning += ` | ambiguous -> default: ${tier}`;\n }\n\n // structured output 请求倾向提升到至少 MEDIUM,避免过度保守地走 SIMPLE。\n if (hasStructuredOutput) {\n const tierRank: Record<Tier, number> = {\n SIMPLE: 0,\n MEDIUM: 1,\n COMPLEX: 2,\n REASONING: 3,\n };\n const minTier = config.overrides?.structuredOutputMinTier ?? \"MEDIUM\";\n if (tierRank[tier] < tierRank[minTier]) {\n reasoning += ` | upgraded to ${minTier} (structured output)`;\n tier = minTier;\n } else {\n reasoning += \" | structured output\";\n }\n }\n\n reasoning += profileSuffix;\n\n const decision = selectModel(\n tier,\n confidence,\n \"rules\",\n reasoning,\n tierConfigs,\n modelPricing,\n estimatedTokens,\n maxOutputTokens,\n ruleResult.agenticScore,\n ruleResult.score,\n );\n return { ...decision, tierConfigs, profile };\n }\n}\n\n/**\n * 预留 profile 扩展点。\n *\n * 当前仓库只使用 default profile,但把结构保留下来,能避免未来新增 profile\n * 时改动 `RulesStrategy.route()` 的主流程。\n */\nfunction chooseTierConfigs(options: RouterOptions | undefined): {\n tierConfigs: Record<Tier, TierConfig>;\n profile: RoutingDecision[\"profile\"];\n profileSuffix: string;\n} {\n const config = options?.config;\n if (!config?.tiers) {\n throw new Error(\"Routing tiers are required at runtime\");\n }\n\n return {\n tierConfigs: config.tiers,\n profile: \"default\",\n profileSuffix: \"\",\n };\n}\n\nconst registry = new Map<string, RouterStrategy>();\nregistry.set(\"rules\", new RulesStrategy());\n\n/**\n * 从策略注册表中读取命名路由策略。\n */\nexport function getStrategy(name: string): RouterStrategy {\n const strategy = registry.get(name);\n if (!strategy) {\n throw new Error(`Unknown routing strategy: ${name}`);\n }\n return strategy;\n}\n\n/**\n * 允许外部测试或扩展模块注册新的路由策略实现。\n */\nexport function registerStrategy(strategy: RouterStrategy): void {\n registry.set(strategy.name, strategy);\n}\n","import type { RoutingConfig } from \"./types.js\";\n\nexport const DEFAULT_ROUTING_CONFIG = {\n version: \"2.0\",\n\n scoring: {\n tokenCountThresholds: { simple: 50, complex: 500 },\n\n // Multilingual keywords: EN + ZH + JA + RU + DE + ES + PT + KO + AR\n codeKeywords: [\n // English\n \"function\",\n \"class\",\n \"import\",\n \"def\",\n \"SELECT\",\n \"async\",\n \"await\",\n \"const\",\n \"let\",\n \"var\",\n \"return\",\n \"```\",\n // Chinese\n \"函数\",\n \"类\",\n \"导入\",\n \"定义\",\n \"查询\",\n \"异步\",\n \"等待\",\n \"常量\",\n \"变量\",\n \"返回\",\n // Japanese\n \"関数\",\n \"クラス\",\n \"インポート\",\n \"非同期\",\n \"定数\",\n \"変数\",\n // Russian\n \"функция\",\n \"класс\",\n \"импорт\",\n \"определ\",\n \"запрос\",\n \"асинхронный\",\n \"ожидать\",\n \"константа\",\n \"переменная\",\n \"вернуть\",\n // German\n \"funktion\",\n \"klasse\",\n \"importieren\",\n \"definieren\",\n \"abfrage\",\n \"asynchron\",\n \"erwarten\",\n \"konstante\",\n \"variable\",\n \"zurückgeben\",\n // Spanish\n \"función\",\n \"clase\",\n \"importar\",\n \"definir\",\n \"consulta\",\n \"asíncrono\",\n \"esperar\",\n \"constante\",\n \"variable\",\n \"retornar\",\n // Portuguese\n \"função\",\n \"classe\",\n \"importar\",\n \"definir\",\n \"consulta\",\n \"assíncrono\",\n \"aguardar\",\n \"constante\",\n \"variável\",\n \"retornar\",\n // Korean\n \"함수\",\n \"클래스\",\n \"가져오기\",\n \"정의\",\n \"쿼리\",\n \"비동기\",\n \"대기\",\n \"상수\",\n \"변수\",\n \"반환\",\n // Arabic\n \"دالة\",\n \"فئة\",\n \"استيراد\",\n \"تعريف\",\n \"استعلام\",\n \"غير متزامن\",\n \"انتظار\",\n \"ثابت\",\n \"متغير\",\n \"إرجاع\",\n ],\n reasoningKeywords: [\n // English\n \"prove\",\n \"theorem\",\n \"derive\",\n \"step by step\",\n \"chain of thought\",\n \"formally\",\n \"mathematical\",\n \"proof\",\n \"logically\",\n // Chinese\n \"证明\",\n \"定理\",\n \"推导\",\n \"逐步\",\n \"思维链\",\n \"形式化\",\n \"数学\",\n \"逻辑\",\n // Japanese\n \"証明\",\n \"定理\",\n \"導出\",\n \"ステップバイステップ\",\n \"論理的\",\n // Russian\n \"доказать\",\n \"докажи\",\n \"доказательств\",\n \"теорема\",\n \"вывести\",\n \"шаг за шагом\",\n \"пошагово\",\n \"поэтапно\",\n \"цепочка рассуждений\",\n \"рассуждени\",\n \"формально\",\n \"математически\",\n \"логически\",\n // German\n \"beweisen\",\n \"beweis\",\n \"theorem\",\n \"ableiten\",\n \"schritt für schritt\",\n \"gedankenkette\",\n \"formal\",\n \"mathematisch\",\n \"logisch\",\n // Spanish\n \"demostrar\",\n \"teorema\",\n \"derivar\",\n \"paso a paso\",\n \"cadena de pensamiento\",\n \"formalmente\",\n \"matemático\",\n \"prueba\",\n \"lógicamente\",\n // Portuguese\n \"provar\",\n \"teorema\",\n \"derivar\",\n \"passo a passo\",\n \"cadeia de pensamento\",\n \"formalmente\",\n \"matemático\",\n \"prova\",\n \"logicamente\",\n // Korean\n \"증명\",\n \"정리\",\n \"도출\",\n \"단계별\",\n \"사고의 연쇄\",\n \"형식적\",\n \"수학적\",\n \"논리적\",\n // Arabic\n \"إثبات\",\n \"نظرية\",\n \"اشتقاق\",\n \"خطوة بخطوة\",\n \"سلسلة التفكير\",\n \"رسمياً\",\n \"رياضي\",\n \"برهان\",\n \"منطقياً\",\n ],\n simpleKeywords: [\n // English\n \"what is\",\n \"define\",\n \"translate\",\n \"hello\",\n \"yes or no\",\n \"capital of\",\n \"how old\",\n \"who is\",\n \"when was\",\n // Chinese\n \"什么是\",\n \"定义\",\n \"翻译\",\n \"你好\",\n \"是否\",\n \"首都\",\n \"多大\",\n \"谁是\",\n \"何时\",\n // Japanese\n \"とは\",\n \"定義\",\n \"翻訳\",\n \"こんにちは\",\n \"はいかいいえ\",\n \"首都\",\n \"誰\",\n // Russian\n \"что такое\",\n \"определение\",\n \"перевести\",\n \"переведи\",\n \"привет\",\n \"да или нет\",\n \"столица\",\n \"сколько лет\",\n \"кто такой\",\n \"когда\",\n \"объясни\",\n // German\n \"was ist\",\n \"definiere\",\n \"übersetze\",\n \"hallo\",\n \"ja oder nein\",\n \"hauptstadt\",\n \"wie alt\",\n \"wer ist\",\n \"wann\",\n \"erkläre\",\n // Spanish\n \"qué es\",\n \"definir\",\n \"traducir\",\n \"hola\",\n \"sí o no\",\n \"capital de\",\n \"cuántos años\",\n \"quién es\",\n \"cuándo\",\n // Portuguese\n \"o que é\",\n \"definir\",\n \"traduzir\",\n \"olá\",\n \"sim ou não\",\n \"capital de\",\n \"quantos anos\",\n \"quem é\",\n \"quando\",\n // Korean\n \"무엇\",\n \"정의\",\n \"번역\",\n \"안녕하세요\",\n \"예 또는 아니오\",\n \"수도\",\n \"누구\",\n \"언제\",\n // Arabic\n \"ما هو\",\n \"تعريف\",\n \"ترجم\",\n \"مرحبا\",\n \"نعم أو لا\",\n \"عاصمة\",\n \"من هو\",\n \"متى\",\n ],\n technicalKeywords: [\n // English\n \"algorithm\",\n \"optimize\",\n \"architecture\",\n \"distributed\",\n \"kubernetes\",\n \"microservice\",\n \"database\",\n \"infrastructure\",\n // Chinese\n \"算法\",\n \"优化\",\n \"架构\",\n \"分布式\",\n \"微服务\",\n \"数据库\",\n \"基础设施\",\n // Japanese\n \"アルゴリズム\",\n \"最適化\",\n \"アーキテクチャ\",\n \"分散\",\n \"マイクロサービス\",\n \"データベース\",\n // Russian\n \"алгоритм\",\n \"оптимизировать\",\n \"оптимизаци\",\n \"оптимизируй\",\n \"архитектура\",\n \"распределённый\",\n \"микросервис\",\n \"база данных\",\n \"инфраструктура\",\n // German\n \"algorithmus\",\n \"optimieren\",\n \"architektur\",\n \"verteilt\",\n \"kubernetes\",\n \"mikroservice\",\n \"datenbank\",\n \"infrastruktur\",\n // Spanish\n \"algoritmo\",\n \"optimizar\",\n \"arquitectura\",\n \"distribuido\",\n \"microservicio\",\n \"base de datos\",\n \"infraestructura\",\n // Portuguese\n \"algoritmo\",\n \"otimizar\",\n \"arquitetura\",\n \"distribuído\",\n \"microsserviço\",\n \"banco de dados\",\n \"infraestrutura\",\n // Korean\n \"알고리즘\",\n \"최적화\",\n \"아키텍처\",\n \"분산\",\n \"마이크로서비스\",\n \"데이터베이스\",\n \"인프라\",\n // Arabic\n \"خوارزمية\",\n \"تحسين\",\n \"بنية\",\n \"موزع\",\n \"خدمة مصغرة\",\n \"قاعدة بيانات\",\n \"بنية تحتية\",\n ],\n creativeKeywords: [\n // English\n \"story\",\n \"poem\",\n \"compose\",\n \"brainstorm\",\n \"creative\",\n \"imagine\",\n \"write a\",\n // Chinese\n \"故事\",\n \"诗\",\n \"创作\",\n \"头脑风暴\",\n \"创意\",\n \"想象\",\n \"写一个\",\n // Japanese\n \"物語\",\n \"詩\",\n \"作曲\",\n \"ブレインストーム\",\n \"創造的\",\n \"想像\",\n // Russian\n \"история\",\n \"рассказ\",\n \"стихотворение\",\n \"сочинить\",\n \"сочини\",\n \"мозговой штурм\",\n \"творческий\",\n \"представить\",\n \"придумай\",\n \"напиши\",\n // German\n \"geschichte\",\n \"gedicht\",\n \"komponieren\",\n \"brainstorming\",\n \"kreativ\",\n \"vorstellen\",\n \"schreibe\",\n \"erzählung\",\n // Spanish\n \"historia\",\n \"poema\",\n \"componer\",\n \"lluvia de ideas\",\n \"creativo\",\n \"imaginar\",\n \"escribe\",\n // Portuguese\n \"história\",\n \"poema\",\n \"compor\",\n \"criativo\",\n \"imaginar\",\n \"escreva\",\n // Korean\n \"이야기\",\n \"시\",\n \"작곡\",\n \"브레인스토밍\",\n \"창의적\",\n \"상상\",\n \"작성\",\n // Arabic\n \"قصة\",\n \"قصيدة\",\n \"تأليف\",\n \"عصف ذهني\",\n \"إبداعي\",\n \"تخيل\",\n \"اكتب\",\n ],\n imperativeVerbs: [\n // English\n \"build\",\n \"create\",\n \"implement\",\n \"design\",\n \"develop\",\n \"construct\",\n \"generate\",\n \"deploy\",\n \"configure\",\n \"set up\",\n // Chinese\n \"构建\",\n \"创建\",\n \"实现\",\n \"设计\",\n \"开发\",\n \"生成\",\n \"部署\",\n \"配置\",\n \"设置\",\n // Japanese\n \"構築\",\n \"作成\",\n \"実装\",\n \"設計\",\n \"開発\",\n \"生成\",\n \"デプロイ\",\n \"設定\",\n // Russian\n \"построить\",\n \"построй\",\n \"создать\",\n \"создай\",\n \"реализовать\",\n \"реализуй\",\n \"спроектировать\",\n \"разработать\",\n \"разработай\",\n \"сконструировать\",\n \"сгенерировать\",\n \"сгенерируй\",\n \"развернуть\",\n \"разверни\",\n \"настроить\",\n \"настрой\",\n // German\n \"erstellen\",\n \"bauen\",\n \"implementieren\",\n \"entwerfen\",\n \"entwickeln\",\n \"konstruieren\",\n \"generieren\",\n \"bereitstellen\",\n \"konfigurieren\",\n \"einrichten\",\n // Spanish\n \"construir\",\n \"crear\",\n \"implementar\",\n \"diseñar\",\n \"desarrollar\",\n \"generar\",\n \"desplegar\",\n \"configurar\",\n // Portuguese\n \"construir\",\n \"criar\",\n \"implementar\",\n \"projetar\",\n \"desenvolver\",\n \"gerar\",\n \"implantar\",\n \"configurar\",\n // Korean\n \"구축\",\n \"생성\",\n \"구현\",\n \"설계\",\n \"개발\",\n \"배포\",\n \"설정\",\n // Arabic\n \"بناء\",\n \"إنشاء\",\n \"تنفيذ\",\n \"تصميم\",\n \"تطوير\",\n \"توليد\",\n \"نشر\",\n \"إعداد\",\n ],\n constraintIndicators: [\n // English\n \"under\",\n \"at most\",\n \"at least\",\n \"within\",\n \"no more than\",\n \"o(\",\n \"maximum\",\n \"minimum\",\n \"limit\",\n \"budget\",\n // Chinese\n \"不超过\",\n \"至少\",\n \"最多\",\n \"在内\",\n \"最大\",\n \"最小\",\n \"限制\",\n \"预算\",\n // Japanese\n \"以下\",\n \"最大\",\n \"最小\",\n \"制限\",\n \"予算\",\n // Russian\n \"не более\",\n \"не менее\",\n \"как минимум\",\n \"в пределах\",\n \"максимум\",\n \"минимум\",\n \"ограничение\",\n \"бюджет\",\n // German\n \"höchstens\",\n \"mindestens\",\n \"innerhalb\",\n \"nicht mehr als\",\n \"maximal\",\n \"minimal\",\n \"grenze\",\n \"budget\",\n // Spanish\n \"como máximo\",\n \"al menos\",\n \"dentro de\",\n \"no más de\",\n \"máximo\",\n \"mínimo\",\n \"límite\",\n \"presupuesto\",\n // Portuguese\n \"no máximo\",\n \"pelo menos\",\n \"dentro de\",\n \"não mais que\",\n \"máximo\",\n \"mínimo\",\n \"limite\",\n \"orçamento\",\n // Korean\n \"이하\",\n \"이상\",\n \"최대\",\n \"최소\",\n \"제한\",\n \"예산\",\n // Arabic\n \"على الأكثر\",\n \"على الأقل\",\n \"ضمن\",\n \"لا يزيد عن\",\n \"أقصى\",\n \"أدنى\",\n \"حد\",\n \"ميزانية\",\n ],\n outputFormatKeywords: [\n // English\n \"json\",\n \"yaml\",\n \"xml\",\n \"table\",\n \"csv\",\n \"markdown\",\n \"schema\",\n \"format as\",\n \"structured\",\n // Chinese\n \"表格\",\n \"格式化为\",\n \"结构化\",\n // Japanese\n \"テーブル\",\n \"フォーマット\",\n \"構造化\",\n // Russian\n \"таблица\",\n \"форматировать как\",\n \"структурированный\",\n // German\n \"tabelle\",\n \"formatieren als\",\n \"strukturiert\",\n // Spanish\n \"tabla\",\n \"formatear como\",\n \"estructurado\",\n // Portuguese\n \"tabela\",\n \"formatar como\",\n \"estruturado\",\n // Korean\n \"테이블\",\n \"형식\",\n \"구조화\",\n // Arabic\n \"جدول\",\n \"تنسيق\",\n \"منظم\",\n ],\n referenceKeywords: [\n // English\n \"above\",\n \"below\",\n \"previous\",\n \"following\",\n \"the docs\",\n \"the api\",\n \"the code\",\n \"earlier\",\n \"attached\",\n // Chinese\n \"上面\",\n \"下面\",\n \"之前\",\n \"接下来\",\n \"文档\",\n \"代码\",\n \"附件\",\n // Japanese\n \"上記\",\n \"下記\",\n \"前の\",\n \"次の\",\n \"ドキュメント\",\n \"コード\",\n // Russian\n \"выше\",\n \"ниже\",\n \"предыдущий\",\n \"следующий\",\n \"документация\",\n \"код\",\n \"ранее\",\n \"вложение\",\n // German\n \"oben\",\n \"unten\",\n \"vorherige\",\n \"folgende\",\n \"dokumentation\",\n \"der code\",\n \"früher\",\n \"anhang\",\n // Spanish\n \"arriba\",\n \"abajo\",\n \"anterior\",\n \"siguiente\",\n \"documentación\",\n \"el código\",\n \"adjunto\",\n // Portuguese\n \"acima\",\n \"abaixo\",\n \"anterior\",\n \"seguinte\",\n \"documentação\",\n \"o código\",\n \"anexo\",\n // Korean\n \"위\",\n \"아래\",\n \"이전\",\n \"다음\",\n \"문서\",\n \"코드\",\n \"첨부\",\n // Arabic\n \"أعلاه\",\n \"أدناه\",\n \"السابق\",\n \"التالي\",\n \"الوثائق\",\n \"الكود\",\n \"مرفق\",\n ],\n negationKeywords: [\n // English\n \"don't\",\n \"do not\",\n \"avoid\",\n \"never\",\n \"without\",\n \"except\",\n \"exclude\",\n \"no longer\",\n // Chinese\n \"不要\",\n \"避免\",\n \"从不\",\n \"没有\",\n \"除了\",\n \"排除\",\n // Japanese\n \"しないで\",\n \"避ける\",\n \"決して\",\n \"なしで\",\n \"除く\",\n // Russian\n \"не делай\",\n \"не надо\",\n \"нельзя\",\n \"избегать\",\n \"никогда\",\n \"без\",\n \"кроме\",\n \"исключить\",\n \"больше не\",\n // German\n \"nicht\",\n \"vermeide\",\n \"niemals\",\n \"ohne\",\n \"außer\",\n \"ausschließen\",\n \"nicht mehr\",\n // Spanish\n \"no hagas\",\n \"evitar\",\n \"nunca\",\n \"sin\",\n \"excepto\",\n \"excluir\",\n // Portuguese\n \"não faça\",\n \"evitar\",\n \"nunca\",\n \"sem\",\n \"exceto\",\n \"excluir\",\n // Korean\n \"하지 마\",\n \"피하다\",\n \"절대\",\n \"없이\",\n \"제외\",\n // Arabic\n \"لا تفعل\",\n \"تجنب\",\n \"أبداً\",\n \"بدون\",\n \"باستثناء\",\n \"استبعاد\",\n ],\n domainSpecificKeywords: [\n // English\n \"quantum\",\n \"fpga\",\n \"vlsi\",\n \"risc-v\",\n \"asic\",\n \"photonics\",\n \"genomics\",\n \"proteomics\",\n \"topological\",\n \"homomorphic\",\n \"zero-knowledge\",\n \"lattice-based\",\n // Chinese\n \"量子\",\n \"光子学\",\n \"基因组学\",\n \"蛋白质组学\",\n \"拓扑\",\n \"同态\",\n \"零知识\",\n \"格密码\",\n // Japanese\n \"量子\",\n \"フォトニクス\",\n \"ゲノミクス\",\n \"トポロジカル\",\n // Russian\n \"квантовый\",\n \"фотоника\",\n \"геномика\",\n \"протеомика\",\n \"топологический\",\n \"гомоморфный\",\n \"с нулевым разглашением\",\n \"на основе решёток\",\n // German\n \"quanten\",\n \"photonik\",\n \"genomik\",\n \"proteomik\",\n \"topologisch\",\n \"homomorph\",\n \"zero-knowledge\",\n \"gitterbasiert\",\n // Spanish\n \"cuántico\",\n \"fotónica\",\n \"genómica\",\n \"proteómica\",\n \"topológico\",\n \"homomórfico\",\n // Portuguese\n \"quântico\",\n \"fotônica\",\n \"genômica\",\n \"proteômica\",\n \"topológico\",\n \"homomórfico\",\n // Korean\n \"양자\",\n \"포토닉스\",\n \"유전체학\",\n \"위상\",\n \"동형\",\n // Arabic\n \"كمي\",\n \"ضوئيات\",\n \"جينوميات\",\n \"طوبولوجي\",\n \"تماثلي\",\n ],\n // Agentic task keywords - file ops, execution, multi-step, iterative work\n // Pruned: removed overly common words like \"then\", \"first\", \"run\", \"test\", \"build\"\n agenticTaskKeywords: [\n // English - File operations (clearly agentic)\n \"read file\",\n \"read the file\",\n \"look at\",\n \"check the\",\n \"open the\",\n \"edit\",\n \"modify\",\n \"update the\",\n \"change the\",\n \"write to\",\n \"create file\",\n // English - Execution (specific commands only)\n \"execute\",\n \"deploy\",\n \"install\",\n \"npm\",\n \"pip\",\n \"compile\",\n // English - Multi-step patterns (specific only)\n \"after that\",\n \"and also\",\n \"once done\",\n \"step 1\",\n \"step 2\",\n // English - Iterative work\n \"fix\",\n \"debug\",\n \"until it works\",\n \"keep trying\",\n \"iterate\",\n \"make sure\",\n \"verify\",\n \"confirm\",\n // Chinese (keep specific ones)\n \"读取文件\",\n \"查看\",\n \"打开\",\n \"编辑\",\n \"修改\",\n \"更新\",\n \"创建\",\n \"执行\",\n \"部署\",\n \"安装\",\n \"第一步\",\n \"第二步\",\n \"修复\",\n \"调试\",\n \"直到\",\n \"确认\",\n \"验证\",\n // Spanish\n \"leer archivo\",\n \"editar\",\n \"modificar\",\n \"actualizar\",\n \"ejecutar\",\n \"desplegar\",\n \"instalar\",\n \"paso 1\",\n \"paso 2\",\n \"arreglar\",\n \"depurar\",\n \"verificar\",\n // Portuguese\n \"ler arquivo\",\n \"editar\",\n \"modificar\",\n \"atualizar\",\n \"executar\",\n \"implantar\",\n \"instalar\",\n \"passo 1\",\n \"passo 2\",\n \"corrigir\",\n \"depurar\",\n \"verificar\",\n // Korean\n \"파일 읽기\",\n \"편집\",\n \"수정\",\n \"업데이트\",\n \"실행\",\n \"배포\",\n \"설치\",\n \"단계 1\",\n \"단계 2\",\n \"디버그\",\n \"확인\",\n // Arabic\n \"قراءة ملف\",\n \"تحرير\",\n \"تعديل\",\n \"تحديث\",\n \"تنفيذ\",\n \"نشر\",\n \"تثبيت\",\n \"الخطوة 1\",\n \"الخطوة 2\",\n \"إصلاح\",\n \"تصحيح\",\n \"تحقق\",\n ],\n\n // Dimension weights (sum to 1.0)\n dimensionWeights: {\n tokenCount: 0.08,\n codePresence: 0.15,\n reasoningMarkers: 0.18,\n technicalTerms: 0.1,\n creativeMarkers: 0.05,\n simpleIndicators: 0.02, // Reduced from 0.12 to make room for agenticTask\n multiStepPatterns: 0.12,\n questionComplexity: 0.05,\n imperativeVerbs: 0.03,\n constraintCount: 0.04,\n outputFormat: 0.03,\n referenceComplexity: 0.02,\n negationComplexity: 0.01,\n domainSpecificity: 0.02,\n agenticTask: 0.04, // Reduced - agentic signals influence tier selection, not dominate it\n },\n // Tier boundaries on weighted score axis\n tierBoundaries: {\n simpleMedium: 0.0,\n mediumComplex: 0.3, // Raised from 0.18 - prevent simple tasks from reaching expensive COMPLEX tier\n complexReasoning: 0.5, // Raised from 0.4 - reserve for true reasoning tasks\n },\n\n // Sigmoid steepness for confidence calibration\n confidenceSteepness: 12,\n // Below this confidence → ambiguous (null tier)\n confidenceThreshold: 0.7,\n },\n\n overrides: {\n structuredOutputMinTier: \"MEDIUM\",\n ambiguousDefaultTier: \"MEDIUM\",\n },\n} satisfies RoutingConfig;\n","import type { Tier } from \"./types.js\";\n\nexport type TraceMode = \"off\" | \"summary\" | \"debug\";\nexport type TraceReason =\n | \"first-pass\"\n | \"user\"\n | \"reasoning\"\n | \"error\";\nexport type TraceSessionAction =\n | \"none\"\n | \"set\"\n | \"reuse\";\n\nexport type TraceAttempt = {\n model: string;\n status: \"success\" | \"error\";\n error?: string;\n};\n\nexport type TraceSummaryInput = {\n requestedModel: string;\n routedModel: string;\n actualModel: string;\n tier: Tier;\n profile: string;\n reason: TraceReason;\n routed: boolean;\n explicit: boolean;\n fallback: boolean;\n};\n\nexport type RouteTraceLog = TraceSummaryInput & {\n trace: string;\n method?: string;\n confidence?: number;\n score?: number;\n agenticScore?: number;\n attempts: TraceAttempt[];\n sessionAction: TraceSessionAction;\n promptPreview?: string;\n};\n\nexport type TraceWriter = (message: string) => void;\nexport type TraceLogger = { debug?: TraceWriter; info?: TraceWriter };\n\nfunction defaultTraceWriter(): TraceWriter {\n return console.debug.bind(console);\n}\n\nexport function resolveTraceWriter(logger?: TraceLogger): TraceWriter {\n return logger?.debug ?? logger?.info ?? defaultTraceWriter();\n}\n\nexport function normalizeTraceMode(mode: unknown): TraceMode {\n if (mode === \"summary\" || mode === \"debug\") {\n return mode;\n }\n\n return \"off\";\n}\n\nexport function getPromptPreview(prompt: string): string {\n const chars = Array.from(prompt);\n\n if (chars.length <= 24) {\n return prompt;\n }\n\n return `${chars.slice(0, 10).join(\"\")}...${chars.slice(-10).join(\"\")}`;\n}\n\nexport function buildTraceSummary(input: TraceSummaryInput): string {\n const requestCode = getRequestCode(input);\n const profileCode = getProfileOrTierCode(input.profile, input.tier);\n return [\n requestCode,\n profileCode,\n input.routedModel,\n input.reason,\n ].join(\":\");\n}\n\nexport function emitRouteTrace(\n mode: TraceMode,\n detail: RouteTraceLog,\n writer: TraceWriter = defaultTraceWriter(),\n): void {\n if (mode === \"off\") {\n return;\n }\n\n if (mode === \"summary\") {\n try {\n writer(\n `[llm-router] ${detail.trace} model=${detail.actualModel} fallback=${detail.fallback}`,\n );\n } catch {\n // Tracing is diagnostic only; logging failures must not affect routing.\n }\n return;\n }\n\n try {\n writer(JSON.stringify(detail));\n } catch {\n // Tracing is diagnostic only; logging failures must not affect routing.\n }\n}\n\nfunction getRequestCode(input: TraceSummaryInput): string {\n if (input.explicit || !input.routed) {\n return \"explicit\";\n }\n\n if (input.requestedModel !== \"auto\") {\n return input.requestedModel;\n }\n\n return \"auto\";\n}\n\nfunction getProfileOrTierCode(profile: string, tier: Tier): string {\n if (profile === \"agentic\") {\n return \"agentic\";\n }\n\n return tier.toLowerCase();\n}\n","import { getStrategy } from \"./strategy.js\";\nimport type { RouterOptions, RoutingDecision } from \"./types.js\";\n\n/**\n * Router 统一入口。\n *\n * 当前总是走规则路由(`rules` strategy),并返回 alias / tier 级别的决策;\n * 它不会解析 physical model,也不会直接接触 HTTP 请求。\n */\nexport function route(\n prompt: string,\n systemPrompt: string | undefined,\n maxOutputTokens: number,\n options: RouterOptions,\n): RoutingDecision {\n return getStrategy(\"rules\").route(\n prompt,\n systemPrompt,\n maxOutputTokens,\n options,\n );\n}\n\nexport { DEFAULT_ROUTING_CONFIG } from \"./config.js\";\nexport { getStrategy, registerStrategy, RulesStrategy } from \"./strategy.js\";\nexport {\n calculateModelCost,\n filterByExcludeList,\n getFallbackChain,\n selectModel,\n} from \"./selector.js\";\nexport {\n buildTraceSummary,\n emitRouteTrace,\n getPromptPreview,\n normalizeTraceMode,\n resolveTraceWriter,\n} from \"./trace.js\";\nexport type {\n OverridesConfig,\n RouterOptions,\n RouterStrategy,\n RoutingConfig,\n RoutingDecision,\n ScoringConfig,\n ScoringResult,\n Tier,\n TierConfig,\n} from \"./types.js\";\nexport type { ModelPricing } from \"./selector.js\";\nexport type {\n RouteTraceLog,\n TraceAttempt,\n TraceLogger,\n TraceMode,\n TraceReason,\n TraceSessionAction,\n TraceSummaryInput,\n TraceWriter,\n} from \"./trace.js\";\n","import { normalizeTraceMode } from \"./router/index.js\";\nimport type { TraceLogger, TraceMode } from \"./router/index.js\";\n\nexport const DEFAULT_BASE_URL = \"https://api.deepseek.com\";\nexport const DEFAULT_PORT = 8402;\n\nexport type RouterConfig = {\n baseUrl: string;\n apiKey?: string;\n headers: Record<string, string>;\n port: number;\n sessionPinning: boolean;\n traceMode: TraceMode;\n traceLogger?: TraceLogger;\n};\n\nexport type RouterConfigInput = Partial<RouterConfig>;\n\nexport function normalizeBaseUrl(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\n/**\n * Pure config resolver — all values come from the caller, no process.env.\n */\nexport function resolveConfig(input: RouterConfigInput = {}): RouterConfig {\n return {\n baseUrl: normalizeBaseUrl(input.baseUrl ?? DEFAULT_BASE_URL),\n apiKey: input.apiKey,\n headers: input.headers ?? {},\n port: input.port ?? DEFAULT_PORT,\n sessionPinning: input.sessionPinning ?? true,\n traceMode: normalizeTraceMode(input.traceMode),\n traceLogger: input.traceLogger,\n };\n}\n","import type { PhysicalModel } from \"./config-schema.js\";\n\n/**\n * 运行时模型注册表,提供 O(1) 模型查询 API\n *\n * 不变性保证:所有返回值均为内部数据的浅拷贝,外部修改不会污染注册表。\n */\nexport type ModelRegistry = {\n /**\n * 根据 ID 获取物理模型,不存在返回 undefined。\n * 返回的对象是副本,修改不会影响注册表内部状态。\n */\n get(id: string): PhysicalModel | undefined;\n /** 检查模型 ID 是否存在 */\n has(id: string): boolean;\n /**\n * 返回所有注册的物理模型。\n * 返回的数组及其元素均为副本,修改不会影响注册表内部状态。\n */\n all(): readonly PhysicalModel[];\n};\n\n/**\n * 从物理模型列表创建运行时注册表\n *\n * 注册表持有传入模型的浅拷贝,调用方后续修改原始数组或对象不会影响注册表。\n *\n * @param models 物理模型列表\n * @returns 模型注册表实例\n * @throws {Error} 如果存在重复的模型 ID\n */\nexport function createModelRegistry(models: PhysicalModel[]): ModelRegistry {\n const map = new Map<string, PhysicalModel>();\n\n for (const model of models) {\n if (map.has(model.id)) {\n throw new Error(`Duplicate model ID: ${model.id}`);\n }\n // 存入浅拷贝,隔离外部对原对象的后续修改\n map.set(model.id, { ...model });\n }\n\n return {\n get(id: string) {\n const model = map.get(id);\n // 返回浅拷贝,防止外部修改污染内部状态\n return model ? { ...model } : undefined;\n },\n has(id: string) {\n return map.has(id);\n },\n all() {\n return Array.from(map.values(), (model) => ({ ...model }));\n },\n };\n}\n","import type { PublicModelConfig } from \"./config-schema.js\";\nimport type { PhysicalModel } from \"./config-schema.js\";\nimport type { ModelRegistry } from \"./model-registry.js\";\n\n/**\n * 将公开模型 ID 解析为物理模型 ID\n * @param publicModelId 公开模型 ID(如 \"flash\", \"pro\")\n * @param publicModels 公开模型配置映射\n * @param registry 物理模型注册表\n * @returns 物理模型 ID\n * @throws {Error} 如果公开模型不存在或 kind 为 \"router\"\n */\nexport function resolvePublicModel(\n publicModelId: string,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n): string {\n return resolvePublicModelCandidate(publicModelId, publicModels, registry).id;\n}\n\n/**\n * 将公开模型 ID 解析为物理模型\n * @param publicModelId 公开模型 ID(如 \"flash\", \"pro\")\n * @param publicModels 公开模型配置映射\n * @param registry 物理模型注册表\n * @returns 选中的物理模型\n * @throws {Error} 如果公开模型不存在、kind 为 \"router\" 或候选模型不可用\n */\nexport function resolvePublicModelCandidate(\n publicModelId: string,\n publicModels: Record<string, PublicModelConfig>,\n registry: ModelRegistry,\n): PhysicalModel {\n const pub = publicModels[publicModelId];\n if (!pub) {\n throw new Error(`Unknown public model: ${publicModelId}`);\n }\n if (pub.kind === \"router\") {\n throw new Error(\"Cannot resolve router model directly\");\n }\n\n const candidateId = selectCandidateId(pub.candidates, registry, pub.selection ?? \"cheapest\");\n const candidate = registry.get(candidateId);\n if (!candidate) {\n throw new Error(`Candidate model not found in registry: ${candidateId}`);\n }\n return candidate;\n}\n\nfunction selectCandidateId(\n candidates: string[],\n registry: ModelRegistry,\n selection: \"cheapest\" | \"first\",\n): string {\n if (candidates.length === 0) {\n throw new Error(\"No candidates available for public model\");\n }\n if (selection === \"first\") {\n const first = candidates[0]!;\n if (!registry.has(first)) {\n throw new Error(`Candidate model not found in registry: ${first}`);\n }\n return first;\n }\n\n let selectedId: string | undefined;\n let selectedCost = Number.POSITIVE_INFINITY;\n\n for (const candidateId of candidates) {\n const model = registry.get(candidateId);\n if (!model) {\n throw new Error(`Candidate model not found in registry: ${candidateId}`);\n }\n\n const cost = model.inputPrice + model.outputPrice;\n if (cost < selectedCost) {\n selectedId = candidateId;\n selectedCost = cost;\n }\n }\n\n if (!selectedId) {\n throw new Error(\"No candidates available for public model\");\n }\n\n return selectedId;\n}\n","import { createHash } from \"node:crypto\";\n\nimport type { Tier } from \"./router/types.js\";\n\n/**\n * 自动路由请求的 session pinning 状态。\n *\n * 这里缓存的是最近一次稳定命中的 alias / physical model 组合,用来减少同一\n * 会话内的 tier 抖动。\n */\nexport type SessionEntry = {\n sessionId: string;\n physicalModelId: string;\n routedPublicModel: string;\n pinnedTier: Tier;\n createdAt: number;\n updatedAt: number;\n expiresAt: number;\n inputTokens: number;\n outputTokens: number;\n costEstimate: number;\n};\n\nexport type SessionConfig = {\n enabled: boolean;\n ttlMs: number;\n cleanupIntervalMs: number;\n};\n\nexport const DEFAULT_SESSION_CONFIG: SessionConfig = {\n enabled: true,\n ttlMs: 30 * 60 * 1000,\n cleanupIntervalMs: 5 * 60 * 1000,\n};\n\nexport type SessionStats = {\n enabled: boolean;\n size: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCostEstimate: number;\n};\n\n/**\n * 仅服务于 `auto` 路由的轻量内存 session store。\n *\n * 它不是通用会话数据库;这里只关心 pinning、TTL 和少量成本统计。\n */\nexport class SessionStore {\n private readonly config: SessionConfig;\n private readonly sessions = new Map<string, SessionEntry>();\n private readonly cleanupTimer?: NodeJS.Timeout;\n\n constructor(config: Partial<SessionConfig> = {}) {\n this.config = { ...DEFAULT_SESSION_CONFIG, ...config };\n\n // 后台被动清理过期 session,避免长期运行时内存无限增长。\n if (this.config.enabled && this.config.cleanupIntervalMs > 0) {\n this.cleanupTimer = setInterval(() => {\n this.cleanupExpired();\n }, this.config.cleanupIntervalMs);\n this.cleanupTimer.unref?.();\n }\n }\n\n /**\n * 读取一个未过期 session;如果已经过期,会顺手删除并返回 undefined。\n */\n getSession(sessionId: string | undefined): SessionEntry | undefined {\n if (!this.config.enabled || !sessionId) return undefined;\n\n const entry = this.sessions.get(sessionId);\n if (!entry) return undefined;\n if (this.isExpired(entry)) {\n this.sessions.delete(sessionId);\n return undefined;\n }\n\n return entry;\n }\n\n /**\n * 创建或更新某个 session 的 pinning 结果,同时保留历史用量累计值。\n */\n setSession(\n sessionId: string | undefined,\n input: {\n physicalModelId: string;\n routedPublicModel: string;\n pinnedTier: Tier;\n },\n ): SessionEntry | undefined {\n if (!this.config.enabled || !sessionId) return undefined;\n\n const now = Date.now();\n const existing = this.getSession(sessionId);\n const entry: SessionEntry = {\n sessionId,\n physicalModelId: input.physicalModelId,\n routedPublicModel: input.routedPublicModel,\n pinnedTier: input.pinnedTier,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n expiresAt: now + this.config.ttlMs,\n inputTokens: existing?.inputTokens ?? 0,\n outputTokens: existing?.outputTokens ?? 0,\n costEstimate: existing?.costEstimate ?? 0,\n };\n\n this.sessions.set(sessionId, entry);\n return entry;\n }\n\n /**\n * 只刷新 TTL,不改动当前 alias / physical model 选择结果。\n */\n touchSession(sessionId: string | undefined): boolean {\n const entry = this.getSession(sessionId);\n if (!entry) return false;\n\n const now = Date.now();\n entry.updatedAt = now;\n entry.expiresAt = now + this.config.ttlMs;\n return true;\n }\n\n clearSession(sessionId: string | undefined): boolean {\n if (!sessionId) return false;\n return this.sessions.delete(sessionId);\n }\n\n clearAll(): void {\n this.sessions.clear();\n }\n\n /**\n * 返回清理过期项后的聚合统计。\n */\n getStats(): SessionStats {\n this.cleanupExpired();\n\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n let totalCostEstimate = 0;\n\n for (const entry of this.sessions.values()) {\n totalInputTokens += entry.inputTokens;\n totalOutputTokens += entry.outputTokens;\n totalCostEstimate += entry.costEstimate;\n }\n\n return {\n enabled: this.config.enabled,\n size: this.sessions.size,\n totalInputTokens,\n totalOutputTokens,\n totalCostEstimate,\n };\n }\n\n /**\n * 把一次上游调用的 token / cost 增量累计到 session 上。\n */\n recordUsage(\n sessionId: string | undefined,\n usage: {\n inputTokens?: number;\n outputTokens?: number;\n costEstimate?: number;\n },\n ): void {\n const entry = this.getSession(sessionId);\n if (!entry) return;\n\n entry.inputTokens += usage.inputTokens ?? 0;\n entry.outputTokens += usage.outputTokens ?? 0;\n entry.costEstimate += usage.costEstimate ?? 0;\n entry.updatedAt = Date.now();\n }\n\n /**\n * 停止后台清理定时器。\n */\n close(): void {\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer);\n }\n }\n\n private cleanupExpired(): void {\n if (!this.config.enabled) return;\n\n for (const [sessionId, entry] of this.sessions) {\n if (this.isExpired(entry)) {\n this.sessions.delete(sessionId);\n }\n }\n }\n\n private isExpired(entry: SessionEntry): boolean {\n return Date.now() >= entry.expiresAt;\n }\n}\n\n/**\n * 把请求文本和工具集合归一化后压缩成稳定短哈希,作为隐式 session key。\n */\nexport function hashRequestContent(\n content: string,\n toolNames: string[] = [],\n): string {\n const normalizedContent = content.trim().replace(/\\s+/g, \" \");\n const normalizedTools = [...toolNames]\n .map((tool) => tool.trim())\n .filter(Boolean)\n .sort()\n .join(\",\");\n return hashHex(`${normalizedContent}\\n${normalizedTools}`, 8);\n}\n\n/**\n * 优先读取显式 `x-session-id`,否则退化为首条 user 消息内容哈希。\n */\nexport function deriveSessionId(\n headers: Record<string, string | string[] | undefined>,\n messages: unknown[],\n): string | undefined {\n const explicit = headers[\"x-session-id\"];\n const explicitId = pickHeaderValue(explicit);\n if (explicitId) return explicitId;\n\n const firstUserContent = findFirstUserContent(messages);\n if (!firstUserContent) return undefined;\n\n return hashRequestContent(firstUserContent);\n}\n\nfunction pickHeaderValue(\n value: string | string[] | undefined,\n): string | undefined {\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed || undefined;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n const trimmed = item.trim();\n if (trimmed) return trimmed;\n }\n }\n\n return undefined;\n}\n\n/**\n * 提取第一条 user 消息文本,用作默认 session key 来源。\n */\nfunction findFirstUserContent(messages: unknown[]): string | undefined {\n for (const message of messages) {\n if (!message || typeof message !== \"object\") continue;\n\n const record = message as Record<string, unknown>;\n if (record.role !== \"user\") continue;\n\n const text = contentToText(record.content);\n if (text.trim()) return text;\n }\n\n return undefined;\n}\n\n/**\n * 同时兼容 `content: string` 和 OpenAI 风格的多 part 文本数组。\n */\nfunction contentToText(content: unknown): string {\n if (typeof content === \"string\") return content;\n\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (!part || typeof part !== \"object\") return \"\";\n const record = part as Record<string, unknown>;\n return record.type === \"text\" && typeof record.text === \"string\"\n ? record.text\n : \"\";\n })\n .filter(Boolean)\n .join(\" \");\n }\n\n return \"\";\n}\n\nfunction hashHex(value: string, length: number): string {\n return createHash(\"sha256\").update(value).digest(\"hex\").slice(0, length);\n}\n"],"mappings":"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AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA;AAAA,MAEd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA;AAAA,MAEf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,sBAAsB;AAAA;AAAA,MAEpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,mBAAmB;AAAA;AAAA,MAEjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,kBAAkB;AAAA;AAAA,MAEhB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,wBAAwB;AAAA;AAAA,MAEtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA;AAAA;AAAA,IAGA,qBAAqB;AAAA;AAAA,MAEnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA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","names":["registry","text","registry","physicalModel","trace","headers"]}
|