mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: 重构和更新 Claude Code 验证器组件
- 重构 src/utils/contents.js,添加完整的 prompt 定义和相似度检测功能 - 删除重复的 src/utils/text-similarity.js 文件 - 更新 claudeCodeValidator.js 中的验证逻辑和错误处理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,81 +0,0 @@
|
|||||||
const MAX_TEXT_LENGTH = 4000
|
|
||||||
|
|
||||||
function normalize(value) {
|
|
||||||
return value.replace(/\s+/g, ' ').trim().toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
function clamp(value) {
|
|
||||||
if (value.length <= MAX_TEXT_LENGTH) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// 截断极长文本,避免耗时的相似度计算
|
|
||||||
return value.slice(0, MAX_TEXT_LENGTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildBigramStats(text) {
|
|
||||||
const stats = new Map()
|
|
||||||
|
|
||||||
for (let index = 0; index < text.length - 1; index += 1) {
|
|
||||||
const gram = text[index] + text[index + 1]
|
|
||||||
stats.set(gram, (stats.get(gram) || 0) + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
stats,
|
|
||||||
total: Math.max(text.length - 1, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function diceCoefficient(left, right) {
|
|
||||||
if (left === right) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (left.length < 2 || right.length < 2) {
|
|
||||||
return left === right && left.length > 0 ? 1 : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const { stats: leftStats, total: leftTotal } = buildBigramStats(left)
|
|
||||||
const { stats: rightStats, total: rightTotal } = buildBigramStats(right)
|
|
||||||
|
|
||||||
let intersection = 0
|
|
||||||
const [smaller, larger] =
|
|
||||||
leftStats.size <= rightStats.size ? [leftStats, rightStats] : [rightStats, leftStats]
|
|
||||||
|
|
||||||
smaller.forEach((count, gram) => {
|
|
||||||
if (larger.has(gram)) {
|
|
||||||
intersection += Math.min(count, larger.get(gram))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (leftTotal + rightTotal === 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return (2 * intersection) / (leftTotal + rightTotal)
|
|
||||||
}
|
|
||||||
|
|
||||||
function simple(actual, expected, threshold = 0.9) {
|
|
||||||
if (typeof expected !== 'string' || !expected.trim()) {
|
|
||||||
throw new Error('期望的提示词必须是非空字符串')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof actual !== 'string' || !actual.trim()) {
|
|
||||||
return { score: 0, threshold, passed: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizedExpected = clamp(normalize(expected))
|
|
||||||
const normalizedActual = clamp(normalize(actual))
|
|
||||||
const score = diceCoefficient(normalizedActual, normalizedExpected)
|
|
||||||
|
|
||||||
return {
|
|
||||||
score,
|
|
||||||
threshold,
|
|
||||||
passed: score >= threshold
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
simple
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
const logger = require('../../utils/logger')
|
const logger = require('../../utils/logger')
|
||||||
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
|
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
|
||||||
const {
|
const { bestSimilarityByTemplates, SYSTEM_PROMPT_THRESHOLD } = require('../../utils/contents')
|
||||||
haikuSystemPrompt,
|
|
||||||
claudeOtherSystemPrompt1,
|
|
||||||
claudeOtherSystemPrompt2,
|
|
||||||
claudeOtherSystemPromptCompact
|
|
||||||
} = require('../../utils/contents')
|
|
||||||
const { simple: similaritySimple } = require('../../utils/text-similarity')
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claude Code CLI 验证器
|
* Claude Code CLI 验证器
|
||||||
@@ -56,35 +50,17 @@ class ClaudeCodeValidator {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.startsWith('claude-3-5-haiku')) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const systemEntries = Array.isArray(body.system) ? body.system : []
|
const systemEntries = Array.isArray(body.system) ? body.system : []
|
||||||
const system0Text =
|
for (const entry of systemEntries) {
|
||||||
systemEntries.length > 0 && typeof systemEntries[0]?.text === 'string'
|
const rawText = typeof entry?.text === 'string' ? entry.text : ''
|
||||||
? systemEntries[0].text
|
const { bestScore } = bestSimilarityByTemplates(rawText)
|
||||||
: null
|
if (bestScore < SYSTEM_PROMPT_THRESHOLD) {
|
||||||
const system1Text =
|
logger.error(
|
||||||
systemEntries.length > 1 && typeof systemEntries[1]?.text === 'string'
|
`Claude system prompt similarity below threshold: score=${bestScore.toFixed(4)}, threshold=${SYSTEM_PROMPT_THRESHOLD}, prompt=${rawText}`
|
||||||
? systemEntries[1].text
|
)
|
||||||
: null
|
return false
|
||||||
|
}
|
||||||
if (!system0Text || !system1Text) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sys0 = similaritySimple(system0Text, claudeOtherSystemPrompt1, 0.9)
|
|
||||||
if (!sys0.passed) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const sys1 = similaritySimple(system1Text, claudeOtherSystemPrompt2, 0.5)
|
|
||||||
const sysCompact = similaritySimple(system1Text, claudeOtherSystemPromptCompact, 0.9)
|
|
||||||
if (!sys1.passed && !sysCompact.passed) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +77,8 @@ class ClaudeCodeValidator {
|
|||||||
// 1. 先检查是否是 Claude Code 的 User-Agent
|
// 1. 先检查是否是 Claude Code 的 User-Agent
|
||||||
// 格式: claude-cli/1.0.86 (external, cli) sdk-cli sdk-py
|
// 格式: claude-cli/1.0.86 (external, cli) sdk-cli sdk-py
|
||||||
|
|
||||||
const claudeCodePattern = /^claude-cli\/[\d.]+(?:[-\w]*)?\s+\(external,\s*(?:cli|sdk-[a-z]+)\)$/i
|
const claudeCodePattern =
|
||||||
|
/^claude-cli\/[\d.]+(?:[-\w]*)?\s+\(external,\s*(?:cli|sdk-[a-z]+)\)$/i
|
||||||
|
|
||||||
if (!claudeCodePattern.test(userAgent)) {
|
if (!claudeCodePattern.test(userAgent)) {
|
||||||
// 不是 Claude Code 的请求,此验证器不处理
|
// 不是 Claude Code 的请求,此验证器不处理
|
||||||
|
|||||||
Reference in New Issue
Block a user