UNPKG

@llamaindex/core

Version:
712 lines (684 loc) 24 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var index_cjs = require('../../utils/dist/index.cjs'); var zod = require('zod'); /** * MIT License * * Copyright (c) 2019 jhonararipe * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ function formatImpl(...args_) { // Create variables let self = this; const __patterns__ = self.match(/({.*?})/g); const { REF, FILL_CHAR, MASK_NUMBER, ALIGN_OP, CROP_SIZE, FRACTION, TYPE_VAR } = { REF: 1, FILL_CHAR: 2, MASK_NUMBER: 3, ALIGN_OP: 4, CROP_SIZE: 5, FRACTION: 7, TYPE_VAR: 8 }; const DEFAULT_PLACE = 6; const ALL_REGEXP = /{(\w+)?:([^>\^<\d#]|0)?([#%,])?([>^<\.])?(\d+)?(\.)?(\d+)?([eEfFgGdxXobn#%])?}/g; const regExpBasic = /{\[?(\w+)\]?}/; // it's not best solution const isObject = typeof args_[0] === "object"; // types/use logic __patterns__?.map((pattern, patt_index)=>{ const kargs = ALL_REGEXP.exec(pattern) || ALL_REGEXP.exec(pattern); const wargs = regExpBasic.exec(pattern); // Insert values (one 2 one / array / object) const INDEX_VAR = (wargs ? wargs[REF] : kargs ? kargs[REF] : patt_index) || patt_index; // @ts-expect-error const NATUAL_VALUE = isObject ? args_[0][INDEX_VAR] : args_[INDEX_VAR]; // @ts-expect-error let ACTUAL_VALUE = isObject ? args_[0][INDEX_VAR] : args_[INDEX_VAR]; // Verify sintax/semantic if (ACTUAL_VALUE === null || ACTUAL_VALUE === undefined) throw new Error(`Replacement index ${INDEX_VAR} out of range for positional args tuple`); if (kargs) { // If TYPE_VAR is not defined and the first argument is a number, pad a string should from left, so set TYPE_VAR to "d" if (kargs[TYPE_VAR] === undefined && typeof ACTUAL_VALUE === "number") { kargs[TYPE_VAR] = "d"; } const LETTER = (!kargs[FILL_CHAR] ? false : !kargs[ALIGN_OP] && [ ..."FfbefoxXn" ].includes(kargs[FILL_CHAR].toLowerCase()) ? kargs[FILL_CHAR] : kargs[TYPE_VAR]) || kargs[TYPE_VAR]; // padronaze if (LETTER) { const floatSize = pattern.includes(".") ? Number(kargs[FRACTION] || kargs[CROP_SIZE]) : DEFAULT_PLACE; switch(LETTER){ case "E": ACTUAL_VALUE = ACTUAL_VALUE.toExponential(DEFAULT_PLACE).toUpperCase(); break; case "e": ACTUAL_VALUE = ACTUAL_VALUE.toExponential(DEFAULT_PLACE); break; case "X": ACTUAL_VALUE = ACTUAL_VALUE.toString(16).toUpperCase(); break; case "x": ACTUAL_VALUE = ACTUAL_VALUE.toString(16); // Hexadecimal break; case "b": ACTUAL_VALUE = ACTUAL_VALUE.toString(2); // Binary break; case "f": case "F": ACTUAL_VALUE = ACTUAL_VALUE.toFixed(floatSize); break; case "o": ACTUAL_VALUE = ACTUAL_VALUE.toString(8); // Octal break; } // mask switch(kargs[MASK_NUMBER]){ case "#": const MASK = { x: "0x", X: "0X", o: "0o", b: "0b" }[LETTER]; ACTUAL_VALUE = MASK + ACTUAL_VALUE; break; } } // signal if (// @ts-expect-error [ ..." +-,%" ].includes(kargs[FILL_CHAR]) && typeof NATUAL_VALUE === "number") { ACTUAL_VALUE = ACTUAL_VALUE.toString().replace("-", ""); if (NATUAL_VALUE >= 0) switch(kargs[FILL_CHAR]){ case "+": ACTUAL_VALUE = "+" + ACTUAL_VALUE; break; case " ": ACTUAL_VALUE = " " + ACTUAL_VALUE; break; case ",": ACTUAL_VALUE = NATUAL_VALUE.toString().split(/(?=(?:...)*$)/).join(kargs[FILL_CHAR]); break; case "%": ACTUAL_VALUE = (NATUAL_VALUE * 100).toFixed(// @ts-expect-error kargs[FRACTION] || DEFAULT_PLACE) + "%"; break; } else ACTUAL_VALUE = "-" + ACTUAL_VALUE; } // space / order / trim if (kargs[CROP_SIZE]) { ACTUAL_VALUE = ACTUAL_VALUE.toString(); const FILL_ELEMENT = kargs[FILL_CHAR] || " "; const SIZE_STRING = ACTUAL_VALUE.length; const SIZE_ARG = kargs[CROP_SIZE]; const FILL_LENGTH = SIZE_STRING > SIZE_ARG ? SIZE_STRING : SIZE_ARG; const FILL = FILL_ELEMENT.repeat(FILL_LENGTH); switch(kargs[ALIGN_OP] || kargs[FILL_CHAR]){ case "<": ACTUAL_VALUE = ACTUAL_VALUE.padEnd(FILL_LENGTH, FILL_ELEMENT); break; case ".": if (!(LETTER && /[fF]/.test(LETTER))) ACTUAL_VALUE = ACTUAL_VALUE.slice(0, SIZE_ARG); break; case ">": ACTUAL_VALUE = ACTUAL_VALUE.padStart(FILL_LENGTH, FILL_ELEMENT); break; case "^": const length_start = Math.floor((FILL_LENGTH - SIZE_STRING) / 2); const string_start = length_start > 0 ? FILL_ELEMENT.repeat(length_start) + ACTUAL_VALUE : ACTUAL_VALUE; ACTUAL_VALUE = FILL.replace(RegExp(`.{${string_start.length}}`), string_start); break; default: ACTUAL_VALUE = LETTER ? ACTUAL_VALUE.padStart(FILL_LENGTH, FILL_ELEMENT) : ACTUAL_VALUE.padEnd(FILL_LENGTH, FILL_ELEMENT); break; } } } // SET Definitive value self = self.replace(pattern, ACTUAL_VALUE); }); return self; } const format = (inputString, ...param)=>formatImpl.apply(inputString, param); const promptType = { SUMMARY: "summary", TREE_INSERT: "insert", TREE_SELECT: "tree_select", TREE_SELECT_MULTIPLE: "tree_select_multiple", QUESTION_ANSWER: "text_qa", REFINE: "refine", KEYWORD_EXTRACT: "keyword_extract", QUERY_KEYWORD_EXTRACT: "query_keyword_extract", SCHEMA_EXTRACT: "schema_extract", TEXT_TO_SQL: "text_to_sql", TEXT_TO_GRAPH_QUERY: "text_to_graph_query", TABLE_CONTEXT: "table_context", KNOWLEDGE_TRIPLET_EXTRACT: "knowledge_triplet_extract", SIMPLE_INPUT: "simple_input", PANDAS: "pandas", JSON_PATH: "json_path", SINGLE_SELECT: "single_select", MULTI_SELECT: "multi_select", VECTOR_STORE_QUERY: "vector_store_query", SUB_QUESTION: "sub_question", SQL_RESPONSE_SYNTHESIS: "sql_response_synthesis", SQL_RESPONSE_SYNTHESIS_V2: "sql_response_synthesis_v2", CONVERSATION: "conversation", DECOMPOSE: "decompose", CHOICE_SELECT: "choice_select", CUSTOM: "custom", RANKGPT_RERANK: "rankgpt_rerank" }; const promptTypeSchema = zod.z.enum([ promptType.SUMMARY, promptType.TREE_INSERT, promptType.TREE_SELECT, promptType.TREE_SELECT_MULTIPLE, promptType.QUESTION_ANSWER, promptType.REFINE, promptType.KEYWORD_EXTRACT, promptType.QUERY_KEYWORD_EXTRACT, promptType.SCHEMA_EXTRACT, promptType.TEXT_TO_SQL, promptType.TEXT_TO_GRAPH_QUERY, promptType.TABLE_CONTEXT, promptType.KNOWLEDGE_TRIPLET_EXTRACT, promptType.SIMPLE_INPUT, promptType.PANDAS, promptType.JSON_PATH, promptType.SINGLE_SELECT, promptType.MULTI_SELECT, promptType.VECTOR_STORE_QUERY, promptType.SUB_QUESTION, promptType.SQL_RESPONSE_SYNTHESIS, promptType.SQL_RESPONSE_SYNTHESIS_V2, promptType.CONVERSATION, promptType.DECOMPOSE, promptType.CHOICE_SELECT, promptType.CUSTOM, promptType.RANKGPT_RERANK ]); const PromptType = promptTypeSchema.enum; class BasePromptTemplate { constructor(options){ this.metadata = {}; /** * Set of template variables used in the prompt template. Used for type hints only. * To get the list of template variables used in the prompt at run-time, use the `vars` method. */ this.templateVars = new Set(); this.options = {}; this.templateVarMappings = {}; this.functionMappings = {}; const { metadata, templateVars, outputParser, templateVarMappings, functionMappings } = options; if (metadata) { this.metadata = metadata; } if (templateVars) { this.templateVars = new Set(templateVars); } if (options.options) { this.options = options.options; } this.outputParser = outputParser; if (templateVarMappings) { this.templateVarMappings = templateVarMappings; } if (functionMappings) { this.functionMappings = functionMappings; } } mapTemplateVars(options) { const templateVarMappings = this.templateVarMappings; return Object.fromEntries(index_cjs.objectEntries(options).map(([k, v])=>[ templateVarMappings[k] || k, v ])); } mapFunctionVars(options) { const functionMappings = this.functionMappings; const newOptions = {}; for (const [k, v] of index_cjs.objectEntries(functionMappings)){ newOptions[k] = v(options); } for (const [k, v] of index_cjs.objectEntries(options)){ if (!(k in newOptions)) { newOptions[k] = v; } } return newOptions; } mapAllVars(options) { const newOptions = this.mapFunctionVars(options); return this.mapTemplateVars(newOptions); } } class PromptTemplate extends BasePromptTemplate { #template; constructor(options){ const { template, promptType, ...rest } = options; super(rest); this.#template = template; this.promptType = promptType ?? PromptType.custom; } partialFormat(options) { const prompt = new PromptTemplate({ template: this.template, templateVars: [ ...this.templateVars ], options: this.options, outputParser: this.outputParser, templateVarMappings: this.templateVarMappings, functionMappings: this.functionMappings, metadata: this.metadata, promptType: this.promptType }); prompt.options = { ...prompt.options, ...options }; return prompt; } format(options) { const allOptions = { ...this.options, ...options }; const mappedAllOptions = this.mapAllVars(allOptions); const prompt = format(this.template, mappedAllOptions); if (this.outputParser) { return this.outputParser.format(prompt); } return prompt; } formatMessages(options) { const prompt = this.format(options); return [ { role: "user", content: prompt } ]; } get template() { return this.#template; } /** * Returns all the template variables used in the prompt template. */ vars() { const template = this.template; const matches = template.match(/\{([^}]+)\}/g) || []; return [ ...new Set(matches.map((match)=>match.slice(1, -1))) ]; } } class PromptMixin { validatePrompts(promptsDict, moduleDict) { for (const key of Object.keys(promptsDict)){ if (key.includes(":")) { throw new Error(`Prompt key ${key} cannot contain ':'.`); } } for (const key of Object.keys(moduleDict)){ if (key.includes(":")) { throw new Error(`Module key ${key} cannot contain ':'.`); } } } getPrompts() { const promptsDict = this._getPrompts(); const moduleDict = this._getPromptModules(); this.validatePrompts(promptsDict, moduleDict); const allPrompts = { ...promptsDict }; for (const [module_name, prompt_module] of index_cjs.objectEntries(moduleDict)){ for (const [key, prompt] of index_cjs.objectEntries(prompt_module.getPrompts())){ allPrompts[`${module_name}:${key}`] = prompt; } } return allPrompts; } updatePrompts(prompts) { const promptModules = this._getPromptModules(); this._updatePrompts(prompts); const subPrompt = {}; for(const key in prompts){ if (key.includes(":")) { const [moduleName, subKey] = key.split(":"); if (!subPrompt[moduleName]) { subPrompt[moduleName] = {}; } subPrompt[moduleName][subKey] = prompts[key]; } } for (const [moduleName, subPromptDict] of Object.entries(subPrompt)){ if (!promptModules[moduleName]) { throw new Error(`Module ${moduleName} not found.`); } const moduleToUpdate = promptModules[moduleName]; moduleToUpdate.updatePrompts(subPromptDict); } } } const defaultTextQAPrompt = new PromptTemplate({ templateVars: [ "context", "query" ], template: `Context information is below. --------------------- {context} --------------------- Given the context information and not prior knowledge, answer the query. Query: {query} Answer:` }); const anthropicTextQaPrompt = new PromptTemplate({ templateVars: [ "context", "query" ], template: `Context information: <context> {context} </context> Given the context information and not prior knowledge, answer the query. Query: {query}` }); const defaultSummaryPrompt = new PromptTemplate({ templateVars: [ "context" ], template: `Write a summary of the following. Try to use only the information provided. Try to include as many key details as possible. {context} SUMMARY:""" ` }); const anthropicSummaryPrompt = new PromptTemplate({ templateVars: [ "context" ], template: `Summarize the following text. Try to use only the information provided. Try to include as many key details as possible. <original-text> {context} </original-text> SUMMARY: ` }); const defaultRefinePrompt = new PromptTemplate({ templateVars: [ "query", "existingAnswer", "context" ], template: `The original query is as follows: {query} We have provided an existing answer: {existingAnswer} We have the opportunity to refine the existing answer (only if needed) with some more context below. ------------ {context} ------------ Given the new context, refine the original answer to better answer the query. If the context isn't useful, return the original answer. Refined Answer:` }); const defaultTreeSummarizePrompt = new PromptTemplate({ templateVars: [ "context", "query" ], template: `Context information from multiple sources is below. --------------------- {context} --------------------- Given the information from multiple sources and not prior knowledge, answer the query. Query: {query} Answer:` }); const defaultChoiceSelectPrompt = new PromptTemplate({ templateVars: [ "context", "query" ], template: `A list of documents is shown below. Each document has a number next to it along with a summary of the document. A question is also provided. Respond with the numbers of the documents you should consult to answer the question, in order of relevance, as well as the relevance score. The relevance score is a number from 1-10 based on how relevant you think the document is to the question. Do not include any documents that are not relevant to the question. Example format: Document 1: <summary of document 1> Document 2: <summary of document 2> ... Document 10:\n<summary of document 10> Question: <question> Answer: Doc: 9, Relevance: 7 Doc: 3, Relevance: 4 Doc: 7, Relevance: 3 Let's try this now: {context} Question: {query} Answer:` }); function buildToolsText(tools) { const toolsObj = tools.reduce((acc, tool)=>{ acc[tool.name] = tool.description; return acc; }, {}); return JSON.stringify(toolsObj, null, 4); } const exampleTools = [ { name: "uber_10k", description: "Provides information about Uber financials for year 2021" }, { name: "lyft_10k", description: "Provides information about Lyft financials for year 2021" } ]; const exampleQueryStr = `Compare and contrast the revenue growth and EBITDA of Uber and Lyft for year 2021`; const exampleOutput = [ { subQuestion: "What is the revenue growth of Uber", toolName: "uber_10k" }, { subQuestion: "What is the EBITDA of Uber", toolName: "uber_10k" }, { subQuestion: "What is the revenue growth of Lyft", toolName: "lyft_10k" }, { subQuestion: "What is the EBITDA of Lyft", toolName: "lyft_10k" } ]; const defaultSubQuestionPrompt = new PromptTemplate({ templateVars: [ "toolsStr", "queryStr" ], template: `Given a user question, and a list of tools, output a list of relevant sub-questions that when composed can help answer the full user question: # Example 1 <Tools> \`\`\`json ${buildToolsText(exampleTools)} \`\`\` <User Question> ${exampleQueryStr} <Output> \`\`\`json ${JSON.stringify(exampleOutput, null, 4)} \`\`\` # Example 2 <Tools> \`\`\`json {toolsStr} \`\`\` <User Question> {queryStr} <Output> ` }); const defaultCondenseQuestionPrompt = new PromptTemplate({ templateVars: [ "chatHistory", "question" ], template: `Given a conversation (between Human and Assistant) and a follow up message from Human, rewrite the message to be a standalone question that captures all relevant context from the conversation. <Chat History> {chatHistory} <Follow Up Message> {question} <Standalone question> ` }); const defaultContextSystemPrompt = new PromptTemplate({ templateVars: [ "context" ], template: `Context information is below. --------------------- {context} ---------------------` }); const defaultKeywordExtractPrompt = new PromptTemplate({ templateVars: [ "maxKeywords", "context" ], template: ` Some text is provided below. Given the text, extract up to {maxKeywords} keywords from the text. Avoid stopwords. --------------------- {context} --------------------- Provide keywords in the following comma-separated format: 'KEYWORDS: <keywords>' ` }).partialFormat({ maxKeywords: "10" }); const defaultQueryKeywordExtractPrompt = new PromptTemplate({ templateVars: [ "maxKeywords", "question" ], template: `( "A question is provided below. Given the question, extract up to {maxKeywords} " "keywords from the text. Focus on extracting the keywords that we can use " "to best lookup answers to the question. Avoid stopwords." "---------------------" "{question}" "---------------------" "Provide keywords in the following comma-separated format: 'KEYWORDS: <keywords>'" )` }).partialFormat({ maxKeywords: "10" }); const defaultQuestionExtractPrompt = new PromptTemplate({ templateVars: [ "numQuestions", "context" ], template: `( "Given the contextual informations below, generate {numQuestions} questions this context can provides specific answers to which are unlikely to be found else where. Higher-level summaries of surrounding context may be provided as well. " "Try using these summaries to generate better questions that this context can answer." "---------------------" "{context}" "---------------------" "Provide questions in the following format: 'QUESTIONS: <questions>'" )` }).partialFormat({ numQuestions: "5" }); const defaultTitleExtractorPromptTemplate = new PromptTemplate({ templateVars: [ "context" ], template: `{context} Give a title that summarizes all of the unique entities, titles or themes found in the context. Title: ` }); const defaultTitleCombinePromptTemplate = new PromptTemplate({ templateVars: [ "context" ], template: `{context} Based on the above candidate titles and contents, what is the comprehensive title for this document? Title: ` }); new PromptTemplate({ templateVars: [ "context", "numKeywords" ], template: `{context} Give {numKeywords} unique keywords for this document. Format as comma separated. Keywords: ` }).partialFormat({ keywordCount: "5" }); const defaultNodeTextTemplate = new PromptTemplate({ templateVars: [ "metadataStr", "content" ], template: `[Excerpt from document] {metadataStr} Excerpt: ----- {content} ----- ` }).partialFormat({ metadataStr: "", content: "" }); exports.BasePromptTemplate = BasePromptTemplate; exports.PromptMixin = PromptMixin; exports.PromptTemplate = PromptTemplate; exports.anthropicSummaryPrompt = anthropicSummaryPrompt; exports.anthropicTextQaPrompt = anthropicTextQaPrompt; exports.defaultChoiceSelectPrompt = defaultChoiceSelectPrompt; exports.defaultCondenseQuestionPrompt = defaultCondenseQuestionPrompt; exports.defaultContextSystemPrompt = defaultContextSystemPrompt; exports.defaultKeywordExtractPrompt = defaultKeywordExtractPrompt; exports.defaultNodeTextTemplate = defaultNodeTextTemplate; exports.defaultQueryKeywordExtractPrompt = defaultQueryKeywordExtractPrompt; exports.defaultQuestionExtractPrompt = defaultQuestionExtractPrompt; exports.defaultRefinePrompt = defaultRefinePrompt; exports.defaultSubQuestionPrompt = defaultSubQuestionPrompt; exports.defaultSummaryPrompt = defaultSummaryPrompt; exports.defaultTextQAPrompt = defaultTextQAPrompt; exports.defaultTitleCombinePromptTemplate = defaultTitleCombinePromptTemplate; exports.defaultTitleExtractorPromptTemplate = defaultTitleExtractorPromptTemplate; exports.defaultTreeSummarizePrompt = defaultTreeSummarizePrompt;