@autobe/agent
Version:
AI backend server code generator
343 lines • 15 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.orchestrateRealizeCorrectOverall = void 0;
const uuid_1 = require("uuid");
const executeCachedBatch_1 = require("../../../utils/executeCachedBatch");
const forceRetry_1 = require("../../../utils/forceRetry");
const compileRealizeFiles_1 = require("../programmers/compileRealizeFiles");
/**
* Deduplicate diagnostics by grouping identical messages and capping total
* count.
*
* Single root causes (e.g., `null` in select) can produce 50-300 cascading
* errors with identical messages. This function collapses them so the LLM
* focuses on the root cause instead of being overwhelmed by repetition.
*/
const deduplicateDiagnostics = (diagnostics) => {
const byMessage = new Map();
for (const d of diagnostics) {
const key = d.messageText;
const existing = byMessage.get(key);
if (existing) {
existing.count++;
}
else {
byMessage.set(key, { diag: d, count: 1 });
}
}
const deduped = [];
for (const [, { diag, count }] of byMessage) {
deduped.push(Object.assign(Object.assign({}, diag), { messageText: count > 1
? `${diag.messageText} (repeated ${count} times - fix the root cause)`
: diag.messageText }));
}
if (deduped.length > 25) {
const truncated = deduped.slice(0, 25);
truncated.push({
file: deduped[0].file,
start: null,
length: null,
code: 0,
messageText: `[+${deduped.length - 25} additional unique errors omitted - focus on the above errors first]`,
category: "error",
});
return truncated;
}
return deduped;
};
/**
* Sanitize LLM-generated code by removing common artifacts:
*
* - Chain-of-thought text leaked into code output
* - Token truncation artifacts (e.g., standalone 'n' characters)
* - Markdown code fences
*/
const sanitizeGeneratedCode = (code) => {
let result = code;
// 1. Extract code from markdown fences if present
const codeBlockMatch = result.match(/```(?:typescript|ts)?\s*\n([\s\S]*?)\n```/);
if (codeBlockMatch) {
result = codeBlockMatch[1];
}
// 2. Remove everything before the first export statement
const exportMatch = result.match(/(export\s+(?:namespace|async\s+function|function|const)\s+[\s\S]*)/);
if (exportMatch) {
result = exportMatch[1];
}
// 3. Remove standalone 'n' token artifacts (minimax-m2.7 pattern)
// Only remove lines that are EXACTLY 'n' (with optional whitespace)
result = result.replace(/^\s*n\s*$/gm, "");
// 4. Remove trailing 'n' after commas (another truncation pattern)
result = result.replace(/,\s*n\s*\n/g, ",\n");
return result.trim();
};
const orchestrateRealizeCorrectOverall = (ctx_1, props_1, ...args_1) => __awaiter(void 0, [ctx_1, props_1, ...args_1], void 0, function* (ctx, props, life = 4 /* AutoBeConfigConstant.COMPILER_RETRY */) {
const preliminaries = new Map(props.functions.map((func) => [
func.location,
props.programmer.preliminary({
function: func,
source: SOURCE,
}),
]));
const validateEvent = yield compileWithFiltering(ctx, {
functions: props.functions,
programmer: props.programmer,
progress: props.progress,
});
return predicate(ctx, {
programmer: props.programmer,
functions: props.functions,
preliminaries,
previousFailures: [],
progress: props.progress,
event: validateEvent,
}, life);
});
exports.orchestrateRealizeCorrectOverall = orchestrateRealizeCorrectOverall;
const predicate = (ctx, props, life) => __awaiter(void 0, void 0, void 0, function* () {
if (props.event.result.type === "failure") {
ctx.dispatch(props.event);
return yield correct(ctx, props, life);
}
return props.functions.map((f) => ({ success: true, function: f }));
});
const correct = (ctx, props, life) => __awaiter(void 0, void 0, void 0, function* () {
// Early returns for non-correctable cases
if (props.event.result.type !== "failure")
return props.functions.map((f) => ({ success: true, function: f }));
else if (life < 0)
return props.functions.map((f) => ({ success: false, function: f }));
const failure = props.event.result;
const allErrorLocations = getErrorFiles({
location: props.programmer.location,
failure,
}).filter((l) => props.functions.map((f) => f.location).includes(l));
// If no locations to correct, return original functions
if (allErrorLocations.length === 0) {
return props.functions.map((f) => ({ success: false, function: f }));
}
const errorLocations = allErrorLocations;
const converted = yield (0, executeCachedBatch_1.executeCachedBatch)(ctx, errorLocations.map((location) => () => __awaiter(void 0, void 0, void 0, function* () {
const localFunction = props.functions.find((f) => f.location === location);
const rawDiagnostics = failure.diagnostics.filter((d) => d.file === localFunction.location);
// P2-5: Log when error count is very high (suggests regeneration may be better than correction)
if (rawDiagnostics.length > 20) {
console.warn(`[realizeCorrectOverall] ${rawDiagnostics.length} errors in ${localFunction.location} — consider regeneration instead of correction`);
}
const localFailures = [
...props.previousFailures
.map((pf) => {
var _a;
return (_a = pf.find((f) => f.function.location === localFunction.location)) !== null && _a !== void 0 ? _a : null;
})
.filter((x) => x !== null),
{
function: localFunction,
diagnostics: deduplicateDiagnostics(rawDiagnostics),
},
];
try {
return yield (0, forceRetry_1.forceRetry)(() => process(ctx, {
programmer: props.programmer,
progress: props.progress,
preliminary: props.preliminaries.get(location),
function: localFunction,
failures: localFailures,
}));
}
catch (error) {
console.log("realizeCorrectOverall", localFunction.location, error);
return {
type: "exception",
function: localFunction,
};
}
})));
// Get functions that were not modified (not in corrected locations).
// Deferred files (cross-file dependency victims) are included here
// automatically — they will be retried in the next iteration after
// root-cause corrections have been applied and recompiled.
const unchangedFunctions = props.functions.filter((f) => !errorLocations.includes(f.location));
// Merge converted functions with unchanged functions for validation
const allFunctionsForValidation = [
...converted.map((c) => c.function),
...unchangedFunctions,
];
const newValidate = yield compileWithFiltering(ctx, {
functions: allFunctionsForValidation,
programmer: props.programmer,
progress: props.progress,
});
const newResult = newValidate.result;
if (newResult.type === "success") {
return allFunctionsForValidation.map((f) => ({
success: true,
function: f,
}));
}
else if (newResult.type === "exception") {
// Compilation exception, return current functions. because retrying won't help.
return props.functions.map((f) => ({ success: false, function: f }));
}
const newLocations = newValidate.result.type === "failure"
? getErrorFiles({
failure: newValidate.result,
location: props.programmer.location,
})
: [];
// Separate successful, failed, and ignored corrections
const { success, failed, ignored } = separateCorrectionResults(converted, newLocations);
// If no failures to retry, return all functions
if (failed.length === 0) {
return [
...success.map((f) => ({ success: true, function: f })),
...ignored.map((f) => ({ success: false, function: f })),
...unchangedFunctions.map((f) => ({ success: true, function: f })),
];
}
// Recursively retry failed functions
const retriedResults = yield predicate(ctx, {
programmer: props.programmer,
preliminaries: props.preliminaries,
functions: failed,
previousFailures: [
...props.previousFailures,
failed.map((f) => ({
function: f,
diagnostics: newValidate.result.type === "failure"
? newValidate.result.diagnostics.filter((d) => d.file === f.location)
: [],
})),
],
progress: props.progress,
event: newValidate,
}, life - 1);
return [
...success.map((f) => ({ success: true, function: f })),
...ignored.map((f) => ({ success: false, function: f })),
...retriedResults,
...unchangedFunctions.map((f) => ({ success: true, function: f })),
];
});
const process = (ctx, props) => __awaiter(void 0, void 0, void 0, function* () {
const event = yield props.preliminary.orchestrate(ctx, (out) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b, _c;
const pointer = {
value: null,
};
const controller = props.programmer.controller({
preliminary: props.preliminary,
build(next) {
pointer.value = next;
},
function: props.function,
source: SOURCE,
});
const result = yield ctx.conversate(Object.assign({ source: SOURCE, controller, enforceFunctionCall: true }, (yield props.programmer.histories({
preliminary: props.preliminary,
function: props.function,
failures: props.failures,
}))));
if (pointer.value === null)
return out(result)(null);
const template = props.programmer.template(props.function);
const content = yield props.programmer.replaceImportStatements({
function: props.function,
code: sanitizeGeneratedCode((_a = pointer.value.revise.final) !== null && _a !== void 0 ? _a : pointer.value.draft),
});
const corrected = Object.assign(Object.assign({}, props.function), { content,
template });
return out(result)({
id: (0, uuid_1.v7)(),
type: "realizeCorrect",
kind: "overall",
function: corrected,
created_at: new Date().toISOString(),
step: (_c = (_b = ctx.state().analyze) === null || _b === void 0 ? void 0 : _b.step) !== null && _c !== void 0 ? _c : 0,
metric: result.metric,
tokenUsage: result.tokenUsage,
});
}));
ctx.dispatch(event);
return {
type: "success",
function: event.function,
};
});
const compileWithFiltering = (ctx, props) => __awaiter(void 0, void 0, void 0, function* () {
const compiled = yield (0, compileRealizeFiles_1.compileRealizeFiles)(ctx, {
functions: props.functions,
additional: props.programmer.additional(props.functions),
progress: (result) => {
if (result.type === "success")
props.progress.completed = props.functions.length;
else if (result.type === "failure")
props.progress.completed =
props.progress.total -
new Set(result.diagnostics
.map((d) => d.file)
.filter((f) => f !== null)
.filter((x) => !!props.functions.find((y) => y.location === x))).size;
return props.progress;
},
});
if (compiled.result.type !== "failure")
return compiled;
const functionLocations = new Set(props.functions.map((f) => f.location));
const directErrors = compiled.result.diagnostics.filter((d) => d.file !== null && functionLocations.has(d.file));
const crossFileErrors = compiled.result.diagnostics.filter((d) => d.file !== null && !functionLocations.has(d.file));
// Log cross-file errors for debugging (P1-4)
if (crossFileErrors.length > 0) {
console.warn(`[realizeCorrectOverall] ${crossFileErrors.length} cross-file errors detected in: ` +
[...new Set(crossFileErrors.map((d) => d.file))].join(", "));
}
compiled.result.diagnostics = directErrors;
if (compiled.result.diagnostics.length === 0) {
compiled.result = { type: "success" };
}
return compiled;
});
/**
* Extract unique file locations from validation event diagnostics
*
* @param event - Validation event containing compilation results
* @returns Array of unique file paths that have errors
*/
const getErrorFiles = (props) => {
const diagnostics = props.failure.diagnostics;
const locations = diagnostics
.map((d) => d.file)
.filter((f) => f !== null)
.filter((f) => f.startsWith(props.location));
return Array.from(new Set(locations));
};
/**
* Separate correction results into successful, failed, and ignored functions
*
* @param corrections - Array of correction results
* @param errorLocations - File paths that still have errors
* @returns Object with success, failed, and ignored function arrays
*/
const separateCorrectionResults = (corrections, errorLocations) => {
const success = corrections
.filter((c) => c.type === "success" && !errorLocations.includes(c.function.location))
.map((c) => c.function);
const failed = corrections
.filter((c) => c.type === "success" && errorLocations.includes(c.function.location))
.map((c) => c.function);
const ignored = corrections
.filter((c) => c.type === "ignore" || c.type === "exception")
.map((c) => c.function);
return { success, failed, ignored };
};
const SOURCE = "realizeCorrect";
//# sourceMappingURL=orchestrateRealizeCorrectOverall.js.map