UNPKG

@lobehub/chat

Version:

Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.

325 lines (288 loc) 7.47 kB
import { DeploymentOption } from '@lobehub/market-types'; import { DiscoverMcpDetail } from '@/types/discover'; export interface ScoreItem { check: boolean; required?: boolean; weight?: number; // 权重,可选 } export interface ScoreResult { grade: 'a' | 'b' | 'f'; maxRequiredScore: number; maxScore: number; percentage: number; requiredPercentage: number; requiredScore: number; totalScore: number; } // 扩展的评分项目接口,用于显示 export interface ScoreListItem extends ScoreItem { desc: string; key: string; title: string; } // 原始数据接口 export interface ScoreDataInput { deploymentOptions?: Array<{ installationMethod?: string; }>; github?: { license?: string; }; installationMethods?: string; // 列表页使用 isClaimed?: boolean; isValidated?: boolean; overview?: { readme?: string; }; promptsCount?: number; resourcesCount?: number; toolsCount?: number; } // 计算后的布尔值结果 export interface ScoreFlags { hasClaimed: boolean; hasDeployMoreThanManual: boolean; hasDeployment: boolean; hasLicense: boolean; hasPrompts: boolean; hasReadme: boolean; hasResources: boolean; hasTools: boolean; hasValidated: boolean; } export const DEFAULT_WEIGHTS = { claimed: 4, // 必需项 deployMoreThanManual: 12, deployment: 15, // 必需项 license: 8, // 必需项 prompts: 8, readme: 10, resources: 8, // 必需项,权重最高 tools: 15, validated: 20, }; // 评分计算输入数据类型 export interface ScoreCalculationInput extends Partial< Pick< DiscoverMcpDetail, | 'deploymentOptions' | 'github' | 'isValidated' | 'overview' | 'promptsCount' | 'resourcesCount' | 'toolsCount' > > { installationMethods?: string; // 列表页使用 isClaimed?: boolean; // 添加 isClaimed 属性 } /** * 根据原始数据计算所有的评分标志 * @param data 原始数据 * @returns 计算后的布尔值标志 */ export function calculateScoreFlags(data: ScoreCalculationInput): ScoreFlags { const { overview, github, deploymentOptions, installationMethods, isValidated, toolsCount, promptsCount, resourcesCount, isClaimed, } = data; // 计算基础标志 const hasReadme = Boolean(overview?.readme); const hasLicense = Boolean(github?.license); // 优先使用 deploymentOptions(详情页),然后使用 installationMethods(列表页) const effectiveDeploymentOptions: DeploymentOption[] = deploymentOptions || (installationMethods ? [{ installationMethod: installationMethods } as DeploymentOption] : []); const hasDeployment = Boolean( effectiveDeploymentOptions && effectiveDeploymentOptions.length > 0, ); const hasDeployMoreThanManual = Boolean( hasDeployment && effectiveDeploymentOptions?.find((item) => item.installationMethod !== 'manual'), ); const hasTools = Boolean(toolsCount && toolsCount > 0); const hasPrompts = Boolean(promptsCount && promptsCount > 0); const hasResources = Boolean(resourcesCount && resourcesCount > 0); const hasValidated = Boolean(isValidated); const hasClaimed = Boolean(isClaimed); return { hasClaimed, hasDeployMoreThanManual, hasDeployment, hasLicense, hasPrompts, hasReadme, hasResources, hasTools, hasValidated, }; } /** * 获取评级对应的颜色 * @param grade 评级 * @param theme 主题对象(可选) * @returns 颜色值 */ export function getGradeColor(grade: string, theme?: any): string { if (theme) { switch (grade) { case 'a': { return theme.colorSuccess; } case 'b': { return theme.colorWarning; } case 'f': { return theme.colorError; } default: { return theme.colorTextSecondary || theme.colorBorderSecondary; } } } // 默认颜色值(用于没有主题对象的情况) switch (grade) { case 'a': { return '#52c41a'; } case 'b': { return '#faad14'; } case 'f': { return '#ff4d4f'; } default: { return '#8c8c8c'; } } } /** * 获取评级对应的样式类名映射 * @param grade 评级 * @param styles 样式对象 * @returns 对应的样式类名 */ export function getGradeStyleClass(grade: string, styles: any): string { switch (grade) { case 'a': { return styles.gradeA; } case 'b': { return styles.gradeB; } case 'f': { return styles.gradeF; } default: { return styles.disable || ''; } } } /** * 按优先级排序评分项目 * @param items 评分项目数组 * @returns 排序后的项目数组 */ export function sortItemsByPriority<T extends ScoreItem>(items: T[]): T[] { return items.sort((a, b) => { // 1. 必需项优先 if (a.required !== b.required) { return a.required ? -1 : 1; } // 2. 按权重从高到低 const weightA = a.weight || 0; const weightB = b.weight || 0; if (weightA !== weightB) { return weightB - weightA; } // 3. 已完成的在前 if (a.check !== b.check) { return a.check ? -1 : 1; } return 0; }); } /** * 计算 MCP Server 的总分和评级 * @param items 评分项目 * @param weights 权重配置,默认使用 DEFAULT_WEIGHTS * @returns 包含总分、最高分、百分比和评级的结果 */ export function calculateScore( items: Record<string, ScoreItem>, weights: Record<string, number> = DEFAULT_WEIGHTS, ): ScoreResult { let totalScore = 0; let maxScore = 0; let requiredScore = 0; let maxRequiredScore = 0; // 计算实际得分和最大可能得分 Object.entries(items).forEach(([key, item]) => { const weight = weights[key] || 5; // 默认权重为 5 maxScore += weight; if (item.required) { maxRequiredScore += weight; if (item.check) { requiredScore += weight; } } if (item.check) { totalScore += weight; } }); const percentage = maxScore > 0 ? (totalScore / maxScore) * 100 : 0; const requiredPercentage = maxRequiredScore > 0 ? (requiredScore / maxRequiredScore) * 100 : 0; // 评级计算逻辑 let grade: 'a' | 'b' | 'f'; // 如果必需项没有全部满足,直接评为 F if (requiredPercentage < 100) { grade = 'f'; } else { // 必需项全部满足的情况下,根据总分百分比评级 if (percentage >= 80) { grade = 'a'; // 80% 以上为 A } else if (percentage >= 60) { grade = 'b'; // 60-79% 为 B } else { grade = 'f'; // 60% 以下为 F } } return { grade, maxRequiredScore, maxScore, percentage, requiredPercentage, requiredScore, totalScore, }; } /** * 根据评分项目数据创建用于计算的对象 */ export function createScoreItems(data: ScoreFlags): Record<string, ScoreItem> { return { claimed: { check: data.hasClaimed }, deployMoreThanManual: { check: data.hasDeployMoreThanManual }, deployment: { check: data.hasDeployment, required: true }, license: { check: data.hasLicense }, prompts: { check: data.hasPrompts }, readme: { check: data.hasReadme, required: true }, resources: { check: data.hasResources }, tools: { check: data.hasTools, required: true }, validated: { check: data.hasValidated, required: true }, }; }