Compare commits

..

69 Commits

Author SHA1 Message Date
Marcus Schiesser f6dabd0d3e RELEASING: Releasing 1 package(s)
Releases:
  create-llama@0.0.22

[skip ci]
2024-02-01 15:51:44 +07:00
Marcus Schiesser 74caaa2bf7 fix: clarify CLI output for create-llama 2024-02-01 15:26:48 +07:00
Marcus Schiesser 552403b370 fix: don't create d.ts files for create-llama 2024-02-01 14:33:29 +07:00
Huu Le (Lee) a93d09d159 fix(create-llama): generate code option fail (#491) 2024-02-01 10:57:10 +07:00
Thuc Pham 0fb757f6c1 feat: use pnpm pack for e2e (#490) 2024-02-01 10:12:21 +07:00
Alex Yang a68053ca4e fix: remove as any type (#494) 2024-02-01 10:10:41 +07:00
Alex Yang 36d4f4027b RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.7

[skip ci]
2024-01-31 17:20:26 -06:00
Alex Yang 19d92507e3 chore: use the patch version 2024-01-31 17:20:17 -06:00
Emanuel Ferreira 664e92a3d6 chore: remove collapsed router query (#497) 2024-01-31 16:31:49 -06:00
Emanuel Ferreira d687c110e4 docs(changeset): feat(router): add router query engine (#496) 2024-01-31 14:24:51 -08:00
Emanuel Ferreira af5ae7054e feat(router): setup router query engine (#484) 2024-01-31 13:58:47 -08:00
Alex Yang dff1e7f552 RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.6

[skip ci]
2024-01-31 12:50:40 -06:00
Alex Yang cf446401e5 fix: instanceof issue (#492) 2024-01-31 12:45:02 -06:00
Huu Le (Lee) e9b87ef09b feat(create-llama) add folder selection & support more context data types (#489) 2024-01-31 16:42:30 +07:00
yisding 7231ddb1b3 allow simpledirectoryreader to get a string (#488)
Co-authored-by: Alex Yang <himself65@outlook.com>
2024-01-31 11:11:57 +07:00
Alex Yang 1ead36f1bb RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.5

[skip ci]
2024-01-30 14:21:37 -06:00
Alex Yang 8a9b78a4ab docs(changeset): chore: split readers into different files 2024-01-30 14:21:17 -06:00
Motoki saito 569299724a add --vector-db option to create-llama (#473) 2024-01-30 15:24:35 +07:00
Huu Le (Lee) 6dd401e1c7 Feat: add + fix chat with web (#481) 2024-01-30 14:17:33 +07:00
Marcus Schiesser c4cb37786b fix: use Array not Float32Array (#482) 2024-01-30 11:28:14 +07:00
Alex Yang b757d9a94e chore: split readers into different files (#479) 2024-01-29 22:05:23 -06:00
Motoki saito 6cc083f370 define mergePoetryDependencies type (#465) 2024-01-30 10:47:41 +07:00
yisding c419027db9 add docs for azure openai (#480) 2024-01-30 10:41:05 +07:00
metonym 6a16b47406 Fix typo in node_parser.md (#475) 2024-01-30 10:34:53 +07:00
Alex Yang 4197ae8b9f RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.4

[skip ci]
2024-01-29 18:30:54 -06:00
Alex Yang 88696e1407 refactor: use pdf2json instead of pdfjs-dist (#477) 2024-01-29 18:28:20 -06:00
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
Emanuel Ferreira 9ba4547c4d fix: not overwrite metadata (#453) 2024-01-26 09:07:52 -03:00
Tyrone Avnit 4fea0adf43 Expose BaseExtractor Class (#454) 2024-01-26 09:07:36 -03:00
Marcus Schiesser de070dbfa7 RELEASING: Releasing 1 package(s)
Releases:
  create-llama@0.0.19

[skip ci]
2024-01-26 16:33:12 +07:00
Marcus Schiesser 87eb72bdb2 fix: don't install root package for llamapack examples (as there isn't one) 2024-01-26 16:30:50 +07:00
Thuc Pham fe03aaae55 feat: generate llama pack example (#429) 2024-01-26 15:02:49 +07:00
yisding 9ce7d3d648 Update packages (#448) 2024-01-26 11:54:58 +07:00
Alex Yang 0471407761 RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.2

[skip ci]
2024-01-25 22:26:59 -06:00
Alex Yang e4b807a018 fix(core): invalid package.json 2024-01-25 22:26:31 -06:00
Alex Yang 0a0ec37725 RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.1.1

[skip ci]
2024-01-25 22:18:14 -06:00
Alex Yang 8abca5d818 build(core): release with node resolution compatibility (#451) 2024-01-25 22:12:15 -06:00
jess-render 3a29a8036b Add node_modules to gitignore in Express backends (#447)
Co-authored-by: Jess Lin <jesslin@Jesss-MBP.render.com>
2024-01-26 09:34:53 +07:00
yisding e2b9b66f71 RELEASING: Releasing 2 package(s)
Releases:
  llamaindex@0.1.0
  docs@0.0.1

[skip ci]
2024-01-25 15:47:02 -08:00
yisding bb66cb7e36 Openai embeddings 3 (#445) 2024-01-25 15:45:21 -08:00
Alex Yang 2159e77c9d RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.0.51

[skip ci]
2024-01-25 14:59:22 -06:00
Emanuel Ferreira 3154f521d9 chore: add qdrant readme (#444) 2024-01-25 17:58:37 -03:00
Alex Yang fda8024607 revert: export conditions not working with moduleResolution node (#443) 2024-01-25 13:51:05 -06:00
Marcus Schiesser 89336e4ddf feat: add deno jupyter examples (#428) 2024-01-25 18:09:19 +07:00
Marcus Schiesser a94f747307 RELEASING: Releasing 1 package(s)
Releases:
  create-llama@0.0.18

[skip ci]
2024-01-25 17:46:19 +07:00
Marcus Schiesser 88d3b41044 fix: create-llama packaging 2024-01-25 17:44:29 +07:00
Marcus Schiesser 7fd02ab8d1 RELEASING: Releasing 1 package(s)
Releases:
  create-llama@0.0.17

[skip ci]
2024-01-25 16:58:45 +07:00
Huu Le (Lee) 9e5d8e143e Feat: add local pdf file option (#441) 2024-01-25 15:20:42 +07:00
Huu Le (Lee) f0f7df29b3 remove chromadb override (as llamaindex is forcing now chromadb 1.7.3) 2024-01-25 14:24:01 +07:00
yisding 05ba70881c RELEASING: Releasing 1 package(s)
Releases:
  llamaindex@0.0.50

[skip ci]
2024-01-24 23:00:50 -08:00
yisding 8a729cdd0d minor bug fixes with together AI (#440) 2024-01-24 22:59:09 -08:00
147 changed files with 5193 additions and 3081 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"create-llama": patch
---
Add an option that allows the user to run the generated app
-5
View File
@@ -1,5 +0,0 @@
---
"llamaindex": patch
---
fix bugs in Together.AI integration (thanks @Nutlope for reporting)
+6
View File
@@ -49,6 +49,12 @@ jobs:
- name: Build create-llama
run: pnpm run build
working-directory: ./packages/create-llama
- name: Pack
run: pnpm pack --pack-destination ./output
working-directory: ./packages/create-llama
- name: Extract Pack
run: tar -xvzf ./output/*.tgz -C ./output
working-directory: ./packages/create-llama
- name: Run Playwright tests
run: pnpm exec playwright test
env:
+30 -2
View File
@@ -32,7 +32,35 @@ jobs:
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build
working-directory: ./packages/core
run: pnpm run build --filter llamaindex
- name: Run Type Check
run: pnpm run type-check
- name: Run Circular Dependency Check
run: pnpm run circular-check
working-directory: ./packages/core
typecheck-examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build --filter llamaindex
- name: Copy examples
run: rsync -rv --exclude=node_modules ./examples ${{ runner.temp }}
- name: Pack
run: pnpm pack --pack-destination ${{ runner.temp }}
working-directory: packages/core
- name: Install llamaindex
run: npm add ${{ runner.temp }}/*.tgz
working-directory: ${{ runner.temp }}/examples
- name: Run Type Check
run: npx tsc --project ./tsconfig.json
working-directory: ${{ runner.temp }}/examples
-3
View File
@@ -1,6 +1,3 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm format
pnpm lint
npx lint-staged
-3
View File
@@ -1,4 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm test
+1
View File
@@ -1 +1,2 @@
auto-install-peers = true
enable-pre-post-scripts = true
+3
View File
@@ -8,5 +8,8 @@
"jest.rootPath": "./packages/core",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
+3
View File
@@ -105,6 +105,9 @@ export const runtime = "nodejs"; // default
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverComponentsExternalPackages: ["pdf2json"],
},
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
+7
View File
@@ -0,0 +1,7 @@
# docs
## 0.0.1
### Patch Changes
- 3154f52: chore: add qdrant readme
+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
@@ -1,5 +1,5 @@
---
sidebar_position: 0
sidebar_position: 1
---
# Documents and Nodes
@@ -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);
```
+13 -1
View File
@@ -1,5 +1,5 @@
---
sidebar_position: 0
sidebar_position: 3
---
# LLM
@@ -16,6 +16,18 @@ const openaiLLM = new OpenAI({ model: "gpt-3.5-turbo", temperature: 0 });
const serviceContext = serviceContextFromDefaults({ llm: openaiLLM });
```
## Azure OpenAI
To use Azure OpenAI, you only need to set a few environment variables.
For example:
```
export AZURE_OPENAI_KEY="<YOUR KEY HERE>"
export AZURE_OPENAI_ENDPOINT="<YOUR ENDPOINT, see https://learn.microsoft.com/en-us/azure/ai-services/openai/quickstart?tabs=command-line%2Cpython&pivots=rest-api>"
export AZURE_OPENAI_DEPLOYMENT="gpt-4" # or some other deployment name
```
## API Reference
- [OpenAI](../../api/classes/OpenAI.md)
+1 -1
View File
@@ -4,7 +4,7 @@ sidebar_position: 3
# NodeParser
The `NodeParser` in LlamaIndex is responbile for splitting `Document` objects into more manageable `Node` objects. When you call `.fromDocuments()`, the `NodeParser` from the `ServiceContext` is used to do this automatically for you. Alternatively, you can use it to split documents ahead of time.
The `NodeParser` in LlamaIndex is responsible for splitting `Document` objects into more manageable `Node` objects. When you call `.fromDocuments()`, the `NodeParser` from the `ServiceContext` is used to do this automatically for you. Alternatively, you can use it to split documents ahead of time.
```typescript
import { Document, SimpleNodeParser } from "llamaindex";
@@ -0,0 +1,2 @@
label: "Query Engines"
position: 2
@@ -1,7 +1,3 @@
---
sidebar_position: 3
---
# QueryEngine
A query engine wraps a `Retriever` and a `ResponseSynthesizer` into a pipeline, that will use the query string to fetech nodes and then send them to the LLM to generate a response.
@@ -0,0 +1,189 @@
# Router Query Engine
In this tutorial, we define a custom router query engine that selects one out of several candidate query engines to execute a query.
## Setup
First, we need to install import the necessary modules from `llamaindex`:
```bash
pnpm i lamaindex
```
```ts
import {
OpenAI,
RouterQueryEngine,
SimpleDirectoryReader,
SimpleNodeParser,
SummaryIndex,
VectorStoreIndex,
serviceContextFromDefaults,
} from "llamaindex";
```
## Loading Data
Next, we need to load some data. We will use the `SimpleDirectoryReader` to load documents from a directory:
```ts
const documents = await new SimpleDirectoryReader().loadData({
directoryPath: "node_modules/llamaindex/examples",
});
```
## Service Context
Next, we need to define some basic rules and parse the documents into nodes. We will use the `SimpleNodeParser` to parse the documents into nodes and `ServiceContext` to define the rules (eg. LLM API key, chunk size, etc.):
```ts
const nodeParser = new SimpleNodeParser({
chunkSize: 1024,
});
const serviceContext = serviceContextFromDefaults({
nodeParser,
llm: new OpenAI(),
});
```
## Creating Indices
Next, we need to create some indices. We will create a `VectorStoreIndex` and a `SummaryIndex`:
```ts
const vectorIndex = await VectorStoreIndex.fromDocuments(documents, {
serviceContext,
});
const summaryIndex = await SummaryIndex.fromDocuments(documents, {
serviceContext,
});
```
## Creating Query Engines
Next, we need to create some query engines. We will create a `VectorStoreQueryEngine` and a `SummaryQueryEngine`:
```ts
const vectorQueryEngine = vectorIndex.asQueryEngine();
const summaryQueryEngine = summaryIndex.asQueryEngine();
```
## Creating a Router Query Engine
Next, we need to create a router query engine. We will use the `RouterQueryEngine` to create a router query engine:
We're defining two query engines, one for summarization and one for retrieving specific context. The router query engine will select the most appropriate query engine based on the query.
```ts
const queryEngine = RouterQueryEngine.fromDefaults({
queryEngineTools: [
{
queryEngine: vectorQueryEngine,
description: "Useful for summarization questions related to Abramov",
},
{
queryEngine: summaryQueryEngine,
description: "Useful for retrieving specific context from Abramov",
},
],
serviceContext,
});
```
## Querying the Router Query Engine
Finally, we can query the router query engine:
```ts
const summaryResponse = await queryEngine.query({
query: "Give me a summary about his past experiences?",
});
console.log({
answer: summaryResponse.response,
metadata: summaryResponse?.metadata?.selectorResult,
});
```
## Full code
```ts
import {
OpenAI,
RouterQueryEngine,
SimpleDirectoryReader,
SimpleNodeParser,
SummaryIndex,
VectorStoreIndex,
serviceContextFromDefaults,
} from "llamaindex";
async function main() {
// Load documents from a directory
const documents = await new SimpleDirectoryReader().loadData({
directoryPath: "node_modules/llamaindex/examples",
});
// Parse the documents into nodes
const nodeParser = new SimpleNodeParser({
chunkSize: 1024,
});
// Create a service context
const serviceContext = serviceContextFromDefaults({
nodeParser,
llm: new OpenAI(),
});
// Create indices
const vectorIndex = await VectorStoreIndex.fromDocuments(documents, {
serviceContext,
});
const summaryIndex = await SummaryIndex.fromDocuments(documents, {
serviceContext,
});
// Create query engines
const vectorQueryEngine = vectorIndex.asQueryEngine();
const summaryQueryEngine = summaryIndex.asQueryEngine();
// Create a router query engine
const queryEngine = RouterQueryEngine.fromDefaults({
queryEngineTools: [
{
queryEngine: vectorQueryEngine,
description: "Useful for summarization questions related to Abramov",
},
{
queryEngine: summaryQueryEngine,
description: "Useful for retrieving specific context from Abramov",
},
],
serviceContext,
});
// Query the router query engine
const summaryResponse = await queryEngine.query({
query: "Give me a summary about his past experiences?",
});
console.log({
answer: summaryResponse.response,
metadata: summaryResponse?.metadata?.selectorResult,
});
const specificResponse = await queryEngine.query({
query: "Tell me about abramov first job?",
});
console.log({
answer: specificResponse.response,
metadata: specificResponse.metadata.selectorResult,
});
}
main().then(() => console.log("Done"));
```
@@ -0,0 +1,2 @@
label: "Vector Stores"
position: 1
@@ -0,0 +1,86 @@
# Qdrant Vector Store
To run this example, you need to have a Qdrant instance running. You can run it with Docker:
```bash
docker pull qdrant/qdrant
docker run -p 6333:6333 qdrant/qdrant
```
## Importing the modules
```ts
import fs from "node:fs/promises";
import { Document, VectorStoreIndex, QdrantVectorStore } from "llamaindex";
```
## Load the documents
```ts
const path = "node_modules/llamaindex/examples/abramov.txt";
const essay = await fs.readFile(path, "utf-8");
```
## Setup Qdrant
```ts
const vectorStore = new QdrantVectorStore({
url: "http://localhost:6333",
});
```
## Setup the index
```ts
const document = new Document({ text: essay, id_: path });
const index = await VectorStoreIndex.fromDocuments([document], {
vectorStore,
});
```
## Query the index
```ts
const queryEngine = index.asQueryEngine();
const response = await queryEngine.query({
query: "What did the author do in college?",
});
// Output response
console.log(response.toString());
```
## Full code
```ts
import fs from "node:fs/promises";
import { Document, VectorStoreIndex, QdrantVectorStore } from "llamaindex";
async function main() {
const path = "node_modules/llamaindex/examples/abramov.txt";
const essay = await fs.readFile(path, "utf-8");
const vectorStore = new QdrantVectorStore({
url: "http://localhost:6333",
});
const document = new Document({ text: essay, id_: path });
const index = await VectorStoreIndex.fromDocuments([document], {
vectorStore,
});
const queryEngine = index.asQueryEngine();
const response = await queryEngine.query({
query: "What did the author do in college?",
});
// Output response
console.log(response.toString());
}
main().catch(console.error);
```
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "docs",
"version": "0.0.0",
"version": "0.0.1",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -15,8 +15,8 @@
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "^3.1.0",
"@docusaurus/remark-plugin-npm2yarn": "^3.1.0",
"@docusaurus/core": "^3.1.1",
"@docusaurus/remark-plugin-npm2yarn": "^3.1.1",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.1.0",
"postcss": "^8.4.33",
@@ -27,11 +27,11 @@
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.1.0",
"@docusaurus/preset-classic": "^3.1.0",
"@docusaurus/theme-classic": "^3.1.0",
"@docusaurus/types": "^3.1.0",
"@docusaurus/preset-classic": "^3.1.1",
"@docusaurus/theme-classic": "^3.1.1",
"@docusaurus/types": "^3.1.1",
"@tsconfig/docusaurus": "^2.0.2",
"@types/node": "^18.19.6",
"@types/node": "^18.19.10",
"docusaurus-plugin-typedoc": "^0.22.0",
"typedoc": "^0.25.7",
"typedoc-plugin-markdown": "^3.17.1",
-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
-2
View File
@@ -1,6 +1,4 @@
import { stdin as input, stdout as output } from "node:process";
// readline/promises is still experimental so not in @types/node yet
// @ts-ignore
import readline from "node:readline/promises";
import {
+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
View File
@@ -1,6 +1,4 @@
import { stdin as input, stdout as output } from "node:process";
// readline/promises is still experimental so not in @types/node yet
// @ts-ignore
import readline from "node:readline/promises";
import { ChatMessage, LlamaDeuce, OpenAI } from "llamaindex";
+1
View File
@@ -0,0 +1 @@
.ipynb_checkpoints/
+31
View File
@@ -0,0 +1,31 @@
# Jupyter examples
## Preparation
1. Install Deno, e.g. on macOS:
```
brew install deno
```
2. Install Jupyter
```
pip3 install jupyterlab
```
3. Install Deno kernel
```
deno jupyter --unstable --install
```
4. Run Jupyter
```
jupyter lab
```
## Run examples
Then you can open in Jupyter any of the examples in this directory.
+82
View File
@@ -0,0 +1,82 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"id": "8be89595-8885-4d5e-b1da-1df04eda5c7a",
"metadata": {},
"outputs": [],
"source": [
"import {\n",
" Document,\n",
" SimpleNodeParser\n",
"} from \"npm:llamaindex\";"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "65de03f9-455a-4c59-9089-093cb6998af7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\n",
" TextNode {\n",
" id_: \u001b[32m\"1b2ab25e-562a-4821-bdde-860bd23121c1\"\u001b[39m,\n",
" metadata: {},\n",
" excludedEmbedMetadataKeys: [],\n",
" excludedLlmMetadataKeys: [],\n",
" relationships: {\n",
" SOURCE: {\n",
" nodeId: \u001b[32m\"0cb8de0e-845f-4e73-a7bd-f04426aacfed\"\u001b[39m,\n",
" metadata: {},\n",
" hash: \u001b[32m\"jatVVXETDFjV2fV1/fbTrdpY6ZGnSYekq9m1X/Ff1qs=\"\u001b[39m\n",
" }\n",
" },\n",
" hash: \u001b[32m\"zVyeDsfMwWH1CqK2269o5uzGWl/DpIWO4ZcVCuyENi4=\"\u001b[39m,\n",
" text: \u001b[32m\"I am 10 years old. John is 20 years old.\"\u001b[39m,\n",
" metadataSeparator: \u001b[32m\"\\n\"\u001b[39m\n",
" }\n",
"]\n"
]
}
],
"source": [
"const nodeParser = new SimpleNodeParser();\n",
"const nodes = nodeParser.getNodesFromDocuments([\n",
" new Document({ text: \"I am 10 years old. John is 20 years old.\" }),\n",
"]);\n",
"\n",
"console.log(nodes);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc0ec12f-2062-47af-916d-7c77ca39433a",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Deno",
"language": "typescript",
"name": "deno"
},
"language_info": {
"file_extension": ".ts",
"mimetype": "text/x.typescript",
"name": "typescript",
"nb_converter": "script",
"pygments_lexer": "typescript",
"version": "5.3.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+83
View File
@@ -0,0 +1,83 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 7,
"id": "8be89595-8885-4d5e-b1da-1df04eda5c7a",
"metadata": {},
"outputs": [],
"source": [
"import {\n",
" Document,\n",
" VectorStoreIndex\n",
"} from \"npm:llamaindex\";"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "65de03f9-455a-4c59-9089-093cb6998af7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"In college, the author studied subjects such as linear algebra and physics, but did not find them particularly interesting. They also slacked off and skipped lectures, leading to gaps in their knowledge. They had a negative experience with their English classes and became resentful and suspicious of higher education. They eventually dropped out of college and did not return until five years later to pick up their papers.\n"
]
}
],
"source": [
"// Create Document object with essay\n",
"const resp = await fetch('https://raw.githubusercontent.com/run-llama/LlamaIndexTS/main/packages/core/examples/abramov.txt');\n",
"const text = await resp.text();\n",
"const document = new Document({ text });\n",
"\n",
"// Split text and create embeddings. Store them in a VectorStoreIndex\n",
"const index = await VectorStoreIndex.fromDocuments([document]);\n",
"\n",
"// Query the index\n",
"const queryEngine = index.asQueryEngine();\n",
"const response = await queryEngine.query({\n",
" query: \"What did the author do in college?\",\n",
"});\n",
"\n",
"// Output response\n",
"console.log(response.toString());"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc0ec12f-2062-47af-916d-7c77ca39433a",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "68bdd292-5cf7-46e0-8646-51be1f070ad6",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Deno",
"language": "typescript",
"name": "deno"
},
"language_info": {
"file_extension": ".ts",
"mimetype": "text/x.typescript",
"name": "typescript",
"nb_converter": "script",
"pygments_lexer": "typescript",
"version": "5.3.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+8 -6
View File
@@ -1,19 +1,21 @@
{
"name": "examples",
"private": true,
"version": "0.0.3",
"dependencies": {
"@datastax/astra-db-ts": "^0.1.2",
"@datastax/astra-db-ts": "^0.1.4",
"@notionhq/client": "^2.2.14",
"@pinecone-database/pinecone": "^1.1.2",
"chromadb": "^1.7.3",
"@pinecone-database/pinecone": "^1.1.3",
"chromadb": "^1.8.1",
"commander": "^11.1.0",
"dotenv": "^16.3.1",
"dotenv": "^16.4.1",
"llamaindex": "latest",
"mongodb": "^6.2.0"
},
"devDependencies": {
"@types/node": "^18.18.6",
"ts-node": "^10.9.1"
"@types/node": "^18.19.10",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"scripts": {
"lint": "eslint ."
-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(),
],
});
+2 -7
View File
@@ -1,12 +1,7 @@
import { program } from "commander";
import {
AudioTranscriptReader,
TranscribeParams,
VectorStoreIndex,
} from "llamaindex";
import { TranscribeParams, VectorStoreIndex } from "llamaindex";
import { AudioTranscriptReader } from "llamaindex/readers/AssemblyAIReader";
import { stdin as input, stdout as output } from "node:process";
// readline/promises is still experimental so not in @types/node yet
// @ts-ignore
import readline from "node:readline/promises";
program
+1 -1
View File
@@ -1,11 +1,11 @@
import {
CompactAndRefine,
OpenAI,
PapaCSVReader,
ResponseSynthesizer,
serviceContextFromDefaults,
VectorStoreIndex,
} from "llamaindex";
import { PapaCSVReader } from "llamaindex/readers/CSVReader";
async function main() {
// Load CSV
+2 -1
View File
@@ -1,4 +1,5 @@
import { DocxReader, VectorStoreIndex } from "llamaindex";
import { VectorStoreIndex } from "llamaindex";
import { DocxReader } from "llamaindex/readers/DocxReader";
const FILE_PATH = "./data/stars.docx";
const SAMPLE_QUERY = "Information about Zodiac";
+2 -1
View File
@@ -1,4 +1,5 @@
import { HTMLReader, VectorStoreIndex } from "llamaindex";
import { VectorStoreIndex } from "llamaindex";
import { HTMLReader } from "llamaindex/readers/HTMLReader";
async function main() {
// Load page
+2 -1
View File
@@ -1,4 +1,5 @@
import { MarkdownReader, VectorStoreIndex } from "llamaindex";
import { VectorStoreIndex } from "llamaindex";
import { MarkdownReader } from "llamaindex/readers/MarkdownReader";
const FILE_PATH = "./data/planets.md";
const SAMPLE_QUERY = "List all planets";
+2 -3
View File
@@ -1,9 +1,8 @@
import { Client } from "@notionhq/client";
import { program } from "commander";
import { NotionReader, VectorStoreIndex } from "llamaindex";
import { VectorStoreIndex } from "llamaindex";
import { NotionReader } from "llamaindex/readers/NotionReader";
import { stdin as input, stdout as output } from "node:process";
// readline/promises is still experimental so not in @types/node yet
// @ts-ignore
import readline from "node:readline/promises";
program
+7 -3
View File
@@ -1,9 +1,13 @@
import { PDFReader, VectorStoreIndex } from "llamaindex";
import { VectorStoreIndex } from "llamaindex";
import { PDFReader } from "llamaindex/readers/PDFReader";
import { resolve } from "node:path";
async function main() {
// Load PDF
const reader = new PDFReader();
const documents = await reader.loadData("data/brk-2022.pdf");
const documents = await reader.loadData(
resolve(__dirname, "../data/brk-2022.pdf"),
);
// Split text and create embeddings. Store them in a VectorStoreIndex
const index = await VectorStoreIndex.fromDocuments(documents);
@@ -11,7 +15,7 @@ async function main() {
// Query the index
const queryEngine = index.asQueryEngine();
const response = await queryEngine.query({
query: "What mistakes did they make?",
query: "What mistakes did Warren E. Buffett make?",
});
// Output response
+76
View File
@@ -0,0 +1,76 @@
import {
OpenAI,
RouterQueryEngine,
SimpleDirectoryReader,
SimpleNodeParser,
SummaryIndex,
VectorStoreIndex,
serviceContextFromDefaults,
} from "llamaindex";
async function main() {
// Load documents from a directory
const documents = await new SimpleDirectoryReader().loadData({
directoryPath: "node_modules/llamaindex/examples",
});
// Parse the documents into nodes
const nodeParser = new SimpleNodeParser({
chunkSize: 1024,
});
// Create a service context
const serviceContext = serviceContextFromDefaults({
nodeParser,
llm: new OpenAI(),
});
// Create indices
const vectorIndex = await VectorStoreIndex.fromDocuments(documents, {
serviceContext,
});
const summaryIndex = await SummaryIndex.fromDocuments(documents, {
serviceContext,
});
// Create query engines
const vectorQueryEngine = vectorIndex.asQueryEngine();
const summaryQueryEngine = summaryIndex.asQueryEngine();
// Create a router query engine
const queryEngine = RouterQueryEngine.fromDefaults({
queryEngineTools: [
{
queryEngine: vectorQueryEngine,
description: "Useful for summarization questions related to Abramov",
},
{
queryEngine: summaryQueryEngine,
description: "Useful for retrieving specific context from Abramov",
},
],
serviceContext,
});
// Query the router query engine
const summaryResponse = await queryEngine.query({
query: "Give me a summary about his past experiences?",
});
console.log({
answer: summaryResponse.response,
metadata: summaryResponse?.metadata?.selectorResult,
});
const specificResponse = await queryEngine.query({
query: "Tell me about abramov first job?",
});
console.log({
answer: specificResponse.response,
metadata: specificResponse.metadata.selectorResult,
});
}
main().then(() => console.log("Done"));
+41
View File
@@ -0,0 +1,41 @@
import fs from "node:fs/promises";
import {
Document,
OpenAIEmbedding,
VectorStoreIndex,
serviceContextFromDefaults,
} 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 });
// Create service context and specify text-embedding-3-large
const embedModel = new OpenAIEmbedding({
model: "text-embedding-3-large",
dimensions: 1024,
});
const serviceContext = serviceContextFromDefaults({ embedModel });
// Split text and create embeddings. Store them in a VectorStoreIndex
const index = await VectorStoreIndex.fromDocuments([document], {
serviceContext,
});
// Query the index
const queryEngine = index.asQueryEngine();
const response = await queryEngine.query({
query: "What did the author do in college?",
});
// Output response
console.log(response.toString());
}
main().catch(console.error);
+6 -6
View File
@@ -8,7 +8,7 @@
"format": "prettier --ignore-unknown --cache --check .",
"format:write": "prettier --ignore-unknown --write .",
"lint": "turbo run lint",
"prepare": "husky install",
"prepare": "husky",
"test": "turbo run test",
"type-check": "tsc -b --diagnostics",
"release": "pnpm run build:release && changeset publish",
@@ -18,20 +18,20 @@
},
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@turbo/gen": "^1.11.2",
"@turbo/gen": "^1.11.3",
"@types/jest": "^29.5.11",
"eslint": "^8.56.0",
"eslint-config-custom": "workspace:*",
"husky": "^8.0.3",
"husky": "^9.0.6",
"jest": "^29.7.0",
"lint-staged": "^15.2.0",
"prettier": "^3.2.4",
"prettier-plugin-organize-imports": "^3.2.4",
"ts-jest": "^29.1.1",
"turbo": "^1.11.2",
"ts-jest": "^29.1.2",
"turbo": "^1.11.3",
"typescript": "^5.3.3"
},
"packageManager": "pnpm@8.10.5+sha256.a4bd9bb7b48214bbfcd95f264bd75bb70d100e5d4b58808f5cd6ab40c6ac21c5",
"packageManager": "pnpm@8.14.3+sha256.2d0363bb6c314daa67087ef07743eea1ba2e2d360c835e8fec6b5575e4ed9484",
"pnpm": {
"overrides": {
"trim": "1.0.1",
+79
View File
@@ -1,5 +1,84 @@
# llamaindex
## 0.1.7
### Patch Changes
- d687c11: feat(router): add router query engine
## 0.1.6
### Patch Changes
- cf44640: fix: `instanceof` issue
This will fix QueryEngine cannot run.
- 7231ddb: feat: allow `SimpleDirectoryReader` to get a string
## 0.1.5
### Patch Changes
- 8a9b78a: chore: split readers into different files
## 0.1.4
### Patch Changes
- 88696e1: refactor: use `pdf2json` instead of `pdfjs-dist`
Please add `pdf2json` to `serverComponentsExternalPackages` if you have to parse pdf in runtime.
```js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverComponentsExternalPackages: ["pdf2json"],
},
};
module.exports = nextConfig;
```
## 0.1.3
### Patch Changes
- 9ce7d3d: update dependencies
- 7d50196: fix: output target causes not implemented error
## 0.1.2
- e4b807a: fix: invalid package.json
## 0.1.1
No changes for this release.
## 0.1.0
### Minor Changes
- 3154f52: chore: add qdrant readme
### Patch Changes
- bb66cb7: add new OpenAI embeddings (with dimension reduction support)
## 0.0.51
### Patch Changes
- fda8024: revert: export conditions not working with moduleResolution `node`
## 0.0.50
### Patch Changes
- 8a729cd: fix bugs in Together.AI integration (thanks @Nutlope for reporting)
## 0.0.49
### Patch Changes
-130
View File
@@ -1,130 +0,0 @@
# LlamaIndex.TS
[![NPM Version](https://img.shields.io/npm/v/llamaindex)](https://www.npmjs.com/package/llamaindex)
[![NPM License](https://img.shields.io/npm/l/llamaindex)](https://www.npmjs.com/package/llamaindex)
[![NPM Downloads](https://img.shields.io/npm/dm/llamaindex)](https://www.npmjs.com/package/llamaindex)
[![Discord](https://img.shields.io/discord/1059199217496772688)](https://discord.com/invite/eN6D2HQ4aX)
LlamaIndex is a data framework for your LLM application.
Use your own data with large language models (LLMs, OpenAI ChatGPT and others) in Typescript and Javascript.
Documentation: https://ts.llamaindex.ai/
## What is LlamaIndex.TS?
LlamaIndex.TS aims to be a lightweight, easy to use set of libraries to help you integrate large language models into your applications with your own data.
## Getting started with an example:
LlamaIndex.TS requires Node v18 or higher. You can download it from https://nodejs.org or use https://nvm.sh (our preferred option).
In a new folder:
```bash
export OPENAI_API_KEY="sk-......" # Replace with your key from https://platform.openai.com/account/api-keys
pnpm init
pnpm install typescript
pnpm exec tsc --init # if needed
pnpm install llamaindex
pnpm install @types/node
```
Create the file example.ts
```ts
// example.ts
import fs from "fs/promises";
import { Document, VectorStoreIndex } from "llamaindex";
async function main() {
// Load essay from abramov.txt in Node
const essay = await fs.readFile(
"node_modules/llamaindex/examples/abramov.txt",
"utf-8",
);
// Create Document object with essay
const document = new Document({ text: essay });
// Split text and create embeddings. Store them in a VectorStoreIndex
const index = await VectorStoreIndex.fromDocuments([document]);
// Query the index
const queryEngine = index.asQueryEngine();
const response = await queryEngine.query(
"What did the author do in college?",
);
// Output response
console.log(response.toString());
}
main();
```
Then you can run it using
```bash
pnpx ts-node example.ts
```
## Playground
Check out our NextJS playground at https://llama-playground.vercel.app/. The source is available at https://github.com/run-llama/ts-playground
## Core concepts for getting started:
- [Document](/packages/core/src/Node.ts): A document represents a text file, PDF file or other contiguous piece of data.
- [Node](/packages/core/src/Node.ts): 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.
- [Embedding](/packages/core/src/Embedding.ts): Embeddings are sets of floating point numbers which represent the data in a Node. By comparing the similarity of embeddings, we can derive an understanding of the similarity of two pieces of data. One use case is to compare the embedding of a question with the embeddings of our Nodes to see which Nodes may contain the data needed to answer that quesiton.
- [Indices](/packages/core/src/indices/): Indices store the Nodes and the embeddings of those nodes. QueryEngines retrieve Nodes from these Indices using embedding similarity.
- [QueryEngine](/packages/core/src/QueryEngine.ts): 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](/packages/core/src/ChatEngine.ts): A ChatEngine helps you build a chatbot that will interact with your Indices.
- [SimplePrompt](/packages/core/src/Prompt.ts): A simple standardized function call definition that takes in inputs and formats them in a template literal. SimplePrompts can be specialized using currying and combined using other SimplePrompt functions.
## Note: NextJS:
If you're using NextJS App Router, you'll need to use the NodeJS runtime (default) and add the following config to your next.config.js to have it use imports/exports in the same way Node does.
```js
export const runtime = "nodejs"; // default
```
```js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
sharp$: false,
"onnxruntime-node$": false,
};
return config;
},
};
module.exports = nextConfig;
```
## Supported LLMs:
- OpenAI GPT-3.5-turbo and GPT-4
- Anthropic Claude Instant and Claude 2
- Llama2 Chat LLMs (70B, 13B, and 7B parameters)
- MistralAI Chat LLMs
## Contributing:
We are in the very early days of LlamaIndex.TS. If youre interested in hacking on it with us check out our [contributing guide](/CONTRIBUTING.md)
## Bugs? Questions?
Please join our Discord! https://discord.com/invite/eN6D2HQ4aX
+1 -1
View File
@@ -2,5 +2,5 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
testPathIgnorePatterns: ["/lib/"],
testPathIgnorePatterns: ["/lib/", "/node_modules/", "/dist/"],
};
+36 -39
View File
@@ -1,16 +1,17 @@
{
"name": "llamaindex",
"version": "0.0.49",
"private": true,
"version": "0.1.7",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.9.1",
"@datastax/astra-db-ts": "^0.1.2",
"@mistralai/mistralai": "^0.0.7",
"@anthropic-ai/sdk": "^0.12.4",
"@datastax/astra-db-ts": "^0.1.4",
"@mistralai/mistralai": "^0.0.10",
"@notionhq/client": "^2.2.14",
"@pinecone-database/pinecone": "^1.1.2",
"@pinecone-database/pinecone": "^1.1.3",
"@qdrant/js-client-rest": "^1.7.0",
"@xenova/transformers": "^2.10.0",
"assemblyai": "^4.0.0",
"@xenova/transformers": "^2.14.1",
"assemblyai": "^4.2.1",
"chromadb": "~1.7.3",
"file-type": "^18.7.0",
"js-tiktoken": "^1.0.8",
@@ -19,26 +20,28 @@
"md-utils-ts": "^2.0.0",
"mongodb": "^6.3.0",
"notion-md-crawler": "^0.0.2",
"openai": "^4.20.1",
"openai": "^4.26.0",
"papaparse": "^5.4.1",
"pathe": "^1.1.2",
"pdfjs-dist": "4.0.269",
"pdf2json": "^3.0.5",
"pg": "^8.11.3",
"pgvector": "^0.1.5",
"pgvector": "^0.1.7",
"portkey-ai": "^0.1.16",
"rake-modified": "^1.0.8",
"replicate": "^0.21.1",
"string-strip-html": "^13.4.3",
"replicate": "^0.25.2",
"string-strip-html": "^13.4.5",
"wink-nlp": "^1.14.3"
},
"devDependencies": {
"@aws-crypto/sha256-js": "^5.2.0",
"@types/edit-json-file": "^1.7.3",
"@types/jest": "^29.5.11",
"@types/lodash": "^4.14.202",
"@types/node": "^18.19.6",
"@types/node": "^18.19.10",
"@types/papaparse": "^5.3.14",
"@types/pg": "^8.10.9",
"bunchee": "^4.4.1",
"@types/pg": "^8.11.0",
"bunchee": "^4.4.3",
"edit-json-file": "^1.8.0",
"madge": "^6.1.0",
"typescript": "^5.3.3"
},
@@ -50,21 +53,20 @@
"exports": {
".": {
"types": "./dist/index.d.mts",
"edge-light": "./dist/index.edge-light.mjs",
"import": "./dist/index.mjs",
"edge-light": "./dist/index.edge-light.mjs",
"require": "./dist/index.js"
},
"./env": {
"types": "./dist/env.d.mts",
"edge-light": "./dist/env.edge-light.mjs",
"import": "./dist/env.mjs",
"edge-light": "./dist/env.edge-light.mjs",
"require": "./dist/env.js"
},
"./storage/FileSystem": {
"types": "./dist/storage/FileSystem.d.mts",
"edge-light": "./dist/storage/FileSystem.edge-light.mjs",
"import": "./dist/storage/FileSystem.mjs",
"require": "./dist/storage/FileSystem.js"
"./ChatEngine": {
"types": "./dist/ChatEngine.d.mts",
"import": "./dist/ChatEngine.mjs",
"require": "./dist/ChatEngine.js"
},
"./ChatHistory": {
"types": "./dist/ChatHistory.d.mts",
@@ -136,15 +138,10 @@
"import": "./dist/Tool.mjs",
"require": "./dist/Tool.js"
},
"./readers/AssemblyAI": {
"types": "./dist/readers/AssemblyAI.d.mts",
"import": "./dist/readers/AssemblyAI.mjs",
"require": "./dist/readers/AssemblyAI.js"
},
"./readers/base": {
"types": "./dist/readers/base.d.mts",
"import": "./dist/readers/base.mjs",
"require": "./dist/readers/base.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",
@@ -193,11 +190,7 @@
}
},
"files": [
"dist",
"examples",
"src",
"types",
"CHANGELOG.md"
"**"
],
"repository": {
"type": "git",
@@ -207,8 +200,12 @@
"scripts": {
"lint": "eslint .",
"test": "jest",
"build": "bunchee",
"dev": "bunchee -w",
"circular-check": "madge --circular ./src/*.ts"
"build": "NODE_OPTIONS=\"--max-old-space-size=8192\" 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=8192\" bunchee -w",
"circular-check": "madge -c ./src/index.ts"
}
}
+25
View File
@@ -0,0 +1,25 @@
#!/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",
);
}
+2 -17
View File
@@ -1,20 +1,5 @@
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 { SubQuestion } from "./engines/query/types";
import { BaseOutputParser, StructuredOutput } from "./types";
/**
* Error class for output parsing. Due to the nature of LLMs, anytime we use LLM
+2 -2
View File
@@ -1,6 +1,6 @@
import { SubQuestion } from "./engines/query/types";
import { ChatMessage } from "./llm/types";
import { SubQuestion } from "./QuestionGenerator";
import { ToolMetadata } from "./Tool";
import { ToolMetadata } from "./types";
/**
* A SimplePrompt is a function that takes a dictionary of inputs and returns a string.
+3 -18
View File
@@ -1,28 +1,13 @@
import {
BaseOutputParser,
StructuredOutput,
SubQuestionOutputParser,
} from "./OutputParser";
import { SubQuestionOutputParser } from "./OutputParser";
import {
SubQuestionPrompt,
buildToolsText,
defaultSubQuestionPrompt,
} from "./Prompt";
import { ToolMetadata } from "./Tool";
import { BaseQuestionGenerator, SubQuestion } from "./engines/query/types";
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, StructuredOutput, ToolMetadata } from "./types";
/**
* LLMQuestionGenerator uses the LLM to generate new questions for the LLM using tools and a user query.
+1
View File
@@ -6,6 +6,7 @@ import { BaseNode } from "./Node";
export class Response {
response: string;
sourceNodes?: BaseNode[];
metadata: Record<string, unknown> = {};
constructor(response: string, sourceNodes?: BaseNode[]) {
this.response = response;
-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;
}
-2
View File
@@ -6,6 +6,4 @@ export const DEFAULT_CHUNK_OVERLAP = 20;
export const DEFAULT_CHUNK_OVERLAP_RATIO = 0.1;
export const DEFAULT_SIMILARITY_TOP_K = 2;
// NOTE: for text-embedding-ada-002
export const DEFAULT_EMBEDDING_DIM = 1536;
export const DEFAULT_PADDING = 5;
@@ -39,7 +39,7 @@ export class HuggingFaceEmbedding extends BaseEmbedding {
async getTextEmbedding(text: string): Promise<number[]> {
const extractor = await this.getExtractor();
const output = await extractor(text, { pooling: "mean", normalize: true });
return output.data;
return Array.from(output.data);
}
async getQueryEmbedding(query: string): Promise<number[]> {
@@ -6,31 +6,58 @@ import {
getAzureModel,
shouldUseAzure,
} from "../llm/azure";
import { OpenAISession, getOpenAISession } from "../llm/openai";
import { OpenAISession, getOpenAISession } from "../llm/open_ai";
import { BaseEmbedding } from "./types";
export enum OpenAIEmbeddingModelType {
TEXT_EMBED_ADA_002 = "text-embedding-ada-002",
}
export const ALL_OPENAI_EMBEDDING_MODELS = {
"text-embedding-ada-002": {
dimensions: 1536,
maxTokens: 8191,
},
"text-embedding-3-small": {
dimensions: 1536,
dimensionOptions: [512, 1536],
maxTokens: 8191,
},
"text-embedding-3-large": {
dimensions: 3072,
dimensionOptions: [256, 1024, 3072],
maxTokens: 8191,
},
};
export class OpenAIEmbedding extends BaseEmbedding {
model: OpenAIEmbeddingModelType | string;
/** embeddding model. defaults to "text-embedding-ada-002" */
model: string;
/** number of dimensions of the resulting vector, for models that support choosing fewer dimensions. undefined will default to model default */
dimensions: number | undefined;
// OpenAI session params
/** api key */
apiKey?: string = undefined;
/** maximum number of retries, default 10 */
maxRetries: number;
/** timeout in ms, default 60 seconds */
timeout?: number;
/** other session options for OpenAI */
additionalSessionOptions?: Omit<
Partial<OpenAIClientOptions>,
"apiKey" | "maxRetries" | "timeout"
>;
/** session object */
session: OpenAISession;
/**
* OpenAI Embedding
* @param init - initial parameters
*/
constructor(init?: Partial<OpenAIEmbedding> & { azure?: AzureOpenAIConfig }) {
super();
this.model = init?.model ?? OpenAIEmbeddingModelType.TEXT_EMBED_ADA_002;
this.model = init?.model ?? "text-embedding-ada-002";
this.dimensions = init?.dimensions; // if no dimensions provided, will be undefined/not sent to OpenAI
this.maxRetries = init?.maxRetries ?? 10;
this.timeout = init?.timeout ?? 60 * 1000; // Default is 60 seconds
@@ -76,6 +103,7 @@ export class OpenAIEmbedding extends BaseEmbedding {
private async getOpenAIEmbedding(input: string) {
const { data } = await this.session.openai.embeddings.create({
model: this.model,
dimensions: this.dimensions, // only sent to OpenAI if set by user
input,
});
+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,
@@ -0,0 +1,82 @@
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 { BaseSynthesizer, ResponseSynthesizer } from "../../synthesizers";
import {
BaseQueryEngine,
QueryEngineParamsNonStreaming,
QueryEngineParamsStreaming,
} from "../../types";
/**
* A query engine that uses a retriever to query an index and then synthesizes the response.
*/
export class RetrieverQueryEngine implements BaseQueryEngine {
retriever: BaseRetriever;
responseSynthesizer: BaseSynthesizer;
nodePostprocessors: BaseNodePostprocessor[];
preFilters?: unknown;
constructor(
retriever: BaseRetriever,
responseSynthesizer?: BaseSynthesizer,
preFilters?: unknown,
nodePostprocessors?: BaseNodePostprocessor[],
) {
this.retriever = retriever;
const serviceContext: ServiceContext | undefined =
this.retriever.getServiceContext();
this.responseSynthesizer =
responseSynthesizer || new ResponseSynthesizer({ serviceContext });
this.preFilters = preFilters;
this.nodePostprocessors = nodePostprocessors || [];
}
private applyNodePostprocessors(nodes: NodeWithScore[]) {
return this.nodePostprocessors.reduce(
(nodes, nodePostprocessor) => nodePostprocessor.postprocessNodes(nodes),
nodes,
);
}
private async retrieve(query: string, parentEvent: Event) {
const nodes = await this.retriever.retrieve(
query,
parentEvent,
this.preFilters,
);
return this.applyNodePostprocessors(nodes);
}
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
async query(
params: QueryEngineParamsStreaming | QueryEngineParamsNonStreaming,
): Promise<Response | AsyncIterable<Response>> {
const { query, stream } = params;
const parentEvent: Event = params.parentEvent || {
id: randomUUID(),
type: "wrapper",
tags: ["final"],
};
const nodesWithScore = await this.retrieve(query, parentEvent);
if (stream) {
return this.responseSynthesizer.synthesize({
query,
nodesWithScore,
parentEvent,
stream: true,
});
}
return this.responseSynthesizer.synthesize({
query,
nodesWithScore,
parentEvent,
});
}
}
@@ -0,0 +1,181 @@
import { Response } from "../../Response";
import {
ServiceContext,
serviceContextFromDefaults,
} from "../../ServiceContext";
import { BaseSelector, LLMSingleSelector } from "../../selectors";
import { TreeSummarize } from "../../synthesizers";
import {
BaseQueryEngine,
QueryBundle,
QueryEngineParamsNonStreaming,
QueryEngineParamsStreaming,
} from "../../types";
type RouterQueryEngineTool = {
queryEngine: BaseQueryEngine;
description: string;
};
type RouterQueryEngineMetadata = {
description: string;
};
async function combineResponses(
summarizer: TreeSummarize,
responses: Response[],
queryBundle: QueryBundle,
verbose: boolean = false,
): Promise<Response> {
if (verbose) {
console.log("Combining responses from multiple query engines.");
}
const responseStrs = [];
const sourceNodes = [];
for (const response of responses) {
if (response?.sourceNodes) {
sourceNodes.push(...response.sourceNodes);
}
responseStrs.push(response.response);
}
const summary = await summarizer.getResponse({
query: queryBundle.queryStr,
textChunks: responseStrs,
});
return new Response(summary, sourceNodes);
}
/**
* A query engine that uses multiple query engines and selects the best one.
*/
export class RouterQueryEngine implements BaseQueryEngine {
serviceContext: ServiceContext;
private selector: BaseSelector;
private queryEngines: BaseQueryEngine[];
private metadatas: RouterQueryEngineMetadata[];
private summarizer: TreeSummarize;
private verbose: boolean;
constructor(init: {
selector: BaseSelector;
queryEngineTools: RouterQueryEngineTool[];
serviceContext?: ServiceContext;
summarizer?: TreeSummarize;
verbose?: boolean;
}) {
this.serviceContext = init.serviceContext || serviceContextFromDefaults({});
this.selector = init.selector;
this.queryEngines = init.queryEngineTools.map((tool) => tool.queryEngine);
this.metadatas = init.queryEngineTools.map((tool) => ({
description: tool.description,
}));
this.summarizer = init.summarizer || new TreeSummarize(this.serviceContext);
this.verbose = init.verbose ?? false;
}
static fromDefaults(init: {
queryEngineTools: RouterQueryEngineTool[];
selector?: BaseSelector;
serviceContext?: ServiceContext;
summarizer?: TreeSummarize;
verbose?: boolean;
}) {
const serviceContext =
init.serviceContext ?? serviceContextFromDefaults({});
return new RouterQueryEngine({
selector:
init.selector ?? new LLMSingleSelector({ llm: serviceContext.llm }),
queryEngineTools: init.queryEngineTools,
serviceContext,
summarizer: init.summarizer,
verbose: init.verbose,
});
}
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
async query(
params: QueryEngineParamsStreaming | QueryEngineParamsNonStreaming,
): Promise<Response | AsyncIterable<Response>> {
const { query, stream } = params;
const response = await this.queryRoute({ queryStr: query });
if (stream) {
throw new Error("Streaming is not supported yet.");
}
return response;
}
private async queryRoute(queryBundle: QueryBundle): Promise<Response> {
const result = await this.selector.select(this.metadatas, queryBundle);
if (result.selections.length > 1) {
const responses = [];
for (let i = 0; i < result.selections.length; i++) {
const engineInd = result.selections[i];
const logStr = `Selecting query engine ${engineInd}: ${result.selections[i]}.`;
if (this.verbose) {
console.log(logStr + "\n");
}
const selectedQueryEngine = this.queryEngines[engineInd.index];
responses.push(
await selectedQueryEngine.query({
query: queryBundle.queryStr,
}),
);
}
if (responses.length > 1) {
const finalResponse = await combineResponses(
this.summarizer,
responses,
queryBundle,
this.verbose,
);
return finalResponse;
} else {
return responses[0];
}
} else {
let selectedQueryEngine;
try {
selectedQueryEngine = this.queryEngines[result.selections[0].index];
const logStr = `Selecting query engine ${result.selections[0].index}: ${result.selections[0].reason}`;
if (this.verbose) {
console.log(logStr + "\n");
}
} catch (e) {
throw new Error("Failed to select query engine");
}
if (!selectedQueryEngine) {
throw new Error("Selected query engine is null");
}
const finalResponse = await selectedQueryEngine.query({
query: queryBundle.queryStr,
});
// add selected result
finalResponse.metadata = finalResponse.metadata || {};
finalResponse.metadata["selectorResult"] = result;
return finalResponse;
}
}
}
@@ -1,118 +1,25 @@
import { NodeWithScore, TextNode } from "./Node";
import { NodeWithScore, TextNode } from "../../Node";
import { LLMQuestionGenerator } from "../../QuestionGenerator";
import { Response } from "../../Response";
import {
BaseQuestionGenerator,
LLMQuestionGenerator,
SubQuestion,
} 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";
ServiceContext,
serviceContextFromDefaults,
} from "../../ServiceContext";
import { Event } from "../../callbacks/CallbackManager";
import { randomUUID } from "../../env";
import {
BaseSynthesizer,
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>;
}
/**
* A query engine that uses a retriever to query an index and then synthesizes the response.
*/
export class RetrieverQueryEngine implements BaseQueryEngine {
retriever: BaseRetriever;
responseSynthesizer: BaseSynthesizer;
nodePostprocessors: BaseNodePostprocessor[];
preFilters?: unknown;
constructor(
retriever: BaseRetriever,
responseSynthesizer?: BaseSynthesizer,
preFilters?: unknown,
nodePostprocessors?: BaseNodePostprocessor[],
) {
this.retriever = retriever;
const serviceContext: ServiceContext | undefined =
this.retriever.getServiceContext();
this.responseSynthesizer =
responseSynthesizer || new ResponseSynthesizer({ serviceContext });
this.preFilters = preFilters;
this.nodePostprocessors = nodePostprocessors || [];
}
private applyNodePostprocessors(nodes: NodeWithScore[]) {
return this.nodePostprocessors.reduce(
(nodes, nodePostprocessor) => nodePostprocessor.postprocessNodes(nodes),
nodes,
);
}
private async retrieve(query: string, parentEvent: Event) {
const nodes = await this.retriever.retrieve(
query,
parentEvent,
this.preFilters,
);
return this.applyNodePostprocessors(nodes);
}
query(params: QueryEngineParamsStreaming): Promise<AsyncIterable<Response>>;
query(params: QueryEngineParamsNonStreaming): Promise<Response>;
async query(
params: QueryEngineParamsStreaming | QueryEngineParamsNonStreaming,
): Promise<Response | AsyncIterable<Response>> {
const { query, stream } = params;
const parentEvent: Event = params.parentEvent || {
id: randomUUID(),
type: "wrapper",
tags: ["final"],
};
const nodesWithScore = await this.retrieve(query, parentEvent);
if (stream) {
return this.responseSynthesizer.synthesize({
query,
nodesWithScore,
parentEvent,
stream: true,
});
}
return this.responseSynthesizer.synthesize({
query,
nodesWithScore,
parentEvent,
});
}
}
} from "../../synthesizers";
import {
BaseQueryEngine,
QueryEngineParamsNonStreaming,
QueryEngineParamsStreaming,
QueryEngineTool,
ToolMetadata,
} from "../../types";
import { BaseQuestionGenerator, SubQuestion } from "./types";
/**
* SubQuestionQueryEngine decomposes a question into subquestions and then
+3
View File
@@ -0,0 +1,3 @@
export * from "./RetrieverQueryEngine";
export * from "./RouterQueryEngine";
export * from "./SubQuestionQueryEngine";
+13
View File
@@ -0,0 +1,13 @@
import { ToolMetadata } from "../../types";
/**
* QuestionGenerators generate new questions for the LLM using tools and a user query.
*/
export interface BaseQuestionGenerator {
generate(tools: ToolMetadata[], query: string): Promise<SubQuestion[]>;
}
export interface SubQuestion {
subQuestion: string;
toolName: string;
}
+15 -1
View File
@@ -18,6 +18,20 @@ export function createSHA256(): SHA256 {
};
}
export const defaultFS: CompleteFileSystem = fs;
export const defaultFS: CompleteFileSystem = {
writeFile: function (path: string, content: string) {
return fs.writeFile(path, content, "utf-8");
},
readRawFile(path: string): Promise<Buffer> {
return fs.readFile(path);
},
readFile: function (path: string) {
return fs.readFile(path, "utf-8");
},
access: fs.access,
mkdir: fs.mkdir,
readdir: fs.readdir,
stat: fs.stat,
};
export { EOL, ok, path, randomUUID };
@@ -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;
}
/**
+1
View File
@@ -4,3 +4,4 @@ export {
SummaryExtractor,
TitleExtractor,
} from "./MetadataExtractors";
export { BaseExtractor } from "./types";
+4 -1
View File
@@ -46,7 +46,10 @@ export abstract class BaseExtractor implements TransformComponent {
let curMetadataList = await this.extract(newNodes);
for (let idx in newNodes) {
newNodes[idx].metadata = curMetadataList[idx];
newNodes[idx].metadata = {
...newNodes[idx].metadata,
...curMetadataList[idx],
};
}
for (let idx in newNodes) {
+4 -3
View File
@@ -4,24 +4,23 @@ export * from "./Node";
export * from "./OutputParser";
export * from "./Prompt";
export * from "./PromptHelper";
export * from "./QueryEngine";
export * from "./QuestionGenerator";
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";
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 "./postprocessors";
export * from "./readers/AssemblyAI";
export * from "./readers/AssemblyAIReader";
export * from "./readers/CSVReader";
export * from "./readers/DocxReader";
export * from "./readers/HTMLReader";
@@ -31,5 +30,7 @@ export * from "./readers/PDFReader";
export * from "./readers/SimpleDirectoryReader";
export * from "./readers/SimpleMongoReader";
export * from "./readers/base";
export * from "./selectors";
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,11 +1,11 @@
import { BaseNode, Document, MetadataMode } from "../../Node";
import { defaultKeywordExtractPrompt } from "../../Prompt";
import { BaseQueryEngine, RetrieverQueryEngine } from "../../QueryEngine";
import { BaseRetriever } from "../../Retriever";
import {
ServiceContext,
serviceContextFromDefaults,
} from "../../ServiceContext";
import { RetrieverQueryEngine } from "../../engines/query";
import { BaseNodePostprocessor } from "../../postprocessors";
import {
BaseDocumentStore,
@@ -13,6 +13,7 @@ import {
storageContextFromDefaults,
} from "../../storage";
import { BaseSynthesizer } from "../../synthesizers";
import { BaseQueryEngine } from "../../types";
import {
BaseIndex,
BaseIndexInit,
@@ -1,11 +1,11 @@
import _ from "lodash";
import { BaseNode, Document } from "../../Node";
import { BaseQueryEngine, RetrieverQueryEngine } from "../../QueryEngine";
import { BaseRetriever } from "../../Retriever";
import {
ServiceContext,
serviceContextFromDefaults,
} from "../../ServiceContext";
import { RetrieverQueryEngine } from "../../engines/query";
import { BaseNodePostprocessor } from "../../postprocessors";
import {
BaseDocumentStore,
@@ -18,6 +18,7 @@ import {
CompactAndRefine,
ResponseSynthesizer,
} from "../../synthesizers";
import { BaseQueryEngine } from "../../types";
import {
BaseIndex,
BaseIndexInit,
@@ -6,7 +6,6 @@ import {
ObjectType,
splitNodesByType,
} from "../../Node";
import { BaseQueryEngine, RetrieverQueryEngine } from "../../QueryEngine";
import { BaseRetriever } from "../../Retriever";
import {
ServiceContext,
@@ -17,6 +16,8 @@ import {
ClipEmbedding,
MultiModalEmbedding,
} from "../../embeddings";
import { RetrieverQueryEngine } from "../../engines/query";
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,48 @@
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";
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) {
+11 -3
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,
@@ -41,14 +41,21 @@ import {
export const GPT4_MODELS = {
"gpt-4": { contextWindow: 8192 },
"gpt-4-32k": { contextWindow: 32768 },
"gpt-4-32k-0613": { contextWindow: 32768 },
"gpt-4-turbo-preview": { contextWindow: 128000 },
"gpt-4-1106-preview": { contextWindow: 128000 },
"gpt-4-vision-preview": { contextWindow: 8192 },
"gpt-4-0125-preview": { contextWindow: 128000 },
"gpt-4-vision-preview": { contextWindow: 128000 },
};
// NOTE we don't currently support gpt-3.5-turbo-instruct and don't plan to in the near future
export const GPT35_MODELS = {
"gpt-3.5-turbo": { contextWindow: 4096 },
"gpt-3.5-turbo-0613": { contextWindow: 4096 },
"gpt-3.5-turbo-16k": { contextWindow: 16384 },
"gpt-3.5-turbo-16k-0613": { contextWindow: 16384 },
"gpt-3.5-turbo-1106": { contextWindow: 16384 },
"gpt-3.5-turbo-0125": { contextWindow: 16384 },
};
/**
@@ -209,6 +216,7 @@ export class OpenAI extends BaseLLM {
top_p: this.topP,
...this.additionalChatOptions,
};
// Streaming
if (stream) {
return this.streamChat(params);
+26 -2
View File
@@ -17,6 +17,14 @@ const ALL_AZURE_OPENAI_CHAT_MODELS = {
},
"gpt-4": { contextWindow: 8192, openAIModel: "gpt-4" },
"gpt-4-32k": { contextWindow: 32768, openAIModel: "gpt-4-32k" },
"gpt-4-vision-preview": {
contextWindow: 128000,
openAIModel: "gpt-4-vision-preview",
},
"gpt-4-1106-preview": {
contextWindow: 128000,
openAIModel: "gpt-4-1106-preview",
},
};
const ALL_AZURE_OPENAI_EMBEDDING_MODELS = {
@@ -25,13 +33,29 @@ const ALL_AZURE_OPENAI_EMBEDDING_MODELS = {
openAIModel: "text-embedding-ada-002",
maxTokens: 8191,
},
"text-embedding-3-small": {
dimensions: 1536,
dimensionOptions: [512, 1536],
openAIModel: "text-embedding-3-small",
maxTokens: 8191,
},
"text-embedding-3-large": {
dimensions: 3072,
dimensionOptions: [256, 1024, 3072],
openAIModel: "text-embedding-3-large",
maxTokens: 8191,
},
};
const ALL_AZURE_API_VERSIONS = [
"2022-12-01",
"2023-05-15",
"2023-06-01-preview",
"2023-07-01-preview",
"2023-03-15-preview", // retiring 2024-04-02
"2023-06-01-preview", // retiring 2024-04-02
"2023-07-01-preview", // retiring 2024-04-02
"2023-08-01-preview", // retiring 2024-04-02
"2023-09-01-preview",
"2023-12-01-preview",
];
const DEFAULT_API_VERSION = "2023-05-15";
+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";
@@ -0,0 +1,52 @@
import { parseJsonMarkdown } from "../OutputParser";
import { BaseOutputParser, StructuredOutput } from "../types";
export type Answer = {
choice: number;
reason: string;
};
const formatStr = `The output should be ONLY JSON formatted as a JSON instance.
Here is an example:
[
{
choice: 1,
reason: "<insert reason for choice>"
},
...
]
`;
/*
* An OutputParser is used to extract structured data from the raw output of the LLM.
*/
export class SelectionOutputParser
implements BaseOutputParser<StructuredOutput<Answer[]>>
{
/**
*
* @param output
*/
parse(output: string): StructuredOutput<Answer[]> {
let parsed;
try {
parsed = parseJsonMarkdown(output);
} catch (e) {
try {
parsed = JSON.parse(output);
} catch (e) {
throw new Error(
`Got invalid JSON object. Error: ${e}. Got JSON string: ${output}`,
);
}
}
return { rawOutput: output, parsedOutput: parsed };
}
format(output: string): string {
return output + "\n\n" + formatStr;
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ export class PapaCSVReader implements BaseReader {
file: string,
fs: GenericFileSystem = defaultFS,
): Promise<Document[]> {
const fileContent: string = await fs.readFile(file, "utf-8");
const fileContent = await fs.readFile(file);
const result = Papa.parse(fileContent, this.papaConfig);
const textList = result.data.map((row: any) => {
// Compatible with header row mode
+1 -1
View File
@@ -10,7 +10,7 @@ export class DocxReader implements BaseReader {
file: string,
fs: GenericFileSystem = defaultFS,
): Promise<Document[]> {
const dataBuffer = (await fs.readFile(file)) as any;
const dataBuffer = await fs.readRawFile(file);
const { value } = await mammoth.extractRawText({ buffer: dataBuffer });
return [new Document({ text: value, id_: file })];
}
+1 -1
View File
@@ -22,7 +22,7 @@ export class HTMLReader implements BaseReader {
file: string,
fs: GenericFileSystem = defaultFS,
): Promise<Document[]> {
const dataBuffer = await fs.readFile(file, "utf-8");
const dataBuffer = await fs.readFile(file);
const htmlOptions = this.getOptions();
const content = await this.parseContent(dataBuffer, htmlOptions);
return [new Document({ text: content, id_: file })];
+1 -1
View File
@@ -92,7 +92,7 @@ export class MarkdownReader implements BaseReader {
file: string,
fs: GenericFileSystem = defaultFS,
): Promise<Document[]> {
const content = await fs.readFile(file, { encoding: "utf-8" });
const content = await fs.readFile(file);
const tups = this.parseTups(content);
const results: Document[] = [];
for (const [header, value] of tups) {
+24 -71
View File
@@ -1,5 +1,5 @@
import { Document } from "../Node";
import { defaultFS } from "../env";
import { createSHA256, defaultFS } from "../env";
import { GenericFileSystem } from "../storage/FileSystem";
import { BaseReader } from "./base";
@@ -11,78 +11,31 @@ export class PDFReader implements BaseReader {
file: string,
fs: GenericFileSystem = defaultFS,
): Promise<Document[]> {
const content = (await fs.readFile(file)) as any;
if (!(content instanceof Buffer)) {
console.warn(`PDF File ${file} can only be loaded using the Node FS`);
return [];
}
const data = new Uint8Array(
content.buffer,
content.byteOffset,
content.byteLength,
);
const pdf = await readPDF(data);
return [new Document({ text: pdf.text, id_: file })];
const content = await fs.readRawFile(file);
const text = await readPDF(content);
return text.map((text) => {
const sha256 = createSHA256();
sha256.update(text);
return new Document({
text,
id_: sha256.digest(),
});
});
}
}
// NOTE: the following code is taken from https://www.npmjs.com/package/pdf-parse and modified
async function readPage(pageData: any) {
//check documents https://mozilla.github.io/pdf.js/
const textContent = await pageData.getTextContent({
includeMarkedContent: false,
async function readPDF(data: Buffer): Promise<string[]> {
const parser = await import("pdf2json").then(
({ default: Pdfparser }) => new Pdfparser(null, 1),
);
const text = await new Promise<string>((resolve, reject) => {
parser.on("pdfParser_dataError", (error) => {
reject(error);
});
parser.on("pdfParser_dataReady", () => {
resolve((parser as any).getRawTextContent() as string);
});
parser.parseBuffer(data);
});
let lastY = null,
text = "";
//https://github.com/mozilla/pdf.js/issues/8963
//https://github.com/mozilla/pdf.js/issues/2140
//https://gist.github.com/hubgit/600ec0c224481e910d2a0f883a7b98e3
//https://gist.github.com/hubgit/600ec0c224481e910d2a0f883a7b98e3
for (const item of textContent.items) {
if (lastY == item.transform[5] || !lastY) {
text += item.str;
} else {
text += "\n" + item.str;
}
lastY = item.transform[5];
}
return text;
}
const PDF_DEFAULT_OPTIONS = {
max: 0,
};
async function readPDF(data: Uint8Array, options = PDF_DEFAULT_OPTIONS) {
const { getDocument, version } = await import("pdfjs-dist");
const doc = await getDocument({ data }).promise;
const metaData = await doc.getMetadata().catch(() => null);
const counter =
options.max === 0 ? doc.numPages : Math.max(options.max, doc.numPages);
let text = "";
for (let i = 1; i <= counter; i++) {
try {
const pageData = await doc.getPage(i);
const pageText = await readPage(pageData);
text += `\n\n${pageText}`;
} catch (err) {
console.log(err);
}
}
await doc.destroy();
return {
numpages: doc.numPages,
numrender: counter,
info: metaData?.info,
metadata: metaData?.metadata,
text,
version,
};
return text.split(/----------------Page \(\d+\) Break----------------/g);
}
@@ -30,7 +30,7 @@ export class TextFileReader implements BaseReader {
file: string,
fs: CompleteFileSystem = defaultFS,
): Promise<Document[]> {
const dataBuffer = await fs.readFile(file, "utf-8");
const dataBuffer = await fs.readFile(file);
return [new Document({ text: dataBuffer, id_: file })];
}
}
@@ -49,7 +49,7 @@ export const FILE_EXT_TO_READER: Record<string, BaseReader> = {
gif: new ImageReader(),
};
export type SimpleDirectoryReaderLoadDataProps = {
export type SimpleDirectoryReaderLoadDataParams = {
directoryPath: string;
fs?: CompleteFileSystem;
defaultReader?: BaseReader | null;
@@ -64,12 +64,20 @@ export type SimpleDirectoryReaderLoadDataProps = {
export class SimpleDirectoryReader implements BaseReader {
constructor(private observer?: ReaderCallback) {}
async loadData({
directoryPath,
fs = defaultFS,
defaultReader = new TextFileReader(),
fileExtToReader = FILE_EXT_TO_READER,
}: SimpleDirectoryReaderLoadDataProps): Promise<Document[]> {
async loadData(
params: SimpleDirectoryReaderLoadDataParams | string,
): Promise<Document[]> {
if (typeof params === "string") {
params = { directoryPath: params };
}
const {
directoryPath,
fs = defaultFS,
defaultReader = new TextFileReader(),
fileExtToReader = FILE_EXT_TO_READER,
} = params;
// Observer can decide to skip the directory
if (
!this.doObserverCheck("directory", directoryPath, ReaderStatus.STARTED)
+45
View File
@@ -0,0 +1,45 @@
import { QueryBundle, ToolMetadataOnlyDescription } from "../types";
export interface SingleSelection {
index: number;
reason: string;
}
export type SelectorResult = {
selections: SingleSelection[];
};
type QueryType = string | QueryBundle;
function wrapChoice(
choice: string | ToolMetadataOnlyDescription,
): ToolMetadataOnlyDescription {
if (typeof choice === "string") {
return { description: choice };
} else {
return choice;
}
}
function wrapQuery(query: QueryType): QueryBundle {
if (typeof query === "string") {
return { queryStr: query };
}
return query;
}
type MetadataType = string | ToolMetadataOnlyDescription;
export abstract class BaseSelector {
async select(choices: MetadataType[], query: QueryType) {
const metadatas = choices.map((choice) => wrapChoice(choice));
const queryBundle = wrapQuery(query);
return await this._select(metadatas, queryBundle);
}
abstract _select(
choices: ToolMetadataOnlyDescription[],
query: QueryBundle,
): Promise<SelectorResult>;
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./base";
export * from "./llmSelectors";
export * from "./utils";
+168
View File
@@ -0,0 +1,168 @@
import { DefaultPromptTemplate } from "../extractors/prompts";
import { LLM } from "../llm";
import { Answer, SelectionOutputParser } from "../outputParsers/selectors";
import {
BaseOutputParser,
QueryBundle,
StructuredOutput,
ToolMetadataOnlyDescription,
} from "../types";
import { BaseSelector, SelectorResult } from "./base";
import { defaultSingleSelectPrompt } from "./prompts";
function buildChoicesText(choices: ToolMetadataOnlyDescription[]): string {
const texts: string[] = [];
for (const [ind, choice] of choices.entries()) {
let text = choice.description.split("\n").join(" ");
text = `(${ind + 1}) ${text}`; // to one indexing
texts.push(text);
}
return texts.join("");
}
function _structuredOutputToSelectorResult(
output: StructuredOutput<Answer[]>,
): SelectorResult {
const structuredOutput = output;
const answers = structuredOutput.parsedOutput;
// adjust for zero indexing
const selections = answers.map((answer: any) => {
return { index: answer.choice - 1, reason: answer.reason };
});
return { selections };
}
type LLMPredictorType = LLM;
/**
* A selector that uses the LLM to select a single or multiple choices from a list of choices.
*/
export class LLMMultiSelector extends BaseSelector {
_llm: LLMPredictorType;
_prompt: DefaultPromptTemplate | undefined;
_maxOutputs: number | null;
_outputParser: BaseOutputParser<any> | null;
constructor(init: {
llm: LLMPredictorType;
prompt?: DefaultPromptTemplate;
maxOutputs?: number;
outputParser?: BaseOutputParser<any>;
}) {
super();
this._llm = init.llm;
this._prompt = init.prompt;
this._maxOutputs = init.maxOutputs ?? null;
this._outputParser = init.outputParser ?? new SelectionOutputParser();
}
_getPrompts(): Record<string, any> {
return { prompt: this._prompt };
}
_updatePrompts(prompts: Record<string, any>): void {
if ("prompt" in prompts) {
this._prompt = prompts.prompt;
}
}
/**
* Selects a single choice from a list of choices.
* @param choices
* @param query
*/
async _select(
choices: ToolMetadataOnlyDescription[],
query: QueryBundle,
): Promise<SelectorResult> {
const choicesText = buildChoicesText(choices);
const prompt =
this._prompt?.contextStr ??
defaultSingleSelectPrompt(
choicesText.length,
choicesText,
query.queryStr,
);
const formattedPrompt = this._outputParser?.format(prompt);
const prediction = await this._llm.complete({
prompt: formattedPrompt,
});
const parsed = this._outputParser?.parse(prediction.text);
return _structuredOutputToSelectorResult(parsed);
}
asQueryComponent(): unknown {
throw new Error("Method not implemented.");
}
}
/**
* A selector that uses the LLM to select a single choice from a list of choices.
*/
export class LLMSingleSelector extends BaseSelector {
_llm: LLMPredictorType;
_prompt: DefaultPromptTemplate | undefined;
_outputParser: BaseOutputParser<any> | null;
constructor(init: {
llm: LLMPredictorType;
prompt?: DefaultPromptTemplate;
outputParser?: BaseOutputParser<any>;
}) {
super();
this._llm = init.llm;
this._prompt = init.prompt;
this._outputParser = init.outputParser ?? new SelectionOutputParser();
}
_getPrompts(): Record<string, any> {
return { prompt: this._prompt };
}
_updatePrompts(prompts: Record<string, any>): void {
if ("prompt" in prompts) {
this._prompt = prompts.prompt;
}
}
/**
* Selects a single choice from a list of choices.
* @param choices
* @param query
*/
async _select(
choices: ToolMetadataOnlyDescription[],
query: QueryBundle,
): Promise<SelectorResult> {
const choicesText = buildChoicesText(choices);
const prompt =
this._prompt?.contextStr ??
defaultSingleSelectPrompt(
choicesText.length,
choicesText,
query.queryStr,
);
const formattedPrompt = this._outputParser?.format(prompt);
const prediction = await this._llm.complete({
prompt: formattedPrompt,
});
const parsed = this._outputParser?.parse(prediction.text);
return _structuredOutputToSelectorResult(parsed);
}
asQueryComponent(): unknown {
throw new Error("Method not implemented.");
}
}

Some files were not shown because too many files have changed in this diff Show More