@@ -4,162 +4,225 @@ import modelConsumptionData from "./model-consumption.json"
44import { state } from "./state"
55
66/**
7- * Get model consumption value
7+ * 获取模型消耗值
8+ * 从配置文件中查询指定模型的消耗系数
9+ *
10+ * @param modelName - 模型名称
11+ * @returns 消耗系数(如 1.0, 2.0 等),未找到或解析失败返回 999
12+ *
13+ * @example
14+ * getModelConsumption("claude-3.5-sonnet") // 返回 1.0
15+ * getModelConsumption("gpt-4") // 返回 2.0
16+ * getModelConsumption("unknown-model") // 返回 999
817 */
918function getModelConsumption ( modelName : string ) : number {
19+ // 将模型消耗数据转换为 Map 结构,方便快速查询
1020 const consumptionMap = new Map (
1121 modelConsumptionData . models . map ( ( m ) => [ m . name , m . consumption ] ) ,
1222 )
23+
24+ // 获取消耗值,未找到则返回 "N/A"
1325 const consumption = consumptionMap . get ( modelName ) || "N/A"
1426
27+ // 如果模型不在配置中,返回最大值 999
1528 if ( consumption === "N/A" ) return 999
29+
30+ // 解析消耗值(格式如 "1.0x", "2.5x")
1631 const match = consumption . match ( / ^ ( [ \d . ] + ) x $ / )
1732 return match ? Number . parseFloat ( match [ 1 ] ) : 999
1833}
1934
2035/**
21- * Check if premium interactions usage is high (>50%)
36+ * 检查高级交互配额使用率是否过高(超过 50%)
37+ *
38+ * 当高级模型使用率超过 50% 时,系统会倾向于使用 0x 消耗的模型,
39+ * 以避免快速耗尽配额
40+ *
41+ * @returns true 表示使用率 >50%,false 表示使用率 ≤50% 或未初始化
2242 */
2343function isPremiumUsageHigh ( ) : boolean {
44+ // 如果没有高级交互数据,认为使用率不高
2445 if ( ! state . premiumInteractions ) {
2546 return false
2647 }
2748
49+ // 计算已使用百分比:100% - 剩余百分比 = 已使用百分比
2850 const usagePercent = 100 - state . premiumInteractions . percent_remaining
2951 return usagePercent > 50
3052}
3153
3254/**
33- * Get all 0x consumption models
55+ * 获取所有 0x 消耗的模型列表
56+ *
57+ * 这些模型不计入高级交互配额,可以在配额紧张时优先使用
58+ *
59+ * @returns 0x 消耗模型的 ID 数组
3460 */
3561function getZeroConsumptionModels ( ) : string [ ] {
62+ // 筛选出有效的可用模型(必须有上下文窗口限制配置)
3663 const availableModels = state . models ?. data . filter (
3764 ( m ) => typeof m . capabilities ?. limits ?. max_context_window_tokens === "number" ,
3865 )
3966
4067 if ( ! availableModels ) return [ ]
4168
69+ // 过滤出消耗值为 0 的模型,返回其 ID
4270 return availableModels
4371 . filter ( ( m ) => getModelConsumption ( m . name ) === 0 )
4472 . map ( ( m ) => m . id )
4573}
4674
4775/**
48- * Find a matching model from available models
49- * If exact match exists, return it
50- * If no exact match, try to find by prefix (e.g., claude-haiku-4-5-xxx -> claude-haiku-4.5)
51- * If premium usage >50%, only match to 0x consumption models
76+ * 从可用模型中查找匹配的模型
77+ *
78+ * 匹配策略:
79+ * 1. 标准化匹配:标准化后的模型名称匹配(下划线转连字符、版本号格式化)
80+ * 2. 前缀匹配:模型名称前缀匹配
81+ * 3. 基础名称匹配:忽略版本号后的基础名称匹配
82+ *
83+ * 配额保护:
84+ * - 当高级交互使用率 >50% 时,所有匹配仅限于 0x 消耗模型
85+ * - 无匹配时降级到第一个 0x 模型
86+ *
87+ * @param requestedModel - 请求的模型标识符
88+ * @returns 匹配的模型 ID,未找到返回 null
89+ *
90+ * @example
91+ * findMatchingModel("claude-3-5-sonnet") // 返回 "claude-3.5-sonnet"
92+ * findMatchingModel("gpt-4-20240101") // 返回 "gpt-4"
5293 */
5394export function findMatchingModel ( requestedModel : string ) : string | null {
95+ // 获取所有有效的可用模型(必须配置了上下文窗口限制)
5496 const availableModels = state . models ?. data . filter (
5597 ( m ) => typeof m . capabilities ?. limits ?. max_context_window_tokens === "number" ,
5698 )
5799
100+ // 如果没有可用模型,直接返回 null
58101 if ( ! availableModels || availableModels . length === 0 ) {
59102 return null
60103 }
61104
105+ // 检查是否处于高使用率状态
62106 const highUsage = isPremiumUsageHigh ( )
107+
108+ // 如果使用率高,获取 0x 消耗模型列表用于限制匹配范围
63109 const zeroConsumptionModels = highUsage ? getZeroConsumptionModels ( ) : [ ]
110+
111+ // 提取所有可用模型的 ID
64112 const allAvailableModelIds = availableModels . map ( ( m ) => m . id )
65113
66- consola . debug ( `Looking for match for: ${ requestedModel } ` )
67- consola . debug ( `All available models: ${ allAvailableModelIds . join ( ", " ) } ` )
114+ consola . debug ( `正在查找匹配模型: ${ requestedModel } ` )
115+ consola . debug ( `所有可用模型: ${ allAvailableModelIds . join ( ", " ) } ` )
68116
69- // Try exact match first (always allow exact match, even if high usage)
70- if ( allAvailableModelIds . includes ( requestedModel ) ) {
71- // If high usage and model is not 0x, warn but still allow
72- if ( highUsage && ! zeroConsumptionModels . includes ( requestedModel ) ) {
73- consola . warn (
74- `⚠️ Premium usage >50%, but exact match found: ${ requestedModel } ` ,
75- )
76- }
77- return requestedModel
78- }
79-
80- // For fuzzy matching when usage is high, only consider 0x models
117+ // ========== 配额保护:高使用率时限制模糊匹配范围 ==========
81118 let availableModelIds = allAvailableModelIds
82119 if ( highUsage && zeroConsumptionModels . length > 0 ) {
83120 consola . info (
84- `⚠️ Premium usage >50%, restricting fuzzy matching to 0x consumption models ` ,
121+ `⚠️ 高级交互使用率 >50%,模糊匹配仅限 0x 消耗模型 ` ,
85122 )
86123 availableModelIds = zeroConsumptionModels
87- consola . debug ( `0x models for matching: ${ availableModelIds . join ( ", " ) } ` )
124+ consola . debug ( `用于匹配的 0x 模型: ${ availableModelIds . join ( ", " ) } ` )
88125 }
89126
90- // Normalize the requested model
91- // 1. Replace underscores with hyphens
92- // 2. Remove date suffix (8 digits at the end)
93- // 3. Replace version numbers: 4-5 -> 4.5
127+ // ========== 标准化处理:统一模型名称格式 ==========
128+ // 1. 转换为小写
129+ // 2. 下划线转连字符(claude_3_5 -> claude-3-5)
130+ // 3. 移除日期后缀(-20251001 等 8 位数字)
131+ // 4. 版本号格式化(4-5 -> 4.5)
94132 let normalizedRequested = requestedModel
95133 . toLowerCase ( )
96- . replace ( / _ / g, "-" )
97- . replace ( / - ( \d { 8 } ) $ / , "" ) // Remove -20251001 style suffix
98- . replace ( / ( \d ) - ( \d ) / g, "$1.$2" ) // Replace 4-5 with 4.5
134+ . replace ( / _ / g, "-" ) // 下划线转连字符
135+ . replace ( / - ( \d { 8 } ) $ / , "" ) // 移除 -20251001 风格的日期后缀
136+ . replace ( / ( \d ) - ( \d ) / g, "$1.$2" ) // 版本号: 4-5 -> 4.5
99137
100- consola . debug ( `Normalized requested: ${ normalizedRequested } ` )
138+ consola . debug ( `标准化后的请求模型: ${ normalizedRequested } ` )
101139
102- // Try exact match after normalization
140+ // ========== 策略 1:标准化后精确匹配 ==========
103141 for ( const availableId of availableModelIds ) {
104142 if ( availableId . toLowerCase ( ) === normalizedRequested ) {
105143 consola . info (
106- `🔄 Model normalized match: '${ requestedModel } ' -> '${ availableId } '` ,
144+ `🔄 标准化匹配成功: '${ requestedModel } ' -> '${ availableId } '` ,
107145 )
108146 return availableId
109147 }
110148 }
111149
112- // Try prefix matching
150+ // ========== 策略 2:前缀匹配 ==========
151+ // 检查请求的模型和可用模型是否有前缀关系
152+ // 例如:claude-3.5 可以匹配 claude-3.5-sonnet-20241022
113153 for ( const availableId of availableModelIds ) {
114154 const normalizedAvailable = availableId . toLowerCase ( )
115155
116- // Check if they start with each other
156+ // 双向前缀检查:请求模型是可用模型的前缀,或可用模型是请求模型的前缀
117157 if (
118158 normalizedAvailable . startsWith ( normalizedRequested ) ||
119159 normalizedRequested . startsWith ( normalizedAvailable )
120160 ) {
121161 consola . info (
122- `🔄 Model prefix match: '${ requestedModel } ' -> '${ availableId } '` ,
162+ `🔄 前缀匹配成功: '${ requestedModel } ' -> '${ availableId } '` ,
123163 )
124164 return availableId
125165 }
126166 }
127167
128- // Try fuzzy matching by comparing main parts
168+ // ========== 策略 3:基础名称匹配(忽略版本号) ==========
169+ // 将模型名称按 "-" 分割,比较除最后一部分外的所有部分
170+ // 例如:claude-3-5-sonnet-v2 和 claude-3-5-sonnet-v1 的基础名称都是 claude-3-5-sonnet
129171 const requestedParts = normalizedRequested . split ( "-" )
130172 for ( const availableId of availableModelIds ) {
131173 const normalizedAvailable = availableId . toLowerCase ( )
132174 const availableParts = normalizedAvailable . split ( "-" )
133175
134- // Match by comparing first N-1 parts (everything except version)
176+ // 只对至少有 3 个部分的模型名称进行基础匹配(避免过于宽泛)
135177 if ( requestedParts . length >= 3 && availableParts . length >= 3 ) {
178+ // 提取基础名称(去掉最后一个部分,通常是版本号或日期)
136179 const requestedBase = requestedParts . slice ( 0 , - 1 ) . join ( "-" )
137180 const availableBase = availableParts . slice ( 0 , - 1 ) . join ( "-" )
138181
139182 if ( requestedBase === availableBase ) {
140183 consola . info (
141- `🔄 Model base match: '${ requestedModel } ' -> '${ availableId } '` ,
184+ `🔄 基础名称匹配成功: '${ requestedModel } ' -> '${ availableId } '` ,
142185 )
143186 return availableId
144187 }
145188 }
146189 }
147190
148- // Fallback: if high usage and no match found, use first 0x model
191+ // ========== 降级策略:使用率高时降级到第一个 0x 模型 ==========
149192 if ( highUsage && zeroConsumptionModels . length > 0 ) {
150193 consola . warn (
151- `⚠️ No matching 0x model found, falling back to: ${ zeroConsumptionModels [ 0 ] } ` ,
194+ `⚠️ 未找到匹配的 0x 模型,降级到: ${ zeroConsumptionModels [ 0 ] } ` ,
152195 )
153196 return zeroConsumptionModels [ 0 ]
154197 }
155198
156- consola . debug ( `No match found for: ${ requestedModel } ` )
199+ // 所有策略都失败,返回 null
200+ consola . debug ( `未找到匹配模型:${ requestedModel } ` )
157201 return null
158202}
159203
160204/**
161- * Validate and potentially replace the requested model
162- * Returns the validated model ID or throws/returns error info
205+ * 验证并替换请求的模型
206+ *
207+ * 该函数是模型匹配的主要入口点,负责:
208+ * 1. 调用 findMatchingModel 查找匹配的模型
209+ * 2. 验证模型是否可用
210+ * 3. 返回验证结果或错误信息
211+ *
212+ * @param requestedModel - 用户请求的模型标识符
213+ * @returns 包含验证结果的对象
214+ * - success: true 表示验证成功,false 表示失败
215+ * - model: 匹配的模型 ID(成功时)
216+ * - error: 错误详情(失败时)
217+ *
218+ * @example
219+ * // 成功匹配
220+ * validateAndReplaceModel("claude-3-5-sonnet")
221+ * // 返回:{ success: true, model: "claude-3.5-sonnet" }
222+ *
223+ * // 匹配失败
224+ * validateAndReplaceModel("unknown-model")
225+ * // 返回:{ success: false, error: { ... } }
163226 */
164227export function validateAndReplaceModel ( requestedModel : string ) : {
165228 success : boolean
@@ -171,34 +234,40 @@ export function validateAndReplaceModel(requestedModel: string): {
171234 type : string
172235 }
173236} {
237+ // 获取所有有效的可用模型列表
174238 const availableModels = state . models ?. data . filter (
175239 ( m ) => typeof m . capabilities ?. limits ?. max_context_window_tokens === "number" ,
176240 )
177241 const availableModelIds = availableModels ?. map ( ( m ) => m . id ) || [ ]
178242
243+ // 尝试查找匹配的模型
179244 const matchedModel = findMatchingModel ( requestedModel )
180245
246+ // ========== 验证失败:未找到匹配的模型 ==========
181247 if ( ! matchedModel ) {
182- consola . error ( `❌ Model not available: ${ requestedModel } ` )
183- consola . error ( `Available models: ${ availableModelIds . join ( ", " ) } ` )
248+ consola . error ( `❌ 模型不可用: ${ requestedModel } ` )
249+ consola . error ( `可用模型列表: ${ availableModelIds . join ( ", " ) } ` )
184250
185251 return {
186252 success : false ,
187253 error : {
188- message : `The requested model '${ requestedModel } ' is not supported. Available models: ${ availableModelIds . join ( ", " ) } ` ,
254+ message : `请求的模型 '${ requestedModel } ' 不受支持。可用模型: ${ availableModelIds . join ( ", " ) } ` ,
189255 code : "model_not_supported" ,
190256 param : "model" ,
191257 type : "invalid_request_error" ,
192258 } ,
193259 }
194260 }
195261
262+ // ========== 验证成功:记录结果 ==========
196263 if ( matchedModel !== requestedModel ) {
264+ // 模型被替换(通过模糊匹配找到)
197265 consola . success (
198- `✓ Model matched and replaced: ${ requestedModel } -> ${ matchedModel } ` ,
266+ `✓ 模型匹配并替换: ${ requestedModel } -> ${ matchedModel } ` ,
199267 )
200268 } else {
201- consola . success ( `✓ Model validated: ${ matchedModel } ` )
269+ // 精确匹配
270+ consola . success ( `✓ 模型验证通过:${ matchedModel } ` )
202271 }
203272
204273 return {
0 commit comments