mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat(account): 支持 Pro 账号使用 Opus 4.5+ 模型
Opus 4.5 已对 Claude Pro 用户开放,调整账户模型限制逻辑: - Pro 账号:支持 Opus 4.5+,不支持历史版本 (3.x/4.0/4.1) - Free 账号:不支持任何 Opus 模型 - Max 账号:支持所有 Opus 版本 修改内容: - 新增 isOpus45OrNewer() 函数用于精确识别模型版本 - 更新 claudeAccountService.js 中的账户选择逻辑 - 更新 unifiedClaudeScheduler.js 中的模型支持检查 - 新增测试脚本验证官方模型名称识别 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
166
scripts/test-official-models.js
Normal file
166
scripts/test-official-models.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* 官方模型版本识别测试 - 最终版 v2
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查模型是否为 Opus 4.5 或更新版本
|
||||||
|
* 支持格式:
|
||||||
|
* - 新格式: claude-opus-{major}[-{minor}][-date] 如 claude-opus-4-5-20251101
|
||||||
|
* - 新格式: claude-opus-{major}.{minor} 如 claude-opus-4.5
|
||||||
|
* - 旧格式: claude-{version}-opus[-date] 如 claude-3-opus-20240229
|
||||||
|
*
|
||||||
|
* @param {string} modelName - 模型名称
|
||||||
|
* @returns {boolean} - 是否为 Opus 4.5+
|
||||||
|
*/
|
||||||
|
function isOpus45OrNewer(modelName) {
|
||||||
|
if (!modelName) return false
|
||||||
|
|
||||||
|
const lowerModel = modelName.toLowerCase()
|
||||||
|
if (!lowerModel.includes('opus')) return false
|
||||||
|
|
||||||
|
// 处理 latest 特殊情况
|
||||||
|
if (lowerModel.includes('opus-latest') || lowerModel.includes('opus_latest')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧格式: claude-{version}-opus (版本在 opus 前面)
|
||||||
|
// 例如: claude-3-opus-20240229, claude-3.5-opus
|
||||||
|
const oldFormatMatch = lowerModel.match(/claude[- ](\d+)(?:[\.-](\d+))?[- ]opus/)
|
||||||
|
if (oldFormatMatch) {
|
||||||
|
const majorVersion = parseInt(oldFormatMatch[1], 10)
|
||||||
|
const minorVersion = oldFormatMatch[2] ? parseInt(oldFormatMatch[2], 10) : 0
|
||||||
|
|
||||||
|
// 旧格式的版本号指的是 Claude 大版本
|
||||||
|
if (majorVersion > 4) return true
|
||||||
|
if (majorVersion === 4 && minorVersion >= 5) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新格式 1: opus-{major}.{minor} (点分隔)
|
||||||
|
// 例如: claude-opus-4.5, opus-4.5
|
||||||
|
const dotFormatMatch = lowerModel.match(/opus[- ]?(\d+)\.(\d+)/)
|
||||||
|
if (dotFormatMatch) {
|
||||||
|
const majorVersion = parseInt(dotFormatMatch[1], 10)
|
||||||
|
const minorVersion = parseInt(dotFormatMatch[2], 10)
|
||||||
|
|
||||||
|
if (majorVersion > 4) return true
|
||||||
|
if (majorVersion === 4 && minorVersion >= 5) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新格式 2: opus-{major}[-{minor}][-date] (横线分隔)
|
||||||
|
// 例如: claude-opus-4-5-20251101, claude-opus-4-20250514, claude-opus-4-1-20250805
|
||||||
|
// 关键:小版本号必须是 1 位数字,且后面紧跟 8 位日期或结束
|
||||||
|
// 如果 opus-{major} 后面直接是 8 位日期,则没有小版本号
|
||||||
|
|
||||||
|
// 提取 opus 后面的部分
|
||||||
|
const opusIndex = lowerModel.indexOf('opus')
|
||||||
|
const afterOpus = lowerModel.substring(opusIndex + 4) // 'opus' 后面的内容
|
||||||
|
|
||||||
|
// 尝试匹配: -{major}-{minor}-{date} 或 -{major}-{date} 或 -{major}
|
||||||
|
// 小版本号只能是 1 位数字 (如 1, 5),不会是 2 位以上
|
||||||
|
const versionMatch = afterOpus.match(/^[- ](\d+)(?:[- ](\d)(?=[- ]\d{8}|$))?/)
|
||||||
|
|
||||||
|
if (versionMatch) {
|
||||||
|
const majorVersion = parseInt(versionMatch[1], 10)
|
||||||
|
const minorVersion = versionMatch[2] ? parseInt(versionMatch[2], 10) : 0
|
||||||
|
|
||||||
|
if (majorVersion > 4) return true
|
||||||
|
if (majorVersion === 4 && minorVersion >= 5) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他包含 opus 但无法解析版本的情况,默认认为是旧版本
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 官方模型
|
||||||
|
const officialModels = [
|
||||||
|
{ name: 'claude-3-opus-20240229', desc: 'Opus 3 (已弃用)', expectPro: false },
|
||||||
|
{ name: 'claude-opus-4-20250514', desc: 'Opus 4.0', expectPro: false },
|
||||||
|
{ name: 'claude-opus-4-1-20250805', desc: 'Opus 4.1', expectPro: false },
|
||||||
|
{ name: 'claude-opus-4-5-20251101', desc: 'Opus 4.5', expectPro: true },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 非 Opus 模型
|
||||||
|
const nonOpusModels = [
|
||||||
|
{ name: 'claude-sonnet-4-20250514', desc: 'Sonnet 4' },
|
||||||
|
{ name: 'claude-sonnet-4-5-20250929', desc: 'Sonnet 4.5' },
|
||||||
|
{ name: 'claude-haiku-4-5-20251001', desc: 'Haiku 4.5' },
|
||||||
|
{ name: 'claude-3-5-haiku-20241022', desc: 'Haiku 3.5' },
|
||||||
|
{ name: 'claude-3-haiku-20240307', desc: 'Haiku 3' },
|
||||||
|
{ name: 'claude-3-7-sonnet-20250219', desc: 'Sonnet 3.7 (已弃用)' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 其他格式测试
|
||||||
|
const otherFormats = [
|
||||||
|
{ name: 'claude-opus-4.5', expected: true, desc: 'Opus 4.5 点分隔' },
|
||||||
|
{ name: 'claude-opus-4-5', expected: true, desc: 'Opus 4.5 横线分隔' },
|
||||||
|
{ name: 'opus-4.5', expected: true, desc: 'Opus 4.5 无前缀' },
|
||||||
|
{ name: 'opus-4-5', expected: true, desc: 'Opus 4-5 无前缀' },
|
||||||
|
{ name: 'opus-latest', expected: true, desc: 'Opus latest' },
|
||||||
|
{ name: 'claude-opus-5', expected: true, desc: 'Opus 5 (未来)' },
|
||||||
|
{ name: 'claude-opus-5-0', expected: true, desc: 'Opus 5.0 (未来)' },
|
||||||
|
{ name: 'opus-4.0', expected: false, desc: 'Opus 4.0' },
|
||||||
|
{ name: 'opus-4.1', expected: false, desc: 'Opus 4.1' },
|
||||||
|
{ name: 'opus-4.4', expected: false, desc: 'Opus 4.4' },
|
||||||
|
{ name: 'opus-4', expected: false, desc: 'Opus 4' },
|
||||||
|
{ name: 'opus-4-0', expected: false, desc: 'Opus 4-0' },
|
||||||
|
{ name: 'opus-4-1', expected: false, desc: 'Opus 4-1' },
|
||||||
|
{ name: 'opus-4-4', expected: false, desc: 'Opus 4-4' },
|
||||||
|
{ name: 'opus', expected: false, desc: '仅 opus' },
|
||||||
|
{ name: null, expected: false, desc: 'null' },
|
||||||
|
{ name: '', expected: false, desc: '空字符串' },
|
||||||
|
]
|
||||||
|
|
||||||
|
console.log('='.repeat(90))
|
||||||
|
console.log('官方模型版本识别测试 - 最终版 v2')
|
||||||
|
console.log('='.repeat(90))
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
let passed = 0
|
||||||
|
let failed = 0
|
||||||
|
|
||||||
|
// 测试官方 Opus 模型
|
||||||
|
console.log('📌 官方 Opus 模型:')
|
||||||
|
for (const m of officialModels) {
|
||||||
|
const result = isOpus45OrNewer(m.name)
|
||||||
|
const status = result === m.expectPro ? '✅ PASS' : '❌ FAIL'
|
||||||
|
if (result === m.expectPro) passed++
|
||||||
|
else failed++
|
||||||
|
const proSupport = result ? 'Pro 可用 ✅' : 'Pro 不可用 ❌'
|
||||||
|
console.log(` ${status} | ${m.name.padEnd(32)} | ${m.desc.padEnd(18)} | ${proSupport}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log()
|
||||||
|
console.log('📌 非 Opus 模型 (不受此函数影响):')
|
||||||
|
for (const m of nonOpusModels) {
|
||||||
|
const result = isOpus45OrNewer(m.name)
|
||||||
|
console.log(` ➖ | ${m.name.padEnd(32)} | ${m.desc.padEnd(18)} | ${result ? '⚠️ 异常' : '正确跳过'}`)
|
||||||
|
if (result) failed++ // 非 Opus 模型不应返回 true
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log()
|
||||||
|
console.log('📌 其他格式测试:')
|
||||||
|
for (const m of otherFormats) {
|
||||||
|
const result = isOpus45OrNewer(m.name)
|
||||||
|
const status = result === m.expected ? '✅ PASS' : '❌ FAIL'
|
||||||
|
if (result === m.expected) passed++
|
||||||
|
else failed++
|
||||||
|
const display = m.name === null ? 'null' : m.name === '' ? '""' : m.name
|
||||||
|
console.log(` ${status} | ${display.padEnd(25)} | ${m.desc.padEnd(18)} | ${result ? 'Pro 可用' : 'Pro 不可用'}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log()
|
||||||
|
console.log('='.repeat(90))
|
||||||
|
console.log('测试结果:', passed, '通过,', failed, '失败')
|
||||||
|
console.log('='.repeat(90))
|
||||||
|
|
||||||
|
if (failed > 0) {
|
||||||
|
console.log('\n❌ 有测试失败,请检查函数逻辑')
|
||||||
|
process.exit(1)
|
||||||
|
} else {
|
||||||
|
console.log('\n✅ 所有测试通过!函数可以安全使用')
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ const {
|
|||||||
const tokenRefreshService = require('./tokenRefreshService')
|
const tokenRefreshService = require('./tokenRefreshService')
|
||||||
const LRUCache = require('../utils/lruCache')
|
const LRUCache = require('../utils/lruCache')
|
||||||
const { formatDateWithTimezone, getISOStringWithTimezone } = require('../utils/dateHelper')
|
const { formatDateWithTimezone, getISOStringWithTimezone } = require('../utils/dateHelper')
|
||||||
|
const { isOpus45OrNewer } = require('../utils/modelHelper')
|
||||||
|
|
||||||
class ClaudeAccountService {
|
class ClaudeAccountService {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -852,22 +853,32 @@ class ClaudeAccountService {
|
|||||||
!this.isSubscriptionExpired(account)
|
!this.isSubscriptionExpired(account)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 如果请求的是 Opus 模型,过滤掉 Pro 和 Free 账号
|
// 如果请求的是 Opus 模型,根据账号类型和模型版本过滤
|
||||||
if (modelName && modelName.toLowerCase().includes('opus')) {
|
if (modelName && modelName.toLowerCase().includes('opus')) {
|
||||||
|
const isNewOpus = isOpus45OrNewer(modelName)
|
||||||
|
|
||||||
activeAccounts = activeAccounts.filter((account) => {
|
activeAccounts = activeAccounts.filter((account) => {
|
||||||
// 检查账号的订阅信息
|
|
||||||
if (account.subscriptionInfo) {
|
if (account.subscriptionInfo) {
|
||||||
try {
|
try {
|
||||||
const info = JSON.parse(account.subscriptionInfo)
|
const info = JSON.parse(account.subscriptionInfo)
|
||||||
// Pro 和 Free 账号不支持 Opus
|
|
||||||
|
// Free 账号不支持任何 Opus 模型
|
||||||
|
if (info.accountType === 'claude_free' || info.accountType === 'free') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pro 账号:仅支持 Opus 4.5+
|
||||||
if (info.hasClaudePro === true && info.hasClaudeMax !== true) {
|
if (info.hasClaudePro === true && info.hasClaudeMax !== true) {
|
||||||
return false // Claude Pro 不支持 Opus
|
return isNewOpus // 仅新版 Opus 支持
|
||||||
}
|
}
|
||||||
if (info.accountType === 'claude_pro' || info.accountType === 'claude_free') {
|
if (info.accountType === 'claude_pro') {
|
||||||
return false // 明确标记为 Pro 或 Free 的账号不支持
|
return isNewOpus // 仅新版 Opus 支持
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Max 账号支持所有 Opus 版本
|
||||||
|
return true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 解析失败,假设为旧数据,默认支持(兼容旧数据为 Max)
|
// 解析失败,假设为旧数据(Max),默认支持
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -876,7 +887,8 @@ class ClaudeAccountService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (activeAccounts.length === 0) {
|
if (activeAccounts.length === 0) {
|
||||||
throw new Error('No Claude accounts available that support Opus model')
|
const modelDesc = isNewOpus ? 'Opus 4.5+' : 'legacy Opus (requires Max subscription)'
|
||||||
|
throw new Error(`No Claude accounts available that support ${modelDesc} model`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,22 +982,32 @@ class ClaudeAccountService {
|
|||||||
!this.isSubscriptionExpired(account)
|
!this.isSubscriptionExpired(account)
|
||||||
)
|
)
|
||||||
|
|
||||||
// 如果请求的是 Opus 模型,过滤掉 Pro 和 Free 账号
|
// 如果请求的是 Opus 模型,根据账号类型和模型版本过滤
|
||||||
if (modelName && modelName.toLowerCase().includes('opus')) {
|
if (modelName && modelName.toLowerCase().includes('opus')) {
|
||||||
|
const isNewOpus = isOpus45OrNewer(modelName)
|
||||||
|
|
||||||
sharedAccounts = sharedAccounts.filter((account) => {
|
sharedAccounts = sharedAccounts.filter((account) => {
|
||||||
// 检查账号的订阅信息
|
|
||||||
if (account.subscriptionInfo) {
|
if (account.subscriptionInfo) {
|
||||||
try {
|
try {
|
||||||
const info = JSON.parse(account.subscriptionInfo)
|
const info = JSON.parse(account.subscriptionInfo)
|
||||||
// Pro 和 Free 账号不支持 Opus
|
|
||||||
|
// Free 账号不支持任何 Opus 模型
|
||||||
|
if (info.accountType === 'claude_free' || info.accountType === 'free') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pro 账号:仅支持 Opus 4.5+
|
||||||
if (info.hasClaudePro === true && info.hasClaudeMax !== true) {
|
if (info.hasClaudePro === true && info.hasClaudeMax !== true) {
|
||||||
return false // Claude Pro 不支持 Opus
|
return isNewOpus // 仅新版 Opus 支持
|
||||||
}
|
}
|
||||||
if (info.accountType === 'claude_pro' || info.accountType === 'claude_free') {
|
if (info.accountType === 'claude_pro') {
|
||||||
return false // 明确标记为 Pro 或 Free 的账号不支持
|
return isNewOpus // 仅新版 Opus 支持
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Max 账号支持所有 Opus 版本
|
||||||
|
return true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 解析失败,假设为旧数据,默认支持(兼容旧数据为 Max)
|
// 解析失败,假设为旧数据(Max),默认支持
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -994,7 +1016,8 @@ class ClaudeAccountService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (sharedAccounts.length === 0) {
|
if (sharedAccounts.length === 0) {
|
||||||
throw new Error('No shared Claude accounts available that support Opus model')
|
const modelDesc = isNewOpus ? 'Opus 4.5+' : 'legacy Opus (requires Max subscription)'
|
||||||
|
throw new Error(`No shared Claude accounts available that support ${modelDesc} model`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const ccrAccountService = require('./ccrAccountService')
|
|||||||
const accountGroupService = require('./accountGroupService')
|
const accountGroupService = require('./accountGroupService')
|
||||||
const redis = require('../models/redis')
|
const redis = require('../models/redis')
|
||||||
const logger = require('../utils/logger')
|
const logger = require('../utils/logger')
|
||||||
const { parseVendorPrefixedModel } = require('../utils/modelHelper')
|
const { parseVendorPrefixedModel, isOpus45OrNewer } = require('../utils/modelHelper')
|
||||||
|
|
||||||
class UnifiedClaudeScheduler {
|
class UnifiedClaudeScheduler {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -48,6 +48,8 @@ class UnifiedClaudeScheduler {
|
|||||||
|
|
||||||
// 2. Opus 模型的订阅级别检查
|
// 2. Opus 模型的订阅级别检查
|
||||||
if (requestedModel.toLowerCase().includes('opus')) {
|
if (requestedModel.toLowerCase().includes('opus')) {
|
||||||
|
const isNewOpus = isOpus45OrNewer(requestedModel)
|
||||||
|
|
||||||
if (account.subscriptionInfo) {
|
if (account.subscriptionInfo) {
|
||||||
try {
|
try {
|
||||||
const info =
|
const info =
|
||||||
@@ -55,21 +57,39 @@ class UnifiedClaudeScheduler {
|
|||||||
? JSON.parse(account.subscriptionInfo)
|
? JSON.parse(account.subscriptionInfo)
|
||||||
: account.subscriptionInfo
|
: account.subscriptionInfo
|
||||||
|
|
||||||
// Pro 和 Free 账号不支持 Opus
|
// Free 账号不支持任何 Opus 模型
|
||||||
|
if (info.accountType === 'claude_free' || info.accountType === 'free') {
|
||||||
|
logger.info(
|
||||||
|
`🚫 Claude account ${account.name} (Free) does not support Opus model${context ? ` ${context}` : ''}`
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pro 账号:仅支持 Opus 4.5+
|
||||||
if (info.hasClaudePro === true && info.hasClaudeMax !== true) {
|
if (info.hasClaudePro === true && info.hasClaudeMax !== true) {
|
||||||
|
if (!isNewOpus) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`🚫 Claude account ${account.name} (Pro) does not support Opus model${context ? ` ${context}` : ''}`
|
`🚫 Claude account ${account.name} (Pro) does not support legacy Opus model${context ? ` ${context}` : ''}`
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (info.accountType === 'claude_pro' || info.accountType === 'claude_free') {
|
// Opus 4.5+ 支持
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (info.accountType === 'claude_pro') {
|
||||||
|
if (!isNewOpus) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`🚫 Claude account ${account.name} (${info.accountType}) does not support Opus model${context ? ` ${context}` : ''}`
|
`🚫 Claude account ${account.name} (Pro) does not support legacy Opus model${context ? ` ${context}` : ''}`
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Opus 4.5+ 支持
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max 账号支持所有 Opus 版本
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 解析失败,假设为旧数据,默认支持(兼容旧数据为 Max)
|
// 解析失败,假设为旧数据(Max),默认支持
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Account ${account.name} has invalid subscriptionInfo${context ? ` ${context}` : ''}, assuming Max`
|
`Account ${account.name} has invalid subscriptionInfo${context ? ` ${context}` : ''}, assuming Max`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -70,9 +70,82 @@ function getVendorType(modelStr) {
|
|||||||
return vendor
|
return vendor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查模型是否为 Opus 4.5 或更新版本
|
||||||
|
* 支持格式:
|
||||||
|
* - 新格式: claude-opus-{major}[-{minor}][-date] 如 claude-opus-4-5-20251101
|
||||||
|
* - 新格式: claude-opus-{major}.{minor} 如 claude-opus-4.5
|
||||||
|
* - 旧格式: claude-{version}-opus[-date] 如 claude-3-opus-20240229
|
||||||
|
*
|
||||||
|
* @param {string} modelName - 模型名称
|
||||||
|
* @returns {boolean} - 是否为 Opus 4.5+
|
||||||
|
*/
|
||||||
|
function isOpus45OrNewer(modelName) {
|
||||||
|
if (!modelName) return false
|
||||||
|
|
||||||
|
const lowerModel = modelName.toLowerCase()
|
||||||
|
if (!lowerModel.includes('opus')) return false
|
||||||
|
|
||||||
|
// 处理 latest 特殊情况
|
||||||
|
if (lowerModel.includes('opus-latest') || lowerModel.includes('opus_latest')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 旧格式: claude-{version}-opus (版本在 opus 前面)
|
||||||
|
// 例如: claude-3-opus-20240229, claude-3.5-opus
|
||||||
|
const oldFormatMatch = lowerModel.match(/claude[- ](\d+)(?:[\.-](\d+))?[- ]opus/)
|
||||||
|
if (oldFormatMatch) {
|
||||||
|
const majorVersion = parseInt(oldFormatMatch[1], 10)
|
||||||
|
const minorVersion = oldFormatMatch[2] ? parseInt(oldFormatMatch[2], 10) : 0
|
||||||
|
|
||||||
|
// 旧格式的版本号指的是 Claude 大版本
|
||||||
|
if (majorVersion > 4) return true
|
||||||
|
if (majorVersion === 4 && minorVersion >= 5) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新格式 1: opus-{major}.{minor} (点分隔)
|
||||||
|
// 例如: claude-opus-4.5, opus-4.5
|
||||||
|
const dotFormatMatch = lowerModel.match(/opus[- ]?(\d+)\.(\d+)/)
|
||||||
|
if (dotFormatMatch) {
|
||||||
|
const majorVersion = parseInt(dotFormatMatch[1], 10)
|
||||||
|
const minorVersion = parseInt(dotFormatMatch[2], 10)
|
||||||
|
|
||||||
|
if (majorVersion > 4) return true
|
||||||
|
if (majorVersion === 4 && minorVersion >= 5) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新格式 2: opus-{major}[-{minor}][-date] (横线分隔)
|
||||||
|
// 例如: claude-opus-4-5-20251101, claude-opus-4-20250514, claude-opus-4-1-20250805
|
||||||
|
// 关键:小版本号必须是 1 位数字,且后面紧跟 8 位日期或结束
|
||||||
|
// 如果 opus-{major} 后面直接是 8 位日期,则没有小版本号
|
||||||
|
|
||||||
|
// 提取 opus 后面的部分
|
||||||
|
const opusIndex = lowerModel.indexOf('opus')
|
||||||
|
const afterOpus = lowerModel.substring(opusIndex + 4) // 'opus' 后面的内容
|
||||||
|
|
||||||
|
// 尝试匹配: -{major}-{minor}-{date} 或 -{major}-{date} 或 -{major}
|
||||||
|
// 小版本号只能是 1 位数字 (如 1, 5),不会是 2 位以上
|
||||||
|
const versionMatch = afterOpus.match(/^[- ](\d+)(?:[- ](\d)(?=[- ]\d{8}|$))?/)
|
||||||
|
|
||||||
|
if (versionMatch) {
|
||||||
|
const majorVersion = parseInt(versionMatch[1], 10)
|
||||||
|
const minorVersion = versionMatch[2] ? parseInt(versionMatch[2], 10) : 0
|
||||||
|
|
||||||
|
if (majorVersion > 4) return true
|
||||||
|
if (majorVersion === 4 && minorVersion >= 5) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他包含 opus 但无法解析版本的情况,默认认为是旧版本
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
parseVendorPrefixedModel,
|
parseVendorPrefixedModel,
|
||||||
hasVendorPrefix,
|
hasVendorPrefix,
|
||||||
getEffectiveModel,
|
getEffectiveModel,
|
||||||
getVendorType
|
getVendorType,
|
||||||
|
isOpus45OrNewer
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user