mirror of
https://github.com/antvis/chart-visualization-skills.git
synced 2026-09-14 15:58:00 +08:00
b43ddb6543
* feat: skill use context service (#97) * feat: retrieve document as context with `zvec` (#87) * feat: 召回策略升级到 zvec * chore: 删除不必要的 createContext --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * fix: 修复 playground 图表渲染异常 (#88) Co-authored-by: 福晋 <liufu.lf@antgroup.com> * refactor: antv skill refactor (#90) * refactor: antv skill refactor * chore: remove reference data from the eval process * chore: update api path * chore: remove mistakes content * chore: includeInfo → includeConstraints --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * chore: skill content update (#93) * chore: skill content update * chore: update skill content --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * chore: skill rename to doc (#92) * chore: skill rename to doc * chore: code opt --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * refactor: use context for retrieval (#94) * refactor: use context for retrieval * chore: update dependence * chore: code opt * chore: update eval results * chore: cut down redundent code * chore: reduce external dependence * chore: update dependence version * fix: build * fix: utils * fix: test * chore: update node version --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * refactor: 简化代码 (#96) * refactor: 简化代码 * chore: remove command * chore: update timeout * feat: add cli and format output * feat: add maxTokens * docs: add publish action * chore: update tc * feat: use exist zvec * chore: 0.1.4 --------- Co-authored-by: 逍为 <xiaowei.wzw@antgroup.com> * chore: merge master * chore: test should be first --------- Co-authored-by: Joel Alan <31396322+lxfu1@users.noreply.github.com> Co-authored-by: 福晋 <liufu.lf@antgroup.com> Co-authored-by: 逍为 <xiaowei.wzw@antgroup.com> * chore: add postinstall * chore: improvement of retrieval quality (#99) * chore: improvement of retrieval quality * chore: remove content from dist * chore: remove default ftsFields * chore: update version --------- Co-authored-by: 福晋 <liufu.lf@antgroup.com> * chore: update retrieve host (#100) Co-authored-by: 福晋 <liufu.lf@antgroup.com> --------- Co-authored-by: hustcc <i@hust.cc> Co-authored-by: 福晋 <liufu.lf@antgroup.com> Co-authored-by: 逍为 <xiaowei.wzw@antgroup.com>
134 lines
5.4 KiB
TypeScript
134 lines
5.4 KiB
TypeScript
/**
|
||
* 评测公共工具函数
|
||
*/
|
||
|
||
import { calculateSimilarity, extractStructuralFeatures, StructuralFeatures } from './code-similarity.js';
|
||
|
||
// ── 库检测 ──────────────────────────────────────────────────────────────────────
|
||
|
||
function detectLibrary(codeString: string): string {
|
||
if (codeString.includes('@antv/x6')) return 'x6';
|
||
if (codeString.includes('@antv/g6')) return 'g6';
|
||
return 'g2';
|
||
}
|
||
|
||
// ── 数据提取 ───────────────────────────────────────────────────────────────────
|
||
|
||
function extractDataFromCode(codeString: string): string[] {
|
||
const arrayMatch = codeString.match(/(?:const|let|var)\s+\w*[Dd]ata\w*\s*=\s*(\[[\s\S]*?\]);/);
|
||
if (arrayMatch) return [arrayMatch[1].slice(0, 500)];
|
||
return [];
|
||
}
|
||
|
||
// ── 查询构建 ───────────────────────────────────────────────────────────────────
|
||
|
||
export interface TestCase {
|
||
id?: string;
|
||
description: string;
|
||
codeString: string;
|
||
}
|
||
|
||
export interface QueryResult {
|
||
query: string;
|
||
library: string;
|
||
}
|
||
|
||
export function buildQuery(testCase: TestCase, options: { includeData?: boolean } = {}): QueryResult {
|
||
const { description, codeString } = testCase;
|
||
const library = detectLibrary(codeString);
|
||
const { includeData = true } = options;
|
||
|
||
// Strip inline "参考数据" from description for retrieval.
|
||
// Dataset descriptions embed reference JSON inline in two formats:
|
||
// "…高亮交互。\n参考数据:\n[{sets:…}]" (newline-separated)
|
||
// "…高亮交互。参考数据:[{sets:…}]" (inline after period)
|
||
const cleanDescription = description.split(/参考数据[::]/)[0].trim();
|
||
|
||
let query = includeData ? description : cleanDescription;
|
||
if (includeData) {
|
||
const refData = extractDataFromCode(codeString);
|
||
if (refData.length > 0 && !description.includes('参考数据')) {
|
||
query += `\n\n参考数据:\n${refData[0]}`;
|
||
}
|
||
}
|
||
|
||
return { query, library };
|
||
}
|
||
|
||
// ── 代码提取 ───────────────────────────────────────────────────────────────────
|
||
|
||
export function extractCodeFromResponse(response: string): string {
|
||
const codeBlockMatch = response.match(/```(?:javascript|js|typescript|ts)?\s*([\s\S]*?)```/);
|
||
if (codeBlockMatch) return codeBlockMatch[1].trim();
|
||
const importMatch = response.match(/import[\s\S]*/);
|
||
if (importMatch) return importMatch[0].trim();
|
||
return response;
|
||
}
|
||
|
||
// ── 代码评估 ───────────────────────────────────────────────────────────────────
|
||
|
||
export interface EvaluationResult {
|
||
hasIssues: boolean;
|
||
issues: string[];
|
||
warnings: string[];
|
||
codeLength: number;
|
||
expectedLength: number;
|
||
similarity: number;
|
||
extractedCode: string;
|
||
structuralFeatures: StructuralFeatures;
|
||
}
|
||
|
||
export function evaluateCode(
|
||
generatedCode: string,
|
||
expectedCode: string,
|
||
options: { similarityAlgorithm?: string; library?: string } = {}
|
||
): EvaluationResult {
|
||
const issues: string[] = [];
|
||
const warnings: string[] = [];
|
||
const extractedCode = extractCodeFromResponse(generatedCode);
|
||
const isX6 = options.library === 'x6' || extractedCode.includes('@antv/x6');
|
||
|
||
if (!extractedCode.includes('import') && !extractedCode.includes('require')) {
|
||
issues.push('缺少 import/require 语句');
|
||
}
|
||
if (!extractedCode.includes('new Chart') && !extractedCode.includes('new Graph')) {
|
||
issues.push('缺少 Chart/Graph 实例化');
|
||
}
|
||
// X6 不需要显式 .render() 调用;Graph 实例化后通过 fromJSON/addNode 即可渲染
|
||
if (!isX6 && !extractedCode.includes('.render')) {
|
||
issues.push('缺少 render() 调用');
|
||
}
|
||
if (/chart\.(interval|line|point|area|cell)\s*\(/.test(extractedCode)) {
|
||
issues.push('使用了 V4 链式 API(chart.interval() 等)');
|
||
}
|
||
if (extractedCode.includes('createView')) issues.push('使用了 V4 createView');
|
||
// X6 中 .position() 是合法的节点方法,仅对 G2 检查
|
||
if (!isX6 && /\.position\s*\(/.test(extractedCode)) issues.push('使用了 V4 .position() 语法');
|
||
if (/coordinate\s*:\s*\{\s*type\s*:\s*['"]transpose['"]/.test(extractedCode)) {
|
||
warnings.push('coordinate transpose 应使用 transform 数组而非 type');
|
||
}
|
||
if (/transform\s*:\s*\{\s*type\s*:/.test(extractedCode)) {
|
||
warnings.push('transform 应为数组 [...] 而非对象 {...}');
|
||
}
|
||
if (/(?<![a-zA-Z])label\s*:\s*\{/.test(extractedCode) && !extractedCode.includes('labels:')) {
|
||
warnings.push('应使用 labels(复数)而非 label(单数)');
|
||
}
|
||
|
||
const similarity = calculateSimilarity(extractedCode, expectedCode, {
|
||
algorithm: (options.similarityAlgorithm as 'hybrid') ?? 'hybrid',
|
||
library: options.library,
|
||
});
|
||
const structuralFeatures = extractStructuralFeatures(extractedCode);
|
||
|
||
return {
|
||
hasIssues: issues.length > 0,
|
||
issues,
|
||
warnings,
|
||
codeLength: extractedCode.length,
|
||
expectedLength: expectedCode.length,
|
||
similarity,
|
||
extractedCode,
|
||
structuralFeatures
|
||
};
|
||
}
|