Compare commits

..

19 Commits

Author SHA1 Message Date
Marcus Schiesser 24cb48f195 RELEASING: Releasing 1 package(s)
Releases:
      create-llama@0.0.21

    [skip ci]
2024-01-29 17:38:57 +07:00
Marcus Schiesser 965cfd291e fix: use pipeline instead of nodeparser (#471) 2024-01-29 17:31:09 +07:00
Marcus Schiesser 873329c052 Revert "feat: Add chat with web data (#450)"
This reverts commit 27d55fde8c.
2024-01-29 17:28:16 +07:00
Marcus Schiesser 3d8023b9a9 feat: add ingestion pipeline cache (#442) 2024-01-29 16:45:09 +07:00
Marcus Schiesser 93d1450fc1 RELEASING: Releasing 1 package(s)
Releases:
  create-llama@0.0.20

[skip ci]
2024-01-29 16:37:42 +07:00
Huu Le (Lee) 27d55fde8c feat: Add chat with web data (#450) 2024-01-29 14:16:57 +07:00
Marcus Schiesser 690399b04c feat: also test context template engine (#470) 2024-01-29 11:36:35 +07:00
hiepxanh 65b84f1ab3 docs: fix dead link (#452) 2024-01-28 12:11:40 -06:00
Ian Sinnott 835acb89d0 docs: remove unused arg in qdrant docs (#461) 2024-01-28 12:11:29 -06:00
Alex Yang d9df9ea75c RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.3

[skip ci]
2024-01-28 11:59:22 -06:00
Alex Yang 06874ffb69 fix: cannot run examples 2024-01-28 11:59:02 -06:00
Alex Yang f0898a3930 fix: ignore release examples 2024-01-28 11:57:16 -06:00
Alex Yang 7d50196d2f fix: output edge-light (#469) 2024-01-28 11:51:12 -06:00
Emanuel Ferreira f90f7fee64 docs: ingestion pipeline, transformations (#464) 2024-01-27 11:53:52 -03:00
Emanuel Ferreira 3d860df873 chore: update example (#463) 2024-01-27 10:11:50 -03:00
Emanuel Ferreira 2fe3a2b6a8 chore: enhancement optional args extractors (#462) 2024-01-27 09:18:17 -03:00
Emanuel Ferreira eb3d4af204 docs: usage metadata extraction (#460) 2024-01-27 08:37:24 -03:00
Alex Yang 0652352e92 chore: fix circular dependency (#459) 2024-01-27 00:46:04 -06:00
Alex Yang 103949513b chore: bump version (#458) 2024-01-26 22:46:00 -06:00
58 changed files with 644 additions and 270 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"llamaindex": patch
---
update dependencies
+3
View File
@@ -36,3 +36,6 @@ jobs:
working-directory: ./packages/core
- name: Run Type Check
run: pnpm run type-check
- name: Run Circular Dependency Check
run: pnpm run circular-check
working-directory: ./packages/core
+7 -7
View File
@@ -32,12 +32,12 @@ LlamaIndex.TS help you prepare the knowledge base with a suite of data connector
![](../_static/concepts/indexing.jpg)
[**Data Loaders**](./modules/high_level/data_loader.md):
[**Data Loaders**](../modules/data_loader.md):
A data connector (i.e. `Reader`) ingest data from different data sources and data formats into a simple `Document` representation (text and simple metadata).
[**Documents / Nodes**](./modules/high_level/documents_and_nodes.md): A `Document` is a generic container around any data source - for instance, a PDF, an API output, or retrieved data from a database. A `Node` is the atomic unit of data in LlamaIndex and represents a "chunk" of a source `Document`. It's a rich representation that includes metadata and relationships (to other nodes) to enable accurate and expressive retrieval operations.
[**Documents / Nodes**](../modules/documents_and_nodes.md): A `Document` is a generic container around any data source - for instance, a PDF, an API output, or retrieved data from a database. A `Node` is the atomic unit of data in LlamaIndex and represents a "chunk" of a source `Document`. It's a rich representation that includes metadata and relationships (to other nodes) to enable accurate and expressive retrieval operations.
[**Data Indexes**](./modules/high_level/data_index.md):
[**Data Indexes**](../modules/data_index.md):
Once you've ingested your data, LlamaIndex helps you index data into a format that's easy to retrieve.
Under the hood, LlamaIndex parses the raw documents into intermediate representations, calculates vector embeddings, and stores your data in-memory or to disk.
@@ -60,19 +60,19 @@ These building blocks can be customized to reflect ranking preferences, as well
#### Building Blocks
[**Retrievers**](./modules/low_level/retriever.md):
[**Retrievers**](../modules/retriever.md):
A retriever defines how to efficiently retrieve relevant context from a knowledge base (i.e. index) when given a query.
The specific retrieval logic differs for difference indices, the most popular being dense retrieval against a vector index.
[**Response Synthesizers**](./modules/low_level/response_synthesizer.md):
[**Response Synthesizers**](../modules/response_synthesizer.md):
A response synthesizer generates a response from an LLM, using a user query and a given set of retrieved text chunks.
#### Pipelines
[**Query Engines**](./modules/high_level/query_engine.md):
[**Query Engines**](../modules/query_engine.md):
A query engine is an end-to-end pipeline that allow you to ask question over your data.
It takes in a natural language query, and returns a response, along with reference context retrieved and passed to the LLM.
[**Chat Engines**](./modules/high_level/chat_engine.md):
[**Chat Engines**](../modules/chat_engine.md):
A chat engine is an end-to-end pipeline for having a conversation with your data
(multiple back-and-forth instead of a single question & answer).
+1 -1
View File
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 4
---
# Index
+1 -1
View File
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 3
---
# Reader / Loader
@@ -0,0 +1,2 @@
label: "Document / Nodes"
position: 0
@@ -0,0 +1,45 @@
# Metadata Extraction Usage Pattern
You can use LLMs to automate metadata extraction with our `Metadata Extractor` modules.
Our metadata extractor modules include the following "feature extractors":
- `SummaryExtractor` - automatically extracts a summary over a set of Nodes
- `QuestionsAnsweredExtractor` - extracts a set of questions that each Node can answer
- `TitleExtractor` - extracts a title over the context of each Node by document and combine them
- `KeywordExtractor` - extracts keywords over the context of each Node
Then you can chain the `Metadata Extractors` with the `IngestionPipeline` to extract metadata from a set of documents.
```ts
import {
IngestionPipeline,
TitleExtractor,
QuestionsAnsweredExtractor,
Document,
OpenAI,
} from "llamaindex";
async function main() {
const pipeline = new IngestionPipeline({
transformations: [
new TitleExtractor(),
new QuestionsAnsweredExtractor({
questions: 5,
}),
],
});
const nodes = await pipeline.run({
documents: [
new Document({ text: "I am 10 years old. John is 20 years old." }),
],
});
for (const node of nodes) {
console.log(node.metadata);
}
}
main().then(() => console.log("done"));
```
+1 -1
View File
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 3
---
# Embedding
-31
View File
@@ -1,31 +0,0 @@
# Core Modules
LlamaIndex.TS offers several core modules, seperated into high-level modules for quickly getting started, and low-level modules for customizing key components as you need.
## High-Level Modules
- [**Document**](./high_level/documents_and_nodes.md): A document represents a text file, PDF file or other contiguous piece of data.
- [**Node**](./high_level/documents_and_nodes.md): The basic data building block. Most commonly, these are parts of the document split into manageable pieces that are small enough to be fed into an embedding model and LLM.
- [**Reader/Loader**](./high_level/data_loader.md): A reader or loader is something that takes in a document in the real world and transforms into a Document class that can then be used in your Index and queries. We currently support plain text files and PDFs with many many more to come.
- [**Indexes**](./high_level/data_index.md): indexes store the Nodes and the embeddings of those nodes.
- [**QueryEngine**](./high_level/query_engine.md): Query engines are what generate the query you put in and give you back the result. Query engines generally combine a pre-built prompt with selected nodes from your Index to give the LLM the context it needs to answer your query.
- [**ChatEngine**](./high_level/chat_engine.md): A ChatEngine helps you build a chatbot that will interact with your Indexes.
## Low Level Module
- [**LLM**](./low_level/llm.md): The LLM class is a unified interface over a large language model provider such as OpenAI GPT-4, Anthropic Claude, or Meta LLaMA. You can subclass it to write a connector to your own large language model.
- [**Embedding**](./low_level/embedding.md): An embedding is represented as a vector of floating point numbers. OpenAI's text-embedding-ada-002 is our default embedding model and each embedding it generates consists of 1,536 floating point numbers. Another popular embedding model is BERT which uses 768 floating point numbers to represent each Node. We provide a number of utilities to work with embeddings including 3 similarity calculation options and Maximum Marginal Relevance
- [**TextSplitter/NodeParser**](./low_level/node_parser.md): Text splitting strategies are incredibly important to the overall efficacy of the embedding search. Currently, while we do have a default, there's no one size fits all solution. Depending on the source documents, you may want to use different splitting sizes and strategies. Currently we support spliltting by fixed size, splitting by fixed size with overlapping sections, splitting by sentence, and splitting by paragraph. The text splitter is used by the NodeParser when splitting `Document`s into `Node`s.
- [**Retriever**](./low_level/retriever.md): The Retriever is what actually chooses the Nodes to retrieve from the index. Here, you may wish to try retrieving more or fewer Nodes per query, changing your similarity function, or creating your own retriever for each individual use case in your application. For example, you may wish to have a separate retriever for code content vs. text content.
- [**ResponseSynthesizer**](./low_level/response_synthesizer.md): The ResponseSynthesizer is responsible for taking a query string, and using a list of `Node`s to generate a response. This can take many forms, like iterating over all the context and refining an answer, or building a tree of summaries and returning the root summary.
- [**Storage**](./low_level/storage.md): At some point you're going to want to store your indexes, data and vectors instead of re-running the embedding models every time. IndexStore, DocStore, VectorStore, and KVStore are abstractions that let you do that. Combined, they form the StorageContext. Currently, we allow you to persist your embeddings in files on the filesystem (or a virtual in memory file system), but we are also actively adding integrations to Vector Databases.
@@ -0,0 +1,2 @@
label: "Ingestion Pipeline"
position: 2
@@ -0,0 +1,99 @@
# Ingestion Pipeline
An `IngestionPipeline` uses a concept of `Transformations` that are applied to input data.
These `Transformations` are applied to your input data, and the resulting nodes are either returned or inserted into a vector database (if given).
## Usage Pattern
The simplest usage is to instantiate an IngestionPipeline like so:
```ts
import fs from "node:fs/promises";
import {
Document,
IngestionPipeline,
MetadataMode,
OpenAIEmbedding,
TitleExtractor,
SimpleNodeParser,
} from "llamaindex";
async function main() {
// Load essay from abramov.txt in Node
const path = "node_modules/llamaindex/examples/abramov.txt";
const essay = await fs.readFile(path, "utf-8");
// Create Document object with essay
const document = new Document({ text: essay, id_: path });
const pipeline = new IngestionPipeline({
transformations: [
new SimpleNodeParser({ chunkSize: 1024, chunkOverlap: 20 }),
new TitleExtractor(),
new OpenAIEmbedding(),
],
});
// run the pipeline
const nodes = await pipeline.run({ documents: [document] });
// print out the result of the pipeline run
for (const node of nodes) {
console.log(node.getContent(MetadataMode.NONE));
}
}
main().catch(console.error);
```
## Connecting to Vector Databases
When running an ingestion pipeline, you can also chose to automatically insert the resulting nodes into a remote vector store.
Then, you can construct an index from that vector store later on.
```ts
import fs from "node:fs/promises";
import {
Document,
IngestionPipeline,
MetadataMode,
OpenAIEmbedding,
TitleExtractor,
SimpleNodeParser,
QdrantVectorStore,
VectorStoreIndex,
} from "llamaindex";
async function main() {
// Load essay from abramov.txt in Node
const path = "node_modules/llamaindex/examples/abramov.txt";
const essay = await fs.readFile(path, "utf-8");
const vectorStore = new QdrantVectorStore({
host: "http://localhost:6333",
});
// Create Document object with essay
const document = new Document({ text: essay, id_: path });
const pipeline = new IngestionPipeline({
transformations: [
new SimpleNodeParser({ chunkSize: 1024, chunkOverlap: 20 }),
new TitleExtractor(),
new OpenAIEmbedding(),
],
vectorStore,
});
// run the pipeline
const nodes = await pipeline.run({ documents: [document] });
// create an index
const index = VectorStoreIndex.fromVectorStore(vectorStore);
}
main().catch(console.error);
```
@@ -0,0 +1,77 @@
# Transformations
A transformation is something that takes a list of nodes as an input, and returns a list of nodes. Each component that implements the Transformatio class has both a `transform` definition responsible for transforming the nodes
Currently, the following components are Transformation objects:
- [SimpleNodeParser](../../api/classes/SimpleNodeParser.md)
- [MetadataExtractor](../documents_and_nodes/metadata_extraction.md)
- Embeddings
## Usage Pattern
While transformations are best used with with an IngestionPipeline, they can also be used directly.
```ts
import { SimpleNodeParser, TitleExtractor, Document } from "llamaindex";
async function main() {
let nodes = new SimpleNodeParser().getNodesFromDocuments([
new Document({ text: "I am 10 years old. John is 20 years old." }),
]);
const titleExtractor = new TitleExtractor();
nodes = await titleExtractor.transform(nodes);
for (const node of nodes) {
console.log(node.getContent(MetadataMode.NONE));
}
}
main().catch(console.error);
```
## Custom Transformations
You can implement any transformation yourself by implementing the `TransformerComponent`.
The following custom transformation will remove any special characters or punctutaion in text.
```ts
import { TransformerComponent, Node } from "llamaindex";
class RemoveSpecialCharacters extends TransformerComponent {
async transform(nodes: Node[]): Promise<Node[]> {
for (const node of nodes) {
node.text = node.text.replace(/[^\w\s]/gi, "");
}
return nodes;
}
}
```
These can then be used directly or in any IngestionPipeline.
```ts
import { IngestionPipeline, Document } from "llamaindex";
async function main() {
const pipeline = new IngestionPipeline({
transformations: [new RemoveSpecialCharacters()],
});
const nodes = await pipeline.run({
documents: [
new Document({ text: "I am 10 years old. John is 20 years old." }),
],
});
for (const node of nodes) {
console.log(node.getContent(MetadataMode.NONE));
}
}
main().catch(console.error);
```
+1 -1
View File
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 3
---
# LLM
+1 -1
View File
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---
# QueryEngine
@@ -1,2 +1,2 @@
label: "Vector Stores"
position: 0
position: 1
@@ -26,7 +26,6 @@ const essay = await fs.readFile(path, "utf-8");
```ts
const vectorStore = new QdrantVectorStore({
url: "http://localhost:6333",
port: 6333,
});
```
@@ -65,7 +64,6 @@ async function main() {
const vectorStore = new QdrantVectorStore({
url: "http://localhost:6333",
port: 6333,
});
const document = new Document({ text: essay, id_: path });
-22
View File
@@ -1,22 +0,0 @@
# simple
## 0.0.3
### Patch Changes
- Updated dependencies [5a765aa]
- llamaindex@0.0.5
## 0.0.2
### Patch Changes
- Updated dependencies [c65d671]
- llamaindex@0.0.4
## 0.0.1
### Patch Changes
- Updated dependencies [ca9410f]
- llamaindex@0.0.3
+4 -1
View File
@@ -16,7 +16,10 @@ import {
console.log(nodes);
const keywordExtractor = new KeywordExtractor(openaiLLM, 5);
const keywordExtractor = new KeywordExtractor({
llm: openaiLLM,
keywords: 5,
});
const nodesWithKeywordMetadata = await keywordExtractor.processNodes(nodes);
@@ -19,10 +19,10 @@ import {
}),
]);
const questionsAnsweredExtractor = new QuestionsAnsweredExtractor(
openaiLLM,
5,
);
const questionsAnsweredExtractor = new QuestionsAnsweredExtractor({
llm: openaiLLM,
questions: 5,
});
const nodesWithQuestionsMetadata =
await questionsAnsweredExtractor.processNodes(nodes);
+3 -1
View File
@@ -16,7 +16,9 @@ import {
}),
]);
const summaryExtractor = new SummaryExtractor(openaiLLM);
const summaryExtractor = new SummaryExtractor({
llm: openaiLLM,
});
const nodesWithSummaryMetadata = await summaryExtractor.processNodes(nodes);
+4 -1
View File
@@ -11,7 +11,10 @@ import { Document, OpenAI, SimpleNodeParser, TitleExtractor } from "llamaindex";
}),
]);
const titleExtractor = new TitleExtractor(openaiLLM, 1);
const titleExtractor = new TitleExtractor({
llm: openaiLLM,
nodes: 5,
});
const nodesWithTitledMetadata = await titleExtractor.processNodes(nodes);
+2 -1
View File
@@ -1,6 +1,7 @@
{
"name": "examples",
"private": true,
"version": "0.0.3",
"dependencies": {
"@datastax/astra-db-ts": "^0.1.4",
"@notionhq/client": "^2.2.14",
@@ -8,7 +9,7 @@
"chromadb": "^1.8.1",
"commander": "^11.1.0",
"dotenv": "^16.4.1",
"llamaindex": "workspace:^",
"llamaindex": "latest",
"mongodb": "^6.2.0"
},
"devDependencies": {
-1
View File
@@ -19,7 +19,6 @@ async function main() {
const pipeline = new IngestionPipeline({
transformations: [
new SimpleNodeParser({ chunkSize: 1024, chunkOverlap: 20 }),
// new TitleExtractor(llm),
new OpenAIEmbedding(),
],
});
+7
View File
@@ -1,5 +1,12 @@
# llamaindex
## 0.1.3
### Patch Changes
- 9ce7d3d: update dependencies
- 7d50196: fix: output target causes not implemented error
## 0.1.2
- e4b807a: fix: invalid package.json
+1 -1
View File
@@ -2,5 +2,5 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testPathIgnorePatterns: ["/lib/"],
testPathIgnorePatterns: ["/lib/", "/node_modules/", "/dist/"],
};
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "llamaindex",
"private": true,
"version": "0.1.2",
"version": "0.1.3",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.12.4",
@@ -54,13 +54,13 @@
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"edge-light": "./dist/index.mjs",
"edge-light": "./dist/index.edge-light.mjs",
"require": "./dist/index.js"
},
"./env": {
"types": "./dist/env.d.mts",
"import": "./dist/env.mjs",
"edge-light": "./dist/env.mjs",
"edge-light": "./dist/env.edge-light.mjs",
"require": "./dist/env.js"
}
},
@@ -81,6 +81,6 @@
"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": "bunchee -w",
"circular-check": "madge --circular ./src/*.ts"
"circular-check": "madge -c ./src/index.ts"
}
}
+1 -17
View File
@@ -1,20 +1,4 @@
import { SubQuestion } from "./QuestionGenerator";
/**
* An OutputParser is used to extract structured data from the raw output of the LLM.
*/
export interface BaseOutputParser<T> {
parse(output: string): T;
format(output: string): string;
}
/**
* StructuredOutput is just a combo of the raw output and the parsed output.
*/
export interface StructuredOutput<T> {
rawOutput: string;
parsedOutput: T;
}
import { BaseOutputParser, StructuredOutput, SubQuestion } from "./types";
/**
* Error class for output parsing. Due to the nature of LLMs, anytime we use LLM
+1 -2
View File
@@ -1,6 +1,5 @@
import { ChatMessage } from "./llm/types";
import { SubQuestion } from "./QuestionGenerator";
import { ToolMetadata } from "./Tool";
import { SubQuestion, ToolMetadata } from "./types";
/**
* A SimplePrompt is a function that takes a dictionary of inputs and returns a string.
+10 -34
View File
@@ -1,13 +1,8 @@
import { NodeWithScore, TextNode } from "./Node";
import {
BaseQuestionGenerator,
LLMQuestionGenerator,
SubQuestion,
} from "./QuestionGenerator";
import { LLMQuestionGenerator } from "./QuestionGenerator";
import { Response } from "./Response";
import { BaseRetriever } from "./Retriever";
import { ServiceContext, serviceContextFromDefaults } from "./ServiceContext";
import { QueryEngineTool, ToolMetadata } from "./Tool";
import { Event } from "./callbacks/CallbackManager";
import { randomUUID } from "./env";
import { BaseNodePostprocessor } from "./postprocessors";
@@ -16,34 +11,15 @@ import {
CompactAndRefine,
ResponseSynthesizer,
} from "./synthesizers";
/**
* Parameters for sending a query.
*/
export interface QueryEngineParamsBase {
query: string;
parentEvent?: Event;
}
export interface QueryEngineParamsStreaming extends QueryEngineParamsBase {
stream: true;
}
export interface QueryEngineParamsNonStreaming extends QueryEngineParamsBase {
stream?: false | null;
}
/**
* A query engine is a question answerer that can use one or more steps.
*/
export interface BaseQueryEngine {
/**
* Query the query engine and get a response.
* @param params
*/
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
}
import {
BaseQueryEngine,
BaseQuestionGenerator,
QueryEngineParamsNonStreaming,
QueryEngineParamsStreaming,
QueryEngineTool,
SubQuestion,
ToolMetadata,
} from "./types";
/**
* A query engine that uses a retriever to query an index and then synthesizes the response.
+8 -18
View File
@@ -1,28 +1,18 @@
import {
BaseOutputParser,
StructuredOutput,
SubQuestionOutputParser,
} from "./OutputParser";
import { SubQuestionOutputParser } from "./OutputParser";
import {
SubQuestionPrompt,
buildToolsText,
defaultSubQuestionPrompt,
} from "./Prompt";
import { ToolMetadata } from "./Tool";
import { OpenAI } from "./llm/LLM";
import { LLM } from "./llm/types";
export interface SubQuestion {
subQuestion: string;
toolName: string;
}
/**
* QuestionGenerators generate new questions for the LLM using tools and a user query.
*/
export interface BaseQuestionGenerator {
generate(tools: ToolMetadata[], query: string): Promise<SubQuestion[]>;
}
import {
BaseOutputParser,
BaseQuestionGenerator,
StructuredOutput,
SubQuestion,
ToolMetadata,
} from "./types";
/**
* LLMQuestionGenerator uses the LLM to generate new questions for the LLM using tools and a user query.
-20
View File
@@ -1,20 +0,0 @@
import { BaseQueryEngine } from "./QueryEngine";
export interface ToolMetadata {
description: string;
name: string;
}
/**
* Simple Tool interface. Likely to change.
*/
export interface BaseTool {
metadata: ToolMetadata;
}
/**
* A Tool that uses a QueryEngine.
*/
export interface QueryEngineTool extends BaseTool {
queryEngine: BaseQueryEngine;
}
@@ -6,7 +6,7 @@ import {
getAzureModel,
shouldUseAzure,
} from "../llm/azure";
import { OpenAISession, getOpenAISession } from "../llm/openai";
import { OpenAISession, getOpenAISession } from "../llm/open_ai";
import { BaseEmbedding } from "./types";
export const ALL_OPENAI_EMBEDDING_MODELS = {
+1 -11
View File
@@ -1,16 +1,6 @@
import { BaseNode, MetadataMode } from "../Node";
import { TransformComponent } from "../ingestion";
import { similarity } from "./utils";
/**
* Similarity type
* Default is cosine similarity. Dot product and negative Euclidean distance are also supported.
*/
export enum SimilarityType {
DEFAULT = "cosine",
DOT_PRODUCT = "dot_product",
EUCLIDEAN = "euclidean",
}
import { SimilarityType, similarity } from "./utils";
export abstract class BaseEmbedding implements TransformComponent {
similarity(
+11 -2
View File
@@ -2,8 +2,17 @@ import _ from "lodash";
import { ImageType } from "../Node";
import { DEFAULT_SIMILARITY_TOP_K } from "../constants";
import { defaultFS } from "../env";
import { VectorStoreQueryMode } from "../storage";
import { SimilarityType } from "./types";
import { VectorStoreQueryMode } from "../storage/vectorStore/types";
/**
* Similarity type
* Default is cosine similarity. Dot product and negative Euclidean distance are also supported.
*/
export enum SimilarityType {
DEFAULT = "cosine",
DOT_PRODUCT = "dot_product",
EUCLIDEAN = "euclidean",
}
/**
* The similarity between two embeddings.
@@ -4,7 +4,6 @@ import {
defaultCondenseQuestionPrompt,
messagesToHistoryStr,
} from "../../Prompt";
import { BaseQueryEngine } from "../../QueryEngine";
import { Response } from "../../Response";
import {
ServiceContext,
@@ -12,6 +11,7 @@ import {
} from "../../ServiceContext";
import { ChatMessage, LLM } from "../../llm";
import { extractText, streamReducer } from "../../llm/utils";
import { BaseQueryEngine } from "../../types";
import {
ChatEngine,
ChatEngineParamsNonStreaming,
@@ -1,5 +1,5 @@
import { BaseNode, MetadataMode, TextNode } from "../Node";
import { LLM } from "../llm";
import { LLM, OpenAI } from "../llm";
import {
defaultKeywordExtractorPromptTemplate,
defaultQuestionAnswerPromptTemplate,
@@ -11,6 +11,11 @@ import { BaseExtractor } from "./types";
const STRIP_REGEX = /(\r\n|\n|\r)/gm;
type KeywordExtractArgs = {
llm?: LLM;
keywords?: number;
};
type ExtractKeyword = {
excerptKeywords: string;
};
@@ -38,12 +43,14 @@ export class KeywordExtractor extends BaseExtractor {
* @param {number} keywords Number of keywords to extract.
* @throws {Error} If keywords is less than 1.
*/
constructor(llm: LLM, keywords: number = 5) {
if (keywords < 1) throw new Error("Keywords must be greater than 0");
constructor(options?: KeywordExtractArgs) {
if (options?.keywords && options.keywords < 1)
throw new Error("Keywords must be greater than 0");
super();
this.llm = llm;
this.keywords = keywords;
this.llm = options?.llm ?? new OpenAI();
this.keywords = options?.keywords ?? 5;
}
/**
@@ -81,6 +88,13 @@ export class KeywordExtractor extends BaseExtractor {
}
}
type TitleExtractorsArgs = {
llm?: LLM;
nodes?: number;
nodeTemplate?: string;
combineTemplate?: string;
};
type ExtractTitle = {
documentTitle: string;
};
@@ -128,20 +142,16 @@ export class TitleExtractor extends BaseExtractor {
* @param {string} node_template The prompt template to use for the title extractor.
* @param {string} combine_template The prompt template to merge title with..
*/
constructor(
llm: LLM,
nodes: number = 5,
node_template?: string,
combine_template?: string,
) {
constructor(options?: TitleExtractorsArgs) {
super();
this.llm = llm;
this.nodes = nodes;
this.llm = options?.llm ?? new OpenAI();
this.nodes = options?.nodes ?? 5;
this.nodeTemplate = node_template ?? defaultTitleExtractorPromptTemplate();
this.nodeTemplate =
options?.nodeTemplate ?? defaultTitleExtractorPromptTemplate();
this.combineTemplate =
combine_template ?? defaultTitleCombinePromptTemplate();
options?.combineTemplate ?? defaultTitleCombinePromptTemplate();
}
/**
@@ -197,6 +207,13 @@ export class TitleExtractor extends BaseExtractor {
}
}
type QuestionAnswerExtractArgs = {
llm?: LLM;
questions?: number;
promptTemplate?: string;
embeddingOnly?: boolean;
};
type ExtractQuestion = {
questionsThisExcerptCanAnswer: string;
};
@@ -238,25 +255,21 @@ export class QuestionsAnsweredExtractor extends BaseExtractor {
* @param {string} promptTemplate The prompt template to use for the question extractor.
* @param {boolean} embeddingOnly Wheter to use metadata for embeddings only.
*/
constructor(
llm: LLM,
questions: number = 5,
promptTemplate?: string,
embeddingOnly: boolean = false,
) {
if (questions < 1) throw new Error("Questions must be greater than 0");
constructor(options?: QuestionAnswerExtractArgs) {
if (options?.questions && options.questions < 1)
throw new Error("Questions must be greater than 0");
super();
this.llm = llm;
this.questions = questions;
this.llm = options?.llm ?? new OpenAI();
this.questions = options?.questions ?? 5;
this.promptTemplate =
promptTemplate ??
options?.promptTemplate ??
defaultQuestionAnswerPromptTemplate({
numQuestions: questions,
numQuestions: this.questions,
contextStr: "",
});
this.embeddingOnly = embeddingOnly;
this.embeddingOnly = options?.embeddingOnly ?? false;
}
/**
@@ -303,6 +316,12 @@ export class QuestionsAnsweredExtractor extends BaseExtractor {
}
}
type SummaryExtractArgs = {
llm?: LLM;
summaries?: string[];
promptTemplate?: string;
};
type ExtractSummary = {
sectionSummary: string;
prevSectionSummary: string;
@@ -335,24 +354,25 @@ export class SummaryExtractor extends BaseExtractor {
private _prevSummary: boolean;
private _nextSummary: boolean;
constructor(
llm: LLM,
summaries: string[] = ["self"],
promptTemplate?: string,
) {
if (!summaries.some((s) => ["self", "prev", "next"].includes(s)))
constructor(options?: SummaryExtractArgs) {
const summaries = options?.summaries ?? ["self"];
if (
summaries &&
!summaries.some((s) => ["self", "prev", "next"].includes(s))
)
throw new Error("Summaries must be one of 'self', 'prev', 'next'");
super();
this.llm = llm;
this.llm = options?.llm ?? new OpenAI();
this.summaries = summaries;
this.promptTemplate =
promptTemplate ?? defaultSummaryExtractorPromptTemplate();
options?.promptTemplate ?? defaultSummaryExtractorPromptTemplate();
this._selfSummary = summaries.includes("self");
this._prevSummary = summaries.includes("prev");
this._nextSummary = summaries.includes("next");
this._selfSummary = summaries?.includes("self") ?? false;
this._prevSummary = summaries?.includes("prev") ?? false;
this._nextSummary = summaries?.includes("next") ?? false;
}
/**
+2 -2
View File
@@ -10,7 +10,6 @@ export * from "./Response";
export * from "./Retriever";
export * from "./ServiceContext";
export * from "./TextSplitter";
export * from "./Tool";
export * from "./callbacks/CallbackManager";
export * from "./constants";
export * from "./embeddings";
@@ -21,7 +20,7 @@ export * from "./ingestion";
export * from "./llm";
export * from "./nodeParsers";
export * from "./postprocessors";
export * from "./readers/AssemblyAI";
export * from "./readers/AssemblyAIReader";
export * from "./readers/CSVReader";
export * from "./readers/DocxReader";
export * from "./readers/HTMLReader";
@@ -33,3 +32,4 @@ export * from "./readers/SimpleMongoReader";
export * from "./readers/base";
export * from "./storage";
export * from "./synthesizers";
export type * from "./types";
+6 -4
View File
@@ -1,13 +1,14 @@
import { BaseNode, Document, jsonToNode } from "../Node";
import { BaseQueryEngine } from "../QueryEngine";
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.
@@ -188,9 +189,10 @@ export abstract class BaseIndex<T> {
* @param document
*/
async insert(document: Document) {
const nodes = this.serviceContext.nodeParser.getNodesFromDocuments([
document,
]);
const nodes = await runTransformations(
[document],
[this.serviceContext.nodeParser],
);
await this.insertNodes(nodes);
this.docStore.setDocumentHash(document.id_, document.hash);
}
@@ -1,6 +1,6 @@
import { BaseNode, Document, MetadataMode } from "../../Node";
import { defaultKeywordExtractPrompt } from "../../Prompt";
import { BaseQueryEngine, RetrieverQueryEngine } from "../../QueryEngine";
import { RetrieverQueryEngine } from "../../QueryEngine";
import { BaseRetriever } from "../../Retriever";
import {
ServiceContext,
@@ -13,6 +13,7 @@ import {
storageContextFromDefaults,
} from "../../storage";
import { BaseSynthesizer } from "../../synthesizers";
import { BaseQueryEngine } from "../../types";
import {
BaseIndex,
BaseIndexInit,
@@ -1,6 +1,6 @@
import _ from "lodash";
import { BaseNode, Document } from "../../Node";
import { BaseQueryEngine, RetrieverQueryEngine } from "../../QueryEngine";
import { RetrieverQueryEngine } from "../../QueryEngine";
import { BaseRetriever } from "../../Retriever";
import {
ServiceContext,
@@ -18,6 +18,7 @@ import {
CompactAndRefine,
ResponseSynthesizer,
} from "../../synthesizers";
import { BaseQueryEngine } from "../../types";
import {
BaseIndex,
BaseIndexInit,
@@ -6,7 +6,7 @@ import {
ObjectType,
splitNodesByType,
} from "../../Node";
import { BaseQueryEngine, RetrieverQueryEngine } from "../../QueryEngine";
import { RetrieverQueryEngine } from "../../QueryEngine";
import { BaseRetriever } from "../../Retriever";
import {
ServiceContext,
@@ -17,6 +17,7 @@ import {
ClipEmbedding,
MultiModalEmbedding,
} from "../../embeddings";
import { runTransformations } from "../../ingestion";
import { BaseNodePostprocessor } from "../../postprocessors";
import {
BaseIndexStore,
@@ -26,6 +27,7 @@ import {
storageContextFromDefaults,
} from "../../storage";
import { BaseSynthesizer } from "../../synthesizers";
import { BaseQueryEngine } from "../../types";
import {
BaseIndex,
BaseIndexInit,
@@ -224,8 +226,9 @@ export class VectorStoreIndex extends BaseIndex<IndexDict> {
if (args.logProgress) {
console.log("Using node parser on documents...");
}
args.nodes =
args.serviceContext.nodeParser.getNodesFromDocuments(documents);
args.nodes = await runTransformations(documents, [
args.serviceContext.nodeParser,
]);
if (args.logProgress) {
console.log("Finished parsing documents.");
}
@@ -0,0 +1,47 @@
import { BaseNode, MetadataMode } from "../Node";
import { createSHA256 } from "../env";
import { BaseKVStore, SimpleKVStore } from "../storage";
import { docToJson, jsonToDoc } from "../storage/docStore/utils";
import { TransformComponent } from "./types";
export function getTransformationHash(
nodes: BaseNode[],
transform: TransformComponent,
) {
const nodesStr: string = nodes
.map((node) => node.getContent(MetadataMode.ALL))
.join("");
const transformString: string = JSON.stringify(transform);
const hash = createSHA256();
hash.update(nodesStr + transformString);
return hash.digest();
}
export class IngestionCache {
collection: string = "llama_cache";
cache: BaseKVStore;
nodesKey = "nodes";
constructor(collection?: string) {
if (collection) {
this.collection = collection;
}
this.cache = new SimpleKVStore();
}
async put(hash: string, nodes: BaseNode[]) {
const val = {
[this.nodesKey]: nodes.map((node) => docToJson(node)),
};
await this.cache.put(hash, val, this.collection);
}
async get(hash: string): Promise<BaseNode[] | undefined> {
const json = await this.cache.get(hash, this.collection);
if (!json || !json[this.nodesKey] || !Array.isArray(json[this.nodesKey])) {
return undefined;
}
return json[this.nodesKey].map((doc: any) => jsonToDoc(doc));
}
}
@@ -1,32 +1,47 @@
import { BaseNode, Document } from "../Node";
import { BaseReader } from "../readers/base";
import { BaseDocumentStore, VectorStore } from "../storage";
import { IngestionCache, getTransformationHash } from "./IngestionCache";
import { DocStoreStrategy, createDocStoreStrategy } from "./strategies";
import { TransformComponent } from "./types";
interface IngestionRunArgs {
type IngestionRunArgs = {
documents?: Document[];
nodes?: BaseNode[];
};
type TransformRunArgs = {
inPlace?: boolean;
}
cache?: IngestionCache;
};
export async function runTransformations(
nodesToRun: BaseNode[],
transformations: TransformComponent[],
transformOptions: any = {},
{ inPlace = true }: IngestionRunArgs,
{ inPlace = true, cache }: TransformRunArgs = {},
): Promise<BaseNode[]> {
let nodes = nodesToRun;
if (!inPlace) {
nodes = [...nodesToRun];
}
for (const transform of transformations) {
nodes = await transform.transform(nodes, transformOptions);
if (cache) {
const hash = getTransformationHash(nodes, transform);
const cachedNodes = await cache.get(hash);
if (cachedNodes) {
nodes = cachedNodes;
} else {
nodes = await transform.transform(nodes, transformOptions);
await cache.put(hash, nodes);
}
} else {
nodes = await transform.transform(nodes, transformOptions);
}
}
return nodes;
}
// TODO: add caching, add concurrency
export class IngestionPipeline {
transformations: TransformComponent[] = [];
documents?: Document[];
@@ -34,7 +49,8 @@ export class IngestionPipeline {
vectorStore?: VectorStore;
docStore?: BaseDocumentStore;
docStoreStrategy: DocStoreStrategy = DocStoreStrategy.UPSERTS;
disableCache: boolean = true;
cache?: IngestionCache;
disableCache: boolean = false;
private _docStoreStrategy?: TransformComponent;
@@ -45,6 +61,9 @@ export class IngestionPipeline {
this.docStore,
this.vectorStore,
);
if (!this.disableCache) {
this.cache = new IngestionCache();
}
}
async prepareInput(
@@ -68,9 +87,10 @@ export class IngestionPipeline {
}
async run(
args: IngestionRunArgs = {},
args: IngestionRunArgs & TransformRunArgs = {},
transformOptions?: any,
): Promise<BaseNode[]> {
args.cache = args.cache ?? this.cache;
const inputNodes = await this.prepareInput(args.documents, args.nodes);
let nodesToRun;
if (this._docStoreStrategy) {
+2 -2
View File
@@ -25,9 +25,9 @@ import {
shouldUseAzure,
} from "./azure";
import { BaseLLM } from "./base";
import { OpenAISession, getOpenAISession } from "./openai";
import { OpenAISession, getOpenAISession } from "./open_ai";
import { PortkeySession, getPortkeySession } from "./portkey";
import { ReplicateSession } from "./replicate";
import { ReplicateSession } from "./replicate_ai";
import {
ChatMessage,
ChatResponse,
+6 -1
View File
@@ -1,5 +1,10 @@
export * from "./LLM";
export * from "./mistral";
export {
ALL_AVAILABLE_MISTRAL_MODELS,
MistralAI,
MistralAISession,
} from "./mistral";
export { Ollama } from "./ollama";
export * from "./open_ai";
export { TogetherLLM } from "./together";
export * from "./types";
@@ -28,5 +28,3 @@ export function getReplicateSession(replicateKey: string | null = null) {
return defaultReplicateSession;
}
export * from "openai";
@@ -4,7 +4,7 @@ import {
getTopKEmbeddings,
getTopKEmbeddingsLearner,
getTopKMMREmbeddings,
} from "../../embeddings";
} from "../../embeddings/utils";
import { defaultFS, path } from "../../env";
import { GenericFileSystem, exists } from "../FileSystem";
import { DEFAULT_PERSIST_DIR } from "../constants";
@@ -13,7 +13,7 @@ import { ResponseSynthesizer, SimpleResponseBuilder } from "../synthesizers";
import { mockEmbeddingModel, mockLlmGeneration } from "./utility/mockOpenAI";
// Mock the OpenAI getOpenAISession function during testing
jest.mock("../llm/openai", () => {
jest.mock("../llm/open_ai", () => {
return {
getOpenAISession: jest.fn().mockImplementation(() => null),
};
@@ -21,7 +21,7 @@ import {
} from "./utility/mockOpenAI";
// Mock the OpenAI getOpenAISession function during testing
jest.mock("../llm/openai", () => {
jest.mock("../llm/open_ai", () => {
return {
getOpenAISession: jest.fn().mockImplementation(() => null),
};
@@ -75,7 +75,10 @@ describe("[MetadataExtractor]: Extractors should populate the metadata", () => {
new Document({ text: DEFAULT_LLM_TEXT_OUTPUT }),
]);
const keywordExtractor = new KeywordExtractor(serviceContext.llm, 5);
const keywordExtractor = new KeywordExtractor({
llm: serviceContext.llm,
keywords: 5,
});
const nodesWithKeywordMetadata = await keywordExtractor.processNodes(nodes);
@@ -91,7 +94,10 @@ describe("[MetadataExtractor]: Extractors should populate the metadata", () => {
new Document({ text: DEFAULT_LLM_TEXT_OUTPUT }),
]);
const titleExtractor = new TitleExtractor(serviceContext.llm, 5);
const titleExtractor = new TitleExtractor({
llm: serviceContext.llm,
nodes: 5,
});
const nodesWithKeywordMetadata = await titleExtractor.processNodes(nodes);
@@ -107,10 +113,10 @@ describe("[MetadataExtractor]: Extractors should populate the metadata", () => {
new Document({ text: DEFAULT_LLM_TEXT_OUTPUT }),
]);
const questionsAnsweredExtractor = new QuestionsAnsweredExtractor(
serviceContext.llm,
5,
);
const questionsAnsweredExtractor = new QuestionsAnsweredExtractor({
llm: serviceContext.llm,
questions: 5,
});
const nodesWithKeywordMetadata =
await questionsAnsweredExtractor.processNodes(nodes);
@@ -127,7 +133,9 @@ describe("[MetadataExtractor]: Extractors should populate the metadata", () => {
new Document({ text: DEFAULT_LLM_TEXT_OUTPUT }),
]);
const summaryExtractor = new SummaryExtractor(serviceContext.llm);
const summaryExtractor = new SummaryExtractor({
llm: serviceContext.llm,
});
const nodesWithKeywordMetadata = await summaryExtractor.processNodes(nodes);
@@ -0,0 +1,74 @@
import { BaseNode, TextNode } from "../../Node";
import { TransformComponent } from "../../ingestion";
import {
IngestionCache,
getTransformationHash,
} from "../../ingestion/IngestionCache";
import { SimpleNodeParser } from "../../nodeParsers";
describe("IngestionCache", () => {
let cache: IngestionCache;
const hash = "1";
beforeAll(() => {
cache = new IngestionCache();
});
test("should put and get", async () => {
const nodes = [new TextNode({ text: "some text", id_: "some id" })];
await cache.put(hash, nodes);
const result = await cache.get(hash);
expect(result).toEqual(nodes);
});
test("should return undefined if not found", async () => {
const result = await cache.get("not found");
expect(result).toBeUndefined();
});
});
describe("getTransformationHash", () => {
let nodes: BaseNode[], transform: TransformComponent;
beforeAll(() => {
nodes = [new TextNode({ text: "some text", id_: "some id" })];
transform = new SimpleNodeParser({
chunkOverlap: 10,
chunkSize: 1024,
});
});
test("should return a hash", () => {
const result = getTransformationHash(nodes, transform);
expect(typeof result).toBe("string");
});
test("should return the same hash for the same inputs", () => {
const result1 = getTransformationHash(nodes, transform);
const result2 = getTransformationHash(nodes, transform);
expect(result1).toBe(result2);
});
test("should return the same hash for other instances with same inputs", () => {
const result1 = getTransformationHash(
[new TextNode({ text: "some text", id_: "some id" })],
transform,
);
const result2 = getTransformationHash(nodes, transform);
expect(result1).toBe(result2);
});
test("should return different hashes for different nodes", () => {
const result1 = getTransformationHash(nodes, transform);
const result2 = getTransformationHash(
[new TextNode({ text: "some other text", id_: "some id" })],
transform,
);
expect(result1).not.toBe(result2);
});
test("should return different hashes for different transforms", () => {
const result1 = getTransformationHash(nodes, transform);
const result2 = getTransformationHash(
nodes,
new SimpleNodeParser({
chunkOverlap: 10,
chunkSize: 512,
}),
);
expect(result1).not.toBe(result2);
});
});
+82
View File
@@ -0,0 +1,82 @@
/**
* Top level types to avoid circular dependencies
*/
import { Event } from "./callbacks/CallbackManager";
import { Response } from "./Response";
/**
* Parameters for sending a query.
*/
export interface QueryEngineParamsBase {
query: string;
parentEvent?: Event;
}
export interface QueryEngineParamsStreaming extends QueryEngineParamsBase {
stream: true;
}
export interface QueryEngineParamsNonStreaming extends QueryEngineParamsBase {
stream?: false | null;
}
/**
* A query engine is a question answerer that can use one or more steps.
*/
export interface BaseQueryEngine {
/**
* Query the query engine and get a response.
* @param params
*/
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
}
/**
* Simple Tool interface. Likely to change.
*/
export interface BaseTool {
metadata: ToolMetadata;
}
/**
* A Tool that uses a QueryEngine.
*/
export interface QueryEngineTool extends BaseTool {
queryEngine: BaseQueryEngine;
}
export interface SubQuestion {
subQuestion: string;
toolName: string;
}
/**
* An OutputParser is used to extract structured data from the raw output of the LLM.
*/
export interface BaseOutputParser<T> {
parse(output: string): T;
format(output: string): string;
}
/**
* StructuredOutput is just a combo of the raw output and the parsed output.
*/
export interface StructuredOutput<T> {
rawOutput: string;
parsedOutput: T;
}
export interface ToolMetadata {
description: string;
name: string;
}
/**
* QuestionGenerators generate new questions for the LLM using tools and a user query.
*/
export interface BaseQuestionGenerator {
generate(tools: ToolMetadata[], query: string): Promise<SubQuestion[]>;
}
+6
View File
@@ -1,5 +1,11 @@
# create-llama
## 0.0.20
### Patch Changes
- 27d55fd: Add an option to provide an URL and chat with the website data
## 0.0.19
### Patch Changes
-4
View File
@@ -28,10 +28,6 @@ for (const templateType of templateTypes) {
// nextjs doesn't support simple templates - skip tests
continue;
}
if (templateEngine === "context") {
// we don't test context templates because it needs OPEN_AI_KEY
continue;
}
const appType: AppType =
templateFramework === "express" || templateFramework === "fastapi"
? templateType === "simple"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-llama",
"version": "0.0.19",
"version": "0.0.21",
"keywords": [
"rag",
"llamaindex",
+1 -1
View File
@@ -134,7 +134,7 @@ importers:
specifier: ^16.4.1
version: 16.4.1
llamaindex:
specifier: workspace:^
specifier: latest
version: link:../packages/core
mongodb:
specifier: ^6.2.0