File size: 5,016 Bytes
e5d8d3a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
import type { FrontendToken } from '../api/GLTR_API';
/**
* 验证值是否为合法的 real_topk 元组
*/
export const isValidRealTopkTuple = (value: unknown): value is [number, number] => {
return Array.isArray(value)
&& value.length === 2
&& value.every((item) => typeof item === 'number' && Number.isFinite(item));
};
/**
* 验证 token 的概率数据
*/
export const validateTokenProbabilities = (
tokens: Array<{ real_topk?: [number, number] }>
): string | null => {
if (!Array.isArray(tokens) || tokens.length === 0) {
return null;
}
for (let i = 0; i < tokens.length; i++) {
const tuple = tokens[i]?.real_topk;
if (!isValidRealTopkTuple(tuple)) {
return `Token #${i} 缺少合法 real_topk 数据,已取消本次处理。`;
}
}
return null;
};
/**
* 验证值是否为合法的 pred_topk 条目
*/
export const isValidPredTopkEntry = (value: unknown): value is [string, number] => {
return Array.isArray(value)
&& value.length === 2
&& typeof value[0] === 'string'
&& typeof value[1] === 'number'
&& Number.isFinite(value[1]);
};
/**
* 验证 token 的预测数据
* 注意:pred_topk 可以为空数组(例如内存优化策略跳过 TopK 计算时),这是正常情况
*/
export const validateTokenPredictions = (
tokens: Array<{ pred_topk?: [string, number][] }>
): string | null => {
if (!Array.isArray(tokens) || tokens.length === 0) {
return '返回数据缺少 token 序列,已取消本次处理。';
}
for (let i = 0; i < tokens.length; i++) {
const list = tokens[i]?.pred_topk;
// pred_topk 必须存在且为数组类型(允许为空数组)
if (!Array.isArray(list)) {
return `Token #${i} 缺少合法 pred_topk 数组,已取消本次处理。`;
}
// 只有当 pred_topk 不为空时,才验证其内容格式
if (list.length > 0) {
for (let j = 0; j < list.length; j++) {
if (!isValidPredTopkEntry(list[j])) {
return `Token #${i} 的 pred_topk 项 #${j} 格式非法,已取消本次处理。`;
}
}
}
}
return null;
};
/**
* 格式化 token 预览文本(用于错误消息)
*/
export const formatTokenPreview = (text: string): string => {
if (!text) {
return '[空]';
}
const chars = Array.from(text);
if (chars.length <= 12) {
return text;
}
const head = chars.slice(0, 6).join('');
const tail = chars.slice(-3).join('');
return `${head}…${tail}`;
};
/**
* 验证 token 数据的一致性(offset 和 raw 是否匹配)
*/
export const validateTokenConsistency = (
bpeStrings: Array<{ offset?: [number, number]; raw?: string }>,
originalText: string,
options: { allowOverlap?: boolean } = {}
): string | null => {
const { allowOverlap = false } = options;
if (!Array.isArray(bpeStrings) || bpeStrings.length === 0) {
return null;
}
if (typeof originalText !== 'string') {
return '响应缺少原始文本,无法校验 token 数据,已取消本次 demo。';
}
const charArray = Array.from(originalText);
const totalChars = charArray.length;
for (let i = 0; i < bpeStrings.length; i++) {
const token = bpeStrings[i];
if (!token) {
return `Token #${i} 数据缺失,已取消本次 demo。`;
}
const offset = token.offset;
if (!Array.isArray(offset) || offset.length !== 2) {
return `Token #${i} 缺少合法 offset,已取消本次 demo。`;
}
const [start, end] = offset;
if (!Number.isInteger(start) || !Number.isInteger(end)) {
return `Token #${i} 的 offset (${start}, ${end}) 不是整数,已取消本次 demo。`;
}
if (start < 0 || end < start || end > totalChars) {
return `Token #${i} 的 offset (${start}, ${end}) 超出原文范围,已取消本次 demo。`;
}
if (!allowOverlap && i > 0) {
const prevOffset = bpeStrings[i - 1]?.offset;
if (Array.isArray(prevOffset) && prevOffset.length === 2) {
const prevEnd = prevOffset[1];
if (Number.isInteger(prevEnd) && start < prevEnd) {
return `Token #${i} 的 offset (${start}, ${end}) 与 Token #${i - 1} 重叠,已取消本次 demo。`;
}
}
}
const expected = charArray.slice(start, end).join('');
const raw = token.raw ?? '';
if (expected !== raw) {
const previewExpected = formatTokenPreview(expected);
const previewRaw = formatTokenPreview(raw);
return `Token #${i} 数据异常:offset(${start}, ${end}) 对应原文 "${previewExpected}",但 raw 为 "${previewRaw}"。请重新分析或修复数据。`;
}
}
return null;
};
|