UNPKG

@autobe/agent

Version:

AI backend server code generator

376 lines (352 loc) 12.5 kB
import { AgenticaExecuteHistory, IAgenticaTokenUsageJson, IAgenticaVendor, MicroAgentica, MicroAgenticaHistory, } from "@agentica/core"; import { AutoBeAssistantMessageHistory, AutoBeHistory, AutoBePhase, AutoBeProcessAggregateCollection, AutoBeUserMessageContent, AutoBeUserMessageHistory, IAutoBeAgent, IAutoBeCompilerListener, IAutoBeGetFilesOptions, } from "@autobe/interface"; import { AutoBeProcessAggregateFactory } from "@autobe/utils"; import { ILlmSchema } from "@samchon/openapi"; import { Semaphore, Singleton } from "tstl"; import { v7 } from "uuid"; import { AutoBeAgentBase } from "./AutoBeAgentBase"; import { AutoBeConfigConstant } from "./constants/AutoBeConfigConstant"; import { AutoBeContext } from "./context/AutoBeContext"; import { AutoBeState } from "./context/AutoBeState"; import { AutoBeTokenUsage } from "./context/AutoBeTokenUsage"; import { createAgenticaHistory } from "./factory/createAgenticaHistory"; import { createAutoBeContext } from "./factory/createAutoBeContext"; import { createAutoBeState } from "./factory/createAutoBeState"; import { getAutoBeGenerated } from "./factory/getAutoBeGenerated"; import { getCommonPrompt } from "./factory/getCommonPrompt"; import { supportMistral } from "./factory/supportMistral"; import { createAutoBeFacadeController } from "./orchestrate/facade/createAutoBeFacadeController"; import { transformFacadeStateMessage } from "./orchestrate/facade/structures/transformFacadeStateMessage"; import { IAutoBeProps } from "./structures/IAutoBeProps"; import { randomBackoffStrategy } from "./utils/backoffRetry"; /** * Main agent class that orchestrates the entire vibe coding pipeline through * conversation-driven development. * * The AutoBeAgent serves as the central coordinator for the waterfall-based * development process with spiral model iterative improvements. It manages the * five specialized agents (Analyze, Prisma, Interface, Test, Realize) that * transform user conversations into complete working applications through a * sophisticated AST-based compilation infrastructure. * * The agent operates through natural language conversation, supporting * multimodal input including text, images, files, and audio. It maintains * conversation history, tracks development progress through real-time events, * and provides access to all generated artifacts including requirements * documentation, database schemas, API specifications, test suites, and * implementation code. * * The vibe coding approach eliminates traditional development barriers by * enabling users to express requirements naturally while the agent handles all * technical implementation details through validated AST transformations and * continuous quality assurance feedback loops. * * @author Samchon */ export class AutoBeAgent<Model extends ILlmSchema.Model> extends AutoBeAgentBase implements IAutoBeAgent { /** @internal */ private readonly props_: IAutoBeProps<Model>; /** @internal */ private readonly agentica_: MicroAgentica<Model>; /** @internal */ private readonly histories_: AutoBeHistory[]; /** @internal */ private readonly context_: AutoBeContext<Model>; /** @internal */ private readonly state_: AutoBeState; /** @internal */ private readonly usage_: AutoBeTokenUsage; /** @internal */ private readonly aggregates_: AutoBeProcessAggregateCollection; /* ----------------------------------------------------------- CONSTRUCTOR ----------------------------------------------------------- */ /** * Initializes a new AutoBeAgent instance with the specified configuration. * * Creates and configures the agent with AI vendor settings, behavioral * context (locale/timezone), and compilation infrastructure. The agent can * optionally resume from previous conversation histories to continue * development sessions or build upon existing work. * * The constructor sets up the internal MicroAgentica engine, initializes the * development state from provided histories, and establishes the event * dispatch system for real-time progress notifications. The agent becomes * ready for conversation-driven development immediately after construction. * * @param props Configuration properties including AI vendor settings, * behavioral context, compilation tools, and optional conversation * histories for session continuation */ public constructor(props: IAutoBeProps<Model>) { // INITIALIZE MEMBERS super(); this.props_ = props; this.histories_ = props.histories?.slice() ?? []; this.state_ = createAutoBeState(this.histories_); this.usage_ = props.tokenUsage instanceof AutoBeTokenUsage ? props.tokenUsage : new AutoBeTokenUsage(props.tokenUsage); // CONSTRUCT AGENTICA const vendor: IAgenticaVendor = { ...props.vendor, semaphore: new Semaphore(props.vendor.semaphore ?? 16), }; const compilerListener: IAutoBeCompilerListener = { realize: { test: { onOperation: async () => {}, onReset: async () => {}, }, }, }; const compiler = new Singleton(async () => props.compiler(compilerListener), ); // CONTEXT this.aggregates_ = !!props.histories?.length ? AutoBeProcessAggregateFactory.reduce( props.histories .filter( (h) => h.type === "analyze" || h.type === "prisma" || h.type === "interface" || h.type === "test" || h.type === "realize", ) .map((h) => h.aggregates), ) : AutoBeProcessAggregateFactory.createCollection(); this.context_ = createAutoBeContext({ model: props.model, vendor: props.vendor, aggregates: this.aggregates_, config: { backoffStrategy: randomBackoffStrategy, ...props.config, }, compiler: () => compiler.get(), compilerListener, state: () => this.state_, files: (options) => this.getFiles(options), histories: () => this.histories_, usage: () => this.usage_, dispatch: (event) => this.dispatch(event), }); // AGENTICA this.agentica_ = new MicroAgentica({ vendor, model: props.model, config: { ...(props.config ?? {}), retry: props.config?.retry ?? AutoBeConfigConstant.RETRY, executor: { describe: null, }, systemPrompt: { common: (config) => getCommonPrompt(config), execute: () => transformFacadeStateMessage(this.state_), }, }, controllers: [ createAutoBeFacadeController({ model: props.model, context: this.getContext(), }), ], }); supportMistral(this.agentica_, props.vendor); this.agentica_.getHistories().push( ...this.histories_ .map((history) => createAgenticaHistory({ operations: this.agentica_.getOperations(), history, }), ) .filter((h) => h !== null), ); // TRACE FACADE TOKEN USAGE let previous: IAgenticaTokenUsageJson.IComponent = this.agentica_ .getTokenUsage() .toJSON().aggregate; const increment = () => { const current: IAgenticaTokenUsageJson.IComponent = this.agentica_ .getTokenUsage() .toJSON().aggregate; this.usage_.facade.increment({ total: current.total - previous.total, input: { total: current.input.total - previous.input.total, cached: current.input.cached - previous.input.cached, }, output: { total: current.output.total - previous.output.total, reasoning: current.output.reasoning - previous.output.reasoning, accepted_prediction: current.output.accepted_prediction - previous.output.accepted_prediction, rejected_prediction: current.output.rejected_prediction - previous.output.rejected_prediction, }, }); previous = current; }; // SHIFT EVENTS this.agentica_.on("assistantMessage", async (message) => { const start = new Date(); const history: AutoBeAssistantMessageHistory = { id: v7(), type: "assistantMessage", text: await message.join(), created_at: start.toISOString(), completed_at: new Date().toISOString(), }; increment(); this.histories_.push(history); this.dispatch({ type: "assistantMessage", id: history.id, text: history.text, created_at: history.created_at, }).catch(() => {}); }); this.agentica_.on("call", async () => { increment(); }); this.agentica_.on("request", (e) => { if (e.body.parallel_tool_calls !== undefined) delete e.body.parallel_tool_calls; void this.dispatch({ ...e, type: "vendorRequest", source: "facade", retry: 0, }).catch(() => {}); }); this.agentica_.on("response", (e) => { void this.dispatch({ ...e, type: "vendorResponse", source: "facade", retry: 0, }).catch(() => {}); }); } /** @internal */ public clone(): AutoBeAgent<Model> { return new AutoBeAgent<Model>({ ...this.props_, histories: this.histories_.slice(), }); } /* ----------------------------------------------------------- ACCESSORS ----------------------------------------------------------- */ public async conversate( content: string | AutoBeUserMessageContent | AutoBeUserMessageContent[], ): Promise<AutoBeHistory[]> { const index: number = this.histories_.length; const userMessageHistory: AutoBeUserMessageHistory = { id: v7(), type: "userMessage", contents: typeof content === "string" ? [ { type: "text", text: content, }, ] : Array.isArray(content) ? content : [content], created_at: new Date().toISOString(), }; this.histories_.push(userMessageHistory); this.dispatch(userMessageHistory).catch(() => {}); const agenticaHistories: MicroAgenticaHistory<Model>[] = await this.agentica_.conversate(content); const errorHistory: AgenticaExecuteHistory<Model> | undefined = agenticaHistories.find( (h): h is AgenticaExecuteHistory<Model> => h.type === "execute" && h.success === false, ); if (errorHistory !== undefined) { console.error("Error from", errorHistory.operation.name); if (errorHistory.value instanceof Error) throw errorHistory.value; else { const v = new Error(); console.log("errorHistory", errorHistory.value); Object.assign(v, errorHistory.value); throw v; } } return this.histories_.slice(index); } public getHistories(): AutoBeHistory[] { return this.histories_; } public getTokenUsage(): AutoBeTokenUsage { return this.usage_; } public async getFiles( options?: IAutoBeGetFilesOptions, ): Promise<Record<string, string>> { return await getAutoBeGenerated({ compiler: await this.context_.compiler(), state: this.state_, histories: this.getHistories(), tokenUsage: this.getTokenUsage(), options, }); } public getAggregates( latest: boolean = false, ): AutoBeProcessAggregateCollection { if (latest === false) return this.aggregates_; const state: AutoBeState = this.context_.state(); return AutoBeProcessAggregateFactory.reduce( [state.analyze, state.prisma, state.interface, state.test, state.realize] .filter((x) => x !== null) .map((x) => x.aggregates), ); } public getPhase(): AutoBePhase | null { if (this.state_.analyze === null) return null; else if (this.state_.realize?.step === this.state_.analyze.step) return "realize"; else if (this.state_.test?.step === this.state_.analyze.step) return "test"; else if (this.state_.interface?.step === this.state_.analyze.step) return "interface"; else if (this.state_.prisma?.step === this.state_.analyze.step) return "prisma"; return "analyze"; } /** @internal */ public getContext(): AutoBeContext<Model> { return this.context_; } }