CodeBench: file-by-file build-validointi vasta kun kaikki tiedostot valmiina
- Vaihe 1: generoi kaikki 4 tiedostoa peräkkäin (konteksti kasvaa) - Vaihe 2: go build kaikilla tiedostoilla → virheet per tiedosto → korjaus - Ratkaisee: models.go yksinään ei käänny (ei main-funktiota) - Virheet ryhmitellään tiedostoittain, korjataan vain viallinen tiedosto
This commit is contained in:
@@ -379,76 +379,90 @@ async function runPipeline(model, scenario, round = 1) {
|
|||||||
const codeTokens = isConvert ? 8192 : (LANG === 'rust' ? 12288 : LANG === 'go' ? 10240 : 8192);
|
const codeTokens = isConvert ? 8192 : (LANG === 'rust' ? 12288 : LANG === 'go' ? 10240 : 8192);
|
||||||
let files;
|
let files;
|
||||||
|
|
||||||
// File-by-file: generoi yksi tiedosto kerrallaan + välitön validointi
|
// File-by-file: generoi yksi tiedosto kerrallaan, build-validointi kun kaikki valmiina
|
||||||
if (FILE_BY_FILE && LCONF.fileByFile) {
|
if (FILE_BY_FILE && LCONF.fileByFile) {
|
||||||
const fbf = LCONF.fileByFile;
|
const fbf = LCONF.fileByFile;
|
||||||
const MAX_FILE_FIX = 2;
|
const MAX_BUILD_FIX = 2;
|
||||||
console.log(` [3/5] Koodigenerointi (file-by-file, ${fbf.length} tiedostoa)...`);
|
console.log(` [3/5] Koodigenerointi (file-by-file, ${fbf.length} tiedostoa)...`);
|
||||||
files = {};
|
files = {};
|
||||||
let context = '';
|
let context = '';
|
||||||
|
|
||||||
|
// Vaihe 1: generoi jokainen tiedosto
|
||||||
for (const fileDef of fbf) {
|
for (const fileDef of fbf) {
|
||||||
const contextBlock = context ? `\nEXISTING CODE:\n${context}\n` : '';
|
const contextBlock = context ? `\nEXISTING CODE:\n${context}\n` : '';
|
||||||
const basePrompt = `${goldenExample}\n---\n\nPROJECT REQUIREMENTS:\n${req.text}\n\nJSON SPECIFICATION:\n${JSON.stringify(spec, null, 2)}\n${contextBlock}\nWrite ONLY the file "${fileDef.name}": ${fileDef.desc}\nOutput raw code, no markdown fences, no explanations. Start with "package main".`;
|
const filePrompt = `${goldenExample}\n---\n\nPROJECT REQUIREMENTS:\n${req.text}\n\nJSON SPECIFICATION:\n${JSON.stringify(spec, null, 2)}\n${contextBlock}\nWrite ONLY the file "${fileDef.name}": ${fileDef.desc}\nOutput raw code, no markdown fences, no explanations. Start with "package main".`;
|
||||||
|
|
||||||
let code = '';
|
console.log(` [3/5] → ${fileDef.name}...`);
|
||||||
for (let attempt = 0; attempt <= MAX_FILE_FIX; attempt++) {
|
const fileResp = await ollamaChat(model, filePrompt, CODE_SYSTEM, 2048);
|
||||||
const prompt = attempt === 0 ? basePrompt
|
|
||||||
: `Fix the following Go compilation errors in "${fileDef.name}". Return ONLY the corrected file, no explanations.\n\nERRORS:\n${code.__buildErrors}\n\nCURRENT FILE:\n${code}\n\nOTHER FILES:\n${context}`;
|
|
||||||
|
|
||||||
const label = attempt === 0 ? fileDef.name : `${fileDef.name} (fix ${attempt})`;
|
|
||||||
console.log(` [3/5] → ${label}...`);
|
|
||||||
const fileResp = await ollamaChat(model, prompt, CODE_SYSTEM, 2048);
|
|
||||||
timings.push(fileResp);
|
timings.push(fileResp);
|
||||||
|
|
||||||
code = fileResp.text
|
let code = fileResp.text
|
||||||
.replace(/^```(?:go|golang)?\s*\n?/m, '').replace(/\n?```\s*$/m, '')
|
.replace(/^```(?:go|golang)?\s*\n?/m, '').replace(/\n?```\s*$/m, '')
|
||||||
.replace(/^(?:Here|Sure|Below|This|The|I )[\s\S]*?(?=package\s)/m, '')
|
.replace(/^(?:Here|Sure|Below|This|The|I )[\s\S]*?(?=package\s)/m, '')
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
if (!code) break;
|
if (code) {
|
||||||
|
files[fileDef.name] = code + '\n';
|
||||||
|
context += `=== ${fileDef.name} ===\n${code}\n\n`;
|
||||||
const loc = code.split('\n').length;
|
const loc = code.split('\n').length;
|
||||||
console.log(` [3/5] ${fileResp.tokens} tok, ${loc} lines, ${fileResp.tokPerSec.toFixed(0)} tok/s`);
|
console.log(` [3/5] ${fileResp.tokens} tok, ${loc} lines, ${fileResp.tokPerSec.toFixed(0)} tok/s`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Välitön validointi: kirjoita tiedostot levylle ja aja go build
|
// Vaihe 2: go build -validointi + per-tiedosto korjaus
|
||||||
if (LANG === 'go' && !fileDef.name.endsWith('_test.go')) {
|
if (LANG === 'go') {
|
||||||
files[fileDef.name] = code + '\n';
|
for (let buildRound = 0; buildRound < MAX_BUILD_FIX; buildRound++) {
|
||||||
// Kirjoita kaikki tähänastiset tiedostot + go.mod
|
// Kirjoita kaikki tiedostot levylle
|
||||||
const goldenMod = readFileSync(join(GOLDEN_DIR, 'todo-go', 'go.mod'), 'utf-8');
|
const goldenMod = readFileSync(join(GOLDEN_DIR, 'todo-go', 'go.mod'), 'utf-8');
|
||||||
const modName = spec.project_name?.replace(/[^a-z0-9-]/gi, '-') || 'generated-api';
|
const modName = spec.project_name?.replace(/[^a-z0-9-]/gi, '-') || 'generated-api';
|
||||||
writeFileSync(join(dir, 'go.mod'), goldenMod.replace(/^module\s+\S+/m, `module ${modName}`));
|
writeFileSync(join(dir, 'go.mod'), goldenMod.replace(/^module\s+\S+/m, `module ${modName}`));
|
||||||
for (const [fn, c] of Object.entries(files)) {
|
for (const [fn, c] of Object.entries(files)) {
|
||||||
writeFileSync(join(dir, fn), c);
|
writeFileSync(join(dir, fn), c);
|
||||||
}
|
}
|
||||||
// go build tarkistus
|
|
||||||
try {
|
try {
|
||||||
execSync(
|
execSync(
|
||||||
`docker run --rm --entrypoint sh -v "${dir}:/src:ro" ${LCONF.dockerImage} -c "cp -r /src/* . && go mod tidy 2>&1 && go build ./... 2>&1"`,
|
`docker run --rm --entrypoint sh -v "${dir}:/src:ro" ${LCONF.dockerImage} -c "cp -r /src/* . && go mod tidy 2>&1 && go build ./... 2>&1"`,
|
||||||
{ timeout: 60000, encoding: 'utf-8', stdio: 'pipe' }
|
{ timeout: 60000, encoding: 'utf-8', stdio: 'pipe' }
|
||||||
);
|
);
|
||||||
console.log(` [3/5] ✓ kääntyy`);
|
console.log(` [3/5] ✓ kääntyy`);
|
||||||
break; // OK — seuraava tiedosto
|
|
||||||
} catch (e) {
|
|
||||||
const buildErrors = (e.stdout || e.stderr || '').split('\n').filter(l => /\.go:\d+/.test(l)).slice(0, 10).join('\n');
|
|
||||||
if (!buildErrors || attempt >= MAX_FILE_FIX) {
|
|
||||||
console.log(` [3/5] ⚠ käännösvirhe (ei korjata)`);
|
|
||||||
break;
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
const allErrors = (e.stdout || e.stderr || '').split('\n').filter(l => /\.go:\d+/.test(l));
|
||||||
|
if (allErrors.length === 0) { console.log(` [3/5] ⚠ build failed`); break; }
|
||||||
|
|
||||||
|
// Ryhmittele virheet tiedostoittain
|
||||||
|
const errorsByFile = {};
|
||||||
|
for (const line of allErrors) {
|
||||||
|
const m = line.match(/\.\/(\S+\.go):\d+/);
|
||||||
|
if (m) { (errorsByFile[m[1]] = errorsByFile[m[1]] || []).push(line); }
|
||||||
|
}
|
||||||
|
const filesToFix = Object.keys(errorsByFile).filter(f => !f.endsWith('_test.go'));
|
||||||
|
if (filesToFix.length === 0) break;
|
||||||
|
|
||||||
|
console.log(` [3/5] ✗ ${allErrors.length} errors in ${filesToFix.join(', ')} → fixing`);
|
||||||
|
|
||||||
|
for (const fname of filesToFix) {
|
||||||
|
const errors = errorsByFile[fname].slice(0, 10).join('\n');
|
||||||
|
const fixPrompt = `Fix the following Go compilation errors in "${fname}". Return ONLY the corrected file, no explanations.\n\nERRORS:\n${errors}\n\nCURRENT FILE:\n${files[fname]}\n\nOTHER FILES:\n${Object.entries(files).filter(([f]) => f !== fname).map(([f, c]) => `=== ${f} ===\n${c}`).join('\n\n')}`;
|
||||||
|
console.log(` [3/5] → ${fname} (fix)...`);
|
||||||
|
const fixResp = await ollamaChat(model, fixPrompt, CODE_SYSTEM, 2048);
|
||||||
|
timings.push(fixResp);
|
||||||
|
let fixed = fixResp.text
|
||||||
|
.replace(/^```(?:go|golang)?\s*\n?/m, '').replace(/\n?```\s*$/m, '')
|
||||||
|
.replace(/^(?:Here|Sure|Below|This|The|I )[\s\S]*?(?=package\s)/m, '')
|
||||||
|
.trim();
|
||||||
|
if (fixed) {
|
||||||
|
files[fname] = fixed + '\n';
|
||||||
|
console.log(` [3/5] ${fixResp.tokens} tok, ${fixed.split('\n').length} lines`);
|
||||||
}
|
}
|
||||||
console.log(` [3/5] ✗ ${buildErrors.split('\n').length} errors → fixing`);
|
|
||||||
code.__buildErrors = buildErrors;
|
|
||||||
result.fixRounds++;
|
result.fixRounds++;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
break; // Testitiedostoa ei validoida go buildilla
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code && typeof code === 'string') {
|
// Päivitä context lopullisilla tiedostoilla
|
||||||
files[fileDef.name] = code + '\n';
|
context = Object.entries(files).map(([fn, c]) => `=== ${fn} ===\n${c}`).join('\n\n');
|
||||||
context += `=== ${fileDef.name} ===\n${code}\n\n`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeFileSync(`${dir}/_code_raw.txt`, context);
|
writeFileSync(`${dir}/_code_raw.txt`, context);
|
||||||
result.promptChars = CODE_SYSTEM.length + (context.length || 0);
|
result.promptChars = CODE_SYSTEM.length + (context.length || 0);
|
||||||
result.promptTokensEst = Math.round(result.promptChars / 4);
|
result.promptTokensEst = Math.round(result.promptChars / 4);
|
||||||
|
|||||||
Reference in New Issue
Block a user