"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    CompactAndRefine: function() {
        return CompactAndRefine;
    },
    Refine: function() {
        return Refine;
    },
    SimpleResponseBuilder: function() {
        return SimpleResponseBuilder;
    },
    TreeSummarize: function() {
        return TreeSummarize;
    },
    getResponseBuilder: function() {
        return getResponseBuilder;
    }
});
const _utils = require("../llm/utils.js");
const _Prompt = require("../Prompt.js");
const _PromptHelper = require("../PromptHelper.js");
const _Mixin = require("../prompts/Mixin.js");
const _Settings = require("../Settings.js");
var ResponseMode;
/**
 * Response modes of the response synthesizer
 */ (function(ResponseMode) {
    ResponseMode["REFINE"] = "refine";
    ResponseMode["COMPACT"] = "compact";
    ResponseMode["TREE_SUMMARIZE"] = "tree_summarize";
    ResponseMode["SIMPLE"] = "simple";
})(ResponseMode || (ResponseMode = {}));
class SimpleResponseBuilder {
    llm;
    textQATemplate;
    constructor(serviceContext, textQATemplate){
        this.llm = (0, _Settings.llmFromSettingsOrContext)(serviceContext);
        this.textQATemplate = textQATemplate ?? _Prompt.defaultTextQaPrompt;
    }
    async getResponse({ query, textChunks, stream }) {
        const input = {
            query,
            context: textChunks.join("\n\n")
        };
        const prompt = this.textQATemplate(input);
        if (stream) {
            const response = await this.llm.complete({
                prompt,
                stream
            });
            return (0, _utils.streamConverter)(response, (chunk)=>chunk.text);
        } else {
            const response = await this.llm.complete({
                prompt,
                stream
            });
            return response.text;
        }
    }
}
class Refine extends _Mixin.PromptMixin {
    llm;
    promptHelper;
    textQATemplate;
    refineTemplate;
    constructor(serviceContext, textQATemplate, refineTemplate){
        super();
        this.llm = (0, _Settings.llmFromSettingsOrContext)(serviceContext);
        this.promptHelper = (0, _Settings.promptHelperFromSettingsOrContext)(serviceContext);
        this.textQATemplate = textQATemplate ?? _Prompt.defaultTextQaPrompt;
        this.refineTemplate = refineTemplate ?? _Prompt.defaultRefinePrompt;
    }
    _getPrompts() {
        return {
            textQATemplate: this.textQATemplate,
            refineTemplate: this.refineTemplate
        };
    }
    _updatePrompts(prompts) {
        if (prompts.textQATemplate) {
            this.textQATemplate = prompts.textQATemplate;
        }
        if (prompts.refineTemplate) {
            this.refineTemplate = prompts.refineTemplate;
        }
    }
    async getResponse({ query, textChunks, prevResponse, stream }) {
        let response = prevResponse;
        for(let i = 0; i < textChunks.length; i++){
            const chunk = textChunks[i];
            const lastChunk = i === textChunks.length - 1;
            if (!response) {
                response = await this.giveResponseSingle(query, chunk, !!stream && lastChunk);
            } else {
                response = await this.refineResponseSingle(response, query, chunk, !!stream && lastChunk);
            }
        }
        return response ?? "Empty Response";
    }
    async giveResponseSingle(queryStr, textChunk, stream) {
        const textQATemplate = (input)=>this.textQATemplate({
                ...input,
                query: queryStr
            });
        const textChunks = this.promptHelper.repack(textQATemplate, [
            textChunk
        ]);
        let response = undefined;
        for(let i = 0; i < textChunks.length; i++){
            const chunk = textChunks[i];
            const lastChunk = i === textChunks.length - 1;
            if (!response) {
                response = await this.complete({
                    prompt: textQATemplate({
                        context: chunk
                    }),
                    stream: stream && lastChunk
                });
            } else {
                response = await this.refineResponseSingle(response, queryStr, chunk, stream && lastChunk);
            }
        }
        return response;
    }
    // eslint-disable-next-line max-params
    async refineResponseSingle(initialReponse, queryStr, textChunk, stream) {
        const refineTemplate = (input)=>this.refineTemplate({
                ...input,
                query: queryStr
            });
        const textChunks = this.promptHelper.repack(refineTemplate, [
            textChunk
        ]);
        let response = initialReponse;
        for(let i = 0; i < textChunks.length; i++){
            const chunk = textChunks[i];
            const lastChunk = i === textChunks.length - 1;
            response = await this.complete({
                prompt: refineTemplate({
                    context: chunk,
                    existingAnswer: response
                }),
                stream: stream && lastChunk
            });
        }
        return response;
    }
    async complete(params) {
        if (params.stream) {
            const response = await this.llm.complete({
                ...params,
                stream: true
            });
            return (0, _utils.streamConverter)(response, (chunk)=>chunk.text);
        } else {
            const response = await this.llm.complete({
                ...params,
                stream: false
            });
            return response.text;
        }
    }
}
class CompactAndRefine extends Refine {
    async getResponse({ query, textChunks, prevResponse, stream }) {
        const textQATemplate = (input)=>this.textQATemplate({
                ...input,
                query: query
            });
        const refineTemplate = (input)=>this.refineTemplate({
                ...input,
                query: query
            });
        const maxPrompt = (0, _PromptHelper.getBiggestPrompt)([
            textQATemplate,
            refineTemplate
        ]);
        const newTexts = this.promptHelper.repack(maxPrompt, textChunks);
        const params = {
            query,
            textChunks: newTexts,
            prevResponse
        };
        if (stream) {
            return super.getResponse({
                ...params,
                stream
            });
        }
        return super.getResponse(params);
    }
}
class TreeSummarize extends _Mixin.PromptMixin {
    llm;
    promptHelper;
    summaryTemplate;
    constructor(serviceContext, summaryTemplate){
        super();
        this.llm = (0, _Settings.llmFromSettingsOrContext)(serviceContext);
        this.promptHelper = (0, _Settings.promptHelperFromSettingsOrContext)(serviceContext);
        this.summaryTemplate = summaryTemplate ?? _Prompt.defaultTreeSummarizePrompt;
    }
    _getPrompts() {
        return {
            summaryTemplate: this.summaryTemplate
        };
    }
    _updatePrompts(prompts) {
        if (prompts.summaryTemplate) {
            this.summaryTemplate = prompts.summaryTemplate;
        }
    }
    async getResponse({ query, textChunks, stream }) {
        if (!textChunks || textChunks.length === 0) {
            throw new Error("Must have at least one text chunk");
        }
        // Should we send the query here too?
        const packedTextChunks = this.promptHelper.repack(this.summaryTemplate, textChunks);
        if (packedTextChunks.length === 1) {
            const params = {
                prompt: this.summaryTemplate({
                    context: packedTextChunks[0],
                    query
                })
            };
            if (stream) {
                const response = await this.llm.complete({
                    ...params,
                    stream
                });
                return (0, _utils.streamConverter)(response, (chunk)=>chunk.text);
            }
            return (await this.llm.complete(params)).text;
        } else {
            const summaries = await Promise.all(packedTextChunks.map((chunk)=>this.llm.complete({
                    prompt: this.summaryTemplate({
                        context: chunk,
                        query
                    })
                })));
            const params = {
                query,
                textChunks: summaries.map((s)=>s.text)
            };
            if (stream) {
                return this.getResponse({
                    ...params,
                    stream
                });
            }
            return this.getResponse(params);
        }
    }
}
function getResponseBuilder(serviceContext, responseMode) {
    switch(responseMode){
        case "simple":
            return new SimpleResponseBuilder(serviceContext);
        case "refine":
            return new Refine(serviceContext);
        case "tree_summarize":
            return new TreeSummarize(serviceContext);
        default:
            return new CompactAndRefine(serviceContext);
    }
}
