mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-09 03:23:09 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65d834615d | |||
| b8be4c09e2 | |||
| e5fb332538 | |||
| 491033d534 | |||
| 885fa316a5 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"llamaindex": patch
|
||||
---
|
||||
|
||||
build: use ESM as default
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"llamaindex": patch
|
||||
---
|
||||
|
||||
feat: abstract `@llamaindex/env` package
|
||||
@@ -9,6 +9,7 @@ module.exports = {
|
||||
},
|
||||
rules: {
|
||||
"max-params": ["error", 4],
|
||||
"prefer-const": "error",
|
||||
},
|
||||
ignorePatterns: ["dist/"],
|
||||
};
|
||||
|
||||
@@ -61,10 +61,13 @@ jobs:
|
||||
run: pnpm run build --filter llamaindex
|
||||
- name: Copy examples
|
||||
run: rsync -rv --exclude=node_modules ./examples ${{ runner.temp }}
|
||||
- name: Pack
|
||||
- name: Pack @llamaindex/env
|
||||
run: pnpm pack --pack-destination ${{ runner.temp }}
|
||||
working-directory: packages/env
|
||||
- name: Pack llamaindex
|
||||
run: pnpm pack --pack-destination ${{ runner.temp }}
|
||||
working-directory: packages/core
|
||||
- name: Install llamaindex
|
||||
- name: Install
|
||||
run: npm add ${{ runner.temp }}/*.tgz
|
||||
working-directory: ${{ runner.temp }}/examples
|
||||
- name: Run Type Check
|
||||
|
||||
Vendored
-1
@@ -5,7 +5,6 @@
|
||||
"[xml]": {
|
||||
"editor.defaultFormatter": "redhat.vscode-xml"
|
||||
},
|
||||
"jest.rootPath": "./packages/core",
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Ollama } from "llamaindex";
|
||||
import { Ollama } from "llamaindex/llm/ollama";
|
||||
|
||||
(async () => {
|
||||
const llm = new Ollama({ model: "llama2", temperature: 0.75 });
|
||||
|
||||
@@ -18,15 +18,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.27.1",
|
||||
"@types/jest": "^29.5.12",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-custom": "workspace:*",
|
||||
"husky": "^9.0.10",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-organize-imports": "^3.2.4",
|
||||
"ts-jest": "^29.1.2",
|
||||
"turbo": "^1.12.3",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript"
|
||||
},
|
||||
"target": "esnext"
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "typescript"
|
||||
},
|
||||
"target": "esnext"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
testPathIgnorePatterns: ["/lib/", "/node_modules/", "/dist/"],
|
||||
};
|
||||
+37
-160
@@ -3,9 +3,13 @@
|
||||
"private": true,
|
||||
"version": "0.1.12",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.13.0",
|
||||
"@aws-crypto/sha256-js": "^5.2.0",
|
||||
"@datastax/astra-db-ts": "^0.1.4",
|
||||
"@llamaindex/cloud": "^0.0.1",
|
||||
"@llamaindex/env": "workspace:*",
|
||||
"@mistralai/mistralai": "^0.0.10",
|
||||
"@notionhq/client": "^2.2.14",
|
||||
"@pinecone-database/pinecone": "^2.0.1",
|
||||
@@ -34,171 +38,46 @@
|
||||
"wink-nlp": "^1.14.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-crypto/sha256-js": "^5.2.0",
|
||||
"@llamaindex/cloud": "^0.0.1",
|
||||
"@types/edit-json-file": "^1.7.3",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@swc/cli": "^0.3.9",
|
||||
"@swc/core": "^1.4.2",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^18.19.14",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/pg": "^8.11.0",
|
||||
"bunchee": "^4.4.6",
|
||||
"edit-json-file": "^1.8.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"glob": "^10.3.10",
|
||||
"madge": "^6.1.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"types": "./dist/index.d.ts",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/type/index.d.ts",
|
||||
"main": "./dist/cjs/index.cjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs",
|
||||
"edge-light": "./dist/index.edge-light.mjs",
|
||||
"require": "./dist/index.js"
|
||||
"import": {
|
||||
"types": "./dist/type/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"edge-light": {
|
||||
"types": "./dist/type/index.d.ts",
|
||||
"default": "./dist/index.edge-light.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/type/index.d.ts",
|
||||
"default": "./dist/cjs/index.cjs"
|
||||
}
|
||||
},
|
||||
"./env": {
|
||||
"types": "./dist/env.d.mts",
|
||||
"import": "./dist/env.mjs",
|
||||
"edge-light": "./dist/env.edge-light.mjs",
|
||||
"require": "./dist/env.js"
|
||||
},
|
||||
"./ChatEngine": {
|
||||
"types": "./dist/ChatEngine.d.mts",
|
||||
"import": "./dist/ChatEngine.mjs",
|
||||
"require": "./dist/ChatEngine.js"
|
||||
},
|
||||
"./ChatHistory": {
|
||||
"types": "./dist/ChatHistory.d.mts",
|
||||
"import": "./dist/ChatHistory.mjs",
|
||||
"require": "./dist/ChatHistory.js"
|
||||
},
|
||||
"./constants": {
|
||||
"types": "./dist/constants.d.mts",
|
||||
"import": "./dist/constants.mjs",
|
||||
"require": "./dist/constants.js"
|
||||
},
|
||||
"./GlobalsHelper": {
|
||||
"types": "./dist/GlobalsHelper.d.mts",
|
||||
"import": "./dist/GlobalsHelper.mjs",
|
||||
"require": "./dist/GlobalsHelper.js"
|
||||
},
|
||||
"./Node": {
|
||||
"types": "./dist/Node.d.mts",
|
||||
"import": "./dist/Node.mjs",
|
||||
"require": "./dist/Node.js"
|
||||
},
|
||||
"./OutputParser": {
|
||||
"types": "./dist/OutputParser.d.mts",
|
||||
"import": "./dist/OutputParser.mjs",
|
||||
"require": "./dist/OutputParser.js"
|
||||
},
|
||||
"./Prompt": {
|
||||
"types": "./dist/Prompt.d.mts",
|
||||
"import": "./dist/Prompt.mjs",
|
||||
"require": "./dist/Prompt.js"
|
||||
},
|
||||
"./PromptHelper": {
|
||||
"types": "./dist/PromptHelper.d.mts",
|
||||
"import": "./dist/PromptHelper.mjs",
|
||||
"require": "./dist/PromptHelper.js"
|
||||
},
|
||||
"./QueryEngine": {
|
||||
"types": "./dist/QueryEngine.d.mts",
|
||||
"import": "./dist/QueryEngine.mjs",
|
||||
"require": "./dist/QueryEngine.js"
|
||||
},
|
||||
"./QuestionGenerator": {
|
||||
"types": "./dist/QuestionGenerator.d.mts",
|
||||
"import": "./dist/QuestionGenerator.mjs",
|
||||
"require": "./dist/QuestionGenerator.js"
|
||||
},
|
||||
"./Response": {
|
||||
"types": "./dist/Response.d.mts",
|
||||
"import": "./dist/Response.mjs",
|
||||
"require": "./dist/Response.js"
|
||||
},
|
||||
"./ServiceContext": {
|
||||
"types": "./dist/ServiceContext.d.mts",
|
||||
"import": "./dist/ServiceContext.mjs",
|
||||
"require": "./dist/ServiceContext.js"
|
||||
},
|
||||
"./TextSplitter": {
|
||||
"types": "./dist/TextSplitter.d.mts",
|
||||
"import": "./dist/TextSplitter.mjs",
|
||||
"require": "./dist/TextSplitter.js"
|
||||
},
|
||||
"./tools": {
|
||||
"types": "./dist/tools.d.mts",
|
||||
"import": "./dist/tools.mjs",
|
||||
"require": "./dist/tools.js"
|
||||
},
|
||||
"./objects": {
|
||||
"types": "./dist/objects.d.mts",
|
||||
"import": "./dist/objects.mjs",
|
||||
"require": "./dist/objects.js"
|
||||
},
|
||||
"./readers": {
|
||||
"types": "./dist/readers.d.mts",
|
||||
"import": "./dist/readers.mjs",
|
||||
"require": "./dist/readers.js"
|
||||
},
|
||||
"./readers/AssemblyAIReader": {
|
||||
"types": "./dist/readers/AssemblyAIReader.d.mts",
|
||||
"import": "./dist/readers/AssemblyAIReader.mjs",
|
||||
"require": "./dist/readers/AssemblyAIReader.js"
|
||||
},
|
||||
"./readers/CSVReader": {
|
||||
"types": "./dist/readers/CSVReader.d.mts",
|
||||
"import": "./dist/readers/CSVReader.mjs",
|
||||
"require": "./dist/readers/CSVReader.js"
|
||||
},
|
||||
"./readers/DocxReader": {
|
||||
"types": "./dist/readers/DocxReader.d.mts",
|
||||
"import": "./dist/readers/DocxReader.mjs",
|
||||
"require": "./dist/readers/DocxReader.js"
|
||||
},
|
||||
"./readers/HTMLReader": {
|
||||
"types": "./dist/readers/HTMLReader.d.mts",
|
||||
"import": "./dist/readers/HTMLReader.mjs",
|
||||
"require": "./dist/readers/HTMLReader.js"
|
||||
},
|
||||
"./readers/ImageReader": {
|
||||
"types": "./dist/readers/ImageReader.d.mts",
|
||||
"import": "./dist/readers/ImageReader.mjs",
|
||||
"require": "./dist/readers/ImageReader.js"
|
||||
},
|
||||
"./readers/MarkdownReader": {
|
||||
"types": "./dist/readers/MarkdownReader.d.mts",
|
||||
"import": "./dist/readers/MarkdownReader.mjs",
|
||||
"require": "./dist/readers/MarkdownReader.js"
|
||||
},
|
||||
"./readers/NotionReader": {
|
||||
"types": "./dist/readers/NotionReader.d.mts",
|
||||
"import": "./dist/readers/NotionReader.mjs",
|
||||
"require": "./dist/readers/NotionReader.js"
|
||||
},
|
||||
"./readers/PDFReader": {
|
||||
"types": "./dist/readers/PDFReader.d.mts",
|
||||
"import": "./dist/readers/PDFReader.mjs",
|
||||
"require": "./dist/readers/PDFReader.js"
|
||||
},
|
||||
"./readers/SimpleDirectoryReader": {
|
||||
"types": "./dist/readers/SimpleDirectoryReader.d.mts",
|
||||
"import": "./dist/readers/SimpleDirectoryReader.mjs",
|
||||
"require": "./dist/readers/SimpleDirectoryReader.js"
|
||||
},
|
||||
"./readers/SimpleMongoReader": {
|
||||
"types": "./dist/readers/SimpleMongoReader.d.mts",
|
||||
"import": "./dist/readers/SimpleMongoReader.mjs",
|
||||
"require": "./dist/readers/SimpleMongoReader.js"
|
||||
},
|
||||
"./cloud": {
|
||||
"types": "./dist/cloud.d.mts",
|
||||
"import": "./dist/cloud.mjs",
|
||||
"require": "./dist/cloud.js"
|
||||
"./*": {
|
||||
"import": {
|
||||
"types": "./dist/type/*.d.ts",
|
||||
"default": "./dist/*.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/type/*.d.ts",
|
||||
"default": "./dist/cjs/*.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
@@ -211,13 +90,11 @@
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "jest",
|
||||
"build": "rm -rf ./dist && NODE_OPTIONS=\"--max-old-space-size=12288\" bunchee",
|
||||
"postbuild": "pnpm run copy && pnpm run modify-package-json",
|
||||
"copy": "cp -r package.json CHANGELOG.md ../../README.md ../../LICENSE examples src dist/",
|
||||
"modify-package-json": "node ./scripts/modify-package-json.mjs",
|
||||
"prepublish": "pnpm run modify-package-json && echo \"please cd ./dist and run pnpm publish\" && exit 1",
|
||||
"dev": "NODE_OPTIONS=\"--max-old-space-size=16384\" bunchee -w",
|
||||
"circular-check": "madge -c ./src/index.ts"
|
||||
"build": "rm -rf ./dist && pnpm run build:esm && pnpm run build:cjs && pnpm run build:type",
|
||||
"build:esm": "swc src -d dist --strip-leading-paths --config-file .swcrc",
|
||||
"build:cjs": "swc src -d dist/cjs --strip-leading-paths --config-file .cjs.swcrc --out-file-extension cjs",
|
||||
"build:type": "tsc -p tsconfig.json",
|
||||
"circular-check": "madge -c ./src/index.ts",
|
||||
"dev": "concurrently \"pnpm run build:esm --watch\" \"pnpm run build:cjs --watch\" \"pnpm run build:type --watch\""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* This script is used to modify the package.json file in the dist folder
|
||||
* so that it can be published to npm.
|
||||
*/
|
||||
import editJsonFile from "edit-json-file";
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
{
|
||||
await fs.copyFile("./package.json", "./dist/package.json");
|
||||
const file = editJsonFile("./dist/package.json");
|
||||
|
||||
file.unset("scripts");
|
||||
file.unset("private");
|
||||
await new Promise((resolve) => file.save(resolve));
|
||||
}
|
||||
{
|
||||
const packageJson = await fs.readFile("./dist/package.json", "utf8");
|
||||
const modifiedPackageJson = packageJson.replaceAll("./dist/", "./");
|
||||
await fs.writeFile(
|
||||
"./dist/package.json",
|
||||
JSON.stringify(JSON.parse(modifiedPackageJson), null, 2),
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { OpenAI } from "./llm/LLM";
|
||||
import { ChatMessage, LLM, MessageType } from "./llm/types";
|
||||
import { OpenAI } from "./llm/LLM.js";
|
||||
import { ChatMessage, LLM, MessageType } from "./llm/types.js";
|
||||
import {
|
||||
defaultSummaryPrompt,
|
||||
messagesToHistoryStr,
|
||||
SummaryPrompt,
|
||||
} from "./Prompt";
|
||||
} from "./Prompt.js";
|
||||
|
||||
/**
|
||||
* A ChatHistory is used to keep the state of back and forth chat messages
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { encodingForModel } from "js-tiktoken";
|
||||
|
||||
import { Event, EventTag, EventType } from "./callbacks/CallbackManager";
|
||||
import { randomUUID } from "./env";
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { Event, EventTag, EventType } from "./callbacks/CallbackManager.js";
|
||||
|
||||
export enum Tokenizers {
|
||||
CL100K_BASE = "cl100k_base",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createSHA256, path, randomUUID } from "@llamaindex/env";
|
||||
import _ from "lodash";
|
||||
import { createSHA256, path, randomUUID } from "./env";
|
||||
|
||||
export enum NodeRelationship {
|
||||
SOURCE = "SOURCE",
|
||||
@@ -353,10 +353,10 @@ export function splitNodesByType(nodes: BaseNode[]): {
|
||||
imageNodes: ImageNode[];
|
||||
textNodes: TextNode[];
|
||||
} {
|
||||
let imageNodes: ImageNode[] = [];
|
||||
let textNodes: TextNode[] = [];
|
||||
const imageNodes: ImageNode[] = [];
|
||||
const textNodes: TextNode[] = [];
|
||||
|
||||
for (let node of nodes) {
|
||||
for (const node of nodes) {
|
||||
if (node instanceof ImageNode) {
|
||||
imageNodes.push(node);
|
||||
} else if (node instanceof TextNode) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SubQuestion } from "./engines/query/types";
|
||||
import { BaseOutputParser, StructuredOutput } from "./types";
|
||||
import { SubQuestion } from "./engines/query/types.js";
|
||||
import { BaseOutputParser, StructuredOutput } from "./types.js";
|
||||
|
||||
/**
|
||||
* Error class for output parsing. Due to the nature of LLMs, anytime we use LLM
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SubQuestion } from "./engines/query/types";
|
||||
import { ChatMessage } from "./llm/types";
|
||||
import { ToolMetadata } from "./types";
|
||||
import { SubQuestion } from "./engines/query/types.js";
|
||||
import { ChatMessage } from "./llm/types.js";
|
||||
import { ToolMetadata } from "./types.js";
|
||||
|
||||
/**
|
||||
* A SimplePrompt is a function that takes a dictionary of inputs and returns a string.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { globalsHelper } from "./GlobalsHelper";
|
||||
import { SimplePrompt } from "./Prompt";
|
||||
import { SentenceSplitter } from "./TextSplitter";
|
||||
import { globalsHelper } from "./GlobalsHelper.js";
|
||||
import { SimplePrompt } from "./Prompt.js";
|
||||
import { SentenceSplitter } from "./TextSplitter.js";
|
||||
import {
|
||||
DEFAULT_CHUNK_OVERLAP_RATIO,
|
||||
DEFAULT_CONTEXT_WINDOW,
|
||||
DEFAULT_NUM_OUTPUTS,
|
||||
DEFAULT_PADDING,
|
||||
} from "./constants";
|
||||
} from "./constants.js";
|
||||
|
||||
export function getEmptyPromptTxt(prompt: SimplePrompt) {
|
||||
return prompt({});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { SubQuestionOutputParser } from "./OutputParser";
|
||||
import { SubQuestionOutputParser } from "./OutputParser.js";
|
||||
import {
|
||||
SubQuestionPrompt,
|
||||
buildToolsText,
|
||||
defaultSubQuestionPrompt,
|
||||
} from "./Prompt";
|
||||
import { BaseQuestionGenerator, SubQuestion } from "./engines/query/types";
|
||||
import { OpenAI } from "./llm/LLM";
|
||||
import { LLM } from "./llm/types";
|
||||
import { PromptMixin } from "./prompts";
|
||||
import { BaseOutputParser, StructuredOutput, ToolMetadata } from "./types";
|
||||
} from "./Prompt.js";
|
||||
import { BaseQuestionGenerator, SubQuestion } from "./engines/query/types.js";
|
||||
import { OpenAI } from "./llm/LLM.js";
|
||||
import { LLM } from "./llm/types.js";
|
||||
import { PromptMixin } from "./prompts/index.js";
|
||||
import { BaseOutputParser, StructuredOutput, ToolMetadata } from "./types.js";
|
||||
|
||||
/**
|
||||
* LLMQuestionGenerator uses the LLM to generate new questions for the LLM using tools and a user query.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseNode } from "./Node";
|
||||
import { BaseNode } from "./Node.js";
|
||||
|
||||
/**
|
||||
* Response is the output of a LLM
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Event } from "./callbacks/CallbackManager";
|
||||
import { NodeWithScore } from "./Node";
|
||||
import { ServiceContext } from "./ServiceContext";
|
||||
import { Event } from "./callbacks/CallbackManager.js";
|
||||
import { NodeWithScore } from "./Node.js";
|
||||
import { ServiceContext } from "./ServiceContext.js";
|
||||
|
||||
/**
|
||||
* Retrievers retrieve the nodes that most closely match our query in similarity.
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { CallbackManager } from "./callbacks/CallbackManager";
|
||||
import { BaseEmbedding, OpenAIEmbedding } from "./embeddings";
|
||||
import { LLM, OpenAI } from "./llm";
|
||||
import { NodeParser, SimpleNodeParser } from "./nodeParsers";
|
||||
import { PromptHelper } from "./PromptHelper";
|
||||
import { PromptHelper } from "./PromptHelper.js";
|
||||
import { CallbackManager } from "./callbacks/CallbackManager.js";
|
||||
import { OpenAIEmbedding } from "./embeddings/OpenAIEmbedding.js";
|
||||
import { BaseEmbedding } from "./embeddings/types.js";
|
||||
import { LLM, OpenAI } from "./llm/index.js";
|
||||
import { SimpleNodeParser } from "./nodeParsers/SimpleNodeParser.js";
|
||||
import { NodeParser } from "./nodeParsers/types.js";
|
||||
|
||||
/**
|
||||
* The ServiceContext is a collection of components that are used in different parts of the application.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EOL } from "./env";
|
||||
import { EOL } from "@llamaindex/env";
|
||||
// GitHub translated
|
||||
import { globalsHelper } from "./GlobalsHelper";
|
||||
import { DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE } from "./constants";
|
||||
import { globalsHelper } from "./GlobalsHelper.js";
|
||||
import { DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE } from "./constants.js";
|
||||
|
||||
class TextSplit {
|
||||
textChunk: string;
|
||||
@@ -130,7 +130,7 @@ export class SentenceSplitter {
|
||||
|
||||
getParagraphSplits(text: string, effectiveChunkSize?: number): string[] {
|
||||
// get paragraph splits
|
||||
let paragraphSplits: string[] = text.split(this.paragraphSeparator);
|
||||
const paragraphSplits: string[] = text.split(this.paragraphSeparator);
|
||||
let idx = 0;
|
||||
if (effectiveChunkSize == undefined) {
|
||||
return paragraphSplits;
|
||||
@@ -155,9 +155,9 @@ export class SentenceSplitter {
|
||||
}
|
||||
|
||||
getSentenceSplits(text: string, effectiveChunkSize?: number): string[] {
|
||||
let paragraphSplits = this.getParagraphSplits(text, effectiveChunkSize);
|
||||
const paragraphSplits = this.getParagraphSplits(text, effectiveChunkSize);
|
||||
// Next we split the text using the chunk tokenizer fn/
|
||||
let splits = [];
|
||||
const splits = [];
|
||||
for (const parText of paragraphSplits) {
|
||||
const sentenceSplits = this.chunkingTokenizerFn(parText);
|
||||
|
||||
@@ -194,9 +194,9 @@ export class SentenceSplitter {
|
||||
}));
|
||||
}
|
||||
|
||||
let newSplits: SplitRep[] = [];
|
||||
const newSplits: SplitRep[] = [];
|
||||
for (const split of sentenceSplits) {
|
||||
let splitTokens = this.tokenizer(split);
|
||||
const splitTokens = this.tokenizer(split);
|
||||
const splitLen = splitTokens.length;
|
||||
if (splitLen <= effectiveChunkSize) {
|
||||
newSplits.push({ text: split, numTokens: splitLen });
|
||||
@@ -219,7 +219,7 @@ export class SentenceSplitter {
|
||||
// go through sentence splits, combine to chunks that are within the chunk size
|
||||
|
||||
// docs represents final list of text chunks
|
||||
let docs: TextSplit[] = [];
|
||||
const docs: TextSplit[] = [];
|
||||
// curChunkSentences represents the current list of sentence splits (that)
|
||||
// will be merged into a chunk
|
||||
let curChunkSentences: SplitRep[] = [];
|
||||
@@ -287,18 +287,18 @@ export class SentenceSplitter {
|
||||
return [];
|
||||
}
|
||||
|
||||
let effectiveChunkSize = this.getEffectiveChunkSize(extraInfoStr);
|
||||
let sentenceSplits = this.getSentenceSplits(text, effectiveChunkSize);
|
||||
const effectiveChunkSize = this.getEffectiveChunkSize(extraInfoStr);
|
||||
const sentenceSplits = this.getSentenceSplits(text, effectiveChunkSize);
|
||||
|
||||
// Check if any sentences exceed the chunk size. If they don't,
|
||||
// force split by tokenizer
|
||||
let newSentenceSplits = this.processSentenceSplits(
|
||||
const newSentenceSplits = this.processSentenceSplits(
|
||||
sentenceSplits,
|
||||
effectiveChunkSize,
|
||||
);
|
||||
|
||||
// combine sentence splits into chunks of text that can then be returned
|
||||
let combinedTextSplits = this.combineTextSplits(
|
||||
const combinedTextSplits = this.combineTextSplits(
|
||||
newSentenceSplits,
|
||||
effectiveChunkSize,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from "./openai/base";
|
||||
export * from "./openai/worker";
|
||||
export * from "./react/base";
|
||||
export * from "./react/worker";
|
||||
export * from "./types";
|
||||
export * from "./openai/base.js";
|
||||
export * from "./openai/worker.js";
|
||||
export * from "./react/base.js";
|
||||
export * from "./react/worker.js";
|
||||
export * from "./types.js";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager";
|
||||
import { ChatMessage, OpenAI } from "../../llm";
|
||||
import { ObjectRetriever } from "../../objects/base";
|
||||
import { BaseTool } from "../../types";
|
||||
import { AgentRunner } from "../runner/base";
|
||||
import { OpenAIAgentWorker } from "./worker";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager.js";
|
||||
import { ChatMessage, OpenAI } from "../../llm/index.js";
|
||||
import { ObjectRetriever } from "../../objects/base.js";
|
||||
import { BaseTool } from "../../types.js";
|
||||
import { AgentRunner } from "../runner/base.js";
|
||||
import { OpenAIAgentWorker } from "./worker.js";
|
||||
|
||||
type OpenAIAgentParams = {
|
||||
tools?: BaseTool[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ToolMetadata } from "../../types";
|
||||
import { ToolMetadata } from "../../types.js";
|
||||
|
||||
export type OpenAIFunction = {
|
||||
type: "function";
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
// Assuming that the necessary interfaces and classes (like BaseTool, OpenAI, ChatMessage, CallbackManager, etc.) are defined elsewhere
|
||||
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager";
|
||||
import { AgentChatResponse, ChatResponseMode } from "../../engines/chat";
|
||||
import { randomUUID } from "../../env";
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager.js";
|
||||
import {
|
||||
AgentChatResponse,
|
||||
ChatResponseMode,
|
||||
} from "../../engines/chat/types.js";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseChunk,
|
||||
OpenAI,
|
||||
} from "../../llm";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer";
|
||||
import { ObjectRetriever } from "../../objects/base";
|
||||
import { ToolOutput } from "../../tools/types";
|
||||
import { callToolWithErrorHandling } from "../../tools/utils";
|
||||
import { BaseTool } from "../../types";
|
||||
import { AgentWorker, Task, TaskStep, TaskStepOutput } from "../types";
|
||||
import { addUserStepToMemory, getFunctionByName } from "../utils";
|
||||
import { OpenAIToolCall } from "./types/chat";
|
||||
import { toOpenAiTool } from "./utils";
|
||||
} from "../../llm/index.js";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer.js";
|
||||
import { ObjectRetriever } from "../../objects/base.js";
|
||||
import { ToolOutput } from "../../tools/types.js";
|
||||
import { callToolWithErrorHandling } from "../../tools/utils.js";
|
||||
import { BaseTool } from "../../types.js";
|
||||
import { AgentWorker, Task, TaskStep, TaskStepOutput } from "../types.js";
|
||||
import { addUserStepToMemory, getFunctionByName } from "../utils.js";
|
||||
import { OpenAIToolCall } from "./types/chat.js";
|
||||
import { toOpenAiTool } from "./utils.js";
|
||||
|
||||
const DEFAULT_MAX_FUNCTION_CALLS = 5;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager";
|
||||
import { ChatMessage, LLM } from "../../llm";
|
||||
import { ObjectRetriever } from "../../objects/base";
|
||||
import { BaseTool } from "../../types";
|
||||
import { AgentRunner } from "../runner/base";
|
||||
import { ReActAgentWorker } from "./worker";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager.js";
|
||||
import { ChatMessage, LLM } from "../../llm/index.js";
|
||||
import { ObjectRetriever } from "../../objects/base.js";
|
||||
import { BaseTool } from "../../types.js";
|
||||
import { AgentRunner } from "../runner/base.js";
|
||||
import { ReActAgentWorker } from "./worker.js";
|
||||
|
||||
type ReActAgentParams = {
|
||||
tools: BaseTool[];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChatMessage } from "../../llm";
|
||||
import { BaseTool } from "../../types";
|
||||
import { getReactChatSystemHeader } from "./prompts";
|
||||
import { BaseReasoningStep, ObservationReasoningStep } from "./types";
|
||||
import { ChatMessage } from "../../llm/index.js";
|
||||
import { BaseTool } from "../../types.js";
|
||||
import { getReactChatSystemHeader } from "./prompts.js";
|
||||
import { BaseReasoningStep, ObservationReasoningStep } from "./types.js";
|
||||
|
||||
function getReactToolDescriptions(tools: BaseTool[]): string[] {
|
||||
const toolDescs: string[] = [];
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
BaseOutputParser,
|
||||
BaseReasoningStep,
|
||||
ResponseReasoningStep,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
function extractJsonStr(text: string): string {
|
||||
const pattern = /\{.*\}/s;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChatMessage } from "../../llm";
|
||||
import { ChatMessage } from "../../llm/index.js";
|
||||
|
||||
export interface BaseReasoningStep {
|
||||
getContent(): string;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { randomUUID } from "crypto";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager";
|
||||
import { AgentChatResponse } from "../../engines/chat";
|
||||
import { ChatResponse, LLM, OpenAI } from "../../llm";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer";
|
||||
import { ObjectRetriever } from "../../objects/base";
|
||||
import { ToolOutput } from "../../tools";
|
||||
import { BaseTool } from "../../types";
|
||||
import { AgentWorker, Task, TaskStep, TaskStepOutput } from "../types";
|
||||
import { ReActChatFormatter } from "./formatter";
|
||||
import { ReActOutputParser } from "./outputParser";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager.js";
|
||||
import { AgentChatResponse } from "../../engines/chat/index.js";
|
||||
import { ChatResponse, LLM, OpenAI } from "../../llm/index.js";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer.js";
|
||||
import { ObjectRetriever } from "../../objects/base.js";
|
||||
import { ToolOutput } from "../../tools/index.js";
|
||||
import { BaseTool } from "../../types.js";
|
||||
import { AgentWorker, Task, TaskStep, TaskStepOutput } from "../types.js";
|
||||
import { ReActChatFormatter } from "./formatter.js";
|
||||
import { ReActOutputParser } from "./outputParser.js";
|
||||
import {
|
||||
ActionReasoningStep,
|
||||
BaseReasoningStep,
|
||||
ObservationReasoningStep,
|
||||
ResponseReasoningStep,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
type ReActAgentWorkerParams = {
|
||||
tools: BaseTool[];
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { randomUUID } from "crypto";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager";
|
||||
import { CallbackManager } from "../../callbacks/CallbackManager.js";
|
||||
import {
|
||||
AgentChatResponse,
|
||||
ChatEngineAgentParams,
|
||||
ChatResponseMode,
|
||||
} from "../../engines/chat";
|
||||
import { ChatMessage, LLM } from "../../llm";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer";
|
||||
import { BaseMemory } from "../../memory/types";
|
||||
import { AgentWorker, Task, TaskStep, TaskStepOutput } from "../types";
|
||||
import { AgentState, BaseAgentRunner, TaskState } from "./types";
|
||||
} from "../../engines/chat/index.js";
|
||||
import { ChatMessage, LLM } from "../../llm/index.js";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer.js";
|
||||
import { BaseMemory } from "../../memory/types.js";
|
||||
import { AgentWorker, Task, TaskStep, TaskStepOutput } from "../types.js";
|
||||
import { AgentState, BaseAgentRunner, TaskState } from "./types.js";
|
||||
|
||||
const validateStepFromArgs = (
|
||||
taskId: string,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AgentChatResponse } from "../../engines/chat";
|
||||
import { BaseAgent, Task, TaskStep, TaskStepOutput } from "../types";
|
||||
import { AgentChatResponse } from "../../engines/chat/index.js";
|
||||
import { BaseAgent, Task, TaskStep, TaskStepOutput } from "../types.js";
|
||||
|
||||
export class TaskState {
|
||||
task!: Task;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { AgentChatResponse, ChatEngineAgentParams } from "../engines/chat";
|
||||
import { QueryEngineParamsNonStreaming } from "../types";
|
||||
import {
|
||||
AgentChatResponse,
|
||||
ChatEngineAgentParams,
|
||||
} from "../engines/chat/index.js";
|
||||
import { QueryEngineParamsNonStreaming } from "../types.js";
|
||||
|
||||
export interface AgentWorker {
|
||||
initializeStep(task: Task, kwargs?: any): TaskStep;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChatMessage } from "../llm";
|
||||
import { ChatMemoryBuffer } from "../memory/ChatMemoryBuffer";
|
||||
import { BaseTool } from "../types";
|
||||
import { TaskStep } from "./types";
|
||||
import { ChatMessage } from "../llm/index.js";
|
||||
import { ChatMemoryBuffer } from "../memory/ChatMemoryBuffer.js";
|
||||
import { BaseTool } from "../types.js";
|
||||
import { TaskStep } from "./types.js";
|
||||
|
||||
/**
|
||||
* Adds the user's input to the memory.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Anthropic } from "@anthropic-ai/sdk";
|
||||
import { NodeWithScore } from "../Node";
|
||||
import { NodeWithScore } from "../Node.js";
|
||||
|
||||
/*
|
||||
An event is a wrapper that groups related operations.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BaseRetriever } from "../Retriever";
|
||||
import { RetrieverQueryEngine } from "../engines/query/RetrieverQueryEngine";
|
||||
import { BaseNodePostprocessor } from "../postprocessors";
|
||||
import { BaseSynthesizer } from "../synthesizers";
|
||||
import { BaseQueryEngine } from "../types";
|
||||
import { LlamaCloudRetriever, RetrieveParams } from "./LlamaCloudRetriever";
|
||||
import { CloudConstructorParams } from "./types";
|
||||
import { BaseRetriever } from "../Retriever.js";
|
||||
import { RetrieverQueryEngine } from "../engines/query/RetrieverQueryEngine.js";
|
||||
import { BaseNodePostprocessor } from "../postprocessors/types.js";
|
||||
import { BaseSynthesizer } from "../synthesizers/types.js";
|
||||
import { BaseQueryEngine } from "../types.js";
|
||||
import { LlamaCloudRetriever, RetrieveParams } from "./LlamaCloudRetriever.js";
|
||||
import { CloudConstructorParams } from "./types.js";
|
||||
|
||||
export class LlamaCloudIndex {
|
||||
params: CloudConstructorParams;
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { PlatformApi, PlatformApiClient } from "@llamaindex/cloud";
|
||||
import { globalsHelper } from "../GlobalsHelper";
|
||||
import { NodeWithScore, ObjectType, jsonToNode } from "../Node";
|
||||
import { BaseRetriever } from "../Retriever";
|
||||
import { ServiceContext, serviceContextFromDefaults } from "../ServiceContext";
|
||||
import { Event } from "../callbacks/CallbackManager";
|
||||
import { globalsHelper } from "../GlobalsHelper.js";
|
||||
import { NodeWithScore, ObjectType, jsonToNode } from "../Node.js";
|
||||
import { BaseRetriever } from "../Retriever.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../ServiceContext.js";
|
||||
import { Event } from "../callbacks/CallbackManager.js";
|
||||
import {
|
||||
ClientParams,
|
||||
CloudConstructorParams,
|
||||
DEFAULT_PROJECT_NAME,
|
||||
} from "./types";
|
||||
import { getClient } from "./utils";
|
||||
} from "./types.js";
|
||||
import { getClient } from "./utils.js";
|
||||
|
||||
export type RetrieveParams = Omit<
|
||||
PlatformApi.RetrievalParams,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from "./LlamaCloudIndex";
|
||||
export * from "./LlamaCloudRetriever";
|
||||
export * from "./LlamaCloudIndex.js";
|
||||
export * from "./LlamaCloudRetriever.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ServiceContext } from "../ServiceContext";
|
||||
import { ServiceContext } from "../ServiceContext.js";
|
||||
|
||||
export const DEFAULT_PROJECT_NAME = "default";
|
||||
export const DEFAULT_BASE_URL = "https://api.cloud.llamaindex.ai";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PlatformApiClient } from "@llamaindex/cloud";
|
||||
import { ClientParams, DEFAULT_BASE_URL } from "./types";
|
||||
import { ClientParams, DEFAULT_BASE_URL } from "./types.js";
|
||||
|
||||
export async function getClient({
|
||||
apiKey,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ImageType } from "../Node";
|
||||
import { MultiModalEmbedding } from "./MultiModalEmbedding";
|
||||
import { readImage } from "./utils";
|
||||
import { ImageType } from "../Node.js";
|
||||
import { MultiModalEmbedding } from "./MultiModalEmbedding.js";
|
||||
import { readImage } from "./utils.js";
|
||||
|
||||
export enum ClipEmbeddingModelType {
|
||||
XENOVA_CLIP_VIT_BASE_PATCH32 = "Xenova/clip-vit-base-patch32",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseEmbedding } from "./types";
|
||||
import { BaseEmbedding } from "./types.js";
|
||||
|
||||
export enum HuggingFaceEmbeddingModelType {
|
||||
XENOVA_ALL_MINILM_L6_V2 = "Xenova/all-MiniLM-L6-v2",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MistralAISession } from "../llm/mistral";
|
||||
import { BaseEmbedding } from "./types";
|
||||
import { MistralAISession } from "../llm/mistral.js";
|
||||
import { BaseEmbedding } from "./types.js";
|
||||
|
||||
export enum MistralAIEmbeddingModelType {
|
||||
MISTRAL_EMBED = "mistral-embed",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ImageType } from "../Node";
|
||||
import { BaseEmbedding } from "./types";
|
||||
import { ImageType } from "../Node.js";
|
||||
import { BaseEmbedding } from "./types.js";
|
||||
|
||||
/*
|
||||
* Base class for Multi Modal embeddings.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Ollama } from "../llm/ollama";
|
||||
import { BaseEmbedding } from "./types";
|
||||
import { Ollama } from "../llm/ollama.js";
|
||||
import { BaseEmbedding } from "./types.js";
|
||||
|
||||
/**
|
||||
* OllamaEmbedding is an alias for Ollama that implements the BaseEmbedding interface.
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
getAzureConfigFromEnv,
|
||||
getAzureModel,
|
||||
shouldUseAzure,
|
||||
} from "../llm/azure";
|
||||
import { OpenAISession, getOpenAISession } from "../llm/open_ai";
|
||||
import { BaseEmbedding } from "./types";
|
||||
} from "../llm/azure.js";
|
||||
import { OpenAISession, getOpenAISession } from "../llm/open_ai.js";
|
||||
import { BaseEmbedding } from "./types.js";
|
||||
|
||||
export const ALL_OPENAI_EMBEDDING_MODELS = {
|
||||
"text-embedding-ada-002": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding";
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding.js";
|
||||
|
||||
export class FireworksEmbedding extends OpenAIEmbedding {
|
||||
constructor(init?: Partial<OpenAIEmbedding>) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export * from "./ClipEmbedding";
|
||||
export * from "./HuggingFaceEmbedding";
|
||||
export * from "./MistralAIEmbedding";
|
||||
export * from "./MultiModalEmbedding";
|
||||
export { OllamaEmbedding } from "./OllamaEmbedding";
|
||||
export * from "./OpenAIEmbedding";
|
||||
export { FireworksEmbedding } from "./fireworks";
|
||||
export { TogetherEmbedding } from "./together";
|
||||
export * from "./types";
|
||||
export * from "./utils";
|
||||
export * from "./ClipEmbedding.js";
|
||||
export * from "./HuggingFaceEmbedding.js";
|
||||
export * from "./MistralAIEmbedding.js";
|
||||
export * from "./MultiModalEmbedding.js";
|
||||
export { OllamaEmbedding } from "./OllamaEmbedding.js";
|
||||
export * from "./OpenAIEmbedding.js";
|
||||
export { FireworksEmbedding } from "./fireworks.js";
|
||||
export { TogetherEmbedding } from "./together.js";
|
||||
export * from "./types.js";
|
||||
export * from "./utils.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding";
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding.js";
|
||||
|
||||
export class TogetherEmbedding extends OpenAIEmbedding {
|
||||
constructor(init?: Partial<OpenAIEmbedding>) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseNode, MetadataMode } from "../Node";
|
||||
import { TransformComponent } from "../ingestion";
|
||||
import { SimilarityType, similarity } from "./utils";
|
||||
import { BaseNode, MetadataMode } from "../Node.js";
|
||||
import { TransformComponent } from "../ingestion/types.js";
|
||||
import { SimilarityType, similarity } from "./utils.js";
|
||||
|
||||
const DEFAULT_EMBED_BATCH_SIZE = 10;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import _ from "lodash";
|
||||
import { ImageType } from "../Node";
|
||||
import { DEFAULT_SIMILARITY_TOP_K } from "../constants";
|
||||
import { defaultFS } from "../env";
|
||||
import { VectorStoreQueryMode } from "../storage/vectorStore/types";
|
||||
import { ImageType } from "../Node.js";
|
||||
import { DEFAULT_SIMILARITY_TOP_K } from "../constants.js";
|
||||
import { VectorStoreQueryMode } from "../storage/vectorStore/types.js";
|
||||
|
||||
/**
|
||||
* Similarity type
|
||||
@@ -46,7 +46,7 @@ export function similarity(
|
||||
|
||||
switch (mode) {
|
||||
case SimilarityType.EUCLIDEAN: {
|
||||
let difference = embedding1.map((x, i) => x - embedding2[i]);
|
||||
const difference = embedding1.map((x, i) => x - embedding2[i]);
|
||||
return -norm(difference);
|
||||
}
|
||||
case SimilarityType.DOT_PRODUCT: {
|
||||
@@ -94,7 +94,7 @@ export function getTopKEmbeddings(
|
||||
);
|
||||
}
|
||||
|
||||
let similarities: { similarity: number; id: number }[] = [];
|
||||
const similarities: { similarity: number; id: number }[] = [];
|
||||
|
||||
for (let i = 0; i < embeddings.length; i++) {
|
||||
const sim = similarity(queryEmbedding, embeddings[i]);
|
||||
@@ -105,8 +105,8 @@ export function getTopKEmbeddings(
|
||||
|
||||
similarities.sort((a, b) => b.similarity - a.similarity); // Reverse sort
|
||||
|
||||
let resultSimilarities: number[] = [];
|
||||
let resultIds: any[] = [];
|
||||
const resultSimilarities: number[] = [];
|
||||
const resultIds: any[] = [];
|
||||
|
||||
for (let i = 0; i < similarityTopK; i++) {
|
||||
if (i >= similarities.length) {
|
||||
@@ -142,21 +142,21 @@ export function getTopKMMREmbeddings(
|
||||
_similarityCutoff: number | null = null,
|
||||
mmrThreshold: number | null = null,
|
||||
): [number[], any[]] {
|
||||
let threshold = mmrThreshold || 0.5;
|
||||
const threshold = mmrThreshold || 0.5;
|
||||
similarityFn = similarityFn || similarity;
|
||||
|
||||
if (embeddingIds === null || embeddingIds.length === 0) {
|
||||
embeddingIds = Array.from({ length: embeddings.length }, (_, i) => i);
|
||||
}
|
||||
let fullEmbedMap = new Map(embeddingIds.map((value, i) => [value, i]));
|
||||
let embedMap = new Map(fullEmbedMap);
|
||||
let embedSimilarity: Map<any, number> = new Map();
|
||||
const fullEmbedMap = new Map(embeddingIds.map((value, i) => [value, i]));
|
||||
const embedMap = new Map(fullEmbedMap);
|
||||
const embedSimilarity: Map<any, number> = new Map();
|
||||
let score: number = Number.NEGATIVE_INFINITY;
|
||||
let highScoreId: any | null = null;
|
||||
|
||||
for (let i = 0; i < embeddings.length; i++) {
|
||||
let emb = embeddings[i];
|
||||
let similarity = similarityFn(queryEmbedding, emb);
|
||||
const emb = embeddings[i];
|
||||
const similarity = similarityFn(queryEmbedding, emb);
|
||||
embedSimilarity.set(embeddingIds[i], similarity);
|
||||
if (similarity * threshold > score) {
|
||||
highScoreId = embeddingIds[i];
|
||||
@@ -164,18 +164,18 @@ export function getTopKMMREmbeddings(
|
||||
}
|
||||
}
|
||||
|
||||
let results: [number, any][] = [];
|
||||
const results: [number, any][] = [];
|
||||
|
||||
let embeddingLength = embeddings.length;
|
||||
let similarityTopKCount = similarityTopK || embeddingLength;
|
||||
const embeddingLength = embeddings.length;
|
||||
const similarityTopKCount = similarityTopK || embeddingLength;
|
||||
|
||||
while (results.length < Math.min(similarityTopKCount, embeddingLength)) {
|
||||
results.push([score, highScoreId]);
|
||||
embedMap.delete(highScoreId!);
|
||||
let recentEmbeddingId = highScoreId;
|
||||
const recentEmbeddingId = highScoreId;
|
||||
score = Number.NEGATIVE_INFINITY;
|
||||
for (let embedId of Array.from(embedMap.keys())) {
|
||||
let overlapWithRecent = similarityFn(
|
||||
for (const embedId of Array.from(embedMap.keys())) {
|
||||
const overlapWithRecent = similarityFn(
|
||||
embeddings[embedMap.get(embedId)!],
|
||||
embeddings[fullEmbedMap.get(recentEmbeddingId!)!],
|
||||
);
|
||||
@@ -192,8 +192,8 @@ export function getTopKMMREmbeddings(
|
||||
}
|
||||
}
|
||||
|
||||
let resultSimilarities = results.map(([s, _]) => s);
|
||||
let resultIds = results.map(([_, n]) => n);
|
||||
const resultSimilarities = results.map(([s, _]) => s);
|
||||
const resultIds = results.map(([_, n]) => n);
|
||||
|
||||
return [resultSimilarities, resultIds];
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { ChatHistory, getHistory } from "../../ChatHistory";
|
||||
import { ChatHistory, getHistory } from "../../ChatHistory.js";
|
||||
import {
|
||||
CondenseQuestionPrompt,
|
||||
defaultCondenseQuestionPrompt,
|
||||
messagesToHistoryStr,
|
||||
} from "../../Prompt";
|
||||
import { Response } from "../../Response";
|
||||
} from "../../Prompt.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext";
|
||||
import { ChatMessage, LLM } from "../../llm";
|
||||
import { extractText, streamReducer } from "../../llm/utils";
|
||||
import { PromptMixin } from "../../prompts";
|
||||
import { BaseQueryEngine } from "../../types";
|
||||
} from "../../ServiceContext.js";
|
||||
import { ChatMessage, LLM } from "../../llm/index.js";
|
||||
import { extractText, streamReducer } from "../../llm/utils.js";
|
||||
import { PromptMixin } from "../../prompts/index.js";
|
||||
import { BaseQueryEngine } from "../../types.js";
|
||||
import {
|
||||
ChatEngine,
|
||||
ChatEngineParamsNonStreaming,
|
||||
ChatEngineParamsStreaming,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
/**
|
||||
* CondenseQuestionChatEngine is used in conjunction with a Index (for example VectorStoreIndex).
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
import { ChatHistory, getHistory } from "../../ChatHistory";
|
||||
import { ContextSystemPrompt } from "../../Prompt";
|
||||
import { Response } from "../../Response";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { randomUUID } from "../../env";
|
||||
import { ChatMessage, ChatResponseChunk, LLM, OpenAI } from "../../llm";
|
||||
import { MessageContent } from "../../llm/types";
|
||||
import { extractText, streamConverter, streamReducer } from "../../llm/utils";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors";
|
||||
import { PromptMixin } from "../../prompts";
|
||||
import { DefaultContextGenerator } from "./DefaultContextGenerator";
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { ChatHistory, getHistory } from "../../ChatHistory.js";
|
||||
import { ContextSystemPrompt } from "../../Prompt.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import { BaseRetriever } from "../../Retriever.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatResponseChunk,
|
||||
LLM,
|
||||
OpenAI,
|
||||
} from "../../llm/index.js";
|
||||
import { MessageContent } from "../../llm/types.js";
|
||||
import {
|
||||
extractText,
|
||||
streamConverter,
|
||||
streamReducer,
|
||||
} from "../../llm/utils.js";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import { PromptMixin } from "../../prompts/Mixin.js";
|
||||
import { DefaultContextGenerator } from "./DefaultContextGenerator.js";
|
||||
import {
|
||||
ChatEngine,
|
||||
ChatEngineParamsNonStreaming,
|
||||
ChatEngineParamsStreaming,
|
||||
ContextGenerator,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
/**
|
||||
* ContextChatEngine uses the Index to get the appropriate context for each query.
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { NodeWithScore, TextNode } from "../../Node";
|
||||
import { ContextSystemPrompt, defaultContextSystemPrompt } from "../../Prompt";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { randomUUID } from "../../env";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors";
|
||||
import { PromptMixin } from "../../prompts";
|
||||
import { Context, ContextGenerator } from "./types";
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { NodeWithScore, TextNode } from "../../Node.js";
|
||||
import {
|
||||
ContextSystemPrompt,
|
||||
defaultContextSystemPrompt,
|
||||
} from "../../Prompt.js";
|
||||
import { BaseRetriever } from "../../Retriever.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import { PromptMixin } from "../../prompts/index.js";
|
||||
import { Context, ContextGenerator } from "./types.js";
|
||||
|
||||
export class DefaultContextGenerator
|
||||
extends PromptMixin
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ChatHistory, getHistory } from "../../ChatHistory";
|
||||
import { Response } from "../../Response";
|
||||
import { ChatResponseChunk, LLM, OpenAI } from "../../llm";
|
||||
import { streamConverter, streamReducer } from "../../llm/utils";
|
||||
import { ChatHistory, getHistory } from "../../ChatHistory.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import { ChatResponseChunk, LLM, OpenAI } from "../../llm/index.js";
|
||||
import { streamConverter, streamReducer } from "../../llm/utils.js";
|
||||
import {
|
||||
ChatEngine,
|
||||
ChatEngineParamsNonStreaming,
|
||||
ChatEngineParamsStreaming,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
/**
|
||||
* SimpleChatEngine is the simplest possible chat engine. Useful for using your own custom prompts.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { CondenseQuestionChatEngine } from "./CondenseQuestionChatEngine";
|
||||
export { ContextChatEngine } from "./ContextChatEngine";
|
||||
export { SimpleChatEngine } from "./SimpleChatEngine";
|
||||
export * from "./types";
|
||||
export { CondenseQuestionChatEngine } from "./CondenseQuestionChatEngine.js";
|
||||
export { ContextChatEngine } from "./ContextChatEngine.js";
|
||||
export { SimpleChatEngine } from "./SimpleChatEngine.js";
|
||||
export * from "./types.js";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ChatHistory } from "../../ChatHistory";
|
||||
import { BaseNode, NodeWithScore } from "../../Node";
|
||||
import { Response } from "../../Response";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { ChatMessage } from "../../llm";
|
||||
import { MessageContent } from "../../llm/types";
|
||||
import { ToolOutput } from "../../tools/types";
|
||||
import { ChatHistory } from "../../ChatHistory.js";
|
||||
import { BaseNode, NodeWithScore } from "../../Node.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import { ChatMessage } from "../../llm/index.js";
|
||||
import { MessageContent } from "../../llm/types.js";
|
||||
import { ToolOutput } from "../../tools/types.js";
|
||||
|
||||
/**
|
||||
* Represents the base parameters for ChatEngine.
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { NodeWithScore } from "../../Node";
|
||||
import { Response } from "../../Response";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import { ServiceContext } from "../../ServiceContext";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { randomUUID } from "../../env";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors";
|
||||
import { PromptMixin } from "../../prompts";
|
||||
import { BaseSynthesizer, ResponseSynthesizer } from "../../synthesizers";
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { NodeWithScore } from "../../Node.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import { BaseRetriever } from "../../Retriever.js";
|
||||
import { ServiceContext } from "../../ServiceContext.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import { PromptMixin } from "../../prompts/Mixin.js";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
ResponseSynthesizer,
|
||||
} from "../../synthesizers/index.js";
|
||||
import {
|
||||
BaseQueryEngine,
|
||||
QueryEngineParamsNonStreaming,
|
||||
QueryEngineParamsStreaming,
|
||||
} from "../../types";
|
||||
} from "../../types.js";
|
||||
|
||||
/**
|
||||
* A query engine that uses a retriever to query an index and then synthesizes the response.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { BaseNode } from "../../Node";
|
||||
import { Response } from "../../Response";
|
||||
import { BaseNode } from "../../Node.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext";
|
||||
import { PromptMixin } from "../../prompts";
|
||||
import { BaseSelector, LLMSingleSelector } from "../../selectors";
|
||||
import { TreeSummarize } from "../../synthesizers";
|
||||
} from "../../ServiceContext.js";
|
||||
import { PromptMixin } from "../../prompts/index.js";
|
||||
import { BaseSelector, LLMSingleSelector } from "../../selectors/index.js";
|
||||
import { TreeSummarize } from "../../synthesizers/index.js";
|
||||
import {
|
||||
BaseQueryEngine,
|
||||
QueryBundle,
|
||||
QueryEngineParamsNonStreaming,
|
||||
QueryEngineParamsStreaming,
|
||||
} from "../../types";
|
||||
} from "../../types.js";
|
||||
|
||||
type RouterQueryEngineTool = {
|
||||
queryEngine: BaseQueryEngine;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { NodeWithScore, TextNode } from "../../Node";
|
||||
import { LLMQuestionGenerator } from "../../QuestionGenerator";
|
||||
import { Response } from "../../Response";
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { NodeWithScore, TextNode } from "../../Node.js";
|
||||
import { LLMQuestionGenerator } from "../../QuestionGenerator.js";
|
||||
import { Response } from "../../Response.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { randomUUID } from "../../env";
|
||||
import { PromptMixin } from "../../prompts";
|
||||
} from "../../ServiceContext.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import { PromptMixin } from "../../prompts/Mixin.js";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
CompactAndRefine,
|
||||
ResponseSynthesizer,
|
||||
} from "../../synthesizers";
|
||||
} from "../../synthesizers/index.js";
|
||||
|
||||
import {
|
||||
BaseQueryEngine,
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
QueryEngineParamsNonStreaming,
|
||||
QueryEngineParamsStreaming,
|
||||
ToolMetadata,
|
||||
} from "../../types";
|
||||
} from "../../types.js";
|
||||
|
||||
import { BaseQuestionGenerator, SubQuestion } from "./types";
|
||||
import { BaseQuestionGenerator, SubQuestion } from "./types.js";
|
||||
|
||||
/**
|
||||
* SubQuestionQueryEngine decomposes a question into subquestions and then
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from "./RetrieverQueryEngine";
|
||||
export * from "./RouterQueryEngine";
|
||||
export * from "./SubQuestionQueryEngine";
|
||||
export * from "./RetrieverQueryEngine.js";
|
||||
export * from "./RouterQueryEngine.js";
|
||||
export * from "./SubQuestionQueryEngine.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ToolMetadata } from "../../types";
|
||||
import { ToolMetadata } from "../../types.js";
|
||||
|
||||
/**
|
||||
* QuestionGenerators generate new questions for the LLM using tools and a user query.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { BaseNode, MetadataMode, TextNode } from "../Node";
|
||||
import { LLM, OpenAI } from "../llm";
|
||||
import { BaseNode, MetadataMode, TextNode } from "../Node.js";
|
||||
import { LLM, OpenAI } from "../llm/index.js";
|
||||
import {
|
||||
defaultKeywordExtractorPromptTemplate,
|
||||
defaultQuestionAnswerPromptTemplate,
|
||||
defaultSummaryExtractorPromptTemplate,
|
||||
defaultTitleCombinePromptTemplate,
|
||||
defaultTitleExtractorPromptTemplate,
|
||||
} from "./prompts";
|
||||
import { BaseExtractor } from "./types";
|
||||
} from "./prompts.js";
|
||||
import { BaseExtractor } from "./types.js";
|
||||
|
||||
const STRIP_REGEX = /(\r\n|\n|\r)/gm;
|
||||
|
||||
@@ -172,7 +172,7 @@ export class TitleExtractor extends BaseExtractor {
|
||||
|
||||
if (nodesToExtractTitle.length === 0) return [];
|
||||
|
||||
let titlesCandidates: string[] = [];
|
||||
const titlesCandidates: string[] = [];
|
||||
let title: string = "";
|
||||
|
||||
for (let i = 0; i < nodesToExtractTitle.length; i++) {
|
||||
@@ -411,7 +411,7 @@ export class SummaryExtractor extends BaseExtractor {
|
||||
nodes.map((node) => this.generateNodeSummary(node)),
|
||||
);
|
||||
|
||||
let metadataList: any[] = nodes.map(() => ({}));
|
||||
const metadataList: any[] = nodes.map(() => ({}));
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
if (i > 0 && this._prevSummary && nodeSummaries[i - 1]) {
|
||||
|
||||
@@ -3,5 +3,5 @@ export {
|
||||
QuestionsAnsweredExtractor,
|
||||
SummaryExtractor,
|
||||
TitleExtractor,
|
||||
} from "./MetadataExtractors";
|
||||
export { BaseExtractor } from "./types";
|
||||
} from "./MetadataExtractors.js";
|
||||
export { BaseExtractor } from "./types.js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseNode, MetadataMode, TextNode } from "../Node";
|
||||
import { TransformComponent } from "../ingestion";
|
||||
import { defaultNodeTextTemplate } from "./prompts";
|
||||
import { BaseNode, MetadataMode, TextNode } from "../Node.js";
|
||||
import { TransformComponent } from "../ingestion/types.js";
|
||||
import { defaultNodeTextTemplate } from "./prompts.js";
|
||||
|
||||
/*
|
||||
* Abstract class for all extractors.
|
||||
@@ -43,16 +43,16 @@ export abstract class BaseExtractor implements TransformComponent {
|
||||
newNodes = nodes.slice();
|
||||
}
|
||||
|
||||
let curMetadataList = await this.extract(newNodes);
|
||||
const curMetadataList = await this.extract(newNodes);
|
||||
|
||||
for (let idx in newNodes) {
|
||||
for (const idx in newNodes) {
|
||||
newNodes[idx].metadata = {
|
||||
...newNodes[idx].metadata,
|
||||
...curMetadataList[idx],
|
||||
};
|
||||
}
|
||||
|
||||
for (let idx in newNodes) {
|
||||
for (const idx in newNodes) {
|
||||
if (excludedEmbedMetadataKeys) {
|
||||
newNodes[idx].excludedEmbedMetadataKeys.concat(
|
||||
excludedEmbedMetadataKeys,
|
||||
|
||||
+31
-31
@@ -1,31 +1,31 @@
|
||||
export * from "./ChatHistory";
|
||||
export * from "./GlobalsHelper";
|
||||
export * from "./Node";
|
||||
export * from "./OutputParser";
|
||||
export * from "./Prompt";
|
||||
export * from "./PromptHelper";
|
||||
export * from "./QuestionGenerator";
|
||||
export * from "./Response";
|
||||
export * from "./Retriever";
|
||||
export * from "./ServiceContext";
|
||||
export * from "./TextSplitter";
|
||||
export * from "./agent";
|
||||
export * from "./callbacks/CallbackManager";
|
||||
export * from "./cloud";
|
||||
export * from "./constants";
|
||||
export * from "./embeddings";
|
||||
export * from "./engines/chat";
|
||||
export * from "./engines/query";
|
||||
export * from "./extractors";
|
||||
export * from "./indices";
|
||||
export * from "./ingestion";
|
||||
export * from "./llm";
|
||||
export * from "./nodeParsers";
|
||||
export * from "./objects";
|
||||
export * from "./postprocessors";
|
||||
export * from "./prompts";
|
||||
export * from "./readers";
|
||||
export * from "./selectors";
|
||||
export * from "./storage";
|
||||
export * from "./synthesizers";
|
||||
export * from "./tools";
|
||||
export * from "./ChatHistory.js";
|
||||
export * from "./GlobalsHelper.js";
|
||||
export * from "./Node.js";
|
||||
export * from "./OutputParser.js";
|
||||
export * from "./Prompt.js";
|
||||
export * from "./PromptHelper.js";
|
||||
export * from "./QuestionGenerator.js";
|
||||
export * from "./Response.js";
|
||||
export * from "./Retriever.js";
|
||||
export * from "./ServiceContext.js";
|
||||
export * from "./TextSplitter.js";
|
||||
export * from "./agent/index.js";
|
||||
export * from "./callbacks/CallbackManager.js";
|
||||
export * from "./cloud/index.js";
|
||||
export * from "./constants.js";
|
||||
export * from "./embeddings/index.js";
|
||||
export * from "./engines/chat/index.js";
|
||||
export * from "./engines/query/index.js";
|
||||
export * from "./extractors/index.js";
|
||||
export * from "./indices/index.js";
|
||||
export * from "./ingestion/index.js";
|
||||
export * from "./llm/index.js";
|
||||
export * from "./nodeParsers/index.js";
|
||||
export * from "./objects/index.js";
|
||||
export * from "./postprocessors/index.js";
|
||||
export * from "./prompts/index.js";
|
||||
export * from "./readers/index.js";
|
||||
export * from "./selectors/index.js";
|
||||
export * from "./storage/index.js";
|
||||
export * from "./synthesizers/index.js";
|
||||
export * from "./tools/index.js";
|
||||
|
||||
@@ -1,112 +1,15 @@
|
||||
import { BaseNode, Document, jsonToNode } from "../Node";
|
||||
import { BaseRetriever } from "../Retriever";
|
||||
import { ServiceContext } from "../ServiceContext";
|
||||
import { randomUUID } from "../env";
|
||||
import { runTransformations } from "../ingestion";
|
||||
import { StorageContext } from "../storage/StorageContext";
|
||||
import { BaseDocumentStore } from "../storage/docStore/types";
|
||||
import { BaseIndexStore } from "../storage/indexStore/types";
|
||||
import { VectorStore } from "../storage/vectorStore/types";
|
||||
import { BaseSynthesizer } from "../synthesizers";
|
||||
import { BaseQueryEngine } from "../types";
|
||||
|
||||
/**
|
||||
* The underlying structure of each index.
|
||||
*/
|
||||
export abstract class IndexStruct {
|
||||
indexId: string;
|
||||
summary?: string;
|
||||
|
||||
constructor(indexId = randomUUID(), summary = undefined) {
|
||||
this.indexId = indexId;
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
indexId: this.indexId,
|
||||
summary: this.summary,
|
||||
};
|
||||
}
|
||||
|
||||
getSummary(): string {
|
||||
if (this.summary === undefined) {
|
||||
throw new Error("summary field of the index dict is not set");
|
||||
}
|
||||
return this.summary;
|
||||
}
|
||||
}
|
||||
|
||||
export enum IndexStructType {
|
||||
SIMPLE_DICT = "simple_dict",
|
||||
LIST = "list",
|
||||
KEYWORD_TABLE = "keyword_table",
|
||||
}
|
||||
|
||||
export class IndexDict extends IndexStruct {
|
||||
nodesDict: Record<string, BaseNode> = {};
|
||||
type: IndexStructType = IndexStructType.SIMPLE_DICT;
|
||||
|
||||
getSummary(): string {
|
||||
if (this.summary === undefined) {
|
||||
throw new Error("summary field of the index dict is not set");
|
||||
}
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
addNode(node: BaseNode, textId?: string) {
|
||||
const vectorId = textId ?? node.id_;
|
||||
this.nodesDict[vectorId] = node;
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
...super.toJson(),
|
||||
nodesDict: this.nodesDict,
|
||||
type: this.type,
|
||||
};
|
||||
}
|
||||
|
||||
delete(nodeId: string) {
|
||||
delete this.nodesDict[nodeId];
|
||||
}
|
||||
}
|
||||
|
||||
export function jsonToIndexStruct(json: any): IndexStruct {
|
||||
if (json.type === IndexStructType.LIST) {
|
||||
const indexList = new IndexList(json.indexId, json.summary);
|
||||
indexList.nodes = json.nodes;
|
||||
return indexList;
|
||||
} else if (json.type === IndexStructType.SIMPLE_DICT) {
|
||||
const indexDict = new IndexDict(json.indexId, json.summary);
|
||||
indexDict.nodesDict = Object.entries(json.nodesDict).reduce<
|
||||
Record<string, BaseNode>
|
||||
>((acc, [key, value]) => {
|
||||
acc[key] = jsonToNode(value);
|
||||
return acc;
|
||||
}, {});
|
||||
return indexDict;
|
||||
} else {
|
||||
throw new Error(`Unknown index struct type: ${json.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class IndexList extends IndexStruct {
|
||||
nodes: string[] = [];
|
||||
type: IndexStructType = IndexStructType.LIST;
|
||||
|
||||
addNode(node: BaseNode) {
|
||||
this.nodes.push(node.id_);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
...super.toJson(),
|
||||
nodes: this.nodes,
|
||||
type: this.type,
|
||||
};
|
||||
}
|
||||
}
|
||||
import { BaseNode, Document } from "../Node.js";
|
||||
import { BaseRetriever } from "../Retriever.js";
|
||||
import { ServiceContext } from "../ServiceContext.js";
|
||||
import { runTransformations } from "../ingestion/IngestionPipeline.js";
|
||||
import { StorageContext } from "../storage/StorageContext.js";
|
||||
import { BaseDocumentStore } from "../storage/docStore/types.js";
|
||||
import { BaseIndexStore } from "../storage/indexStore/types.js";
|
||||
import { VectorStore } from "../storage/vectorStore/types.js";
|
||||
import { BaseSynthesizer } from "../synthesizers/types.js";
|
||||
import { BaseQueryEngine } from "../types.js";
|
||||
import { IndexStruct } from "./IndexStruct.js";
|
||||
import { IndexStructType } from "./json-to-index-struct.js";
|
||||
|
||||
// A table of keywords mapping keywords to text chunks.
|
||||
export class KeywordTable extends IndexStruct {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
|
||||
/**
|
||||
* The underlying structure of each index.
|
||||
*/
|
||||
export abstract class IndexStruct {
|
||||
indexId: string;
|
||||
summary?: string;
|
||||
|
||||
constructor(indexId = randomUUID(), summary = undefined) {
|
||||
this.indexId = indexId;
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
indexId: this.indexId,
|
||||
summary: this.summary,
|
||||
};
|
||||
}
|
||||
|
||||
getSummary(): string {
|
||||
if (this.summary === undefined) {
|
||||
throw new Error("summary field of the index dict is not set");
|
||||
}
|
||||
return this.summary;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "./BaseIndex";
|
||||
export * from "./keyword";
|
||||
export * from "./summary";
|
||||
export * from "./vectorStore";
|
||||
export * from "./BaseIndex.js";
|
||||
export * from "./keyword/index.js";
|
||||
export * from "./summary/index.js";
|
||||
export * from "./vectorStore/index.js";
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { BaseNode, jsonToNode } from "../Node.js";
|
||||
import { IndexStruct } from "./IndexStruct.js";
|
||||
|
||||
export enum IndexStructType {
|
||||
SIMPLE_DICT = "simple_dict",
|
||||
LIST = "list",
|
||||
KEYWORD_TABLE = "keyword_table",
|
||||
}
|
||||
export class IndexDict extends IndexStruct {
|
||||
nodesDict: Record<string, BaseNode> = {};
|
||||
type: IndexStructType = IndexStructType.SIMPLE_DICT;
|
||||
|
||||
getSummary(): string {
|
||||
if (this.summary === undefined) {
|
||||
throw new Error("summary field of the index dict is not set");
|
||||
}
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
addNode(node: BaseNode, textId?: string) {
|
||||
const vectorId = textId ?? node.id_;
|
||||
this.nodesDict[vectorId] = node;
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
...super.toJson(),
|
||||
nodesDict: this.nodesDict,
|
||||
type: this.type,
|
||||
};
|
||||
}
|
||||
|
||||
delete(nodeId: string) {
|
||||
delete this.nodesDict[nodeId];
|
||||
}
|
||||
}
|
||||
export function jsonToIndexStruct(json: any): IndexStruct {
|
||||
if (json.type === IndexStructType.LIST) {
|
||||
const indexList = new IndexList(json.indexId, json.summary);
|
||||
indexList.nodes = json.nodes;
|
||||
return indexList;
|
||||
} else if (json.type === IndexStructType.SIMPLE_DICT) {
|
||||
const indexDict = new IndexDict(json.indexId, json.summary);
|
||||
indexDict.nodesDict = Object.entries(json.nodesDict).reduce<
|
||||
Record<string, BaseNode>
|
||||
>((acc, [key, value]) => {
|
||||
acc[key] = jsonToNode(value);
|
||||
return acc;
|
||||
}, {});
|
||||
return indexDict;
|
||||
} else {
|
||||
throw new Error(`Unknown index struct type: ${json.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class IndexList extends IndexStruct {
|
||||
nodes: string[] = [];
|
||||
type: IndexStructType = IndexStructType.LIST;
|
||||
|
||||
addNode(node: BaseNode) {
|
||||
this.nodes.push(node.id_);
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
return {
|
||||
...super.toJson(),
|
||||
nodes: this.nodes,
|
||||
type: this.type,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
import { BaseNode, Document, MetadataMode } from "../../Node";
|
||||
import { defaultKeywordExtractPrompt } from "../../Prompt";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext";
|
||||
import { RetrieverQueryEngine } from "../../engines/query";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors";
|
||||
import {
|
||||
BaseDocumentStore,
|
||||
StorageContext,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage";
|
||||
import { BaseSynthesizer } from "../../synthesizers";
|
||||
import { BaseQueryEngine } from "../../types";
|
||||
import {
|
||||
BaseIndex,
|
||||
BaseIndexInit,
|
||||
IndexStructType,
|
||||
KeywordTable,
|
||||
} from "../BaseIndex";
|
||||
import {
|
||||
KeywordTableLLMRetriever,
|
||||
KeywordTableRAKERetriever,
|
||||
KeywordTableSimpleRetriever,
|
||||
} from "./KeywordTableIndexRetriever";
|
||||
import { extractKeywordsGivenResponse } from "./utils";
|
||||
|
||||
export interface KeywordIndexOptions {
|
||||
nodes?: BaseNode[];
|
||||
indexStruct?: KeywordTable;
|
||||
indexId?: string;
|
||||
serviceContext?: ServiceContext;
|
||||
storageContext?: StorageContext;
|
||||
}
|
||||
export enum KeywordTableRetrieverMode {
|
||||
DEFAULT = "DEFAULT",
|
||||
SIMPLE = "SIMPLE",
|
||||
RAKE = "RAKE",
|
||||
}
|
||||
|
||||
const KeywordTableRetrieverMap = {
|
||||
[KeywordTableRetrieverMode.DEFAULT]: KeywordTableLLMRetriever,
|
||||
[KeywordTableRetrieverMode.SIMPLE]: KeywordTableSimpleRetriever,
|
||||
[KeywordTableRetrieverMode.RAKE]: KeywordTableRAKERetriever,
|
||||
};
|
||||
|
||||
/**
|
||||
* The KeywordTableIndex, an index that extracts keywords from each Node and builds a mapping from each keyword to the corresponding Nodes of that keyword.
|
||||
*/
|
||||
export class KeywordTableIndex extends BaseIndex<KeywordTable> {
|
||||
constructor(init: BaseIndexInit<KeywordTable>) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
static async init(options: KeywordIndexOptions): Promise<KeywordTableIndex> {
|
||||
const storageContext =
|
||||
options.storageContext ?? (await storageContextFromDefaults({}));
|
||||
const serviceContext =
|
||||
options.serviceContext ?? serviceContextFromDefaults({});
|
||||
const { docStore, indexStore } = storageContext;
|
||||
|
||||
// Setup IndexStruct from storage
|
||||
let indexStructs = (await indexStore.getIndexStructs()) as KeywordTable[];
|
||||
let indexStruct: KeywordTable | null;
|
||||
|
||||
if (options.indexStruct && indexStructs.length > 0) {
|
||||
throw new Error(
|
||||
"Cannot initialize index with both indexStruct and indexStore",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
)) as KeywordTable;
|
||||
} else {
|
||||
indexStruct = null;
|
||||
}
|
||||
|
||||
// check indexStruct type
|
||||
if (indexStruct && indexStruct.type !== IndexStructType.KEYWORD_TABLE) {
|
||||
throw new Error(
|
||||
"Attempting to initialize KeywordTableIndex with non-keyword table indexStruct",
|
||||
);
|
||||
}
|
||||
|
||||
if (indexStruct) {
|
||||
if (options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize KeywordTableIndex with both nodes and indexStruct",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize KeywordTableIndex without nodes or indexStruct",
|
||||
);
|
||||
}
|
||||
indexStruct = await KeywordTableIndex.buildIndexFromNodes(
|
||||
options.nodes,
|
||||
storageContext.docStore,
|
||||
serviceContext,
|
||||
);
|
||||
|
||||
await indexStore.addIndexStruct(indexStruct);
|
||||
}
|
||||
|
||||
return new KeywordTableIndex({
|
||||
storageContext,
|
||||
serviceContext,
|
||||
docStore,
|
||||
indexStore,
|
||||
indexStruct,
|
||||
});
|
||||
}
|
||||
|
||||
asRetriever(options?: any): BaseRetriever {
|
||||
const { mode = KeywordTableRetrieverMode.DEFAULT, ...otherOptions } =
|
||||
options ?? {};
|
||||
const KeywordTableRetriever =
|
||||
KeywordTableRetrieverMap[mode as KeywordTableRetrieverMode];
|
||||
if (KeywordTableRetriever) {
|
||||
return new KeywordTableRetriever({ index: this, ...otherOptions });
|
||||
}
|
||||
throw new Error(`Unknown retriever mode: ${mode}`);
|
||||
}
|
||||
|
||||
asQueryEngine(options?: {
|
||||
retriever?: BaseRetriever;
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: unknown;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine {
|
||||
const { retriever, responseSynthesizer } = options ?? {};
|
||||
return new RetrieverQueryEngine(
|
||||
retriever ?? this.asRetriever(),
|
||||
responseSynthesizer,
|
||||
options?.preFilters,
|
||||
options?.nodePostprocessors,
|
||||
);
|
||||
}
|
||||
|
||||
static async extractKeywords(
|
||||
text: string,
|
||||
serviceContext: ServiceContext,
|
||||
): Promise<Set<string>> {
|
||||
const response = await serviceContext.llm.complete({
|
||||
prompt: defaultKeywordExtractPrompt({
|
||||
context: text,
|
||||
}),
|
||||
});
|
||||
return extractKeywordsGivenResponse(response.text, "KEYWORDS:");
|
||||
}
|
||||
|
||||
/**
|
||||
* High level API: split documents, get keywords, and build index.
|
||||
* @param documents
|
||||
* @param storageContext
|
||||
* @param serviceContext
|
||||
* @returns
|
||||
*/
|
||||
static async fromDocuments(
|
||||
documents: Document[],
|
||||
args: {
|
||||
storageContext?: StorageContext;
|
||||
serviceContext?: ServiceContext;
|
||||
} = {},
|
||||
): Promise<KeywordTableIndex> {
|
||||
let { storageContext, serviceContext } = args;
|
||||
storageContext = storageContext ?? (await storageContextFromDefaults({}));
|
||||
serviceContext = serviceContext ?? serviceContextFromDefaults({});
|
||||
const docStore = storageContext.docStore;
|
||||
|
||||
docStore.addDocuments(documents, true);
|
||||
for (const doc of documents) {
|
||||
docStore.setDocumentHash(doc.id_, doc.hash);
|
||||
}
|
||||
|
||||
const nodes = serviceContext.nodeParser.getNodesFromDocuments(documents);
|
||||
const index = await KeywordTableIndex.init({
|
||||
nodes,
|
||||
storageContext,
|
||||
serviceContext,
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get keywords for nodes and place them into the index.
|
||||
* @param nodes
|
||||
* @param serviceContext
|
||||
* @param vectorStore
|
||||
* @returns
|
||||
*/
|
||||
static async buildIndexFromNodes(
|
||||
nodes: BaseNode[],
|
||||
docStore: BaseDocumentStore,
|
||||
serviceContext: ServiceContext,
|
||||
): Promise<KeywordTable> {
|
||||
const indexStruct = new KeywordTable();
|
||||
await docStore.addDocuments(nodes, true);
|
||||
for (const node of nodes) {
|
||||
const keywords = await KeywordTableIndex.extractKeywords(
|
||||
node.getContent(MetadataMode.LLM),
|
||||
serviceContext,
|
||||
);
|
||||
indexStruct.addNode([...keywords], node.id_);
|
||||
}
|
||||
return indexStruct;
|
||||
}
|
||||
|
||||
async insertNodes(nodes: BaseNode[]) {
|
||||
for (let node of nodes) {
|
||||
const keywords = await KeywordTableIndex.extractKeywords(
|
||||
node.getContent(MetadataMode.LLM),
|
||||
this.serviceContext,
|
||||
);
|
||||
this.indexStruct.addNode([...keywords], node.id_);
|
||||
}
|
||||
}
|
||||
|
||||
deleteNode(nodeId: string): void {
|
||||
const keywordsToDelete: Set<string> = new Set();
|
||||
for (const [keyword, existingNodeIds] of Object.entries(
|
||||
this.indexStruct.table,
|
||||
)) {
|
||||
const index = existingNodeIds.indexOf(nodeId);
|
||||
if (index !== -1) {
|
||||
existingNodeIds.splice(index, 1);
|
||||
|
||||
// Delete keywords that have zero nodes
|
||||
if (existingNodeIds.length === 0) {
|
||||
keywordsToDelete.add(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.indexStruct.deleteNode([...keywordsToDelete], nodeId);
|
||||
}
|
||||
|
||||
async deleteNodes(nodeIds: string[], deleteFromDocStore: boolean) {
|
||||
nodeIds.forEach((nodeId) => {
|
||||
this.deleteNode(nodeId);
|
||||
});
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
for (const nodeId of nodeIds) {
|
||||
await this.docStore.deleteDocument(nodeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
await this.storageContext.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
|
||||
async deleteRefDoc(
|
||||
refDocId: string,
|
||||
deleteFromDocStore?: boolean,
|
||||
): Promise<void> {
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refDocId);
|
||||
|
||||
if (!refDocInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.deleteNodes(refDocInfo.nodeIds, false);
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
await this.docStore.deleteRefDoc(refDocId, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
import { NodeWithScore } from "../../Node";
|
||||
import {
|
||||
defaultKeywordExtractPrompt,
|
||||
defaultQueryKeywordExtractPrompt,
|
||||
KeywordExtractPrompt,
|
||||
QueryKeywordExtractPrompt,
|
||||
} from "../../Prompt";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import { ServiceContext } from "../../ServiceContext";
|
||||
import { BaseDocumentStore } from "../../storage/docStore/types";
|
||||
import { KeywordTable } from "../BaseIndex";
|
||||
import { KeywordTableIndex } from "./KeywordTableIndex";
|
||||
import {
|
||||
extractKeywordsGivenResponse,
|
||||
rakeExtractKeywords,
|
||||
simpleExtractKeywords,
|
||||
} from "./utils";
|
||||
|
||||
// Base Keyword Table Retriever
|
||||
abstract class BaseKeywordTableRetriever implements BaseRetriever {
|
||||
protected index: KeywordTableIndex;
|
||||
protected indexStruct: KeywordTable;
|
||||
protected docstore: BaseDocumentStore;
|
||||
protected serviceContext: ServiceContext;
|
||||
|
||||
protected maxKeywordsPerQuery: number; // Maximum number of keywords to extract from query.
|
||||
protected numChunksPerQuery: number; // Maximum number of text chunks to query.
|
||||
protected keywordExtractTemplate: KeywordExtractPrompt; // A Keyword Extraction Prompt
|
||||
protected queryKeywordExtractTemplate: QueryKeywordExtractPrompt; // A Query Keyword Extraction Prompt
|
||||
|
||||
constructor({
|
||||
index,
|
||||
keywordExtractTemplate,
|
||||
queryKeywordExtractTemplate,
|
||||
maxKeywordsPerQuery = 10,
|
||||
numChunksPerQuery = 10,
|
||||
}: {
|
||||
index: KeywordTableIndex;
|
||||
keywordExtractTemplate?: KeywordExtractPrompt;
|
||||
queryKeywordExtractTemplate?: QueryKeywordExtractPrompt;
|
||||
maxKeywordsPerQuery: number;
|
||||
numChunksPerQuery: number;
|
||||
}) {
|
||||
this.index = index;
|
||||
this.indexStruct = index.indexStruct;
|
||||
this.docstore = index.docStore;
|
||||
this.serviceContext = index.serviceContext;
|
||||
|
||||
this.maxKeywordsPerQuery = maxKeywordsPerQuery;
|
||||
this.numChunksPerQuery = numChunksPerQuery;
|
||||
this.keywordExtractTemplate =
|
||||
keywordExtractTemplate || defaultKeywordExtractPrompt;
|
||||
this.queryKeywordExtractTemplate =
|
||||
queryKeywordExtractTemplate || defaultQueryKeywordExtractPrompt;
|
||||
}
|
||||
|
||||
abstract getKeywords(query: string): Promise<string[]>;
|
||||
|
||||
async retrieve(query: string): Promise<NodeWithScore[]> {
|
||||
const keywords = await this.getKeywords(query);
|
||||
const chunkIndicesCount: { [key: string]: number } = {};
|
||||
const filteredKeywords = keywords.filter((keyword) =>
|
||||
this.indexStruct.table.has(keyword),
|
||||
);
|
||||
|
||||
for (let keyword of filteredKeywords) {
|
||||
for (let nodeId of this.indexStruct.table.get(keyword) || []) {
|
||||
chunkIndicesCount[nodeId] = (chunkIndicesCount[nodeId] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const sortedChunkIndices = Object.keys(chunkIndicesCount)
|
||||
.sort((a, b) => chunkIndicesCount[b] - chunkIndicesCount[a])
|
||||
.slice(0, this.numChunksPerQuery);
|
||||
|
||||
const sortedNodes = await this.docstore.getNodes(sortedChunkIndices);
|
||||
|
||||
return sortedNodes.map((node) => ({ node }));
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.index.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts keywords using LLMs.
|
||||
export class KeywordTableLLMRetriever extends BaseKeywordTableRetriever {
|
||||
async getKeywords(query: string): Promise<string[]> {
|
||||
const response = await this.serviceContext.llm.complete({
|
||||
prompt: this.queryKeywordExtractTemplate({
|
||||
question: query,
|
||||
maxKeywords: this.maxKeywordsPerQuery,
|
||||
}),
|
||||
});
|
||||
const keywords = extractKeywordsGivenResponse(response.text, "KEYWORDS:");
|
||||
return [...keywords];
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts keywords using simple regex-based keyword extractor.
|
||||
export class KeywordTableSimpleRetriever extends BaseKeywordTableRetriever {
|
||||
getKeywords(query: string): Promise<string[]> {
|
||||
return Promise.resolve([
|
||||
...simpleExtractKeywords(query, this.maxKeywordsPerQuery),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts keywords using RAKE keyword extractor
|
||||
export class KeywordTableRAKERetriever extends BaseKeywordTableRetriever {
|
||||
getKeywords(query: string): Promise<string[]> {
|
||||
return Promise.resolve([
|
||||
...rakeExtractKeywords(query, this.maxKeywordsPerQuery),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,377 @@
|
||||
export {
|
||||
KeywordTableIndex,
|
||||
KeywordTableRetrieverMode,
|
||||
} from "./KeywordTableIndex";
|
||||
export {
|
||||
KeywordTableLLMRetriever,
|
||||
KeywordTableRAKERetriever,
|
||||
KeywordTableSimpleRetriever,
|
||||
} from "./KeywordTableIndexRetriever";
|
||||
import { BaseNode, Document, MetadataMode, NodeWithScore } from "../../Node.js";
|
||||
import {
|
||||
KeywordExtractPrompt,
|
||||
QueryKeywordExtractPrompt,
|
||||
defaultKeywordExtractPrompt,
|
||||
defaultQueryKeywordExtractPrompt,
|
||||
} from "../../Prompt.js";
|
||||
import { BaseRetriever } from "../../Retriever.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext.js";
|
||||
import { RetrieverQueryEngine } from "../../engines/query/index.js";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import {
|
||||
BaseDocumentStore,
|
||||
StorageContext,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage/index.js";
|
||||
import { BaseSynthesizer } from "../../synthesizers/index.js";
|
||||
import { BaseQueryEngine } from "../../types.js";
|
||||
import { BaseIndex, BaseIndexInit, KeywordTable } from "../BaseIndex.js";
|
||||
import { IndexStructType } from "../json-to-index-struct.js";
|
||||
import {
|
||||
extractKeywordsGivenResponse,
|
||||
rakeExtractKeywords,
|
||||
simpleExtractKeywords,
|
||||
} from "./utils.js";
|
||||
|
||||
export interface KeywordIndexOptions {
|
||||
nodes?: BaseNode[];
|
||||
indexStruct?: KeywordTable;
|
||||
indexId?: string;
|
||||
serviceContext?: ServiceContext;
|
||||
storageContext?: StorageContext;
|
||||
}
|
||||
export enum KeywordTableRetrieverMode {
|
||||
DEFAULT = "DEFAULT",
|
||||
SIMPLE = "SIMPLE",
|
||||
RAKE = "RAKE",
|
||||
}
|
||||
|
||||
// Base Keyword Table Retriever
|
||||
abstract class BaseKeywordTableRetriever implements BaseRetriever {
|
||||
protected index: KeywordTableIndex;
|
||||
protected indexStruct: KeywordTable;
|
||||
protected docstore: BaseDocumentStore;
|
||||
protected serviceContext: ServiceContext;
|
||||
|
||||
protected maxKeywordsPerQuery: number; // Maximum number of keywords to extract from query.
|
||||
protected numChunksPerQuery: number; // Maximum number of text chunks to query.
|
||||
protected keywordExtractTemplate: KeywordExtractPrompt; // A Keyword Extraction Prompt
|
||||
protected queryKeywordExtractTemplate: QueryKeywordExtractPrompt; // A Query Keyword Extraction Prompt
|
||||
|
||||
constructor({
|
||||
index,
|
||||
keywordExtractTemplate,
|
||||
queryKeywordExtractTemplate,
|
||||
maxKeywordsPerQuery = 10,
|
||||
numChunksPerQuery = 10,
|
||||
}: {
|
||||
index: KeywordTableIndex;
|
||||
keywordExtractTemplate?: KeywordExtractPrompt;
|
||||
queryKeywordExtractTemplate?: QueryKeywordExtractPrompt;
|
||||
maxKeywordsPerQuery: number;
|
||||
numChunksPerQuery: number;
|
||||
}) {
|
||||
this.index = index;
|
||||
this.indexStruct = index.indexStruct;
|
||||
this.docstore = index.docStore;
|
||||
this.serviceContext = index.serviceContext;
|
||||
|
||||
this.maxKeywordsPerQuery = maxKeywordsPerQuery;
|
||||
this.numChunksPerQuery = numChunksPerQuery;
|
||||
this.keywordExtractTemplate =
|
||||
keywordExtractTemplate || defaultKeywordExtractPrompt;
|
||||
this.queryKeywordExtractTemplate =
|
||||
queryKeywordExtractTemplate || defaultQueryKeywordExtractPrompt;
|
||||
}
|
||||
|
||||
abstract getKeywords(query: string): Promise<string[]>;
|
||||
|
||||
async retrieve(query: string): Promise<NodeWithScore[]> {
|
||||
const keywords = await this.getKeywords(query);
|
||||
const chunkIndicesCount: { [key: string]: number } = {};
|
||||
const filteredKeywords = keywords.filter((keyword) =>
|
||||
this.indexStruct.table.has(keyword),
|
||||
);
|
||||
|
||||
for (const keyword of filteredKeywords) {
|
||||
for (const nodeId of this.indexStruct.table.get(keyword) || []) {
|
||||
chunkIndicesCount[nodeId] = (chunkIndicesCount[nodeId] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
const sortedChunkIndices = Object.keys(chunkIndicesCount)
|
||||
.sort((a, b) => chunkIndicesCount[b] - chunkIndicesCount[a])
|
||||
.slice(0, this.numChunksPerQuery);
|
||||
|
||||
const sortedNodes = await this.docstore.getNodes(sortedChunkIndices);
|
||||
|
||||
return sortedNodes.map((node) => ({ node }));
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.index.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts keywords using LLMs.
|
||||
export class KeywordTableLLMRetriever extends BaseKeywordTableRetriever {
|
||||
async getKeywords(query: string): Promise<string[]> {
|
||||
const response = await this.serviceContext.llm.complete({
|
||||
prompt: this.queryKeywordExtractTemplate({
|
||||
question: query,
|
||||
maxKeywords: this.maxKeywordsPerQuery,
|
||||
}),
|
||||
});
|
||||
const keywords = extractKeywordsGivenResponse(response.text, "KEYWORDS:");
|
||||
return [...keywords];
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts keywords using simple regex-based keyword extractor.
|
||||
export class KeywordTableSimpleRetriever extends BaseKeywordTableRetriever {
|
||||
getKeywords(query: string): Promise<string[]> {
|
||||
return Promise.resolve([
|
||||
...simpleExtractKeywords(query, this.maxKeywordsPerQuery),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts keywords using RAKE keyword extractor
|
||||
export class KeywordTableRAKERetriever extends BaseKeywordTableRetriever {
|
||||
getKeywords(query: string): Promise<string[]> {
|
||||
return Promise.resolve([
|
||||
...rakeExtractKeywords(query, this.maxKeywordsPerQuery),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const KeywordTableRetrieverMap = {
|
||||
[KeywordTableRetrieverMode.DEFAULT]: KeywordTableLLMRetriever,
|
||||
[KeywordTableRetrieverMode.SIMPLE]: KeywordTableSimpleRetriever,
|
||||
[KeywordTableRetrieverMode.RAKE]: KeywordTableRAKERetriever,
|
||||
};
|
||||
|
||||
/**
|
||||
* The KeywordTableIndex, an index that extracts keywords from each Node and builds a mapping from each keyword to the corresponding Nodes of that keyword.
|
||||
*/
|
||||
export class KeywordTableIndex extends BaseIndex<KeywordTable> {
|
||||
constructor(init: BaseIndexInit<KeywordTable>) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
static async init(options: KeywordIndexOptions): Promise<KeywordTableIndex> {
|
||||
const storageContext =
|
||||
options.storageContext ?? (await storageContextFromDefaults({}));
|
||||
const serviceContext =
|
||||
options.serviceContext ?? serviceContextFromDefaults({});
|
||||
const { docStore, indexStore } = storageContext;
|
||||
|
||||
// Setup IndexStruct from storage
|
||||
const indexStructs = (await indexStore.getIndexStructs()) as KeywordTable[];
|
||||
let indexStruct: KeywordTable | null;
|
||||
|
||||
if (options.indexStruct && indexStructs.length > 0) {
|
||||
throw new Error(
|
||||
"Cannot initialize index with both indexStruct and indexStore",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
)) as KeywordTable;
|
||||
} else {
|
||||
indexStruct = null;
|
||||
}
|
||||
|
||||
// check indexStruct type
|
||||
if (indexStruct && indexStruct.type !== IndexStructType.KEYWORD_TABLE) {
|
||||
throw new Error(
|
||||
"Attempting to initialize KeywordTableIndex with non-keyword table indexStruct",
|
||||
);
|
||||
}
|
||||
|
||||
if (indexStruct) {
|
||||
if (options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize KeywordTableIndex with both nodes and indexStruct",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize KeywordTableIndex without nodes or indexStruct",
|
||||
);
|
||||
}
|
||||
indexStruct = await KeywordTableIndex.buildIndexFromNodes(
|
||||
options.nodes,
|
||||
storageContext.docStore,
|
||||
serviceContext,
|
||||
);
|
||||
|
||||
await indexStore.addIndexStruct(indexStruct);
|
||||
}
|
||||
|
||||
return new KeywordTableIndex({
|
||||
storageContext,
|
||||
serviceContext,
|
||||
docStore,
|
||||
indexStore,
|
||||
indexStruct,
|
||||
});
|
||||
}
|
||||
|
||||
asRetriever(options?: any): BaseRetriever {
|
||||
const { mode = KeywordTableRetrieverMode.DEFAULT, ...otherOptions } =
|
||||
options ?? {};
|
||||
const KeywordTableRetriever =
|
||||
KeywordTableRetrieverMap[mode as KeywordTableRetrieverMode];
|
||||
if (KeywordTableRetriever) {
|
||||
return new KeywordTableRetriever({ index: this, ...otherOptions });
|
||||
}
|
||||
throw new Error(`Unknown retriever mode: ${mode}`);
|
||||
}
|
||||
|
||||
asQueryEngine(options?: {
|
||||
retriever?: BaseRetriever;
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: unknown;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine {
|
||||
const { retriever, responseSynthesizer } = options ?? {};
|
||||
return new RetrieverQueryEngine(
|
||||
retriever ?? this.asRetriever(),
|
||||
responseSynthesizer,
|
||||
options?.preFilters,
|
||||
options?.nodePostprocessors,
|
||||
);
|
||||
}
|
||||
|
||||
static async extractKeywords(
|
||||
text: string,
|
||||
serviceContext: ServiceContext,
|
||||
): Promise<Set<string>> {
|
||||
const response = await serviceContext.llm.complete({
|
||||
prompt: defaultKeywordExtractPrompt({
|
||||
context: text,
|
||||
}),
|
||||
});
|
||||
return extractKeywordsGivenResponse(response.text, "KEYWORDS:");
|
||||
}
|
||||
|
||||
/**
|
||||
* High level API: split documents, get keywords, and build index.
|
||||
* @param documents
|
||||
* @param storageContext
|
||||
* @param serviceContext
|
||||
* @returns
|
||||
*/
|
||||
static async fromDocuments(
|
||||
documents: Document[],
|
||||
args: {
|
||||
storageContext?: StorageContext;
|
||||
serviceContext?: ServiceContext;
|
||||
} = {},
|
||||
): Promise<KeywordTableIndex> {
|
||||
let { storageContext, serviceContext } = args;
|
||||
storageContext = storageContext ?? (await storageContextFromDefaults({}));
|
||||
serviceContext = serviceContext ?? serviceContextFromDefaults({});
|
||||
const docStore = storageContext.docStore;
|
||||
|
||||
docStore.addDocuments(documents, true);
|
||||
for (const doc of documents) {
|
||||
docStore.setDocumentHash(doc.id_, doc.hash);
|
||||
}
|
||||
|
||||
const nodes = serviceContext.nodeParser.getNodesFromDocuments(documents);
|
||||
const index = await KeywordTableIndex.init({
|
||||
nodes,
|
||||
storageContext,
|
||||
serviceContext,
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get keywords for nodes and place them into the index.
|
||||
* @param nodes
|
||||
* @param serviceContext
|
||||
* @param vectorStore
|
||||
* @returns
|
||||
*/
|
||||
static async buildIndexFromNodes(
|
||||
nodes: BaseNode[],
|
||||
docStore: BaseDocumentStore,
|
||||
serviceContext: ServiceContext,
|
||||
): Promise<KeywordTable> {
|
||||
const indexStruct = new KeywordTable();
|
||||
await docStore.addDocuments(nodes, true);
|
||||
for (const node of nodes) {
|
||||
const keywords = await KeywordTableIndex.extractKeywords(
|
||||
node.getContent(MetadataMode.LLM),
|
||||
serviceContext,
|
||||
);
|
||||
indexStruct.addNode([...keywords], node.id_);
|
||||
}
|
||||
return indexStruct;
|
||||
}
|
||||
|
||||
async insertNodes(nodes: BaseNode[]) {
|
||||
for (const node of nodes) {
|
||||
const keywords = await KeywordTableIndex.extractKeywords(
|
||||
node.getContent(MetadataMode.LLM),
|
||||
this.serviceContext,
|
||||
);
|
||||
this.indexStruct.addNode([...keywords], node.id_);
|
||||
}
|
||||
}
|
||||
|
||||
deleteNode(nodeId: string): void {
|
||||
const keywordsToDelete: Set<string> = new Set();
|
||||
for (const [keyword, existingNodeIds] of Object.entries(
|
||||
this.indexStruct.table,
|
||||
)) {
|
||||
const index = existingNodeIds.indexOf(nodeId);
|
||||
if (index !== -1) {
|
||||
existingNodeIds.splice(index, 1);
|
||||
|
||||
// Delete keywords that have zero nodes
|
||||
if (existingNodeIds.length === 0) {
|
||||
keywordsToDelete.add(keyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.indexStruct.deleteNode([...keywordsToDelete], nodeId);
|
||||
}
|
||||
|
||||
async deleteNodes(nodeIds: string[], deleteFromDocStore: boolean) {
|
||||
nodeIds.forEach((nodeId) => {
|
||||
this.deleteNode(nodeId);
|
||||
});
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
for (const nodeId of nodeIds) {
|
||||
await this.docStore.deleteDocument(nodeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
await this.storageContext.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
|
||||
async deleteRefDoc(
|
||||
refDocId: string,
|
||||
deleteFromDocStore?: boolean,
|
||||
): Promise<void> {
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refDocId);
|
||||
|
||||
if (!refDocInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.deleteNodes(refDocInfo.nodeIds, false);
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
await this.docStore.deleteRefDoc(refDocId, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ export function expandTokensWithSubtokens(tokens: Set<string>): Set<string> {
|
||||
const results: Set<string> = new Set();
|
||||
const regex: RegExp = /\w+/g;
|
||||
|
||||
for (let token of tokens) {
|
||||
for (const token of tokens) {
|
||||
results.add(token);
|
||||
const subTokens: RegExpMatchArray | null = token.match(regex);
|
||||
if (subTokens && subTokens.length > 1) {
|
||||
for (let w of subTokens) {
|
||||
for (const w of subTokens) {
|
||||
results.add(w);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ export function extractKeywordsGivenResponse(
|
||||
}
|
||||
|
||||
const keywords: string[] = response.split(",");
|
||||
for (let k of keywords) {
|
||||
for (const k of keywords) {
|
||||
let rk: string = k;
|
||||
if (lowercase) {
|
||||
rk = rk.toLowerCase();
|
||||
@@ -47,13 +47,13 @@ export function simpleExtractKeywords(
|
||||
maxKeywords?: number,
|
||||
): Set<string> {
|
||||
const regex: RegExp = /\w+/g;
|
||||
let tokens: string[] = [...textChunk.matchAll(regex)].map((token) =>
|
||||
const tokens: string[] = [...textChunk.matchAll(regex)].map((token) =>
|
||||
token[0].toLowerCase().trim(),
|
||||
);
|
||||
|
||||
// Creating a frequency map
|
||||
const valueCounts: { [key: string]: number } = {};
|
||||
for (let token of tokens) {
|
||||
for (const token of tokens) {
|
||||
valueCounts[token] = (valueCounts[token] || 0) + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
import _ from "lodash";
|
||||
import { BaseNode, Document } from "../../Node";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext";
|
||||
import { RetrieverQueryEngine } from "../../engines/query";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors";
|
||||
import {
|
||||
BaseDocumentStore,
|
||||
RefDocInfo,
|
||||
StorageContext,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
CompactAndRefine,
|
||||
ResponseSynthesizer,
|
||||
} from "../../synthesizers";
|
||||
import { BaseQueryEngine } from "../../types";
|
||||
import {
|
||||
BaseIndex,
|
||||
BaseIndexInit,
|
||||
IndexList,
|
||||
IndexStructType,
|
||||
} from "../BaseIndex";
|
||||
import {
|
||||
SummaryIndexLLMRetriever,
|
||||
SummaryIndexRetriever,
|
||||
} from "./SummaryIndexRetriever";
|
||||
|
||||
export enum SummaryRetrieverMode {
|
||||
DEFAULT = "default",
|
||||
// EMBEDDING = "embedding",
|
||||
LLM = "llm",
|
||||
}
|
||||
|
||||
export interface SummaryIndexOptions {
|
||||
nodes?: BaseNode[];
|
||||
indexStruct?: IndexList;
|
||||
indexId?: string;
|
||||
serviceContext?: ServiceContext;
|
||||
storageContext?: StorageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* A SummaryIndex keeps nodes in a sequential order for use with summarization.
|
||||
*/
|
||||
export class SummaryIndex extends BaseIndex<IndexList> {
|
||||
constructor(init: BaseIndexInit<IndexList>) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
static async init(options: SummaryIndexOptions): Promise<SummaryIndex> {
|
||||
const storageContext =
|
||||
options.storageContext ?? (await storageContextFromDefaults({}));
|
||||
const serviceContext =
|
||||
options.serviceContext ?? serviceContextFromDefaults({});
|
||||
const { docStore, indexStore } = storageContext;
|
||||
|
||||
// Setup IndexStruct from storage
|
||||
let indexStructs = (await indexStore.getIndexStructs()) as IndexList[];
|
||||
let indexStruct: IndexList | null;
|
||||
|
||||
if (options.indexStruct && indexStructs.length > 0) {
|
||||
throw new Error(
|
||||
"Cannot initialize index with both indexStruct and indexStore",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
)) as IndexList;
|
||||
} else {
|
||||
indexStruct = null;
|
||||
}
|
||||
|
||||
// check indexStruct type
|
||||
if (indexStruct && indexStruct.type !== IndexStructType.LIST) {
|
||||
throw new Error(
|
||||
"Attempting to initialize SummaryIndex with non-list indexStruct",
|
||||
);
|
||||
}
|
||||
|
||||
if (indexStruct) {
|
||||
if (options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize SummaryIndex with both nodes and indexStruct",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize SummaryIndex without nodes or indexStruct",
|
||||
);
|
||||
}
|
||||
indexStruct = await SummaryIndex.buildIndexFromNodes(
|
||||
options.nodes,
|
||||
storageContext.docStore,
|
||||
);
|
||||
|
||||
await indexStore.addIndexStruct(indexStruct);
|
||||
}
|
||||
|
||||
return new SummaryIndex({
|
||||
storageContext,
|
||||
serviceContext,
|
||||
docStore,
|
||||
indexStore,
|
||||
indexStruct,
|
||||
});
|
||||
}
|
||||
|
||||
static async fromDocuments(
|
||||
documents: Document[],
|
||||
args: {
|
||||
storageContext?: StorageContext;
|
||||
serviceContext?: ServiceContext;
|
||||
} = {},
|
||||
): Promise<SummaryIndex> {
|
||||
let { storageContext, serviceContext } = args;
|
||||
storageContext = storageContext ?? (await storageContextFromDefaults({}));
|
||||
serviceContext = serviceContext ?? serviceContextFromDefaults({});
|
||||
const docStore = storageContext.docStore;
|
||||
|
||||
docStore.addDocuments(documents, true);
|
||||
for (const doc of documents) {
|
||||
docStore.setDocumentHash(doc.id_, doc.hash);
|
||||
}
|
||||
|
||||
const nodes = serviceContext.nodeParser.getNodesFromDocuments(documents);
|
||||
const index = await SummaryIndex.init({
|
||||
nodes,
|
||||
storageContext,
|
||||
serviceContext,
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
asRetriever(options?: { mode: SummaryRetrieverMode }): BaseRetriever {
|
||||
const { mode = SummaryRetrieverMode.DEFAULT } = options ?? {};
|
||||
|
||||
switch (mode) {
|
||||
case SummaryRetrieverMode.DEFAULT:
|
||||
return new SummaryIndexRetriever(this);
|
||||
case SummaryRetrieverMode.LLM:
|
||||
return new SummaryIndexLLMRetriever(this);
|
||||
default:
|
||||
throw new Error(`Unknown retriever mode: ${mode}`);
|
||||
}
|
||||
}
|
||||
|
||||
asQueryEngine(options?: {
|
||||
retriever?: BaseRetriever;
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: unknown;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine {
|
||||
let { retriever, responseSynthesizer } = options ?? {};
|
||||
|
||||
if (!retriever) {
|
||||
retriever = this.asRetriever();
|
||||
}
|
||||
|
||||
if (!responseSynthesizer) {
|
||||
let responseBuilder = new CompactAndRefine(this.serviceContext);
|
||||
responseSynthesizer = new ResponseSynthesizer({
|
||||
serviceContext: this.serviceContext,
|
||||
responseBuilder,
|
||||
});
|
||||
}
|
||||
|
||||
return new RetrieverQueryEngine(
|
||||
retriever,
|
||||
responseSynthesizer,
|
||||
options?.preFilters,
|
||||
options?.nodePostprocessors,
|
||||
);
|
||||
}
|
||||
|
||||
static async buildIndexFromNodes(
|
||||
nodes: BaseNode[],
|
||||
docStore: BaseDocumentStore,
|
||||
indexStruct?: IndexList,
|
||||
): Promise<IndexList> {
|
||||
indexStruct = indexStruct || new IndexList();
|
||||
|
||||
await docStore.addDocuments(nodes, true);
|
||||
for (const node of nodes) {
|
||||
indexStruct.addNode(node);
|
||||
}
|
||||
|
||||
return indexStruct;
|
||||
}
|
||||
|
||||
async insertNodes(nodes: BaseNode[]): Promise<void> {
|
||||
for (const node of nodes) {
|
||||
this.indexStruct.addNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteRefDoc(
|
||||
refDocId: string,
|
||||
deleteFromDocStore?: boolean,
|
||||
): Promise<void> {
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refDocId);
|
||||
|
||||
if (!refDocInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.deleteNodes(refDocInfo.nodeIds, false);
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
await this.docStore.deleteRefDoc(refDocId, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async deleteNodes(nodeIds: string[], deleteFromDocStore: boolean) {
|
||||
this.indexStruct.nodes = this.indexStruct.nodes.filter(
|
||||
(existingNodeId: string) => !nodeIds.includes(existingNodeId),
|
||||
);
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
for (const nodeId of nodeIds) {
|
||||
await this.docStore.deleteDocument(nodeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
await this.storageContext.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
|
||||
async getRefDocInfo(): Promise<Record<string, RefDocInfo>> {
|
||||
const nodeDocIds = this.indexStruct.nodes;
|
||||
const nodes = await this.docStore.getNodes(nodeDocIds);
|
||||
|
||||
const refDocInfoMap: Record<string, RefDocInfo> = {};
|
||||
|
||||
for (const node of nodes) {
|
||||
const refNode = node.sourceNode;
|
||||
if (_.isNil(refNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refNode.nodeId);
|
||||
|
||||
if (_.isNil(refDocInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
refDocInfoMap[refNode.nodeId] = refDocInfo;
|
||||
}
|
||||
|
||||
return refDocInfoMap;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy
|
||||
export type ListIndex = SummaryIndex;
|
||||
export type ListRetrieverMode = SummaryRetrieverMode;
|
||||
@@ -1,137 +0,0 @@
|
||||
import _ from "lodash";
|
||||
import { globalsHelper } from "../../GlobalsHelper";
|
||||
import { NodeWithScore } from "../../Node";
|
||||
import { ChoiceSelectPrompt, defaultChoiceSelectPrompt } from "../../Prompt";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import { ServiceContext } from "../../ServiceContext";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { SummaryIndex } from "./SummaryIndex";
|
||||
import {
|
||||
ChoiceSelectParserFunction,
|
||||
NodeFormatterFunction,
|
||||
defaultFormatNodeBatchFn,
|
||||
defaultParseChoiceSelectAnswerFn,
|
||||
} from "./utils";
|
||||
|
||||
/**
|
||||
* Simple retriever for SummaryIndex that returns all nodes
|
||||
*/
|
||||
export class SummaryIndexRetriever implements BaseRetriever {
|
||||
index: SummaryIndex;
|
||||
|
||||
constructor(index: SummaryIndex) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
async retrieve(query: string, parentEvent?: Event): Promise<NodeWithScore[]> {
|
||||
const nodeIds = this.index.indexStruct.nodes;
|
||||
const nodes = await this.index.docStore.getNodes(nodeIds);
|
||||
const result = nodes.map((node) => ({
|
||||
node: node,
|
||||
score: 1,
|
||||
}));
|
||||
|
||||
if (this.index.serviceContext.callbackManager.onRetrieve) {
|
||||
this.index.serviceContext.callbackManager.onRetrieve({
|
||||
query,
|
||||
nodes: result,
|
||||
event: globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
type: "retrieve",
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.index.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LLM retriever for SummaryIndex which lets you select the most relevant chunks.
|
||||
*/
|
||||
export class SummaryIndexLLMRetriever implements BaseRetriever {
|
||||
index: SummaryIndex;
|
||||
choiceSelectPrompt: ChoiceSelectPrompt;
|
||||
choiceBatchSize: number;
|
||||
formatNodeBatchFn: NodeFormatterFunction;
|
||||
parseChoiceSelectAnswerFn: ChoiceSelectParserFunction;
|
||||
serviceContext: ServiceContext;
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
constructor(
|
||||
index: SummaryIndex,
|
||||
choiceSelectPrompt?: ChoiceSelectPrompt,
|
||||
choiceBatchSize: number = 10,
|
||||
formatNodeBatchFn?: NodeFormatterFunction,
|
||||
parseChoiceSelectAnswerFn?: ChoiceSelectParserFunction,
|
||||
serviceContext?: ServiceContext,
|
||||
) {
|
||||
this.index = index;
|
||||
this.choiceSelectPrompt = choiceSelectPrompt || defaultChoiceSelectPrompt;
|
||||
this.choiceBatchSize = choiceBatchSize;
|
||||
this.formatNodeBatchFn = formatNodeBatchFn || defaultFormatNodeBatchFn;
|
||||
this.parseChoiceSelectAnswerFn =
|
||||
parseChoiceSelectAnswerFn || defaultParseChoiceSelectAnswerFn;
|
||||
this.serviceContext = serviceContext || index.serviceContext;
|
||||
}
|
||||
|
||||
async retrieve(query: string, parentEvent?: Event): Promise<NodeWithScore[]> {
|
||||
const nodeIds = this.index.indexStruct.nodes;
|
||||
const results: NodeWithScore[] = [];
|
||||
|
||||
for (let idx = 0; idx < nodeIds.length; idx += this.choiceBatchSize) {
|
||||
const nodeIdsBatch = nodeIds.slice(idx, idx + this.choiceBatchSize);
|
||||
const nodesBatch = await this.index.docStore.getNodes(nodeIdsBatch);
|
||||
|
||||
const fmtBatchStr = this.formatNodeBatchFn(nodesBatch);
|
||||
const input = { context: fmtBatchStr, query: query };
|
||||
const rawResponse = (
|
||||
await this.serviceContext.llm.complete({
|
||||
prompt: this.choiceSelectPrompt(input),
|
||||
})
|
||||
).text;
|
||||
|
||||
// parseResult is a map from doc number to relevance score
|
||||
const parseResult = this.parseChoiceSelectAnswerFn(
|
||||
rawResponse,
|
||||
nodesBatch.length,
|
||||
);
|
||||
const choiceNodeIds = nodeIdsBatch.filter((nodeId, idx) => {
|
||||
return `${idx}` in parseResult;
|
||||
});
|
||||
|
||||
const choiceNodes = await this.index.docStore.getNodes(choiceNodeIds);
|
||||
const nodeWithScores = choiceNodes.map((node, i) => ({
|
||||
node: node,
|
||||
score: _.get(parseResult, `${i + 1}`, 1),
|
||||
}));
|
||||
|
||||
results.push(...nodeWithScores);
|
||||
}
|
||||
|
||||
if (this.serviceContext.callbackManager.onRetrieve) {
|
||||
this.serviceContext.callbackManager.onRetrieve({
|
||||
query,
|
||||
nodes: results,
|
||||
event: globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
type: "retrieve",
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy
|
||||
export type ListIndexRetriever = SummaryIndexRetriever;
|
||||
export type ListIndexLLMRetriever = SummaryIndexLLMRetriever;
|
||||
@@ -1,10 +1,392 @@
|
||||
export { SummaryIndex, SummaryRetrieverMode } from "./SummaryIndex";
|
||||
export type { ListIndex, ListRetrieverMode } from "./SummaryIndex";
|
||||
export {
|
||||
SummaryIndexLLMRetriever,
|
||||
SummaryIndexRetriever,
|
||||
} from "./SummaryIndexRetriever";
|
||||
export type {
|
||||
ListIndexLLMRetriever,
|
||||
ListIndexRetriever,
|
||||
} from "./SummaryIndexRetriever";
|
||||
import _ from "lodash";
|
||||
import { globalsHelper } from "../../GlobalsHelper.js";
|
||||
import { BaseNode, Document, NodeWithScore } from "../../Node.js";
|
||||
import { ChoiceSelectPrompt, defaultChoiceSelectPrompt } from "../../Prompt.js";
|
||||
import { BaseRetriever } from "../../Retriever.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import { RetrieverQueryEngine } from "../../engines/query/index.js";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import {
|
||||
BaseDocumentStore,
|
||||
RefDocInfo,
|
||||
StorageContext,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage/index.js";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
CompactAndRefine,
|
||||
ResponseSynthesizer,
|
||||
} from "../../synthesizers/index.js";
|
||||
import { BaseQueryEngine } from "../../types.js";
|
||||
import { BaseIndex, BaseIndexInit } from "../BaseIndex.js";
|
||||
import { IndexList, IndexStructType } from "../json-to-index-struct.js";
|
||||
import {
|
||||
ChoiceSelectParserFunction,
|
||||
NodeFormatterFunction,
|
||||
defaultFormatNodeBatchFn,
|
||||
defaultParseChoiceSelectAnswerFn,
|
||||
} from "./utils.js";
|
||||
|
||||
export enum SummaryRetrieverMode {
|
||||
DEFAULT = "default",
|
||||
// EMBEDDING = "embedding",
|
||||
LLM = "llm",
|
||||
}
|
||||
|
||||
export interface SummaryIndexOptions {
|
||||
nodes?: BaseNode[];
|
||||
indexStruct?: IndexList;
|
||||
indexId?: string;
|
||||
serviceContext?: ServiceContext;
|
||||
storageContext?: StorageContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* A SummaryIndex keeps nodes in a sequential order for use with summarization.
|
||||
*/
|
||||
export class SummaryIndex extends BaseIndex<IndexList> {
|
||||
constructor(init: BaseIndexInit<IndexList>) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
static async init(options: SummaryIndexOptions): Promise<SummaryIndex> {
|
||||
const storageContext =
|
||||
options.storageContext ?? (await storageContextFromDefaults({}));
|
||||
const serviceContext =
|
||||
options.serviceContext ?? serviceContextFromDefaults({});
|
||||
const { docStore, indexStore } = storageContext;
|
||||
|
||||
// Setup IndexStruct from storage
|
||||
const indexStructs = (await indexStore.getIndexStructs()) as IndexList[];
|
||||
let indexStruct: IndexList | null;
|
||||
|
||||
if (options.indexStruct && indexStructs.length > 0) {
|
||||
throw new Error(
|
||||
"Cannot initialize index with both indexStruct and indexStore",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
)) as IndexList;
|
||||
} else {
|
||||
indexStruct = null;
|
||||
}
|
||||
|
||||
// check indexStruct type
|
||||
if (indexStruct && indexStruct.type !== IndexStructType.LIST) {
|
||||
throw new Error(
|
||||
"Attempting to initialize SummaryIndex with non-list indexStruct",
|
||||
);
|
||||
}
|
||||
|
||||
if (indexStruct) {
|
||||
if (options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize SummaryIndex with both nodes and indexStruct",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (!options.nodes) {
|
||||
throw new Error(
|
||||
"Cannot initialize SummaryIndex without nodes or indexStruct",
|
||||
);
|
||||
}
|
||||
indexStruct = await SummaryIndex.buildIndexFromNodes(
|
||||
options.nodes,
|
||||
storageContext.docStore,
|
||||
);
|
||||
|
||||
await indexStore.addIndexStruct(indexStruct);
|
||||
}
|
||||
|
||||
return new SummaryIndex({
|
||||
storageContext,
|
||||
serviceContext,
|
||||
docStore,
|
||||
indexStore,
|
||||
indexStruct,
|
||||
});
|
||||
}
|
||||
|
||||
static async fromDocuments(
|
||||
documents: Document[],
|
||||
args: {
|
||||
storageContext?: StorageContext;
|
||||
serviceContext?: ServiceContext;
|
||||
} = {},
|
||||
): Promise<SummaryIndex> {
|
||||
let { storageContext, serviceContext } = args;
|
||||
storageContext = storageContext ?? (await storageContextFromDefaults({}));
|
||||
serviceContext = serviceContext ?? serviceContextFromDefaults({});
|
||||
const docStore = storageContext.docStore;
|
||||
|
||||
docStore.addDocuments(documents, true);
|
||||
for (const doc of documents) {
|
||||
docStore.setDocumentHash(doc.id_, doc.hash);
|
||||
}
|
||||
|
||||
const nodes = serviceContext.nodeParser.getNodesFromDocuments(documents);
|
||||
const index = await SummaryIndex.init({
|
||||
nodes,
|
||||
storageContext,
|
||||
serviceContext,
|
||||
});
|
||||
return index;
|
||||
}
|
||||
|
||||
asRetriever(options?: { mode: SummaryRetrieverMode }): BaseRetriever {
|
||||
const { mode = SummaryRetrieverMode.DEFAULT } = options ?? {};
|
||||
|
||||
switch (mode) {
|
||||
case SummaryRetrieverMode.DEFAULT:
|
||||
return new SummaryIndexRetriever(this);
|
||||
case SummaryRetrieverMode.LLM:
|
||||
return new SummaryIndexLLMRetriever(this);
|
||||
default:
|
||||
throw new Error(`Unknown retriever mode: ${mode}`);
|
||||
}
|
||||
}
|
||||
|
||||
asQueryEngine(options?: {
|
||||
retriever?: BaseRetriever;
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: unknown;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine {
|
||||
let { retriever, responseSynthesizer } = options ?? {};
|
||||
|
||||
if (!retriever) {
|
||||
retriever = this.asRetriever();
|
||||
}
|
||||
|
||||
if (!responseSynthesizer) {
|
||||
const responseBuilder = new CompactAndRefine(this.serviceContext);
|
||||
responseSynthesizer = new ResponseSynthesizer({
|
||||
serviceContext: this.serviceContext,
|
||||
responseBuilder,
|
||||
});
|
||||
}
|
||||
|
||||
return new RetrieverQueryEngine(
|
||||
retriever,
|
||||
responseSynthesizer,
|
||||
options?.preFilters,
|
||||
options?.nodePostprocessors,
|
||||
);
|
||||
}
|
||||
|
||||
static async buildIndexFromNodes(
|
||||
nodes: BaseNode[],
|
||||
docStore: BaseDocumentStore,
|
||||
indexStruct?: IndexList,
|
||||
): Promise<IndexList> {
|
||||
indexStruct = indexStruct || new IndexList();
|
||||
|
||||
await docStore.addDocuments(nodes, true);
|
||||
for (const node of nodes) {
|
||||
indexStruct.addNode(node);
|
||||
}
|
||||
|
||||
return indexStruct;
|
||||
}
|
||||
|
||||
async insertNodes(nodes: BaseNode[]): Promise<void> {
|
||||
for (const node of nodes) {
|
||||
this.indexStruct.addNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteRefDoc(
|
||||
refDocId: string,
|
||||
deleteFromDocStore?: boolean,
|
||||
): Promise<void> {
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refDocId);
|
||||
|
||||
if (!refDocInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.deleteNodes(refDocInfo.nodeIds, false);
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
await this.docStore.deleteRefDoc(refDocId, false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async deleteNodes(nodeIds: string[], deleteFromDocStore: boolean) {
|
||||
this.indexStruct.nodes = this.indexStruct.nodes.filter(
|
||||
(existingNodeId: string) => !nodeIds.includes(existingNodeId),
|
||||
);
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
for (const nodeId of nodeIds) {
|
||||
await this.docStore.deleteDocument(nodeId, false);
|
||||
}
|
||||
}
|
||||
|
||||
await this.storageContext.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
|
||||
async getRefDocInfo(): Promise<Record<string, RefDocInfo>> {
|
||||
const nodeDocIds = this.indexStruct.nodes;
|
||||
const nodes = await this.docStore.getNodes(nodeDocIds);
|
||||
|
||||
const refDocInfoMap: Record<string, RefDocInfo> = {};
|
||||
|
||||
for (const node of nodes) {
|
||||
const refNode = node.sourceNode;
|
||||
if (_.isNil(refNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refNode.nodeId);
|
||||
|
||||
if (_.isNil(refDocInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
refDocInfoMap[refNode.nodeId] = refDocInfo;
|
||||
}
|
||||
|
||||
return refDocInfoMap;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy
|
||||
export type ListIndex = SummaryIndex;
|
||||
export type ListRetrieverMode = SummaryRetrieverMode;
|
||||
|
||||
/**
|
||||
* Simple retriever for SummaryIndex that returns all nodes
|
||||
*/
|
||||
export class SummaryIndexRetriever implements BaseRetriever {
|
||||
index: SummaryIndex;
|
||||
|
||||
constructor(index: SummaryIndex) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
async retrieve(query: string, parentEvent?: Event): Promise<NodeWithScore[]> {
|
||||
const nodeIds = this.index.indexStruct.nodes;
|
||||
const nodes = await this.index.docStore.getNodes(nodeIds);
|
||||
const result = nodes.map((node) => ({
|
||||
node: node,
|
||||
score: 1,
|
||||
}));
|
||||
|
||||
if (this.index.serviceContext.callbackManager.onRetrieve) {
|
||||
this.index.serviceContext.callbackManager.onRetrieve({
|
||||
query,
|
||||
nodes: result,
|
||||
event: globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
type: "retrieve",
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.index.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LLM retriever for SummaryIndex which lets you select the most relevant chunks.
|
||||
*/
|
||||
export class SummaryIndexLLMRetriever implements BaseRetriever {
|
||||
index: SummaryIndex;
|
||||
choiceSelectPrompt: ChoiceSelectPrompt;
|
||||
choiceBatchSize: number;
|
||||
formatNodeBatchFn: NodeFormatterFunction;
|
||||
parseChoiceSelectAnswerFn: ChoiceSelectParserFunction;
|
||||
serviceContext: ServiceContext;
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
constructor(
|
||||
index: SummaryIndex,
|
||||
choiceSelectPrompt?: ChoiceSelectPrompt,
|
||||
choiceBatchSize: number = 10,
|
||||
formatNodeBatchFn?: NodeFormatterFunction,
|
||||
parseChoiceSelectAnswerFn?: ChoiceSelectParserFunction,
|
||||
serviceContext?: ServiceContext,
|
||||
) {
|
||||
this.index = index;
|
||||
this.choiceSelectPrompt = choiceSelectPrompt || defaultChoiceSelectPrompt;
|
||||
this.choiceBatchSize = choiceBatchSize;
|
||||
this.formatNodeBatchFn = formatNodeBatchFn || defaultFormatNodeBatchFn;
|
||||
this.parseChoiceSelectAnswerFn =
|
||||
parseChoiceSelectAnswerFn || defaultParseChoiceSelectAnswerFn;
|
||||
this.serviceContext = serviceContext || index.serviceContext;
|
||||
}
|
||||
|
||||
async retrieve(query: string, parentEvent?: Event): Promise<NodeWithScore[]> {
|
||||
const nodeIds = this.index.indexStruct.nodes;
|
||||
const results: NodeWithScore[] = [];
|
||||
|
||||
for (let idx = 0; idx < nodeIds.length; idx += this.choiceBatchSize) {
|
||||
const nodeIdsBatch = nodeIds.slice(idx, idx + this.choiceBatchSize);
|
||||
const nodesBatch = await this.index.docStore.getNodes(nodeIdsBatch);
|
||||
|
||||
const fmtBatchStr = this.formatNodeBatchFn(nodesBatch);
|
||||
const input = { context: fmtBatchStr, query: query };
|
||||
const rawResponse = (
|
||||
await this.serviceContext.llm.complete({
|
||||
prompt: this.choiceSelectPrompt(input),
|
||||
})
|
||||
).text;
|
||||
|
||||
// parseResult is a map from doc number to relevance score
|
||||
const parseResult = this.parseChoiceSelectAnswerFn(
|
||||
rawResponse,
|
||||
nodesBatch.length,
|
||||
);
|
||||
const choiceNodeIds = nodeIdsBatch.filter((nodeId, idx) => {
|
||||
return `${idx}` in parseResult;
|
||||
});
|
||||
|
||||
const choiceNodes = await this.index.docStore.getNodes(choiceNodeIds);
|
||||
const nodeWithScores = choiceNodes.map((node, i) => ({
|
||||
node: node,
|
||||
score: _.get(parseResult, `${i + 1}`, 1),
|
||||
}));
|
||||
|
||||
results.push(...nodeWithScores);
|
||||
}
|
||||
|
||||
if (this.serviceContext.callbackManager.onRetrieve) {
|
||||
this.serviceContext.callbackManager.onRetrieve({
|
||||
query,
|
||||
nodes: results,
|
||||
event: globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
type: "retrieve",
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy
|
||||
export type ListIndexRetriever = SummaryIndexRetriever;
|
||||
export type ListIndexLLMRetriever = SummaryIndexLLMRetriever;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import _ from "lodash";
|
||||
import { BaseNode, MetadataMode } from "../../Node";
|
||||
import { BaseNode, MetadataMode } from "../../Node.js";
|
||||
|
||||
export type NodeFormatterFunction = (summaryNodes: BaseNode[]) => string;
|
||||
export const defaultFormatNodeBatchFn: NodeFormatterFunction = (
|
||||
@@ -32,7 +32,7 @@ export const defaultParseChoiceSelectAnswerFn: ChoiceSelectParserFunction = (
|
||||
const lineTokens: string[][] = answer
|
||||
.split("\n")
|
||||
.map((line: string) => {
|
||||
let lineTokens = line.split(",");
|
||||
const lineTokens = line.split(",");
|
||||
if (lineTokens.length !== 2) {
|
||||
if (raiseErr) {
|
||||
throw new Error(
|
||||
@@ -50,8 +50,8 @@ export const defaultParseChoiceSelectAnswerFn: ChoiceSelectParserFunction = (
|
||||
return lineTokens.reduce(
|
||||
(parseResult: ChoiceSelectParseResult, lineToken: string[]) => {
|
||||
try {
|
||||
let docNum = parseInt(lineToken[0].split(":")[1].trim());
|
||||
let answerRelevance = parseFloat(lineToken[1].split(":")[1].trim());
|
||||
const docNum = parseInt(lineToken[0].split(":")[1].trim());
|
||||
const answerRelevance = parseFloat(lineToken[1].split(":")[1].trim());
|
||||
if (docNum < 1 || docNum > numChoices) {
|
||||
if (raiseErr) {
|
||||
throw new Error(
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import { globalsHelper } from "../../GlobalsHelper";
|
||||
import { ImageNode, Metadata, NodeWithScore } from "../../Node";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import { ServiceContext } from "../../ServiceContext";
|
||||
import { Event } from "../../callbacks/CallbackManager";
|
||||
import { DEFAULT_SIMILARITY_TOP_K } from "../../constants";
|
||||
import { BaseEmbedding } from "../../embeddings";
|
||||
import {
|
||||
MetadataFilters,
|
||||
VectorStoreQuery,
|
||||
VectorStoreQueryMode,
|
||||
VectorStoreQueryResult,
|
||||
} from "../../storage/vectorStore/types";
|
||||
import { VectorStoreIndex } from "./VectorStoreIndex";
|
||||
|
||||
/**
|
||||
* VectorIndexRetriever retrieves nodes from a VectorIndex.
|
||||
*/
|
||||
|
||||
export type VectorIndexRetrieverOptions = {
|
||||
index: VectorStoreIndex;
|
||||
similarityTopK?: number;
|
||||
imageSimilarityTopK?: number;
|
||||
};
|
||||
|
||||
export class VectorIndexRetriever implements BaseRetriever {
|
||||
index: VectorStoreIndex;
|
||||
similarityTopK: number;
|
||||
imageSimilarityTopK: number;
|
||||
private serviceContext: ServiceContext;
|
||||
|
||||
constructor({
|
||||
index,
|
||||
similarityTopK,
|
||||
imageSimilarityTopK,
|
||||
}: VectorIndexRetrieverOptions) {
|
||||
this.index = index;
|
||||
this.serviceContext = this.index.serviceContext;
|
||||
this.similarityTopK = similarityTopK ?? DEFAULT_SIMILARITY_TOP_K;
|
||||
this.imageSimilarityTopK = imageSimilarityTopK ?? DEFAULT_SIMILARITY_TOP_K;
|
||||
}
|
||||
|
||||
async retrieve(
|
||||
query: string,
|
||||
parentEvent?: Event,
|
||||
preFilters?: MetadataFilters,
|
||||
): Promise<NodeWithScore[]> {
|
||||
let nodesWithScores = await this.textRetrieve(query, preFilters);
|
||||
nodesWithScores = nodesWithScores.concat(
|
||||
await this.textToImageRetrieve(query, preFilters),
|
||||
);
|
||||
this.sendEvent(query, nodesWithScores, parentEvent);
|
||||
return nodesWithScores;
|
||||
}
|
||||
|
||||
protected async textRetrieve(
|
||||
query: string,
|
||||
preFilters?: MetadataFilters,
|
||||
): Promise<NodeWithScore[]> {
|
||||
const options = {};
|
||||
const q = await this.buildVectorStoreQuery(
|
||||
this.index.embedModel,
|
||||
query,
|
||||
this.similarityTopK,
|
||||
preFilters,
|
||||
);
|
||||
const result = await this.index.vectorStore.query(q, options);
|
||||
return this.buildNodeListFromQueryResult(result);
|
||||
}
|
||||
|
||||
private async textToImageRetrieve(
|
||||
query: string,
|
||||
preFilters?: MetadataFilters,
|
||||
) {
|
||||
if (!this.index.imageEmbedModel || !this.index.imageVectorStore) {
|
||||
// no-op if image embedding and vector store are not set
|
||||
return [];
|
||||
}
|
||||
const q = await this.buildVectorStoreQuery(
|
||||
this.index.imageEmbedModel,
|
||||
query,
|
||||
this.imageSimilarityTopK,
|
||||
preFilters,
|
||||
);
|
||||
const result = await this.index.imageVectorStore.query(q, preFilters);
|
||||
return this.buildNodeListFromQueryResult(result);
|
||||
}
|
||||
|
||||
protected sendEvent(
|
||||
query: string,
|
||||
nodesWithScores: NodeWithScore<Metadata>[],
|
||||
parentEvent: Event | undefined,
|
||||
) {
|
||||
if (this.serviceContext.callbackManager.onRetrieve) {
|
||||
this.serviceContext.callbackManager.onRetrieve({
|
||||
query,
|
||||
nodes: nodesWithScores,
|
||||
event: globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
type: "retrieve",
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async buildVectorStoreQuery(
|
||||
embedModel: BaseEmbedding,
|
||||
query: string,
|
||||
similarityTopK: number,
|
||||
preFilters?: MetadataFilters,
|
||||
): Promise<VectorStoreQuery> {
|
||||
const queryEmbedding = await embedModel.getQueryEmbedding(query);
|
||||
|
||||
return {
|
||||
queryEmbedding: queryEmbedding,
|
||||
mode: VectorStoreQueryMode.DEFAULT,
|
||||
similarityTopK: similarityTopK,
|
||||
filters: preFilters ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
protected buildNodeListFromQueryResult(result: VectorStoreQueryResult) {
|
||||
let nodesWithScores: NodeWithScore[] = [];
|
||||
for (let i = 0; i < result.ids.length; i++) {
|
||||
const nodeFromResult = result.nodes?.[i];
|
||||
if (!this.index.indexStruct.nodesDict[result.ids[i]] && nodeFromResult) {
|
||||
this.index.indexStruct.nodesDict[result.ids[i]] = nodeFromResult;
|
||||
}
|
||||
|
||||
const node = this.index.indexStruct.nodesDict[result.ids[i]];
|
||||
// XXX: Hack, if it's an image node, we reconstruct the image from the URL
|
||||
// Alternative: Store image in doc store and retrieve it here
|
||||
if (node instanceof ImageNode) {
|
||||
node.image = node.getUrl();
|
||||
}
|
||||
|
||||
nodesWithScores.push({
|
||||
node: node,
|
||||
score: result.similarities[i],
|
||||
});
|
||||
}
|
||||
|
||||
return nodesWithScores;
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.serviceContext;
|
||||
}
|
||||
}
|
||||
@@ -1,398 +0,0 @@
|
||||
import {
|
||||
BaseNode,
|
||||
Document,
|
||||
ImageNode,
|
||||
MetadataMode,
|
||||
ObjectType,
|
||||
splitNodesByType,
|
||||
} from "../../Node";
|
||||
import { BaseRetriever } from "../../Retriever";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext";
|
||||
import {
|
||||
BaseEmbedding,
|
||||
ClipEmbedding,
|
||||
MultiModalEmbedding,
|
||||
} from "../../embeddings";
|
||||
import { RetrieverQueryEngine } from "../../engines/query";
|
||||
import { runTransformations } from "../../ingestion";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors";
|
||||
import {
|
||||
BaseIndexStore,
|
||||
MetadataFilters,
|
||||
StorageContext,
|
||||
VectorStore,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage";
|
||||
import { BaseSynthesizer } from "../../synthesizers";
|
||||
import { BaseQueryEngine } from "../../types";
|
||||
import {
|
||||
BaseIndex,
|
||||
BaseIndexInit,
|
||||
IndexDict,
|
||||
IndexStructType,
|
||||
} from "../BaseIndex";
|
||||
import {
|
||||
VectorIndexRetriever,
|
||||
VectorIndexRetrieverOptions,
|
||||
} from "./VectorIndexRetriever";
|
||||
|
||||
interface IndexStructOptions {
|
||||
indexStruct?: IndexDict;
|
||||
indexId?: string;
|
||||
}
|
||||
export interface VectorIndexOptions extends IndexStructOptions {
|
||||
nodes?: BaseNode[];
|
||||
serviceContext?: ServiceContext;
|
||||
storageContext?: StorageContext;
|
||||
imageVectorStore?: VectorStore;
|
||||
vectorStore?: VectorStore;
|
||||
logProgress?: boolean;
|
||||
}
|
||||
|
||||
export interface VectorIndexConstructorProps extends BaseIndexInit<IndexDict> {
|
||||
indexStore: BaseIndexStore;
|
||||
imageVectorStore?: VectorStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* The VectorStoreIndex, an index that stores the nodes only according to their vector embedings.
|
||||
*/
|
||||
export class VectorStoreIndex extends BaseIndex<IndexDict> {
|
||||
vectorStore: VectorStore;
|
||||
indexStore: BaseIndexStore;
|
||||
embedModel: BaseEmbedding;
|
||||
imageVectorStore?: VectorStore;
|
||||
imageEmbedModel?: MultiModalEmbedding;
|
||||
|
||||
private constructor(init: VectorIndexConstructorProps) {
|
||||
super(init);
|
||||
this.indexStore = init.indexStore;
|
||||
this.vectorStore = init.vectorStore ?? init.storageContext.vectorStore;
|
||||
this.embedModel = init.serviceContext.embedModel;
|
||||
this.imageVectorStore =
|
||||
init.imageVectorStore ?? init.storageContext.imageVectorStore;
|
||||
if (this.imageVectorStore) {
|
||||
this.imageEmbedModel = new ClipEmbedding();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The async init function creates a new VectorStoreIndex.
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
public static async init(
|
||||
options: VectorIndexOptions,
|
||||
): Promise<VectorStoreIndex> {
|
||||
const storageContext =
|
||||
options.storageContext ?? (await storageContextFromDefaults({}));
|
||||
const serviceContext =
|
||||
options.serviceContext ?? serviceContextFromDefaults({});
|
||||
const indexStore = storageContext.indexStore;
|
||||
const docStore = storageContext.docStore;
|
||||
|
||||
let indexStruct = await VectorStoreIndex.setupIndexStructFromStorage(
|
||||
indexStore,
|
||||
options,
|
||||
);
|
||||
|
||||
if (!options.nodes && !indexStruct) {
|
||||
throw new Error(
|
||||
"Cannot initialize VectorStoreIndex without nodes or indexStruct",
|
||||
);
|
||||
}
|
||||
|
||||
indexStruct = indexStruct ?? new IndexDict();
|
||||
|
||||
const index = new this({
|
||||
storageContext,
|
||||
serviceContext,
|
||||
docStore,
|
||||
indexStruct,
|
||||
indexStore,
|
||||
vectorStore: options.vectorStore,
|
||||
imageVectorStore: options.imageVectorStore,
|
||||
});
|
||||
|
||||
if (options.nodes) {
|
||||
// If nodes are passed in, then we need to update the index
|
||||
await index.buildIndexFromNodes(options.nodes, {
|
||||
logProgress: options.logProgress,
|
||||
});
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private static async setupIndexStructFromStorage(
|
||||
indexStore: BaseIndexStore,
|
||||
options: IndexStructOptions,
|
||||
) {
|
||||
let indexStructs = (await indexStore.getIndexStructs()) as IndexDict[];
|
||||
let indexStruct: IndexDict | undefined;
|
||||
|
||||
if (options.indexStruct && indexStructs.length > 0) {
|
||||
throw new Error(
|
||||
"Cannot initialize index with both indexStruct and indexStore",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
)) as IndexDict;
|
||||
}
|
||||
// Check indexStruct type
|
||||
if (indexStruct && indexStruct.type !== IndexStructType.SIMPLE_DICT) {
|
||||
throw new Error(
|
||||
"Attempting to initialize VectorStoreIndex with non-vector indexStruct",
|
||||
);
|
||||
}
|
||||
return indexStruct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the embeddings for the given nodes.
|
||||
*
|
||||
* @param nodes - An array of BaseNode objects representing the nodes for which embeddings are to be calculated.
|
||||
* @param {Object} [options] - An optional object containing additional parameters.
|
||||
* @param {boolean} [options.logProgress] - A boolean indicating whether to log progress to the console (useful for debugging).
|
||||
*/
|
||||
async getNodeEmbeddingResults(
|
||||
nodes: BaseNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
): Promise<BaseNode[]> {
|
||||
const texts = nodes.map((node) => node.getContent(MetadataMode.EMBED));
|
||||
const embeddings = await this.embedModel.getTextEmbeddingsBatch(texts, {
|
||||
logProgress: options?.logProgress,
|
||||
});
|
||||
return nodes.map((node, i) => {
|
||||
node.embedding = embeddings[i];
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get embeddings for nodes and place them into the index.
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
async buildIndexFromNodes(
|
||||
nodes: BaseNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
) {
|
||||
// Check if the index already has nodes with the same hash
|
||||
const newNodes = nodes.filter((node) =>
|
||||
Object.entries(this.indexStruct!.nodesDict).reduce(
|
||||
(acc, [key, value]) => {
|
||||
if (value.hash === node.hash) {
|
||||
acc = false;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
true,
|
||||
),
|
||||
);
|
||||
|
||||
await this.insertNodes(newNodes, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* High level API: split documents, get embeddings, and build index.
|
||||
* @param documents
|
||||
* @param args
|
||||
* @returns
|
||||
*/
|
||||
static async fromDocuments(
|
||||
documents: Document[],
|
||||
args: VectorIndexOptions = {},
|
||||
): Promise<VectorStoreIndex> {
|
||||
args.storageContext =
|
||||
args.storageContext ?? (await storageContextFromDefaults({}));
|
||||
args.serviceContext = args.serviceContext ?? serviceContextFromDefaults({});
|
||||
const docStore = args.storageContext.docStore;
|
||||
|
||||
for (const doc of documents) {
|
||||
docStore.setDocumentHash(doc.id_, doc.hash);
|
||||
}
|
||||
|
||||
if (args.logProgress) {
|
||||
console.log("Using node parser on documents...");
|
||||
}
|
||||
args.nodes = await runTransformations(documents, [
|
||||
args.serviceContext.nodeParser,
|
||||
]);
|
||||
if (args.logProgress) {
|
||||
console.log("Finished parsing documents.");
|
||||
}
|
||||
return await this.init(args);
|
||||
}
|
||||
|
||||
static async fromVectorStore(
|
||||
vectorStore: VectorStore,
|
||||
serviceContext: ServiceContext,
|
||||
imageVectorStore?: VectorStore,
|
||||
) {
|
||||
if (!vectorStore.storesText) {
|
||||
throw new Error(
|
||||
"Cannot initialize from a vector store that does not store text",
|
||||
);
|
||||
}
|
||||
|
||||
const storageContext = await storageContextFromDefaults({
|
||||
vectorStore,
|
||||
imageVectorStore,
|
||||
});
|
||||
|
||||
const index = await this.init({
|
||||
nodes: [],
|
||||
storageContext,
|
||||
serviceContext,
|
||||
});
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
asRetriever(
|
||||
options?: Omit<VectorIndexRetrieverOptions, "index">,
|
||||
): VectorIndexRetriever {
|
||||
return new VectorIndexRetriever({ index: this, ...options });
|
||||
}
|
||||
|
||||
asQueryEngine(options?: {
|
||||
retriever?: BaseRetriever;
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: MetadataFilters;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine & RetrieverQueryEngine {
|
||||
const { retriever, responseSynthesizer } = options ?? {};
|
||||
return new RetrieverQueryEngine(
|
||||
retriever ?? this.asRetriever(),
|
||||
responseSynthesizer,
|
||||
options?.preFilters,
|
||||
options?.nodePostprocessors,
|
||||
);
|
||||
}
|
||||
|
||||
protected async insertNodesToStore(
|
||||
vectorStore: VectorStore,
|
||||
nodes: BaseNode[],
|
||||
): Promise<void> {
|
||||
const newIds = await vectorStore.add(nodes);
|
||||
|
||||
// NOTE: if the vector store doesn't store text,
|
||||
// we need to add the nodes to the index struct and document store
|
||||
// NOTE: if the vector store keeps text,
|
||||
// we only need to add image and index nodes
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
const type = nodes[i].getType();
|
||||
if (
|
||||
!vectorStore.storesText ||
|
||||
type === ObjectType.INDEX ||
|
||||
type === ObjectType.IMAGE
|
||||
) {
|
||||
const nodeWithoutEmbedding = nodes[i].clone();
|
||||
nodeWithoutEmbedding.embedding = undefined;
|
||||
this.indexStruct.addNode(nodeWithoutEmbedding, newIds[i]);
|
||||
await this.docStore.addDocuments([nodeWithoutEmbedding], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async insertNodes(
|
||||
nodes: BaseNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
): Promise<void> {
|
||||
if (!nodes || nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const { imageNodes, textNodes } = splitNodesByType(nodes);
|
||||
if (imageNodes.length > 0) {
|
||||
if (!this.imageVectorStore) {
|
||||
throw new Error("Cannot insert image nodes without image vector store");
|
||||
}
|
||||
const imageNodesWithEmbedding = await this.getImageNodeEmbeddingResults(
|
||||
imageNodes,
|
||||
options,
|
||||
);
|
||||
await this.insertNodesToStore(
|
||||
this.imageVectorStore,
|
||||
imageNodesWithEmbedding,
|
||||
);
|
||||
}
|
||||
const embeddingResults = await this.getNodeEmbeddingResults(
|
||||
textNodes,
|
||||
options,
|
||||
);
|
||||
await this.insertNodesToStore(this.vectorStore, embeddingResults);
|
||||
await this.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
|
||||
async deleteRefDoc(
|
||||
refDocId: string,
|
||||
deleteFromDocStore: boolean = true,
|
||||
): Promise<void> {
|
||||
await this.deleteRefDocFromStore(this.vectorStore, refDocId);
|
||||
if (this.imageVectorStore) {
|
||||
await this.deleteRefDocFromStore(this.imageVectorStore, refDocId);
|
||||
}
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
await this.docStore.deleteDocument(refDocId, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected async deleteRefDocFromStore(
|
||||
vectorStore: VectorStore,
|
||||
refDocId: string,
|
||||
): Promise<void> {
|
||||
vectorStore.delete(refDocId);
|
||||
|
||||
if (!vectorStore.storesText) {
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refDocId);
|
||||
|
||||
if (refDocInfo) {
|
||||
for (const nodeId of refDocInfo.nodeIds) {
|
||||
this.indexStruct.delete(nodeId);
|
||||
vectorStore.delete(nodeId);
|
||||
}
|
||||
}
|
||||
await this.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the embeddings for the given image nodes.
|
||||
*
|
||||
* @param nodes - An array of ImageNode objects representing the nodes for which embeddings are to be calculated.
|
||||
* @param {Object} [options] - An optional object containing additional parameters.
|
||||
* @param {boolean} [options.logProgress] - A boolean indicating whether to log progress to the console (useful for debugging).
|
||||
*/
|
||||
async getImageNodeEmbeddingResults(
|
||||
nodes: ImageNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
): Promise<ImageNode[]> {
|
||||
if (!this.imageEmbedModel) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const nodesWithEmbeddings: ImageNode[] = [];
|
||||
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
const node = nodes[i];
|
||||
if (options?.logProgress) {
|
||||
console.log(`Getting embedding for node ${i + 1}/${nodes.length}`);
|
||||
}
|
||||
node.embedding = await this.imageEmbedModel.getImageEmbedding(node.image);
|
||||
nodesWithEmbeddings.push(node);
|
||||
}
|
||||
|
||||
return nodesWithEmbeddings;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,533 @@
|
||||
export { VectorIndexRetriever } from "./VectorIndexRetriever";
|
||||
export { VectorStoreIndex } from "./VectorStoreIndex";
|
||||
import { globalsHelper } from "../../GlobalsHelper.js";
|
||||
import {
|
||||
BaseNode,
|
||||
Document,
|
||||
ImageNode,
|
||||
Metadata,
|
||||
MetadataMode,
|
||||
NodeWithScore,
|
||||
ObjectType,
|
||||
splitNodesByType,
|
||||
} from "../../Node.js";
|
||||
import { BaseRetriever } from "../../Retriever.js";
|
||||
import {
|
||||
ServiceContext,
|
||||
serviceContextFromDefaults,
|
||||
} from "../../ServiceContext.js";
|
||||
import { Event } from "../../callbacks/CallbackManager.js";
|
||||
import { DEFAULT_SIMILARITY_TOP_K } from "../../constants.js";
|
||||
import {
|
||||
BaseEmbedding,
|
||||
ClipEmbedding,
|
||||
MultiModalEmbedding,
|
||||
} from "../../embeddings/index.js";
|
||||
import { RetrieverQueryEngine } from "../../engines/query/RetrieverQueryEngine.js";
|
||||
import { runTransformations } from "../../ingestion/index.js";
|
||||
import { BaseNodePostprocessor } from "../../postprocessors/types.js";
|
||||
import {
|
||||
BaseIndexStore,
|
||||
MetadataFilters,
|
||||
StorageContext,
|
||||
VectorStore,
|
||||
VectorStoreQuery,
|
||||
VectorStoreQueryMode,
|
||||
VectorStoreQueryResult,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage/index.js";
|
||||
import { BaseSynthesizer } from "../../synthesizers/types.js";
|
||||
import { BaseQueryEngine } from "../../types.js";
|
||||
import { BaseIndex, BaseIndexInit } from "../BaseIndex.js";
|
||||
import { IndexDict, IndexStructType } from "../json-to-index-struct.js";
|
||||
|
||||
interface IndexStructOptions {
|
||||
indexStruct?: IndexDict;
|
||||
indexId?: string;
|
||||
}
|
||||
export interface VectorIndexOptions extends IndexStructOptions {
|
||||
nodes?: BaseNode[];
|
||||
serviceContext?: ServiceContext;
|
||||
storageContext?: StorageContext;
|
||||
imageVectorStore?: VectorStore;
|
||||
vectorStore?: VectorStore;
|
||||
logProgress?: boolean;
|
||||
}
|
||||
|
||||
export interface VectorIndexConstructorProps extends BaseIndexInit<IndexDict> {
|
||||
indexStore: BaseIndexStore;
|
||||
imageVectorStore?: VectorStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* The VectorStoreIndex, an index that stores the nodes only according to their vector embedings.
|
||||
*/
|
||||
export class VectorStoreIndex extends BaseIndex<IndexDict> {
|
||||
vectorStore: VectorStore;
|
||||
indexStore: BaseIndexStore;
|
||||
embedModel: BaseEmbedding;
|
||||
imageVectorStore?: VectorStore;
|
||||
imageEmbedModel?: MultiModalEmbedding;
|
||||
|
||||
private constructor(init: VectorIndexConstructorProps) {
|
||||
super(init);
|
||||
this.indexStore = init.indexStore;
|
||||
this.vectorStore = init.vectorStore ?? init.storageContext.vectorStore;
|
||||
this.embedModel = init.serviceContext.embedModel;
|
||||
this.imageVectorStore =
|
||||
init.imageVectorStore ?? init.storageContext.imageVectorStore;
|
||||
if (this.imageVectorStore) {
|
||||
this.imageEmbedModel = new ClipEmbedding();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The async init function creates a new VectorStoreIndex.
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
public static async init(
|
||||
options: VectorIndexOptions,
|
||||
): Promise<VectorStoreIndex> {
|
||||
const storageContext =
|
||||
options.storageContext ?? (await storageContextFromDefaults({}));
|
||||
const serviceContext =
|
||||
options.serviceContext ?? serviceContextFromDefaults({});
|
||||
const indexStore = storageContext.indexStore;
|
||||
const docStore = storageContext.docStore;
|
||||
|
||||
let indexStruct = await VectorStoreIndex.setupIndexStructFromStorage(
|
||||
indexStore,
|
||||
options,
|
||||
);
|
||||
|
||||
if (!options.nodes && !indexStruct) {
|
||||
throw new Error(
|
||||
"Cannot initialize VectorStoreIndex without nodes or indexStruct",
|
||||
);
|
||||
}
|
||||
|
||||
indexStruct = indexStruct ?? new IndexDict();
|
||||
|
||||
const index = new this({
|
||||
storageContext,
|
||||
serviceContext,
|
||||
docStore,
|
||||
indexStruct,
|
||||
indexStore,
|
||||
vectorStore: options.vectorStore,
|
||||
imageVectorStore: options.imageVectorStore,
|
||||
});
|
||||
|
||||
if (options.nodes) {
|
||||
// If nodes are passed in, then we need to update the index
|
||||
await index.buildIndexFromNodes(options.nodes, {
|
||||
logProgress: options.logProgress,
|
||||
});
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private static async setupIndexStructFromStorage(
|
||||
indexStore: BaseIndexStore,
|
||||
options: IndexStructOptions,
|
||||
) {
|
||||
const indexStructs = (await indexStore.getIndexStructs()) as IndexDict[];
|
||||
let indexStruct: IndexDict | undefined;
|
||||
|
||||
if (options.indexStruct && indexStructs.length > 0) {
|
||||
throw new Error(
|
||||
"Cannot initialize index with both indexStruct and indexStore",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
)) as IndexDict;
|
||||
}
|
||||
// Check indexStruct type
|
||||
if (indexStruct && indexStruct.type !== IndexStructType.SIMPLE_DICT) {
|
||||
throw new Error(
|
||||
"Attempting to initialize VectorStoreIndex with non-vector indexStruct",
|
||||
);
|
||||
}
|
||||
return indexStruct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the embeddings for the given nodes.
|
||||
*
|
||||
* @param nodes - An array of BaseNode objects representing the nodes for which embeddings are to be calculated.
|
||||
* @param {Object} [options] - An optional object containing additional parameters.
|
||||
* @param {boolean} [options.logProgress] - A boolean indicating whether to log progress to the console (useful for debugging).
|
||||
*/
|
||||
async getNodeEmbeddingResults(
|
||||
nodes: BaseNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
): Promise<BaseNode[]> {
|
||||
const texts = nodes.map((node) => node.getContent(MetadataMode.EMBED));
|
||||
const embeddings = await this.embedModel.getTextEmbeddingsBatch(texts, {
|
||||
logProgress: options?.logProgress,
|
||||
});
|
||||
return nodes.map((node, i) => {
|
||||
node.embedding = embeddings[i];
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get embeddings for nodes and place them into the index.
|
||||
* @param nodes
|
||||
* @returns
|
||||
*/
|
||||
async buildIndexFromNodes(
|
||||
nodes: BaseNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
) {
|
||||
// Check if the index already has nodes with the same hash
|
||||
const newNodes = nodes.filter((node) =>
|
||||
Object.entries(this.indexStruct!.nodesDict).reduce(
|
||||
(acc, [key, value]) => {
|
||||
if (value.hash === node.hash) {
|
||||
acc = false;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
true,
|
||||
),
|
||||
);
|
||||
|
||||
await this.insertNodes(newNodes, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* High level API: split documents, get embeddings, and build index.
|
||||
* @param documents
|
||||
* @param args
|
||||
* @returns
|
||||
*/
|
||||
static async fromDocuments(
|
||||
documents: Document[],
|
||||
args: VectorIndexOptions = {},
|
||||
): Promise<VectorStoreIndex> {
|
||||
args.storageContext =
|
||||
args.storageContext ?? (await storageContextFromDefaults({}));
|
||||
args.serviceContext = args.serviceContext ?? serviceContextFromDefaults({});
|
||||
const docStore = args.storageContext.docStore;
|
||||
|
||||
for (const doc of documents) {
|
||||
docStore.setDocumentHash(doc.id_, doc.hash);
|
||||
}
|
||||
|
||||
if (args.logProgress) {
|
||||
console.log("Using node parser on documents...");
|
||||
}
|
||||
args.nodes = await runTransformations(documents, [
|
||||
args.serviceContext.nodeParser,
|
||||
]);
|
||||
if (args.logProgress) {
|
||||
console.log("Finished parsing documents.");
|
||||
}
|
||||
return await this.init(args);
|
||||
}
|
||||
|
||||
static async fromVectorStore(
|
||||
vectorStore: VectorStore,
|
||||
serviceContext: ServiceContext,
|
||||
imageVectorStore?: VectorStore,
|
||||
) {
|
||||
if (!vectorStore.storesText) {
|
||||
throw new Error(
|
||||
"Cannot initialize from a vector store that does not store text",
|
||||
);
|
||||
}
|
||||
|
||||
const storageContext = await storageContextFromDefaults({
|
||||
vectorStore,
|
||||
imageVectorStore,
|
||||
});
|
||||
|
||||
const index = await this.init({
|
||||
nodes: [],
|
||||
storageContext,
|
||||
serviceContext,
|
||||
});
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
asRetriever(
|
||||
options?: Omit<VectorIndexRetrieverOptions, "index">,
|
||||
): VectorIndexRetriever {
|
||||
return new VectorIndexRetriever({ index: this, ...options });
|
||||
}
|
||||
|
||||
asQueryEngine(options?: {
|
||||
retriever?: BaseRetriever;
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: MetadataFilters;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine & RetrieverQueryEngine {
|
||||
const { retriever, responseSynthesizer } = options ?? {};
|
||||
return new RetrieverQueryEngine(
|
||||
retriever ?? this.asRetriever(),
|
||||
responseSynthesizer,
|
||||
options?.preFilters,
|
||||
options?.nodePostprocessors,
|
||||
);
|
||||
}
|
||||
|
||||
protected async insertNodesToStore(
|
||||
vectorStore: VectorStore,
|
||||
nodes: BaseNode[],
|
||||
): Promise<void> {
|
||||
const newIds = await vectorStore.add(nodes);
|
||||
|
||||
// NOTE: if the vector store doesn't store text,
|
||||
// we need to add the nodes to the index struct and document store
|
||||
// NOTE: if the vector store keeps text,
|
||||
// we only need to add image and index nodes
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
const type = nodes[i].getType();
|
||||
if (
|
||||
!vectorStore.storesText ||
|
||||
type === ObjectType.INDEX ||
|
||||
type === ObjectType.IMAGE
|
||||
) {
|
||||
const nodeWithoutEmbedding = nodes[i].clone();
|
||||
nodeWithoutEmbedding.embedding = undefined;
|
||||
this.indexStruct.addNode(nodeWithoutEmbedding, newIds[i]);
|
||||
await this.docStore.addDocuments([nodeWithoutEmbedding], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async insertNodes(
|
||||
nodes: BaseNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
): Promise<void> {
|
||||
if (!nodes || nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const { imageNodes, textNodes } = splitNodesByType(nodes);
|
||||
if (imageNodes.length > 0) {
|
||||
if (!this.imageVectorStore) {
|
||||
throw new Error("Cannot insert image nodes without image vector store");
|
||||
}
|
||||
const imageNodesWithEmbedding = await this.getImageNodeEmbeddingResults(
|
||||
imageNodes,
|
||||
options,
|
||||
);
|
||||
await this.insertNodesToStore(
|
||||
this.imageVectorStore,
|
||||
imageNodesWithEmbedding,
|
||||
);
|
||||
}
|
||||
const embeddingResults = await this.getNodeEmbeddingResults(
|
||||
textNodes,
|
||||
options,
|
||||
);
|
||||
await this.insertNodesToStore(this.vectorStore, embeddingResults);
|
||||
await this.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
|
||||
async deleteRefDoc(
|
||||
refDocId: string,
|
||||
deleteFromDocStore: boolean = true,
|
||||
): Promise<void> {
|
||||
await this.deleteRefDocFromStore(this.vectorStore, refDocId);
|
||||
if (this.imageVectorStore) {
|
||||
await this.deleteRefDocFromStore(this.imageVectorStore, refDocId);
|
||||
}
|
||||
|
||||
if (deleteFromDocStore) {
|
||||
await this.docStore.deleteDocument(refDocId, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected async deleteRefDocFromStore(
|
||||
vectorStore: VectorStore,
|
||||
refDocId: string,
|
||||
): Promise<void> {
|
||||
vectorStore.delete(refDocId);
|
||||
|
||||
if (!vectorStore.storesText) {
|
||||
const refDocInfo = await this.docStore.getRefDocInfo(refDocId);
|
||||
|
||||
if (refDocInfo) {
|
||||
for (const nodeId of refDocInfo.nodeIds) {
|
||||
this.indexStruct.delete(nodeId);
|
||||
vectorStore.delete(nodeId);
|
||||
}
|
||||
}
|
||||
await this.indexStore.addIndexStruct(this.indexStruct);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the embeddings for the given image nodes.
|
||||
*
|
||||
* @param nodes - An array of ImageNode objects representing the nodes for which embeddings are to be calculated.
|
||||
* @param {Object} [options] - An optional object containing additional parameters.
|
||||
* @param {boolean} [options.logProgress] - A boolean indicating whether to log progress to the console (useful for debugging).
|
||||
*/
|
||||
async getImageNodeEmbeddingResults(
|
||||
nodes: ImageNode[],
|
||||
options?: { logProgress?: boolean },
|
||||
): Promise<ImageNode[]> {
|
||||
if (!this.imageEmbedModel) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const nodesWithEmbeddings: ImageNode[] = [];
|
||||
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
const node = nodes[i];
|
||||
if (options?.logProgress) {
|
||||
console.log(`Getting embedding for node ${i + 1}/${nodes.length}`);
|
||||
}
|
||||
node.embedding = await this.imageEmbedModel.getImageEmbedding(node.image);
|
||||
nodesWithEmbeddings.push(node);
|
||||
}
|
||||
|
||||
return nodesWithEmbeddings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* VectorIndexRetriever retrieves nodes from a VectorIndex.
|
||||
*/
|
||||
|
||||
export type VectorIndexRetrieverOptions = {
|
||||
index: VectorStoreIndex;
|
||||
similarityTopK?: number;
|
||||
imageSimilarityTopK?: number;
|
||||
};
|
||||
|
||||
export class VectorIndexRetriever implements BaseRetriever {
|
||||
index: VectorStoreIndex;
|
||||
similarityTopK: number;
|
||||
imageSimilarityTopK: number;
|
||||
private serviceContext: ServiceContext;
|
||||
|
||||
constructor({
|
||||
index,
|
||||
similarityTopK,
|
||||
imageSimilarityTopK,
|
||||
}: VectorIndexRetrieverOptions) {
|
||||
this.index = index;
|
||||
this.serviceContext = this.index.serviceContext;
|
||||
this.similarityTopK = similarityTopK ?? DEFAULT_SIMILARITY_TOP_K;
|
||||
this.imageSimilarityTopK = imageSimilarityTopK ?? DEFAULT_SIMILARITY_TOP_K;
|
||||
}
|
||||
|
||||
async retrieve(
|
||||
query: string,
|
||||
parentEvent?: Event,
|
||||
preFilters?: MetadataFilters,
|
||||
): Promise<NodeWithScore[]> {
|
||||
let nodesWithScores = await this.textRetrieve(query, preFilters);
|
||||
nodesWithScores = nodesWithScores.concat(
|
||||
await this.textToImageRetrieve(query, preFilters),
|
||||
);
|
||||
this.sendEvent(query, nodesWithScores, parentEvent);
|
||||
return nodesWithScores;
|
||||
}
|
||||
|
||||
protected async textRetrieve(
|
||||
query: string,
|
||||
preFilters?: MetadataFilters,
|
||||
): Promise<NodeWithScore[]> {
|
||||
const options = {};
|
||||
const q = await this.buildVectorStoreQuery(
|
||||
this.index.embedModel,
|
||||
query,
|
||||
this.similarityTopK,
|
||||
preFilters,
|
||||
);
|
||||
const result = await this.index.vectorStore.query(q, options);
|
||||
return this.buildNodeListFromQueryResult(result);
|
||||
}
|
||||
|
||||
private async textToImageRetrieve(
|
||||
query: string,
|
||||
preFilters?: MetadataFilters,
|
||||
) {
|
||||
if (!this.index.imageEmbedModel || !this.index.imageVectorStore) {
|
||||
// no-op if image embedding and vector store are not set
|
||||
return [];
|
||||
}
|
||||
const q = await this.buildVectorStoreQuery(
|
||||
this.index.imageEmbedModel,
|
||||
query,
|
||||
this.imageSimilarityTopK,
|
||||
preFilters,
|
||||
);
|
||||
const result = await this.index.imageVectorStore.query(q, preFilters);
|
||||
return this.buildNodeListFromQueryResult(result);
|
||||
}
|
||||
|
||||
protected sendEvent(
|
||||
query: string,
|
||||
nodesWithScores: NodeWithScore<Metadata>[],
|
||||
parentEvent: Event | undefined,
|
||||
) {
|
||||
if (this.serviceContext.callbackManager.onRetrieve) {
|
||||
this.serviceContext.callbackManager.onRetrieve({
|
||||
query,
|
||||
nodes: nodesWithScores,
|
||||
event: globalsHelper.createEvent({
|
||||
parentEvent,
|
||||
type: "retrieve",
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected async buildVectorStoreQuery(
|
||||
embedModel: BaseEmbedding,
|
||||
query: string,
|
||||
similarityTopK: number,
|
||||
preFilters?: MetadataFilters,
|
||||
): Promise<VectorStoreQuery> {
|
||||
const queryEmbedding = await embedModel.getQueryEmbedding(query);
|
||||
|
||||
return {
|
||||
queryEmbedding: queryEmbedding,
|
||||
mode: VectorStoreQueryMode.DEFAULT,
|
||||
similarityTopK: similarityTopK,
|
||||
filters: preFilters ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
protected buildNodeListFromQueryResult(result: VectorStoreQueryResult) {
|
||||
const nodesWithScores: NodeWithScore[] = [];
|
||||
for (let i = 0; i < result.ids.length; i++) {
|
||||
const nodeFromResult = result.nodes?.[i];
|
||||
if (!this.index.indexStruct.nodesDict[result.ids[i]] && nodeFromResult) {
|
||||
this.index.indexStruct.nodesDict[result.ids[i]] = nodeFromResult;
|
||||
}
|
||||
|
||||
const node = this.index.indexStruct.nodesDict[result.ids[i]];
|
||||
// XXX: Hack, if it's an image node, we reconstruct the image from the URL
|
||||
// Alternative: Store image in doc store and retrieve it here
|
||||
if (node instanceof ImageNode) {
|
||||
node.image = node.getUrl();
|
||||
}
|
||||
|
||||
nodesWithScores.push({
|
||||
node: node,
|
||||
score: result.similarities[i],
|
||||
});
|
||||
}
|
||||
|
||||
return nodesWithScores;
|
||||
}
|
||||
|
||||
getServiceContext(): ServiceContext {
|
||||
return this.serviceContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { BaseNode, MetadataMode } from "../Node";
|
||||
import { createSHA256 } from "../env";
|
||||
import { docToJson, jsonToDoc } from "../storage/docStore/utils";
|
||||
import { SimpleKVStore } from "../storage/kvStore/SimpleKVStore";
|
||||
import { BaseKVStore } from "../storage/kvStore/types";
|
||||
import { TransformComponent } from "./types";
|
||||
import { createSHA256 } from "@llamaindex/env";
|
||||
import { BaseNode, MetadataMode } from "../Node.js";
|
||||
import { docToJson, jsonToDoc } from "../storage/docStore/utils.js";
|
||||
import { SimpleKVStore } from "../storage/kvStore/SimpleKVStore.js";
|
||||
import { BaseKVStore } from "../storage/kvStore/types.js";
|
||||
import { TransformComponent } from "./types.js";
|
||||
|
||||
const transformToJSON = (obj: TransformComponent) => {
|
||||
let seen: any[] = [];
|
||||
const seen: any[] = [];
|
||||
|
||||
const replacer = (key: string, value: any) => {
|
||||
if (value != null && typeof value == "object") {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { BaseNode, Document } from "../Node";
|
||||
import { BaseReader } from "../readers/type";
|
||||
import { BaseDocumentStore, VectorStore } from "../storage";
|
||||
import { IngestionCache, getTransformationHash } from "./IngestionCache";
|
||||
import { DocStoreStrategy, createDocStoreStrategy } from "./strategies";
|
||||
import { TransformComponent } from "./types";
|
||||
import { BaseNode, Document } from "../Node.js";
|
||||
import { BaseReader } from "../readers/type.js";
|
||||
import { BaseDocumentStore } from "../storage/docStore/types.js";
|
||||
import { VectorStore } from "../storage/vectorStore/types.js";
|
||||
import { IngestionCache, getTransformationHash } from "./IngestionCache.js";
|
||||
import {
|
||||
DocStoreStrategy,
|
||||
createDocStoreStrategy,
|
||||
} from "./strategies/index.js";
|
||||
import { TransformComponent } from "./types.js";
|
||||
|
||||
type IngestionRunArgs = {
|
||||
documents?: Document[];
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from "./IngestionPipeline";
|
||||
export * from "./types";
|
||||
export * from "./IngestionPipeline.js";
|
||||
export * from "./types.js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseNode } from "../../Node";
|
||||
import { BaseDocumentStore } from "../../storage";
|
||||
import { TransformComponent } from "../types";
|
||||
import { BaseNode } from "../../Node.js";
|
||||
import { BaseDocumentStore } from "../../storage/docStore/types.js";
|
||||
import { TransformComponent } from "../types.js";
|
||||
|
||||
/**
|
||||
* Handle doc store duplicates by checking all hashes.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseNode } from "../../Node";
|
||||
import { BaseDocumentStore, VectorStore } from "../../storage";
|
||||
import { classify } from "./classify";
|
||||
import { BaseNode } from "../../Node.js";
|
||||
import { BaseDocumentStore, VectorStore } from "../../storage/index.js";
|
||||
import { classify } from "./classify.js";
|
||||
|
||||
/**
|
||||
* Handle docstore upserts by checking hashes and ids.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { BaseNode } from "../../Node";
|
||||
import { BaseDocumentStore, VectorStore } from "../../storage";
|
||||
import { TransformComponent } from "../types";
|
||||
import { classify } from "./classify";
|
||||
import { BaseNode } from "../../Node.js";
|
||||
import { BaseDocumentStore } from "../../storage/docStore/types.js";
|
||||
import { VectorStore } from "../../storage/vectorStore/types.js";
|
||||
import { TransformComponent } from "../types.js";
|
||||
import { classify } from "./classify.js";
|
||||
|
||||
/**
|
||||
* Handles doc store upserts by checking hashes and ids.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseNode } from "../../Node";
|
||||
import { BaseDocumentStore } from "../../storage";
|
||||
import { BaseNode } from "../../Node.js";
|
||||
import { BaseDocumentStore } from "../../storage/docStore/types.js";
|
||||
|
||||
export async function classify(docStore: BaseDocumentStore, nodes: BaseNode[]) {
|
||||
const existingDocIds = Object.values(await docStore.getAllDocumentHashes());
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { BaseDocumentStore, VectorStore } from "../../storage";
|
||||
import { TransformComponent } from "../types";
|
||||
import { DuplicatesStrategy } from "./DuplicatesStrategy";
|
||||
import { UpsertsStrategy } from "./UpsertsStrategy";
|
||||
import { BaseDocumentStore } from "../../storage/docStore/types.js";
|
||||
import { VectorStore } from "../../storage/vectorStore/types.js";
|
||||
import { TransformComponent } from "../types.js";
|
||||
import { DuplicatesStrategy } from "./DuplicatesStrategy.js";
|
||||
import { UpsertsStrategy } from "./UpsertsStrategy.js";
|
||||
|
||||
export enum DocStoreStrategy {
|
||||
UPSERTS = "upserts",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseNode } from "../Node";
|
||||
import { BaseNode } from "../Node.js";
|
||||
|
||||
export interface TransformComponent {
|
||||
transform(nodes: BaseNode[], options?: any): Promise<BaseNode[]>;
|
||||
|
||||
@@ -6,28 +6,28 @@ import {
|
||||
EventType,
|
||||
OpenAIStreamToken,
|
||||
StreamCallbackResponse,
|
||||
} from "../callbacks/CallbackManager";
|
||||
} from "../callbacks/CallbackManager.js";
|
||||
|
||||
import { ChatCompletionMessageParam } from "openai/resources";
|
||||
import { ChatCompletionMessageParam } from "openai/resources/index.js";
|
||||
import { LLMOptions } from "portkey-ai";
|
||||
import { Tokenizers, globalsHelper } from "../GlobalsHelper";
|
||||
import { Tokenizers, globalsHelper } from "../GlobalsHelper.js";
|
||||
import {
|
||||
ANTHROPIC_AI_PROMPT,
|
||||
ANTHROPIC_HUMAN_PROMPT,
|
||||
AnthropicSession,
|
||||
getAnthropicSession,
|
||||
} from "./anthropic";
|
||||
} from "./anthropic.js";
|
||||
import {
|
||||
AzureOpenAIConfig,
|
||||
getAzureBaseUrl,
|
||||
getAzureConfigFromEnv,
|
||||
getAzureModel,
|
||||
shouldUseAzure,
|
||||
} from "./azure";
|
||||
import { BaseLLM } from "./base";
|
||||
import { OpenAISession, getOpenAISession } from "./open_ai";
|
||||
import { PortkeySession, getPortkeySession } from "./portkey";
|
||||
import { ReplicateSession } from "./replicate_ai";
|
||||
} from "./azure.js";
|
||||
import { BaseLLM } from "./base.js";
|
||||
import { OpenAISession, getOpenAISession } from "./open_ai.js";
|
||||
import { PortkeySession, getPortkeySession } from "./portkey.js";
|
||||
import { ReplicateSession } from "./replicate_ai.js";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
LLMChatParamsStreaming,
|
||||
LLMMetadata,
|
||||
MessageType,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
export const GPT4_MODELS = {
|
||||
"gpt-4": { contextWindow: 8192 },
|
||||
@@ -235,7 +235,7 @@ export class OpenAI extends BaseLLM {
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream, tools, toolChoice } = params;
|
||||
let baseRequestParams: OpenAILLM.Chat.ChatCompletionCreateParams = {
|
||||
const baseRequestParams: OpenAILLM.Chat.ChatCompletionCreateParams = {
|
||||
model: this.model,
|
||||
temperature: this.temperature,
|
||||
max_tokens: this.maxTokens,
|
||||
|
||||
@@ -26,7 +26,7 @@ export class AnthropicSession {
|
||||
// I'm not 100% sure this is necessary vs. just starting a new session
|
||||
// every time we make a call. They say they try to reuse connections
|
||||
// so in theory this is more efficient, but we should test it in the future.
|
||||
let defaultAnthropicSession: {
|
||||
const defaultAnthropicSession: {
|
||||
session: AnthropicSession;
|
||||
options: ClientOptions;
|
||||
}[] = [];
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
LLMCompletionParamsNonStreaming,
|
||||
LLMCompletionParamsStreaming,
|
||||
LLMMetadata,
|
||||
} from "./types";
|
||||
import { streamConverter } from "./utils";
|
||||
} from "./types.js";
|
||||
import { streamConverter } from "./utils.js";
|
||||
|
||||
export abstract class BaseLLM implements LLM {
|
||||
abstract metadata: LLMMetadata;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OpenAI } from "./LLM";
|
||||
import { OpenAI } from "./LLM.js";
|
||||
|
||||
export class FireworksLLM extends OpenAI {
|
||||
constructor(init?: Partial<OpenAI>) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export * from "./LLM";
|
||||
export { FireworksLLM } from "./fireworks";
|
||||
export * from "./LLM.js";
|
||||
export { FireworksLLM } from "./fireworks.js";
|
||||
export {
|
||||
ALL_AVAILABLE_MISTRAL_MODELS,
|
||||
MistralAI,
|
||||
MistralAISession,
|
||||
} from "./mistral";
|
||||
export { Ollama } from "./ollama";
|
||||
export * from "./open_ai";
|
||||
export { TogetherLLM } from "./together";
|
||||
export * from "./types";
|
||||
} from "./mistral.js";
|
||||
export { Ollama } from "./ollama.js";
|
||||
export * from "./open_ai.js";
|
||||
export { TogetherLLM } from "./together.js";
|
||||
export * from "./types.js";
|
||||
|
||||
@@ -3,15 +3,15 @@ import {
|
||||
Event,
|
||||
EventType,
|
||||
StreamCallbackResponse,
|
||||
} from "../callbacks/CallbackManager";
|
||||
import { BaseLLM } from "./base";
|
||||
} from "../callbacks/CallbackManager.js";
|
||||
import { BaseLLM } from "./base.js";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
ChatResponseChunk,
|
||||
LLMChatParamsNonStreaming,
|
||||
LLMChatParamsStreaming,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
export const ALL_AVAILABLE_MISTRAL_MODELS = {
|
||||
"mistral-tiny": { contextWindow: 32000 },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CallbackManager, Event } from "../callbacks/CallbackManager";
|
||||
import { BaseEmbedding } from "../embeddings/types";
|
||||
import { ok } from "../env";
|
||||
import { ok } from "@llamaindex/env";
|
||||
import { CallbackManager, Event } from "../callbacks/CallbackManager.js";
|
||||
import { BaseEmbedding } from "../embeddings/types.js";
|
||||
import {
|
||||
ChatMessage,
|
||||
ChatResponse,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
LLMCompletionParamsNonStreaming,
|
||||
LLMCompletionParamsStreaming,
|
||||
LLMMetadata,
|
||||
} from "./types";
|
||||
} from "./types.js";
|
||||
|
||||
const messageAccessor = (data: any): ChatResponseChunk => {
|
||||
return {
|
||||
|
||||
@@ -35,8 +35,10 @@ export class OpenAISession {
|
||||
// I'm not 100% sure this is necessary vs. just starting a new session
|
||||
// every time we make a call. They say they try to reuse connections
|
||||
// so in theory this is more efficient, but we should test it in the future.
|
||||
let defaultOpenAISession: { session: OpenAISession; options: ClientOptions }[] =
|
||||
[];
|
||||
const defaultOpenAISession: {
|
||||
session: OpenAISession;
|
||||
options: ClientOptions;
|
||||
}[] = [];
|
||||
|
||||
/**
|
||||
* Get a session for the OpenAI API. If one already exists with the same options,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user