🌐 chore: translate non-English comments to English in model-runtime utils

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
claude[bot]
2025-12-18 02:39:50 +00:00
parent 5ed88d7947
commit 730c0db6d0
2 changed files with 83 additions and 83 deletions

View File

@@ -4,7 +4,7 @@ import { AIBaseModelCard } from 'model-bank';
import type { ModelProviderKey } from '../types';
export interface ModelProcessorConfig {
excludeKeywords?: readonly string[]; // 对符合的模型不添加标签
excludeKeywords?: readonly string[]; // Do not add tags to models matching these keywords
functionCallKeywords?: readonly string[];
imageOutputKeywords?: readonly string[];
reasoningKeywords?: readonly string[];
@@ -13,10 +13,10 @@ export interface ModelProcessorConfig {
visionKeywords?: readonly string[];
}
// 默认关键字:任意包含 -search 的模型 ID 视为支持联网搜索
// Default keywords: any model ID containing -search is considered to support internet search
const DEFAULT_SEARCH_KEYWORDS = ['-search'] as const;
// 模型能力标签关键词配置
// Model capability tag keyword configuration
export const MODEL_LIST_CONFIGS = {
anthropic: {
functionCallKeywords: ['claude'],
@@ -131,7 +131,7 @@ export const MODEL_LIST_CONFIGS = {
},
} as const;
// 模型所有者 (提供商) 关键词配置
// Model owner (provider) keyword configuration
export const MODEL_OWNER_DETECTION_CONFIG = {
anthropic: ['claude'],
comfyui: ['comfyui/'], // ComfyUI models detection - all ComfyUI models have comfyui/ prefix
@@ -167,31 +167,31 @@ export const IMAGE_MODEL_KEYWORDS = [
'wanxiang',
'DESCRIBE',
'UPSCALE',
'!gemini', // 排除 gemini 模型,即使包含 -image 也是 chat 模型
'!gemini', // Exclude gemini models, they are chat models even if they contain -image
'-image',
'^V3',
'^V_2',
'^V_1',
] as const;
// 嵌入模型关键词配置
// Embedding model keyword configuration
export const EMBEDDING_MODEL_KEYWORDS = ['embedding', 'embed', 'bge', 'm3e'] as const;
/**
* 检测关键词列表是否匹配模型ID支持多种匹配模式
* @param modelId 模型ID小写
* @param keywords 关键词列表,支持以下前缀:
* - ^ 开头只在模型ID开头匹配
* - ! 开头:排除匹配,优先级最高
* - 无前缀:包含匹配(默认行为)
* @returns 是否匹配(排除逻辑优先)
* Detect if keyword list matches model ID (supports multiple matching patterns)
* @param modelId Model ID (lowercase)
* @param keywords Keyword list, supports the following prefixes:
* - ^ prefix: match only at the beginning of model ID
* - ! prefix: exclude match, highest priority
* - no prefix: contains match (default behavior)
* @returns Whether it matches (exclusion logic takes priority)
*/
const isKeywordListMatch = (modelId: string, keywords: readonly string[]): boolean => {
// 先检查排除规则(感叹号开头)
// First check exclusion rules (exclamation mark prefix)
const excludeKeywords = keywords.filter((keyword) => keyword.startsWith('!'));
const includeKeywords = keywords.filter((keyword) => !keyword.startsWith('!'));
// 如果匹配任何排除规则,直接返回 false
// If matches any exclusion rule, return false immediately
for (const excludeKeyword of excludeKeywords) {
const keywordWithoutPrefix = excludeKeyword.slice(1);
const isMatch = keywordWithoutPrefix.startsWith('^')
@@ -203,23 +203,23 @@ const isKeywordListMatch = (modelId: string, keywords: readonly string[]): boole
}
}
// 检查包含规则
// Check inclusion rules
return includeKeywords.some((keyword) => {
if (keyword.startsWith('^')) {
// ^ 开头则只在开头匹配
// ^ prefix means match only at the beginning
const keywordWithoutPrefix = keyword.slice(1);
return modelId.startsWith(keywordWithoutPrefix);
}
// 默认行为:包含匹配
// Default behavior: contains match
return modelId.includes(keyword);
});
};
/**
* 根据提供商类型查找对应的本地模型配置
* @param modelId 模型ID
* @param provider 提供商类型
* @returns 匹配的本地模型配置
* Find corresponding local model configuration based on provider type
* @param modelId Model ID
* @param provider Provider type
* @returns Matched local model configuration
*/
const findKnownModelByProvider = async (
modelId: string,
@@ -228,32 +228,32 @@ const findKnownModelByProvider = async (
const lowerModelId = modelId.toLowerCase();
try {
// 尝试动态导入对应的配置文件
// Try to dynamically import the corresponding configuration file
const modules = await import('model-bank');
// 如果提供商配置文件不存在,跳过
// If provider configuration file doesn't exist, skip
if (!(provider in modules)) {
return null;
}
const providerModels = modules[provider as keyof typeof modules] as AIBaseModelCard[];
// 如果导入成功且有数据,进行查找
// If import succeeds and has data, perform lookup
if (Array.isArray(providerModels)) {
return providerModels.find((m) => m.id.toLowerCase() === lowerModelId);
}
return null;
} catch {
// 如果导入失败(文件不存在或其他错误),返回 null
// If import fails (file doesn't exist or other errors), return null
return null;
}
};
/**
* 检测单个模型的提供商类型
* @param modelId 模型ID
* @returns 检测到的提供商配置键名,默认为 'openai'
* Detect provider type for a single model
* @param modelId Model ID
* @returns Detected provider configuration key name, defaults to 'openai'
*/
export const detectModelProvider = (modelId: string): keyof typeof MODEL_LIST_CONFIGS => {
const lowerModelId = modelId.toLowerCase();
@@ -270,20 +270,20 @@ export const detectModelProvider = (modelId: string): keyof typeof MODEL_LIST_CO
};
/**
* 将时间戳转换为日期字符串
* @param timestamp 时间戳(秒)
* @returns 格式化的日期字符串 (YYYY-MM-DD)
* Convert timestamp to date string
* @param timestamp Timestamp (seconds)
* @returns Formatted date string (YYYY-MM-DD)
*/
const formatTimestampToDate = (timestamp: number): string | undefined => {
if (timestamp === null || timestamp === undefined || Number.isNaN(timestamp)) return undefined;
// 支持秒级或毫秒级时间戳:
// - 如果是毫秒级(>= 1e12直接当作毫秒
// - 否则视为秒,需要 *1000 转为毫秒
// Support both second-level and millisecond-level timestamps:
// - If millisecond-level (>= 1e12), use as milliseconds directly;
// - Otherwise treat as seconds, need to *1000 to convert to milliseconds
const msTimestamp = timestamp > 1e12 ? timestamp : timestamp * 1000;
const date = new Date(msTimestamp);
// 验证解析结果和年份范围(只接受 4 位年份,避免超出 varchar(10) YYYY-MM-DD
// Validate parsing result and year range (only accept 4-digit years to avoid exceeding varchar(10) for YYYY-MM-DD)
const year = date.getUTCFullYear();
if (year < 1000 || year > 9999) return undefined;
@@ -292,22 +292,22 @@ const formatTimestampToDate = (timestamp: number): string | undefined => {
};
/**
* 处理 releasedAt 字段
* @param model 模型对象
* @param knownModel 已知模型配置
* @returns 处理后的 releasedAt
* Process releasedAt field
* @param model Model object
* @param knownModel Known model configuration
* @returns Processed releasedAt value
*/
const processReleasedAt = (model: any, knownModel?: any): string | undefined => {
// 优先检查 model.created
// Check model.created first
if (model.created !== undefined && model.created !== null) {
// 检查是否为时间戳格式
// Check if it's a timestamp format
if (typeof model.created === 'number' && model.created > 1_630_000_000) {
// AiHubMix 错误时间戳为 1626777600
// AiHubMix incorrect timestamp is 1626777600
return formatTimestampToDate(model.created);
}
// 如果 created 是字符串且已经是日期格式,直接返回
// If created is a string and already in date format, return directly
if (typeof model.created === 'string') {
// Anthropic:若为 '2025-02-19T00:00:00Z' 只取日期部分
// Anthropic: if it's '2025-02-19T00:00:00Z', only extract the date part
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/.test(model.created)) {
return model.created.split('T')[0];
}
@@ -315,17 +315,17 @@ const processReleasedAt = (model: any, knownModel?: any): string | undefined =>
}
}
// 回退到原有逻辑
// Fall back to original logic
return model.releasedAt ?? knownModel?.releasedAt ?? undefined;
};
/**
* 处理模型显示名称
* @param displayName 原始显示名称
* @returns 处理后的显示名称
* Process model display name
* @param displayName Original display name
* @returns Processed display name
*/
const processDisplayName = (displayName: string): string => {
// 如果包含 "Gemini 2.5 Flash Image Preview",替换对应部分为 "Nano Banana"
// If it contains "Gemini 2.5 Flash Image Preview", replace that part with "Nano Banana"
if (displayName.includes('Gemini 2.5 Flash Image Preview')) {
return displayName.replace('Gemini 2.5 Flash Image Preview', 'Nano Banana');
}
@@ -334,9 +334,9 @@ const processDisplayName = (displayName: string): string => {
};
/**
* 获取模型提供商的本地配置
* @param provider 模型提供商
* @returns 模型提供商的本地配置
* Get model provider's local configuration
* @param provider Model provider
* @returns Model provider's local configuration
*/
const getProviderLocalConfig = async (provider?: ModelProviderKey): Promise<any[] | null> => {
let providerLocalConfig: any[] | null = null;
@@ -346,7 +346,7 @@ const getProviderLocalConfig = async (provider?: ModelProviderKey): Promise<any[
providerLocalConfig = modules[provider];
} catch {
// 如果配置文件不存在或导入失败,保持为 null
// If configuration file doesn't exist or import fails, keep as null
providerLocalConfig = null;
}
}
@@ -354,16 +354,16 @@ const getProviderLocalConfig = async (provider?: ModelProviderKey): Promise<any[
};
/**
* 获取模型本地配置
* @param providerLocalConfig 模型提供商的本地配置
* @param model 模型对象
* @returns 模型本地配置
* Get model local configuration
* @param providerLocalConfig Model provider's local configuration
* @param model Model object
* @returns Model local configuration
*/
const getModelLocalEnableConfig = (
providerLocalConfig: any[],
model: { id: string },
): any | null => {
// 如果提供了 providerid 且有本地配置,尝试从中获取模型的 enabled 状态
// If providerid is provided and has local configuration, try to get the model's enabled status from it
let providerLocalModelConfig = null;
if (providerLocalConfig && Array.isArray(providerLocalConfig)) {
providerLocalModelConfig = providerLocalConfig.find((m) => m.id === model.id);
@@ -372,7 +372,7 @@ const getModelLocalEnableConfig = (
};
/**
* 处理模型卡片的通用逻辑
* Common logic for processing model cards
*/
const processModelCard = (
model: { [key: string]: any; id: string },
@@ -511,11 +511,11 @@ const processModelCard = (
};
/**
* 处理单一提供商的模型列表
* @param modelList 模型列表
* @param config 提供商配置
* @param provider 提供商类型(可选,用于优先匹配对应的本地配置, 当提供了 provider 时,才会尝试从本地配置覆盖 enabled
* @returns 处理后的模型卡片列表
* Process model list for a single provider
* @param modelList Model list
* @param config Provider configuration
* @param provider Provider type (optional, used to prioritize matching corresponding local configuration, will only attempt to override enabled from local configuration when provider is provided)
* @returns Processed model card list
*/
export const processModelList = async (
modelList: Array<{ id: string }>,
@@ -524,19 +524,19 @@ export const processModelList = async (
): Promise<ChatModelCard[]> => {
const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
// 如果提供了 provider,尝试获取该提供商的本地配置
// If provider is provided, try to get the provider's local configuration
const providerLocalConfig = await getProviderLocalConfig(provider as ModelProviderKey);
return Promise.all(
modelList.map(async (model) => {
let knownModel: any = null;
// 如果提供了provider优先使用提供商特定的配置
// If provider is provided, prioritize using provider-specific configuration
if (provider) {
knownModel = await findKnownModelByProvider(model.id, provider);
}
// 如果未找到,回退到全局配置
// If not found, fall back to global configuration
if (!knownModel) {
knownModel = LOBE_DEFAULT_MODEL_LIST.find(
(m) => model.id.toLowerCase() === m.id.toLowerCase(),
@@ -545,13 +545,13 @@ export const processModelList = async (
const processedModel = processModelCard(model, config, knownModel);
// 如果提供了 provider 且有本地配置,尝试从中获取模型的 enabled 状态
// If provider is provided and has local configuration, try to get the model's enabled status from it
const providerLocalModelConfig = getModelLocalEnableConfig(
providerLocalConfig as any[],
model,
);
// 如果找到了本地配置中的模型,使用其 enabled 状态
// If model is found in local configuration, use its enabled status
if (
processedModel &&
providerLocalModelConfig &&
@@ -566,10 +566,10 @@ export const processModelList = async (
};
/**
* 处理混合提供商的模型列表
* @param modelList 模型列表
* @param providerid 可选的提供商ID用于获取其本地配置文件
* @returns 处理后的模型卡片列表
* Process model list for multiple providers
* @param modelList Model list
* @param providerid Optional provider ID, used to get its local configuration file
* @returns Processed model card list
*/
export const processMultiProviderModelList = async (
modelList: Array<{ id: string }>,
@@ -577,7 +577,7 @@ export const processMultiProviderModelList = async (
): Promise<ChatModelCard[]> => {
const { LOBE_DEFAULT_MODEL_LIST } = await import('model-bank');
// 如果提供了 providerid,尝试获取该提供商的本地配置
// If providerid is provided, try to get the provider's local configuration
const providerLocalConfig = await getProviderLocalConfig(providerid);
return Promise.all(
@@ -585,17 +585,17 @@ export const processMultiProviderModelList = async (
const detectedProvider = detectModelProvider(model.id);
const config = MODEL_LIST_CONFIGS[detectedProvider];
// 优先使用提供商特定的配置
// Prioritize using provider-specific configuration
let knownModel = await findKnownModelByProvider(model.id, detectedProvider);
// 如果未找到,回退到全局配置
// If not found, fall back to global configuration
if (!knownModel) {
knownModel = LOBE_DEFAULT_MODEL_LIST.find(
(m) => model.id.toLowerCase() === m.id.toLowerCase(),
);
}
// 如果提供了 providerid 且有本地配置,尝试从中获取模型的 enabled 状态
// If providerid is provided and has local configuration, try to get the model's enabled status from it
const providerLocalModelConfig = getModelLocalEnableConfig(
providerLocalConfig as any[],
model,
@@ -603,7 +603,7 @@ export const processMultiProviderModelList = async (
const processedModel = processModelCard(model, config, knownModel);
// 如果找到了本地配置中的模型,使用其 enabled 状态
// If model is found in local configuration, use its enabled status
if (
processedModel &&
providerLocalModelConfig &&

View File

@@ -5,20 +5,20 @@ interface UriParserResult {
}
export const parseDataUri = (dataUri: string): UriParserResult => {
// 正则表达式匹配整个 Data URI 结构
// Regular expression to match the entire Data URI structure
const dataUriMatch = dataUri.match(/^data:([^;]+);base64,(.+)$/);
if (dataUriMatch) {
// 如果是合法的 Data URI
// If it's a valid Data URI
return { base64: dataUriMatch[2], mimeType: dataUriMatch[1], type: 'base64' };
}
try {
new URL(dataUri);
// 如果是合法的 URL
// If it's a valid URL
return { base64: null, mimeType: null, type: 'url' };
} catch {
// 既不是 Data URI 也不是合法 URL
// Neither a Data URI nor a valid URL
return { base64: null, mimeType: null, type: null };
}
};