@yzj01/llm-router 1.0.0
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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.js +2490 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +370 -0
- package/dist/index.js +2726 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy-CrRX9deF.d.ts +222 -0
- package/openclaw.plugin.json +37 -0
- package/package.json +63 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { T as Tier, R as RawConfig, b as PhysicalModel, P as ProxyOptions, a as ProxyHandle, c as TraceMode, d as TraceLogger, C as ConfigSource, e as PublicModelConfig } from './proxy-CrRX9deF.js';
|
|
2
|
+
export { D as DEFAULT_SESSION_CONFIG, f as PublicModelMetadata, g as RouteTraceLog, S as SessionConfig, h as SessionEntry, i as SessionStats, j as SessionStore, k as TierEntry, l as TraceAttempt, m as TraceReason, n as TraceSessionAction, o as TraceSummaryInput, p as TraceWriter, V as VERSION, q as buildTraceSummary, r as deriveSessionId, s as emitRouteTrace, t as getPromptPreview, u as hashRequestContent, v as normalizeTraceMode, w as resolveTraceWriter, x as startProxy } from './proxy-CrRX9deF.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 路由层只关心 alias 的成本画像,因此这里的 key 是 public alias,而不是
|
|
6
|
+
* physical model ID。
|
|
7
|
+
*/
|
|
8
|
+
type ModelPricing = {
|
|
9
|
+
inputPrice: number;
|
|
10
|
+
outputPrice: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 把 tier 选择结果转换成统一的 RoutingDecision,并补齐成本估算字段。
|
|
14
|
+
*/
|
|
15
|
+
declare function selectModel(tier: Tier, confidence: number, method: "rules" | "llm", reasoning: string, tierConfigs: Record<Tier, TierConfig>, modelPricing: Map<string, ModelPricing>, estimatedInputTokens: number, maxOutputTokens: number, agenticScore?: number, score?: number): RoutingDecision;
|
|
16
|
+
/**
|
|
17
|
+
* 返回某个 tier 的完整尝试链:先 primary,再按顺序追加 fallback。
|
|
18
|
+
*/
|
|
19
|
+
declare function getFallbackChain(tier: Tier, tierConfigs: Record<Tier, TierConfig>): string[];
|
|
20
|
+
/**
|
|
21
|
+
* 按 1M token 单价估算当前 alias 的输入 / 输出成本,并和 baseline 进行对比。
|
|
22
|
+
*/
|
|
23
|
+
declare function calculateModelCost(model: string, modelPricing: Map<string, ModelPricing>, estimatedInputTokens: number, maxOutputTokens: number, baselineModelId?: string): {
|
|
24
|
+
costEstimate: number;
|
|
25
|
+
baselineCost: number;
|
|
26
|
+
savings: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* 从候选链里剔除不允许继续尝试的模型;如果剔除后为空,则保底返回原链,避免
|
|
30
|
+
* 调用方拿到“无路可走”的空数组。
|
|
31
|
+
*/
|
|
32
|
+
declare function filterByExcludeList(models: string[], excludeList: Set<string>): string[];
|
|
33
|
+
|
|
34
|
+
type ScoringResult = {
|
|
35
|
+
score: number;
|
|
36
|
+
tier: Tier | null;
|
|
37
|
+
confidence: number;
|
|
38
|
+
signals: string[];
|
|
39
|
+
agenticScore?: number;
|
|
40
|
+
dimensions?: Array<{
|
|
41
|
+
name: string;
|
|
42
|
+
score: number;
|
|
43
|
+
signal: string | null;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
type RoutingDecision = {
|
|
47
|
+
publicModel: string;
|
|
48
|
+
tier: Tier;
|
|
49
|
+
confidence: number;
|
|
50
|
+
method: "rules" | "llm";
|
|
51
|
+
reasoning: string;
|
|
52
|
+
score?: number;
|
|
53
|
+
costEstimate: number;
|
|
54
|
+
baselineCost: number;
|
|
55
|
+
savings: number;
|
|
56
|
+
agenticScore?: number;
|
|
57
|
+
tierConfigs?: Record<Tier, TierConfig>;
|
|
58
|
+
profile?: "default";
|
|
59
|
+
};
|
|
60
|
+
interface RouterStrategy {
|
|
61
|
+
readonly name: string;
|
|
62
|
+
route(prompt: string, systemPrompt: string | undefined, maxOutputTokens: number, options: RouterOptions): RoutingDecision;
|
|
63
|
+
}
|
|
64
|
+
type RouterOptions = {
|
|
65
|
+
strategy?: string;
|
|
66
|
+
config?: RoutingConfig;
|
|
67
|
+
modelPricing?: Map<string, ModelPricing>;
|
|
68
|
+
};
|
|
69
|
+
type TierConfig = {
|
|
70
|
+
primary: string;
|
|
71
|
+
fallback: string[];
|
|
72
|
+
};
|
|
73
|
+
type ScoringConfig = {
|
|
74
|
+
tokenCountThresholds: {
|
|
75
|
+
simple: number;
|
|
76
|
+
complex: number;
|
|
77
|
+
};
|
|
78
|
+
codeKeywords: string[];
|
|
79
|
+
reasoningKeywords: string[];
|
|
80
|
+
simpleKeywords: string[];
|
|
81
|
+
technicalKeywords: string[];
|
|
82
|
+
creativeKeywords: string[];
|
|
83
|
+
imperativeVerbs: string[];
|
|
84
|
+
constraintIndicators: string[];
|
|
85
|
+
outputFormatKeywords: string[];
|
|
86
|
+
referenceKeywords: string[];
|
|
87
|
+
negationKeywords: string[];
|
|
88
|
+
domainSpecificKeywords: string[];
|
|
89
|
+
agenticTaskKeywords: string[];
|
|
90
|
+
dimensionWeights: Record<string, number>;
|
|
91
|
+
tierBoundaries: {
|
|
92
|
+
simpleMedium: number;
|
|
93
|
+
mediumComplex: number;
|
|
94
|
+
complexReasoning: number;
|
|
95
|
+
};
|
|
96
|
+
confidenceSteepness: number;
|
|
97
|
+
confidenceThreshold: number;
|
|
98
|
+
};
|
|
99
|
+
type OverridesConfig = {
|
|
100
|
+
structuredOutputMinTier?: Tier;
|
|
101
|
+
ambiguousDefaultTier?: Tier;
|
|
102
|
+
};
|
|
103
|
+
type RoutingConfig = {
|
|
104
|
+
version: string;
|
|
105
|
+
scoring: ScoringConfig;
|
|
106
|
+
tiers?: Record<Tier, TierConfig>;
|
|
107
|
+
overrides?: OverridesConfig;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
declare const DEFAULT_ROUTING_CONFIG: {
|
|
111
|
+
version: string;
|
|
112
|
+
scoring: {
|
|
113
|
+
tokenCountThresholds: {
|
|
114
|
+
simple: number;
|
|
115
|
+
complex: number;
|
|
116
|
+
};
|
|
117
|
+
codeKeywords: string[];
|
|
118
|
+
reasoningKeywords: string[];
|
|
119
|
+
simpleKeywords: string[];
|
|
120
|
+
technicalKeywords: string[];
|
|
121
|
+
creativeKeywords: string[];
|
|
122
|
+
imperativeVerbs: string[];
|
|
123
|
+
constraintIndicators: string[];
|
|
124
|
+
outputFormatKeywords: string[];
|
|
125
|
+
referenceKeywords: string[];
|
|
126
|
+
negationKeywords: string[];
|
|
127
|
+
domainSpecificKeywords: string[];
|
|
128
|
+
agenticTaskKeywords: string[];
|
|
129
|
+
dimensionWeights: {
|
|
130
|
+
tokenCount: number;
|
|
131
|
+
codePresence: number;
|
|
132
|
+
reasoningMarkers: number;
|
|
133
|
+
technicalTerms: number;
|
|
134
|
+
creativeMarkers: number;
|
|
135
|
+
simpleIndicators: number;
|
|
136
|
+
multiStepPatterns: number;
|
|
137
|
+
questionComplexity: number;
|
|
138
|
+
imperativeVerbs: number;
|
|
139
|
+
constraintCount: number;
|
|
140
|
+
outputFormat: number;
|
|
141
|
+
referenceComplexity: number;
|
|
142
|
+
negationComplexity: number;
|
|
143
|
+
domainSpecificity: number;
|
|
144
|
+
agenticTask: number;
|
|
145
|
+
};
|
|
146
|
+
tierBoundaries: {
|
|
147
|
+
simpleMedium: number;
|
|
148
|
+
mediumComplex: number;
|
|
149
|
+
complexReasoning: number;
|
|
150
|
+
};
|
|
151
|
+
confidenceSteepness: number;
|
|
152
|
+
confidenceThreshold: number;
|
|
153
|
+
};
|
|
154
|
+
overrides: {
|
|
155
|
+
structuredOutputMinTier: "MEDIUM";
|
|
156
|
+
ambiguousDefaultTier: "MEDIUM";
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 基于规则的默认路由策略。
|
|
162
|
+
*
|
|
163
|
+
* 它只负责把 prompt 映射到 tier / alias 决策,不参与 physical model 解析,
|
|
164
|
+
* 因此可以长期保持纯函数式、可测试的语义层逻辑。
|
|
165
|
+
*/
|
|
166
|
+
declare class RulesStrategy implements RouterStrategy {
|
|
167
|
+
readonly name = "rules";
|
|
168
|
+
route(prompt: string, systemPrompt: string | undefined, maxOutputTokens: number, options: RouterOptions): RoutingDecision;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 从策略注册表中读取命名路由策略。
|
|
172
|
+
*/
|
|
173
|
+
declare function getStrategy(name: string): RouterStrategy;
|
|
174
|
+
/**
|
|
175
|
+
* 允许外部测试或扩展模块注册新的路由策略实现。
|
|
176
|
+
*/
|
|
177
|
+
declare function registerStrategy(strategy: RouterStrategy): void;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Router 统一入口。
|
|
181
|
+
*
|
|
182
|
+
* 当前总是走规则路由(`rules` strategy),并返回 alias / tier 级别的决策;
|
|
183
|
+
* 它不会解析 physical model,也不会直接接触 HTTP 请求。
|
|
184
|
+
*/
|
|
185
|
+
declare function route(prompt: string, systemPrompt: string | undefined, maxOutputTokens: number, options: RouterOptions): RoutingDecision;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* OpenClaw 侧约定的 provider 标识。
|
|
189
|
+
*
|
|
190
|
+
* Router 本身并不注册 provider 实现,但会持续修复
|
|
191
|
+
* `models.providers.xiaoyiprovider` 这段配置。
|
|
192
|
+
*/
|
|
193
|
+
declare const LLM_ROUTER_PROVIDER_ID = "xiaoyiprovider";
|
|
194
|
+
declare const LLM_ROUTER_PROVIDER_NAME = "LLM Router Provider";
|
|
195
|
+
declare const LLM_ROUTER_PROVIDER_DESCRIPTION = "LLM Router local routing provider for OpenAI-compatible models";
|
|
196
|
+
declare const LLM_ROUTER_PROVIDER_API = "openai-completions";
|
|
197
|
+
/**
|
|
198
|
+
* 写入 OpenClaw `models.providers.*.models[]` 时使用的模型元数据结构。
|
|
199
|
+
*
|
|
200
|
+
* 这里描述的是“OpenClaw 看到的模型目录项”,不等同于实际上游请求里的
|
|
201
|
+
* physical model。
|
|
202
|
+
*/
|
|
203
|
+
type OpenClawModelDefinition = {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
api: typeof LLM_ROUTER_PROVIDER_API;
|
|
207
|
+
reasoning: boolean;
|
|
208
|
+
input: ["text"];
|
|
209
|
+
cost: {
|
|
210
|
+
input: number;
|
|
211
|
+
output: number;
|
|
212
|
+
cacheRead: number;
|
|
213
|
+
cacheWrite: number;
|
|
214
|
+
};
|
|
215
|
+
contextWindow: number;
|
|
216
|
+
maxTokens: number;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* 根据运行时配置生成 OpenClaw provider 模型目录。
|
|
220
|
+
*
|
|
221
|
+
* 注意:这个函数会为所有 `publicModels` 生成完整元数据;最终是否全部暴露给
|
|
222
|
+
* OpenClaw,由调用方(当前是 `plugin.ts`)决定。这样可以把“生成目录”和
|
|
223
|
+
* “裁剪对外暴露范围”两个职责分开。
|
|
224
|
+
*/
|
|
225
|
+
declare function generateOpenClawModels(publicModels: RawConfig["publicModels"], physicalModels: PhysicalModel[]): OpenClawModelDefinition[];
|
|
226
|
+
|
|
227
|
+
type JsonObject = Record<string, unknown>;
|
|
228
|
+
type OpenClawService = {
|
|
229
|
+
id: "llm-router-proxy";
|
|
230
|
+
start: (ctx?: unknown) => Promise<void>;
|
|
231
|
+
stop: (ctx?: unknown) => Promise<void>;
|
|
232
|
+
};
|
|
233
|
+
type OpenClawPluginApi = {
|
|
234
|
+
config: JsonObject;
|
|
235
|
+
pluginConfig?: {
|
|
236
|
+
port?: unknown;
|
|
237
|
+
upstreamUrl?: unknown;
|
|
238
|
+
trace?: unknown;
|
|
239
|
+
config?: RawConfig;
|
|
240
|
+
configPath?: string;
|
|
241
|
+
} | Record<string, unknown>;
|
|
242
|
+
registrationMode?: string;
|
|
243
|
+
runtime?: {
|
|
244
|
+
config?: {
|
|
245
|
+
mutateConfigFile?: <T = void>(params: {
|
|
246
|
+
afterWrite: {
|
|
247
|
+
mode: "auto";
|
|
248
|
+
} | {
|
|
249
|
+
mode: "none";
|
|
250
|
+
reason: string;
|
|
251
|
+
};
|
|
252
|
+
mutate: (draft: JsonObject) => T | void | Promise<T | void>;
|
|
253
|
+
}) => Promise<unknown>;
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
registerProvider?: (provider: unknown) => void;
|
|
257
|
+
registerService: (service: OpenClawService) => void;
|
|
258
|
+
logger?: {
|
|
259
|
+
debug?: (message: string) => void;
|
|
260
|
+
info?: (message: string) => void;
|
|
261
|
+
error?: (message: string) => void;
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
type OpenClawPlugin = {
|
|
265
|
+
id: "llm-router";
|
|
266
|
+
name: "LLM Router";
|
|
267
|
+
description: "LLM Router local routing proxy for OpenClaw";
|
|
268
|
+
version: string;
|
|
269
|
+
register: (api: OpenClawPluginApi) => void;
|
|
270
|
+
};
|
|
271
|
+
type PluginRuntime = {
|
|
272
|
+
startProxy: (options: ProxyOptions) => Promise<ProxyHandle>;
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* 计算 Router 本地 provider base URL。
|
|
276
|
+
*/
|
|
277
|
+
declare function localProviderBaseUrl(port: number): string;
|
|
278
|
+
/**
|
|
279
|
+
* 把 Router 管理的 provider 配置注入到 OpenClaw 全局配置里。
|
|
280
|
+
*
|
|
281
|
+
* 关键策略:虽然 `provider.ts` 会生成完整 alias 元数据,但真正写回 OpenClaw
|
|
282
|
+
* 时只暴露 `auto`,从而把 alias 选择权完全留在 Router 内部。
|
|
283
|
+
*/
|
|
284
|
+
declare function injectLlmRouterModelsConfig(config: JsonObject, providerBaseUrl: string, modelDefinitions: OpenClawModelDefinition[]): void;
|
|
285
|
+
/**
|
|
286
|
+
* OpenClaw 插件主注册函数。
|
|
287
|
+
*
|
|
288
|
+
* 注册顺序很关键:
|
|
289
|
+
* 1. 先解析运行时配置;
|
|
290
|
+
* 2. 生成 provider 元数据;
|
|
291
|
+
* 3. 把 `auto` 注入到 OpenClaw;
|
|
292
|
+
* 4. 只有在运行态模式下才真正注册并启动本地 proxy service。
|
|
293
|
+
*/
|
|
294
|
+
declare function registerOpenClawPlugin(api: OpenClawPluginApi, runtime?: PluginRuntime): void;
|
|
295
|
+
|
|
296
|
+
type RouterConfig = {
|
|
297
|
+
baseUrl: string;
|
|
298
|
+
apiKey?: string;
|
|
299
|
+
headers: Record<string, string>;
|
|
300
|
+
port: number;
|
|
301
|
+
sessionPinning: boolean;
|
|
302
|
+
traceMode: TraceMode;
|
|
303
|
+
traceLogger?: TraceLogger;
|
|
304
|
+
};
|
|
305
|
+
type RouterConfigInput = Partial<RouterConfig>;
|
|
306
|
+
/**
|
|
307
|
+
* Pure config resolver — all values come from the caller, no process.env.
|
|
308
|
+
*/
|
|
309
|
+
declare function resolveConfig(input?: RouterConfigInput): RouterConfig;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 加载并校验配置文件
|
|
313
|
+
* @param source 配置源:内联对象或文件路径
|
|
314
|
+
* @returns 校验通过的配置对象
|
|
315
|
+
* @throws {Error} 如果配置不合法(重复 ID、引用不存在、缺少 auto 等)
|
|
316
|
+
*/
|
|
317
|
+
declare function loadConfig(source: ConfigSource): RawConfig;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 运行时模型注册表,提供 O(1) 模型查询 API
|
|
321
|
+
*
|
|
322
|
+
* 不变性保证:所有返回值均为内部数据的浅拷贝,外部修改不会污染注册表。
|
|
323
|
+
*/
|
|
324
|
+
type ModelRegistry = {
|
|
325
|
+
/**
|
|
326
|
+
* 根据 ID 获取物理模型,不存在返回 undefined。
|
|
327
|
+
* 返回的对象是副本,修改不会影响注册表内部状态。
|
|
328
|
+
*/
|
|
329
|
+
get(id: string): PhysicalModel | undefined;
|
|
330
|
+
/** 检查模型 ID 是否存在 */
|
|
331
|
+
has(id: string): boolean;
|
|
332
|
+
/**
|
|
333
|
+
* 返回所有注册的物理模型。
|
|
334
|
+
* 返回的数组及其元素均为副本,修改不会影响注册表内部状态。
|
|
335
|
+
*/
|
|
336
|
+
all(): readonly PhysicalModel[];
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* 从物理模型列表创建运行时注册表
|
|
340
|
+
*
|
|
341
|
+
* 注册表持有传入模型的浅拷贝,调用方后续修改原始数组或对象不会影响注册表。
|
|
342
|
+
*
|
|
343
|
+
* @param models 物理模型列表
|
|
344
|
+
* @returns 模型注册表实例
|
|
345
|
+
* @throws {Error} 如果存在重复的模型 ID
|
|
346
|
+
*/
|
|
347
|
+
declare function createModelRegistry(models: PhysicalModel[]): ModelRegistry;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* 将公开模型 ID 解析为物理模型 ID
|
|
351
|
+
* @param publicModelId 公开模型 ID(如 "flash", "pro")
|
|
352
|
+
* @param publicModels 公开模型配置映射
|
|
353
|
+
* @param registry 物理模型注册表
|
|
354
|
+
* @returns 物理模型 ID
|
|
355
|
+
* @throws {Error} 如果公开模型不存在或 kind 为 "router"
|
|
356
|
+
*/
|
|
357
|
+
declare function resolvePublicModel(publicModelId: string, publicModels: Record<string, PublicModelConfig>, registry: ModelRegistry): string;
|
|
358
|
+
/**
|
|
359
|
+
* 将公开模型 ID 解析为物理模型
|
|
360
|
+
* @param publicModelId 公开模型 ID(如 "flash", "pro")
|
|
361
|
+
* @param publicModels 公开模型配置映射
|
|
362
|
+
* @param registry 物理模型注册表
|
|
363
|
+
* @returns 选中的物理模型
|
|
364
|
+
* @throws {Error} 如果公开模型不存在、kind 为 "router" 或候选模型不可用
|
|
365
|
+
*/
|
|
366
|
+
declare function resolvePublicModelCandidate(publicModelId: string, publicModels: Record<string, PublicModelConfig>, registry: ModelRegistry): PhysicalModel;
|
|
367
|
+
|
|
368
|
+
declare const plugin: OpenClawPlugin;
|
|
369
|
+
|
|
370
|
+
export { ConfigSource, DEFAULT_ROUTING_CONFIG, LLM_ROUTER_PROVIDER_API, LLM_ROUTER_PROVIDER_DESCRIPTION, LLM_ROUTER_PROVIDER_ID, LLM_ROUTER_PROVIDER_NAME, type ModelPricing, type ModelRegistry, type OpenClawModelDefinition, type OpenClawPlugin, type OpenClawPluginApi, type OpenClawService, PhysicalModel, type PluginRuntime, ProxyHandle, ProxyOptions, PublicModelConfig, RawConfig, type RouterConfig, type RouterConfigInput, type RouterOptions, type RouterStrategy, type RoutingConfig, type RoutingDecision, RulesStrategy, type ScoringConfig, type ScoringResult, Tier, type TierConfig, TraceLogger, TraceMode, calculateModelCost, createModelRegistry, plugin as default, filterByExcludeList, generateOpenClawModels, getFallbackChain, getStrategy, injectLlmRouterModelsConfig, loadConfig, localProviderBaseUrl, registerOpenClawPlugin, registerStrategy, resolveConfig, resolvePublicModel, resolvePublicModelCandidate, route, selectModel };
|