UNPKG

llamaindex

Version:

<p align="center"> <img height="100" width="100" alt="LlamaIndex logo" src="https://ts.llamaindex.ai/square.svg" /> </p> <h1 align="center">LlamaIndex.TS</h1> <h3 align="center"> Data framework for your LLM application. </h3>

896 lines (887 loc) 31.9 kB
Object.defineProperty(exports, '__esModule', { value: true }); var chatEngine = require('@llamaindex/core/chat-engine'); var decorator = require('@llamaindex/core/decorator'); var memory = require('@llamaindex/core/memory'); var prompts = require('@llamaindex/core/prompts'); var utils = require('@llamaindex/core/utils'); var global = require('@llamaindex/core/global'); var indices = require('@llamaindex/core/indices'); var nodeParser = require('@llamaindex/core/node-parser'); var env = require('@llamaindex/env'); var queryEngine = require('@llamaindex/core/query-engine'); var responseSynthesizers = require('@llamaindex/core/response-synthesizers'); var index_cjs = require('../../selectors/dist/index.cjs'); var schema = require('@llamaindex/core/schema'); var openai = require('@llamaindex/openai'); /** * @internal */ class GlobalSettings { #prompt; #promptHelper; #nodeParser; #chunkOverlap; #promptHelperAsyncLocalStorage; #nodeParserAsyncLocalStorage; #chunkOverlapAsyncLocalStorage; #promptAsyncLocalStorage; get debug() { return global.Settings.debug; } get llm() { return global.Settings.llm; } set llm(llm) { global.Settings.llm = llm; } withLLM(llm, fn) { return global.Settings.withLLM(llm, fn); } get promptHelper() { if (this.#promptHelper === null) { this.#promptHelper = new indices.PromptHelper(); } return this.#promptHelperAsyncLocalStorage.getStore() ?? this.#promptHelper; } set promptHelper(promptHelper) { this.#promptHelper = promptHelper; } withPromptHelper(promptHelper, fn) { return this.#promptHelperAsyncLocalStorage.run(promptHelper, fn); } get embedModel() { return global.Settings.embedModel; } set embedModel(embedModel) { global.Settings.embedModel = embedModel; } withEmbedModel(embedModel, fn) { return global.Settings.withEmbedModel(embedModel, fn); } get nodeParser() { if (this.#nodeParser === null) { this.#nodeParser = new nodeParser.SentenceSplitter({ chunkSize: this.chunkSize, chunkOverlap: this.chunkOverlap }); } return this.#nodeParserAsyncLocalStorage.getStore() ?? this.#nodeParser; } set nodeParser(nodeParser) { this.#nodeParser = nodeParser; } withNodeParser(nodeParser, fn) { return this.#nodeParserAsyncLocalStorage.run(nodeParser, fn); } get callbackManager() { return global.Settings.callbackManager; } set callbackManager(callbackManager) { global.Settings.callbackManager = callbackManager; } withCallbackManager(callbackManager, fn) { return global.Settings.withCallbackManager(callbackManager, fn); } set chunkSize(chunkSize) { global.Settings.chunkSize = chunkSize; } get chunkSize() { return global.Settings.chunkSize; } withChunkSize(chunkSize, fn) { return global.Settings.withChunkSize(chunkSize, fn); } get chunkOverlap() { return this.#chunkOverlapAsyncLocalStorage.getStore() ?? this.#chunkOverlap; } set chunkOverlap(chunkOverlap) { if (typeof chunkOverlap === "number") { this.#chunkOverlap = chunkOverlap; } } withChunkOverlap(chunkOverlap, fn) { return this.#chunkOverlapAsyncLocalStorage.run(chunkOverlap, fn); } get prompt() { return this.#promptAsyncLocalStorage.getStore() ?? this.#prompt; } set prompt(prompt) { this.#prompt = prompt; } withPrompt(prompt, fn) { return this.#promptAsyncLocalStorage.run(prompt, fn); } constructor(){ this.#prompt = {}; this.#promptHelper = null; this.#nodeParser = null; this.#promptHelperAsyncLocalStorage = new env.AsyncLocalStorage(); this.#nodeParserAsyncLocalStorage = new env.AsyncLocalStorage(); this.#chunkOverlapAsyncLocalStorage = new env.AsyncLocalStorage(); this.#promptAsyncLocalStorage = new env.AsyncLocalStorage(); } } const Settings = new GlobalSettings(); function applyDecs2203RFactory() { function createAddInitializerMethod(initializers, decoratorFinishedRef) { return function addInitializer(initializer) { assertNotFinished(decoratorFinishedRef, "addInitializer"); assertCallable(initializer, "An initializer"); initializers.push(initializer); }; } function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) { var kindStr; switch(kind){ case 1: kindStr = "accessor"; break; case 2: kindStr = "method"; break; case 3: kindStr = "getter"; break; case 4: kindStr = "setter"; break; default: kindStr = "field"; } var ctx = { kind: kindStr, name: isPrivate ? "#" + name : name, static: isStatic, private: isPrivate, metadata: metadata }; var decoratorFinishedRef = { v: false }; ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef); var get, set; if (kind === 0) { if (isPrivate) { get = desc.get; set = desc.set; } else { get = function() { return this[name]; }; set = function(v) { this[name] = v; }; } } else if (kind === 2) { get = function() { return desc.value; }; } else { if (kind === 1 || kind === 3) { get = function() { return desc.get.call(this); }; } if (kind === 1 || kind === 4) { set = function(v) { desc.set.call(this, v); }; } } ctx.access = get && set ? { get: get, set: set } : get ? { get: get } : { set: set }; try { return dec(value, ctx); } finally{ decoratorFinishedRef.v = true; } } function assertNotFinished(decoratorFinishedRef, fnName) { if (decoratorFinishedRef.v) { throw new Error("attempted to call " + fnName + " after decoration was finished"); } } function assertCallable(fn, hint) { if (typeof fn !== "function") { throw new TypeError(hint + " must be a function"); } } function assertValidReturnValue(kind, value) { var type = typeof value; if (kind === 1) { if (type !== "object" || value === null) { throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } if (value.get !== undefined) { assertCallable(value.get, "accessor.get"); } if (value.set !== undefined) { assertCallable(value.set, "accessor.set"); } if (value.init !== undefined) { assertCallable(value.init, "accessor.init"); } } else if (type !== "function") { var hint; if (kind === 0) { hint = "field"; } else if (kind === 10) { hint = "class"; } else { hint = "method"; } throw new TypeError(hint + " decorators must return a function or void 0"); } } function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) { var decs = decInfo[0]; var desc, init, value; if (isPrivate) { if (kind === 0 || kind === 1) { desc = { get: decInfo[3], set: decInfo[4] }; } else if (kind === 3) { desc = { get: decInfo[3] }; } else if (kind === 4) { desc = { set: decInfo[3] }; } else { desc = { value: decInfo[3] }; } } else if (kind !== 0) { desc = Object.getOwnPropertyDescriptor(base, name); } if (kind === 1) { value = { get: desc.get, set: desc.set }; } else if (kind === 2) { value = desc.value; } else if (kind === 3) { value = desc.get; } else if (kind === 4) { value = desc.set; } var newValue, get, set; if (typeof decs === "function") { newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value); if (newValue !== void 0) { assertValidReturnValue(kind, newValue); if (kind === 0) { init = newValue; } else if (kind === 1) { init = newValue.init; get = newValue.get || value.get; set = newValue.set || value.set; value = { get: get, set: set }; } else { value = newValue; } } } else { for(var i = decs.length - 1; i >= 0; i--){ var dec = decs[i]; newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value); if (newValue !== void 0) { assertValidReturnValue(kind, newValue); var newInit; if (kind === 0) { newInit = newValue; } else if (kind === 1) { newInit = newValue.init; get = newValue.get || value.get; set = newValue.set || value.set; value = { get: get, set: set }; } else { value = newValue; } if (newInit !== void 0) { if (init === void 0) { init = newInit; } else if (typeof init === "function") { init = [ init, newInit ]; } else { init.push(newInit); } } } } } if (kind === 0 || kind === 1) { if (init === void 0) { init = function(instance, init) { return init; }; } else if (typeof init !== "function") { var ownInitializers = init; init = function(instance, init) { var value = init; for(var i = 0; i < ownInitializers.length; i++){ value = ownInitializers[i].call(instance, value); } return value; }; } else { var originalInitializer = init; init = function(instance, init) { return originalInitializer.call(instance, init); }; } ret.push(init); } if (kind !== 0) { if (kind === 1) { desc.get = value.get; desc.set = value.set; } else if (kind === 2) { desc.value = value; } else if (kind === 3) { desc.get = value; } else if (kind === 4) { desc.set = value; } if (isPrivate) { if (kind === 1) { ret.push(function(instance, args) { return value.get.call(instance, args); }); ret.push(function(instance, args) { return value.set.call(instance, args); }); } else if (kind === 2) { ret.push(value); } else { ret.push(function(instance, args) { return value.call(instance, args); }); } } else { Object.defineProperty(base, name, desc); } } } function applyMemberDecs(Class, decInfos, metadata) { var ret = []; var protoInitializers; var staticInitializers; var existingProtoNonFields = new Map(); var existingStaticNonFields = new Map(); for(var i = 0; i < decInfos.length; i++){ var decInfo = decInfos[i]; if (!Array.isArray(decInfo)) continue; var kind = decInfo[1]; var name = decInfo[2]; var isPrivate = decInfo.length > 3; var isStatic = kind >= 5; var base; var initializers; if (isStatic) { base = Class; kind = kind - 5; staticInitializers = staticInitializers || []; initializers = staticInitializers; } else { base = Class.prototype; protoInitializers = protoInitializers || []; initializers = protoInitializers; } if (kind !== 0 && !isPrivate) { var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields; var existingKind = existingNonFields.get(name) || 0; if (existingKind === true || existingKind === 3 && kind !== 4 || existingKind === 4 && kind !== 3) { throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + name); } else if (!existingKind && kind > 2) { existingNonFields.set(name, kind); } else { existingNonFields.set(name, true); } } applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata); } pushInitializers(ret, protoInitializers); pushInitializers(ret, staticInitializers); return ret; } function pushInitializers(ret, initializers) { if (initializers) { ret.push(function(instance) { for(var i = 0; i < initializers.length; i++){ initializers[i].call(instance); } return instance; }); } } function applyClassDecs(targetClass, classDecs, metadata) { if (classDecs.length > 0) { var initializers = []; var newClass = targetClass; var name = targetClass.name; for(var i = classDecs.length - 1; i >= 0; i--){ var decoratorFinishedRef = { v: false }; try { var nextNewClass = classDecs[i](newClass, { kind: "class", name: name, addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef), metadata }); } finally{ decoratorFinishedRef.v = true; } if (nextNewClass !== undefined) { assertValidReturnValue(10, nextNewClass); newClass = nextNewClass; } } return [ defineMetadata(newClass, metadata), function() { for(var i = 0; i < initializers.length; i++){ initializers[i].call(newClass); } } ]; } } function defineMetadata(Class, metadata) { return Object.defineProperty(Class, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: true, enumerable: true, value: metadata }); } return function applyDecs2203R(targetClass, memberDecs, classDecs, parentClass) { if (parentClass !== void 0) { var parentMetadata = parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")]; } var metadata = Object.create(parentMetadata === void 0 ? null : parentMetadata); var e = applyMemberDecs(targetClass, memberDecs, metadata); if (!classDecs.length) defineMetadata(targetClass, metadata); return { e: e, get c () { return applyClassDecs(targetClass, classDecs, metadata); } }; }; } function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass); } var _initProto; /** * CondenseQuestionChatEngine is used in conjunction with a Index (for example VectorStoreIndex). * It does two steps on taking a user's chat message: first, it condenses the chat message * with the previous chat history into a question with more context. * Then, it queries the underlying Index using the new question with context and returns * the response. * CondenseQuestionChatEngine performs well when the input is primarily questions about the * underlying data. It performs less well when the chat messages are not questions about the * data, or are very referential to previous context. */ class CondenseQuestionChatEngine extends chatEngine.BaseChatEngine { static{ ({ e: [_initProto] } = _apply_decs_2203_r(this, [ [ decorator.wrapEventCaller, 2, "chat" ] ], [])); } get chatHistory() { return this.memory.getMessages(); } constructor(init){ super(), _initProto(this); this.queryEngine = init.queryEngine; this.memory = new memory.ChatMemoryBuffer({ chatHistory: init?.chatHistory }); this.llm = Settings.llm; this.condenseMessagePrompt = init?.condenseMessagePrompt ?? prompts.defaultCondenseQuestionPrompt; } _getPromptModules() { return {}; } _getPrompts() { return { condenseMessagePrompt: this.condenseMessagePrompt }; } _updatePrompts(promptsDict) { if (promptsDict.condenseMessagePrompt) { this.condenseMessagePrompt = promptsDict.condenseMessagePrompt; } } async condenseQuestion(chatHistory, question) { const chatHistoryStr = utils.messagesToHistory(await chatHistory.getMessages()); return this.llm.complete({ prompt: this.condenseMessagePrompt.format({ question: question, chatHistory: chatHistoryStr }) }); } async chat(params) { const { message, stream } = params; const chatHistory = params.chatHistory ? new memory.ChatMemoryBuffer({ chatHistory: params.chatHistory instanceof memory.BaseMemory ? await params.chatHistory.getMessages() : params.chatHistory }) : this.memory; const condensedQuestion = (await this.condenseQuestion(chatHistory, utils.extractText(message))).text; chatHistory.put({ content: message, role: "user" }); if (stream) { const stream = await this.queryEngine.query({ query: condensedQuestion, stream: true }); return utils.streamReducer({ stream, initialValue: "", reducer: (accumulator, part)=>accumulator += utils.extractText(part.message.content), finished: (accumulator)=>{ chatHistory.put({ content: accumulator, role: "assistant" }); } }); } const response = await this.queryEngine.query({ query: condensedQuestion }); chatHistory.put({ content: response.message.content, role: "assistant" }); return response; } reset() { this.memory.reset(); } } async function combineResponses(summarizer, responses, queryBundle, verbose = false) { if (verbose) { console.log("Combining responses from multiple query engines."); } const sourceNodes = []; for (const response of responses){ if (response?.sourceNodes) { sourceNodes.push(...response.sourceNodes); } } return await summarizer.synthesize({ query: queryBundle, nodes: sourceNodes }); } /** * A query engine that uses multiple query engines and selects the best one. */ class RouterQueryEngine extends queryEngine.BaseQueryEngine { constructor(init){ super(); this.selector = init.selector; this.queryEngines = init.queryEngineTools.map((tool)=>tool.queryEngine); this.metadatas = init.queryEngineTools.map((tool)=>({ description: tool.description })); this.summarizer = init.summarizer || responseSynthesizers.getResponseSynthesizer("tree_summarize"); this.verbose = init.verbose ?? false; } async _query(strOrQueryBundle, stream) { const response = await this.queryRoute(typeof strOrQueryBundle === "string" ? { query: strOrQueryBundle } : strOrQueryBundle); if (stream) { throw new Error("Streaming is not supported yet."); } return response; } _getPrompts() { return {}; } _updatePrompts() {} _getPromptModules() { return { selector: this.selector, summarizer: this.summarizer }; } static fromDefaults(init) { return new RouterQueryEngine({ selector: init.selector ?? new index_cjs.LLMSingleSelector({ llm: Settings.llm }), queryEngineTools: init.queryEngineTools, summarizer: init.summarizer, verbose: init.verbose }); } async queryRoute(query) { const result = await this.selector.select(this.metadatas, query); if (result.selections.length > 1) { const responses = []; for(let i = 0; i < result.selections.length; i++){ const engineInd = result.selections[i]; const logStr = `Selecting query engine ${engineInd.index}: ${result.selections[i].index}.`; if (this.verbose) { console.log(logStr + "\n"); } const selectedQueryEngine = this.queryEngines[engineInd.index]; responses.push(await selectedQueryEngine.query({ query, stream: false })); } if (responses.length > 1) { const finalResponse = await combineResponses(this.summarizer, responses, query, this.verbose); return finalResponse; } else { return responses[0]; } } else { let selectedQueryEngine; try { selectedQueryEngine = this.queryEngines[result.selections[0].index]; const logStr = `Selecting query engine ${result.selections[0].index}: ${result.selections[0].reason}`; if (this.verbose) { console.log(logStr + "\n"); } } catch (e) { throw new Error("Failed to select query engine"); } if (!selectedQueryEngine) { throw new Error("Selected query engine is null"); } const finalResponse = await selectedQueryEngine.query({ query: utils.extractText(query) }); // add selected result finalResponse.metadata = finalResponse.metadata || {}; finalResponse.metadata["selectorResult"] = result; return finalResponse; } } } /** * Error class for output parsing. Due to the nature of LLMs, anytime we use LLM * to generate structured output, it's possible that it will hallucinate something * that doesn't match the expected output format. So make sure to catch these * errors in production. */ class OutputParserError extends Error { constructor(message, options = {}){ super(message, options); // https://github.com/tc39/proposal-error-cause this.name = "OutputParserError"; if (!this.cause) { // Need to check for those environments that have implemented the proposal this.cause = options.cause; } this.output = options.output; // This line is to maintain proper stack trace in V8 // (https://v8.dev/docs/stack-trace-api) if (Error.captureStackTrace) { Error.captureStackTrace(this, OutputParserError); } } } /** * * @param text A markdown block with JSON * @returns parsed JSON object */ function parseJsonMarkdown(text) { text = text.trim(); const left_square = text.indexOf("["); const left_brace = text.indexOf("{"); let left; let right; if (left_square < left_brace && left_square != -1) { left = left_square; right = text.lastIndexOf("]"); } else { left = left_brace; right = text.lastIndexOf("}"); } const jsonText = text.substring(left, right + 1); try { //Single JSON object case if (left_square === -1) { return [ JSON.parse(jsonText) ]; } //Multiple JSON object case. return JSON.parse(jsonText); } catch (e) { throw new OutputParserError("Not a json markdown", { output: text }); } } /** * SubQuestionOutputParser is used to parse the output of the SubQuestionGenerator. */ class SubQuestionOutputParser { parse(output) { const parsed = parseJsonMarkdown(output); return { rawOutput: output, parsedOutput: parsed }; } format(output) { return output; } } /** * LLMQuestionGenerator uses the LLM to generate new questions for the LLM using tools and a user query. */ class LLMQuestionGenerator extends prompts.PromptMixin { constructor(init){ super(); this.llm = init?.llm ?? new openai.OpenAI(); this.prompt = init?.prompt ?? prompts.defaultSubQuestionPrompt; this.outputParser = init?.outputParser ?? new SubQuestionOutputParser(); } _getPrompts() { return { subQuestion: this.prompt }; } _updatePrompts(promptsDict) { if ("subQuestion" in promptsDict) { this.prompt = promptsDict.subQuestion; } } async generate(tools, query) { const toolsStr = utils.toToolDescriptions(tools); const queryStr = utils.extractText(query); const prediction = (await this.llm.complete({ prompt: this.prompt.format({ toolsStr, queryStr }) })).text; const structuredOutput = this.outputParser.parse(prediction); return structuredOutput.parsedOutput; } _getPromptModules() { return {}; } } /** * SubQuestionQueryEngine decomposes a question into subquestions and then */ class SubQuestionQueryEngine extends queryEngine.BaseQueryEngine { constructor(init){ super(); this.questionGen = init.questionGen; this.responseSynthesizer = init.responseSynthesizer ?? responseSynthesizers.getResponseSynthesizer("compact"); this.queryEngines = init.queryEngineTools; this.metadatas = init.queryEngineTools.map((tool)=>tool.metadata); } async _query(strOrQueryBundle, stream) { let query; if (typeof strOrQueryBundle === "string") { query = { query: strOrQueryBundle }; } else { query = strOrQueryBundle; } const subQuestions = await this.questionGen.generate(this.metadatas, strOrQueryBundle); const subQNodes = await Promise.all(subQuestions.map((subQ)=>this.querySubQ(subQ))); const nodesWithScore = subQNodes.filter((node)=>node !== null); if (stream) { return this.responseSynthesizer.synthesize({ query, nodes: nodesWithScore }, true); } return this.responseSynthesizer.synthesize({ query, nodes: nodesWithScore }, false); } _getPrompts() { return {}; } _updatePrompts() {} // eslint-disable-next-line @typescript-eslint/no-explicit-any _getPromptModules() { return { questionGen: this.questionGen, responseSynthesizer: this.responseSynthesizer }; } static fromDefaults(init) { const questionGen = init.questionGen ?? new LLMQuestionGenerator(); const responseSynthesizer = init.responseSynthesizer ?? responseSynthesizers.getResponseSynthesizer("compact"); return new SubQuestionQueryEngine({ questionGen, responseSynthesizer, queryEngineTools: init.queryEngineTools }); } async querySubQ(subQ) { try { const question = subQ.subQuestion; const queryEngine = this.queryEngines.find((tool)=>tool.metadata.name === subQ.toolName); if (!queryEngine) { return null; } const responseValue = await queryEngine?.call?.({ query: question }); if (responseValue == null) { return null; } const nodeText = `Sub question: ${question}\nResponse: ${typeof responseValue === "string" ? responseValue : JSON.stringify(responseValue)}`; const node = new schema.TextNode({ text: nodeText }); return { node, score: 0 }; } catch (error) { return null; } } } Object.defineProperty(exports, "RetrieverQueryEngine", { enumerable: true, get: function () { return queryEngine.RetrieverQueryEngine; } }); exports.CondenseQuestionChatEngine = CondenseQuestionChatEngine; exports.RouterQueryEngine = RouterQueryEngine; exports.SubQuestionQueryEngine = SubQuestionQueryEngine; Object.keys(chatEngine).forEach(function (k) { if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, { enumerable: true, get: function () { return chatEngine[k]; } }); });