@autobe/agent
Version:
AI backend server code generator
341 lines • 15.5 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.AutoBePreliminaryController = void 0;
const utils_1 = require("@autobe/utils");
const utils_2 = require("@typia/utils");
const uuid_1 = require("uuid");
const AutoBePreliminaryExhaustedError_1 = require("../../utils/AutoBePreliminaryExhaustedError");
const transformPreliminaryHistory_1 = require("./histories/transformPreliminaryHistory");
const complementPreliminaryCollection_1 = require("./internal/complementPreliminaryCollection");
const createPreliminaryCollection_1 = require("./internal/createPreliminaryCollection");
const fixPrelminaryApplication_1 = require("./internal/fixPrelminaryApplication");
const validatePreliminary_1 = require("./internal/validatePreliminary");
const orchestratePreliminary_1 = require("./orchestratePreliminary");
/**
* RAG controller for incremental context loading.
*
* Manages LLM's incremental data requests via function calling, preventing
* duplicates and auto-resolving dependencies.
*
* - `all`: globally available data (from state)
* - `local`: currently loaded data (agent context)
* - Auto-complements prerequisites, $ref dependencies, and neighbors
*
* @author Samchon
*/
class AutoBePreliminaryController {
/**
* Initializes controller with data collections and auto-complements
* dependencies.
*
* @param props Constructor configuration including kinds, state, and initial
* data.
*/
constructor(props) {
var _a, _b, _c, _d;
// PAGINATION
this.analysisPageOffset = 0;
// COMPLETION TRACKING
this.previousWrites = [];
this.completed = {
value: false,
};
this.source = props.source;
this.source_id = (0, uuid_1.v7)();
this.kinds = props.kinds;
this.dispatch = props.dispatch;
// biome-ignore-start lint: intended
this.config = {
database: (_b = (_a = props.config) === null || _a === void 0 ? void 0 : _a.database) !== null && _b !== void 0 ? _b : "text",
databaseProperty: (_d = (_c = props.config) === null || _c === void 0 ? void 0 : _c.databaseProperty) !== null && _d !== void 0 ? _d : false,
};
// biome-ignore-end lint: intended
this.argumentTypeNames = (() => {
var _a, _b, _c;
const func = props.application.functions.find((f) => f.name === "process");
if (func === undefined)
throw new Error("Unable to find 'process' function in application.");
const param = (_a = func.parameters[0]) === null || _a === void 0 ? void 0 : _a.schema;
if (param === undefined ||
utils_2.OpenApiTypeChecker.isReference(param) === false)
throw new Error("'process' function parameter is not a reference type.");
const schema = (_b = props.application.components.schemas) === null || _b === void 0 ? void 0 : _b[param.$ref.split("/").pop()];
if (schema === undefined || utils_2.OpenApiTypeChecker.isObject(schema) === false)
throw new Error("'process' function parameter reference is not an object type.");
const request = (_c = schema.properties) === null || _c === void 0 ? void 0 : _c.request;
if (request === undefined ||
utils_2.OpenApiTypeChecker.isOneOf(request) === false)
throw new Error("'process' function parameter.request is not a oneOf type.");
else if (request.oneOf.length === 0 ||
request.oneOf.every((sch) => utils_2.OpenApiTypeChecker.isReference(sch) === false))
throw new Error("'process' function parameter.request oneOf does not contain any reference type.");
return request.oneOf.map((sch) => {
// biome-ignore lint: intended
const ref = sch.$ref;
return ref.split("/").pop();
});
})();
this.state = props.state;
this.all = (0, createPreliminaryCollection_1.createPreliminaryCollection)(props.state, props.all);
this.local = (0, createPreliminaryCollection_1.createPreliminaryCollection)(null, props.local);
(0, complementPreliminaryCollection_1.complementPreliminaryCollection)({
kinds: props.kinds,
all: this.all,
local: this.local,
prerequisite: false,
});
}
/**
* Validates request for duplicates and non-existent items.
*
* @param input LLM's function calling request to validate.
* @returns Validation result with errors if duplicates or non-existent items
* found.
*/
validate(input) {
return (0, validatePreliminary_1.validatePreliminary)(this, input);
}
/**
* Generates dynamic system prompts with `LOADED`/`AVAILABLE` lists.
*
* @returns Assistant and system messages with loaded/available item lists.
*/
getHistories() {
return (0, transformPreliminaryHistory_1.transformPreliminaryHistory)(this);
}
/**
* Returns the orchestration source that created this controller.
*
* @returns Source event type (e.g., `"realizeWrite"`, `"interfaceSchema"`).
*/
getSource() {
return this.source;
}
/**
* Returns data types enabled for this controller.
*
* @returns Array of enabled kinds (e.g., `["databaseSchemas",
* "interfaceSchemas"]`).
*/
getKinds() {
return this.kinds;
}
/**
* Returns configuration (e.g., database schema format: `"ast"` | `"text"`).
*
* @returns Controller configuration object.
*/
getConfig() {
return this.config;
}
/**
* Returns function calling type names available in LLM application.
*
* @returns Array of request type names from `oneOf` union.
*/
getArgumentTypeNames() {
return this.argumentTypeNames;
}
/**
* Returns all globally available data from state.
*
* @returns Complete dataset that can be requested.
*/
getAll() {
return this.all;
}
/**
* Returns currently loaded data in agent context.
*
* @returns Subset of data already provided to LLM.
*/
getLocal() {
return this.local;
}
/**
* Extracts acquisition metadata from the currently loaded preliminary data.
*
* Transforms the local preliminary collection into a normalized acquisition
* structure that is suitable for event tracking, including only metadata such
* as filenames, schema names, and operation identifiers rather than full
* objects.
*
* @returns Acquisition metadata derived from currently loaded data,
* normalized for event tracking.
*/
getAcquisition() {
const acquisition = {};
const local = this
.local;
for (const kind of this.kinds)
if (kind === "analysisSections")
acquisition.analysisSections = local.analysisSections.map((s) => s.id);
else if (kind === "databaseSchemas")
acquisition.databaseSchemas = local.databaseSchemas.map((s) => s.name);
else if (kind === "interfaceOperations")
acquisition.interfaceOperations = local.interfaceOperations.map((o) => ({
path: o.path,
method: o.method,
}));
else if (kind === "interfaceSchemas")
acquisition.interfaceSchemas = Object.keys(local.interfaceSchemas);
else if (kind === "realizeCollectors")
acquisition.realizeCollectors = local.realizeCollectors.map((f) => f.plan.dtoTypeName);
else if (kind === "realizeTransformers")
acquisition.realizeTransformers = local.realizeTransformers.map((f) => f.plan.dtoTypeName);
else if (kind === "previousAnalysisSections")
acquisition.previousAnalysisSections =
local.previousAnalysisSections.map((s) => s.id);
else if (kind === "previousDatabaseSchemas")
acquisition.previousDatabaseSchemas = local.previousDatabaseSchemas.map((s) => s.name);
else if (kind === "previousInterfaceOperations")
acquisition.previousInterfaceOperations =
local.previousInterfaceOperations.map((o) => ({
path: o.path,
method: o.method,
}));
else if (kind === "previousInterfaceSchemas")
acquisition.previousInterfaceSchemas = Object.keys(local.previousInterfaceSchemas);
else if (kind === "complete")
acquisition.complete = false;
else
kind;
return acquisition;
}
/**
* Returns the current AutoBe state.
*
* @returns Current pipeline state containing all phase histories.
*/
getState() {
return this.state;
}
/** Returns current page offset for analysis section metadata pagination. */
getAnalysisPageOffset() {
return this.analysisPageOffset;
}
getPreviousWrite() {
var _a, _b;
return (_b = (_a = this.previousWrites.at(-1)) === null || _a === void 0 ? void 0 : _a.raw) !== null && _b !== void 0 ? _b : null;
}
/** Advances analysis section metadata page by PAGE_SIZE. */
advanceAnalysisPage() {
this.analysisPageOffset += 75 /* AutoBeConfigConstant.ANALYSIS_PAGE_SIZE */;
}
/**
* Dynamically adjusts LLM application schema at runtime.
*
* Removes `getPreviousXXX` types from union/oneOf when no previous iteration
* exists. Mutates application's `anyOf`/`oneOf` array, `$defs`, and
* `discriminator` mapping. Also erases corresponding `kinds` from
* controller's `all`/`local` collections.
*
* @param application LLM application to modify (mutated in-place)
*/
fixApplication(application, enumerable = false) {
(0, fixPrelminaryApplication_1.fixPreliminaryApplication)({
state: this.state,
preliminary: this,
application,
enumerable,
});
return application;
}
/**
* Runs RAG loop for incremental context loading.
*
* Repeats until process returns non-null value or exceeds the maximum number
* of iterations (`AutoBeConfigConstant.RAG_LIMIT`). Each iteration: LLM
* requests data → `orchestratePreliminary` adds to `local` → next iteration
* with updated context.
*
* When RAG_LIMIT is exhausted, throws `AutoBePreliminaryExhaustedError` which
* can be caught alongside `AutoBeTimeoutError` for force-pass.
*
* @param ctx AutoBe context for `conversate` and state access.
* @param process Callback that runs LLM `conversate` and returns result.
* @returns Final value when process returns non-null or throws after
* exceeding `AutoBeConfigConstant.RAG_LIMIT` retries.
*/
orchestrate(ctx, process) {
return __awaiter(this, void 0, void 0, function* () {
// initialize
this.previousWrites = [];
this.completed.value = false;
const aggregate = utils_1.AutoBeProcessAggregateFactory.createAggregate();
try {
for (let i = 0; i < 7 /* AutoBeConfigConstant.RAG_LIMIT */; ++i) {
// take a process
const result = yield process((x) => (value) => (Object.assign(Object.assign({}, x), { value })));
// swap consumption to aggregate
utils_1.AutoBeFunctionCallingMetricFactory.increment(aggregate.metric, result.metric);
utils_1.TokenUsageComputer.increment(aggregate.tokenUsage, result.tokenUsage);
result.tokenUsage = aggregate.tokenUsage;
result.metric = aggregate.metric;
if (result.value !== null) {
const executes = result.histories.filter((h) => h.type === "execute");
const history = executes.find((h) => h.arguments.request.type === "write");
if (history === undefined)
continue;
// store write result and raw arguments
this.previousWrites.push({
value: result.value,
metric: result.metric,
tokenUsage: result.tokenUsage,
raw: history.arguments,
});
if (this.previousWrites.length > 1) {
this.dispatch({
id: (0, uuid_1.v7)(),
type: "preliminaryRewrite",
source: this.source,
thinking: history.arguments.thinking,
oldbie: this.previousWrites.at(-2).raw,
newbie: history.arguments,
created_at: new Date().toISOString(),
});
}
if (this.previousWrites.length >=
(this.kinds.includes("complete")
? 3 /* AutoBeConfigConstant.PRELIMINARY_WRITE_LIMIT */
: 1))
break;
}
else {
// orchestrate next iteration
yield (0, orchestratePreliminary_1.orchestratePreliminary)(ctx, {
source_id: this.source_id,
source: this.source,
preliminary: this,
trial: i + 1,
histories: result.histories,
completed: this.completed,
});
if (this.completed.value === true && this.previousWrites.length !== 0)
break;
}
}
}
catch (error) {
if (this.previousWrites.length === 0)
throw error;
}
// check success
const last = this.previousWrites.at(-1);
if (last !== undefined)
return last.value;
throw new AutoBePreliminaryExhaustedError_1.AutoBePreliminaryExhaustedError();
});
}
}
exports.AutoBePreliminaryController = AutoBePreliminaryController;
//# sourceMappingURL=AutoBePreliminaryController.js.map