"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAutoBeContext = void 0;
const core_1 = require("@agentica/core");
const tstl_1 = require("tstl");
const typia_1 = __importDefault(require("typia"));
const uuid_1 = require("uuid");
const AutoBeTokenUsageComponent_1 = require("../context/AutoBeTokenUsageComponent");
const AutoBeTimeoutError_1 = require("../utils/AutoBeTimeoutError");
const TimedConversation_1 = require("../utils/TimedConversation");
const consentFunctionCall_1 = require("./consentFunctionCall");
const getCriticalCompiler_1 = require("./getCriticalCompiler");
const createAutoBeContext = (props) => {
    var _a, _b, _c;
    const config = {
        retry: (_a = props.config.retry) !== null && _a !== void 0 ? _a : 4 /* AutoBeConfigConstant.RETRY */,
        locale: (_b = props.config.locale) !== null && _b !== void 0 ? _b : "en-US",
        timeout: (_c = props.config.timeout) !== null && _c !== void 0 ? _c : null,
    };
    const critical = new tstl_1.Semaphore(2);
    return {
        model: props.model,
        vendor: props.vendor,
        retry: config.retry,
        locale: config.locale,
        compilerListener: props.compilerListener,
        compiler: () => __awaiter(void 0, void 0, void 0, function* () {
            const compiler = yield props.compiler();
            return (0, getCriticalCompiler_1.getCriticalCompiler)(critical, compiler);
        }),
        files: props.files,
        histories: props.histories,
        state: props.state,
        usage: props.usage,
        dispatch: createDispatch(props),
        assistantMessage: (message) => {
            props.histories().push(message);
            setTimeout(() => {
                void props.dispatch(message).catch(() => { });
            });
            return message;
        },
        conversate: (next, closure) => __awaiter(void 0, void 0, void 0, function* () {
            const trial = {
                request: 0,
                response: 0,
                timeout: 0,
            };
            const execute = () => __awaiter(void 0, void 0, void 0, function* () {
                var _a, _b, _c, _d, _e;
                // CREATE AGENT
                const agent = new core_1.MicroAgentica({
                    model: props.model,
                    vendor: props.vendor,
                    config: Object.assign(Object.assign({}, ((_a = props.config) !== null && _a !== void 0 ? _a : {})), { retry: (_c = (_b = props.config) === null || _b === void 0 ? void 0 : _b.retry) !== null && _c !== void 0 ? _c : 4 /* AutoBeConfigConstant.RETRY */, executor: {
                            describe: null,
                        } }),
                    histories: next.histories,
                    controllers: [next.controller],
                });
                // ADD EVENT LISTENERS
                agent.on("request", (event) => __awaiter(void 0, void 0, void 0, function* () {
                    if (next.enforceFunctionCall === true && event.body.tools)
                        event.body.tool_choice = "required";
                    if (event.body.parallel_tool_calls !== undefined)
                        delete event.body.parallel_tool_calls;
                    if (next.promptCacheKey)
                        event.body.prompt_cache_key = next.promptCacheKey;
                    yield props.dispatch(Object.assign(Object.assign({}, event), { type: "vendorRequest", source: next.source, retry: trial.request++ }));
                }));
                agent.on("response", (event) => __awaiter(void 0, void 0, void 0, function* () {
                    void props
                        .dispatch(Object.assign(Object.assign({}, event), { type: "vendorResponse", source: next.source, retry: trial.response++ }))
                        .catch(() => { });
                }));
                agent.on("jsonParseError", (event) => {
                    void props
                        .dispatch(Object.assign(Object.assign({}, event), { source: next.source }))
                        .catch(() => { });
                });
                agent.on("validate", (event) => {
                    void props
                        .dispatch({
                        type: "jsonValidateError",
                        id: (0, uuid_1.v7)(),
                        source: next.source,
                        result: event.result,
                        life: event.life,
                        created_at: event.created_at,
                    })
                        .catch(() => { });
                });
                if (closure)
                    closure(agent);
                // DO CONVERSATE
                const result = yield TimedConversation_1.TimedConversation.process({
                    agent,
                    timeout: config.timeout,
                    message: next.message,
                });
                const tokenUsage = agent
                    .getTokenUsage()
                    .toJSON().aggregate;
                props
                    .usage()
                    .record(tokenUsage, [
                    (_d = STAGES.find((stage) => next.source.startsWith(stage))) !== null && _d !== void 0 ? _d : "analyze",
                ]);
                if (result.type === "error")
                    throw result.error;
                else if (result.type === "timeout") {
                    void props
                        .dispatch({
                        type: "vendorTimeout",
                        id: (0, uuid_1.v7)(),
                        source: next.source,
                        timeout: config.timeout,
                        retry: trial.timeout++,
                        created_at: new Date().toISOString(),
                    })
                        .catch(() => { });
                    throw result.error;
                }
                else if (true === next.enforceFunctionCall &&
                    false ===
                        result.histories.some((h) => h.type === "execute" && h.success === true)) {
                    const failure = () => {
                        throw new Error(`Failed to function calling in the ${next.source} step`);
                    };
                    const last = result.histories.at(-1);
                    if ((last === null || last === void 0 ? void 0 : last.type) === "assistantMessage" &&
                        last.text.trim().length !== 0) {
                        const consent = yield (0, consentFunctionCall_1.consentFunctionCall)({
                            source: next.source,
                            dispatch: (e) => {
                                props.dispatch(e).catch(() => { });
                            },
                            config: props.config,
                            vendor: props.vendor,
                            assistantMessage: last.text,
                        });
                        if (consent !== null) {
                            const newHistories = yield agent.conversate(consent);
                            const newTokenUsage = agent
                                .getTokenUsage()
                                .toJSON().aggregate;
                            props
                                .usage()
                                .record(AutoBeTokenUsageComponent_1.AutoBeTokenUsageComponent.minus(new AutoBeTokenUsageComponent_1.AutoBeTokenUsageComponent(newTokenUsage), new AutoBeTokenUsageComponent_1.AutoBeTokenUsageComponent(tokenUsage)), [
                                (_e = STAGES.find((stage) => next.source.startsWith(stage))) !== null && _e !== void 0 ? _e : "analyze",
                            ]);
                            if (newHistories.some((h) => h.type === "execute" && h.success === true))
                                return {
                                    histories: newHistories,
                                    tokenUsage: newTokenUsage,
                                };
                        }
                    }
                    failure();
                }
                return {
                    histories: result.histories,
                    tokenUsage,
                };
            });
            if (next.enforceFunctionCall === true)
                return yield forceRetry(execute, config.retry);
            else
                return yield execute();
        }),
    };
};
exports.createAutoBeContext = createAutoBeContext;
const createDispatch = (props) => {
    let analyzeStart = null;
    let prismaStart = null;
    let interfaceStart = null;
    let testStart = null;
    let realizeStart = null;
    return (event) => {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        // starts
        if (event.type === "analyzeStart")
            analyzeStart = event;
        else if (event.type === "prismaStart")
            prismaStart = event;
        else if (event.type === "interfaceStart")
            interfaceStart = event;
        else if (event.type === "testStart")
            testStart = event;
        else if (event.type === "realizeStart")
            realizeStart = event;
        // completes
        else if (event.type === "analyzeComplete")
            return transformAndDispatch({
                dispatch: props.dispatch,
                histories: props.histories,
                state: props.state,
                event,
                history: {
                    type: "analyze",
                    id: (0, uuid_1.v7)(),
                    instruction: (_a = analyzeStart === null || analyzeStart === void 0 ? void 0 : analyzeStart.reason) !== null && _a !== void 0 ? _a : "",
                    prefix: event.prefix,
                    roles: event.roles,
                    files: event.files,
                    created_at: (_b = analyzeStart === null || analyzeStart === void 0 ? void 0 : analyzeStart.created_at) !== null && _b !== void 0 ? _b : new Date().toISOString(),
                    completed_at: event.created_at,
                    step: event.step,
                },
            });
        else if (event.type === "prismaComplete")
            return transformAndDispatch({
                dispatch: props.dispatch,
                histories: props.histories,
                state: props.state,
                event,
                history: {
                    type: "prisma",
                    id: (0, uuid_1.v7)(),
                    instruction: (_c = prismaStart === null || prismaStart === void 0 ? void 0 : prismaStart.reason) !== null && _c !== void 0 ? _c : "",
                    schemas: event.schemas,
                    result: event.result,
                    compiled: event.compiled,
                    created_at: (_d = prismaStart === null || prismaStart === void 0 ? void 0 : prismaStart.created_at) !== null && _d !== void 0 ? _d : new Date().toISOString(),
                    completed_at: event.created_at,
                    step: event.step,
                },
            });
        else if (event.type === "interfaceComplete")
            return transformAndDispatch({
                dispatch: props.dispatch,
                histories: props.histories,
                state: props.state,
                event,
                history: {
                    type: "interface",
                    id: (0, uuid_1.v7)(),
                    instruction: (_e = interfaceStart === null || interfaceStart === void 0 ? void 0 : interfaceStart.reason) !== null && _e !== void 0 ? _e : "",
                    authorizations: event.authorizations,
                    document: event.document,
                    created_at: (_f = interfaceStart === null || interfaceStart === void 0 ? void 0 : interfaceStart.created_at) !== null && _f !== void 0 ? _f : new Date().toISOString(),
                    completed_at: new Date().toISOString(),
                    step: event.step,
                },
            });
        else if (event.type === "testComplete")
            return transformAndDispatch({
                dispatch: props.dispatch,
                histories: props.histories,
                state: props.state,
                event,
                history: {
                    type: "test",
                    id: (0, uuid_1.v7)(),
                    instruction: (_g = testStart === null || testStart === void 0 ? void 0 : testStart.reason) !== null && _g !== void 0 ? _g : "",
                    files: event.files,
                    compiled: event.compiled,
                    created_at: (_h = testStart === null || testStart === void 0 ? void 0 : testStart.created_at) !== null && _h !== void 0 ? _h : new Date().toISOString(),
                    completed_at: new Date().toISOString(),
                    step: event.step,
                },
            });
        else if (event.type === "realizeComplete")
            return transformAndDispatch({
                dispatch: props.dispatch,
                histories: props.histories,
                state: props.state,
                event,
                history: {
                    type: "realize",
                    id: (0, uuid_1.v7)(),
                    instruction: (_j = realizeStart === null || realizeStart === void 0 ? void 0 : realizeStart.reason) !== null && _j !== void 0 ? _j : "",
                    authorizations: event.authorizations,
                    functions: event.functions,
                    controllers: event.controllers,
                    compiled: event.compiled,
                    created_at: (_k = realizeStart === null || realizeStart === void 0 ? void 0 : realizeStart.created_at) !== null && _k !== void 0 ? _k : new Date().toISOString(),
                    completed_at: new Date().toISOString(),
                    step: event.step,
                },
            });
        void props.dispatch(event).catch(() => { });
        return null;
    };
};
const transformAndDispatch = (props) => {
    props.histories().push(props.history);
    props.state()[props.history.type] = props.history;
    void props.dispatch(props.event).catch(() => { });
    return props.history;
};
const forceRetry = (task, count) => __awaiter(void 0, void 0, void 0, function* () {
    let error = undefined;
    for (let i = 0; i < count; ++i)
        try {
            return yield task();
        }
        catch (e) {
            if (e instanceof AutoBeTimeoutError_1.AutoBeTimeoutError)
                throw e;
            error = e;
        }
    throw error;
});
const STAGES = [
    "analyze",
    "prisma",
    "interface",
    "test",
    "realize"
];
//# sourceMappingURL=createAutoBeContext.js.map