Compare commits

...

35 Commits

Author SHA1 Message Date
Emanuel Ferreira 8f758f3212 changeset 2024-03-28 16:14:31 -03:00
Emanuel Ferreira 25b47b6ee2 feat: add result type json 2024-03-28 16:11:33 -03:00
Emanuel Ferreira 1115f83b8f fix: pipeline not found (#672) 2024-03-28 15:31:18 -03:00
Thuc Pham 60a1603636 fix: make edge run build after core (#670) 2024-03-28 18:26:35 +08:00
Peter Goldstein ea467fa031 Update to latest supported version list as of 2024-04-02. (#669) 2024-03-28 10:53:33 +07:00
Marcus Schiesser b0e6f73b1d docs: update readme for Edge runtime 2024-03-26 15:18:19 +08:00
Marcus Schiesser 6d9e015b5e feat: use claude3 with react agent (#661)
Co-authored-by: Emanuel Ferreira <contatoferreirads@gmail.com>
2024-03-22 09:25:31 -03:00
Thuc Pham fececd89ab feat: add tool factory (#663) 2024-03-22 14:40:41 +07:00
Marcus Schiesser 48e287892f test: use unique tmp dir for storage tests and wait to clean VectorStoreIndex files 2024-03-21 13:04:25 +07:00
Marcus Schiesser f118400820 docs: Add changeset instructions for PRs 2024-03-20 11:45:33 +07:00
Marcus Schiesser 3f8407c7af docs: changeset for pipeline.register added 2024-03-20 10:20:30 +07:00
Marcus Schiesser 83317739c7 feat: add pipeline.register (#589) 2024-03-19 13:32:32 -07:00
Thuc Pham 0b665bd1ca feat: add wikipedia tool (#648)
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
2024-03-19 11:31:08 +07:00
Alex Yang 98d4cbdf95 fix: support import subdirectory (#655) 2024-03-18 21:00:46 -05:00
Marcus Schiesser 6cb75b54a0 docs: update release process 2024-03-18 16:22:59 +07:00
Marcus Schiesser 53edfe93cf release llamaindex@0.2.1 2024-03-18 16:17:58 +07:00
Marcus Schiesser b856deae43 fix: fix syncing edge with core version 2024-03-18 15:53:31 +07:00
Marcus Schiesser 259c842259 Support NextJS edge runtime (#618) 2024-03-18 15:13:27 +07:00
shodevacc ffb195ea7a Fix: Metadata filters doesn't seem to work for Qdrant (#623) 2024-03-18 11:53:51 +07:00
Alex Yang b4677534d1 ci: install node_modules (#653) 2024-03-18 12:49:28 +08:00
Peli de Halleux f967b82467 [docs] missing await in sample (#650) 2024-03-15 16:23:27 -03:00
Marcus Schiesser c81946930e test: fix openai mock 2024-03-15 15:20:57 +07:00
Marcus Schiesser 1008b775a4 test: cleaned up tests and added test to ignore duplicates 2024-03-15 12:05:58 +07:00
Huu Le (Lee) 41210dfc51 feat: Add auto create collection and node metadata for Milvus vector store (#645) 2024-03-15 10:46:25 +07:00
Emanuel Ferreira 67b7272249 feat: expected minor version (#644) 2024-03-14 09:34:21 -03:00
Marcus Schiesser 964e045903 feat: add support for snapshots 2024-03-14 10:23:58 +07:00
Marcus Schiesser 137cf67f40 fix: Use Pinecone namespaces for all operations (#633) 2024-03-14 10:15:52 +07:00
Emanuel Ferreira 309a526e3c RELEASING: Releasing 5 package(s) (#643) 2024-03-13 22:17:27 -03:00
yisding dd95927498 Claude haiku (#642) 2024-03-13 19:57:45 -03:00
Thuc Pham 4f72feae91 Feat: add tools module (#621) 2024-03-13 16:41:36 +07:00
Marcus Schiesser 3cd8f9f597 refactor: move create-llama to own repo (#641) 2024-03-13 15:53:33 +07:00
Huu Le (Lee) d2e8d0c62a feat: Add Milvus vector store (#640)
Co-authored-by: Michael Schramm <michael@tucan.ai>
2024-03-13 13:55:48 +07:00
Huu Le (Lee) fafbd8c9c7 fix: add missing env value; improve docs and error message (#638) 2024-03-13 09:08:53 +07:00
Marcus Schiesser a40c91b054 docs: fixed path 2024-03-12 14:45:27 +07:00
Marcus Schiesser 98894055c6 fix: create-llama release 2024-03-12 13:42:38 +07:00
279 changed files with 3245 additions and 8017 deletions
-5
View File
@@ -1,5 +0,0 @@
---
"llamaindex": patch
---
feat: experimental package + json query engine
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": true,
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
-12
View File
@@ -1,12 +0,0 @@
---
"llamaindex": patch
"@llamaindex/core-test": patch
---
- Add missing exports:
- `IndexStructType`,
- `IndexDict`,
- `jsonToIndexStruct`,
- `IndexList`,
- `IndexStruct`
- Fix `IndexDict.toJson()` method
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
Add pipeline.register to create a managed index in LlamaCloud
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
fix: make edge run build after core
@@ -2,4 +2,4 @@
"llamaindex": patch
---
Add streaming to agents
feat: add tool factory
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
fix: throw error when no pipelines exist for the retriever
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
Update the list of supported Azure OpenAI API versions as of 2024-04-02.
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
fix: support import subdirectory
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
feat: use claude3 with react agent
-5
View File
@@ -1,5 +0,0 @@
---
"llamaindex": minor
---
Use parameter object for retrieve function of Retriever (to align usage with query function of QueryEngine)
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
feat: add wikipedia tool
+5
View File
@@ -0,0 +1,5 @@
---
"llamaindex": patch
---
feat: add result type json
-68
View File
@@ -1,68 +0,0 @@
name: E2E Tests
on:
push:
branches: [main]
pull_request:
paths:
- "packages/create-llama/**"
- ".github/workflows/e2e.yml"
branches: [main]
env:
POETRY_VERSION: "1.6.1"
jobs:
e2e:
name: create-llama
timeout-minutes: 60
strategy:
fail-fast: true
matrix:
node-version: [18, 20]
python-version: ["3.11"]
os: [macos-latest, windows-latest]
defaults:
run:
shell: bash
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: ${{ env.POETRY_VERSION }}
- uses: pnpm/action-setup@v2
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps
working-directory: ./packages/create-llama
- name: Build create-llama
run: pnpm run build
working-directory: ./packages/create-llama
- name: Pack
run: pnpm pack --pack-destination ./output
working-directory: ./packages/create-llama
- name: Extract Pack
run: tar -xvzf ./output/*.tgz -C ./output
working-directory: ./packages/create-llama
- name: Run Playwright tests
run: pnpm exec playwright test
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
working-directory: ./packages/create-llama
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: ./packages/create-llama/playwright-report/
retention-days: 30
+8
View File
@@ -14,6 +14,14 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Publish @llamaindex/env
run: npx jsr publish
+18
View File
@@ -44,6 +44,24 @@ jobs:
name: typecheck-build-dist
path: ./packages/core/dist
if-no-files-found: error
core-edge-runtime:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run build --filter @llamaindex/edge
- name: Build Edge Runtime
run: pnpm run build
working-directory: ./packages/edge/e2e/test-edge-runtime
typecheck-examples:
runs-on: ubuntu-latest
-1
View File
@@ -45,7 +45,6 @@ playwright-report/
blob-report/
playwright/.cache/
.tsbuildinfo
packages/create-llama/e2e/cache
# intellij
**/.idea
-1
View File
@@ -4,4 +4,3 @@ pnpm-lock.yaml
lib/
dist/
.docusaurus/
packages/create-llama/e2e/cache/
+18 -4
View File
@@ -79,13 +79,27 @@ That should start a webserver which will serve the docs on https://localhost:300
Any changes you make should be reflected in the browser. If you need to regenerate the API docs and find that your TSDoc isn't getting the updates, feel free to remove apps/docs/api. It will automatically regenerate itself when you run pnpm start again.
## Publishing
## Changeset
To publish a new version of the library, run
We use [changesets](https://github.com/changesets/changesets) for managing versions and changelogs. To create a new changeset, run:
```
pnpm changeset
```
Please send a descriptive changeset for each PR.
## Publishing (maintainers only)
To publish a new version of the library, first create a new version:
```shell
pnpm new-version
```
If everything looks good, commit the generated files and release the new version:
```shell
pnpm new-llamaindex
pnpm new-create-llama
pnpm release
git push # push to the main branch
git push --tags
+63 -6
View File
@@ -93,20 +93,28 @@ Check out our NextJS playground at https://llama-playground.vercel.app/. The sou
- [SimplePrompt](/packages/core/src/Prompt.ts): A simple standardized function call definition that takes in inputs and formats them in a template literal. SimplePrompts can be specialized using currying and combined using other SimplePrompt functions.
## Note: NextJS:
## Using NextJS
If you're using NextJS App Router, you'll need to use the NodeJS runtime (default) and add the following config to your next.config.js to have it use imports/exports in the same way Node does.
If you're using the NextJS App Router, you can choose between the Node.js and the [Edge runtime](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#edge-runtime).
```js
export const runtime = "nodejs"; // default
With NextJS 13 and 14, using the Node.js runtime is the default. You can explicitly set the Edge runtime in your [router handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) by adding this line:
```typescript
export const runtime = "edge";
```
The following sections explain further differences in using the Node.js or Edge runtime.
### Using the Node.js runtime
Add the following config to your `next.config.js` to ignore specific packages in the server-side bundling:
```js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverComponentsExternalPackages: ["pdf2json"],
serverComponentsExternalPackages: ["pdf2json", "@zilliz/milvus2-sdk-node"],
},
webpack: (config) => {
config.resolve.alias = {
@@ -121,10 +129,59 @@ const nextConfig = {
module.exports = nextConfig;
```
### Using the Edge runtime
We publish a dedicated package (`@llamaindex/edge` instead of `llamaindex`) for using the Edge runtime. To use it, first install the package:
```shell
pnpm install @llamaindex/edge
```
> _Note_: Ensure that your `package.json` doesn't include the `llamaindex` package if you're using `@llamaindex/edge`.
Then make sure to use the correct import statement in your code:
```typescript
// replace 'llamaindex' with '@llamaindex/edge'
import {} from "@llamaindex/edge";
```
A further difference is that the `@llamaindex/edge` package doesn't export classes from the `readers` or `storage` folders. The reason is that most of these classes are not compatible with the Edge runtime.
If you need any of those classes, you have to import them instead directly. Here's an example for importing the `PineconeVectorStore` class:
```typescript
import { PineconeVectorStore } from "@llamaindex/edge/storage/vectorStore/PineconeVectorStore";
```
As the `PDFReader` is not with the Edge runtime, here's how to use the `SimpleDirectoryReader` with the `LlamaParseReader` to load PDFs:
```typescript
import { SimpleDirectoryReader } from "@llamaindex/edge/readers/SimpleDirectoryReader";
import { LlamaParseReader } from "@llamaindex/edge/readers/LlamaParseReader";
export const DATA_DIR = "./data";
export async function getDocuments() {
const reader = new SimpleDirectoryReader();
// Load PDFs using LlamaParseReader
return await reader.loadData({
directoryPath: DATA_DIR,
fileExtToReader: {
pdf: new LlamaParseReader({ resultType: "markdown" }),
},
});
}
```
> _Note_: Reader classes have to be added explictly to the `fileExtToReader` map in the Edge version of the `SimpleDirectoryReader`.
You'll find a complete example of using the Edge runtime with LlamaIndexTS here: https://github.com/run-llama/create_llama_projects/tree/main/nextjs-edge-llamaparse
## Supported LLMs:
- OpenAI GPT-3.5-turbo and GPT-4
- Anthropic Claude Instant and Claude 2
- Anthropic Claude 3 (Opus, Sonnet, and Haiku) and the legacy models (Claude 2 and Instant)
- Groq LLMs
- Llama2 Chat LLMs (70B, 13B, and 7B parameters)
- MistralAI Chat LLMs
@@ -36,7 +36,7 @@ const processor = new SimilarityPostprocessor({
similarityCutoff: 0.7,
});
const filteredNodes = processor.postprocessNodes(nodes);
const filteredNodes = await processor.postprocessNodes(nodes);
// cohere rerank: rerank nodes given query using trained model
const reranker = new CohereRerank({
+14
View File
@@ -0,0 +1,14 @@
# examples
## 0.0.4
### Patch Changes
- d2e8d0c: add support for Milvus vector store
- Updated dependencies [d2e8d0c]
- Updated dependencies [aefc326]
- Updated dependencies [484a710]
- Updated dependencies [d766bd0]
- Updated dependencies [dd95927]
- Updated dependencies [bf583a7]
- llamaindex@0.2.0
+8 -2
View File
@@ -1,4 +1,4 @@
import { FunctionTool, ReActAgent } from "llamaindex";
import { Anthropic, FunctionTool, ReActAgent } from "llamaindex";
// Define a function to sum two numbers
function sumNumbers({ a, b }: { a: number; b: number }): number {
@@ -56,8 +56,14 @@ async function main() {
parameters: divideJSON,
});
// Create an OpenAIAgent with the function tools
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
model: "claude-3-opus",
});
// Create an ReActAgent with the function tools
const agent = new ReActAgent({
llm: anthropic,
tools: [functionTool, functionTool2],
verbose: true,
});
+23
View File
@@ -0,0 +1,23 @@
import { OpenAIAgent, WikipediaTool } from "llamaindex";
async function main() {
const wikipediaTool = new WikipediaTool();
// Create an OpenAIAgent with the function tools
const agent = new OpenAIAgent({
tools: [wikipediaTool],
verbose: true,
});
// Chat with the agent
const response = await agent.chat({
message: "Where is Ho Chi Minh City?",
});
// Print the response
console.log(response);
}
main().then(() => {
console.log("Done");
});
+19
View File
@@ -0,0 +1,19 @@
import { Anthropic } from "llamaindex";
(async () => {
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
model: "claude-3-haiku",
});
const result = 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",
},
],
});
console.log(result);
})();
+2 -2
View File
@@ -32,10 +32,10 @@ run `ts-node astradb/example`
This sample loads the same dataset of movie reviews as the Astra Portal sample dataset. (Feel free to load the data in your the Astra Data Explorer to compare)
run `ts-node astradb/load`
run `npx ts-node astradb/load`
### Use RAG to Query the data
Check out your data in the Astra Data Explorer and change the sample query as you see fit.
run `ts-node astradb/query`
run `npx ts-node astradb/query`
+8
View File
@@ -31,3 +31,11 @@ This example shows how to use the managed index with a query engine.
```shell
pnpx ts-node cloud/query.ts
```
## Pipeline
This example shows how to create a managed index with a pipeline.
```shell
pnpx ts-node cloud/pipeline.ts
```
+34
View File
@@ -0,0 +1,34 @@
import fs from "node:fs/promises";
import {
Document,
IngestionPipeline,
OpenAIEmbedding,
SimpleNodeParser,
} from "llamaindex";
async function main() {
// Load essay from abramov.txt in Node
const path = "node_modules/llamaindex/examples/abramov.txt";
const essay = await fs.readFile(path, "utf-8");
// Create Document object with essay
const document = new Document({ text: essay, id_: path });
const pipeline = new IngestionPipeline({
name: "pipeline",
transformations: [
new SimpleNodeParser({ chunkSize: 1024, chunkOverlap: 20 }),
new OpenAIEmbedding({ apiKey: "api-key" }),
],
});
const pipelineId = await pipeline.register({
documents: [document],
verbose: true,
});
console.log(`Pipeline with id ${pipelineId} successfully created.`);
}
main().catch(console.error);
+34
View File
@@ -0,0 +1,34 @@
# Milvus Vector Store
Here are two sample scripts which work with loading and querying data from a Milvus Vector Store.
## Prerequisites
- An Milvus Vector Database
- Hosted https://milvus.io/
- Self Hosted https://milvus.io/docs/install_standalone-docker.md
- An OpenAI API Key
## Setup
1. Set your env variables:
- `MILVUS_ADDRESS`: Address of your Milvus Vector Store (like localhost:19530)
- `MILVUS_USERNAME`: empty or username for your Milvus Vector Store
- `MILVUS_PASSWORD`: empty or password for your Milvus Vector Store
- `OPENAI_API_KEY`: Your OpenAI key
2. `cd` Into the `examples` directory
3. run `npm i`
## Load the data
This sample loads the same dataset of movie reviews as sample dataset. You can install https://github.com/zilliztech/attu to inspect the loaded data.
run `npx ts-node milvus/load`
## Use RAG to Query the data
Check out your data in Attu and change the sample query as you see fit.
run `npx ts-node milvus/query`
+26
View File
@@ -0,0 +1,26 @@
import {
MilvusVectorStore,
PapaCSVReader,
storageContextFromDefaults,
VectorStoreIndex,
} from "llamaindex";
const collectionName = "movie_reviews";
async function main() {
try {
const reader = new PapaCSVReader(false);
const docs = await reader.loadData("./data/movie_reviews.csv");
const vectorStore = new MilvusVectorStore({ collection: collectionName });
const ctx = await storageContextFromDefaults({ vectorStore });
const index = await VectorStoreIndex.fromDocuments(docs, {
storageContext: ctx,
});
} catch (e) {
console.error(e);
}
}
main();
+30
View File
@@ -0,0 +1,30 @@
import {
MilvusVectorStore,
serviceContextFromDefaults,
VectorStoreIndex,
} from "llamaindex";
const collectionName = "movie_reviews";
async function main() {
try {
const milvus = new MilvusVectorStore({ collection: collectionName });
const ctx = serviceContextFromDefaults();
const index = await VectorStoreIndex.fromVectorStore(milvus, ctx);
const retriever = await index.asRetriever({ similarityTopK: 20 });
const queryEngine = await index.asQueryEngine({ retriever });
const results = await queryEngine.query({
query: "What is the best reviewed movie?",
});
console.log(results.response);
} catch (e) {
console.error(e);
}
}
main();
+5 -2
View File
@@ -1,16 +1,19 @@
{
"name": "examples",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"dependencies": {
"@aws-crypto/sha256-js": "^5.2.0",
"@datastax/astra-db-ts": "^0.1.4",
"@notionhq/client": "^2.2.14",
"@pinecone-database/pinecone": "^1.1.3",
"@zilliz/milvus2-sdk-node": "^2.3.5",
"chromadb": "^1.8.1",
"commander": "^11.1.0",
"dotenv": "^16.4.1",
"llamaindex": "latest",
"mongodb": "^6.2.0"
"mongodb": "^6.2.0",
"pathe": "^1.1.2"
},
"devDependencies": {
"@types/node": "^18.19.10",
+11
View File
@@ -0,0 +1,11 @@
# Qdrant Vector Store Example
How to run `examples/qdrantdb/preFilters.ts`:
Add your OpenAI API Key into a file called `.env` in the parent folder of this directory. It should look like this:
```
OPEN_API_KEY=sk-you-key
```
Now, open a new terminal window and inside `examples`, run `npx ts-node qdrantdb/preFilters.ts`.
+82
View File
@@ -0,0 +1,82 @@
import * as dotenv from "dotenv";
import {
CallbackManager,
Document,
MetadataMode,
QdrantVectorStore,
VectorStoreIndex,
serviceContextFromDefaults,
storageContextFromDefaults,
} from "llamaindex";
// Load environment variables from local .env file
dotenv.config();
const collectionName = "dog_colors";
const qdrantUrl = "http://127.0.0.1:6333";
async function main() {
try {
const docs = [
new Document({
text: "The dog is brown",
metadata: {
dogId: "1",
},
}),
new Document({
text: "The dog is red",
metadata: {
dogId: "2",
},
}),
];
console.log("Creating QdrantDB vector store");
const qdrantVs = new QdrantVectorStore({ url: qdrantUrl, collectionName });
const ctx = await storageContextFromDefaults({ vectorStore: qdrantVs });
console.log("Embedding documents and adding to index");
const index = await VectorStoreIndex.fromDocuments(docs, {
storageContext: ctx,
serviceContext: serviceContextFromDefaults({
callbackManager: new CallbackManager({
onRetrieve: (data) => {
console.log(
"The retrieved nodes are:",
data.nodes.map((node) => node.node.getContent(MetadataMode.NONE)),
);
},
}),
}),
});
console.log(
"Querying index with no filters: Expected output: Brown probably",
);
const queryEngineNoFilters = index.asQueryEngine();
const noFilterResponse = await queryEngineNoFilters.query({
query: "What is the color of the dog?",
});
console.log("No filter response:", noFilterResponse.toString());
console.log("Querying index with dogId 2: Expected output: Red");
const queryEngineDogId2 = index.asQueryEngine({
preFilters: {
filters: [
{
key: "dogId",
value: "2",
filterType: "ExactMatch",
},
],
},
});
const response = await queryEngineDogId2.query({
query: "What is the color of the dog?",
});
console.log("Filter with dogId 2 response:", response.toString());
} catch (e) {
console.error(e);
}
}
main();
+11
View File
@@ -0,0 +1,11 @@
# llamaindex-loader-example
### Patch Changes
- Updated dependencies [d2e8d0c]
- Updated dependencies [aefc326]
- Updated dependencies [484a710]
- Updated dependencies [d766bd0]
- Updated dependencies [dd95927]
- Updated dependencies [bf583a7]
- llamaindex@0.2.0
+2 -2
View File
@@ -1,6 +1,6 @@
import { program } from "commander";
import { TranscribeParams, VectorStoreIndex } from "llamaindex";
import { AudioTranscriptReader } from "llamaindex/readers/AssemblyAIReader";
import { VectorStoreIndex, type TranscribeParams } from "llamaindex";
import { AudioTranscriptReader } from "llamaindex/readers";
import { stdin as input, stdout as output } from "node:process";
import { createInterface } from "node:readline/promises";
+1 -1
View File
@@ -5,7 +5,7 @@ import {
serviceContextFromDefaults,
VectorStoreIndex,
} from "llamaindex";
import { PapaCSVReader } from "llamaindex/readers/CSVReader";
import { PapaCSVReader } from "llamaindex/readers";
async function main() {
// Load CSV
@@ -3,7 +3,7 @@ import {
FILE_EXT_TO_READER,
SimpleDirectoryReader,
TextFileReader,
} from "llamaindex/readers/SimpleDirectoryReader";
} from "llamaindex/readers";
class ZipReader implements BaseReader {
loadData(...args: any[]): Promise<Document<Metadata>[]> {
+2 -2
View File
@@ -1,5 +1,5 @@
import { VectorStoreIndex } from "llamaindex";
import { DocxReader } from "llamaindex/readers/DocxReader";
import { VectorStoreIndex } from "llamaindex/indices";
import { DocxReader } from "llamaindex/readers";
const FILE_PATH = "../data/stars.docx";
const SAMPLE_QUERY = "Information about Zodiac";
+2 -2
View File
@@ -1,5 +1,5 @@
import { VectorStoreIndex } from "llamaindex";
import { HTMLReader } from "llamaindex/readers/HTMLReader";
import { VectorStoreIndex } from "llamaindex/indices";
import { HTMLReader } from "llamaindex/readers";
async function main() {
// Load page
+2 -1
View File
@@ -1,4 +1,5 @@
import { LlamaParseReader, VectorStoreIndex } from "llamaindex";
import { VectorStoreIndex } from "llamaindex/indices";
import { LlamaParseReader } from "llamaindex/readers";
async function main() {
// Load PDF using LlamaParse
+2 -2
View File
@@ -1,5 +1,5 @@
import { VectorStoreIndex } from "llamaindex";
import { MarkdownReader } from "llamaindex/readers/MarkdownReader";
import { VectorStoreIndex } from "llamaindex/indices";
import { MarkdownReader } from "llamaindex/readers";
const FILE_PATH = "../data/planets.md";
const SAMPLE_QUERY = "List all planets";
+2 -2
View File
@@ -1,7 +1,7 @@
import { Client } from "@notionhq/client";
import { program } from "commander";
import { VectorStoreIndex } from "llamaindex";
import { NotionReader } from "llamaindex/readers/NotionReader";
import { VectorStoreIndex } from "llamaindex/indices";
import { NotionReader } from "llamaindex/readers";
import { stdin as input, stdout as output } from "node:process";
import { createInterface } from "node:readline/promises";
+2 -2
View File
@@ -1,5 +1,5 @@
import { VectorStoreIndex } from "llamaindex";
import { PDFReader } from "llamaindex/readers/PDFReader";
import { VectorStoreIndex } from "llamaindex/indices";
import { PDFReader } from "llamaindex/readers";
async function main() {
// Load PDF
+4 -2
View File
@@ -1,5 +1,7 @@
import { FireworksEmbedding, FireworksLLM, VectorStoreIndex } from "llamaindex";
import { PDFReader } from "llamaindex/readers/PDFReader";
import { FireworksEmbedding } from "llamaindex/embeddings";
import { VectorStoreIndex } from "llamaindex/indices";
import { FireworksLLM } from "llamaindex/llm";
import { PDFReader } from "llamaindex/readers";
import { serviceContextFromDefaults } from "llamaindex";
+4 -2
View File
@@ -1,5 +1,7 @@
import { OpenAI, OpenAIEmbedding, VectorStoreIndex } from "llamaindex";
import { PDFReader } from "llamaindex/readers/PDFReader";
import { OpenAIEmbedding } from "llamaindex/embeddings";
import { VectorStoreIndex } from "llamaindex/indices";
import { OpenAI } from "llamaindex/llm";
import { PDFReader } from "llamaindex/readers";
import { serviceContextFromDefaults } from "llamaindex";
@@ -1,4 +1,4 @@
import { SimpleDirectoryReader } from "llamaindex/readers/SimpleDirectoryReader";
import { SimpleDirectoryReader } from "llamaindex/readers";
// or
// import { SimpleDirectoryReader } from 'llamaindex'
+6 -4
View File
@@ -11,10 +11,12 @@
"prepare": "husky",
"test": "turbo run test",
"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 --ignore @llamaindex/core-test",
"new-experimental": "pnpm run build:release && changeset version --ignore create-llama"
"release": "pnpm run check-minor-version && pnpm run build:release && changeset publish",
"release-snapshot": "pnpm run check-minor-version && pnpm run build:release && changeset publish --tag snapshot",
"check-minor-version": "node ./scripts/check-minor-version",
"update-version": "node ./scripts/update-version",
"new-version": "pnpm run build:release && changeset version && pnpm run check-minor-version && pnpm run update-version",
"new-snapshot": "pnpm run build:release && changeset version --snapshot && pnpm run update-version"
},
"devDependencies": {
"@changesets/cli": "^2.27.1",
+28
View File
@@ -1,5 +1,33 @@
# llamaindex
## 0.2.1
### Patch Changes
- 41210df: Add auto create milvus collection and add milvus node metadata
- 137cf67: Use Pinecone namespaces for all operations
- 259c842: Add support for edge runtime by using @llamaindex/edge
## 0.2.0
### Minor Changes
- bf583a7: Use parameter object for retrieve function of Retriever (to align usage with query function of QueryEngine)
### Patch Changes
- d2e8d0c: add support for Milvus vector store
- aefc326: feat: experimental package + json query engine
- 484a710: - Add missing exports:
- `IndexStructType`,
- `IndexDict`,
- `jsonToIndexStruct`,
- `IndexList`,
- `IndexStruct`
- Fix `IndexDict.toJson()` method
- d766bd0: Add streaming to agents
- dd95927: add Claude Haiku support and update anthropic SDK
## 0.1.21
### Patch Changes
+188 -8
View File
@@ -1,12 +1,14 @@
{
"name": "llamaindex",
"version": "0.1.21",
"version": "0.2.1",
"expectedMinorVersion": "2",
"license": "MIT",
"type": "module",
"dependencies": {
"@anthropic-ai/sdk": "^0.15.0",
"@anthropic-ai/sdk": "^0.18.0",
"@aws-crypto/sha256-js": "^5.2.0",
"@datastax/astra-db-ts": "^0.1.4",
"@grpc/grpc-js": "^1.10.2",
"@llamaindex/cloud": "0.0.4",
"@llamaindex/env": "workspace:*",
"@mistralai/mistralai": "^0.0.10",
@@ -18,12 +20,13 @@
"@types/papaparse": "^5.3.14",
"@types/pg": "^8.11.0",
"@xenova/transformers": "^2.15.0",
"@zilliz/milvus2-sdk-node": "^2.3.5",
"assemblyai": "^4.2.2",
"chromadb": "~1.7.3",
"cohere-ai": "^7.7.5",
"file-type": "^18.7.0",
"js-tiktoken": "^1.0.10",
"lodash": "^4.17.21",
"magic-bytes.js": "^1.10.0",
"mammoth": "^1.6.0",
"md-utils-ts": "^2.0.0",
"mongodb": "^6.3.0",
@@ -38,7 +41,8 @@
"rake-modified": "^1.0.8",
"replicate": "^0.25.2",
"string-strip-html": "^13.4.6",
"wink-nlp": "^1.14.3"
"wink-nlp": "^1.14.3",
"wikipedia": "^2.1.2"
},
"devDependencies": {
"@swc/cli": "^0.3.9",
@@ -59,15 +63,191 @@
"types": "./dist/type/index.d.ts",
"default": "./dist/index.js"
},
"edge-light": {
"types": "./dist/type/index.d.ts",
"default": "./dist/index.edge-light.js"
},
"require": {
"types": "./dist/type/index.d.ts",
"default": "./dist/cjs/index.js"
}
},
"./agent": {
"import": {
"types": "./dist/type/agent/index.d.ts",
"default": "./dist/agent/index.js"
},
"require": {
"types": "./dist/type/agent/index.d.ts",
"default": "./dist/cjs/agent/index.js"
}
},
"./cloud": {
"import": {
"types": "./dist/type/cloud/index.d.ts",
"default": "./dist/cloud/index.js"
},
"require": {
"types": "./dist/type/cloud/index.d.ts",
"default": "./dist/cjs/cloud/index.js"
}
},
"./embeddings": {
"import": {
"types": "./dist/type/embeddings/index.d.ts",
"default": "./dist/embeddings/index.js"
},
"require": {
"types": "./dist/type/embeddings/index.d.ts",
"default": "./dist/cjs/embeddings/index.js"
}
},
"./engines": {
"import": {
"types": "./dist/type/engines/index.d.ts",
"default": "./dist/engines/index.js"
},
"require": {
"types": "./dist/type/engines/index.d.ts",
"default": "./dist/cjs/engines/index.js"
}
},
"./evaluation": {
"import": {
"types": "./dist/type/evaluation/index.d.ts",
"default": "./dist/evaluation/index.js"
},
"require": {
"types": "./dist/type/evaluation/index.d.ts",
"default": "./dist/cjs/evaluation/index.js"
}
},
"./extractors": {
"import": {
"types": "./dist/type/extractors/index.d.ts",
"default": "./dist/extractors/index.js"
},
"require": {
"types": "./dist/type/extractors/index.d.ts",
"default": "./dist/cjs/extractors/index.js"
}
},
"./indices": {
"import": {
"types": "./dist/type/indices/index.d.ts",
"default": "./dist/indices/index.js"
},
"require": {
"types": "./dist/type/indices/index.d.ts",
"default": "./dist/cjs/indices/index.js"
}
},
"./ingestion": {
"import": {
"types": "./dist/type/ingestion/index.d.ts",
"default": "./dist/ingestion/index.js"
},
"require": {
"types": "./dist/type/ingestion/index.d.ts",
"default": "./dist/cjs/ingestion/index.js"
}
},
"./llm": {
"import": {
"types": "./dist/type/llm/index.d.ts",
"default": "./dist/llm/index.js"
},
"require": {
"types": "./dist/type/llm/index.d.ts",
"default": "./dist/cjs/llm/index.js"
}
},
"./nodeParsers": {
"import": {
"types": "./dist/type/nodeParsers/index.d.ts",
"default": "./dist/nodeParsers/index.js"
},
"require": {
"types": "./dist/type/nodeParsers/index.d.ts",
"default": "./dist/cjs/nodeParsers/index.js"
}
},
"./objects": {
"import": {
"types": "./dist/type/objects/index.d.ts",
"default": "./dist/objects/index.js"
},
"require": {
"types": "./dist/type/objects/index.d.ts",
"default": "./dist/cjs/objects/index.js"
}
},
"./postprocessors": {
"import": {
"types": "./dist/type/postprocessors/index.d.ts",
"default": "./dist/postprocessors/index.js"
},
"require": {
"types": "./dist/type/postprocessors/index.d.ts",
"default": "./dist/cjs/postprocessors/index.js"
}
},
"./prompts": {
"import": {
"types": "./dist/type/prompts/index.d.ts",
"default": "./dist/prompts/index.js"
},
"require": {
"types": "./dist/type/prompts/index.d.ts",
"default": "./dist/cjs/prompts/index.js"
}
},
"./readers": {
"import": {
"types": "./dist/type/readers/index.d.ts",
"default": "./dist/readers/index.js"
},
"require": {
"types": "./dist/type/readers/index.d.ts",
"default": "./dist/cjs/readers/index.js"
}
},
"./selectors": {
"import": {
"types": "./dist/type/selectors/index.d.ts",
"default": "./dist/selectors/index.js"
},
"require": {
"types": "./dist/type/selectors/index.d.ts",
"default": "./dist/cjs/selectors/index.js"
}
},
"./storage": {
"import": {
"types": "./dist/type/storage/index.d.ts",
"default": "./dist/storage/index.js"
},
"require": {
"types": "./dist/type/storage/index.d.ts",
"default": "./dist/cjs/storage/index.js"
}
},
"./synthesizers": {
"import": {
"types": "./dist/type/synthesizers/index.d.ts",
"default": "./dist/synthesizers/index.js"
},
"require": {
"types": "./dist/type/synthesizers/index.d.ts",
"default": "./dist/cjs/synthesizers/index.js"
}
},
"./tools": {
"import": {
"types": "./dist/type/tools/index.d.ts",
"default": "./dist/tools/index.js"
},
"require": {
"types": "./dist/type/tools/index.d.ts",
"default": "./dist/cjs/tools/index.js"
}
},
"./*": {
"import": {
"types": "./dist/type/*.d.ts",
+1 -1
View File
@@ -60,7 +60,7 @@ export class ReActChatFormatter implements BaseAgentChatFormatter {
} else {
message = {
content: reasoningStep.getContent(),
role: "system",
role: "assistant",
};
}
+1 -2
View File
@@ -1,4 +1,4 @@
import { randomUUID } from "crypto";
import { randomUUID } from "@llamaindex/env";
import { CallbackManager } from "../../callbacks/CallbackManager.js";
import { AgentChatResponse } from "../../engines/chat/index.js";
import type { ChatResponse, LLM } from "../../llm/index.js";
@@ -17,7 +17,6 @@ import {
ObservationReasoningStep,
ResponseReasoningStep,
} from "./types.js";
type ReActAgentWorkerParams = {
tools: BaseTool[];
llm?: LLM;
+1 -2
View File
@@ -1,4 +1,4 @@
import { randomUUID } from "crypto";
import { randomUUID } from "@llamaindex/env";
import { CallbackManager } from "../../callbacks/CallbackManager.js";
import type { ChatEngineAgentParams } from "../../engines/chat/index.js";
import {
@@ -12,7 +12,6 @@ import type { BaseMemory } from "../../memory/types.js";
import type { AgentWorker, TaskStepOutput } from "../types.js";
import { Task, TaskStep } from "../types.js";
import { AgentState, BaseAgentRunner, TaskState } from "./types.js";
const validateStepFromArgs = (
taskId: string,
input?: string | null,
@@ -65,7 +65,7 @@ export class LlamaCloudRetriever implements BaseRetriever {
projectName: this.projectName,
pipelineName: this.pipelineName,
});
if (pipelines.length !== 1 && !pipelines[0].id) {
if (pipelines.length !== 1 && !pipelines[0]?.id) {
throw new Error(
`No pipeline found with name ${this.pipelineName} in project ${this.projectName}`,
);
+81
View File
@@ -0,0 +1,81 @@
import type { PlatformApi } from "@llamaindex/cloud";
import { BaseNode, Document } from "../Node.js";
import { OpenAIEmbedding } from "../embeddings/OpenAIEmbedding.js";
import type { TransformComponent } from "../ingestion/types.js";
import { SimpleNodeParser } from "../nodeParsers/SimpleNodeParser.js";
export type GetPipelineCreateParams = {
pipelineName: string;
pipelineType: any; // TODO: PlatformApi.PipelineType is not exported
transformations?: TransformComponent[];
inputNodes?: BaseNode[];
};
function getTransformationConfig(
transformation: TransformComponent,
): PlatformApi.ConfiguredTransformationItem {
if (transformation instanceof SimpleNodeParser) {
return {
configurableTransformationType: "SENTENCE_AWARE_NODE_PARSER",
component: {
// TODO: API returns 422 if these parameters are included
// chunkSize: transformation.textSplitter.chunkSize, // TODO: set to public in SentenceSplitter
// chunkOverlap: transformation.textSplitter.chunkOverlap, // TODO: set to public in SentenceSplitter
// includeMetadata: transformation.includeMetadata,
// includePrevNextRel: transformation.includePrevNextRel,
},
};
}
if (transformation instanceof OpenAIEmbedding) {
return {
configurableTransformationType: "OPENAI_EMBEDDING",
component: {
modelName: transformation.model,
apiKey: transformation.apiKey,
embedBatchSize: transformation.embedBatchSize,
dimensions: transformation.dimensions,
},
};
}
throw new Error(`Unsupported transformation: ${typeof transformation}`);
}
function getDataSourceConfig(node: BaseNode): PlatformApi.DataSourceCreate {
if (node instanceof Document) {
return {
name: node.id_,
sourceType: "DOCUMENT",
component: {
id: node.id_,
text: node.text,
textTemplate: node.textTemplate,
startCharIdx: node.startCharIdx,
endCharIdx: node.endCharIdx,
metadataSeparator: node.metadataSeparator,
excludedEmbedMetadataKeys: node.excludedEmbedMetadataKeys,
excludedLlmMetadataKeys: node.excludedLlmMetadataKeys,
extraInfo: node.metadata,
},
};
}
throw new Error(`Unsupported node: ${typeof node}`);
}
export async function getPipelineCreate(
params: GetPipelineCreateParams,
): Promise<PlatformApi.PipelineCreate> {
const {
pipelineName,
pipelineType,
transformations = [],
inputNodes = [],
} = params;
return {
name: pipelineName,
configuredTransformations: transformations.map(getTransformationConfig),
dataSources: inputNodes.map(getDataSourceConfig),
dataSinks: [],
pipelineType,
};
}
+1
View File
@@ -1,5 +1,6 @@
import type { ServiceContext } from "../ServiceContext.js";
export const DEFAULT_PIPELINE_NAME = "default";
export const DEFAULT_PROJECT_NAME = "default";
export const DEFAULT_BASE_URL = "https://api.cloud.llamaindex.ai";
+9 -1
View File
@@ -3,12 +3,20 @@ import { getEnv } from "@llamaindex/env";
import type { ClientParams } from "./types.js";
import { DEFAULT_BASE_URL } from "./types.js";
function getBaseUrl(baseUrl?: string): string {
return baseUrl ?? getEnv("LLAMA_CLOUD_BASE_URL") ?? DEFAULT_BASE_URL;
}
export function getAppBaseUrl(baseUrl?: string): string {
return getBaseUrl(baseUrl).replace(/api\./, "");
}
export async function getClient({
apiKey,
baseUrl,
}: ClientParams = {}): Promise<PlatformApiClient> {
// Get the environment variables or use defaults
baseUrl = baseUrl ?? getEnv("LLAMA_CLOUD_BASE_URL") ?? DEFAULT_BASE_URL;
baseUrl = getBaseUrl(baseUrl);
apiKey = apiKey ?? getEnv("LLAMA_CLOUD_API_KEY");
const { PlatformApiClient } = await import("@llamaindex/cloud");
+4 -4
View File
@@ -1,5 +1,6 @@
import { defaultFS } from "@llamaindex/env";
import _ from "lodash";
import { filetypemime } from "magic-bytes.js";
import type { ImageType } from "../Node.js";
import { DEFAULT_SIMILARITY_TOP_K } from "../constants.js";
import { VectorStoreQueryMode } from "../storage/vectorStore/types.js";
@@ -199,13 +200,12 @@ export function getTopKMMREmbeddings(
}
async function blobToDataUrl(input: Blob) {
const { fileTypeFromBuffer } = await import("file-type");
const buffer = Buffer.from(await input.arrayBuffer());
const type = await fileTypeFromBuffer(buffer);
if (!type) {
const mimes = filetypemime(buffer);
if (mimes.length < 1) {
throw new Error("Unsupported image type");
}
return "data:" + type.mime + ";base64," + buffer.toString("base64");
return "data:" + mimes[0] + ";base64," + buffer.toString("base64");
}
export async function readImage(input: ImageType) {
+31
View File
@@ -0,0 +1,31 @@
export * from "./ChatHistory.js";
export * from "./GlobalsHelper.js";
export * from "./Node.js";
export * from "./OutputParser.js";
export * from "./Prompt.js";
export * from "./PromptHelper.js";
export * from "./QuestionGenerator.js";
export * from "./Response.js";
export * from "./Retriever.js";
export * from "./ServiceContext.js";
export * from "./TextSplitter.js";
export * from "./agent/index.js";
export * from "./callbacks/CallbackManager.js";
export * from "./cloud/index.js";
export * from "./constants.js";
export * from "./embeddings/index.js";
export * from "./engines/chat/index.js";
export * from "./engines/query/index.js";
export * from "./evaluation/index.js";
export * from "./extractors/index.js";
export * from "./indices/index.js";
export * from "./ingestion/index.js";
export * from "./llm/index.js";
export * from "./nodeParsers/index.js";
export * from "./objects/index.js";
export * from "./postprocessors/index.js";
export * from "./prompts/index.js";
export * from "./selectors/index.js";
export * from "./synthesizers/index.js";
export * from "./tools/index.js";
export * from "./types.js";
+1 -31
View File
@@ -1,33 +1,3 @@
export * from "./ChatHistory.js";
export * from "./GlobalsHelper.js";
export * from "./Node.js";
export * from "./OutputParser.js";
export * from "./Prompt.js";
export * from "./PromptHelper.js";
export * from "./QuestionGenerator.js";
export * from "./Response.js";
export * from "./Retriever.js";
export * from "./ServiceContext.js";
export * from "./TextSplitter.js";
export * from "./agent/index.js";
export * from "./callbacks/CallbackManager.js";
export * from "./cloud/index.js";
export * from "./constants.js";
export * from "./embeddings/index.js";
export * from "./engines/chat/index.js";
export * from "./engines/query/index.js";
export * from "./evaluation/index.js";
export * from "./extractors/index.js";
export * from "./indices/index.js";
export * from "./ingestion/index.js";
export * from "./llm/index.js";
export * from "./nodeParsers/index.js";
export * from "./objects/index.js";
export * from "./postprocessors/index.js";
export * from "./prompts/index.js";
export * from "./index.edge.js";
export * from "./readers/index.js";
export * from "./selectors/index.js";
export * from "./storage/index.js";
export * from "./synthesizers/index.js";
export * from "./tools/index.js";
export * from "./types.js";
+3 -2
View File
@@ -13,8 +13,9 @@ import type { ServiceContext } from "../../ServiceContext.js";
import { serviceContextFromDefaults } from "../../ServiceContext.js";
import { RetrieverQueryEngine } from "../../engines/query/index.js";
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
import type { BaseDocumentStore, StorageContext } from "../../storage/index.js";
import { storageContextFromDefaults } from "../../storage/index.js";
import type { StorageContext } from "../../storage/StorageContext.js";
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
import type { BaseDocumentStore } from "../../storage/docStore/types.js";
import type { BaseSynthesizer } from "../../synthesizers/index.js";
import type { BaseQueryEngine } from "../../types.js";
import type { BaseIndexInit } from "../BaseIndex.js";
+3 -3
View File
@@ -8,12 +8,12 @@ import type { ServiceContext } from "../../ServiceContext.js";
import { serviceContextFromDefaults } from "../../ServiceContext.js";
import { RetrieverQueryEngine } from "../../engines/query/index.js";
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
import type { StorageContext } from "../../storage/StorageContext.js";
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
import type {
BaseDocumentStore,
RefDocInfo,
StorageContext,
} from "../../storage/index.js";
import { storageContextFromDefaults } from "../../storage/index.js";
} from "../../storage/docStore/types.js";
import type { BaseSynthesizer } from "../../synthesizers/index.js";
import {
CompactAndRefine,
@@ -24,15 +24,15 @@ 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 type { StorageContext } from "../../storage/StorageContext.js";
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
import type { BaseIndexStore } from "../../storage/indexStore/types.js";
import type {
BaseIndexStore,
MetadataFilters,
StorageContext,
VectorStore,
VectorStoreQuery,
VectorStoreQueryResult,
} from "../../storage/index.js";
} from "../../storage/vectorStore/types.js";
import { VectorStoreQueryMode } from "../../storage/vectorStore/types.js";
import type { BaseSynthesizer } from "../../synthesizers/types.js";
import type { BaseQueryEngine } from "../../types.js";
@@ -1,4 +1,12 @@
import type { PlatformApiClient } from "@llamaindex/cloud";
import type { BaseNode, Document } from "../Node.js";
import { getPipelineCreate } from "../cloud/config.js";
import {
DEFAULT_PIPELINE_NAME,
DEFAULT_PROJECT_NAME,
type ClientParams,
} from "../cloud/types.js";
import { getAppBaseUrl, getClient } from "../cloud/utils.js";
import type { BaseReader } from "../readers/type.js";
import type { BaseDocumentStore } from "../storage/docStore/types.js";
import type { VectorStore } from "../storage/vectorStore/types.js";
@@ -55,11 +63,16 @@ export class IngestionPipeline {
docStoreStrategy: DocStoreStrategy = DocStoreStrategy.UPSERTS;
cache?: IngestionCache;
disableCache: boolean = false;
client?: PlatformApiClient;
clientParams?: ClientParams;
projectName: string = DEFAULT_PROJECT_NAME;
name: string = DEFAULT_PIPELINE_NAME;
private _docStoreStrategy?: TransformComponent;
constructor(init?: Partial<IngestionPipeline>) {
constructor(init?: Partial<IngestionPipeline> & ClientParams) {
Object.assign(this, init);
this.clientParams = { apiKey: init?.apiKey, baseUrl: init?.baseUrl };
this._docStoreStrategy = createDocStoreStrategy(
this.docStoreStrategy,
this.docStore,
@@ -115,4 +128,50 @@ export class IngestionPipeline {
}
return nodes;
}
private async getClient(): Promise<PlatformApiClient> {
if (!this.client) {
this.client = await getClient(this.clientParams);
}
return this.client;
}
async register(params: {
documents?: Document[];
nodes?: BaseNode[];
verbose?: boolean;
}): Promise<string> {
const client = await this.getClient();
const inputNodes = await this.prepareInput(params.documents, params.nodes);
const project = await client.project.upsertProject({
name: this.projectName,
});
if (!project.id) {
throw new Error("Project ID should be defined");
}
// upload
const pipeline = await client.project.upsertPipelineForProject(
project.id,
await getPipelineCreate({
pipelineName: this.name,
pipelineType: "PLAYGROUND",
transformations: this.transformations,
inputNodes,
}),
);
if (!pipeline.id) {
throw new Error("Pipeline ID must be defined");
}
// Print playground URL if not running remote
if (params.verbose) {
console.log(
`Pipeline available at: ${getAppBaseUrl(this.clientParams?.baseUrl)}/project/${project.id}/playground/${pipeline.id}`,
);
}
return pipeline.id;
}
}
@@ -1,5 +1,6 @@
import type { BaseNode } from "../../Node.js";
import type { BaseDocumentStore, VectorStore } from "../../storage/index.js";
import type { BaseDocumentStore } from "../../storage/docStore/types.js";
import type { VectorStore } from "../../storage/vectorStore/types.js";
import { classify } from "./classify.js";
/**
+2
View File
@@ -620,6 +620,7 @@ export const ALL_AVAILABLE_ANTHROPIC_LEGACY_MODELS = {
export const ALL_AVAILABLE_V3_MODELS = {
"claude-3-opus": { contextWindow: 200000 },
"claude-3-sonnet": { contextWindow: 200000 },
"claude-3-haiku": { contextWindow: 200000 },
};
export const ALL_AVAILABLE_ANTHROPIC_MODELS = {
@@ -630,6 +631,7 @@ export const ALL_AVAILABLE_ANTHROPIC_MODELS = {
const AVAILABLE_ANTHROPIC_MODELS_WITHOUT_DATE: { [key: string]: string } = {
"claude-3-opus": "claude-3-opus-20240229",
"claude-3-sonnet": "claude-3-sonnet-20240229",
"claude-3-haiku": "claude-3-haiku-20240307",
} as { [key in keyof typeof ALL_AVAILABLE_ANTHROPIC_MODELS]: string };
/**
+6 -6
View File
@@ -49,15 +49,15 @@ const ALL_AZURE_OPENAI_EMBEDDING_MODELS = {
},
};
// Current version list found here - https://learn.microsoft.com/en-us/azure/ai-services/openai/reference
const ALL_AZURE_API_VERSIONS = [
"2022-12-01",
"2023-05-15",
"2023-03-15-preview", // retiring 2024-04-02
"2023-06-01-preview", // retiring 2024-04-02
"2023-07-01-preview", // retiring 2024-04-02
"2023-08-01-preview", // retiring 2024-04-02
"2023-09-01-preview",
"2023-12-01-preview",
"2023-06-01-preview", // Maintained for DALL-E 2
"2023-10-01-preview",
"2024-02-01",
"2024-02-15-preview",
"2024-03-01-preview",
];
const DEFAULT_API_VERSION = "2023-05-15";
@@ -1,8 +1,9 @@
import { defaultFS, getEnv, type GenericFileSystem } from "@llamaindex/env";
import { filetypemime } from "magic-bytes.js";
import { Document } from "../Node.js";
import type { FileReader } from "./type.js";
type ResultType = "text" | "markdown";
type ResultType = "text" | "markdown" | "json";
/**
* Represents a reader for parsing files using the LlamaParse API.
@@ -109,11 +110,10 @@ export class LlamaParseReader implements FileReader {
}
private async getMimeType(data: Buffer): Promise<string> {
const { fileTypeFromBuffer } = await import("file-type");
const type = await fileTypeFromBuffer(data);
if (type?.mime !== "application/pdf") {
const mimes = filetypemime(data);
if (!mimes.includes("application/pdf")) {
throw new Error("Currently, only PDF files are supported.");
}
return type.mime;
return "application/pdf";
}
}
@@ -0,0 +1,126 @@
import type { CompleteFileSystem } from "@llamaindex/env";
import { defaultFS, path } from "@llamaindex/env";
import { Document } from "../Node.js";
import { walk } from "../storage/FileSystem.js";
import { TextFileReader } from "./TextFileReader.js";
import type { BaseReader } from "./type.js";
type ReaderCallback = (
category: "file" | "directory",
name: string,
status: ReaderStatus,
message?: string,
) => boolean;
enum ReaderStatus {
STARTED = 0,
COMPLETE,
ERROR,
}
export type SimpleDirectoryReaderLoadDataParams = {
directoryPath: string;
fs?: CompleteFileSystem;
defaultReader?: BaseReader | null;
fileExtToReader?: Record<string, BaseReader>;
};
/**
* Read all the documents in a directory.
* By default, supports the list of file types
* in the FILE_EXT_TO_READER map.
*/
export class SimpleDirectoryReader implements BaseReader {
constructor(private observer?: ReaderCallback) {}
async loadData(
params: SimpleDirectoryReaderLoadDataParams,
): Promise<Document[]>;
async loadData(directoryPath: string): Promise<Document[]>;
async loadData(
params: SimpleDirectoryReaderLoadDataParams | string,
): Promise<Document[]> {
if (typeof params === "string") {
params = { directoryPath: params };
}
const {
directoryPath,
fs = defaultFS,
defaultReader = new TextFileReader(),
fileExtToReader,
} = params;
// Observer can decide to skip the directory
if (
!this.doObserverCheck("directory", directoryPath, ReaderStatus.STARTED)
) {
return [];
}
const docs: Document[] = [];
for await (const filePath of walk(fs, directoryPath)) {
try {
const fileExt = path.extname(filePath).slice(1).toLowerCase();
// Observer can decide to skip each file
if (!this.doObserverCheck("file", filePath, ReaderStatus.STARTED)) {
// Skip this file
continue;
}
let reader: BaseReader;
if (fileExtToReader && fileExt in fileExtToReader) {
reader = fileExtToReader[fileExt];
} else if (defaultReader != null) {
reader = defaultReader;
} else {
const msg = `No reader for file extension of ${filePath}`;
console.warn(msg);
// In an error condition, observer's false cancels the whole process.
if (
!this.doObserverCheck("file", filePath, ReaderStatus.ERROR, msg)
) {
return [];
}
continue;
}
const fileDocs = await reader.loadData(filePath, fs);
// Observer can still cancel addition of the resulting docs from this file
if (this.doObserverCheck("file", filePath, ReaderStatus.COMPLETE)) {
docs.push(...fileDocs);
}
} catch (e) {
const msg = `Error reading file ${filePath}: ${e}`;
console.error(msg);
// In an error condition, observer's false cancels the whole process.
if (!this.doObserverCheck("file", filePath, ReaderStatus.ERROR, msg)) {
return [];
}
}
}
// After successful import of all files, directory completion
// is only a notification for observer, cannot be cancelled.
this.doObserverCheck("directory", directoryPath, ReaderStatus.COMPLETE);
return docs;
}
private doObserverCheck(
category: "file" | "directory",
name: string,
status: ReaderStatus,
message?: string,
): boolean {
if (this.observer) {
return this.observer(category, name, status, message);
}
return true;
}
}
@@ -1,40 +1,17 @@
import type { CompleteFileSystem } from "@llamaindex/env";
import { defaultFS, path } from "@llamaindex/env";
import { Document } from "../Node.js";
import { walk } from "../storage/FileSystem.js";
import { PapaCSVReader } from "./CSVReader.js";
import { DocxReader } from "./DocxReader.js";
import { HTMLReader } from "./HTMLReader.js";
import { ImageReader } from "./ImageReader.js";
import { MarkdownReader } from "./MarkdownReader.js";
import { PDFReader } from "./PDFReader.js";
import {
SimpleDirectoryReader as EdgeSimpleDirectoryReader,
type SimpleDirectoryReaderLoadDataParams,
} from "./SimpleDirectoryReader.edge.js";
import { TextFileReader } from "./TextFileReader.js";
import type { BaseReader } from "./type.js";
type ReaderCallback = (
category: "file" | "directory",
name: string,
status: ReaderStatus,
message?: string,
) => boolean;
enum ReaderStatus {
STARTED = 0,
COMPLETE,
ERROR,
}
/**
* Read a .txt file
*/
export class TextFileReader implements BaseReader {
async loadData(
file: string,
fs: CompleteFileSystem = defaultFS,
): Promise<Document[]> {
const dataBuffer = await fs.readFile(file);
return [new Document({ text: dataBuffer, id_: file })];
}
}
export const FILE_EXT_TO_READER: Record<string, BaseReader> = {
txt: new TextFileReader(),
pdf: new PDFReader(),
@@ -49,21 +26,12 @@ export const FILE_EXT_TO_READER: Record<string, BaseReader> = {
gif: new ImageReader(),
};
export type SimpleDirectoryReaderLoadDataParams = {
directoryPath: string;
fs?: CompleteFileSystem;
defaultReader?: BaseReader | null;
fileExtToReader?: Record<string, BaseReader>;
};
/**
* Read all the documents in a directory.
* By default, supports the list of file types
* in the FILE_EXT_TO_READER map.
*/
export class SimpleDirectoryReader implements BaseReader {
constructor(private observer?: ReaderCallback) {}
export class SimpleDirectoryReader extends EdgeSimpleDirectoryReader {
async loadData(
params: SimpleDirectoryReaderLoadDataParams,
): Promise<Document[]>;
@@ -74,85 +42,7 @@ export class SimpleDirectoryReader implements BaseReader {
if (typeof params === "string") {
params = { directoryPath: params };
}
const {
directoryPath,
fs = defaultFS,
defaultReader = new TextFileReader(),
fileExtToReader = FILE_EXT_TO_READER,
} = params;
// Observer can decide to skip the directory
if (
!this.doObserverCheck("directory", directoryPath, ReaderStatus.STARTED)
) {
return [];
}
const docs: Document[] = [];
for await (const filePath of walk(fs, directoryPath)) {
try {
const fileExt = path.extname(filePath).slice(1).toLowerCase();
// Observer can decide to skip each file
if (!this.doObserverCheck("file", filePath, ReaderStatus.STARTED)) {
// Skip this file
continue;
}
let reader: BaseReader;
if (fileExt in fileExtToReader) {
reader = fileExtToReader[fileExt];
} else if (defaultReader != null) {
reader = defaultReader;
} else {
const msg = `No reader for file extension of ${filePath}`;
console.warn(msg);
// In an error condition, observer's false cancels the whole process.
if (
!this.doObserverCheck("file", filePath, ReaderStatus.ERROR, msg)
) {
return [];
}
continue;
}
const fileDocs = await reader.loadData(filePath, fs);
// Observer can still cancel addition of the resulting docs from this file
if (this.doObserverCheck("file", filePath, ReaderStatus.COMPLETE)) {
docs.push(...fileDocs);
}
} catch (e) {
const msg = `Error reading file ${filePath}: ${e}`;
console.error(msg);
// In an error condition, observer's false cancels the whole process.
if (!this.doObserverCheck("file", filePath, ReaderStatus.ERROR, msg)) {
return [];
}
}
}
// After successful import of all files, directory completion
// is only a notification for observer, cannot be cancelled.
this.doObserverCheck("directory", directoryPath, ReaderStatus.COMPLETE);
return docs;
}
private doObserverCheck(
category: "file" | "directory",
name: string,
status: ReaderStatus,
message?: string,
): boolean {
if (this.observer) {
return this.observer(category, name, status, message);
}
return true;
params.fileExtToReader = params.fileExtToReader ?? FILE_EXT_TO_READER;
return super.loadData(params);
}
}
@@ -0,0 +1,18 @@
import type { CompleteFileSystem } from "@llamaindex/env";
import { defaultFS } from "@llamaindex/env";
import { Document } from "../Node.js";
import type { BaseReader } from "./type.js";
/**
* Read a .txt file
*/
export class TextFileReader implements BaseReader {
async loadData(
file: string,
fs: CompleteFileSystem = defaultFS,
): Promise<Document[]> {
const dataBuffer = await fs.readFile(file);
return [new Document({ text: dataBuffer, id_: file })];
}
}
+1
View File
@@ -9,4 +9,5 @@ export * from "./NotionReader.js";
export * from "./PDFReader.js";
export * from "./SimpleDirectoryReader.js";
export * from "./SimpleMongoReader.js";
export * from "./TextFileReader.js";
export * from "./type.js";
+1
View File
@@ -11,6 +11,7 @@ export { SimpleKVStore } from "./kvStore/SimpleKVStore.js";
export * from "./kvStore/types.js";
export { AstraDBVectorStore } from "./vectorStore/AstraDBVectorStore.js";
export { ChromaVectorStore } from "./vectorStore/ChromaVectorStore.js";
export { MilvusVectorStore } from "./vectorStore/MilvusVectorStore.js";
export { MongoDBAtlasVectorSearch } from "./vectorStore/MongoDBAtlasVectorStore.js";
export { PGVectorStore } from "./vectorStore/PGVectorStore.js";
export { PineconeVectorStore } from "./vectorStore/PineconeVectorStore.js";
@@ -0,0 +1,214 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import type { ChannelOptions } from "@grpc/grpc-js";
import {
DataType,
MilvusClient,
type ClientConfig,
type DeleteReq,
type RowData,
} from "@zilliz/milvus2-sdk-node";
import { BaseNode, MetadataMode, type Metadata } from "../../Node.js";
import type {
VectorStore,
VectorStoreQuery,
VectorStoreQueryResult,
} from "./types.js";
import { metadataDictToNode, nodeToMetadata } from "./utils.js";
export class MilvusVectorStore implements VectorStore {
public storesText: boolean = true;
public isEmbeddingQuery?: boolean;
private flatMetadata: boolean = true;
private milvusClient: MilvusClient;
private collectionInitialized = false;
private collectionName: string;
private idKey: string;
private contentKey: string;
private metadataKey: string;
private embeddingKey: string;
constructor(
init?: Partial<{ milvusClient: MilvusClient }> & {
params?: {
configOrAddress: ClientConfig | string;
ssl?: boolean;
username?: string;
password?: string;
channelOptions?: ChannelOptions;
};
collection?: string;
idKey?: string;
contentKey?: string;
metadataKey?: string;
embeddingKey?: string;
},
) {
if (init?.milvusClient) {
this.milvusClient = init.milvusClient;
} else {
const configOrAddress =
init?.params?.configOrAddress ?? process.env.MILVUS_ADDRESS;
const ssl = init?.params?.ssl ?? process.env.MILVUS_SSL === "true";
const username = init?.params?.username ?? process.env.MILVUS_USERNAME;
const password = init?.params?.password ?? process.env.MILVUS_PASSWORD;
if (!configOrAddress) {
throw new Error("Must specify MILVUS_ADDRESS via env variable.");
}
this.milvusClient = new MilvusClient(
configOrAddress,
ssl,
username,
password,
init?.params?.channelOptions,
);
}
this.collectionName = init?.collection ?? "llamacollection";
this.idKey = init?.idKey ?? "id";
this.contentKey = init?.contentKey ?? "content";
this.metadataKey = init?.metadataKey ?? "metadata";
this.embeddingKey = init?.embeddingKey ?? "embedding";
}
public client(): MilvusClient {
return this.milvusClient;
}
private async createCollection() {
await this.milvusClient.createCollection({
collection_name: this.collectionName,
fields: [
{
name: this.idKey,
data_type: DataType.VarChar,
is_primary_key: true,
max_length: 200,
},
{
name: this.embeddingKey,
data_type: DataType.FloatVector,
dim: 1536,
},
{
name: this.contentKey,
data_type: DataType.VarChar,
max_length: 9000,
},
{
name: this.metadataKey,
data_type: DataType.JSON,
},
],
});
await this.milvusClient.createIndex({
collection_name: this.collectionName,
field_name: this.embeddingKey,
});
}
private async ensureCollection(): Promise<void> {
if (!this.collectionInitialized) {
await this.milvusClient.connectPromise;
// Check collection exists
const isCollectionExist = await this.milvusClient.hasCollection({
collection_name: this.collectionName,
});
if (!isCollectionExist.value) {
await this.createCollection();
}
await this.milvusClient.loadCollectionSync({
collection_name: this.collectionName,
});
this.collectionInitialized = true;
}
}
public async add(nodes: BaseNode<Metadata>[]): Promise<string[]> {
await this.ensureCollection();
const result = await this.milvusClient.insert({
collection_name: this.collectionName,
data: nodes.map((node) => {
const metadata = nodeToMetadata(
node,
true,
this.contentKey,
this.flatMetadata,
);
const entry: RowData = {
[this.idKey]: node.id_,
[this.embeddingKey]: node.getEmbedding(),
[this.contentKey]: node.getContent(MetadataMode.NONE),
[this.metadataKey]: metadata,
};
return entry;
}),
});
if (!result.IDs) {
return [];
}
if ("int_id" in result.IDs) {
return result.IDs.int_id.data.map((i) => String(i));
}
return result.IDs.str_id.data.map((s) => String(s));
}
public async delete(
refDocId: string,
deleteOptions?: Omit<DeleteReq, "ids">,
): Promise<void> {
this.ensureCollection();
await this.milvusClient.delete({
ids: [refDocId],
collection_name: this.collectionName,
...deleteOptions,
});
}
public async query(
query: VectorStoreQuery,
_options?: any,
): Promise<VectorStoreQueryResult> {
await this.ensureCollection();
const found = await this.milvusClient.search({
collection_name: this.collectionName,
limit: query.similarityTopK,
vector: query.queryEmbedding,
});
const nodes: BaseNode<Metadata>[] = [];
const similarities: number[] = [];
const ids: string[] = [];
found.results.forEach((result) => {
const node = metadataDictToNode(result.metadata);
node.setContent(result.content);
nodes.push(node);
similarities.push(result.score);
ids.push(String(result.id));
});
return {
nodes,
similarities,
ids,
};
}
public async persist() {
// no need to do anything
}
}
@@ -73,7 +73,7 @@ export class PineconeVectorStore implements VectorStore {
async index() {
const db: Pinecone = await this.getDb();
return await db.index(this.indexName);
return db.index(this.indexName).namespace(this.namespace);
}
/**
@@ -82,8 +82,8 @@ export class PineconeVectorStore implements VectorStore {
* @returns The result of the delete query.
*/
async clearIndex() {
const db: Pinecone = await this.getDb();
return await db.index(this.indexName).deleteAll();
const idx = await this.index();
return await idx.deleteAll();
}
/**
@@ -155,12 +155,10 @@ export class PineconeVectorStore implements VectorStore {
};
const idx = await this.index();
const results = await idx.namespace(this.namespace).query(options);
const results = await idx.query(options);
const idList = results.matches.map((row) => row.id);
const records: FetchResponse<any> = await idx
.namespace(this.namespace)
.fetch(idList);
const records: FetchResponse<any> = await idx.fetch(idList);
const rows = Object.values(records.records);
const nodes = rows.map((row) => {
@@ -289,7 +289,7 @@ export class QdrantVectorStore implements VectorStore {
* @param query The VectorStoreQuery to be used
*/
private async buildQueryFilter(query: VectorStoreQuery) {
if (!query.docIds && !query.queryStr) {
if (!query.docIds && !query.queryStr && !query.filters) {
return null;
}
+33
View File
@@ -0,0 +1,33 @@
import type { BaseTool } from "../types.js";
import { WikipediaTool } from "./WikipediaTool.js";
enum Tools {
Wikipedia = "wikipedia.WikipediaToolSpec",
}
type ToolConfig = { [key in Tools]: Record<string, any> };
export class ToolFactory {
private static async createTool(
key: Tools,
options: Record<string, any>,
): Promise<BaseTool> {
if (key === Tools.Wikipedia) {
const tool = new WikipediaTool();
return tool;
}
throw new Error(
`Sorry! Tool ${key} is not supported yet. Options: ${options}`,
);
}
public static async createTools(config: ToolConfig): Promise<BaseTool[]> {
const tools: BaseTool[] = [];
for (const [key, value] of Object.entries(config as ToolConfig)) {
const tool = await ToolFactory.createTool(key as Tools, value);
tools.push(tool);
}
return tools;
}
}
+54
View File
@@ -0,0 +1,54 @@
import { default as wiki } from "wikipedia";
import type { BaseTool, ToolMetadata } from "../types.js";
export type WikipediaToolParams = {
metadata?: ToolMetadata;
};
type WikipediaCallParams = {
query: string;
lang?: string;
};
const DEFAULT_META_DATA: ToolMetadata = {
name: "wikipedia_tool",
description: "A tool that uses a query engine to search Wikipedia.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "The query to search for",
},
},
required: ["query"],
},
};
export class WikipediaTool implements BaseTool {
private readonly DEFAULT_LANG = "en";
metadata: ToolMetadata;
constructor(params?: WikipediaToolParams) {
this.metadata = params?.metadata || DEFAULT_META_DATA;
}
async loadData(
page: string,
lang: string = this.DEFAULT_LANG,
): Promise<string> {
wiki.default.setLang(lang);
const pageResult = await wiki.default.page(page, { autoSuggest: false });
const content = await pageResult.content();
return content;
}
async call({
query,
lang = this.DEFAULT_LANG,
}: WikipediaCallParams): Promise<string> {
const searchResult = await wiki.default.search(query);
if (searchResult.results.length === 0) return "No search results.";
return await this.loadData(searchResult.results[0].title, lang);
}
}
+2
View File
@@ -1,4 +1,6 @@
export * from "./QueryEngineTool.js";
export * from "./ToolFactory.js";
export * from "./WikipediaTool.js";
export * from "./functionTool.js";
export * from "./types.js";
export * from "./utils.js";
+13
View File
@@ -0,0 +1,13 @@
# @llamaindex/core-test
## 0.0.2
### Patch Changes
- 484a710: - Add missing exports:
- `IndexStructType`,
- `IndexDict`,
- `jsonToIndexStruct`,
- `IndexList`,
- `IndexStruct`
- Fix `IndexDict.toJson()` method
+29 -10
View File
@@ -1,21 +1,40 @@
import { storageContextFromDefaults } from "llamaindex/storage/StorageContext";
import {
storageContextFromDefaults,
type StorageContext,
} from "llamaindex/storage/StorageContext";
import { existsSync, rmSync } from "node:fs";
import { describe, expect, test, vi, vitest } from "vitest";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
afterAll,
beforeAll,
describe,
expect,
test,
vi,
vitest,
} from "vitest";
const testDir = await mkdtemp(join(tmpdir(), "test-"));
vitest.spyOn(console, "error");
describe("StorageContext", () => {
let storageContext: StorageContext;
beforeAll(async () => {
storageContext = await storageContextFromDefaults({
persistDir: testDir,
});
});
test("initializes", async () => {
vi.mocked(console.error).mockImplementation(() => {}); // silence console.error
const storageContext = await storageContextFromDefaults({
persistDir: "/tmp/test_dir",
});
expect(existsSync("/tmp/test_dir")).toBe(true);
expect(existsSync(testDir)).toBe(true);
expect(storageContext).toBeDefined();
});
// cleanup
rmSync("/tmp/test_dir", { recursive: true });
afterAll(() => {
rmSync(testDir, { recursive: true });
});
});
@@ -1,48 +1,39 @@
import type { ServiceContext } from "llamaindex";
import {
Document,
OpenAI,
OpenAIEmbedding,
SummaryIndex,
VectorStoreIndex,
serviceContextFromDefaults,
storageContextFromDefaults,
type ServiceContext,
type StorageContext,
} from "llamaindex";
import { beforeAll, describe, expect, it, vi } from "vitest";
import {
mockEmbeddingModel,
mockLlmGeneration,
} from "../utility/mockOpenAI.js";
import { rmSync } from "node:fs";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
const testDir = await mkdtemp(join(tmpdir(), "test-"));
// Mock the OpenAI getOpenAISession function during testing
vi.mock("llamaindex/llm/open_ai", () => {
return {
getOpenAISession: vi.fn().mockImplementation(() => null),
};
});
import { mockServiceContext } from "../utility/mockServiceContext.js";
describe("SummaryIndex", () => {
let serviceContext: ServiceContext;
let storageContext: StorageContext;
beforeAll(() => {
const embeddingModel = new OpenAIEmbedding();
const llm = new OpenAI();
mockEmbeddingModel(embeddingModel);
mockLlmGeneration({ languageModel: llm });
const ctx = serviceContextFromDefaults({
embedModel: embeddingModel,
llm,
beforeAll(async () => {
serviceContext = mockServiceContext();
storageContext = await storageContextFromDefaults({
persistDir: testDir,
});
serviceContext = ctx;
});
it("SummaryIndex and VectorStoreIndex must be able to share the same storage context", async () => {
const storageContext = await storageContextFromDefaults({
persistDir: "/tmp/test_dir",
});
const documents = [new Document({ text: "lorem ipsem", id_: "1" })];
const vectorIndex = await VectorStoreIndex.fromDocuments(documents, {
serviceContext,
@@ -55,4 +46,8 @@ describe("SummaryIndex", () => {
});
expect(summaryIndex).toBeDefined();
});
afterAll(() => {
rmSync(testDir, { recursive: true });
});
});
@@ -0,0 +1,70 @@
import type { ServiceContext, StorageContext } from "llamaindex";
import {
Document,
VectorStoreIndex,
storageContextFromDefaults,
} from "llamaindex";
import { rmSync } from "node:fs";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
const testDir = await mkdtemp(join(tmpdir(), "test-"));
vi.mock("llamaindex/llm/open_ai", () => {
return {
getOpenAISession: vi.fn().mockImplementation(() => null),
};
});
import { mockServiceContext } from "../utility/mockServiceContext.js";
describe.sequential("VectorStoreIndex", () => {
let serviceContext: ServiceContext;
let storageContext: StorageContext;
let testStrategy: (
// strategy?: DocStoreStrategy,
runs?: number,
) => Promise<Array<number>>;
beforeAll(async () => {
serviceContext = mockServiceContext();
storageContext = await storageContextFromDefaults({
persistDir: testDir,
});
testStrategy = async (
// strategy?: DocStoreStrategy,
runs: number = 2,
): Promise<Array<number>> => {
const documents = [new Document({ text: "lorem ipsem", id_: "1" })];
const entries = [];
for (let i = 0; i < runs; i++) {
await VectorStoreIndex.fromDocuments(documents, {
serviceContext,
storageContext,
// docStoreStrategy: strategy,
});
const docs = await storageContext.docStore.docs();
entries.push(Object.keys(docs).length);
}
return entries;
};
});
test("fromDocuments does not stores duplicates per default", async () => {
const entries = await testStrategy();
expect(entries[0]).toBe(entries[1]);
});
// test("fromDocuments ignores duplicates in upserts", async () => {
// const entries = await testStrategy(DocStoreStrategy.DUPLICATES_ONLY);
// expect(entries[0]).toBe(entries[1]);
// });
afterAll(async () => {
// TODO: VectorStoreIndex.fromDocuments running twice is causing a cleanup issue
await new Promise((resolve) => setTimeout(resolve, 100));
rmSync(testDir, { recursive: true });
});
});
@@ -2,17 +2,10 @@ import type { ServiceContext } from "llamaindex";
import {
FunctionTool,
ObjectIndex,
OpenAI,
OpenAIEmbedding,
SimpleToolNodeMapping,
VectorStoreIndex,
serviceContextFromDefaults,
} from "llamaindex";
import { beforeAll, describe, expect, test, vi } from "vitest";
import {
mockEmbeddingModel,
mockLlmGeneration,
} from "../utility/mockOpenAI.js";
vi.mock("llamaindex/llm/open_ai", () => {
return {
@@ -20,22 +13,13 @@ vi.mock("llamaindex/llm/open_ai", () => {
};
});
import { mockServiceContext } from "../utility/mockServiceContext.js";
describe("ObjectIndex", () => {
let serviceContext: ServiceContext;
beforeAll(() => {
const embeddingModel = new OpenAIEmbedding();
const llm = new OpenAI();
mockEmbeddingModel(embeddingModel);
mockLlmGeneration({ languageModel: llm });
const ctx = serviceContextFromDefaults({
embedModel: embeddingModel,
llm,
});
serviceContext = ctx;
serviceContext = mockServiceContext();
});
test("test_object_with_tools", async () => {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/core-test",
"private": true,
"version": "0.0.1",
"version": "0.0.2",
"type": "module",
"scripts": {
"test": "vitest run"
@@ -0,0 +1,23 @@
import {
OpenAI,
OpenAIEmbedding,
serviceContextFromDefaults,
} from "llamaindex";
import {
mockEmbeddingModel,
mockLlmGeneration,
} from "../utility/mockOpenAI.js";
export function mockServiceContext() {
const embeddingModel = new OpenAIEmbedding();
const llm = new OpenAI();
mockEmbeddingModel(embeddingModel);
mockLlmGeneration({ languageModel: llm });
return serviceContextFromDefaults({
embedModel: embeddingModel,
llm,
});
}
-20
View File
@@ -1,20 +0,0 @@
{
"root": false,
"rules": {
"turbo/no-undeclared-env-vars": [
"error",
{
"allowList": [
"OPENAI_API_KEY",
"LLAMA_CLOUD_API_KEY",
"npm_config_user_agent",
"http_proxy",
"https_proxy",
"MODEL",
"NEXT_PUBLIC_CHAT_API",
"NEXT_PUBLIC_MODEL"
]
}
]
}
}
-175
View File
@@ -1,175 +0,0 @@
# create-llama
## 0.0.28
### Patch Changes
- 89a49f4: Add more config variables to .env file
- fdf48dd: Add "Start in VSCode" option to postInstallAction
- fdf48dd: Add devcontainers to generated code
## 0.0.27
### Patch Changes
- 2d29350: Add LlamaParse option when selecting a pdf file or a folder (FastAPI only)
- b354f23: Add embedding model option to create-llama (FastAPI only)
## 0.0.26
### Patch Changes
- 09d532e: feat: generate llama pack project from llama index
- cfdd6db: feat: add pinecone support to create llama
- ef25d69: upgrade llama-index package to version v0.10.7 for create-llama app
- 50dfd7b: update fastapi for CVE-2024-24762
## 0.0.25
### Patch Changes
- d06a85b: Add option to create an agent by selecting tools (Google, Wikipedia)
- 7b7329b: Added latest turbo models for GPT-3.5 and GPT 4
## 0.0.24
### Patch Changes
- ba95ca3: Use condense plus context chat engine for FastAPI as default
## 0.0.23
### Patch Changes
- c680af6: Fixed issues with locating templates path
## 0.0.22
### Patch Changes
- 6dd401e: Add an option to provide an URL and chat with the website data (FastAPI only)
- e9b87ef: Select a folder as data source and support more file types (.pdf, .doc, .docx, .xls, .xlsx, .csv)
## 0.0.20
### Patch Changes
- 27d55fd: Add an option to provide an URL and chat with the website data
## 0.0.19
### Patch Changes
- 3a29a80: Add node_modules to gitignore in Express backends
- fe03aaa: feat: generate llama pack example
## 0.0.18
### Patch Changes
- 88d3b41: fix packaging
## 0.0.17
### Patch Changes
- fa17f7e: Add an option that allows the user to run the generated app
- 9e5d8e1: Add an option to select a local PDF file as data source
## 0.0.16
### Patch Changes
- a73942d: Fix: Bundle mongo dependency with NextJS
- 9492cc6: Feat: Added option to automatically install dependencies (for Python and TS)
- f74dea5: Feat: Show images in chat messages using GPT4 Vision (Express and NextJS only)
## 0.0.15
### Patch Changes
- 8e124e5: feat: support showing image on chat message
## 0.0.14
### Patch Changes
- 2e6b36e: fix: re-organize file structure
- 2b356c8: fix: relative path incorrect
## 0.0.13
### Patch Changes
- Added PostgreSQL vector store (for Typescript and Python)
- Improved async handling in FastAPI
## 0.0.12
### Patch Changes
- 9c5e22a: Added cross-env so frontends with Express/FastAPI backends are working under Windows
- 5ab65eb: Bring Python templates with TS templates to feature parity
- 9c5e22a: Added vector DB selector to create-llama (starting with MongoDB support)
## 0.0.11
### Patch Changes
- 2aeb341: - Added option to create a new project based on community templates
- Added OpenAI model selector for NextJS projects
- Added GPT4 Vision support (and file upload)
## 0.0.10
### Patch Changes
- Bugfixes (thanks @marcusschiesser)
## 0.0.9
### Patch Changes
- acfe232: Deployment fixes (thanks @seldo)
## 0.0.8
### Patch Changes
- 8cdb07f: Fix Next deployment (thanks @seldo and @marcusschiesser)
## 0.0.7
### Patch Changes
- 9f9f293: Added more to README and made it easier to switch models (thanks @seldo)
## 0.0.6
### Patch Changes
- 4431ec7: Label bug fix (thanks @marcusschiesser)
## 0.0.5
### Patch Changes
- 25257f4: Fix issue where it doesn't find OpenAI Key when running npm run generate (#182) (thanks @RayFernando1337)
## 0.0.4
### Patch Changes
- 031e926: Update create-llama readme (thanks @logan-markewich)
## 0.0.3
### Patch Changes
- 91b42a3: change version (thanks @marcusschiesser)
## 0.0.2
### Patch Changes
- e2a6805: Hello Create Llama (thanks @marcusschiesser)
-9
View File
@@ -1,9 +0,0 @@
The MIT License (MIT)
Copyright (c) 2023 LlamaIndex, Vercel, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-126
View File
@@ -1,126 +0,0 @@
# Create LlamaIndex App
The easiest way to get started with [LlamaIndex](https://www.llamaindex.ai/) is by using `create-llama`. This CLI tool enables you to quickly start building a new LlamaIndex application, with everything set up for you.
Just run
```bash
npx create-llama@latest
```
to get started, or see below for more options. Once your app is generated, run
```bash
npm run dev
```
to start the development server. You can then visit [http://localhost:3000](http://localhost:3000) to see your app.
## What you'll get
- A Next.js-powered front-end. The app is set up as a chat interface that can answer questions about your data (see below)
- You can style it with HTML and CSS, or you can optionally use components from [shadcn/ui](https://ui.shadcn.com/)
- Your choice of 3 back-ends:
- **Next.js**: if you select this option, youll have a full stack Next.js application that you can deploy to a host like [Vercel](https://vercel.com/) in just a few clicks. This uses [LlamaIndex.TS](https://www.npmjs.com/package/llamaindex), our TypeScript library.
- **Express**: if you want a more traditional Node.js application you can generate an Express backend. This also uses LlamaIndex.TS.
- **Python FastAPI**: if you select this option youll get a backend powered by the [llama-index python package](https://pypi.org/project/llama-index/), which you can deploy to a service like Render or fly.io.
- The back-end has a single endpoint that allows you to send the state of your chat and receive additional responses
- You can choose whether you want a streaming or non-streaming back-end (if you're not sure, we recommend streaming)
- You can choose whether you want to use `ContextChatEngine` or `SimpleChatEngine`
- `SimpleChatEngine` will just talk to the LLM directly without using your data
- `ContextChatEngine` will use your data to answer questions (see below).
- The app uses OpenAI by default, so you'll need an OpenAI API key, or you can customize it to use any of the dozens of LLMs we support.
## Using your data
If you've enabled `ContextChatEngine`, you can supply your own data and the app will index it and answer questions. Your generated app will have a folder called `data`:
- With the Next.js backend this is `./data`
- With the Express or Python backend this is in `./backend/data`
The app will ingest any supported files you put in this directory. Your Next.js and Express apps use LlamaIndex.TS so they will be able to ingest any PDF, text, CSV, Markdown, Word and HTML files. The Python backend can read even more types, including video and audio files.
Before you can use your data, you need to index it. If you're using the Next.js or Express apps, run:
```bash
npm run generate
```
Then re-start your app. Remember you'll need to re-run `generate` if you add new files to your `data` folder. If you're using the Python backend, you can trigger indexing of your data by deleting the `./storage` folder and re-starting the app.
## Don't want a front-end?
It's optional! If you've selected the Python or Express back-ends, just delete the `frontend` folder and you'll get an API without any front-end code.
## Customizing the LLM
By default the app will use OpenAI's gpt-3.5-turbo model. If you want to use GPT-4, you can modify this by editing a file:
- In the Next.js backend, edit `./app/api/chat/route.ts` and replace `gpt-3.5-turbo` with `gpt-4`
- In the Express backend, edit `./backend/src/controllers/chat.controller.ts` and likewise replace `gpt-3.5-turbo` with `gpt-4`
- In the Python backend, edit `./backend/app/utils/index.py` and once again replace `gpt-3.5-turbo` with `gpt-4`
You can also replace OpenAI with one of our [dozens of other supported LLMs](https://docs.llamaindex.ai/en/stable/module_guides/models/llms/modules.html).
## Example
The simplest thing to do is run `create-llama` in interactive mode:
```bash
npx create-llama@latest
# or
npm create llama@latest
# or
yarn create llama
# or
pnpm create llama@latest
```
You will be asked for the name of your project, along with other configuration options, something like this:
```bash
>> npm create llama@latest
Need to install the following packages:
create-llama@latest
Ok to proceed? (y) y
✔ What is your project named? … my-app
✔ Which template would you like to use? Chat with streaming
✔ Which framework would you like to use? NextJS
✔ Which UI would you like to use? Just HTML
✔ Which chat engine would you like to use? ContextChatEngine
✔ Please provide your OpenAI API key (leave blank to skip): …
✔ Would you like to use ESLint? … No / Yes
Creating a new LlamaIndex app in /home/my-app.
```
### Running non-interactively
You can also pass command line arguments to set up a new project
non-interactively. See `create-llama --help`:
```bash
create-llama <project-directory> [options]
Options:
-V, --version output the version number
--use-npm
Explicitly tell the CLI to bootstrap the app using npm
--use-pnpm
Explicitly tell the CLI to bootstrap the app using pnpm
--use-yarn
Explicitly tell the CLI to bootstrap the app using Yarn
```
## LlamaIndex Documentation
- [TS/JS docs](https://ts.llamaindex.ai/)
- [Python docs](https://docs.llamaindex.ai/en/stable/)
Inspired by and adapted from [create-next-app](https://github.com/vercel/next.js/tree/canary/packages/create-next-app)
-147
View File
@@ -1,147 +0,0 @@
/* eslint-disable import/no-extraneous-dependencies */
import path from "path";
import { green, yellow } from "picocolors";
import { tryGitInit } from "./helpers/git";
import { isFolderEmpty } from "./helpers/is-folder-empty";
import { getOnline } from "./helpers/is-online";
import { isWriteable } from "./helpers/is-writeable";
import { makeDir } from "./helpers/make-dir";
import fs from "fs";
import terminalLink from "terminal-link";
import type { InstallTemplateArgs } from "./helpers";
import { installTemplate } from "./helpers";
import { writeDevcontainer } from "./helpers/devcontainer";
import { templatesDir } from "./helpers/dir";
import { toolsRequireConfig } from "./helpers/tools";
export type InstallAppArgs = Omit<
InstallTemplateArgs,
"appName" | "root" | "isOnline" | "customApiPath"
> & {
appPath: string;
frontend: boolean;
};
export async function createApp({
template,
framework,
engine,
ui,
appPath,
packageManager,
eslint,
frontend,
openAiKey,
llamaCloudKey,
model,
embeddingModel,
communityProjectPath,
llamapack,
vectorDb,
externalPort,
postInstallAction,
dataSource,
tools,
}: InstallAppArgs): Promise<void> {
const root = path.resolve(appPath);
if (!(await isWriteable(path.dirname(root)))) {
console.error(
"The application path is not writable, please check folder permissions and try again.",
);
console.error(
"It is likely you do not have write permissions for this folder.",
);
process.exit(1);
}
const appName = path.basename(root);
await makeDir(root);
if (!isFolderEmpty(root, appName)) {
process.exit(1);
}
const useYarn = packageManager === "yarn";
const isOnline = !useYarn || (await getOnline());
console.log(`Creating a new LlamaIndex app in ${green(root)}.`);
console.log();
const args = {
appName,
root,
template,
framework,
engine,
ui,
packageManager,
isOnline,
eslint,
openAiKey,
llamaCloudKey,
model,
embeddingModel,
communityProjectPath,
llamapack,
vectorDb,
externalPort,
postInstallAction,
dataSource,
tools,
};
if (frontend) {
// install backend
const backendRoot = path.join(root, "backend");
await makeDir(backendRoot);
await installTemplate({ ...args, root: backendRoot, backend: true });
// install frontend
const frontendRoot = path.join(root, "frontend");
await makeDir(frontendRoot);
await installTemplate({
...args,
root: frontendRoot,
framework: "nextjs",
customApiPath: `http://localhost:${externalPort ?? 8000}/api/chat`,
backend: false,
});
// copy readme for fullstack
await fs.promises.copyFile(
path.join(templatesDir, "README-fullstack.md"),
path.join(root, "README.md"),
);
} else {
await installTemplate({ ...args, backend: true });
}
process.chdir(root);
if (tryGitInit(root)) {
console.log("Initialized a git repository.");
console.log();
}
await writeDevcontainer(root, templatesDir, framework, frontend);
if (toolsRequireConfig(tools)) {
console.log(
yellow(
`You have selected tools that require configuration. Please configure them in the ${terminalLink(
"tools_config.json",
`file://${root}/tools_config.json`,
)} file.`,
),
);
}
console.log("");
console.log(`${green("Success!")} Created ${appName} at ${appPath}`);
console.log(
`Now have a look at the ${terminalLink(
"README.md",
`file://${root}/README.md`,
)} and learn how to get started.`,
);
console.log();
}
-145
View File
@@ -1,145 +0,0 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from "@playwright/test";
import { ChildProcess } from "child_process";
import fs from "fs";
import path from "path";
import type {
TemplateEngine,
TemplateFramework,
TemplatePostInstallAction,
TemplateType,
TemplateUI,
} from "../helpers";
import { createTestDir, runCreateLlama, type AppType } from "./utils";
const templateTypes: TemplateType[] = ["streaming", "simple"];
const templateFrameworks: TemplateFramework[] = [
"nextjs",
"express",
"fastapi",
];
const templateEngines: TemplateEngine[] = ["simple", "context"];
const templateUIs: TemplateUI[] = ["shadcn", "html"];
const templatePostInstallActions: TemplatePostInstallAction[] = [
"none",
"runApp",
];
for (const templateType of templateTypes) {
for (const templateFramework of templateFrameworks) {
for (const templateEngine of templateEngines) {
for (const templateUI of templateUIs) {
for (const templatePostInstallAction of templatePostInstallActions) {
if (templateFramework === "nextjs" && templateType === "simple") {
// nextjs doesn't support simple templates - skip tests
continue;
}
const appType: AppType =
templateFramework === "express" || templateFramework === "fastapi"
? templateType === "simple"
? "--no-frontend" // simple templates don't have frontends
: "--frontend"
: "";
if (appType === "--no-frontend" && templateUI !== "html") {
// if there's no frontend, don't iterate over UIs
continue;
}
test.describe(`try create-llama ${templateType} ${templateFramework} ${templateEngine} ${templateUI} ${appType} ${templatePostInstallAction}`, async () => {
let port: number;
let externalPort: number;
let cwd: string;
let name: string;
let appProcess: ChildProcess;
// Only test without using vector db for now
const vectorDb = "none";
test.beforeAll(async () => {
port = Math.floor(Math.random() * 10000) + 10000;
externalPort = port + 1;
cwd = await createTestDir();
const result = await runCreateLlama(
cwd,
templateType,
templateFramework,
templateEngine,
templateUI,
vectorDb,
appType,
port,
externalPort,
templatePostInstallAction,
);
name = result.projectName;
appProcess = result.appProcess;
});
test("App folder should exist", async () => {
const dirExists = fs.existsSync(path.join(cwd, name));
expect(dirExists).toBeTruthy();
});
test("Frontend should have a title", async ({ page }) => {
test.skip(templatePostInstallAction !== "runApp");
test.skip(appType === "--no-frontend");
await page.goto(`http://localhost:${port}`);
await expect(page.getByText("Built by LlamaIndex")).toBeVisible();
});
test("Frontend should be able to submit a message and receive a response", async ({
page,
}) => {
test.skip(templatePostInstallAction !== "runApp");
test.skip(appType === "--no-frontend");
await page.goto(`http://localhost:${port}`);
await page.fill("form input", "hello");
const [response] = await Promise.all([
page.waitForResponse(
(res) => {
return (
res.url().includes("/api/chat") && res.status() === 200
);
},
{
timeout: 1000 * 60,
},
),
page.click("form button[type=submit]"),
]);
const text = await response.text();
console.log("AI response when submitting message: ", text);
expect(response.ok()).toBeTruthy();
});
test("Backend should response when calling API", async ({
request,
}) => {
test.skip(templatePostInstallAction !== "runApp");
test.skip(appType !== "--no-frontend");
const backendPort = appType === "" ? port : externalPort;
const response = await request.post(
`http://localhost:${backendPort}/api/chat`,
{
data: {
messages: [
{
role: "user",
content: "Hello",
},
],
},
},
);
const text = await response.text();
console.log("AI response when calling API: ", text);
expect(response.ok()).toBeTruthy();
});
// clean processes
test.afterAll(async () => {
appProcess?.kill();
});
});
}
}
}
}
}
-16
View File
@@ -1,16 +0,0 @@
{
"compilerOptions": {
"target": "es2019",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"declaration": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"tsBuildInfoFile": "./lib/.tsbuildinfo"
},
"include": ["./**/*.ts"]
}
-181
View File
@@ -1,181 +0,0 @@
import { ChildProcess, exec } from "child_process";
import crypto from "node:crypto";
import { mkdir } from "node:fs/promises";
import * as path from "path";
import waitPort from "wait-port";
import {
TemplateEngine,
TemplateFramework,
TemplatePostInstallAction,
TemplateType,
TemplateUI,
TemplateVectorDB,
} from "../helpers";
export type AppType = "--frontend" | "--no-frontend" | "";
const MODEL = "gpt-3.5-turbo";
const EMBEDDING_MODEL = "text-embedding-ada-002";
export type CreateLlamaResult = {
projectName: string;
appProcess: ChildProcess;
};
// eslint-disable-next-line max-params
export async function checkAppHasStarted(
frontend: boolean,
framework: TemplateFramework,
port: number,
externalPort: number,
timeout: number,
) {
if (frontend) {
await Promise.all([
waitPort({
host: "localhost",
port: port,
timeout,
}),
waitPort({
host: "localhost",
port: externalPort,
timeout,
}),
]).catch((err) => {
console.error(err);
throw err;
});
} else {
let wPort: number;
if (framework === "nextjs") {
wPort = port;
} else {
wPort = externalPort;
}
await waitPort({
host: "localhost",
port: wPort,
timeout,
}).catch((err) => {
console.error(err);
throw err;
});
}
}
// eslint-disable-next-line max-params
export async function runCreateLlama(
cwd: string,
templateType: TemplateType,
templateFramework: TemplateFramework,
templateEngine: TemplateEngine,
templateUI: TemplateUI,
vectorDb: TemplateVectorDB,
appType: AppType,
port: number,
externalPort: number,
postInstallAction: TemplatePostInstallAction,
): Promise<CreateLlamaResult> {
const createLlama = path.join(
__dirname,
"..",
"output",
"package",
"dist",
"index.js",
);
const name = [
templateType,
templateFramework,
templateEngine,
templateUI,
appType,
].join("-");
const command = [
"node",
createLlama,
name,
"--template",
templateType,
"--framework",
templateFramework,
"--engine",
templateEngine,
"--ui",
templateUI,
"--vector-db",
vectorDb,
"--model",
MODEL,
"--embedding-model",
EMBEDDING_MODEL,
"--open-ai-key",
process.env.OPENAI_API_KEY || "testKey",
appType,
"--eslint",
"--use-npm",
"--port",
port,
"--external-port",
externalPort,
"--post-install-action",
postInstallAction,
"--tools",
"none",
"--no-llama-parse",
].join(" ");
console.log(`running command '${command}' in ${cwd}`);
const appProcess = exec(command, {
cwd,
env: {
...process.env,
},
});
appProcess.stderr?.on("data", (data) => {
console.log(data.toString());
});
appProcess.on("exit", (code) => {
if (code !== 0 && code !== null) {
throw new Error(`create-llama command was failed!`);
}
});
// Wait for app to start
if (postInstallAction === "runApp") {
await checkAppHasStarted(
appType === "--frontend",
templateFramework,
port,
externalPort,
1000 * 60 * 5,
);
} else {
// wait create-llama to exit
// we don't test install dependencies for now, so just set timeout for 10 seconds
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error("create-llama timeout error"));
}, 1000 * 10);
appProcess.on("exit", (code) => {
if (code !== 0 && code !== null) {
clearTimeout(timeout);
reject(new Error("create-llama command was failed!"));
} else {
clearTimeout(timeout);
resolve(undefined);
}
});
});
}
return {
projectName: name,
appProcess,
};
}
export async function createTestDir() {
const cwd = path.join(__dirname, "cache", crypto.randomUUID());
await mkdir(cwd, { recursive: true });
return cwd;
}
@@ -1,6 +0,0 @@
export const COMMUNITY_OWNER = "run-llama";
export const COMMUNITY_REPO = "create_llama_projects";
export const LLAMA_PACK_OWNER = "run-llama";
export const LLAMA_PACK_REPO = "llama_index";
export const LLAMA_PACK_FOLDER = "llama-index-packs";
export const LLAMA_PACK_FOLDER_PATH = `${LLAMA_PACK_OWNER}/${LLAMA_PACK_REPO}/main/${LLAMA_PACK_FOLDER}`;
-50
View File
@@ -1,50 +0,0 @@
/* eslint-disable import/no-extraneous-dependencies */
import { async as glob } from "fast-glob";
import fs from "fs";
import path from "path";
interface CopyOption {
cwd?: string;
rename?: (basename: string) => string;
parents?: boolean;
}
const identity = (x: string) => x;
export const copy = async (
src: string | string[],
dest: string,
{ cwd, rename = identity, parents = true }: CopyOption = {},
) => {
const source = typeof src === "string" ? [src] : src;
if (source.length === 0 || !dest) {
throw new TypeError("`src` and `dest` are required");
}
const sourceFiles = await glob(source, {
cwd,
dot: true,
absolute: false,
stats: false,
});
const destRelativeToCwd = cwd ? path.resolve(cwd, dest) : dest;
return Promise.all(
sourceFiles.map(async (p) => {
const dirname = path.dirname(p);
const basename = rename(path.basename(p));
const from = cwd ? path.resolve(cwd, p) : p;
const to = parents
? path.join(destRelativeToCwd, dirname, basename)
: path.join(destRelativeToCwd, basename);
// Ensure the destination directory exists
await fs.promises.mkdir(path.dirname(to), { recursive: true });
return fs.promises.copyFile(from, to);
}),
);
};
@@ -1,61 +0,0 @@
import fs from "fs";
import path from "path";
import { TemplateFramework } from "./types";
function renderDevcontainerContent(
templatesDir: string,
framework: TemplateFramework,
frontend: boolean,
) {
const devcontainerJson: any = JSON.parse(
fs.readFileSync(path.join(templatesDir, "devcontainer.json"), "utf8"),
);
// Modify postCreateCommand
if (frontend) {
devcontainerJson.postCreateCommand =
framework === "fastapi"
? "cd backend && poetry install && cd ../frontend && npm install"
: "cd backend && npm install && cd ../frontend && npm install";
} else {
devcontainerJson.postCreateCommand =
framework === "fastapi" ? "poetry install" : "npm install";
}
// Modify containerEnv
if (framework === "fastapi") {
if (frontend) {
devcontainerJson.containerEnv = {
...devcontainerJson.containerEnv,
PYTHONPATH: "${PYTHONPATH}:${workspaceFolder}/backend",
};
} else {
devcontainerJson.containerEnv = {
...devcontainerJson.containerEnv,
PYTHONPATH: "${PYTHONPATH}:${workspaceFolder}",
};
}
}
return JSON.stringify(devcontainerJson, null, 2);
}
export const writeDevcontainer = async (
root: string,
templatesDir: string,
framework: TemplateFramework,
frontend: boolean,
) => {
console.log("Adding .devcontainer");
const devcontainerContent = renderDevcontainerContent(
templatesDir,
framework,
frontend,
);
const devcontainerDir = path.join(root, ".devcontainer");
fs.mkdirSync(devcontainerDir);
await fs.promises.writeFile(
path.join(devcontainerDir, "devcontainer.json"),
devcontainerContent,
);
};
-3
View File
@@ -1,3 +0,0 @@
import path from "path";
export const templatesDir = path.join(__dirname, "..", "templates");
@@ -1,241 +0,0 @@
import fs from "fs/promises";
import path from "path";
import {
FileSourceConfig,
TemplateDataSource,
TemplateFramework,
TemplateVectorDB,
} from "./types";
type EnvVar = {
name?: string;
description?: string;
value?: string;
};
const renderEnvVar = (envVars: EnvVar[]): string => {
return envVars.reduce(
(prev, env) =>
prev +
(env.description
? `# ${env.description.replaceAll("\n", "\n# ")}\n`
: "") +
(env.name
? env.value
? `${env.name}=${env.value}\n\n`
: `# ${env.name}=\n\n`
: ""),
"",
);
};
const getVectorDBEnvs = (vectorDb: TemplateVectorDB) => {
switch (vectorDb) {
case "mongo":
return [
{
name: "MONGO_URI",
description:
"For generating a connection URI, see https://docs.timescale.com/use-timescale/latest/services/create-a-service\nThe MongoDB connection URI.",
},
{
name: "MONGODB_DATABASE",
},
{
name: "MONGODB_VECTORS",
},
{
name: "MONGODB_VECTOR_INDEX",
},
];
case "pg":
return [
{
name: "PG_CONNECTION_STRING",
description:
"For generating a connection URI, see https://docs.timescale.com/use-timescale/latest/services/create-a-service\nThe PostgreSQL connection string.",
},
];
case "pinecone":
return [
{
name: "PINECONE_API_KEY",
description:
"Configuration for Pinecone vector store\nThe Pinecone API key.",
},
{
name: "PINECONE_ENVIRONMENT",
},
{
name: "PINECONE_INDEX_NAME",
},
];
default:
return [];
}
};
const getDataSourceEnvs = (dataSource: TemplateDataSource) => {
switch (dataSource.type) {
case "web":
return [
{
name: "BASE_URL",
description: "The base URL to start web scraping.",
},
{
name: "URL_PREFIX",
description: "The prefix of the URL to start web scraping.",
},
{
name: "MAX_DEPTH",
description: "The maximum depth to scrape.",
},
];
default:
return [];
}
};
export const createBackendEnvFile = async (
root: string,
opts: {
openAiKey?: string;
llamaCloudKey?: string;
vectorDb?: TemplateVectorDB;
model?: string;
embeddingModel?: string;
framework?: TemplateFramework;
dataSource?: TemplateDataSource;
port?: number;
},
) => {
// Init env values
const envFileName = ".env";
const defaultEnvs = [
{
render: true,
name: "MODEL",
description: "The name of LLM model to use.",
value: opts.model || "gpt-3.5-turbo",
},
{
render: true,
name: "OPENAI_API_KEY",
description: "The OpenAI API key to use.",
value: opts.openAiKey,
},
// Add vector database environment variables
...(opts.vectorDb ? getVectorDBEnvs(opts.vectorDb) : []),
// Add data source environment variables
...(opts.dataSource ? getDataSourceEnvs(opts.dataSource) : []),
];
let envVars: EnvVar[] = [];
if (opts.framework === "fastapi") {
envVars = [
...defaultEnvs,
...[
{
name: "APP_HOST",
description: "The address to start the backend app.",
value: "0.0.0.0",
},
{
name: "APP_PORT",
description: "The port to start the backend app.",
value: opts.port?.toString() || "8000",
},
{
name: "EMBEDDING_MODEL",
description: "Name of the embedding model to use.",
value: opts.embeddingModel,
},
{
name: "EMBEDDING_DIM",
description: "Dimension of the embedding model to use.",
},
{
name: "LLM_TEMPERATURE",
description: "Temperature for sampling from the model.",
},
{
name: "LLM_MAX_TOKENS",
description: "Maximum number of tokens to generate.",
},
{
name: "TOP_K",
description:
"The number of similar embeddings to return when retrieving documents.",
value: "3",
},
{
name: "SYSTEM_PROMPT",
description: `Custom system prompt.
Example:
SYSTEM_PROMPT="
We have provided context information below.
---------------------
{context_str}
---------------------
Given this information, please answer the question: {query_str}
"`,
},
(opts?.dataSource?.config as FileSourceConfig).useLlamaParse
? {
name: "LLAMA_CLOUD_API_KEY",
description: `The Llama Cloud API key.`,
value: opts.llamaCloudKey,
}
: {},
],
];
} else {
envVars = [
...defaultEnvs,
...[
opts.framework === "nextjs"
? {
name: "NEXT_PUBLIC_MODEL",
description:
"The LLM model to use (hardcode to front-end artifact).",
}
: {},
],
];
}
// Render and write env file
const content = renderEnvVar(envVars);
await fs.writeFile(path.join(root, envFileName), content);
console.log(`Created '${envFileName}' file. Please check the settings.`);
};
export const createFrontendEnvFile = async (
root: string,
opts: {
customApiPath?: string;
model?: string;
},
) => {
const defaultFrontendEnvs = [
{
name: "MODEL",
description: "The OpenAI model to use.",
value: opts.model,
},
{
name: "NEXT_PUBLIC_MODEL",
description: "The OpenAI model to use (hardcode to front-end artifact).",
value: opts.model,
},
{
name: "NEXT_PUBLIC_CHAT_API",
description: "The backend API for chat endpoint.",
value: opts.customApiPath
? opts.customApiPath
: "http://localhost:8000/api/chat",
},
];
const content = renderEnvVar(defaultFrontendEnvs);
await fs.writeFile(path.join(root, ".env"), content);
};
@@ -1,15 +0,0 @@
export type PackageManager = "npm" | "pnpm" | "yarn";
export function getPkgManager(): PackageManager {
const userAgent = process.env.npm_config_user_agent || "";
if (userAgent.startsWith("yarn")) {
return "yarn";
}
if (userAgent.startsWith("pnpm")) {
return "pnpm";
}
return "npm";
}

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