@agentica/core
Version:
Agentic AI Library specialized in LLM Function Calling
346 lines • 13.9 kB
JavaScript
"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