UNPKG

@agentica/core

Version:

Agentic AI Library specialized in LLM Function Calling

346 lines 13.9 kB
"use strict"; 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.MicroAgentica = void 0; const tstl_1 = require("tstl"); const uuid_1 = require("uuid"); const AgenticaTokenUsage_1 = require("./context/AgenticaTokenUsage"); const AgenticaOperationComposer_1 = require("./context/internal/AgenticaOperationComposer"); const AgenticaTokenUsageAggregator_1 = require("./context/internal/AgenticaTokenUsageAggregator"); const events_1 = require("./factory/events"); const orchestrate_1 = require("./orchestrate"); const transformHistory_1 = require("./transformers/transformHistory"); const __map_take_1 = require("./utils/__map_take"); const ChatGptCompletionMessageUtil_1 = require("./utils/ChatGptCompletionMessageUtil"); const StreamUtil_1 = require("./utils/StreamUtil"); /** * Micro AI chatbot. * * `MicroAgentica` is a facade class for the micro AI chatbot agent * which performs LLM (Large Language Model) function calling from the * {@link conversate user's conversation} and manages the * {@link getHistories prompt histories}. * * Different between `MicroAgentica` and {@link Agentica} is that * `MicroAgentica` does not have function selecting filter. It directly * list up every functions to the agent. Besides, {@link Agentica} has * a function selecting mechanism to reduce the number of functions to * be listed up to the agent. * * Therefore, if you have a lot of functions to call, you must not * use this `MicroAgentica` class. Use this `MicroAgentica` class only * when you have a few functions to call. * * - [Multi-agent orchestration of `@agentica`](https://wrtnlabs.io/agentica/docs/concepts/function-calling/#orchestration-strategy) * - Internal agents of `MicroAgentica` * - executor * - describier * - Internal agents of {@link Agentica} * - initializer * - **selector** * - executor * - describer * * @author Samchon */ class MicroAgentica { /* ----------------------------------------------------------- CONSTRUCTOR ----------------------------------------------------------- */ /** * Initializer Constructor. * * @param props Properties to construct the micro agent */ constructor(props) { var _a; this.props = props; this.operations_ = AgenticaOperationComposer_1.AgenticaOperationComposer.compose({ controllers: props.controllers, config: props.config, }); this.histories_ = ((_a = props.histories) !== null && _a !== void 0 ? _a : []).map(input => (0, transformHistory_1.transformHistory)({ operations: this.operations_.group, history: input, })); this.token_usage_ = this.props.tokenUsage !== undefined ? this.props.tokenUsage instanceof AgenticaTokenUsage_1.AgenticaTokenUsage ? this.props.tokenUsage : new AgenticaTokenUsage_1.AgenticaTokenUsage(this.props.tokenUsage) : AgenticaTokenUsage_1.AgenticaTokenUsage.zero(); this.listeners_ = new Map(); this.semaphore_ = props.vendor.semaphore != null ? typeof props.vendor.semaphore === "object" ? props.vendor.semaphore : new tstl_1.Semaphore(props.vendor.semaphore) : null; } /** * @internal */ clone() { var _a; return new MicroAgentica(Object.assign(Object.assign({}, this.props), { histories: (_a = this.props.histories) === null || _a === void 0 ? void 0 : _a.slice() })); } /* ----------------------------------------------------------- ACCESSORS ----------------------------------------------------------- */ /** * Conversate with the micro agent. * * User talks to the AI chatbot with the given content. * * When the user's conversation implies the AI chatbot to execute a * function calling, the returned chat prompts will contain the * function callinng information like {@link AgenticaExecuteHistory} * * @param content The content to talk * @returns List of newly created histories */ conversate(content) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; const histories = []; const dispatch = (event) => { this.dispatch(event).catch(() => { }); if ("toHistory" in event) { if ("join" in event) { histories.push(() => __awaiter(this, void 0, void 0, function* () { yield event.join(); return event.toHistory(); })); } else { histories.push(() => __awaiter(this, void 0, void 0, function* () { return event.toHistory(); })); } } }; const prompt = (0, events_1.createUserMessageEvent)({ contents: Array.isArray(content) ? content : typeof content === "string" ? [{ type: "text", text: content, }] : [content], }); dispatch(prompt); const ctx = this.getContext({ prompt, dispatch, usage: this.token_usage_, }); const executes = yield (0, orchestrate_1.call)(ctx, this.operations_.array); if (executes.length && ((_b = (_a = this.props.config) === null || _a === void 0 ? void 0 : _a.executor) === null || _b === void 0 ? void 0 : _b.describe) !== null && ((_d = (_c = this.props.config) === null || _c === void 0 ? void 0 : _c.executor) === null || _d === void 0 ? void 0 : _d.describe) !== false) { yield (0, orchestrate_1.describe)(ctx, executes); } const completed = yield Promise.all(histories.map((h) => __awaiter(this, void 0, void 0, function* () { return h(); }))); this.histories_.push(...completed); return completed; }); } /** * Get configuration. */ getConfig() { return this.props.config; } /** * Get LLM vendor. */ getVendor() { return this.props.vendor; } /** * Get operations. * * Get list of operations, which has capsuled the pair of controller * and function from the {@link getControllers controllers}. * * @returns List of operations */ getOperations() { return this.operations_.array; } /** * Get controllers. * * Get list of controllers, which are the collection of functions that * the agent can execute. */ getControllers() { return this.props.controllers; } /** * Get the chatbot's histories. * * Get list of chat histories that the chatbot has been conversated. * * @returns List of chat histories */ getHistories() { return this.histories_; } /** * Get token usage of the AI chatbot. * * Entire token usage of the AI chatbot during the conversating * with the user by {@link conversate} method callings. * * @returns Cost of the AI chatbot */ getTokenUsage() { return this.token_usage_; } /** * @internal */ getContext(props) { const request = (source, body) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const event = (0, events_1.createRequestEvent)({ source, body: Object.assign(Object.assign({}, body), { model: this.props.vendor.model, stream: true, stream_options: { include_usage: true, } }), options: this.props.vendor.options, }); props.dispatch(event); // completion const backoffStrategy = (_b = (_a = this.props.config) === null || _a === void 0 ? void 0 : _a.backoffStrategy) !== null && _b !== void 0 ? _b : ((props) => { throw props.error; }); const completion = yield (() => __awaiter(this, void 0, void 0, function* () { let count = 0; while (true) { try { return yield this.props.vendor.api.chat.completions.create(event.body, event.options); } catch (error) { const waiting = backoffStrategy({ count, error }); yield new Promise(resolve => setTimeout(resolve, waiting)); count++; } } }))(); const [streamForEvent, temporaryStream] = StreamUtil_1.StreamUtil.transform(completion.toReadableStream(), value => ChatGptCompletionMessageUtil_1.ChatGptCompletionMessageUtil.transformCompletionChunk(value)).tee(); const [streamForAggregate, streamForReturn] = temporaryStream.tee(); void (() => __awaiter(this, void 0, void 0, function* () { const reader = streamForAggregate.getReader(); while (true) { const chunk = yield reader.read(); if (chunk.done) { break; } if (chunk.value.usage != null) { AgenticaTokenUsageAggregator_1.AgenticaTokenUsageAggregator.aggregate({ kind: source, completionUsage: chunk.value.usage, usage: props.usage, }); } } }))().catch(() => { }); const [streamForStream, streamForJoin] = streamForEvent.tee(); props.dispatch({ id: (0, uuid_1.v4)(), type: "response", source, stream: (0, StreamUtil_1.streamDefaultReaderToAsyncGenerator)(streamForStream.getReader()), body: event.body, options: event.options, join: () => __awaiter(this, void 0, void 0, function* () { const chunks = yield StreamUtil_1.StreamUtil.readAll(streamForJoin); return ChatGptCompletionMessageUtil_1.ChatGptCompletionMessageUtil.merge(chunks); }), created_at: new Date().toISOString(), }); return streamForReturn; }); return { operations: this.operations_, config: this.props.config, histories: this.histories_, prompt: props.prompt, dispatch: props.dispatch, request: this.semaphore_ === null ? request : (source, body) => __awaiter(this, void 0, void 0, function* () { yield this.semaphore_.acquire(); try { return yield request(source, body); } finally { void this.semaphore_.release().catch(() => { }); } }), }; } /* ----------------------------------------------------------- EVENT HANDLERS ----------------------------------------------------------- */ /** * Add an event listener. * * Add an event listener to be called whenever the event is emitted. * * @param type Type of event * @param listener Callback function to be called whenever the event is emitted */ on(type, listener) { /** * @TODO remove `as` */ (0, __map_take_1.__map_take)(this.listeners_, type, () => new Set()).add(listener); return this; } /** * Erase an event listener. * * Erase an event listener to stop calling the callback function. * * @param type Type of event * @param listener Callback function to erase */ off(type, listener) { const set = this.listeners_.get(type); if (set !== undefined) { /** * @TODO remove `as` */ set.delete(listener); if (set.size === 0) { this.listeners_.delete(type); } } return this; } dispatch(event) { return __awaiter(this, void 0, void 0, function* () { const set = this.listeners_.get(event.type); if (set !== undefined) { yield Promise.all(Array.from(set).map((listener) => __awaiter(this, void 0, void 0, function* () { try { yield listener(event); } catch (_a) { /* empty */ } }))); } }); } } exports.MicroAgentica = MicroAgentica; //# sourceMappingURL=MicroAgentica.js.map