Files
Joel Alan b43ddb6543 feat: add context service (#101)
* 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>
2026-07-31 11:46:18 +08:00

134 lines
5.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 评测公共工具函数
*/
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 链式 APIchart.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
};
}