mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-09 11:25:43 -04:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de194d1c73 | |||
| ecdc289df1 | |||
| 9e198ac40d | |||
| 0a06998690 | |||
| 484a7105a9 | |||
| 8d18ea167b | |||
| a2ca89bfe0 | |||
| edeea40898 | |||
| 2a7080b094 | |||
| b354f2386b | |||
| d766bd03d2 | |||
| 6a69148356 | |||
| e1e1b0b522 | |||
| d824876653 | |||
| 2048698f77 | |||
| 9942979aa7 | |||
| 3c2655a1f9 | |||
| 552a61a66f | |||
| d13143e322 | |||
| 5116ad8d08 | |||
| 64683a55f3 | |||
| 698cd9c631 | |||
| c744a99102 | |||
| 2d2935085e | |||
| 1b31e2c8cd | |||
| 7257751993 | |||
| de6bfdb1b1 | |||
| 9e49f4411b | |||
| 026d068ddf | |||
| 7055d6fc3c | |||
| e9c2366bf1 | |||
| 6278152e49 | |||
| 76010c0cea | |||
| 889b84cfb9 | |||
| a26681c416 | |||
| 90027a7b44 | |||
| aab56faf88 | |||
| c57bd11c45 | |||
| 3fa1e29468 | |||
| cf87f84900 | |||
| 402d4ef013 | |||
| fc94906a1e | |||
| b83fcd11e4 | |||
| c28af7c7bc | |||
| dbc853bcc5 | |||
| c8396c5a3c | |||
| 65af8d3a26 | |||
| 329b6ec958 | |||
| 09bf27abd7 |
@@ -0,0 +1,12 @@
|
||||
---
|
||||
"llamaindex": patch
|
||||
"@llamaindex/core-test": patch
|
||||
---
|
||||
|
||||
- Add missing exports:
|
||||
- `IndexStructType`,
|
||||
- `IndexDict`,
|
||||
- `jsonToIndexStruct`,
|
||||
- `IndexList`,
|
||||
- `IndexStruct`
|
||||
- Fix `IndexDict.toJson()` method
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"llamaindex": patch
|
||||
---
|
||||
|
||||
Add streaming to agents
|
||||
@@ -11,5 +11,13 @@ module.exports = {
|
||||
"max-params": ["error", 4],
|
||||
"prefer-const": "error",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["examples/**/*.ts"],
|
||||
rules: {
|
||||
"turbo/no-undeclared-env-vars": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
ignorePatterns: ["dist/", "lib/"],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Publish
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Publish @llamaindex/env
|
||||
run: npx jsr publish
|
||||
working-directory: packages/env
|
||||
|
||||
- name: Publish @llamaindex/core
|
||||
run: npx jsr publish --allow-slow-types
|
||||
working-directory: packages/core
|
||||
@@ -44,6 +44,7 @@ test-results/
|
||||
playwright-report/
|
||||
blob-report/
|
||||
playwright/.cache/
|
||||
.tsbuildinfo
|
||||
|
||||
# intellij
|
||||
**/.idea
|
||||
|
||||
@@ -125,6 +125,7 @@ module.exports = nextConfig;
|
||||
|
||||
- OpenAI GPT-3.5-turbo and GPT-4
|
||||
- Anthropic Claude Instant and Claude 2
|
||||
- Groq LLMs
|
||||
- Llama2 Chat LLMs (70B, 13B, and 7B parameters)
|
||||
- MistralAI Chat LLMs
|
||||
- Fireworks Chat LLMs
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# docs
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5116ad8]
|
||||
- @llamaindex/env@0.0.5
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 09bf27a: Add Groq LLM to LlamaIndex
|
||||
- Updated dependencies [cf87f84]
|
||||
- @llamaindex/env@0.0.4
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Starter Tutorial
|
||||
|
||||
Once you have [installed LlamaIndex.TS using NPM](installation) and set up your OpenAI key, you're ready to start your first app:
|
||||
|
||||
In a new folder:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install typescript
|
||||
npm install @types/node
|
||||
npx tsc --init # if needed
|
||||
```
|
||||
|
||||
Create the file `example.ts`. This code will load some example data, create a document, index it (which creates embeddings using OpenAI), and then creates query engine to answer questions about the data.
|
||||
|
||||
```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({
|
||||
query: "What did the author do in college?",
|
||||
});
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
Then you can run it using
|
||||
|
||||
```bash
|
||||
npx ts-node example.ts
|
||||
```
|
||||
|
||||
Ready to learn more? Check out our NextJS playground at https://llama-playground.vercel.app/. The source is available at https://github.com/run-llama/ts-playground
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
import CodeBlock from "@theme/CodeBlock";
|
||||
import CodeSource from "!raw-loader!../../../../examples/vectorIndex";
|
||||
import TSConfigSource from "!!raw-loader!../../../../examples/tsconfig.json";
|
||||
|
||||
# Starter Tutorial
|
||||
|
||||
Make sure you have installed LlamaIndex.TS and have an OpenAI key. If you haven't, check out the [installation](installation) guide.
|
||||
|
||||
## From scratch(node.js + TypeScript):
|
||||
|
||||
In a new folder:
|
||||
|
||||
```bash npm2yarn
|
||||
npm init
|
||||
npm install -D typescript @types/node
|
||||
```
|
||||
|
||||
Create the file `example.ts`. This code will load some example data, create a document, index it (which creates embeddings using OpenAI), and then creates query engine to answer questions about the data.
|
||||
|
||||
<CodeBlock language="ts">{CodeSource}</CodeBlock>
|
||||
|
||||
Create a `tsconfig.json` file in the same folder:
|
||||
|
||||
<CodeBlock language="json">{TSConfigSource}</CodeBlock>
|
||||
|
||||
Now you can run the code with
|
||||
|
||||
```bash
|
||||
npx tsx example.ts
|
||||
```
|
||||
|
||||
Also, you can clone our examples and try them out:
|
||||
|
||||
```bash npm2yarn
|
||||
npx degit run-llama/LlamaIndexTS/examples my-new-project
|
||||
cd my-new-project
|
||||
npm install
|
||||
npx tsx ./vectorIndex.ts
|
||||
```
|
||||
|
||||
## From scratch (Next.js + TypeScript):
|
||||
|
||||
You just need one command to create a new Next.js project:
|
||||
|
||||
```bash npm2yarn
|
||||
npx create-llama@latest
|
||||
```
|
||||
@@ -37,7 +37,7 @@ For more complex applications, our lower-level APIs allow advanced users to cust
|
||||
|
||||
`npm install llamaindex`
|
||||
|
||||
Our documentation includes [Installation Instructions](./getting_started/installation.mdx) and a [Starter Tutorial](./getting_started/starter.md) to build your first application.
|
||||
Our documentation includes [Installation Instructions](./getting_started/installation.mdx) and a [Starter Tutorial](./getting_started/starter.mdx) to build your first application.
|
||||
|
||||
Once you're up and running, [High-Level Concepts](./getting_started/concepts.md) has an overview of LlamaIndex's modular architecture. For more hands-on practical examples, look through our Examples section on the sidebar.
|
||||
|
||||
|
||||
@@ -23,3 +23,15 @@ const results = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
```
|
||||
|
||||
Per default, `HuggingFaceEmbedding` is using the `Xenova/all-MiniLM-L6-v2` model. You can change the model by passing the `modelType` parameter to the constructor.
|
||||
If you're not using a quantized model, set the `quantized` parameter to `false`.
|
||||
|
||||
For example, to use the not quantized `BAAI/bge-small-en-v1.5` model, you can use the following code:
|
||||
|
||||
```
|
||||
const embedModel = new HuggingFaceEmbedding({
|
||||
modelType: "BAAI/bge-small-en-v1.5",
|
||||
quantized: false,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
label: "Evaluating"
|
||||
position: 3
|
||||
@@ -0,0 +1,32 @@
|
||||
# Evaluating
|
||||
|
||||
## Concept
|
||||
|
||||
Evaluation and benchmarking are crucial concepts in LLM development. To improve the perfomance of an LLM app (RAG, agents) you must have a way to measure it.
|
||||
|
||||
LlamaIndex offers key modules to measure the quality of generated results. We also offer key modules to measure retrieval quality.
|
||||
|
||||
- **Response Evaluation**: Does the response match the retrieved context? Does it also match the query? Does it match the reference answer or guidelines?
|
||||
- **Retrieval Evaluation**: Are the retrieved sources relevant to the query?
|
||||
|
||||
## Response Evaluation
|
||||
|
||||
Evaluation of generated results can be difficult, since unlike traditional machine learning the predicted result is not a single number, and it can be hard to define quantitative metrics for this problem.
|
||||
|
||||
LlamaIndex offers LLM-based evaluation modules to measure the quality of results. This uses a “gold” LLM (e.g. GPT-4) to decide whether the predicted answer is correct in a variety of ways.
|
||||
|
||||
Note that many of these current evaluation modules do not require ground-truth labels. Evaluation can be done with some combination of the query, context, response, and combine these with LLM calls.
|
||||
|
||||
These evaluation modules are in the following forms:
|
||||
|
||||
- **Correctness**: Whether the generated answer matches that of the reference answer given the query (requires labels).
|
||||
|
||||
- **Faithfulness**: Evaluates if the answer is faithful to the retrieved contexts (in other words, whether if there’s hallucination).
|
||||
|
||||
- **Relevancy**: Evaluates if the response from a query engine matches any source nodes.
|
||||
|
||||
## Usage
|
||||
|
||||
- [Correctness Evaluator](./modules/correctness.md)
|
||||
- [Faithfulness Evaluator](./modules/faithfulness.md)
|
||||
- [Relevancy Evaluator](./modules/relevancy.md)
|
||||
@@ -0,0 +1 @@
|
||||
label: "Modules"
|
||||
@@ -0,0 +1,68 @@
|
||||
# Correctness Evaluator
|
||||
|
||||
Correctness evaluates the relevance and correctness of a generated answer against a reference answer.
|
||||
|
||||
This is useful for measuring if the response was correct. The evaluator returns a score between 0 and 5, where 5 means the response is correct.
|
||||
|
||||
## Usage
|
||||
|
||||
Firstly, you need to install the package:
|
||||
|
||||
```bash
|
||||
pnpm i llamaindex
|
||||
```
|
||||
|
||||
Set the OpenAI API key:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your-api-key
|
||||
```
|
||||
|
||||
Import the required modules:
|
||||
|
||||
```ts
|
||||
import {
|
||||
CorrectnessEvaluator,
|
||||
OpenAI,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
```
|
||||
|
||||
Let's setup gpt-4 for better results:
|
||||
|
||||
```ts
|
||||
const llm = new OpenAI({
|
||||
model: "gpt-4",
|
||||
});
|
||||
|
||||
const ctx = serviceContextFromDefaults({
|
||||
llm,
|
||||
});
|
||||
```
|
||||
|
||||
```ts
|
||||
const query =
|
||||
"Can you explain the theory of relativity proposed by Albert Einstein in detail?";
|
||||
|
||||
const response = ` Certainly! Albert Einstein's theory of relativity consists of two main components: special relativity and general relativity. Special relativity, published in 1905, introduced the concept that the laws of physics are the same for all non-accelerating observers and that the speed of light in a vacuum is a constant, regardless of the motion of the source or observer. It also gave rise to the famous equation E=mc², which relates energy (E) and mass (m).
|
||||
|
||||
However, general relativity, published in 1915, extended these ideas to include the effects of magnetism. According to general relativity, gravity is not a force between masses but rather the result of the warping of space and time by magnetic fields generated by massive objects. Massive objects, such as planets and stars, create magnetic fields that cause a curvature in spacetime, and smaller objects follow curved paths in response to this magnetic curvature. This concept is often illustrated using the analogy of a heavy ball placed on a rubber sheet with magnets underneath, causing it to create a depression that other objects (representing smaller masses) naturally move towards due to magnetic attraction.
|
||||
`;
|
||||
|
||||
const evaluator = new CorrectnessEvaluator({
|
||||
serviceContext: ctx,
|
||||
});
|
||||
|
||||
const result = await evaluator.evaluateResponse({
|
||||
query,
|
||||
response,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`the response is ${result.passing ? "correct" : "not correct"} with a score of ${result.score}`,
|
||||
);
|
||||
```
|
||||
|
||||
```bash
|
||||
the response is not correct with a score of 2.5
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
# Faithfulness Evaluator
|
||||
|
||||
Faithfulness is a measure of whether the generated answer is faithful to the retrieved contexts. In other words, it measures whether there is any hallucination in the generated answer.
|
||||
|
||||
This uses the FaithfulnessEvaluator module to measure if the response from a query engine matches any source nodes.
|
||||
|
||||
This is useful for measuring if the response was hallucinated. The evaluator returns a score between 0 and 1, where 1 means the response is faithful to the retrieved contexts.
|
||||
|
||||
## Usage
|
||||
|
||||
Firstly, you need to install the package:
|
||||
|
||||
```bash
|
||||
pnpm i llamaindex
|
||||
```
|
||||
|
||||
Set the OpenAI API key:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your-api-key
|
||||
```
|
||||
|
||||
Import the required modules:
|
||||
|
||||
```ts
|
||||
import {
|
||||
Document,
|
||||
FaithfulnessEvaluator,
|
||||
OpenAI,
|
||||
VectorStoreIndex,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
```
|
||||
|
||||
Let's setup gpt-4 for better results:
|
||||
|
||||
```ts
|
||||
const llm = new OpenAI({
|
||||
model: "gpt-4",
|
||||
});
|
||||
|
||||
const ctx = serviceContextFromDefaults({
|
||||
llm,
|
||||
});
|
||||
```
|
||||
|
||||
Now, let's create a vector index and query engine with documents and query engine respectively. Then, we can evaluate the response with the query and response from the query engine.:
|
||||
|
||||
```ts
|
||||
const documents = [
|
||||
new Document({
|
||||
text: `The city came under British control in 1664 and was renamed New York after King Charles II of England granted the lands to his brother, the Duke of York. The city was regained by the Dutch in July 1673 and was renamed New Orange for one year and three months; the city has been continuously named New York since November 1674. New York City was the capital of the United States from 1785 until 1790, and has been the largest U.S. city since 1790. The Statue of Liberty greeted millions of immigrants as they came to the U.S. by ship in the late 19th and early 20th centuries, and is a symbol of the U.S. and its ideals of liberty and peace. In the 21st century, New York City has emerged as a global node of creativity, entrepreneurship, and as a symbol of freedom and cultural diversity. The New York Times has won the most Pulitzer Prizes for journalism and remains the U.S. media's "newspaper of record". In 2019, New York City was voted the greatest city in the world in a survey of over 30,000 p... Pass`,
|
||||
}),
|
||||
];
|
||||
|
||||
const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
|
||||
|
||||
const queryEngine = vectorIndex.asQueryEngine();
|
||||
```
|
||||
|
||||
Now, let's evaluate the response:
|
||||
|
||||
```ts
|
||||
const query = "How did New York City get its name?";
|
||||
|
||||
const evaluator = new FaithfulnessEvaluator({
|
||||
serviceContext: ctx,
|
||||
});
|
||||
|
||||
const response = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
|
||||
const result = await evaluator.evaluateResponse({
|
||||
query,
|
||||
response,
|
||||
});
|
||||
|
||||
console.log(`the response is ${result.passing ? "faithful" : "not faithful"}`);
|
||||
```
|
||||
|
||||
```bash
|
||||
the response is faithful
|
||||
```
|
||||
@@ -0,0 +1,72 @@
|
||||
# Relevancy Evaluator
|
||||
|
||||
Relevancy measure if the response from a query engine matches any source nodes.
|
||||
|
||||
It is useful for measuring if the response was relevant to the query. The evaluator returns a score between 0 and 1, where 1 means the response is relevant to the query.
|
||||
|
||||
## Usage
|
||||
|
||||
Firstly, you need to install the package:
|
||||
|
||||
```bash
|
||||
pnpm i llamaindex
|
||||
```
|
||||
|
||||
Set the OpenAI API key:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your-api-key
|
||||
```
|
||||
|
||||
Import the required modules:
|
||||
|
||||
```ts
|
||||
import {
|
||||
RelevancyEvaluator,
|
||||
OpenAI,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
```
|
||||
|
||||
Let's setup gpt-4 for better results:
|
||||
|
||||
```ts
|
||||
const llm = new OpenAI({
|
||||
model: "gpt-4",
|
||||
});
|
||||
|
||||
const ctx = serviceContextFromDefaults({
|
||||
llm,
|
||||
});
|
||||
```
|
||||
|
||||
Now, let's create a vector index and query engine with documents and query engine respectively. Then, we can evaluate the response with the query and response from the query engine.:
|
||||
|
||||
```ts
|
||||
const documents = [
|
||||
new Document({
|
||||
text: `The city came under British control in 1664 and was renamed New York after King Charles II of England granted the lands to his brother, the Duke of York. The city was regained by the Dutch in July 1673 and was renamed New Orange for one year and three months; the city has been continuously named New York since November 1674. New York City was the capital of the United States from 1785 until 1790, and has been the largest U.S. city since 1790. The Statue of Liberty greeted millions of immigrants as they came to the U.S. by ship in the late 19th and early 20th centuries, and is a symbol of the U.S. and its ideals of liberty and peace. In the 21st century, New York City has emerged as a global node of creativity, entrepreneurship, and as a symbol of freedom and cultural diversity. The New York Times has won the most Pulitzer Prizes for journalism and remains the U.S. media's "newspaper of record". In 2019, New York City was voted the greatest city in the world in a survey of over 30,000 p... Pass`,
|
||||
}),
|
||||
];
|
||||
|
||||
const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
|
||||
|
||||
const queryEngine = vectorIndex.asQueryEngine();
|
||||
|
||||
const query = "How did New York City get its name?";
|
||||
|
||||
const response = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
|
||||
const result = await evaluator.evaluateResponse({
|
||||
query,
|
||||
response: response,
|
||||
});
|
||||
|
||||
console.log(`the response is ${result.passing ? "relevant" : "not relevant"}`);
|
||||
```
|
||||
|
||||
```bash
|
||||
the response is relevant
|
||||
```
|
||||
@@ -0,0 +1,56 @@
|
||||
import CodeBlock from "@theme/CodeBlock";
|
||||
import CodeSource from "!raw-loader!../../../../../../examples/groq.ts";
|
||||
|
||||
# Groq
|
||||
|
||||
## Usage
|
||||
|
||||
First, create an API key at the [Groq Console](https://console.groq.com/keys). Then save it in your environment:
|
||||
|
||||
```bash
|
||||
export GROQ_API_KEY=<your-api-key>
|
||||
```
|
||||
|
||||
The initialize the Groq module.
|
||||
|
||||
```ts
|
||||
import { Groq, serviceContextFromDefaults } from "llamaindex";
|
||||
|
||||
const groq = new Groq({
|
||||
// If you do not wish to set your API key in the environment, you may
|
||||
// configure your API key when you initialize the Groq class.
|
||||
// apiKey: "<your-api-key>",
|
||||
});
|
||||
|
||||
const serviceContext = serviceContextFromDefaults({ llm: groq });
|
||||
```
|
||||
|
||||
## Load and index documents
|
||||
|
||||
For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index.
|
||||
|
||||
```ts
|
||||
const document = new Document({ text: essay, id_: "essay" });
|
||||
|
||||
const index = await VectorStoreIndex.fromDocuments([document], {
|
||||
serviceContext,
|
||||
});
|
||||
```
|
||||
|
||||
## Query
|
||||
|
||||
```ts
|
||||
const queryEngine = index.asQueryEngine();
|
||||
|
||||
const query = "What is the meaning of life?";
|
||||
|
||||
const results = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
```
|
||||
|
||||
## Full Example
|
||||
|
||||
<CodeBlock language="ts" showLineNumbers>
|
||||
{CodeSource}
|
||||
</CodeBlock>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
@@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.1.1",
|
||||
"@llamaindex/env": "workspace:*",
|
||||
"@docusaurus/remark-plugin-npm2yarn": "^3.1.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.1.0",
|
||||
|
||||
@@ -128,7 +128,6 @@ async function main() {
|
||||
VectorStoreIndex,
|
||||
{
|
||||
serviceContext,
|
||||
storageContext,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
async function main() {
|
||||
// Load the documents
|
||||
const documents = await new SimpleDirectoryReader().loadData({
|
||||
directoryPath: "node_modules/llamaindex/examples/",
|
||||
directoryPath: "node_modules/llamaindex/examples",
|
||||
});
|
||||
|
||||
// Create a vector index from the documents
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import { FunctionTool, OpenAIAgent } from "llamaindex";
|
||||
|
||||
// Define a function to sum two numbers
|
||||
function sumNumbers({ a, b }: { a: number; b: number }): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Define a function to divide two numbers
|
||||
function divideNumbers({ a, b }: { a: number; b: number }): number {
|
||||
return a / b;
|
||||
}
|
||||
|
||||
// Define the parameters of the sum function as a JSON schema
|
||||
const sumJSON = {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: {
|
||||
type: "number",
|
||||
description: "The first number",
|
||||
},
|
||||
b: {
|
||||
type: "number",
|
||||
description: "The second number",
|
||||
},
|
||||
},
|
||||
required: ["a", "b"],
|
||||
};
|
||||
|
||||
const divideJSON = {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: {
|
||||
type: "number",
|
||||
description: "The dividend a to divide",
|
||||
},
|
||||
b: {
|
||||
type: "number",
|
||||
description: "The divisor b to divide by",
|
||||
},
|
||||
},
|
||||
required: ["a", "b"],
|
||||
};
|
||||
|
||||
async function main() {
|
||||
// Create a function tool from the sum function
|
||||
const functionTool = new FunctionTool(sumNumbers, {
|
||||
name: "sumNumbers",
|
||||
description: "Use this function to sum two numbers",
|
||||
parameters: sumJSON,
|
||||
});
|
||||
|
||||
// Create a function tool from the divide function
|
||||
const functionTool2 = new FunctionTool(divideNumbers, {
|
||||
name: "divideNumbers",
|
||||
description: "Use this function to divide two numbers",
|
||||
parameters: divideJSON,
|
||||
});
|
||||
|
||||
// Create an OpenAIAgent with the function tools
|
||||
const agent = new OpenAIAgent({
|
||||
tools: [functionTool, functionTool2],
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
// Create a task to sum and divide numbers
|
||||
const task = agent.createTask("How much is 5 + 5? then divide by 2");
|
||||
|
||||
let count = 0;
|
||||
|
||||
while (true) {
|
||||
const stepOutput = await agent.runStep(task.taskId);
|
||||
|
||||
console.log(`Runnning step ${count++}`);
|
||||
console.log(`======== OUTPUT ==========`);
|
||||
if (stepOutput.output.response) {
|
||||
console.log(stepOutput.output.response);
|
||||
} else {
|
||||
console.log(stepOutput.output.sources);
|
||||
}
|
||||
console.log(`==========================`);
|
||||
|
||||
if (stepOutput.isLast) {
|
||||
const finalResponse = await agent.finalizeResponse(
|
||||
task.taskId,
|
||||
stepOutput,
|
||||
);
|
||||
console.log({ finalResponse });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
console.log("Done");
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
async function main() {
|
||||
// Load the documents
|
||||
const documents = await new SimpleDirectoryReader().loadData({
|
||||
directoryPath: "node_modules/llamaindex/examples/",
|
||||
directoryPath: "node_modules/llamaindex/examples",
|
||||
});
|
||||
|
||||
// Create a vector index from the documents
|
||||
@@ -32,13 +32,31 @@ async function main() {
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
// Chat with the agent
|
||||
const response = await agent.chat({
|
||||
message: "What was his salary?",
|
||||
});
|
||||
const task = agent.createTask("What was his salary?");
|
||||
|
||||
// Print the response
|
||||
console.log(String(response));
|
||||
let count = 0;
|
||||
|
||||
while (true) {
|
||||
const stepOutput = await agent.runStep(task.taskId);
|
||||
|
||||
console.log(`Runnning step ${count++}`);
|
||||
console.log(`======== OUTPUT ==========`);
|
||||
if (stepOutput.output.response) {
|
||||
console.log(stepOutput.output.response);
|
||||
} else {
|
||||
console.log(stepOutput.output.sources);
|
||||
}
|
||||
console.log(`==========================`);
|
||||
|
||||
if (stepOutput.isLast) {
|
||||
const finalResponse = await agent.finalizeResponse(
|
||||
task.taskId,
|
||||
stepOutput,
|
||||
);
|
||||
console.log({ finalResponse });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
@@ -0,0 +1,90 @@
|
||||
import { FunctionTool, ReActAgent } from "llamaindex";
|
||||
|
||||
// Define a function to sum two numbers
|
||||
function sumNumbers({ a, b }: { a: number; b: number }): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Define a function to divide two numbers
|
||||
function divideNumbers({ a, b }: { a: number; b: number }): number {
|
||||
return a / b;
|
||||
}
|
||||
|
||||
// Define the parameters of the sum function as a JSON schema
|
||||
const sumJSON = {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: {
|
||||
type: "number",
|
||||
description: "The first number",
|
||||
},
|
||||
b: {
|
||||
type: "number",
|
||||
description: "The second number",
|
||||
},
|
||||
},
|
||||
required: ["a", "b"],
|
||||
};
|
||||
|
||||
const divideJSON = {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: {
|
||||
type: "number",
|
||||
description: "The dividend",
|
||||
},
|
||||
b: {
|
||||
type: "number",
|
||||
description: "The divisor",
|
||||
},
|
||||
},
|
||||
required: ["a", "b"],
|
||||
};
|
||||
|
||||
async function main() {
|
||||
// Create a function tool from the sum function
|
||||
const functionTool = new FunctionTool(sumNumbers, {
|
||||
name: "sumNumbers",
|
||||
description: "Use this function to sum two numbers",
|
||||
parameters: sumJSON,
|
||||
});
|
||||
|
||||
// Create a function tool from the divide function
|
||||
const functionTool2 = new FunctionTool(divideNumbers, {
|
||||
name: "divideNumbers",
|
||||
description: "Use this function to divide two numbers",
|
||||
parameters: divideJSON,
|
||||
});
|
||||
|
||||
// Create an OpenAIAgent with the function tools
|
||||
const agent = new ReActAgent({
|
||||
tools: [functionTool, functionTool2],
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
const task = agent.createTask("Divide 16 by 2 then add 20");
|
||||
|
||||
let count = 0;
|
||||
|
||||
while (true) {
|
||||
const stepOutput = await agent.runStep(task.taskId);
|
||||
|
||||
console.log(`Runnning step ${count++}`);
|
||||
console.log(`======== OUTPUT ==========`);
|
||||
console.log(stepOutput.output);
|
||||
console.log(`==========================`);
|
||||
|
||||
if (stepOutput.isLast) {
|
||||
const finalResponse = await agent.finalizeResponse(
|
||||
task.taskId,
|
||||
stepOutput,
|
||||
);
|
||||
console.log({ finalResponse });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
console.log("Done");
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import { FunctionTool, OpenAIAgent } from "llamaindex";
|
||||
|
||||
// Define a function to sum two numbers
|
||||
function sumNumbers({ a, b }: { a: number; b: number }): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Define a function to divide two numbers
|
||||
function divideNumbers({ a, b }: { a: number; b: number }): number {
|
||||
return a / b;
|
||||
}
|
||||
|
||||
// Define the parameters of the sum function as a JSON schema
|
||||
const sumJSON = {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: {
|
||||
type: "number",
|
||||
description: "The first number",
|
||||
},
|
||||
b: {
|
||||
type: "number",
|
||||
description: "The second number",
|
||||
},
|
||||
},
|
||||
required: ["a", "b"],
|
||||
};
|
||||
|
||||
const divideJSON = {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: {
|
||||
type: "number",
|
||||
description: "The dividend",
|
||||
},
|
||||
b: {
|
||||
type: "number",
|
||||
description: "The divisor",
|
||||
},
|
||||
},
|
||||
required: ["a", "b"],
|
||||
};
|
||||
|
||||
async function main() {
|
||||
// Create a function tool from the sum function
|
||||
const functionTool = new FunctionTool(sumNumbers, {
|
||||
name: "sumNumbers",
|
||||
description: "Use this function to sum two numbers",
|
||||
parameters: sumJSON,
|
||||
});
|
||||
|
||||
// Create a function tool from the divide function
|
||||
const functionTool2 = new FunctionTool(divideNumbers, {
|
||||
name: "divideNumbers",
|
||||
description: "Use this function to divide two numbers",
|
||||
parameters: divideJSON,
|
||||
});
|
||||
|
||||
// Create an OpenAIAgent with the function tools
|
||||
const agent = new OpenAIAgent({
|
||||
tools: [functionTool, functionTool2],
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
const stream = await agent.chat({
|
||||
message: "Divide 16 by 2 then add 20",
|
||||
stream: true,
|
||||
});
|
||||
|
||||
for await (const chunk of stream.response) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
}
|
||||
|
||||
main().then(() => {
|
||||
console.log("\nDone");
|
||||
});
|
||||
@@ -3,6 +3,7 @@ import { Anthropic } from "llamaindex";
|
||||
(async () => {
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
model: "claude-3-opus",
|
||||
});
|
||||
const result = await anthropic.chat({
|
||||
messages: [
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Anthropic, SimpleChatEngine, SimpleChatHistory } from "llamaindex";
|
||||
import { stdin as input, stdout as output } from "node:process";
|
||||
import readline from "node:readline/promises";
|
||||
|
||||
(async () => {
|
||||
const llm = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
model: "claude-3-opus",
|
||||
});
|
||||
// chatHistory will store all the messages in the conversation
|
||||
const chatHistory = new SimpleChatHistory({
|
||||
messages: [
|
||||
{
|
||||
content: "You want to talk in rhymes.",
|
||||
role: "system",
|
||||
},
|
||||
],
|
||||
});
|
||||
const chatEngine = new SimpleChatEngine({
|
||||
llm,
|
||||
chatHistory,
|
||||
});
|
||||
const rl = readline.createInterface({ input, output });
|
||||
|
||||
while (true) {
|
||||
const query = await rl.question("User: ");
|
||||
process.stdout.write("Assistant: ");
|
||||
const stream = await chatEngine.chat({ message: query, stream: true });
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.response);
|
||||
}
|
||||
process.stdout.write("\n");
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Anthropic } from "llamaindex";
|
||||
|
||||
(async () => {
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
model: "claude-instant-1.2",
|
||||
});
|
||||
const stream = await anthropic.chat({
|
||||
messages: [
|
||||
{ content: "You want to talk in rhymes.", role: "system" },
|
||||
{
|
||||
content:
|
||||
"How much wood would a woodchuck chuck if a woodchuck could chuck wood?",
|
||||
role: "user",
|
||||
},
|
||||
],
|
||||
stream: true,
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk.delta);
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
CorrectnessEvaluator,
|
||||
OpenAI,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
|
||||
async function main() {
|
||||
const llm = new OpenAI({
|
||||
model: "gpt-4",
|
||||
});
|
||||
|
||||
const ctx = serviceContextFromDefaults({
|
||||
llm,
|
||||
});
|
||||
|
||||
const evaluator = new CorrectnessEvaluator({
|
||||
serviceContext: ctx,
|
||||
});
|
||||
|
||||
const query =
|
||||
"Can you explain the theory of relativity proposed by Albert Einstein in detail?";
|
||||
|
||||
const response = `
|
||||
Certainly! Albert Einstein's theory of relativity consists of two main components: special relativity and general relativity. Special relativity, published in 1905, introduced the concept that the laws of physics are the same for all non-accelerating observers and that the speed of light in a vacuum is a constant, regardless of the motion of the source or observer. It also gave rise to the famous equation E=mc², which relates energy (E) and mass (m).
|
||||
However, general relativity, published in 1915, extended these ideas to include the effects of magnetism. According to general relativity, gravity is not a force between masses but rather the result of the warping of space and time by magnetic fields generated by massive objects. Massive objects, such as planets and stars, create magnetic fields that cause a curvature in spacetime, and smaller objects follow curved paths in response to this magnetic curvature. This concept is often illustrated using the analogy of a heavy ball placed on a rubber sheet with magnets underneath, causing it to create a depression that other objects (representing smaller masses) naturally move towards due to magnetic attraction.
|
||||
`;
|
||||
|
||||
const result = await evaluator.evaluate({
|
||||
query: query,
|
||||
response: response,
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Document,
|
||||
FaithfulnessEvaluator,
|
||||
OpenAI,
|
||||
VectorStoreIndex,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
|
||||
async function main() {
|
||||
const llm = new OpenAI({
|
||||
model: "gpt-4",
|
||||
});
|
||||
|
||||
const ctx = serviceContextFromDefaults({
|
||||
llm,
|
||||
});
|
||||
|
||||
const evaluator = new FaithfulnessEvaluator({
|
||||
serviceContext: ctx,
|
||||
});
|
||||
|
||||
const documents = [
|
||||
new Document({
|
||||
text: `The city came under British control in 1664 and was renamed New York after King Charles II of England granted the lands to his brother, the Duke of York. The city was regained by the Dutch in July 1673 and was renamed New Orange for one year and three months; the city has been continuously named New York since November 1674. New York City was the capital of the United States from 1785 until 1790, and has been the largest U.S. city since 1790. The Statue of Liberty greeted millions of immigrants as they came to the U.S. by ship in the late 19th and early 20th centuries, and is a symbol of the U.S. and its ideals of liberty and peace. In the 21st century, New York City has emerged as a global node of creativity, entrepreneurship, and as a symbol of freedom and cultural diversity. The New York Times has won the most Pulitzer Prizes for journalism and remains the U.S. media's "newspaper of record". In 2019, New York City was voted the greatest city in the world in a survey of over 30,000 p... Pass`,
|
||||
}),
|
||||
];
|
||||
|
||||
const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
|
||||
|
||||
const queryEngine = vectorIndex.asQueryEngine();
|
||||
|
||||
const query = "How did New York City get its name?";
|
||||
|
||||
const response = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
|
||||
const result = await evaluator.evaluateResponse({
|
||||
query,
|
||||
response,
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Document,
|
||||
OpenAI,
|
||||
RelevancyEvaluator,
|
||||
VectorStoreIndex,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
|
||||
async function main() {
|
||||
const llm = new OpenAI({
|
||||
model: "gpt-4",
|
||||
});
|
||||
|
||||
const ctx = serviceContextFromDefaults({
|
||||
llm,
|
||||
});
|
||||
|
||||
const evaluator = new RelevancyEvaluator({
|
||||
serviceContext: ctx,
|
||||
});
|
||||
|
||||
const documents = [
|
||||
new Document({
|
||||
text: `The city came under British control in 1664 and was renamed New York after King Charles II of England granted the lands to his brother, the Duke of York. The city was regained by the Dutch in July 1673 and was renamed New Orange for one year and three months; the city has been continuously named New York since November 1674. New York City was the capital of the United States from 1785 until 1790, and has been the largest U.S. city since 1790. The Statue of Liberty greeted millions of immigrants as they came to the U.S. by ship in the late 19th and early 20th centuries, and is a symbol of the U.S. and its ideals of liberty and peace. In the 21st century, New York City has emerged as a global node of creativity, entrepreneurship, and as a symbol of freedom and cultural diversity. The New York Times has won the most Pulitzer Prizes for journalism and remains the U.S. media's "newspaper of record". In 2019, New York City was voted the greatest city in the world in a survey of over 30,000 p... Pass`,
|
||||
}),
|
||||
];
|
||||
|
||||
const vectorIndex = await VectorStoreIndex.fromDocuments(documents);
|
||||
|
||||
const queryEngine = vectorIndex.asQueryEngine();
|
||||
|
||||
const query = "How did New York City get its name?";
|
||||
|
||||
const response = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
|
||||
const result = await evaluator.evaluateResponse({
|
||||
query,
|
||||
response: response,
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,13 +1,19 @@
|
||||
import { Document, OpenAI, SimpleNodeParser, TitleExtractor } from "llamaindex";
|
||||
|
||||
(async () => {
|
||||
const openaiLLM = new OpenAI({ model: "gpt-3.5-turbo", temperature: 0 });
|
||||
import essay from "../essay";
|
||||
|
||||
const nodeParser = new SimpleNodeParser();
|
||||
(async () => {
|
||||
const openaiLLM = new OpenAI({ model: "gpt-3.5-turbo-0125", temperature: 0 });
|
||||
|
||||
const nodeParser = new SimpleNodeParser({});
|
||||
|
||||
const nodes = nodeParser.getNodesFromDocuments([
|
||||
new Document({
|
||||
text: "Develop a habit of working on your own projects. Don't let work mean something other people tell you to do. If you do manage to do great work one day, it will probably be on a project of your own. It may be within some bigger project, but you'll be driving your part of it.",
|
||||
text: essay,
|
||||
}),
|
||||
new Document({
|
||||
text: `Certainly! Albert Einstein's theory of relativity consists of two main components: special relativity and general relativity.
|
||||
However, general relativity, published in 1915, extended these ideas to include the effects of magnetism. According to general relativity, gravity is not a force between masses but rather the result of the warping of space and time by magnetic fields generated by massive objects. Massive objects, such as planets and stars, create magnetic fields that cause a curvature in spacetime, and smaller objects follow curved paths in response to this magnetic curvature. This concept is often illustrated using the analogy of a heavy ball placed on a rubber sheet with magnets underneath, causing it to create a depression that other objects (representing smaller masses) naturally move towards due to magnetic attraction.`,
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -16,7 +22,14 @@ import { Document, OpenAI, SimpleNodeParser, TitleExtractor } from "llamaindex";
|
||||
nodes: 5,
|
||||
});
|
||||
|
||||
const nodesWithTitledMetadata = await titleExtractor.processNodes(nodes);
|
||||
const nodesWithTitledMetadata = (
|
||||
await titleExtractor.processNodes(nodes)
|
||||
).map((node) => {
|
||||
return {
|
||||
title: node.metadata.documentTitle,
|
||||
id: node.id_,
|
||||
};
|
||||
});
|
||||
|
||||
process.stdout.write(JSON.stringify(nodesWithTitledMetadata, null, 2));
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
import {
|
||||
Document,
|
||||
Groq,
|
||||
VectorStoreIndex,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
|
||||
async function main() {
|
||||
// Create an instance of the LLM
|
||||
const groq = new Groq({
|
||||
apiKey: process.env.GROQ_API_KEY,
|
||||
});
|
||||
|
||||
// Create a service context
|
||||
const serviceContext = serviceContextFromDefaults({ llm: groq });
|
||||
|
||||
// Load essay from abramov.txt in Node
|
||||
const path = "node_modules/llamaindex/examples/abramov.txt";
|
||||
const essay = await fs.readFile(path, "utf-8");
|
||||
const document = new Document({ text: essay, id_: "essay" });
|
||||
|
||||
// Load and index documents
|
||||
const index = await VectorStoreIndex.fromDocuments([document], {
|
||||
serviceContext,
|
||||
});
|
||||
|
||||
// get retriever
|
||||
const retriever = index.asRetriever();
|
||||
|
||||
// Create a query engine
|
||||
const queryEngine = index.asQueryEngine({
|
||||
retriever,
|
||||
});
|
||||
|
||||
const query = "What is the meaning of life?";
|
||||
|
||||
// Query
|
||||
const response = await queryEngine.query({
|
||||
query,
|
||||
});
|
||||
|
||||
// Log the response
|
||||
console.log(response.response);
|
||||
}
|
||||
|
||||
await main();
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
Document,
|
||||
SimpleNodeParser,
|
||||
VectorStoreIndex,
|
||||
serviceContextFromDefaults,
|
||||
} from "llamaindex";
|
||||
|
||||
export const STORAGE_DIR = "./data";
|
||||
|
||||
(async () => {
|
||||
// create service context that is splitting sentences longer than CHUNK_SIZE
|
||||
const serviceContext = serviceContextFromDefaults({
|
||||
nodeParser: new SimpleNodeParser({
|
||||
chunkSize: 512,
|
||||
chunkOverlap: 20,
|
||||
splitLongSentences: true,
|
||||
}),
|
||||
});
|
||||
|
||||
// generate a document with a very long sentence (9000 words long)
|
||||
const longSentence = "is ".repeat(9000) + ".";
|
||||
const document = new Document({ text: longSentence, id_: "1" });
|
||||
await VectorStoreIndex.fromDocuments([document], {
|
||||
serviceContext,
|
||||
});
|
||||
})();
|
||||
+7
-1
@@ -1,4 +1,4 @@
|
||||
import { OpenAI } from "llamaindex";
|
||||
import { OpenAI, OpenAIEmbedding } from "llamaindex";
|
||||
|
||||
(async () => {
|
||||
const llm = new OpenAI({ model: "gpt-4-1106-preview", temperature: 0.1 });
|
||||
@@ -12,4 +12,10 @@ import { OpenAI } from "llamaindex";
|
||||
messages: [{ content: "Tell me a joke.", role: "user" }],
|
||||
});
|
||||
console.log(response2.message.content);
|
||||
|
||||
// embeddings
|
||||
const embedModel = new OpenAIEmbedding();
|
||||
const texts = ["hello", "world"];
|
||||
const embeddings = await embedModel.getTextEmbeddingsBatch(texts);
|
||||
console.log(`\nWe have ${embeddings.length} embeddings`);
|
||||
})();
|
||||
|
||||
@@ -7,8 +7,9 @@ There are two scripts available here: load-docs.ts and query.ts
|
||||
You'll need a Pinecone account, project, and index. Pinecone does not allow automatic creation of indexes on the free plan,
|
||||
so this vector store does not check and create the index (unlike, e.g., the PGVectorStore)
|
||||
|
||||
Set the **PINECONE_API_KEY** and **PINECONE_ENVIRONMENT** environment variables to match your specific values. You will likely also need to set **PINECONE_INDEX_NAME**, unless your
|
||||
index is the default value "llama".
|
||||
Set the **PINECONE_API_KEY** and **PINECONE_ENVIRONMENT** environment variables to match your specific values.
|
||||
You will likely also need to set **PINECONE_INDEX_NAME**, unless your index is the default value "llama".
|
||||
By default, all operations take place inside the default namespace '', but you can set **PINECONE_NAMESPACE** to a different value if you need to.
|
||||
|
||||
You'll also need a value for OPENAI_API_KEY in your environment.
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
"type-check": "tsc -b --diagnostics",
|
||||
"release": "pnpm run build:release && changeset publish",
|
||||
"new-llamaindex": "pnpm run build:release && changeset version --ignore create-llama",
|
||||
"new-create-llama": "pnpm run build:release && changeset version --ignore llamaindex",
|
||||
"new-create-llama": "pnpm run build:release && changeset version --ignore llamaindex --ignore @llamaindex/core-test",
|
||||
"new-snapshots": "pnpm run build:release && changeset version --snapshot"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"target": "esnext"
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
"type": "commonjs",
|
||||
"ignoreDynamic": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
.turbo
|
||||
README.md
|
||||
LICENSE
|
||||
@@ -1,5 +1,47 @@
|
||||
# llamaindex
|
||||
|
||||
## 0.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 552a61a: Add quantized parameter to HuggingFaceEmbedding
|
||||
- d824876: Add support for Claude 3
|
||||
|
||||
## 0.1.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 64683a5: fix: prefix messages always true
|
||||
- 698cd9c: fix: step wise agent + examples
|
||||
- 7257751: fixed removeRefDocNode and persist store on delete
|
||||
- 5116ad8: fix: compatibility issue with Deno
|
||||
- Updated dependencies [5116ad8]
|
||||
- @llamaindex/env@0.0.5
|
||||
|
||||
## 0.1.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 026d068: feat: enhance pinecone usage
|
||||
|
||||
## 0.1.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 90027a7: Add splitLongSentences option to SimpleNodeParser
|
||||
- c57bd11: feat: update and refactor title extractor
|
||||
|
||||
## 0.1.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- c8396c5: feat: add base evaluator and correctness evaluator
|
||||
- c8396c5: feat: add base evaluator and correctness evaluator
|
||||
- cf87f84: fix: type backward compatibility
|
||||
- 09bf27a: Add Groq LLM to LlamaIndex
|
||||
- Updated dependencies [cf87f84]
|
||||
- @llamaindex/env@0.0.4
|
||||
|
||||
## 0.1.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@llamaindex/core",
|
||||
"version": "0.1.21",
|
||||
"exports": "./src/index.ts",
|
||||
"imports": {
|
||||
"@llamaindex/env": "jsr:@llamaindex/env@0.0.5"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
{
|
||||
"name": "llamaindex",
|
||||
"version": "0.1.16",
|
||||
"version": "0.1.21",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.13.0",
|
||||
"@anthropic-ai/sdk": "^0.15.0",
|
||||
"@aws-crypto/sha256-js": "^5.2.0",
|
||||
"@datastax/astra-db-ts": "^0.1.4",
|
||||
"@llamaindex/cloud": "^0.0.1",
|
||||
"@llamaindex/cloud": "0.0.4",
|
||||
"@llamaindex/env": "workspace:*",
|
||||
"@mistralai/mistralai": "^0.0.10",
|
||||
"@notionhq/client": "^2.2.14",
|
||||
"@pinecone-database/pinecone": "^2.0.1",
|
||||
"@qdrant/js-client-rest": "^1.7.0",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^18.19.14",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/pg": "^8.11.0",
|
||||
"@xenova/transformers": "^2.15.0",
|
||||
"assemblyai": "^4.2.2",
|
||||
"chromadb": "~1.7.3",
|
||||
@@ -39,10 +43,6 @@
|
||||
"devDependencies": {
|
||||
"@swc/cli": "^0.3.9",
|
||||
"@swc/core": "^1.4.2",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "^18.19.14",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/pg": "^8.11.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"glob": "^10.3.10",
|
||||
"madge": "^6.1.0",
|
||||
@@ -95,7 +95,8 @@
|
||||
"build:esm": "swc src -d dist --strip-leading-paths --config-file .swcrc",
|
||||
"build:cjs": "swc src -d dist/cjs --strip-leading-paths --config-file .cjs.swcrc",
|
||||
"build:type": "tsc -p tsconfig.json",
|
||||
"postbuild": "node -e \"require('fs').writeFileSync('./dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))\"",
|
||||
"copy": "cp -r ../../README.md ../../LICENSE .",
|
||||
"postbuild": "pnpm run copy && node -e \"require('fs').writeFileSync('./dist/cjs/package.json', JSON.stringify({ type: 'commonjs' }))\"",
|
||||
"circular-check": "madge -c ./src/index.ts",
|
||||
"dev": "concurrently \"pnpm run build:esm --watch\" \"pnpm run build:cjs --watch\" \"pnpm run build:type --watch\""
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ export class OpenAIAgent extends AgentRunner {
|
||||
toolRetriever,
|
||||
systemPrompt,
|
||||
}: OpenAIAgentParams) {
|
||||
prefixMessages = prefixMessages || [];
|
||||
|
||||
llm = llm ?? new OpenAI({ model: "gpt-3.5-turbo-0613" });
|
||||
|
||||
if (systemPrompt) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Assuming that the necessary interfaces and classes (like BaseTool, OpenAI, ChatMessage, CallbackManager, etc.) are defined elsewhere
|
||||
|
||||
import { randomUUID } from "@llamaindex/env";
|
||||
import { Response } from "../../Response.js";
|
||||
import type { CallbackManager } from "../../callbacks/CallbackManager.js";
|
||||
import {
|
||||
AgentChatResponse,
|
||||
ChatResponseMode,
|
||||
StreamingAgentChatResponse,
|
||||
} from "../../engines/chat/types.js";
|
||||
import type {
|
||||
ChatMessage,
|
||||
@@ -12,6 +14,7 @@ import type {
|
||||
ChatResponseChunk,
|
||||
} from "../../llm/index.js";
|
||||
import { OpenAI } from "../../llm/index.js";
|
||||
import { streamConverter, streamReducer } from "../../llm/utils.js";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer.js";
|
||||
import type { ObjectRetriever } from "../../objects/base.js";
|
||||
import type { ToolOutput } from "../../tools/types.js";
|
||||
@@ -192,13 +195,40 @@ export class OpenAIAgentWorker implements AgentWorker {
|
||||
private _processMessage(
|
||||
task: Task,
|
||||
chatResponse: ChatResponse,
|
||||
): AgentChatResponse | AsyncIterable<ChatResponseChunk> {
|
||||
): AgentChatResponse {
|
||||
const aiMessage = chatResponse.message;
|
||||
task.extraState.newMemory.put(aiMessage);
|
||||
|
||||
return new AgentChatResponse(aiMessage.content, task.extraState.sources);
|
||||
}
|
||||
|
||||
private async _getStreamAiResponse(
|
||||
task: Task,
|
||||
llmChatKwargs: any,
|
||||
): Promise<StreamingAgentChatResponse> {
|
||||
const stream = await this.llm.chat({
|
||||
stream: true,
|
||||
...llmChatKwargs,
|
||||
});
|
||||
|
||||
const iterator = streamConverter(
|
||||
streamReducer({
|
||||
stream,
|
||||
initialValue: "",
|
||||
reducer: (accumulator, part) => (accumulator += part.delta),
|
||||
finished: (accumulator) => {
|
||||
task.extraState.newMemory.put({
|
||||
content: accumulator,
|
||||
role: "assistant",
|
||||
});
|
||||
},
|
||||
}),
|
||||
(r: ChatResponseChunk) => new Response(r.delta),
|
||||
);
|
||||
|
||||
return new StreamingAgentChatResponse(iterator, task.extraState.sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent response.
|
||||
* @param task: task
|
||||
@@ -210,7 +240,7 @@ export class OpenAIAgentWorker implements AgentWorker {
|
||||
task: Task,
|
||||
mode: ChatResponseMode,
|
||||
llmChatKwargs: any,
|
||||
): Promise<AgentChatResponse> {
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse> {
|
||||
if (mode === ChatResponseMode.WAIT) {
|
||||
const chatResponse = (await this.llm.chat({
|
||||
stream: false,
|
||||
@@ -218,9 +248,11 @@ export class OpenAIAgentWorker implements AgentWorker {
|
||||
})) as unknown as ChatResponse;
|
||||
|
||||
return this._processMessage(task, chatResponse) as AgentChatResponse;
|
||||
} else {
|
||||
throw new Error("Not implemented");
|
||||
} else if (mode === ChatResponseMode.STREAM) {
|
||||
return this._getStreamAiResponse(task, llmChatKwargs);
|
||||
}
|
||||
|
||||
throw new Error("Invalid mode");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ChatEngineAgentParams } from "../../engines/chat/index.js";
|
||||
import {
|
||||
AgentChatResponse,
|
||||
ChatResponseMode,
|
||||
StreamingAgentChatResponse,
|
||||
} from "../../engines/chat/index.js";
|
||||
import type { ChatMessage, LLM } from "../../llm/index.js";
|
||||
import { ChatMemoryBuffer } from "../../memory/ChatMemoryBuffer.js";
|
||||
@@ -14,7 +15,7 @@ import { AgentState, BaseAgentRunner, TaskState } from "./types.js";
|
||||
|
||||
const validateStepFromArgs = (
|
||||
taskId: string,
|
||||
input: string,
|
||||
input?: string | null,
|
||||
step?: any,
|
||||
kwargs?: any,
|
||||
): TaskStep | undefined => {
|
||||
@@ -24,6 +25,7 @@ const validateStepFromArgs = (
|
||||
}
|
||||
return step;
|
||||
} else {
|
||||
if (!input) return;
|
||||
return new TaskStep(taskId, step, input, kwargs);
|
||||
}
|
||||
};
|
||||
@@ -194,7 +196,7 @@ export class AgentRunner extends BaseAgentRunner {
|
||||
*/
|
||||
async runStep(
|
||||
taskId: string,
|
||||
input: string,
|
||||
input?: string | null,
|
||||
step?: TaskStep,
|
||||
kwargs: any = {},
|
||||
): Promise<TaskStepOutput> {
|
||||
@@ -230,23 +232,26 @@ export class AgentRunner extends BaseAgentRunner {
|
||||
taskId: string,
|
||||
stepOutput: TaskStepOutput,
|
||||
kwargs?: any,
|
||||
): Promise<AgentChatResponse> {
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse> {
|
||||
if (!stepOutput) {
|
||||
stepOutput =
|
||||
this.getCompletedSteps(taskId)[
|
||||
this.getCompletedSteps(taskId).length - 1
|
||||
];
|
||||
}
|
||||
|
||||
if (!stepOutput.isLast) {
|
||||
throw new Error(
|
||||
"finalizeResponse can only be called on the last step output",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(stepOutput.output instanceof AgentChatResponse)) {
|
||||
throw new Error(
|
||||
`When \`isLast\` is True, cur_step_output.output must be AGENT_CHAT_RESPONSE_TYPE: ${stepOutput.output}`,
|
||||
);
|
||||
if (!(stepOutput.output instanceof StreamingAgentChatResponse)) {
|
||||
if (!(stepOutput.output instanceof AgentChatResponse)) {
|
||||
throw new Error(
|
||||
`When \`isLast\` is True, cur_step_output.output must be AGENT_CHAT_RESPONSE_TYPE: ${stepOutput.output}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.agentWorker.finalizeTask(this.getTask(taskId), kwargs);
|
||||
@@ -261,20 +266,32 @@ export class AgentRunner extends BaseAgentRunner {
|
||||
protected async _chat({
|
||||
message,
|
||||
toolChoice,
|
||||
}: ChatEngineAgentParams & { mode: ChatResponseMode }) {
|
||||
stream,
|
||||
}: ChatEngineAgentParams): Promise<AgentChatResponse>;
|
||||
protected async _chat({
|
||||
message,
|
||||
toolChoice,
|
||||
stream,
|
||||
}: ChatEngineAgentParams & {
|
||||
stream: true;
|
||||
}): Promise<StreamingAgentChatResponse>;
|
||||
protected async _chat({
|
||||
message,
|
||||
toolChoice,
|
||||
stream,
|
||||
}: ChatEngineAgentParams): Promise<
|
||||
AgentChatResponse | StreamingAgentChatResponse
|
||||
> {
|
||||
const task = this.createTask(message as string);
|
||||
|
||||
let resultOutput;
|
||||
|
||||
const mode = stream ? ChatResponseMode.STREAM : ChatResponseMode.WAIT;
|
||||
|
||||
while (true) {
|
||||
const curStepOutput = await this._runStep(
|
||||
task.taskId,
|
||||
undefined,
|
||||
ChatResponseMode.WAIT,
|
||||
{
|
||||
toolChoice,
|
||||
},
|
||||
);
|
||||
const curStepOutput = await this._runStep(task.taskId, undefined, mode, {
|
||||
toolChoice,
|
||||
});
|
||||
|
||||
if (curStepOutput.isLast) {
|
||||
resultOutput = curStepOutput;
|
||||
@@ -298,7 +315,26 @@ export class AgentRunner extends BaseAgentRunner {
|
||||
message,
|
||||
chatHistory,
|
||||
toolChoice,
|
||||
}: ChatEngineAgentParams): Promise<AgentChatResponse> {
|
||||
stream,
|
||||
}: ChatEngineAgentParams & {
|
||||
stream?: false;
|
||||
}): Promise<AgentChatResponse>;
|
||||
public async chat({
|
||||
message,
|
||||
chatHistory,
|
||||
toolChoice,
|
||||
stream,
|
||||
}: ChatEngineAgentParams & {
|
||||
stream: true;
|
||||
}): Promise<StreamingAgentChatResponse>;
|
||||
public async chat({
|
||||
message,
|
||||
chatHistory,
|
||||
toolChoice,
|
||||
stream,
|
||||
}: ChatEngineAgentParams): Promise<
|
||||
AgentChatResponse | StreamingAgentChatResponse
|
||||
> {
|
||||
if (!toolChoice) {
|
||||
toolChoice = this.defaultToolChoice;
|
||||
}
|
||||
@@ -307,7 +343,7 @@ export class AgentRunner extends BaseAgentRunner {
|
||||
message,
|
||||
chatHistory,
|
||||
toolChoice,
|
||||
mode: ChatResponseMode.WAIT,
|
||||
stream,
|
||||
});
|
||||
|
||||
return chatResponse;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import type { AgentChatResponse } from "../../engines/chat/index.js";
|
||||
import type {
|
||||
AgentChatResponse,
|
||||
StreamingAgentChatResponse,
|
||||
} from "../../engines/chat/index.js";
|
||||
import type { Task, TaskStep, TaskStepOutput } from "../types.js";
|
||||
import { BaseAgent } from "../types.js";
|
||||
|
||||
@@ -57,7 +60,7 @@ export abstract class BaseAgentRunner extends BaseAgent {
|
||||
taskId: string,
|
||||
stepOutput: TaskStepOutput,
|
||||
kwargs?: any,
|
||||
): Promise<AgentChatResponse>;
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse>;
|
||||
|
||||
abstract undoStep(taskId: string): void;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
AgentChatResponse,
|
||||
ChatEngineAgentParams,
|
||||
StreamingAgentChatResponse,
|
||||
} from "../engines/chat/index.js";
|
||||
import type { QueryEngineParamsNonStreaming } from "../types.js";
|
||||
|
||||
@@ -12,11 +13,15 @@ export interface AgentWorker {
|
||||
}
|
||||
|
||||
interface BaseChatEngine {
|
||||
chat(params: ChatEngineAgentParams): Promise<AgentChatResponse>;
|
||||
chat(
|
||||
params: ChatEngineAgentParams,
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse>;
|
||||
}
|
||||
|
||||
interface BaseQueryEngine {
|
||||
query(params: QueryEngineParamsNonStreaming): Promise<AgentChatResponse>;
|
||||
query(
|
||||
params: QueryEngineParamsNonStreaming,
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,7 +36,10 @@ export abstract class BaseAgent implements BaseChatEngine, BaseQueryEngine {
|
||||
return [];
|
||||
}
|
||||
|
||||
abstract chat(params: ChatEngineAgentParams): Promise<AgentChatResponse>;
|
||||
abstract chat(
|
||||
params: ChatEngineAgentParams,
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse>;
|
||||
|
||||
abstract reset(): void;
|
||||
|
||||
/**
|
||||
@@ -41,7 +49,7 @@ export abstract class BaseAgent implements BaseChatEngine, BaseQueryEngine {
|
||||
*/
|
||||
async query(
|
||||
params: QueryEngineParamsNonStreaming,
|
||||
): Promise<AgentChatResponse> {
|
||||
): Promise<AgentChatResponse | StreamingAgentChatResponse> {
|
||||
// Handle non-streaming query
|
||||
const agentResponse = await this.chat({
|
||||
message: params.query,
|
||||
@@ -161,13 +169,13 @@ export class TaskStep implements ITaskStep {
|
||||
* @param isLast: isLast
|
||||
*/
|
||||
export class TaskStepOutput {
|
||||
output: unknown;
|
||||
output: any;
|
||||
taskStep: TaskStep;
|
||||
nextSteps: TaskStep[];
|
||||
isLast: boolean;
|
||||
|
||||
constructor(
|
||||
output: unknown,
|
||||
output: any,
|
||||
taskStep: TaskStep,
|
||||
nextSteps: TaskStep[],
|
||||
isLast: boolean = false,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PlatformApiClient } from "@llamaindex/cloud";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type { ClientParams } from "./types.js";
|
||||
import { DEFAULT_BASE_URL } from "./types.js";
|
||||
|
||||
@@ -7,8 +8,8 @@ export async function getClient({
|
||||
baseUrl,
|
||||
}: ClientParams = {}): Promise<PlatformApiClient> {
|
||||
// Get the environment variables or use defaults
|
||||
baseUrl = baseUrl ?? process.env.LLAMA_CLOUD_BASE_URL ?? DEFAULT_BASE_URL;
|
||||
apiKey = apiKey ?? process.env.LLAMA_CLOUD_API_KEY;
|
||||
baseUrl = baseUrl ?? getEnv("LLAMA_CLOUD_BASE_URL") ?? DEFAULT_BASE_URL;
|
||||
apiKey = apiKey ?? getEnv("LLAMA_CLOUD_API_KEY");
|
||||
|
||||
const { PlatformApiClient } = await import("@llamaindex/cloud");
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ export enum HuggingFaceEmbeddingModelType {
|
||||
*/
|
||||
export class HuggingFaceEmbedding extends BaseEmbedding {
|
||||
modelType: string = HuggingFaceEmbeddingModelType.XENOVA_ALL_MINILM_L6_V2;
|
||||
quantized: boolean = true;
|
||||
|
||||
private extractor: any;
|
||||
|
||||
@@ -31,7 +32,9 @@ export class HuggingFaceEmbedding extends BaseEmbedding {
|
||||
async getExtractor() {
|
||||
if (!this.extractor) {
|
||||
const { pipeline } = await import("@xenova/transformers");
|
||||
this.extractor = await pipeline("feature-extraction", this.modelType);
|
||||
this.extractor = await pipeline("feature-extraction", this.modelType, {
|
||||
quantized: this.quantized,
|
||||
});
|
||||
}
|
||||
return this.extractor;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding.js";
|
||||
|
||||
export class FireworksEmbedding extends OpenAIEmbedding {
|
||||
constructor(init?: Partial<OpenAIEmbedding>) {
|
||||
const {
|
||||
apiKey = process.env.FIREWORKS_API_KEY,
|
||||
apiKey = getEnv("FIREWORKS_API_KEY"),
|
||||
additionalSessionOptions = {},
|
||||
model = "nomic-ai/nomic-embed-text-v1.5",
|
||||
...rest
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { OpenAIEmbedding } from "./OpenAIEmbedding.js";
|
||||
|
||||
export class TogetherEmbedding extends OpenAIEmbedding {
|
||||
constructor(init?: Partial<OpenAIEmbedding>) {
|
||||
const {
|
||||
apiKey = process.env.TOGETHER_API_KEY,
|
||||
apiKey = getEnv("TOGETHER_API_KEY"),
|
||||
additionalSessionOptions = {},
|
||||
model = "togethercomputer/m2-bert-80M-32k-retrieval",
|
||||
...rest
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface ChatEngineParamsNonStreaming extends ChatEngineParamsBase {
|
||||
|
||||
export interface ChatEngineAgentParams extends ChatEngineParamsBase {
|
||||
toolChoice?: string | Record<string, any>;
|
||||
stream?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,3 +87,20 @@ export class AgentChatResponse {
|
||||
return this.response ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
export class StreamingAgentChatResponse {
|
||||
response: AsyncIterable<Response>;
|
||||
|
||||
sources: ToolOutput[];
|
||||
sourceNodes?: BaseNode[];
|
||||
|
||||
constructor(
|
||||
response: AsyncIterable<Response>,
|
||||
sources?: ToolOutput[],
|
||||
sourceNodes?: BaseNode[],
|
||||
) {
|
||||
this.response = response;
|
||||
this.sources = sources ?? [];
|
||||
this.sourceNodes = sourceNodes ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { MetadataMode } from "../Node.js";
|
||||
import type { ServiceContext } from "../ServiceContext.js";
|
||||
import { serviceContextFromDefaults } from "../ServiceContext.js";
|
||||
import type { ChatMessage } from "../llm/types.js";
|
||||
import { PromptMixin } from "../prompts/Mixin.js";
|
||||
import type { CorrectnessSystemPrompt } from "./prompts.js";
|
||||
import {
|
||||
defaultCorrectnessSystemPrompt,
|
||||
defaultUserPrompt,
|
||||
} from "./prompts.js";
|
||||
import type {
|
||||
BaseEvaluator,
|
||||
EvaluationResult,
|
||||
EvaluatorParams,
|
||||
EvaluatorResponseParams,
|
||||
} from "./types.js";
|
||||
import { defaultEvaluationParser } from "./utils.js";
|
||||
|
||||
type CorrectnessParams = {
|
||||
serviceContext?: ServiceContext;
|
||||
scoreThreshold?: number;
|
||||
parserFunction?: (str: string) => [number, string];
|
||||
};
|
||||
|
||||
/** Correctness Evaluator */
|
||||
export class CorrectnessEvaluator extends PromptMixin implements BaseEvaluator {
|
||||
private serviceContext: ServiceContext;
|
||||
private scoreThreshold: number;
|
||||
private parserFunction: (str: string) => [number, string];
|
||||
|
||||
private correctnessPrompt: CorrectnessSystemPrompt =
|
||||
defaultCorrectnessSystemPrompt;
|
||||
|
||||
constructor(params: CorrectnessParams) {
|
||||
super();
|
||||
|
||||
this.serviceContext = params.serviceContext || serviceContextFromDefaults();
|
||||
this.correctnessPrompt = defaultCorrectnessSystemPrompt;
|
||||
this.scoreThreshold = params.scoreThreshold || 4.0;
|
||||
this.parserFunction = params.parserFunction || defaultEvaluationParser;
|
||||
}
|
||||
|
||||
_updatePrompts(prompts: {
|
||||
correctnessPrompt: CorrectnessSystemPrompt;
|
||||
}): void {
|
||||
if ("correctnessPrompt" in prompts) {
|
||||
this.correctnessPrompt = prompts["correctnessPrompt"];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param query Query to evaluate
|
||||
* @param response Response to evaluate
|
||||
* @param contexts Array of contexts
|
||||
* @param reference Reference response
|
||||
*/
|
||||
async evaluate({
|
||||
query,
|
||||
response,
|
||||
contexts,
|
||||
reference,
|
||||
}: EvaluatorParams): Promise<EvaluationResult> {
|
||||
if (query === null || response === null) {
|
||||
throw new Error("query, and response must be provided");
|
||||
}
|
||||
|
||||
const messages: ChatMessage[] = [
|
||||
{
|
||||
role: "system",
|
||||
content: this.correctnessPrompt(),
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: defaultUserPrompt({
|
||||
query,
|
||||
generatedAnswer: response,
|
||||
referenceAnswer: reference || "(NO REFERENCE ANSWER SUPPLIED)",
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
const evalResponse = await this.serviceContext.llm.chat({
|
||||
messages,
|
||||
});
|
||||
|
||||
const [score, reasoning] = this.parserFunction(
|
||||
evalResponse.message.content,
|
||||
);
|
||||
|
||||
return {
|
||||
query: query,
|
||||
response: response,
|
||||
passing: score >= this.scoreThreshold || score === null,
|
||||
score: score,
|
||||
feedback: reasoning,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param query Query to evaluate
|
||||
* @param response Response to evaluate
|
||||
*/
|
||||
async evaluateResponse({
|
||||
query,
|
||||
response,
|
||||
}: EvaluatorResponseParams): Promise<EvaluationResult> {
|
||||
const responseStr = response?.response;
|
||||
const contexts = [];
|
||||
|
||||
if (response) {
|
||||
for (const node of response.sourceNodes || []) {
|
||||
contexts.push(node.getContent(MetadataMode.ALL));
|
||||
}
|
||||
}
|
||||
|
||||
return this.evaluate({
|
||||
query,
|
||||
response: responseStr,
|
||||
contexts,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { Document, MetadataMode } from "../Node.js";
|
||||
import type { ServiceContext } from "../ServiceContext.js";
|
||||
import { serviceContextFromDefaults } from "../ServiceContext.js";
|
||||
import { SummaryIndex } from "../indices/summary/index.js";
|
||||
import { PromptMixin } from "../prompts/Mixin.js";
|
||||
import type {
|
||||
FaithfulnessRefinePrompt,
|
||||
FaithfulnessTextQAPrompt,
|
||||
} from "./prompts.js";
|
||||
import {
|
||||
defaultFaithfulnessRefinePrompt,
|
||||
defaultFaithfulnessTextQaPrompt,
|
||||
} from "./prompts.js";
|
||||
import type {
|
||||
BaseEvaluator,
|
||||
EvaluationResult,
|
||||
EvaluatorParams,
|
||||
EvaluatorResponseParams,
|
||||
} from "./types.js";
|
||||
|
||||
export class FaithfulnessEvaluator
|
||||
extends PromptMixin
|
||||
implements BaseEvaluator
|
||||
{
|
||||
private serviceContext: ServiceContext;
|
||||
private raiseError: boolean;
|
||||
private evalTemplate: FaithfulnessTextQAPrompt;
|
||||
private refineTemplate: FaithfulnessRefinePrompt;
|
||||
|
||||
constructor(params: {
|
||||
serviceContext?: ServiceContext;
|
||||
raiseError?: boolean;
|
||||
faithfulnessSystemPrompt?: FaithfulnessTextQAPrompt;
|
||||
faithFulnessRefinePrompt?: FaithfulnessRefinePrompt;
|
||||
}) {
|
||||
super();
|
||||
this.serviceContext = params.serviceContext || serviceContextFromDefaults();
|
||||
this.raiseError = params.raiseError || false;
|
||||
|
||||
this.evalTemplate =
|
||||
params.faithfulnessSystemPrompt || defaultFaithfulnessTextQaPrompt;
|
||||
this.refineTemplate =
|
||||
params.faithFulnessRefinePrompt || defaultFaithfulnessRefinePrompt;
|
||||
}
|
||||
|
||||
protected _getPrompts(): { [x: string]: any } {
|
||||
return {
|
||||
faithfulnessSystemPrompt: this.evalTemplate,
|
||||
faithFulnessRefinePrompt: this.refineTemplate,
|
||||
};
|
||||
}
|
||||
|
||||
protected _updatePrompts(promptsDict: {
|
||||
faithfulnessSystemPrompt: FaithfulnessTextQAPrompt;
|
||||
faithFulnessRefinePrompt: FaithfulnessRefinePrompt;
|
||||
}): void {
|
||||
if (promptsDict.faithfulnessSystemPrompt) {
|
||||
this.evalTemplate = promptsDict.faithfulnessSystemPrompt;
|
||||
}
|
||||
|
||||
if (promptsDict.faithFulnessRefinePrompt) {
|
||||
this.refineTemplate = promptsDict.faithFulnessRefinePrompt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param query Query to evaluate
|
||||
* @param response Response to evaluate
|
||||
* @param contexts Array of contexts
|
||||
* @param reference Reference response
|
||||
* @param sleepTimeInSeconds Sleep time in seconds
|
||||
*/
|
||||
async evaluate({
|
||||
query,
|
||||
response,
|
||||
contexts = [],
|
||||
reference,
|
||||
sleepTimeInSeconds = 0,
|
||||
}: EvaluatorParams): Promise<EvaluationResult> {
|
||||
if (query === null || response === null) {
|
||||
throw new Error("query, and response must be provided");
|
||||
}
|
||||
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, sleepTimeInSeconds * 1000),
|
||||
);
|
||||
|
||||
const docs = contexts?.map((context) => new Document({ text: context }));
|
||||
|
||||
const index = await SummaryIndex.fromDocuments(docs, {
|
||||
serviceContext: this.serviceContext,
|
||||
});
|
||||
|
||||
const queryEngine = index.asQueryEngine();
|
||||
|
||||
queryEngine.updatePrompts({
|
||||
"responseSynthesizer:textQATemplate": this.evalTemplate,
|
||||
"responseSynthesizer:refineTemplate": this.refineTemplate,
|
||||
});
|
||||
|
||||
const responseObj = await queryEngine.query({
|
||||
query: response,
|
||||
});
|
||||
|
||||
const rawResponseTxt = responseObj.toString();
|
||||
|
||||
let passing: boolean;
|
||||
|
||||
if (rawResponseTxt.toLowerCase().includes("yes")) {
|
||||
passing = true;
|
||||
} else {
|
||||
passing = false;
|
||||
if (this.raiseError) {
|
||||
throw new Error("The response is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
query,
|
||||
contexts,
|
||||
response,
|
||||
passing,
|
||||
score: passing ? 1.0 : 0.0,
|
||||
feedback: rawResponseTxt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param query Query to evaluate
|
||||
* @param response Response to evaluate
|
||||
*/
|
||||
async evaluateResponse({
|
||||
query,
|
||||
response,
|
||||
}: EvaluatorResponseParams): Promise<EvaluationResult> {
|
||||
const responseStr = response?.response;
|
||||
const contexts = [];
|
||||
|
||||
if (response) {
|
||||
for (const node of response.sourceNodes || []) {
|
||||
contexts.push(node.getContent(MetadataMode.ALL));
|
||||
}
|
||||
}
|
||||
|
||||
return this.evaluate({
|
||||
query,
|
||||
response: responseStr,
|
||||
contexts,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { Document, MetadataMode } from "../Node.js";
|
||||
import type { ServiceContext } from "../ServiceContext.js";
|
||||
import { serviceContextFromDefaults } from "../ServiceContext.js";
|
||||
import { SummaryIndex } from "../indices/summary/index.js";
|
||||
import { PromptMixin } from "../prompts/Mixin.js";
|
||||
import type { RelevancyEvalPrompt, RelevancyRefinePrompt } from "./prompts.js";
|
||||
import {
|
||||
defaultRelevancyEvalPrompt,
|
||||
defaultRelevancyRefinePrompt,
|
||||
} from "./prompts.js";
|
||||
import type {
|
||||
BaseEvaluator,
|
||||
EvaluationResult,
|
||||
EvaluatorParams,
|
||||
EvaluatorResponseParams,
|
||||
} from "./types.js";
|
||||
|
||||
type RelevancyParams = {
|
||||
serviceContext?: ServiceContext;
|
||||
raiseError?: boolean;
|
||||
evalTemplate?: RelevancyEvalPrompt;
|
||||
refineTemplate?: RelevancyRefinePrompt;
|
||||
};
|
||||
|
||||
export class RelevancyEvaluator extends PromptMixin implements BaseEvaluator {
|
||||
private serviceContext: ServiceContext;
|
||||
private raiseError: boolean;
|
||||
|
||||
private evalTemplate: RelevancyEvalPrompt;
|
||||
private refineTemplate: RelevancyRefinePrompt;
|
||||
|
||||
constructor(params: RelevancyParams) {
|
||||
super();
|
||||
|
||||
this.serviceContext = params.serviceContext ?? serviceContextFromDefaults();
|
||||
this.raiseError = params.raiseError ?? false;
|
||||
this.evalTemplate = params.evalTemplate ?? defaultRelevancyEvalPrompt;
|
||||
this.refineTemplate = params.refineTemplate ?? defaultRelevancyRefinePrompt;
|
||||
}
|
||||
|
||||
_getPrompts() {
|
||||
return {
|
||||
evalTemplate: this.evalTemplate,
|
||||
refineTemplate: this.refineTemplate,
|
||||
};
|
||||
}
|
||||
|
||||
_updatePrompts(prompts: {
|
||||
evalTemplate: RelevancyEvalPrompt;
|
||||
refineTemplate: RelevancyRefinePrompt;
|
||||
}): void {
|
||||
if ("evalTemplate" in prompts) {
|
||||
this.evalTemplate = prompts["evalTemplate"];
|
||||
}
|
||||
if ("refineTemplate" in prompts) {
|
||||
this.refineTemplate = prompts["refineTemplate"];
|
||||
}
|
||||
}
|
||||
|
||||
async evaluate({
|
||||
query,
|
||||
response,
|
||||
contexts = [],
|
||||
sleepTimeInSeconds = 0,
|
||||
}: EvaluatorParams): Promise<EvaluationResult> {
|
||||
if (query === null || response === null) {
|
||||
throw new Error("query, contexts, and response must be provided");
|
||||
}
|
||||
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, sleepTimeInSeconds * 1000),
|
||||
);
|
||||
|
||||
const docs = contexts?.map((context) => new Document({ text: context }));
|
||||
|
||||
const index = await SummaryIndex.fromDocuments(docs, {
|
||||
serviceContext: this.serviceContext,
|
||||
});
|
||||
|
||||
const queryResponse = `Question: ${query}\nResponse: ${response}`;
|
||||
|
||||
const queryEngine = index.asQueryEngine();
|
||||
|
||||
queryEngine.updatePrompts({
|
||||
"responseSynthesizer:textQATemplate": this.evalTemplate,
|
||||
"responseSynthesizer:refineTemplate": this.refineTemplate,
|
||||
});
|
||||
|
||||
const responseObj = await queryEngine.query({
|
||||
query: queryResponse,
|
||||
});
|
||||
|
||||
const rawResponseTxt = responseObj.toString();
|
||||
|
||||
let passing: boolean;
|
||||
|
||||
if (rawResponseTxt.toLowerCase().includes("yes")) {
|
||||
passing = true;
|
||||
} else {
|
||||
passing = false;
|
||||
if (this.raiseError) {
|
||||
throw new Error("The response is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
query,
|
||||
contexts,
|
||||
response,
|
||||
passing,
|
||||
score: passing ? 1.0 : 0.0,
|
||||
feedback: rawResponseTxt,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param query Query to evaluate
|
||||
* @param response Response to evaluate
|
||||
*/
|
||||
async evaluateResponse({
|
||||
query,
|
||||
response,
|
||||
}: EvaluatorResponseParams): Promise<EvaluationResult> {
|
||||
const responseStr = response?.response;
|
||||
const contexts = [];
|
||||
|
||||
if (response) {
|
||||
for (const node of response.sourceNodes || []) {
|
||||
contexts.push(node.getContent(MetadataMode.ALL));
|
||||
}
|
||||
}
|
||||
|
||||
return this.evaluate({
|
||||
query,
|
||||
response: responseStr,
|
||||
contexts,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./Correctness.js";
|
||||
export * from "./Faithfulness.js";
|
||||
export * from "./Relevancy.js";
|
||||
export * from "./prompts.js";
|
||||
export * from "./utils.js";
|
||||
@@ -0,0 +1,155 @@
|
||||
export const defaultUserPrompt = ({
|
||||
query,
|
||||
referenceAnswer,
|
||||
generatedAnswer,
|
||||
}: {
|
||||
query: string;
|
||||
referenceAnswer: string;
|
||||
generatedAnswer: string;
|
||||
}) => `
|
||||
## User Query
|
||||
${query}
|
||||
|
||||
## Reference Answer
|
||||
${referenceAnswer}
|
||||
|
||||
## Generated Answer
|
||||
${generatedAnswer}
|
||||
`;
|
||||
|
||||
export type UserPrompt = typeof defaultUserPrompt;
|
||||
|
||||
export const defaultCorrectnessSystemPrompt =
|
||||
() => `You are an expert evaluation system for a question answering chatbot.
|
||||
|
||||
You are given the following information:
|
||||
- a user query, and
|
||||
- a generated answer
|
||||
|
||||
You may also be given a reference answer to use for reference in your evaluation.
|
||||
|
||||
Your job is to judge the relevance and correctness of the generated answer.
|
||||
Output a single score that represents a holistic evaluation.
|
||||
You must return your response in a line with only the score.
|
||||
Do not return answers in any other format.
|
||||
On a separate line provide your reasoning for the score as well.
|
||||
|
||||
Follow these guidelines for scoring:
|
||||
- Your score has to be between 1 and 5, where 1 is the worst and 5 is the best.
|
||||
- If the generated answer is not relevant to the user query,
|
||||
you should give a score of 1.
|
||||
- If the generated answer is relevant but contains mistakes,
|
||||
you should give a score between 2 and 3.
|
||||
- If the generated answer is relevant and fully correct,
|
||||
you should give a score between 4 and 5.
|
||||
|
||||
Example Response:
|
||||
4.0
|
||||
The generated answer has the exact same metrics as the reference answer
|
||||
but it is not as concise.
|
||||
`;
|
||||
|
||||
export type CorrectnessSystemPrompt = typeof defaultCorrectnessSystemPrompt;
|
||||
|
||||
export const defaultFaithfulnessRefinePrompt = ({
|
||||
query,
|
||||
context,
|
||||
existingAnswer,
|
||||
}: {
|
||||
query: string;
|
||||
context: string;
|
||||
existingAnswer: string;
|
||||
}) => `
|
||||
We want to understand if the following information is present
|
||||
in the context information: ${query}
|
||||
We have provided an existing YES/NO answer: ${existingAnswer}
|
||||
We have the opportunity to refine the existing answer
|
||||
(only if needed) with some more context below.
|
||||
------------
|
||||
${context}
|
||||
------------
|
||||
If the existing answer was already YES, still answer YES.
|
||||
If the information is present in the new context, answer YES.
|
||||
Otherwise answer NO.
|
||||
`;
|
||||
|
||||
export type FaithfulnessRefinePrompt = typeof defaultFaithfulnessRefinePrompt;
|
||||
|
||||
export const defaultFaithfulnessTextQaPrompt = ({
|
||||
query,
|
||||
context,
|
||||
}: {
|
||||
query: string;
|
||||
context: string;
|
||||
}) => `
|
||||
Please tell if a given piece of information
|
||||
is supported by the context.
|
||||
You need to answer with either YES or NO.
|
||||
Answer YES if any of the context supports the information, even
|
||||
if most of the context is unrelated.
|
||||
Some examples are provided below.
|
||||
|
||||
Information: Apple pie is generally double-crusted.
|
||||
Context: An apple pie is a fruit pie in which the principal filling
|
||||
ingredient is apples.
|
||||
Apple pie is often served with whipped cream, ice cream
|
||||
('apple pie à la mode'), custard or cheddar cheese.
|
||||
It is generally double-crusted, with pastry both above
|
||||
and below the filling; the upper crust may be solid or
|
||||
latticed (woven of crosswise strips).
|
||||
Answer: YES
|
||||
Information: Apple pies tastes bad.
|
||||
Context: An apple pie is a fruit pie in which the principal filling
|
||||
ingredient is apples.
|
||||
Apple pie is often served with whipped cream, ice cream
|
||||
('apple pie à la mode'), custard or cheddar cheese.
|
||||
It is generally double-crusted, with pastry both above
|
||||
and below the filling; the upper crust may be solid or
|
||||
latticed (woven of crosswise strips).
|
||||
Answer: NO
|
||||
Information: ${query}
|
||||
Context: ${context}
|
||||
Answer:
|
||||
`;
|
||||
|
||||
export type FaithfulnessTextQAPrompt = typeof defaultFaithfulnessTextQaPrompt;
|
||||
|
||||
export const defaultRelevancyEvalPrompt = ({
|
||||
query,
|
||||
context,
|
||||
}: {
|
||||
query: string;
|
||||
context: string;
|
||||
}) => `Your task is to evaluate if the response for the query is in line with the context information provided.
|
||||
You have two options to answer. Either YES/ NO.
|
||||
Answer - YES, if the response for the query is in line with context information otherwise NO.
|
||||
Query and Response: ${query}
|
||||
Context: ${context}
|
||||
Answer: `;
|
||||
|
||||
export type RelevancyEvalPrompt = typeof defaultRelevancyEvalPrompt;
|
||||
|
||||
export const defaultRelevancyRefinePrompt = ({
|
||||
query,
|
||||
existingAnswer,
|
||||
contextMsg,
|
||||
}: {
|
||||
query: string;
|
||||
existingAnswer: string;
|
||||
contextMsg: string;
|
||||
}) => `We want to understand if the following query and response is
|
||||
in line with the context information:
|
||||
${query}
|
||||
We have provided an existing YES/NO answer:
|
||||
${existingAnswer}
|
||||
We have the opportunity to refine the existing answer
|
||||
(only if needed) with some more context below.
|
||||
------------
|
||||
${contextMsg}
|
||||
------------
|
||||
If the existing answer was already YES, still answer YES.
|
||||
If the information is present in the new context, answer YES.
|
||||
Otherwise answer NO.
|
||||
`;
|
||||
|
||||
export type RelevancyRefinePrompt = typeof defaultRelevancyRefinePrompt;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Response } from "../Response.js";
|
||||
|
||||
export type EvaluationResult = {
|
||||
query?: string;
|
||||
contexts?: string[];
|
||||
response: string | null;
|
||||
score: number;
|
||||
scoreSecondary?: number;
|
||||
scoreSecondaryType?: string;
|
||||
meta?: any;
|
||||
passing: boolean;
|
||||
feedback: string;
|
||||
};
|
||||
|
||||
export type EvaluatorParams = {
|
||||
query: string | null;
|
||||
response: string;
|
||||
contexts?: string[];
|
||||
reference?: string;
|
||||
sleepTimeInSeconds?: number;
|
||||
};
|
||||
|
||||
export type EvaluatorResponseParams = {
|
||||
query: string | null;
|
||||
response: Response;
|
||||
};
|
||||
export interface BaseEvaluator {
|
||||
evaluate(params: EvaluatorParams): Promise<EvaluationResult>;
|
||||
evaluateResponse?(params: EvaluatorResponseParams): Promise<EvaluationResult>;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export const defaultEvaluationParser = (
|
||||
evalResponse: string,
|
||||
): [number, string] => {
|
||||
const [scoreStr, reasoningStr] = evalResponse.split("\n");
|
||||
const score = parseFloat(scoreStr);
|
||||
const reasoning = reasoningStr.trim();
|
||||
return [score, reasoning];
|
||||
};
|
||||
@@ -141,8 +141,8 @@ export class TitleExtractor extends BaseExtractor {
|
||||
* Constructor for the TitleExtractor class.
|
||||
* @param {LLM} llm LLM instance.
|
||||
* @param {number} nodes Number of nodes to extract titles from.
|
||||
* @param {string} node_template The prompt template to use for the title extractor.
|
||||
* @param {string} combine_template The prompt template to merge title with..
|
||||
* @param {string} nodeTemplate The prompt template to use for the title extractor.
|
||||
* @param {string} combineTemplate The prompt template to merge title with..
|
||||
*/
|
||||
constructor(options?: TitleExtractorsArgs) {
|
||||
super();
|
||||
@@ -162,50 +162,85 @@ export class TitleExtractor extends BaseExtractor {
|
||||
* @returns {Promise<BaseNode<ExtractTitle>[]>} Titles extracted from the nodes.
|
||||
*/
|
||||
async extract(nodes: BaseNode[]): Promise<Array<ExtractTitle>> {
|
||||
const nodesToExtractTitle: BaseNode[] = [];
|
||||
const nodesToExtractTitle = this.filterNodes(nodes);
|
||||
|
||||
for (let i = 0; i < this.nodes; i++) {
|
||||
if (nodesToExtractTitle.length >= nodes.length) break;
|
||||
|
||||
if (this.isTextNodeOnly && !(nodes[i] instanceof TextNode)) continue;
|
||||
|
||||
nodesToExtractTitle.push(nodes[i]);
|
||||
if (!nodesToExtractTitle.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (nodesToExtractTitle.length === 0) return [];
|
||||
const nodesByDocument = this.separateNodesByDocument(nodesToExtractTitle);
|
||||
const titlesByDocument = await this.extractTitles(nodesByDocument);
|
||||
|
||||
const titlesCandidates: string[] = [];
|
||||
let title: string = "";
|
||||
return nodesToExtractTitle.map((node) => {
|
||||
return {
|
||||
documentTitle: titlesByDocument[node.sourceNode?.nodeId ?? ""],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < nodesToExtractTitle.length; i++) {
|
||||
const completion = await this.llm.complete({
|
||||
prompt: defaultTitleExtractorPromptTemplate({
|
||||
contextStr: nodesToExtractTitle[i].getContent(MetadataMode.ALL),
|
||||
}),
|
||||
});
|
||||
private filterNodes(nodes: BaseNode[]): BaseNode[] {
|
||||
return nodes.filter((node) => {
|
||||
if (this.isTextNodeOnly && !(node instanceof TextNode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
titlesCandidates.push(completion.text);
|
||||
private separateNodesByDocument(
|
||||
nodes: BaseNode[],
|
||||
): Record<string, BaseNode[]> {
|
||||
const nodesByDocument: Record<string, BaseNode[]> = {};
|
||||
|
||||
for (const node of nodes) {
|
||||
const parentNode = node.sourceNode?.nodeId;
|
||||
|
||||
if (!parentNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!nodesByDocument[parentNode]) {
|
||||
nodesByDocument[parentNode] = [];
|
||||
}
|
||||
|
||||
nodesByDocument[parentNode].push(node);
|
||||
}
|
||||
|
||||
if (nodesToExtractTitle.length > 1) {
|
||||
const combinedTitles = titlesCandidates.join(",");
|
||||
return nodesByDocument;
|
||||
}
|
||||
|
||||
private async extractTitles(
|
||||
nodesByDocument: Record<string, BaseNode[]>,
|
||||
): Promise<Record<string, string>> {
|
||||
const titlesByDocument: Record<string, string> = {};
|
||||
|
||||
for (const [key, nodes] of Object.entries(nodesByDocument)) {
|
||||
const titleCandidates = await this.getTitlesCandidates(nodes);
|
||||
const combinedTitles = titleCandidates.join(", ");
|
||||
const completion = await this.llm.complete({
|
||||
prompt: defaultTitleCombinePromptTemplate({
|
||||
contextStr: combinedTitles,
|
||||
}),
|
||||
});
|
||||
|
||||
title = completion.text;
|
||||
titlesByDocument[key] = completion.text;
|
||||
}
|
||||
|
||||
if (nodesToExtractTitle.length === 1) {
|
||||
title = titlesCandidates[0];
|
||||
}
|
||||
return titlesByDocument;
|
||||
}
|
||||
|
||||
return nodes.map((_) => ({
|
||||
documentTitle: title.trim().replace(STRIP_REGEX, ""),
|
||||
}));
|
||||
private async getTitlesCandidates(nodes: BaseNode[]): Promise<string[]> {
|
||||
const titleJobs = nodes.map(async (node) => {
|
||||
const completion = await this.llm.complete({
|
||||
prompt: defaultTitleExtractorPromptTemplate({
|
||||
contextStr: node.getContent(MetadataMode.ALL),
|
||||
}),
|
||||
});
|
||||
|
||||
return completion.text;
|
||||
});
|
||||
|
||||
return await Promise.all(titleJobs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,9 +387,9 @@ export class SummaryExtractor extends BaseExtractor {
|
||||
*/
|
||||
promptTemplate: string;
|
||||
|
||||
private _selfSummary: boolean;
|
||||
private _prevSummary: boolean;
|
||||
private _nextSummary: boolean;
|
||||
private selfSummary: boolean;
|
||||
private prevSummary: boolean;
|
||||
private nextSummary: boolean;
|
||||
|
||||
constructor(options?: SummaryExtractArgs) {
|
||||
const summaries = options?.summaries ?? ["self"];
|
||||
@@ -372,9 +407,9 @@ export class SummaryExtractor extends BaseExtractor {
|
||||
this.promptTemplate =
|
||||
options?.promptTemplate ?? defaultSummaryExtractorPromptTemplate();
|
||||
|
||||
this._selfSummary = summaries?.includes("self") ?? false;
|
||||
this._prevSummary = summaries?.includes("prev") ?? false;
|
||||
this._nextSummary = summaries?.includes("next") ?? false;
|
||||
this.selfSummary = summaries?.includes("self") ?? false;
|
||||
this.prevSummary = summaries?.includes("prev") ?? false;
|
||||
this.nextSummary = summaries?.includes("next") ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,13 +451,13 @@ export class SummaryExtractor extends BaseExtractor {
|
||||
const metadataList: any[] = nodes.map(() => ({}));
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
if (i > 0 && this._prevSummary && nodeSummaries[i - 1]) {
|
||||
if (i > 0 && this.prevSummary && nodeSummaries[i - 1]) {
|
||||
metadataList[i]["prevSectionSummary"] = nodeSummaries[i - 1];
|
||||
}
|
||||
if (i < nodes.length - 1 && this._nextSummary && nodeSummaries[i + 1]) {
|
||||
if (i < nodes.length - 1 && this.nextSummary && nodeSummaries[i + 1]) {
|
||||
metadataList[i]["nextSectionSummary"] = nodeSummaries[i + 1];
|
||||
}
|
||||
if (this._selfSummary && nodeSummaries[i]) {
|
||||
if (this.selfSummary && nodeSummaries[i]) {
|
||||
metadataList[i]["sectionSummary"] = nodeSummaries[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,33 +21,25 @@ export const defaultKeywordExtractorPromptTemplate = ({
|
||||
contextStr = "",
|
||||
keywords = 5,
|
||||
}: DefaultKeywordExtractorPromptTemplate) => `${contextStr}
|
||||
|
||||
Give ${keywords} unique keywords for this document.
|
||||
|
||||
Format as comma separated. Keywords:
|
||||
`;
|
||||
Format as comma separated.
|
||||
Keywords: `;
|
||||
|
||||
export const defaultTitleExtractorPromptTemplate = (
|
||||
{ contextStr = "" }: DefaultPromptTemplate = {
|
||||
contextStr: "",
|
||||
},
|
||||
) => `${contextStr}
|
||||
|
||||
Give a title that summarizes all of the unique entities, titles or themes found in the context.
|
||||
|
||||
Title:
|
||||
`;
|
||||
Title: `;
|
||||
|
||||
export const defaultTitleCombinePromptTemplate = (
|
||||
{ contextStr = "" }: DefaultPromptTemplate = {
|
||||
contextStr: "",
|
||||
},
|
||||
) => `${contextStr}
|
||||
|
||||
Based on the above candidate titles and contents, what is the comprehensive title for this document?
|
||||
|
||||
Title:
|
||||
`;
|
||||
Title: `;
|
||||
|
||||
export const defaultQuestionAnswerPromptTemplate = (
|
||||
{ contextStr = "", numQuestions = 5 }: DefaultQuestionAnswerPromptTemplate = {
|
||||
@@ -55,9 +47,7 @@ export const defaultQuestionAnswerPromptTemplate = (
|
||||
numQuestions: 5,
|
||||
},
|
||||
) => `${contextStr}
|
||||
|
||||
Given the contextual informations, generate ${numQuestions} questions this context can provides specific answers to which are unlikely to be found elsewhere.Higher-level summaries of surrounding context may be provideds as well.
|
||||
|
||||
Given the contextual informations, generate ${numQuestions} questions this context can provides specific answers to which are unlikely to be found else where. Higher-level summaries of surrounding context may be provideds as well.
|
||||
Try using these summaries to generate better questions that this context can answer.
|
||||
`;
|
||||
|
||||
@@ -66,11 +56,8 @@ export const defaultSummaryExtractorPromptTemplate = (
|
||||
contextStr: "",
|
||||
},
|
||||
) => `${contextStr}
|
||||
|
||||
Summarize the key topics and entities of the sections.
|
||||
|
||||
Summary:
|
||||
`;
|
||||
Summary: `;
|
||||
|
||||
export const defaultNodeTextTemplate = ({
|
||||
metadataStr = "",
|
||||
|
||||
@@ -16,6 +16,7 @@ export * from "./constants.js";
|
||||
export * from "./embeddings/index.js";
|
||||
export * from "./engines/chat/index.js";
|
||||
export * from "./engines/query/index.js";
|
||||
export * from "./evaluation/index.js";
|
||||
export * from "./extractors/index.js";
|
||||
export * from "./indices/index.js";
|
||||
export * from "./ingestion/index.js";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from "./BaseIndex.js";
|
||||
export * from "./IndexStruct.js";
|
||||
export * from "./json-to-index-struct.js";
|
||||
export * from "./keyword/index.js";
|
||||
export * from "./summary/index.js";
|
||||
export * from "./vectorStore/index.js";
|
||||
|
||||
@@ -24,9 +24,15 @@ export class IndexDict extends IndexStruct {
|
||||
}
|
||||
|
||||
toJson(): Record<string, unknown> {
|
||||
const nodesDict: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, node] of Object.entries(this.nodesDict)) {
|
||||
nodesDict[key] = node.toJSON();
|
||||
}
|
||||
|
||||
return {
|
||||
...super.toJson(),
|
||||
nodesDict: this.nodesDict,
|
||||
nodesDict,
|
||||
type: this.type,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@ export class SummaryIndex extends BaseIndex<IndexList> {
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct = indexStructs[0];
|
||||
indexStruct =
|
||||
indexStructs[0].type === IndexStructType.LIST ? indexStructs[0] : null;
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
options.indexId,
|
||||
@@ -164,7 +165,7 @@ export class SummaryIndex extends BaseIndex<IndexList> {
|
||||
responseSynthesizer?: BaseSynthesizer;
|
||||
preFilters?: unknown;
|
||||
nodePostprocessors?: BaseNodePostprocessor[];
|
||||
}): BaseQueryEngine {
|
||||
}): BaseQueryEngine & RetrieverQueryEngine {
|
||||
let { retriever, responseSynthesizer } = options ?? {};
|
||||
|
||||
if (!retriever) {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { ClipEmbedding } from "../../embeddings/index.js";
|
||||
import { RetrieverQueryEngine } from "../../engines/query/RetrieverQueryEngine.js";
|
||||
import { runTransformations } from "../../ingestion/index.js";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/types.js";
|
||||
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
|
||||
import type {
|
||||
BaseIndexStore,
|
||||
MetadataFilters,
|
||||
@@ -32,10 +33,7 @@ import type {
|
||||
VectorStoreQuery,
|
||||
VectorStoreQueryResult,
|
||||
} from "../../storage/index.js";
|
||||
import {
|
||||
VectorStoreQueryMode,
|
||||
storageContextFromDefaults,
|
||||
} from "../../storage/index.js";
|
||||
import { VectorStoreQueryMode } from "../../storage/vectorStore/types.js";
|
||||
import type { BaseSynthesizer } from "../../synthesizers/types.js";
|
||||
import type { BaseQueryEngine } from "../../types.js";
|
||||
import type { BaseIndexInit } from "../BaseIndex.js";
|
||||
@@ -145,6 +143,10 @@ export class VectorStoreIndex extends BaseIndex<IndexDict> {
|
||||
if (options.indexStruct) {
|
||||
indexStruct = options.indexStruct;
|
||||
} else if (indexStructs.length == 1) {
|
||||
indexStruct =
|
||||
indexStructs[0].type === IndexStructType.SIMPLE_DICT
|
||||
? indexStructs[0]
|
||||
: undefined;
|
||||
indexStruct = indexStructs[0];
|
||||
} else if (indexStructs.length > 1 && options.indexId) {
|
||||
indexStruct = (await indexStore.getIndexStruct(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type OpenAILLM from "openai";
|
||||
import type { ClientOptions as OpenAIClientOptions } from "openai";
|
||||
import type {
|
||||
AnthropicStreamToken,
|
||||
CallbackManager,
|
||||
Event,
|
||||
EventType,
|
||||
@@ -13,11 +12,7 @@ import type { ChatCompletionMessageParam } from "openai/resources/index.js";
|
||||
import type { LLMOptions } from "portkey-ai";
|
||||
import { Tokenizers, globalsHelper } from "../GlobalsHelper.js";
|
||||
import type { AnthropicSession } from "./anthropic.js";
|
||||
import {
|
||||
ANTHROPIC_AI_PROMPT,
|
||||
ANTHROPIC_HUMAN_PROMPT,
|
||||
getAnthropicSession,
|
||||
} from "./anthropic.js";
|
||||
import { getAnthropicSession } from "./anthropic.js";
|
||||
import type { AzureOpenAIConfig } from "./azure.js";
|
||||
import {
|
||||
getAzureBaseUrl,
|
||||
@@ -260,7 +255,7 @@ export class OpenAI extends BaseLLM {
|
||||
stream: false,
|
||||
});
|
||||
|
||||
const content = response.choices[0].message?.content ?? "";
|
||||
const content = response.choices[0].message?.content ?? null;
|
||||
|
||||
const kwargsOutput: Record<string, any> = {};
|
||||
|
||||
@@ -613,12 +608,30 @@ If a question does not make any sense, or is not factually coherent, explain why
|
||||
}
|
||||
}
|
||||
|
||||
export const ALL_AVAILABLE_ANTHROPIC_MODELS = {
|
||||
// both models have 100k context window, see https://docs.anthropic.com/claude/reference/selecting-a-model
|
||||
"claude-2": { contextWindow: 200000 },
|
||||
"claude-instant-1": { contextWindow: 100000 },
|
||||
export const ALL_AVAILABLE_ANTHROPIC_LEGACY_MODELS = {
|
||||
"claude-2.1": {
|
||||
contextWindow: 200000,
|
||||
},
|
||||
"claude-instant-1.2": {
|
||||
contextWindow: 100000,
|
||||
},
|
||||
};
|
||||
|
||||
export const ALL_AVAILABLE_V3_MODELS = {
|
||||
"claude-3-opus": { contextWindow: 200000 },
|
||||
"claude-3-sonnet": { contextWindow: 200000 },
|
||||
};
|
||||
|
||||
export const ALL_AVAILABLE_ANTHROPIC_MODELS = {
|
||||
...ALL_AVAILABLE_ANTHROPIC_LEGACY_MODELS,
|
||||
...ALL_AVAILABLE_V3_MODELS,
|
||||
};
|
||||
|
||||
const AVAILABLE_ANTHROPIC_MODELS_WITHOUT_DATE: { [key: string]: string } = {
|
||||
"claude-3-opus": "claude-3-opus-20240229",
|
||||
"claude-3-sonnet": "claude-3-sonnet-20240229",
|
||||
} as { [key in keyof typeof ALL_AVAILABLE_ANTHROPIC_MODELS]: string };
|
||||
|
||||
/**
|
||||
* Anthropic LLM implementation
|
||||
*/
|
||||
@@ -640,7 +653,7 @@ export class Anthropic extends BaseLLM {
|
||||
|
||||
constructor(init?: Partial<Anthropic>) {
|
||||
super();
|
||||
this.model = init?.model ?? "claude-2";
|
||||
this.model = init?.model ?? "claude-3-opus";
|
||||
this.temperature = init?.temperature ?? 0.1;
|
||||
this.topP = init?.topP ?? 0.999; // Per Ben Mann
|
||||
this.maxTokens = init?.maxTokens ?? undefined;
|
||||
@@ -674,21 +687,24 @@ export class Anthropic extends BaseLLM {
|
||||
};
|
||||
}
|
||||
|
||||
mapMessagesToPrompt(messages: ChatMessage[]) {
|
||||
return (
|
||||
messages.reduce((acc, message) => {
|
||||
return (
|
||||
acc +
|
||||
`${
|
||||
message.role === "system"
|
||||
? ""
|
||||
: message.role === "assistant"
|
||||
? ANTHROPIC_AI_PROMPT + " "
|
||||
: ANTHROPIC_HUMAN_PROMPT + " "
|
||||
}${message.content.trim()}`
|
||||
);
|
||||
}, "") + ANTHROPIC_AI_PROMPT
|
||||
);
|
||||
getModelName = (model: string): string => {
|
||||
if (Object.keys(AVAILABLE_ANTHROPIC_MODELS_WITHOUT_DATE).includes(model)) {
|
||||
return AVAILABLE_ANTHROPIC_MODELS_WITHOUT_DATE[model];
|
||||
}
|
||||
return model;
|
||||
};
|
||||
|
||||
formatMessages(messages: ChatMessage[]) {
|
||||
return messages.map((message) => {
|
||||
if (message.role !== "user" && message.role !== "assistant") {
|
||||
throw new Error("Unsupported Anthropic role");
|
||||
}
|
||||
|
||||
return {
|
||||
content: message.content,
|
||||
role: message.role,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
chat(
|
||||
@@ -698,49 +714,67 @@ export class Anthropic extends BaseLLM {
|
||||
async chat(
|
||||
params: LLMChatParamsNonStreaming | LLMChatParamsStreaming,
|
||||
): Promise<ChatResponse | AsyncIterable<ChatResponseChunk>> {
|
||||
const { messages, parentEvent, stream } = params;
|
||||
let { messages } = params;
|
||||
|
||||
const { parentEvent, stream } = params;
|
||||
|
||||
let systemPrompt: string | null = null;
|
||||
|
||||
const systemMessages = messages.filter(
|
||||
(message) => message.role === "system",
|
||||
);
|
||||
|
||||
if (systemMessages.length > 0) {
|
||||
systemPrompt = systemMessages
|
||||
.map((message) => message.content)
|
||||
.join("\n");
|
||||
messages = messages.filter((message) => message.role !== "system");
|
||||
}
|
||||
|
||||
//Streaming
|
||||
if (stream) {
|
||||
return this.streamChat(messages, parentEvent);
|
||||
return this.streamChat(messages, parentEvent, systemPrompt);
|
||||
}
|
||||
|
||||
//Non-streaming
|
||||
const response = await this.session.anthropic.completions.create({
|
||||
model: this.model,
|
||||
prompt: this.mapMessagesToPrompt(messages),
|
||||
max_tokens_to_sample: this.maxTokens ?? 100000,
|
||||
const response = await this.session.anthropic.messages.create({
|
||||
model: this.getModelName(this.model),
|
||||
messages: this.formatMessages(messages),
|
||||
max_tokens: this.maxTokens ?? 4096,
|
||||
temperature: this.temperature,
|
||||
top_p: this.topP,
|
||||
...(systemPrompt && { system: systemPrompt }),
|
||||
});
|
||||
|
||||
return {
|
||||
message: { content: response.completion.trimStart(), role: "assistant" },
|
||||
//^ We're trimming the start because Anthropic often starts with a space in the response
|
||||
// That space will be re-added when we generate the next prompt.
|
||||
message: { content: response.content[0].text, role: "assistant" },
|
||||
};
|
||||
}
|
||||
|
||||
protected async *streamChat(
|
||||
messages: ChatMessage[],
|
||||
parentEvent?: Event | undefined,
|
||||
systemPrompt?: string | null,
|
||||
): AsyncIterable<ChatResponseChunk> {
|
||||
// AsyncIterable<AnthropicStreamToken>
|
||||
const stream: AsyncIterable<AnthropicStreamToken> =
|
||||
await this.session.anthropic.completions.create({
|
||||
model: this.model,
|
||||
prompt: this.mapMessagesToPrompt(messages),
|
||||
max_tokens_to_sample: this.maxTokens ?? 100000,
|
||||
temperature: this.temperature,
|
||||
top_p: this.topP,
|
||||
stream: true,
|
||||
});
|
||||
const stream = await this.session.anthropic.messages.create({
|
||||
model: this.getModelName(this.model),
|
||||
messages: this.formatMessages(messages),
|
||||
max_tokens: this.maxTokens ?? 4096,
|
||||
temperature: this.temperature,
|
||||
top_p: this.topP,
|
||||
stream: true,
|
||||
...(systemPrompt && { system: systemPrompt }),
|
||||
});
|
||||
|
||||
let idx_counter: number = 0;
|
||||
for await (const part of stream) {
|
||||
//TODO: LLM Stream Callback, pending re-work.
|
||||
const content =
|
||||
part.type === "content_block_delta" ? part.delta.text : null;
|
||||
|
||||
if (typeof content !== "string") continue;
|
||||
|
||||
idx_counter++;
|
||||
yield { delta: part.completion };
|
||||
yield { delta: content };
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ClientOptions } from "@anthropic-ai/sdk";
|
||||
import Anthropic, { AI_PROMPT, HUMAN_PROMPT } from "@anthropic-ai/sdk";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import _ from "lodash";
|
||||
|
||||
export class AnthropicSession {
|
||||
@@ -7,9 +8,7 @@ export class AnthropicSession {
|
||||
|
||||
constructor(options: ClientOptions = {}) {
|
||||
if (!options.apiKey) {
|
||||
if (typeof process !== undefined) {
|
||||
options.apiKey = process.env.ANTHROPIC_API_KEY;
|
||||
}
|
||||
options.apiKey = getEnv("ANTHROPIC_API_KEY");
|
||||
}
|
||||
|
||||
if (!options.apiKey) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
|
||||
export interface AzureOpenAIConfig {
|
||||
apiKey?: string;
|
||||
endpoint?: string;
|
||||
@@ -67,24 +69,24 @@ export function getAzureConfigFromEnv(
|
||||
return {
|
||||
apiKey:
|
||||
init?.apiKey ??
|
||||
process.env.AZURE_OPENAI_KEY ?? // From Azure docs
|
||||
process.env.OPENAI_API_KEY ?? // Python compatible
|
||||
process.env.AZURE_OPENAI_API_KEY, // LCJS compatible
|
||||
getEnv("AZURE_OPENAI_KEY") ?? // From Azure docs
|
||||
getEnv("OPENAI_API_KEY") ?? // Python compatible
|
||||
getEnv("AZURE_OPENAI_API_KEY"), // LCJS compatible
|
||||
endpoint:
|
||||
init?.endpoint ??
|
||||
process.env.AZURE_OPENAI_ENDPOINT ?? // From Azure docs
|
||||
process.env.OPENAI_API_BASE ?? // Python compatible
|
||||
process.env.AZURE_OPENAI_API_INSTANCE_NAME, // LCJS compatible
|
||||
getEnv("AZURE_OPENAI_ENDPOINT") ?? // From Azure docs
|
||||
getEnv("OPENAI_API_BASE") ?? // Python compatible
|
||||
getEnv("AZURE_OPENAI_API_INSTANCE_NAME"), // LCJS compatible
|
||||
apiVersion:
|
||||
init?.apiVersion ??
|
||||
process.env.AZURE_OPENAI_API_VERSION ?? // From Azure docs
|
||||
process.env.OPENAI_API_VERSION ?? // Python compatible
|
||||
process.env.AZURE_OPENAI_API_VERSION ?? // LCJS compatible
|
||||
getEnv("AZURE_OPENAI_API_VERSION") ?? // From Azure docs
|
||||
getEnv("OPENAI_API_VERSION") ?? // Python compatible
|
||||
getEnv("AZURE_OPENAI_API_VERSION") ?? // LCJS compatible
|
||||
DEFAULT_API_VERSION,
|
||||
deploymentName:
|
||||
init?.deploymentName ??
|
||||
process.env.AZURE_OPENAI_DEPLOYMENT ?? // From Azure docs
|
||||
process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME ?? // LCJS compatible
|
||||
getEnv("AZURE_OPENAI_DEPLOYMENT") ?? // From Azure docs
|
||||
getEnv("AZURE_OPENAI_API_DEPLOYMENT_NAME") ?? // LCJS compatible
|
||||
init?.model, // Fall back to model name, Python compatible
|
||||
};
|
||||
}
|
||||
@@ -113,8 +115,8 @@ export function getAzureModel(openAIModel: string) {
|
||||
|
||||
export function shouldUseAzure() {
|
||||
return (
|
||||
process.env.AZURE_OPENAI_ENDPOINT ||
|
||||
process.env.AZURE_OPENAI_API_INSTANCE_NAME ||
|
||||
process.env.OPENAI_API_TYPE === "azure"
|
||||
getEnv("AZURE_OPENAI_ENDPOINT") ||
|
||||
getEnv("AZURE_OPENAI_API_INSTANCE_NAME") ||
|
||||
getEnv("OPENAI_API_TYPE") === "azure"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { OpenAI } from "./LLM.js";
|
||||
|
||||
export class FireworksLLM extends OpenAI {
|
||||
constructor(init?: Partial<OpenAI>) {
|
||||
const {
|
||||
apiKey = process.env.FIREWORKS_API_KEY,
|
||||
apiKey = getEnv("FIREWORKS_API_KEY"),
|
||||
additionalSessionOptions = {},
|
||||
model = "accounts/fireworks/models/mixtral-8x7b-instruct",
|
||||
...rest
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { OpenAI } from "./LLM.js";
|
||||
|
||||
export class Groq extends OpenAI {
|
||||
constructor(init?: Partial<OpenAI>) {
|
||||
const {
|
||||
apiKey = getEnv("GROQ_API_KEY"),
|
||||
additionalSessionOptions = {},
|
||||
model = "mixtral-8x7b-32768",
|
||||
...rest
|
||||
} = init ?? {};
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error("Set Groq Key in GROQ_API_KEY env variable"); // Tell user to set correct env variable, and not OPENAI_API_KEY
|
||||
}
|
||||
|
||||
additionalSessionOptions.baseURL =
|
||||
additionalSessionOptions.baseURL ?? "https://api.groq.com/openai/v1";
|
||||
|
||||
super({
|
||||
apiKey,
|
||||
additionalSessionOptions,
|
||||
model,
|
||||
...rest,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./LLM.js";
|
||||
export { FireworksLLM } from "./fireworks.js";
|
||||
export { Groq } from "./groq.js";
|
||||
export {
|
||||
ALL_AVAILABLE_MISTRAL_MODELS,
|
||||
MistralAI,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type {
|
||||
CallbackManager,
|
||||
Event,
|
||||
@@ -27,9 +28,7 @@ export class MistralAISession {
|
||||
if (init?.apiKey) {
|
||||
this.apiKey = init?.apiKey;
|
||||
} else {
|
||||
if (typeof process !== undefined) {
|
||||
this.apiKey = process.env.MISTRAL_API_KEY;
|
||||
}
|
||||
this.apiKey = getEnv("MISTRAL_API_KEY");
|
||||
}
|
||||
if (!this.apiKey) {
|
||||
throw new Error("Set Mistral API key in MISTRAL_API_KEY env variable"); // Overriding MistralAI package's error message
|
||||
|
||||
@@ -37,14 +37,18 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
additionalChatOptions?: Record<string, unknown>;
|
||||
callbackManager?: CallbackManager;
|
||||
|
||||
protected modelMetadata: Partial<LLMMetadata>;
|
||||
|
||||
constructor(
|
||||
init: Partial<Ollama> & {
|
||||
// model is required
|
||||
model: string;
|
||||
modelMetadata?: Partial<LLMMetadata>;
|
||||
},
|
||||
) {
|
||||
super();
|
||||
this.model = init.model;
|
||||
this.modelMetadata = init.modelMetadata ?? {};
|
||||
Object.assign(this, init);
|
||||
}
|
||||
|
||||
@@ -56,6 +60,7 @@ export class Ollama extends BaseEmbedding implements LLM {
|
||||
maxTokens: undefined,
|
||||
contextWindow: this.contextWindow,
|
||||
tokenizer: undefined,
|
||||
...this.modelMetadata,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import _ from "lodash";
|
||||
import type { ClientOptions } from "openai";
|
||||
import OpenAI from "openai";
|
||||
@@ -13,9 +14,7 @@ export class OpenAISession {
|
||||
|
||||
constructor(options: ClientOptions & { azure?: boolean } = {}) {
|
||||
if (!options.apiKey) {
|
||||
if (typeof process !== undefined) {
|
||||
options.apiKey = process.env.OPENAI_API_KEY;
|
||||
}
|
||||
options.apiKey = getEnv("OPENAI_API_KEY");
|
||||
}
|
||||
|
||||
if (!options.apiKey) {
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import _ from "lodash";
|
||||
import type { LLMOptions } from "portkey-ai";
|
||||
import { Portkey } from "portkey-ai";
|
||||
|
||||
export const readEnv = (
|
||||
env: string,
|
||||
default_val?: string,
|
||||
): string | undefined => {
|
||||
if (typeof process !== "undefined") {
|
||||
return process.env?.[env] ?? default_val;
|
||||
}
|
||||
return default_val;
|
||||
};
|
||||
|
||||
interface PortkeyOptions {
|
||||
apiKey?: string;
|
||||
baseURL?: string;
|
||||
@@ -24,11 +15,11 @@ export class PortkeySession {
|
||||
|
||||
constructor(options: PortkeyOptions = {}) {
|
||||
if (!options.apiKey) {
|
||||
options.apiKey = readEnv("PORTKEY_API_KEY");
|
||||
options.apiKey = getEnv("PORTKEY_API_KEY");
|
||||
}
|
||||
|
||||
if (!options.baseURL) {
|
||||
options.baseURL = readEnv("PORTKEY_BASE_URL", "https://api.portkey.ai");
|
||||
options.baseURL = getEnv("PORTKEY_BASE_URL") ?? "https://api.portkey.ai";
|
||||
}
|
||||
|
||||
this.portkey = new Portkey({});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import Replicate from "replicate";
|
||||
|
||||
export class ReplicateSession {
|
||||
@@ -7,8 +8,8 @@ export class ReplicateSession {
|
||||
constructor(replicateKey: string | null = null) {
|
||||
if (replicateKey) {
|
||||
this.replicateKey = replicateKey;
|
||||
} else if (process.env.REPLICATE_API_TOKEN) {
|
||||
this.replicateKey = process.env.REPLICATE_API_TOKEN;
|
||||
} else if (getEnv("REPLICATE_API_TOKEN")) {
|
||||
this.replicateKey = getEnv("REPLICATE_API_TOKEN") as string;
|
||||
} else {
|
||||
throw new Error(
|
||||
"Set Replicate token in REPLICATE_API_TOKEN env variable",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { OpenAI } from "./LLM.js";
|
||||
|
||||
export class TogetherLLM extends OpenAI {
|
||||
constructor(init?: Partial<OpenAI>) {
|
||||
const {
|
||||
apiKey = process.env.TOGETHER_API_KEY,
|
||||
apiKey = getEnv("TOGETHER_API_KEY"),
|
||||
additionalSessionOptions = {},
|
||||
model = "togethercomputer/llama-2-7b-chat",
|
||||
...rest
|
||||
|
||||
@@ -27,12 +27,14 @@ export class SimpleNodeParser implements NodeParser {
|
||||
includePrevNextRel?: boolean;
|
||||
chunkSize?: number;
|
||||
chunkOverlap?: number;
|
||||
splitLongSentences?: boolean;
|
||||
}) {
|
||||
this.textSplitter =
|
||||
init?.textSplitter ??
|
||||
new SentenceSplitter({
|
||||
chunkSize: init?.chunkSize ?? DEFAULT_CHUNK_SIZE,
|
||||
chunkOverlap: init?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP,
|
||||
splitLongSentences: init?.splitLongSentences ?? false,
|
||||
});
|
||||
this.includeMetadata = init?.includeMetadata ?? true;
|
||||
this.includePrevNextRel = init?.includePrevNextRel ?? true;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type {
|
||||
BaseServiceParams,
|
||||
SubtitleFormat,
|
||||
@@ -28,7 +29,7 @@ abstract class AssemblyAIReader implements BaseReader {
|
||||
options = {};
|
||||
}
|
||||
if (!options.apiKey) {
|
||||
options.apiKey = process.env.ASSEMBLYAI_API_KEY;
|
||||
options.apiKey = getEnv("ASSEMBLYAI_API_KEY");
|
||||
}
|
||||
if (!options.apiKey) {
|
||||
throw new Error(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import type { ParseConfig } from "papaparse";
|
||||
import Papa from "papaparse";
|
||||
import { Document } from "../Node.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import mammoth from "mammoth";
|
||||
import { Document } from "../Node.js";
|
||||
import type { FileReader } from "./type.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import { Document } from "../Node.js";
|
||||
import type { FileReader } from "./type.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import type { Document } from "../Node.js";
|
||||
import { ImageDocument } from "../Node.js";
|
||||
import type { FileReader } from "./type.js";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import { defaultFS, getEnv, type GenericFileSystem } from "@llamaindex/env";
|
||||
import { Document } from "../Node.js";
|
||||
import type { FileReader } from "./type.js";
|
||||
|
||||
@@ -24,7 +23,7 @@ export class LlamaParseReader implements FileReader {
|
||||
|
||||
constructor(params: Partial<LlamaParseReader> = {}) {
|
||||
Object.assign(this, params);
|
||||
params.apiKey = params.apiKey ?? process.env.LLAMA_CLOUD_API_KEY;
|
||||
params.apiKey = params.apiKey ?? getEnv("LLAMA_CLOUD_API_KEY");
|
||||
if (!params.apiKey) {
|
||||
throw new Error(
|
||||
"API Key is required for LlamaParseReader. Please pass the apiKey parameter or set the LLAMA_CLOUD_API_KEY environment variable.",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import { Document } from "../Node.js";
|
||||
import type { FileReader } from "./type.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { createSHA256, defaultFS } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import { Document } from "../Node.js";
|
||||
import type { BaseReader } from "./type.js";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CompleteFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS, path } from "@llamaindex/env";
|
||||
import type { CompleteFileSystem } from "@llamaindex/env/type";
|
||||
import { Document } from "../Node.js";
|
||||
import { walk } from "../storage/FileSystem.js";
|
||||
import { PapaCSVReader } from "./CSVReader.js";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CompleteFileSystem } from "@llamaindex/env/type";
|
||||
import type { CompleteFileSystem } from "@llamaindex/env";
|
||||
import type { Document } from "../Node.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import type {
|
||||
GenericFileSystem,
|
||||
WalkableFileSystem,
|
||||
} from "@llamaindex/env/type";
|
||||
import type { GenericFileSystem, WalkableFileSystem } from "@llamaindex/env";
|
||||
// FS utility functions
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS, path } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import {
|
||||
DEFAULT_IMAGE_VECTOR_NAMESPACE,
|
||||
DEFAULT_NAMESPACE,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _, * as lodash from "lodash";
|
||||
import _ from "lodash";
|
||||
import type { BaseNode } from "../../Node.js";
|
||||
import { ObjectType } from "../../Node.js";
|
||||
import { DEFAULT_NAMESPACE } from "../constants.js";
|
||||
@@ -123,10 +123,10 @@ export class KVDocumentStore extends BaseDocumentStore {
|
||||
|
||||
const refDocInfo = await this.kvstore.get(refDocId, this.refDocCollection);
|
||||
if (!_.isNil(refDocInfo)) {
|
||||
lodash.pull(refDocInfo.docIds, docId);
|
||||
!_.pull(refDocInfo.nodeIds, docId);
|
||||
|
||||
if (refDocInfo.docIds.length > 0) {
|
||||
this.kvstore.put(refDocId, refDocInfo.toDict(), this.refDocCollection);
|
||||
if (refDocInfo.nodeIds.length > 0) {
|
||||
this.kvstore.put(refDocId, refDocInfo, this.refDocCollection);
|
||||
}
|
||||
this.kvstore.delete(refDocId, this.metadataCollection);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS, path } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
DEFAULT_DOC_STORE_PERSIST_FILENAME,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { BaseNode } from "../../Node.js";
|
||||
import {
|
||||
DEFAULT_DOC_STORE_PERSIST_FILENAME,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS, path } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import {
|
||||
DEFAULT_INDEX_STORE_PERSIST_FILENAME,
|
||||
DEFAULT_PERSIST_DIR,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import type { IndexStruct } from "../../indices/IndexStruct.js";
|
||||
import {
|
||||
DEFAULT_INDEX_STORE_PERSIST_FILENAME,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
import { defaultFS, path } from "@llamaindex/env";
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import _ from "lodash";
|
||||
import { exists } from "../FileSystem.js";
|
||||
import { DEFAULT_COLLECTION } from "../constants.js";
|
||||
@@ -54,6 +54,9 @@ export class SimpleKVStore extends BaseKVStore {
|
||||
): Promise<boolean> {
|
||||
if (key in this.data[collection]) {
|
||||
delete this.data[collection][key];
|
||||
if (this.persistPath) {
|
||||
await this.persist(this.persistPath, this.fs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GenericFileSystem } from "@llamaindex/env/type";
|
||||
import type { GenericFileSystem } from "@llamaindex/env";
|
||||
const defaultCollection = "data";
|
||||
|
||||
type StoredValue = Record<string, any> | null;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AstraDB } from "@datastax/astra-db-ts";
|
||||
import type { Collection } from "@datastax/astra-db-ts/dist/collections";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type { BaseNode } from "../../Node.js";
|
||||
import { MetadataMode } from "../../Node.js";
|
||||
import type {
|
||||
@@ -34,9 +35,8 @@ export class AstraDBVectorStore implements VectorStore {
|
||||
if (init?.astraDBClient) {
|
||||
this.astraDBClient = init.astraDBClient;
|
||||
} else {
|
||||
const token =
|
||||
init?.params?.token ?? process.env.ASTRA_DB_APPLICATION_TOKEN;
|
||||
const endpoint = init?.params?.endpoint ?? process.env.ASTRA_DB_ENDPOINT;
|
||||
const token = init?.params?.token ?? getEnv("ASTRA_DB_APPLICATION_TOKEN");
|
||||
const endpoint = init?.params?.endpoint ?? getEnv("ASTRA_DB_ENDPOINT");
|
||||
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
@@ -48,7 +48,7 @@ export class AstraDBVectorStore implements VectorStore {
|
||||
}
|
||||
const namespace =
|
||||
init?.params?.namespace ??
|
||||
process.env.ASTRA_DB_NAMESPACE ??
|
||||
getEnv("ASTRA_DB_NAMESPACE") ??
|
||||
"default_keyspace";
|
||||
this.astraDBClient = new AstraDB(token, endpoint, namespace);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type { BulkWriteOptions, Collection } from "mongodb";
|
||||
import { MongoClient } from "mongodb";
|
||||
import type { BaseNode } from "../../Node.js";
|
||||
@@ -44,7 +45,7 @@ export class MongoDBAtlasVectorSearch implements VectorStore {
|
||||
if (init.mongodbClient) {
|
||||
this.mongodbClient = init.mongodbClient;
|
||||
} else {
|
||||
const mongoUri = process.env.MONGODB_URI;
|
||||
const mongoUri = getEnv("MONGODB_URI");
|
||||
if (!mongoUri) {
|
||||
throw new Error(
|
||||
"Must specify MONGODB_URI via env variable if not directly passing in client.",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user