Compare commits

..

25 Commits

Author SHA1 Message Date
github-actions[bot] 74fc725f37 Release 0.6.0 (#1199)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: marcusschiesser <marcusschiesser@users.noreply.github.com>
2024-09-13 16:07:16 +07:00
Marcus Schiesser a0a74aed60 fix: release openai package (#1200) 2024-09-13 16:01:00 +07:00
Marcus Schiesser 11feef8c82 Add workflows (#1188)
Co-authored-by: Thuc Pham <51660321+thucpn@users.noreply.github.com>
2024-09-13 15:46:02 +07:00
github-actions[bot] 9c5ff164ac Release 0.5.27 (#1195)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-12 13:47:11 -07:00
Alex Yang 7edeb1c2d7 feat: decouple openai from llamaindex module (#1194) 2024-09-12 13:36:08 -07:00
github-actions[bot] 8b95abdc85 Release 0.5.26 (#1193)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-12 11:38:07 -07:00
Alex Yang ffe0cd1ef1 chore: update changelog 2024-09-12 11:33:32 -07:00
Alex Yang 5d2111a19f feat: init support openai o1 model (#1192) 2024-09-12 11:31:05 -07:00
Alex Yang 68ac7fd57f ci: fix syntax (#1186) 2024-09-11 16:54:39 -07:00
Alex Yang 7320d96a36 fix: waku build (#1185) 2024-09-11 15:36:39 -07:00
Goran ee17fb475b feat: add PostgreSQL storage (#1180) 2024-09-11 12:31:04 -07:00
github-actions[bot] 28b877e31f Release 0.5.25 (#1182)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-11 12:08:39 -07:00
Alex Yang 4389b80a52 docs: update README.md (#1183) 2024-09-11 11:07:00 -07:00
Alex Yang d3bc663951 fix: vector store cleanup (#1175) 2024-09-11 10:20:55 -07:00
Kieran Simkin 4810364788 fix: handle RouterQueryEngine with string query (#1181) 2024-09-11 10:19:59 -07:00
github-actions[bot] 2dcad52dd9 Release 0.5.24 (#1178)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-10 23:48:51 -07:00
Alex Yang 0bf8d80b12 fix: llama cloud api build 2024-09-10 23:39:58 -07:00
github-actions[bot] e4bba02aec Release 0.5.23 (#1174)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-10 11:41:49 -07:00
Alex Yang 1caa0da657 chore: fix changeset 2024-09-10 11:34:07 -07:00
Alex Yang 711c814bb2 fix: patch python-format-js (#1173) 2024-09-10 09:49:36 -07:00
Alex Yang 5b832eb927 fix: strict type check (#1170) 2024-09-10 09:28:44 -07:00
Alex Yang 49988431f6 refactor: move settings.llm into core package (#1165) 2024-09-09 10:45:57 -07:00
Alex Yang 72d65dd51a docs: fix example (#1168) 2024-09-09 10:45:30 -07:00
Alex Yang 553bc55b19 refactor: move PromptHelper into core package (#1166) 2024-09-09 10:15:21 -07:00
Alex Yang fc6f69833c fix: example code (#1167) 2024-09-09 10:11:58 -07:00
207 changed files with 4266 additions and 1358 deletions
+18 -4
View File
@@ -12,6 +12,10 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
POSTGRES_USER: runneradmin
POSTGRES_HOST_AUTH_METHOD: trust
jobs:
e2e:
strategy:
@@ -22,9 +26,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ankane/setup-postgres@v1
with:
database: llamaindex_node_test
dev-files: true
- run: |
cd /tmp
git clone --branch v0.7.0 https://github.com/pgvector/pgvector.git
cd pgvector
make
sudo make install
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
@@ -42,7 +54,6 @@ jobs:
node-version: [18.x, 20.x, 22.x]
name: Test on Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
@@ -92,7 +103,7 @@ jobs:
- nextjs-agent
- nextjs-edge-runtime
- nextjs-node-runtime
# - waku-query-engine
- waku-query-engine
runs-on: ubuntu-latest
name: Build LlamaIndex Example (${{ matrix.packages }})
steps:
@@ -131,6 +142,9 @@ jobs:
- name: Pack @llamaindex/cloud
run: pnpm pack --pack-destination ${{ runner.temp }}
working-directory: packages/cloud
- name: Pack @llamaindex/openai
run: pnpm pack --pack-destination ${{ runner.temp }}
working-directory: packages/llm/openai
- name: Pack @llamaindex/core
run: pnpm pack --pack-destination ${{ runner.temp }}
working-directory: packages/core
+51 -1
View File
@@ -36,9 +36,44 @@ For now, browser support is limited due to the lack of support for [AsyncLocalSt
npm install llamaindex
pnpm install llamaindex
yarn add llamaindex
jsr install @llamaindex/core
```
### Setup TypeScript
```json5
{
compilerOptions: {
// ⬇️ add this line to your tsconfig.json
moduleResolution: "bundler", // or "node16"
},
}
```
<details>
<summary>Why?</summary>
We are shipping both ESM and CJS module, and compatible with Vercel Edge, Cloudflare Workers, and other serverless platforms.
So we are using [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) to support all environments.
This is a kind of modern way of shipping packages, but might cause TypeScript type check to fail because of legacy module resolution.
Imaging you put output file into `/dist/openai.js` but you are importing `llamaindex/openai` in your code, and set `package.json` like this:
```json
{
"exports": {
"./openai": "./dist/openai.js"
}
}
```
In old module resolution, TypeScript will not be able to find the module because it is not follow the file structure, even you run `node index.js` successfully. (on Node.js >=16)
See more about [moduleResolution](https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution) or
[TypeScript 5.0 blog](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#--moduleresolution-bundler7).
</details>
### Node.js
```ts
@@ -154,6 +189,21 @@ export async function chatWithAgent(
}
```
### Vite
We have some wasm dependencies for better performance. You can use `vite-plugin-wasm` to load them.
```ts
import wasm from "vite-plugin-wasm";
export default {
plugins: [wasm()],
ssr: {
external: ["tiktoken"],
},
};
```
## Playground
Check out our NextJS playground at https://llama-playground.vercel.app/. The source is available at https://github.com/run-llama/ts-playground
+43
View File
@@ -1,5 +1,48 @@
# docs
## 0.0.69
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
- @llamaindex/examples@0.0.8
## 0.0.68
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.0.67
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.0.66
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.0.65
### Patch Changes
- llamaindex@0.5.24
## 0.0.64
### Patch Changes
- llamaindex@0.5.23
## 0.0.63
### Patch Changes
@@ -50,10 +50,10 @@ We want to see what our agent is up to, so we're going to hook into some events
```javascript
Settings.callbackManager.on("llm-tool-call", (event) => {
console.log(event.detail.payload);
console.log(event.detail);
});
Settings.callbackManager.on("llm-tool-result", (event) => {
console.log(event.detail.payload);
console.log(event.detail);
});
```
+37 -3
View File
@@ -4,12 +4,19 @@ sidebar_position: 7
# Storage
Storage in LlamaIndex.TS works automatically once you've configured a `StorageContext` object. Just configure the `persistDir` and attach it to an index.
Storage in LlamaIndex.TS works automatically once you've configured a
`StorageContext` object.
Right now, only saving and loading from disk is supported, with future integrations planned!
## Local Storage
You can configure the `persistDir` and attach it to an index.
```typescript
import { Document, VectorStoreIndex, storageContextFromDefaults } from "./src";
import {
Document,
VectorStoreIndex,
storageContextFromDefaults,
} from "llamaindex";
const storageContext = await storageContextFromDefaults({
persistDir: "./storage",
@@ -21,6 +28,33 @@ const index = await VectorStoreIndex.fromDocuments([document], {
});
```
## PostgreSQL Storage
You can configure the `schemaName`, `tableName`, `namespace`, and
`connectionString`. If a `connectionString` is not
provided, it will use the environment variables `PGHOST`, `PGUSER`,
`PGPASSWORD`, `PGDATABASE` and `PGPORT`.
```typescript
import {
Document,
VectorStoreIndex,
PostgresDocumentStore,
PostgresIndexStore,
storageContextFromDefaults,
} from "llamaindex";
const storageContext = await storageContextFromDefaults({
docStore: new PostgresDocumentStore(),
indexStore: new PostgresIndexStore(),
});
const document = new Document({ text: "Test Text" });
const index = await VectorStoreIndex.fromDocuments([document], {
storageContext,
});
```
## API Reference
- [StorageContext](../api/interfaces/StorageContext.md)
+168
View File
@@ -0,0 +1,168 @@
import CodeBlock from "@theme/CodeBlock";
import CodeSource from "!raw-loader!../../../../examples/workflow/joke.ts";
# Workflows
A `Workflow` in LlamaIndexTS is an event-driven abstraction used to chain together several events. Workflows are made up of `steps`, with each step responsible for handling certain event types and emitting new events.
Workflows in LlamaIndexTS work by defining step functions that handle specific event types and emit new events.
When a step function is added to a workflow, you need to specify the input and optionally the output event types (used for validation). The specification of the input events ensures each step only runs when an accepted event is ready.
You can create a `Workflow` to do anything! Build an agent, a RAG flow, an extraction flow, or anything else you want.
## Getting Started
As an illustrative example, let's consider a naive workflow where a joke is generated and then critiqued.
<CodeBlock language="ts">{CodeSource}</CodeBlock>
There's a few moving pieces here, so let's go through this piece by piece.
### Defining Workflow Events
```typescript
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
```
Events are user-defined classes that extend `WorkflowEvent` and contain arbitrary data provided as template argument. In this case, our workflow relies on a single user-defined event, the `JokeEvent` with a `joke` attribute of type `string`.
### Setting up the Workflow Class
```typescript
const llm = new OpenAI();
...
const jokeFlow = new Workflow({ verbose: true });
```
Our workflow is implemented by initiating the `Workflow` class. For simplicity, we created a `OpenAI` llm instance.
### Workflow Entry Points
```typescript
const generateJoke = async (_context: Context, ev: StartEvent) => {
const prompt = `Write your best joke about ${ev.data.input}.`;
const response = await llm.complete({ prompt });
return new JokeEvent({ joke: response.text });
};
```
Here, we come to the entry-point of our workflow. While events are user-defined, there are two special-case events, the `StartEvent` and the `StopEvent`. Here, the `StartEvent` signifies where to send the initial workflow input.
The `StartEvent` is a bit of a special object since it can hold arbitrary attributes. Here, we accessed the topic with `ev.data.input`.
At this point, you may have noticed that we haven't explicitly told the workflow what events are handled by which steps.
To do so, we use the `addStep` method which adds a step to the workflow. The first argument is the event type that the step will handle, and the second argument is the previously defined step function:
```typescript
jokeFlow.addStep(StartEvent, generateJoke);
```
### Workflow Exit Points
```typescript
const critiqueJoke = async (_context: Context, ev: JokeEvent) => {
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
const response = await llm.complete({ prompt });
return new StopEvent({ result: response.text });
};
```
Here, we have our second, and last step, in the workflow. We know its the last step because the special `StopEvent` is returned. When the workflow encounters a returned `StopEvent`, it immediately stops the workflow and returns whatever the result was.
In this case, the result is a string, but it could be a map, array, or any other object.
Don't forget to add the step to the workflow:
```typescript
jokeFlow.addStep(JokeEvent, critiqueJoke);
```
### Running the Workflow
```typescript
const result = await jokeFlow.run("pirates");
console.log(result.data.result);
```
Lastly, we run the workflow. The `.run()` method is async, so we use await here to wait for the result.
### Validating Workflows
To tell the workflow what events are produced by each step, you can optionally provide a third argument to `addStep` to specify the output event type:
```typescript
jokeFlow.addStep(StartEvent, generateJoke, { outputs: JokeEvent });
jokeFlow.addStep(JokeEvent, critiqueJoke, { outputs: StopEvent });
```
To validate a workflow, you need to call the `validate` method:
```typescript
jokeFlow.validate();
```
To automatically validate a workflow when you run it, you can set the `validate` flag to `true` at initialization:
```typescript
const jokeFlow = new Workflow({ verbose: true, validate: true });
```
## Working with Global Context/State
Optionally, you can choose to use global context between steps. For example, maybe multiple steps access the original `query` input from the user. You can store this in global context so that every step has access.
```typescript
import { Context } from "@llamaindex/core/workflow";
const query = async (context: Context, ev: MyEvent) => {
// get the query from the context
const query = context.get("query");
// do something with context and event
const val = ...
const result = ...
// store in context
context.set("key", val);
return new StopEvent({ result });
};
```
## Waiting for Multiple Events
The context does more than just hold data, it also provides utilities to buffer and wait for multiple events.
For example, you might have a step that waits for a query and retrieved nodes before synthesizing a response:
```typescript
const synthesize = async (context: Context, ev: QueryEvent | RetrieveEvent) => {
const events = context.collectEvents(ev, [QueryEvent | RetrieveEvent]);
if (!events) {
return;
}
const prompt = events
.map((event) => {
if (event instanceof QueryEvent) {
return `Answer this query using the context provided: ${event.data.query}`;
} else if (event instanceof RetrieveEvent) {
return `Context: ${event.data.context}`;
}
return "";
})
.join("\n");
const response = await llm.complete({ prompt });
return new StopEvent({ result: response.text });
};
```
Using `ctx.collectEvents()` we can buffer and wait for ALL expected events to arrive. This function will only return events (in the requested order) once all events have arrived.
## Manually Triggering Events
Normally, events are triggered by returning another event during a step. However, events can also be manually dispatched using the `ctx.sendEvent(event)` method within a workflow.
## Examples
You can find many useful examples of using workflows in the [examples folder](https://github.com/run-llama/LlamaIndexTS/blob/main/examples/workflow).
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "docs",
"version": "0.0.63",
"version": "0.0.69",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
+9
View File
@@ -1,5 +1,14 @@
# examples
## 0.0.8
### Patch Changes
- 11feef8: Add workflows
- Updated dependencies [11feef8]
- @llamaindex/core@0.2.0
- llamaindex@0.6.0
## 0.0.7
### Patch Changes
+1 -1
View File
@@ -3,7 +3,7 @@ import { DeepInfraEmbedding } from "llamaindex";
async function main() {
// API token can be provided as an environment variable too
// using DEEPINFRA_API_TOKEN variable
const apiToken = "YOUR_API_TOKEN" ?? process.env.DEEPINFRA_API_TOKEN;
const apiToken = process.env.DEEPINFRA_API_TOKEN ?? "YOUR_API_TOKEN";
const model = "BAAI/bge-large-en-v1.5";
const embedModel = new DeepInfraEmbedding({
model,
+13
View File
@@ -0,0 +1,13 @@
import { OpenAI } from "llamaindex";
(async () => {
const llm = new OpenAI({ model: "o1-preview", temperature: 1 });
const prompt = `What are three compounds we should consider investigating to advance research
into new antibiotics? Why should we consider them?
`;
// complete api
const response = await llm.complete({ prompt });
console.log(response.text);
})();
+3 -3
View File
@@ -1,12 +1,12 @@
{
"name": "@llamaindex/examples",
"private": true,
"version": "0.0.7",
"version": "0.0.8",
"dependencies": {
"@aws-crypto/sha256-js": "^5.2.0",
"@azure/identity": "^4.4.1",
"@datastax/astra-db-ts": "^1.4.1",
"@llamaindex/core": "^0.1.0",
"@llamaindex/core": "^0.2.0",
"@notionhq/client": "^2.2.15",
"@pinecone-database/pinecone": "^3.0.2",
"@zilliz/milvus2-sdk-node": "^2.4.6",
@@ -14,7 +14,7 @@
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"js-tiktoken": "^1.0.14",
"llamaindex": "^0.5.0",
"llamaindex": "^0.6.0",
"mongodb": "^6.7.0",
"pathe": "^1.1.2"
},
+7
View File
@@ -0,0 +1,7 @@
# Workflow Examples
These examples demonstrate LlamaIndexTS's workflow system. Check out [its documentation](https://ts.llamaindex.ai/modules/workflows) for more information.
## Running the Examples
To run the examples, make sure to run them from the parent folder called `examples`). For example, to run the joke workflow, run `npx tsx workflow/joke.ts`.
+122
View File
@@ -0,0 +1,122 @@
import {
Context,
StartEvent,
StopEvent,
Workflow,
WorkflowEvent,
} from "@llamaindex/core/workflow";
import { OpenAI } from "llamaindex";
const MAX_REVIEWS = 3;
// Using the o1-preview model (see https://platform.openai.com/docs/guides/reasoning?reasoning-prompt-examples=coding-planning)
const llm = new OpenAI({ model: "o1-preview", temperature: 1 });
// example specification from https://platform.openai.com/docs/guides/reasoning?reasoning-prompt-examples=coding-planning
const specification = `Python app that takes user questions and looks them up in a
database where they are mapped to answers. If there is a close match, it retrieves
the matched answer. If there isn't, it asks the user to provide an answer and
stores the question/answer pair in the database.`;
// Create custom event types
export class MessageEvent extends WorkflowEvent<{ msg: string }> {}
export class CodeEvent extends WorkflowEvent<{ code: string }> {}
export class ReviewEvent extends WorkflowEvent<{
review: string;
code: string;
}> {}
// Helper function to truncate long strings
const truncate = (str: string) => {
const MAX_LENGTH = 60;
if (str.length <= MAX_LENGTH) return str;
return str.slice(0, MAX_LENGTH) + "...";
};
// the architect is responsible for writing the structure and the initial code based on the specification
const architect = async (context: Context, ev: StartEvent) => {
// get the specification from the start event and save it to context
context.set("specification", ev.data.input);
const spec = context.get("specification");
// write a message to send an update to the user
context.writeEventToStream(
new MessageEvent({
msg: `Writing app using this specification: ${truncate(spec)}`,
}),
);
const prompt = `Build an app for this specification: <spec>${spec}</spec>. Make a plan for the directory structure you'll need, then return each file in full. Don't supply any reasoning, just code.`;
const code = await llm.complete({ prompt });
return new CodeEvent({ code: code.text });
};
// the coder is responsible for updating the code based on the review
const coder = async (context: Context, ev: ReviewEvent) => {
// get the specification from the context
const spec = context.get("specification");
// get the latest review and code
const { review, code } = ev.data;
// write a message to send an update to the user
context.writeEventToStream(
new MessageEvent({
msg: `Update code based on review: ${truncate(review)}`,
}),
);
const prompt = `We need to improve code that should implement this specification: <spec>${spec}</spec>. Here is the current code: <code>${code}</code>. And here is a review of the code: <review>${review}</review>. Improve the code based on the review, keep the specification in mind, and return the full updated code. Don't supply any reasoning, just code.`;
const updatedCode = await llm.complete({ prompt });
return new CodeEvent({ code: updatedCode.text });
};
// the reviewer is responsible for reviewing the code and providing feedback
const reviewer = async (context: Context, ev: CodeEvent) => {
// get the specification from the context
const spec = context.get("specification");
// get latest code from the event
const { code } = ev.data;
// update and check the number of reviews
const numberReviews = context.get("numberReviews", 0) + 1;
context.set("numberReviews", numberReviews);
if (numberReviews > MAX_REVIEWS) {
// the we've done this too many times - return the code
context.writeEventToStream(
new MessageEvent({
msg: `Already reviewed ${numberReviews - 1} times, stopping!`,
}),
);
return new StopEvent({ result: code });
}
// write a message to send an update to the user
context.writeEventToStream(
new MessageEvent({ msg: `Review #${numberReviews}: ${truncate(code)}` }),
);
const prompt = `Review this code: <code>${code}</code>. Check if the code quality and whether it correctly implements this specification: <spec>${spec}</spec>. If you're satisfied, just return 'Looks great', nothing else. If not, return a review with a list of changes you'd like to see.`;
const review = (await llm.complete({ prompt })).text;
if (review.includes("Looks great")) {
// the reviewer is satisfied with the code, let's return the review
context.writeEventToStream(
new MessageEvent({
msg: `Reviewer says: ${review}`,
}),
);
return new StopEvent({ result: code });
}
return new ReviewEvent({ review, code });
};
const codeAgent = new Workflow({ validate: true });
codeAgent.addStep(StartEvent, architect, { outputs: CodeEvent });
codeAgent.addStep(ReviewEvent, coder, { outputs: CodeEvent });
codeAgent.addStep(CodeEvent, reviewer, { outputs: ReviewEvent });
// Usage
async function main() {
const run = codeAgent.run(specification);
for await (const event of codeAgent.streamEvents()) {
const msg = (event as MessageEvent).data.msg;
console.log(`${msg}\n`);
}
const result = await run;
console.log("Final code:\n", result.data.result);
}
main().catch(console.error);
+70
View File
@@ -0,0 +1,70 @@
import {
Context,
StartEvent,
StopEvent,
Workflow,
WorkflowEvent,
} from "@llamaindex/core/workflow";
import { OpenAI } from "llamaindex";
// Create LLM instance
const llm = new OpenAI();
// Create custom event types
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
export class CritiqueEvent extends WorkflowEvent<{ critique: string }> {}
export class AnalysisEvent extends WorkflowEvent<{ analysis: string }> {}
const generateJoke = async (_context: Context, ev: StartEvent) => {
const prompt = `Write your best joke about ${ev.data.input}.`;
const response = await llm.complete({ prompt });
return new JokeEvent({ joke: response.text });
};
const critiqueJoke = async (_context: Context, ev: JokeEvent) => {
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
const response = await llm.complete({ prompt });
return new CritiqueEvent({ critique: response.text });
};
const analyzeJoke = async (_context: Context, ev: JokeEvent) => {
const prompt = `Give a thorough analysis of the following joke: ${ev.data.joke}`;
const response = await llm.complete({ prompt });
return new AnalysisEvent({ analysis: response.text });
};
const reportJoke = async (
context: Context,
ev: AnalysisEvent | CritiqueEvent,
) => {
const events = context.collectEvents(ev, [AnalysisEvent, CritiqueEvent]);
if (!events) {
return;
}
const subPrompts = events.map((event) => {
if (event instanceof AnalysisEvent) {
return `Analysis: ${event.data.analysis}`;
} else if (event instanceof CritiqueEvent) {
return `Critique: ${event.data.critique}`;
}
return "";
});
const prompt = `Based on the following information about a joke:\n${subPrompts.join("\n")}\nProvide a comprehensive report on the joke's quality and impact.`;
const response = await llm.complete({ prompt });
return new StopEvent({ result: response.text });
};
const jokeFlow = new Workflow();
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, critiqueJoke);
jokeFlow.addStep(JokeEvent, analyzeJoke);
jokeFlow.addStep([AnalysisEvent, CritiqueEvent], reportJoke);
// Usage
async function main() {
const result = await jokeFlow.run("pirates");
console.log(result.data.result);
}
main().catch(console.error);
+38
View File
@@ -0,0 +1,38 @@
import {
Context,
StartEvent,
StopEvent,
Workflow,
WorkflowEvent,
} from "@llamaindex/core/workflow";
import { OpenAI } from "llamaindex";
// Create LLM instance
const llm = new OpenAI();
// Create a custom event type
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
const generateJoke = async (_context: Context, ev: StartEvent) => {
const prompt = `Write your best joke about ${ev.data.input}.`;
const response = await llm.complete({ prompt });
return new JokeEvent({ joke: response.text });
};
const critiqueJoke = async (_context: Context, ev: JokeEvent) => {
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
const response = await llm.complete({ prompt });
return new StopEvent({ result: response.text });
};
const jokeFlow = new Workflow({ verbose: true });
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, critiqueJoke);
// Usage
async function main() {
const result = await jokeFlow.run("pirates");
console.log(result.data.result);
}
main().catch(console.error);
+49
View File
@@ -0,0 +1,49 @@
import {
Context,
StartEvent,
StopEvent,
Workflow,
WorkflowEvent,
} from "@llamaindex/core/workflow";
import { OpenAI } from "llamaindex";
// Create LLM instance
const llm = new OpenAI();
// Create custom event types
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
export class MessageEvent extends WorkflowEvent<{ msg: string }> {}
const generateJoke = async (context: Context, ev: StartEvent) => {
context.writeEventToStream(
new MessageEvent({ msg: `Generating a joke about: ${ev.data.input}` }),
);
const prompt = `Write your best joke about ${ev.data.input}.`;
const response = await llm.complete({ prompt });
return new JokeEvent({ joke: response.text });
};
const critiqueJoke = async (context: Context, ev: JokeEvent) => {
context.writeEventToStream(
new MessageEvent({ msg: `Write a critique of this joke: ${ev.data.joke}` }),
);
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
const response = await llm.complete({ prompt });
return new StopEvent({ result: response.text });
};
const jokeFlow = new Workflow();
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, critiqueJoke);
// Usage
async function main() {
const run = jokeFlow.run("pirates");
for await (const event of jokeFlow.streamEvents()) {
console.log((event as MessageEvent).data.msg);
}
const result = await run;
console.log(result.data.result);
}
main().catch(console.error);
+37
View File
@@ -0,0 +1,37 @@
import {
Context,
StartEvent,
StopEvent,
Workflow,
} from "@llamaindex/core/workflow";
const longRunning = async (_context: Context, ev: StartEvent) => {
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds
return new StopEvent({ result: "We waited 2 seconds" });
};
async function timeout() {
const workflow = new Workflow({ verbose: true, timeout: 1 });
workflow.addStep(StartEvent, longRunning);
// This will timeout
try {
await workflow.run("Let's start");
} catch (error) {
console.error(error);
}
}
async function notimeout() {
// Increase timeout to 3 seconds - no timeout
const workflow = new Workflow({ verbose: true, timeout: 3 });
workflow.addStep(StartEvent, longRunning);
const result = await workflow.run("Let's start");
console.log(result.data.result);
}
async function main() {
await timeout();
await notimeout();
}
main().catch(console.error);
+53
View File
@@ -0,0 +1,53 @@
import {
Context,
StartEvent,
StopEvent,
Workflow,
WorkflowEvent,
} from "@llamaindex/core/workflow";
import { OpenAI } from "llamaindex";
// Create LLM instance
const llm = new OpenAI();
// Create a custom event type
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
const generateJoke = async (_context: Context, ev: StartEvent) => {
const prompt = `Write your best joke about ${ev.data.input}.`;
const response = await llm.complete({ prompt });
return new JokeEvent({ joke: response.text });
};
const critiqueJoke = async (_context: Context, ev: JokeEvent) => {
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
const response = await llm.complete({ prompt });
return new StopEvent({ result: response.text });
};
async function validateFails() {
try {
const jokeFlow = new Workflow({ verbose: true, validate: true });
jokeFlow.addStep(StartEvent, generateJoke, { outputs: StopEvent });
jokeFlow.addStep(JokeEvent, critiqueJoke, { outputs: StopEvent });
await jokeFlow.run("pirates");
} catch (e) {
console.error("Validation failed:", e);
}
}
async function validate() {
const jokeFlow = new Workflow({ verbose: true, validate: true });
jokeFlow.addStep(StartEvent, generateJoke, { outputs: JokeEvent });
jokeFlow.addStep(JokeEvent, critiqueJoke, { outputs: StopEvent });
const result = await jokeFlow.run("pirates");
console.log(result.data.result);
}
// Usage
async function main() {
await validateFails();
await validate();
}
main().catch(console.error);
+3
View File
@@ -38,6 +38,9 @@
"overrides": {
"trim": "1.0.1",
"protobufjs": "7.2.6"
},
"patchedDependencies": {
"python-format-js@1.4.3": "patches/python-format-js@1.4.3.patch"
}
},
"lint-staged": {
+7
View File
@@ -1,5 +1,12 @@
# @llamaindex/autotool
## 3.0.0
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 2.0.1
### Patch Changes
@@ -1,5 +1,53 @@
# @llamaindex/autotool-01-node-example
## 0.0.9
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
- @llamaindex/autotool@3.0.0
## 0.0.8
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
- @llamaindex/autotool@2.0.1
## 0.0.7
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
- @llamaindex/autotool@2.0.1
## 0.0.6
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
- @llamaindex/autotool@2.0.1
## 0.0.5
### Patch Changes
- llamaindex@0.5.24
- @llamaindex/autotool@2.0.1
## 0.0.4
### Patch Changes
- llamaindex@0.5.23
- @llamaindex/autotool@2.0.1
## 0.0.3
### Patch Changes
@@ -13,5 +13,5 @@
"scripts": {
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
},
"version": "0.0.3"
"version": "0.0.9"
}
@@ -16,7 +16,7 @@ const openai = new OpenAI();
stream: false,
});
const toolCalls = response.choices[0].message.tool_calls ?? [];
const toolCalls = response.choices[0]!.message.tool_calls ?? [];
for (const toolCall of toolCalls) {
toolCall.function.name;
}
@@ -1,5 +1,53 @@
# @llamaindex/autotool-02-next-example
## 0.1.53
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
- @llamaindex/autotool@3.0.0
## 0.1.52
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
- @llamaindex/autotool@2.0.1
## 0.1.51
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
- @llamaindex/autotool@2.0.1
## 0.1.50
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
- @llamaindex/autotool@2.0.1
## 0.1.49
### Patch Changes
- llamaindex@0.5.24
- @llamaindex/autotool@2.0.1
## 0.1.48
### Patch Changes
- llamaindex@0.5.23
- @llamaindex/autotool@2.0.1
## 0.1.47
### Patch Changes
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/autotool-02-next-example",
"private": true,
"version": "0.1.47",
"version": "0.1.53",
"scripts": {
"dev": "next dev",
"build": "next build",
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/autotool",
"type": "module",
"version": "2.0.1",
"version": "3.0.0",
"description": "auto transpile your JS function to LLM Agent compatible",
"files": [
"dist",
@@ -51,7 +51,7 @@
"unplugin": "^1.12.2"
},
"peerDependencies": {
"llamaindex": "^0.5.22",
"llamaindex": "^0.6.0",
"openai": "^4",
"typescript": "^4"
},
+10 -5
View File
@@ -16,11 +16,16 @@ const openaiToolsAtom = atom<ChatCompletionTool[]>((get) => {
const metadata = get(toolMetadataAtom);
return metadata.map(([metadata]) => ({
type: "function",
function: {
parameters: metadata.parameters,
name: metadata.name,
description: metadata.description,
},
function: metadata.parameters
? {
parameters: metadata.parameters,
name: metadata.name,
description: metadata.description,
}
: {
name: metadata.name,
description: metadata.description,
},
}));
});
+1 -1
View File
@@ -17,7 +17,7 @@ export type Info = {
* @internal
*/
export type InfoString = {
originalFunction?: string;
originalFunction: string | undefined;
parameterMapping: Record<string, number>;
};
+12
View File
@@ -1,5 +1,17 @@
# @llamaindex/cloud
## 0.2.4
### Patch Changes
- 4810364: fix: bump version
## 0.2.3
### Patch Changes
- 0bf8d80: fix: bump version
## 0.2.2
### Patch Changes
+1
View File
@@ -4,6 +4,7 @@ export default defineConfig({
// you can download this file to get the latest version of the OpenAPI document
// @link https://api.cloud.llamaindex.ai/api/openapi.json
input: "./openapi.json",
client: "@hey-api/client-fetch",
output: {
path: "./src/client",
format: "prettier",
+4 -3
View File
@@ -1,10 +1,10 @@
{
"name": "@llamaindex/cloud",
"version": "0.2.2",
"version": "0.2.4",
"type": "module",
"license": "MIT",
"scripts": {
"generate": "pnpm dlx @hey-api/openapi-ts@0.49.0",
"generate": "pnpx @hey-api/openapi-ts@0.53.0",
"build": "pnpm run generate && bunchee"
},
"files": [
@@ -34,7 +34,8 @@
"directory": "packages/cloud"
},
"devDependencies": {
"@hey-api/openapi-ts": "^0.52.11",
"@hey-api/client-fetch": "^0.2.4",
"@hey-api/openapi-ts": "^0.53.0",
"bunchee": "5.3.2"
}
}
+14
View File
@@ -1,5 +1,19 @@
# @llamaindex/community
## 0.0.34
### Patch Changes
- Updated dependencies [11feef8]
- @llamaindex/core@0.2.0
## 0.0.33
### Patch Changes
- Updated dependencies [711c814]
- @llamaindex/core@0.1.12
## 0.0.32
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/community",
"description": "Community package for LlamaIndexTS",
"version": "0.0.32",
"version": "0.0.34",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
@@ -28,9 +28,9 @@ export const mergeNeighboringSameRoleMessages = (
): AnthropicMessage[] => {
return messages.reduce(
(result: AnthropicMessage[], current: AnthropicMessage, index: number) => {
if (index > 0 && messages[index - 1].role === current.role) {
result[result.length - 1].content = [
...result[result.length - 1].content,
if (index > 0 && messages[index - 1]!.role === current.role) {
result[result.length - 1]!.content = [
...result[result.length - 1]!.content,
...current.content,
];
} else {
@@ -128,7 +128,7 @@ export const mapChatMessagesToAnthropicMessages = <
);
})
.filter((message: AnthropicMessage) => {
const content = message.content[0];
const content = message.content[0]!;
if (content.type === "text" && !content.text) return false;
if (content.type === "image" && !content.source.data) return false;
if (content.type === "image" && message.role === "assistant")
@@ -151,12 +151,12 @@ export const extractDataUrlComponents = (
} => {
const parts = dataUrl.split(";base64,");
if (parts.length !== 2 || !parts[0].startsWith("data:")) {
if (parts.length !== 2 || !parts[0]!.startsWith("data:")) {
throw new Error("Invalid data URL");
}
const mimeType = parts[0].slice(5);
const base64 = parts[1];
const mimeType = parts[0]!.slice(5);
const base64 = parts[1]!;
return {
mimeType,
+4 -1
View File
@@ -153,12 +153,15 @@ export const TOOL_CALL_MODELS = [
const getProvider = (model: string): Provider => {
const providerName = model.split(".")[0];
if (!providerName) {
throw new Error(`Model ${model} is not supported`);
}
if (!(providerName in PROVIDERS)) {
throw new Error(
`Provider ${providerName} for model ${model} is not supported`,
);
}
return PROVIDERS[providerName];
return PROVIDERS[providerName]!;
};
export type BedrockModelParams = {
@@ -34,7 +34,7 @@ export class MetaProvider extends Provider<MetaStreamEvent> {
const result = this.getResultFromResponse(response);
if (!result.generation.trim().startsWith(TOKENS.TOOL_CALL)) return [];
const tool = JSON.parse(
result.generation.trim().split(TOKENS.TOOL_CALL)[1],
result.generation.trim().split(TOKENS.TOOL_CALL)[1]!,
);
return [
{
+12
View File
@@ -1,5 +1,17 @@
# @llamaindex/core
## 0.2.0
### Minor Changes
- 11feef8: Add workflows
## 0.1.12
### Patch Changes
- 711c814: fix: patch `python-format-js`
## 0.1.11
### Patch Changes
+29 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/core",
"type": "module",
"version": "0.1.11",
"version": "0.2.0",
"description": "LlamaIndex Core Module",
"exports": {
"./node-parser": {
@@ -129,6 +129,34 @@
"types": "./dist/prompts/index.d.ts",
"default": "./dist/prompts/index.js"
}
},
"./indices": {
"require": {
"types": "./dist/indices/index.d.cts",
"default": "./dist/indices/index.cjs"
},
"import": {
"types": "./dist/indices/index.d.ts",
"default": "./dist/indices/index.js"
},
"default": {
"types": "./dist/indices/index.d.ts",
"default": "./dist/indices/index.js"
}
},
"./workflow": {
"require": {
"types": "./dist/workflow/index.d.cts",
"default": "./dist/workflow/index.cjs"
},
"import": {
"types": "./dist/workflow/index.d.ts",
"default": "./dist/workflow/index.js"
},
"default": {
"types": "./dist/workflow/index.d.ts",
"default": "./dist/workflow/index.js"
}
}
},
"files": [
+2 -2
View File
@@ -34,7 +34,7 @@ export abstract class BaseEmbedding extends TransformComponent {
const embeddings = await this.getTextEmbeddingsBatch(texts, options);
for (let i = 0; i < nodes.length; i++) {
nodes[i].embedding = embeddings[i];
nodes[i]!.embedding = embeddings[i];
}
return nodes;
@@ -120,7 +120,7 @@ export async function batchEmbeddings<T>(
const curBatch: T[] = [];
for (let i = 0; i < queue.length; i++) {
curBatch.push(queue[i]);
curBatch.push(queue[i]!);
if (i == queue.length - 1 || curBatch.length == chunkSize) {
const embeddings = await embedFunc(curBatch);
+3 -3
View File
@@ -35,20 +35,20 @@ export function similarity(
function norm(x: number[]): number {
let result = 0;
for (let i = 0; i < x.length; i++) {
result += x[i] * x[i];
result += x[i]! * x[i]!;
}
return Math.sqrt(result);
}
switch (mode) {
case SimilarityType.EUCLIDEAN: {
const difference = embedding1.map((x, i) => x - embedding2[i]);
const difference = embedding1.map((x, i) => x - embedding2[i]!);
return -norm(difference);
}
case SimilarityType.DOT_PRODUCT: {
let result = 0;
for (let i = 0; i < embedding1.length; i++) {
result += embedding1[i] * embedding2[i];
result += embedding1[i]! * embedding2[i]!;
}
return result;
}
+11
View File
@@ -1,4 +1,5 @@
import type { Tokenizer } from "@llamaindex/env";
import type { LLM } from "../llms";
import {
type CallbackManager,
getCallbackManager,
@@ -10,6 +11,7 @@ import {
setChunkSize,
withChunkSize,
} from "./settings/chunk-size";
import { getLLM, setLLM, withLLM } from "./settings/llm";
import {
getTokenizer,
setTokenizer,
@@ -17,6 +19,15 @@ import {
} from "./settings/tokenizer";
export const Settings = {
get llm() {
return getLLM();
},
set llm(llm) {
setLLM(llm);
},
withLLM<Result>(llm: LLM, fn: () => Result): Result {
return withLLM(llm, fn);
},
get tokenizer() {
return getTokenizer();
},
+23
View File
@@ -0,0 +1,23 @@
import { AsyncLocalStorage } from "@llamaindex/env";
import type { LLM } from "../../llms";
const llmAsyncLocalStorage = new AsyncLocalStorage<LLM>();
let globalLLM: LLM | undefined;
export function getLLM(): LLM {
const currentLLM = globalLLM ?? llmAsyncLocalStorage.getStore();
if (!currentLLM) {
throw new Error(
"Cannot find LLM, please set `Settings.llm = ...` on the top of your code",
);
}
return currentLLM;
}
export function setLLM(llm: LLM): void {
globalLLM = llm;
}
export function withLLM<Result>(llm: LLM, fn: () => Result): Result {
return llmAsyncLocalStorage.run(llm, fn);
}
+5
View File
@@ -0,0 +1,5 @@
export {
PromptHelper,
getBiggestPrompt,
type PromptHelperOptions,
} from "./prompt-helper";
@@ -1,17 +1,17 @@
import { type Tokenizer, tokenizers } from "@llamaindex/env";
import {
DEFAULT_CHUNK_OVERLAP_RATIO,
DEFAULT_CONTEXT_WINDOW,
DEFAULT_NUM_OUTPUTS,
DEFAULT_PADDING,
} from "@llamaindex/core/global";
import { SentenceSplitter } from "@llamaindex/core/node-parser";
import type { PromptTemplate } from "@llamaindex/core/prompts";
import { type Tokenizer, tokenizers } from "@llamaindex/env";
} from "../global";
import { SentenceSplitter } from "../node-parser";
import type { PromptTemplate } from "../prompts";
/**
* Get the empty prompt text given a prompt.
*/
export function getEmptyPromptTxt(prompt: PromptTemplate) {
function getEmptyPromptTxt(prompt: PromptTemplate) {
return prompt.format({
...Object.fromEntries(
[...prompt.templateVars.keys()].map((key) => [key, ""]),
@@ -23,14 +23,23 @@ export function getEmptyPromptTxt(prompt: PromptTemplate) {
* Get biggest empty prompt size from a list of prompts.
* Used to calculate the maximum size of inputs to the LLM.
*/
export function getBiggestPrompt(prompts: PromptTemplate[]) {
export function getBiggestPrompt(prompts: PromptTemplate[]): PromptTemplate {
const emptyPromptTexts = prompts.map(getEmptyPromptTxt);
const emptyPromptLengths = emptyPromptTexts.map((text) => text.length);
const maxEmptyPromptLength = Math.max(...emptyPromptLengths);
const maxEmptyPromptIndex = emptyPromptLengths.indexOf(maxEmptyPromptLength);
return prompts[maxEmptyPromptIndex];
return prompts[maxEmptyPromptIndex]!;
}
export type PromptHelperOptions = {
contextWindow?: number;
numOutput?: number;
chunkOverlapRatio?: number;
chunkSizeLimit?: number;
tokenizer?: Tokenizer;
separator?: string;
};
/**
* A collection of helper functions for working with prompts.
*/
@@ -38,19 +47,19 @@ export class PromptHelper {
contextWindow = DEFAULT_CONTEXT_WINDOW;
numOutput = DEFAULT_NUM_OUTPUTS;
chunkOverlapRatio = DEFAULT_CHUNK_OVERLAP_RATIO;
chunkSizeLimit?: number;
chunkSizeLimit: number | undefined;
tokenizer: Tokenizer;
separator = " ";
// eslint-disable-next-line max-params
constructor(
contextWindow = DEFAULT_CONTEXT_WINDOW,
numOutput = DEFAULT_NUM_OUTPUTS,
chunkOverlapRatio = DEFAULT_CHUNK_OVERLAP_RATIO,
chunkSizeLimit?: number,
tokenizer?: Tokenizer,
separator = " ",
) {
constructor(options: PromptHelperOptions = {}) {
const {
contextWindow = DEFAULT_CONTEXT_WINDOW,
numOutput = DEFAULT_NUM_OUTPUTS,
chunkOverlapRatio = DEFAULT_CHUNK_OVERLAP_RATIO,
chunkSizeLimit,
tokenizer,
separator = " ",
} = options;
this.contextWindow = contextWindow;
this.numOutput = numOutput;
this.chunkOverlapRatio = chunkOverlapRatio;
@@ -79,7 +88,7 @@ export class PromptHelper {
prompt: PromptTemplate,
numChunks = 1,
padding = 5,
) {
): number {
const availableContextSize = this.getAvailableContextSize(prompt);
const result = Math.floor(availableContextSize / numChunks) - padding;
@@ -104,7 +113,12 @@ export class PromptHelper {
throw new Error("Got 0 as available chunk size");
}
const chunkOverlap = this.chunkOverlapRatio * chunkSize;
return new SentenceSplitter({ chunkSize, chunkOverlap });
return new SentenceSplitter({
chunkSize,
chunkOverlap,
separator: this.separator,
tokenizer: this.tokenizer,
});
}
/**
+2 -2
View File
@@ -103,7 +103,7 @@ export type LLMMetadata = {
model: string;
temperature: number;
topP: number;
maxTokens?: number;
maxTokens?: number | undefined;
contextWindow: number;
tokenizer: Tokenizers | undefined;
};
@@ -141,7 +141,7 @@ export interface LLMCompletionParamsStreaming extends LLMCompletionParamsBase {
export interface LLMCompletionParamsNonStreaming
extends LLMCompletionParamsBase {
stream?: false | null;
stream?: false | null | undefined;
}
export type MessageContentTextDetail = {
+1 -1
View File
@@ -122,7 +122,7 @@ export abstract class MetadataAwareTextSplitter extends TextSplitter {
throw new TypeError("`texts` and `metadata` must have the same length");
}
return texts.flatMap((text, i) =>
this.splitTextMetadataAware(text, metadata[i]),
this.splitTextMetadataAware(text, metadata[i]!),
);
}
+5 -5
View File
@@ -35,8 +35,8 @@ export class MarkdownNodeParser extends NodeParser {
}
metadata = this.updateMetadata(
metadata,
headerMatch[2],
headerMatch[1].trim().length,
headerMatch[2]!,
headerMatch[1]!.trim().length,
);
currentSection = `${headerMatch[2]}\n`;
} else {
@@ -63,7 +63,7 @@ export class MarkdownNodeParser extends NodeParser {
for (let i = 1; i < newHeaderLevel; i++) {
const key = `Header_${i}`;
if (key in headersMetadata) {
updatedHeaders[key] = headersMetadata[key];
updatedHeaders[key] = headersMetadata[key]!;
}
}
@@ -76,10 +76,10 @@ export class MarkdownNodeParser extends NodeParser {
node: TextNode,
metadata: Metadata,
): TextNode {
const newNode = buildNodeFromSplits([textSplit], node, undefined)[0];
const newNode = buildNodeFromSplits([textSplit], node, undefined)[0]!;
if (this.includeMetadata) {
newNode.metadata = { ...newNode.metadata, ...metadata };
newNode.metadata = { ...newNode!.metadata, ...metadata };
}
return newNode;
@@ -168,9 +168,9 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
let lastIndex = lastChunk.length - 1;
while (
lastIndex >= 0 &&
currentChunkLength + lastChunk[lastIndex][1] <= this.chunkOverlap
currentChunkLength + lastChunk[lastIndex]![1] <= this.chunkOverlap
) {
const [text, length] = lastChunk[lastIndex];
const [text, length] = lastChunk[lastIndex]!;
currentChunkLength += length;
currentChunk.unshift([text, length]);
lastIndex -= 1;
@@ -178,7 +178,7 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
};
while (splits.length > 0) {
const curSplit = splits[0];
const curSplit = splits[0]!;
if (curSplit.tokenSize > chunkSize) {
throw new Error("Single token exceeded chunk size");
}
+1 -1
View File
@@ -5,7 +5,7 @@ export type TextSplitterFn = (text: string) => string[];
const truncateText = (text: string, textSplitter: TextSplitter): string => {
const chunks = textSplitter.splitText(text);
return chunks[0];
return chunks[0] ?? text;
};
const splitTextKeepSeparator = (text: string, separator: string): string[] => {
+2 -2
View File
@@ -18,7 +18,7 @@ export type BasePromptTemplateOptions<
// loose type for better type inference
| readonly string[];
options?: Partial<Record<TemplatesVar[number] | (string & {}), string>>;
outputParser?: BaseOutputParser;
outputParser?: BaseOutputParser | undefined;
templateVarMappings?: Partial<
Record<Vars[number] | (string & {}), TemplatesVar[number] | (string & {})>
>;
@@ -34,7 +34,7 @@ export abstract class BasePromptTemplate<
metadata: Metadata = {};
templateVars: Set<string> = new Set();
options: Partial<Record<TemplatesVar[number] | (string & {}), string>> = {};
outputParser?: BaseOutputParser;
outputParser: BaseOutputParser | undefined;
templateVarMappings: Partial<
Record<Vars[number] | (string & {}), TemplatesVar[number] | (string & {})>
> = {};
+8 -8
View File
@@ -45,21 +45,21 @@ export abstract class PromptMixin {
for (const key in prompts) {
if (key.includes(":")) {
const [module_name, sub_key] = key.split(":");
const [moduleName, subKey] = key.split(":") as [string, string];
if (!subPrompt[module_name]) {
subPrompt[module_name] = {};
if (!subPrompt[moduleName]) {
subPrompt[moduleName] = {};
}
subPrompt[module_name][sub_key] = prompts[key];
subPrompt[moduleName][subKey] = prompts[key]!;
}
}
for (const [module_name, subPromptDict] of Object.entries(subPrompt)) {
if (!promptModules[module_name]) {
throw new Error(`Module ${module_name} not found.`);
for (const [moduleName, subPromptDict] of Object.entries(subPrompt)) {
if (!promptModules[moduleName]) {
throw new Error(`Module ${moduleName} not found.`);
}
const moduleToUpdate = promptModules[module_name];
const moduleToUpdate = promptModules[moduleName];
moduleToUpdate.updatePrompts(subPromptDict);
}
+16 -14
View File
@@ -38,13 +38,15 @@ export type RelatedNodeType<T extends Metadata = Metadata> =
| RelatedNodeInfo<T>[];
export type BaseNodeParams<T extends Metadata = Metadata> = {
id_?: string;
metadata?: T;
excludedEmbedMetadataKeys?: string[];
excludedLlmMetadataKeys?: string[];
relationships?: Partial<Record<NodeRelationship, RelatedNodeType<T>>>;
hash?: string;
embedding?: number[];
id_?: string | undefined;
metadata?: T | undefined;
excludedEmbedMetadataKeys?: string[] | undefined;
excludedLlmMetadataKeys?: string[] | undefined;
relationships?:
| Partial<Record<NodeRelationship, RelatedNodeType<T>>>
| undefined;
hash?: string | undefined;
embedding?: number[] | undefined;
};
/**
@@ -58,7 +60,7 @@ export abstract class BaseNode<T extends Metadata = Metadata> {
* Set to a UUID by default.
*/
id_: string;
embedding?: number[];
embedding: number[] | undefined;
// Metadata fields
metadata: T;
@@ -198,11 +200,11 @@ export abstract class BaseNode<T extends Metadata = Metadata> {
export type TextNodeParams<T extends Metadata = Metadata> =
BaseNodeParams<T> & {
text?: string;
textTemplate?: string;
startCharIdx?: number;
endCharIdx?: number;
metadataSeparator?: string;
text?: string | undefined;
textTemplate?: string | undefined;
startCharIdx?: number | undefined;
endCharIdx?: number | undefined;
metadataSeparator?: string | undefined;
};
/**
@@ -418,7 +420,7 @@ export class ImageDocument<T extends Metadata = Metadata> extends ImageNode<T> {
*/
export interface NodeWithScore<T extends Metadata = Metadata> {
node: BaseNode<T>;
score?: number;
score?: number | undefined;
}
export enum ModalityType {
@@ -3,7 +3,7 @@ import { extractText } from "../../utils";
import type { Metadata, NodeWithScore } from "../node";
export class EngineResponse implements ChatResponse, ChatResponseChunk {
sourceNodes?: NodeWithScore[];
sourceNodes: NodeWithScore[] | undefined;
metadata: Metadata = {};
+3 -3
View File
@@ -74,12 +74,12 @@ export const extractDataUrlComponents = (
} => {
const parts = dataUrl.split(";base64,");
if (parts.length !== 2 || !parts[0].startsWith("data:")) {
if (parts.length !== 2 || !parts[0]!.startsWith("data:")) {
throw new Error("Invalid data URL");
}
const mimeType = parts[0].slice(5);
const base64 = parts[1];
const mimeType = parts[0]!.slice(5);
const base64 = parts[1]!;
return {
mimeType,
+111
View File
@@ -0,0 +1,111 @@
import { type EventTypes, type WorkflowEvent } from "./events";
import { type StepFunction, type Workflow } from "./workflow";
export class Context {
#workflow: Workflow;
#queues: Map<StepFunction, WorkflowEvent[]> = new Map();
#eventBuffer: Map<EventTypes, WorkflowEvent[]> = new Map();
#globals: Map<string, any> = new Map();
#streamingQueue: WorkflowEvent[] = [];
running: boolean = true;
#verbose: boolean = false;
constructor(params: { workflow: Workflow; verbose?: boolean }) {
this.#workflow = params.workflow;
this.#verbose = params.verbose ?? false;
}
set(key: string, value: any): void {
this.#globals.set(key, value);
}
get(key: string, defaultValue?: any): any {
if (this.#globals.has(key)) {
return this.#globals.get(key);
} else if (defaultValue !== undefined) {
return defaultValue;
}
throw new Error(`Key '${key}' not found in Context`);
}
collectEvents(
event: WorkflowEvent,
expected: EventTypes[],
): WorkflowEvent[] | null {
const eventType = event.constructor as EventTypes;
if (!this.#eventBuffer.has(eventType)) {
this.#eventBuffer.set(eventType, []);
}
this.#eventBuffer.get(eventType)!.push(event);
const retval: WorkflowEvent[] = [];
for (const expectedType of expected) {
const events = this.#eventBuffer.get(expectedType);
if (events && events.length > 0) {
retval.push(events.shift()!);
}
}
if (retval.length === expected.length) {
return retval;
}
// Put back the events if unable to collect all
for (const ev of retval) {
const eventType = ev.constructor as EventTypes;
if (!this.#eventBuffer.has(eventType)) {
this.#eventBuffer.set(eventType, []);
}
this.#eventBuffer.get(eventType)!.unshift(ev);
}
return null;
}
sendEvent(message: WorkflowEvent, step?: StepFunction): void {
const stepName = step?.name ? `step ${step.name}` : "all steps";
if (this.#verbose) {
console.log(`Sending event ${message} to ${stepName}`);
}
if (step === undefined) {
for (const queue of this.#queues.values()) {
queue.push(message);
}
} else {
if (!this.#workflow.hasStep(step)) {
throw new Error(`Step ${step} does not exist`);
}
if (!this.#queues.has(step)) {
this.#queues.set(step, []);
}
this.#queues.get(step)!.push(message);
}
}
getNextEvent(step: StepFunction): WorkflowEvent | undefined {
const queue = this.#queues.get(step);
if (queue && queue.length > 0) {
return queue.shift();
}
return undefined;
}
writeEventToStream(event: WorkflowEvent): void {
this.#streamingQueue.push(event);
}
async *streamEvents(): AsyncGenerator<WorkflowEvent, void, undefined> {
while (true) {
const event = this.#streamingQueue.shift();
if (event) {
yield event;
} else {
if (!this.running) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
export class WorkflowEvent<T extends Record<string, any> = any> {
data: T;
constructor(data: T) {
this.data = data;
}
toString() {
return `${this.constructor.name}(${JSON.stringify(this.data)})`;
}
}
export type EventTypes<T extends Record<string, any> = any> = new (
data: T,
) => WorkflowEvent<T>;
export class StartEvent extends WorkflowEvent<{ input: string }> {}
export class StopEvent extends WorkflowEvent<{ result: string }> {}
+3
View File
@@ -0,0 +1,3 @@
export * from "./context";
export * from "./events";
export * from "./workflow";
+221
View File
@@ -0,0 +1,221 @@
import { Context } from "./context";
import {
type EventTypes,
StartEvent,
StopEvent,
WorkflowEvent,
} from "./events";
export type StepFunction<T extends WorkflowEvent = WorkflowEvent> = (
context: Context,
ev: T,
) => Promise<WorkflowEvent | void>;
type EventTypeParam = EventTypes | EventTypes[];
export class Workflow {
#steps: Map<
StepFunction<any>,
{ inputs: EventTypes[]; outputs: EventTypes[] | undefined }
> = new Map();
#contexts: Set<Context> = new Set();
#verbose: boolean = false;
#timeout: number | null = null;
#validate: boolean = false;
constructor(
params: {
verbose?: boolean;
timeout?: number;
validate?: boolean;
} = {},
) {
this.#verbose = params.verbose ?? false;
this.#timeout = params.timeout ?? null;
this.#validate = params.validate ?? false;
}
addStep<T extends WorkflowEvent>(
eventType: EventTypeParam,
method: StepFunction<T>,
params: { outputs?: EventTypeParam } = {},
) {
const inputs = Array.isArray(eventType) ? eventType : [eventType];
const outputs = params.outputs
? Array.isArray(params.outputs)
? params.outputs
: [params.outputs]
: undefined;
this.#steps.set(method, { inputs, outputs });
}
hasStep(step: StepFunction<any>): boolean {
return this.#steps.has(step);
}
#acceptsEvent(step: StepFunction<any>, event: WorkflowEvent): boolean {
const eventType = event.constructor as EventTypes;
const stepInfo = this.#steps.get(step);
if (!stepInfo) {
throw new Error(`No method found for step: ${step.name}`);
}
return stepInfo.inputs.includes(eventType);
}
async *streamEvents(): AsyncGenerator<WorkflowEvent, void, unknown> {
if (this.#contexts.size > 1) {
throw new Error(
"This workflow has multiple concurrent runs in progress and cannot stream events. " +
"To be able to stream events, make sure you call `run()` on this workflow only once.",
);
}
const context = this.#contexts.values().next().value;
if (!context) {
throw new Error("No active context found for streaming events.");
}
yield* context.streamEvents();
}
validate(): void {
if (this.#verbose) {
console.log("Validating workflow...");
}
// Check if all steps have outputs defined
// precondition for the validation to work
const allStepsHaveOutputs = Array.from(this.#steps.values()).every(
(stepInfo) => stepInfo.outputs !== undefined,
);
if (!allStepsHaveOutputs) {
throw new Error(
"Not all steps have outputs defined. Can't validate. Add the 'outputs' parameter to each 'addStep' method call to do validation",
);
}
// input events that are consumed by any step of the workflow
const consumedEvents: Set<EventTypes> = new Set();
// output events that are produced by any step of the workflow
const producedEvents: Set<EventTypes> = new Set([StartEvent]);
for (const [, stepInfo] of this.#steps) {
stepInfo.inputs.forEach((eventType) => consumedEvents.add(eventType));
stepInfo.outputs?.forEach((eventType) => producedEvents.add(eventType));
}
// Check if all consumed events are produced
const unconsumedEvents = Array.from(consumedEvents).filter(
(event) => !producedEvents.has(event),
);
if (unconsumedEvents.length > 0) {
const names = unconsumedEvents.map((event) => event.name).join(", ");
throw new Error(
`The following events are consumed but never produced: ${names}`,
);
}
// Check if there are any unused produced events (except StopEvent)
const unusedEvents = Array.from(producedEvents).filter(
(event) => !consumedEvents.has(event) && event !== StopEvent,
);
if (unusedEvents.length > 0) {
const names = unusedEvents.map((event) => event.name).join(", ");
throw new Error(
`The following events are produced but never consumed: ${names}`,
);
}
if (this.#verbose) {
console.log("Workflow validation passed");
}
}
async run(event: StartEvent | string): Promise<StopEvent> {
// Validate the workflow before running if #validate is true
if (this.#validate) {
this.validate();
}
const context = new Context({ workflow: this, verbose: this.#verbose });
this.#contexts.add(context);
const stopWorkflow = () => {
if (context.running) {
context.running = false;
this.#contexts.delete(context);
}
};
const startEvent: WorkflowEvent =
typeof event === "string" ? new StartEvent({ input: event }) : event;
if (this.#verbose) {
console.log(`Starting workflow with event ${startEvent}`);
}
const workflowPromise = new Promise<StopEvent>((resolve, reject) => {
for (const [step] of this.#steps) {
// send initial event to step
context.sendEvent(startEvent, step);
if (this.#verbose) {
console.log(`Starting tasks for step ${step.name}`);
}
queueMicrotask(async () => {
try {
while (context.running) {
const currentEvent = context.getNextEvent(step);
if (!currentEvent) {
// if there's no event, wait and try again
await new Promise((resolve) => setTimeout(resolve, 0));
continue;
}
if (!this.#acceptsEvent(step, currentEvent)) {
// step does not accept current event, skip it
continue;
}
if (this.#verbose) {
console.log(`Step ${step.name} received event ${currentEvent}`);
}
const result = await step.call(this, context, currentEvent);
if (!context.running) {
// workflow was stopped during the execution (e.g. there was a timeout)
return;
}
if (result instanceof StopEvent) {
if (this.#verbose) {
console.log(`Stopping workflow with event ${result}`);
}
resolve(result);
return;
}
if (result instanceof WorkflowEvent) {
context.sendEvent(result);
}
}
} catch (error) {
if (this.#verbose) {
console.error(`Error in calling step ${step.name}:`, error);
}
reject(error as Error);
} finally {
stopWorkflow();
}
});
}
});
if (this.#timeout !== null) {
const timeout = this.#timeout;
const timeoutPromise = new Promise<never>((_, reject) =>
setTimeout(() => {
stopWorkflow();
reject(new Error(`Operation timed out after ${timeout} seconds`));
}, timeout * 1000),
);
return Promise.race([workflowPromise, timeoutPromise]);
}
return workflowPromise;
}
}
@@ -19,12 +19,12 @@ Header 2 content
]);
expect(splits.length).toBe(2);
expect(splits[0].metadata).toEqual({ Header_1: "Main Header" });
expect(splits[1].metadata).toEqual({ Header_1: "Header 2" });
expect(splits[0].getContent(MetadataMode.NONE)).toStrictEqual(
expect(splits[0]!.metadata).toEqual({ Header_1: "Main Header" });
expect(splits[1]!.metadata).toEqual({ Header_1: "Header 2" });
expect(splits[0]!.getContent(MetadataMode.NONE)).toStrictEqual(
"Main Header\n\nHeader 1 content",
);
expect(splits[1].getContent(MetadataMode.NONE)).toStrictEqual(
expect(splits[1]!.getContent(MetadataMode.NONE)).toStrictEqual(
"Header 2\nHeader 2 content",
);
});
@@ -89,16 +89,16 @@ Content
}),
]);
expect(splits.length).toBe(4);
expect(splits[0].metadata).toEqual({ Header_1: "Main Header" });
expect(splits[1].metadata).toEqual({
expect(splits[0]!.metadata).toEqual({ Header_1: "Main Header" });
expect(splits[1]!.metadata).toEqual({
Header_1: "Main Header",
Header_2: "Sub-header",
});
expect(splits[2].metadata).toEqual({
expect(splits[2]!.metadata).toEqual({
Header_1: "Main Header",
Header_2: "Sub-header",
Header_3: "Sub-sub header",
});
expect(splits[3].metadata).toEqual({ Header_1: "New title" });
expect(splits[3]!.metadata).toEqual({ Header_1: "New title" });
});
});
@@ -22,7 +22,7 @@ describe("SentenceSplitter", () => {
});
const result = sentenceSplitter.getNodesFromDocuments([doc]);
expect(result.length).toEqual(1);
const node = result[0];
const node = result[0]!;
// check not the same object
expect(node.metadata).not.toBe(doc.metadata);
expect(node.excludedLlmMetadataKeys).not.toBe(doc.excludedLlmMetadataKeys);
+143
View File
@@ -0,0 +1,143 @@
import { beforeEach, describe, expect, test, vi, type Mocked } from "vitest";
import type { Context } from "../src/workflow/context.js";
import {
StartEvent,
StopEvent,
WorkflowEvent,
} from "../src/workflow/events.js";
import { Workflow } from "../src/workflow/workflow.js";
// mock OpenAI class for testing
class OpenAI {
complete = vi.fn();
}
class JokeEvent extends WorkflowEvent<{ joke: string }> {}
class AnalysisEvent extends WorkflowEvent<{ analysis: string }> {}
describe("Workflow", () => {
let mockLLM: Mocked<OpenAI>;
let generateJoke: Mocked<any>;
let critiqueJoke: Mocked<any>;
let analyzeJoke: Mocked<any>;
beforeEach(() => {
mockLLM = new OpenAI() as Mocked<OpenAI>;
mockLLM.complete
.mockResolvedValueOnce({
text: "Why do pirates make great singers? They can hit the high Cs!",
})
.mockResolvedValueOnce({
text: "This joke is clever but could use improvement...",
})
.mockResolvedValueOnce({
text: "The analysis is insightful and helpful.",
});
generateJoke = vi.fn(async (_context, ev: StartEvent) => {
const response = await mockLLM.complete({
prompt: `Write your best joke about ${ev.data.input}.`,
});
return new JokeEvent({ joke: response.text });
});
critiqueJoke = vi.fn(async (_context, ev: JokeEvent) => {
const response = await mockLLM.complete({
prompt: `Give a thorough critique of the following joke: ${ev.data.joke}`,
});
return new StopEvent({ result: response.text });
});
analyzeJoke = vi.fn(async (_context: Context, ev: JokeEvent) => {
const prompt = `Give a thorough analysis of the following joke: ${ev.data.joke}`;
const response = await mockLLM.complete({ prompt });
return new AnalysisEvent({ analysis: response.text });
});
});
test("addStep", () => {
const jokeFlow = new Workflow({ verbose: true });
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, critiqueJoke);
expect(jokeFlow.hasStep(generateJoke)).toBe(true);
expect(jokeFlow.hasStep(critiqueJoke)).toBe(true);
});
test("run workflow", async () => {
const jokeFlow = new Workflow({ verbose: true });
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, critiqueJoke);
const result = await jokeFlow.run("pirates");
expect(generateJoke).toHaveBeenCalledTimes(1);
expect(critiqueJoke).toHaveBeenCalledTimes(1);
expect(result.data.result).toBe(
"This joke is clever but could use improvement...",
);
});
test("stream events", async () => {
const jokeFlow = new Workflow({ verbose: true });
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, critiqueJoke);
const run = jokeFlow.run("pirates");
const event = await jokeFlow.streamEvents().next(); // get one event to avoid testing timeout
const result = await run;
expect(generateJoke).toHaveBeenCalledTimes(1);
expect(critiqueJoke).toHaveBeenCalledTimes(1);
expect(result.data.result).toBe(
"This joke is clever but could use improvement...",
);
expect(event).not.toBeNull();
});
test("workflow timeout", async () => {
const TIMEOUT = 1;
const jokeFlow = new Workflow({ verbose: true, timeout: TIMEOUT });
const longRunning = async (_context: Context, ev: StartEvent) => {
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds
return new StopEvent({ result: "We waited 2 seconds" });
};
jokeFlow.addStep(StartEvent, longRunning);
const run = jokeFlow.run("Let's start");
await expect(run).rejects.toThrow(
`Operation timed out after ${TIMEOUT} seconds`,
);
});
test("workflow validation", async () => {
const jokeFlow = new Workflow({ verbose: true, validate: true });
jokeFlow.addStep(StartEvent, generateJoke, { outputs: StopEvent });
jokeFlow.addStep(JokeEvent, critiqueJoke, { outputs: StopEvent });
const run = jokeFlow.run("pirates");
await expect(run).rejects.toThrow(
"The following events are consumed but never produced: JokeEvent",
);
});
test("collectEvents", async () => {
let collectedEvents: WorkflowEvent[] | null = null;
const jokeFlow = new Workflow({ verbose: true });
jokeFlow.addStep(StartEvent, generateJoke);
jokeFlow.addStep(JokeEvent, analyzeJoke);
jokeFlow.addStep([AnalysisEvent], async (context, ev) => {
collectedEvents = context.collectEvents(ev, [AnalysisEvent]);
return new StopEvent({ result: "Report generated" });
});
const result = await jokeFlow.run("pirates");
expect(generateJoke).toHaveBeenCalledTimes(1);
expect(analyzeJoke).toHaveBeenCalledTimes(1);
expect(result.data.result).toBe("Report generated");
expect(collectedEvents).toHaveLength(1);
});
});
+42
View File
@@ -1,5 +1,47 @@
# @llamaindex/experimental
## 0.0.78
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 0.0.77
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.0.76
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.0.75
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.0.74
### Patch Changes
- llamaindex@0.5.24
## 0.0.73
### Patch Changes
- llamaindex@0.5.23
## 0.0.72
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/experimental",
"description": "Experimental package for LlamaIndexTS",
"version": "0.0.72",
"version": "0.0.78",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+56
View File
@@ -1,5 +1,61 @@
# llamaindex
## 0.6.0
### Minor Changes
- 11feef8: Add workflows
### Patch Changes
- Updated dependencies [11feef8]
- @llamaindex/core@0.2.0
- @llamaindex/openai@0.1.2
## 0.5.27
### Patch Changes
- 7edeb1c: feat: decouple openai from `llamaindex` module
This should be a non-breaking change, but just you can now only install `@llamaindex/openai` to reduce the bundle size in the future
- Updated dependencies [7edeb1c]
- @llamaindex/openai@0.1.1
## 0.5.26
### Patch Changes
- ffe0cd1: faet: add openai o1 support
- ffe0cd1: feat: add PostgreSQL storage
## 0.5.25
### Patch Changes
- 4810364: fix: handle `RouterQueryEngine` with string query
- d3bc663: refactor: export vector store only in nodejs environment on top level
If you see some missing modules error, please change vector store related imports to `llamaindex/vector-store`
- Updated dependencies [4810364]
- @llamaindex/cloud@0.2.4
## 0.5.24
### Patch Changes
- Updated dependencies [0bf8d80]
- @llamaindex/cloud@0.2.3
## 0.5.23
### Patch Changes
- Updated dependencies [711c814]
- @llamaindex/core@0.1.12
## 0.5.22
### Patch Changes
@@ -1,5 +1,47 @@
# @llamaindex/cloudflare-worker-agent-test
## 0.0.62
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 0.0.61
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.0.60
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.0.59
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.0.58
### Patch Changes
- llamaindex@0.5.24
## 0.0.57
### Patch Changes
- llamaindex@0.5.23
## 0.0.56
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloudflare-worker-agent-test",
"version": "0.0.56",
"version": "0.0.62",
"type": "module",
"private": true,
"scripts": {
@@ -1,5 +1,47 @@
# @llamaindex/next-agent-test
## 0.1.62
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 0.1.61
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.1.60
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.1.59
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.1.58
### Patch Changes
- llamaindex@0.5.24
## 0.1.57
### Patch Changes
- llamaindex@0.5.23
## 0.1.56
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-agent-test",
"version": "0.1.56",
"version": "0.1.62",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,47 @@
# test-edge-runtime
## 0.1.61
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 0.1.60
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.1.59
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.1.58
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.1.57
### Patch Changes
- llamaindex@0.5.24
## 0.1.56
### Patch Changes
- llamaindex@0.5.23
## 0.1.55
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/nextjs-edge-runtime-test",
"version": "0.1.55",
"version": "0.1.61",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,47 @@
# @llamaindex/next-node-runtime
## 0.0.43
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 0.0.42
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.0.41
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.0.40
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.0.39
### Patch Changes
- llamaindex@0.5.24
## 0.0.38
### Patch Changes
- llamaindex@0.5.23
## 0.0.37
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-node-runtime-test",
"version": "0.0.37",
"version": "0.0.43",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,47 @@
# @llamaindex/waku-query-engine-test
## 0.0.62
### Patch Changes
- Updated dependencies [11feef8]
- llamaindex@0.6.0
## 0.0.61
### Patch Changes
- Updated dependencies [7edeb1c]
- llamaindex@0.5.27
## 0.0.60
### Patch Changes
- Updated dependencies [ffe0cd1]
- Updated dependencies [ffe0cd1]
- llamaindex@0.5.26
## 0.0.59
### Patch Changes
- Updated dependencies [4810364]
- Updated dependencies [d3bc663]
- llamaindex@0.5.25
## 0.0.58
### Patch Changes
- llamaindex@0.5.24
## 0.0.57
### Patch Changes
- llamaindex@0.5.23
## 0.0.56
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/waku-query-engine-test",
"version": "0.0.56",
"version": "0.0.62",
"type": "module",
"private": true,
"scripts": {
@@ -20,6 +20,7 @@
"@types/react-dom": "18.3.0",
"autoprefixer": "10.4.20",
"tailwindcss": "3.4.10",
"typescript": "5.5.4"
"typescript": "5.5.4",
"vite-plugin-wasm": "^3.3.0"
}
}
@@ -11,15 +11,22 @@ export default async function RootLayout({ children }: RootLayoutProps) {
const data = await getData();
return (
<div className="font-['Nunito']">
<meta property="description" content={data.description} />
<link rel="icon" type="image/png" href={data.icon} />
<Header />
<main className="m-6 flex items-center *:min-h-64 *:min-w-64 lg:m-0 lg:min-h-svh lg:justify-center">
{children}
</main>
<Footer />
</div>
<html>
<head>
<meta property="description" content={data.description} />
<link rel="icon" type="image/png" href={data.icon} />
<title>LlamaIndex Waku Example</title>
</head>
<body>
<div className="font-['Nunito']">
<Header />
<main className="m-6 flex items-center *:min-h-64 *:min-w-64 lg:m-0 lg:min-h-svh lg:justify-center">
{children}
</main>
<Footer />
</div>
</body>
</html>
);
}
@@ -31,7 +38,6 @@ const getData = async () => {
return data;
};
export const getConfig = async () => {
return {
render: "static",
@@ -0,0 +1,8 @@
import wasm from "vite-plugin-wasm";
export default {
plugins: [wasm()],
ssr: {
external: ["tiktoken"],
},
};
@@ -1,51 +0,0 @@
import { TransformComponent } from "@llamaindex/core/schema";
import {
BaseEmbedding,
BaseNode,
SimilarityType,
type EmbeddingInfo,
type MessageContentDetail,
} from "llamaindex";
export class OpenAIEmbedding
extends TransformComponent
implements BaseEmbedding
{
embedInfo?: EmbeddingInfo | undefined;
embedBatchSize = 512;
constructor() {
super(async (nodes: BaseNode[], _options?: any): Promise<BaseNode[]> => {
nodes.forEach((node) => (node.embedding = [0]));
return nodes;
});
}
async getQueryEmbedding(query: MessageContentDetail) {
return [0];
}
async getTextEmbedding(text: string) {
return [0];
}
async getTextEmbeddings(texts: string[]) {
return [[0]];
}
async getTextEmbeddingsBatch(texts: string[]) {
return [[0]];
}
similarity(
embedding1: number[],
embedding2: number[],
mode?: SimilarityType,
) {
return 1;
}
truncateMaxTokens(input: string[]): string[] {
return input;
}
}
+60 -5
View File
@@ -12,6 +12,15 @@ import type {
import { deepStrictEqual, strictEqual } from "node:assert";
import { llmCompleteMockStorage } from "../../node/utils.js";
import { TransformComponent } from "@llamaindex/core/schema";
import {
BaseEmbedding,
BaseNode,
SimilarityType,
type EmbeddingInfo,
type MessageContentDetail,
} from "llamaindex";
export function getOpenAISession() {
return {};
}
@@ -22,6 +31,7 @@ export function isFunctionCallingModel() {
export class OpenAI implements LLM {
supportToolCall = true;
get metadata() {
return {
model: "mock-model",
@@ -32,6 +42,7 @@ export class OpenAI implements LLM {
isFunctionCallingModel: true,
};
}
chat(
params: LLMChatParamsStreaming<Record<string, unknown>>,
): Promise<AsyncIterable<ChatResponseChunk>>;
@@ -48,8 +59,8 @@ export class OpenAI implements LLM {
llmCompleteMockStorage.llmEventStart.shift()!["messages"];
strictEqual(params.messages.length, chatMessage.length);
for (let i = 0; i < chatMessage.length; i++) {
strictEqual(params.messages[i].role, chatMessage[i].role);
deepStrictEqual(params.messages[i].content, chatMessage[i].content);
strictEqual(params.messages[i]!.role, chatMessage[i]!.role);
deepStrictEqual(params.messages[i]!.content, chatMessage[i]!.content);
}
if (llmCompleteMockStorage.llmEventEnd.length > 0) {
@@ -64,7 +75,7 @@ export class OpenAI implements LLM {
if (idx === -1) {
break;
}
const chunk = llmCompleteMockStorage.llmEventStream[idx].chunk;
const chunk = llmCompleteMockStorage.llmEventStream[idx]!.chunk;
llmCompleteMockStorage.llmEventStream.splice(idx, 1);
yield chunk;
}
@@ -77,6 +88,7 @@ export class OpenAI implements LLM {
}
throw new Error("Method not implemented.");
}
complete(
params: LLMCompletionParamsStreaming,
): Promise<AsyncIterable<CompletionResponse>>;
@@ -90,8 +102,8 @@ export class OpenAI implements LLM {
const chatMessage =
llmCompleteMockStorage.llmEventStart.shift()!["messages"];
strictEqual(1, chatMessage.length);
strictEqual("user", chatMessage[0].role);
strictEqual(params.prompt, chatMessage[0].content);
strictEqual("user", chatMessage[0]!.role);
strictEqual(params.prompt, chatMessage[0]!.content);
}
if (llmCompleteMockStorage.llmEventEnd.length > 0) {
const response = llmCompleteMockStorage.llmEventEnd.shift()!["response"];
@@ -103,3 +115,46 @@ export class OpenAI implements LLM {
throw new Error("Method not implemented.");
}
}
export class OpenAIEmbedding
extends TransformComponent
implements BaseEmbedding
{
embedInfo?: EmbeddingInfo;
embedBatchSize = 512;
constructor() {
super(async (nodes: BaseNode[], _options?: any): Promise<BaseNode[]> => {
nodes.forEach((node) => (node.embedding = [0]));
return nodes;
});
}
async getQueryEmbedding(query: MessageContentDetail) {
return [0];
}
async getTextEmbedding(text: string) {
return [0];
}
async getTextEmbeddings(texts: string[]) {
return [[0]];
}
async getTextEmbeddingsBatch(texts: string[]) {
return [[0]];
}
similarity(
embedding1: number[],
embedding2: number[],
mode?: SimilarityType,
) {
return 1;
}
truncateMaxTokens(input: string[]): string[] {
return input;
}
}
+8 -2
View File
@@ -13,8 +13,14 @@ export async function resolve(specifier, context, nextResolve) {
return result;
}
const targetUrl = fileURLToPath(result.url).replace(/\.js$/, ".ts");
const relativePath = relative(packageDistDir, targetUrl);
if (relativePath.startsWith(".") || relativePath.startsWith("/")) {
let relativePath = relative(packageDistDir, targetUrl);
// todo: make it more generic if we have more sub modules fixtures in the future
if (relativePath.startsWith("../../llm/openai")) {
relativePath = relativePath.replace(
"../../llm/openai/dist/index.ts",
"llm/openai.ts",
);
} else if (relativePath.startsWith(".") || relativePath.startsWith("/")) {
return result;
}
const url = pathToFileURL(join(fixturesDir, relativePath)).toString();
+67
View File
@@ -0,0 +1,67 @@
import { LLMSingleSelector, Settings } from "llamaindex";
import assert from "node:assert";
import { test } from "node:test";
import { mockLLMEvent } from "./utils.js";
await test("#1177", async (t) => {
await mockLLMEvent(t, "#1177");
await t.test(async () => {
const selector = new LLMSingleSelector({
llm: Settings.llm,
});
{
const result = await selector.select(
[
{
description: "Math calculation",
},
{
description: "Search from google",
},
],
"calculate 2 + 2",
);
assert.equal(result.selections.length, 1);
assert.equal(result.selections.at(0)!.index, 0);
}
{
const result = await selector.select(
[
{
description: "Math calculation",
},
{
description: "Search from google",
},
],
{
query: "calculate 2 + 2",
},
);
assert.equal(result.selections.length, 1);
assert.equal(result.selections.at(0)!.index, 0);
}
{
const result = await selector.select(
[
{
description: "Math calculation",
},
{
description: "Search from google",
},
],
{
query: [
{
type: "text",
text: "calculate 2 + 2",
},
],
},
);
assert.equal(result.selections.length, 1);
assert.equal(result.selections.at(0)!.index, 0);
}
});
});
+2 -2
View File
@@ -163,11 +163,11 @@ For questions about more specific sections, please use the vector_tool.`,
}),
];
const originalCall = queryEngineTools[1].call.bind(queryEngineTools[1]);
const originalCall = queryEngineTools[1]!.call.bind(queryEngineTools[1]);
const mockCall = t.mock.fn(({ query }: { query: string }) => {
return originalCall({ query });
});
queryEngineTools[1].call = mockCall;
queryEngineTools[1]!.call = mockCall;
const toolMapping = SimpleToolNodeMapping.fromObjects(queryEngineTools);
@@ -0,0 +1,67 @@
{
"llmEventStart": [
{
"id": "PRESERVE_0",
"messages": [
{
"content": "Some choices are given below. It is provided in a numbered list (1 to 42), where each item in the list corresponds to a summary.\n---------------------\n(1) Math calculation(2) Search from google\n---------------------\nUsing only the choices above and not prior knowledge, return the choice that is most relevant to the question: 'calculate 2 + 2'\n\n\nThe output should be ONLY JSON formatted as a JSON instance.\n\nHere is an example:\n[\n {\n \"choice\": 1,\n \"reason\": \"<insert reason for choice>\"\n },\n ...\n]\n",
"role": "user"
}
]
},
{
"id": "PRESERVE_1",
"messages": [
{
"content": "Some choices are given below. It is provided in a numbered list (1 to 42), where each item in the list corresponds to a summary.\n---------------------\n(1) Math calculation(2) Search from google\n---------------------\nUsing only the choices above and not prior knowledge, return the choice that is most relevant to the question: 'calculate 2 + 2'\n\n\nThe output should be ONLY JSON formatted as a JSON instance.\n\nHere is an example:\n[\n {\n \"choice\": 1,\n \"reason\": \"<insert reason for choice>\"\n },\n ...\n]\n",
"role": "user"
}
]
},
{
"id": "PRESERVE_2",
"messages": [
{
"content": "Some choices are given below. It is provided in a numbered list (1 to 42), where each item in the list corresponds to a summary.\n---------------------\n(1) Math calculation(2) Search from google\n---------------------\nUsing only the choices above and not prior knowledge, return the choice that is most relevant to the question: 'calculate 2 + 2'\n\n\nThe output should be ONLY JSON formatted as a JSON instance.\n\nHere is an example:\n[\n {\n \"choice\": 1,\n \"reason\": \"<insert reason for choice>\"\n },\n ...\n]\n",
"role": "user"
}
]
}
],
"llmEventEnd": [
{
"id": "PRESERVE_0",
"response": {
"raw": null,
"message": {
"content": "[\n {\n \"choice\": 1,\n \"reason\": \"The question 'calculate 2 + 2' is directly asking for a math calculation, which corresponds to choice 1.\"\n }\n]",
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_1",
"response": {
"raw": null,
"message": {
"content": "[\n {\n \"choice\": 1,\n \"reason\": \"The question 'calculate 2 + 2' is asking for a mathematical calculation, which directly corresponds to choice 1: Math calculation.\"\n }\n]",
"role": "assistant",
"options": {}
}
}
},
{
"id": "PRESERVE_2",
"response": {
"raw": null,
"message": {
"content": "[\n {\n \"choice\": 1,\n \"reason\": \"The question 'calculate 2 + 2' is asking for a mathematical calculation, which directly corresponds to choice 1: Math calculation.\"\n }\n]",
"role": "assistant",
"options": {}
}
}
}
],
"llmEventStream": []
}
@@ -0,0 +1,90 @@
import { Document, VectorStoreQueryMode } from "llamaindex";
import { PGVectorStore } from "llamaindex/vector-store/PGVectorStore";
import assert from "node:assert";
import { test } from "node:test";
import pg from "pg";
import { registerTypes } from "pgvector/pg";
let pgClient: pg.Client | pg.Pool;
test.afterEach(async () => {
await pgClient.end();
});
await test("init with client", async () => {
pgClient = new pg.Client({
database: "llamaindex_node_test",
});
await pgClient.connect();
await pgClient.query("CREATE EXTENSION IF NOT EXISTS vector");
await registerTypes(pgClient);
const vectorStore = new PGVectorStore(pgClient);
assert.deepStrictEqual(await vectorStore.client(), pgClient);
});
await test("init with pool", async () => {
pgClient = new pg.Pool({
database: "llamaindex_node_test",
});
await pgClient.query("CREATE EXTENSION IF NOT EXISTS vector");
const client = await pgClient.connect();
await registerTypes(client);
const vectorStore = new PGVectorStore(client);
assert.deepStrictEqual(await vectorStore.client(), client);
client.release();
});
await test("init without client", async () => {
const vectorStore = new PGVectorStore({
database: "llamaindex_node_test",
});
pgClient = (await vectorStore.client()) as pg.Client;
assert.notDeepStrictEqual(pgClient, undefined);
});
await test("simple node", async () => {
const dimensions = 3;
const schemaName =
"llamaindex_vector_store_test_" + Math.random().toString(36).substring(7);
const nodeId = "5bb16627-f6c0-459c-bb18-71642813ef21";
const node = new Document({
text: "hello world",
id_: nodeId,
embedding: [0.1, 0.2, 0.3],
});
const vectorStore = new PGVectorStore({
database: "llamaindex_node_test",
dimensions,
schemaName,
});
await vectorStore.add([node]);
{
const result = await vectorStore.query({
mode: VectorStoreQueryMode.DEFAULT,
similarityTopK: 1,
queryEmbedding: [1, 2, 3],
});
const actualJSON = result.nodes![0]!.toJSON();
assert.deepStrictEqual(actualJSON, {
...node.toJSON(),
hash: actualJSON.hash,
metadata: actualJSON.metadata,
});
assert.deepStrictEqual(result.ids, [nodeId]);
assert.deepStrictEqual(result.similarities, [1]);
}
await vectorStore.delete(nodeId);
{
const result = await vectorStore.query({
mode: VectorStoreQueryMode.DEFAULT,
similarityTopK: 1,
queryEmbedding: [1, 2, 3],
});
assert.deepStrictEqual(result.nodes, []);
}
pgClient = (await vectorStore.client()) as pg.Client;
});
-1
View File
@@ -10,7 +10,6 @@
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@llamaindex/core": "workspace:*",
"@types/node": "^22.5.1",
"consola": "^3.2.3",
"llamaindex": "workspace:*",
+14 -5
View File
@@ -1,6 +1,6 @@
{
"name": "llamaindex",
"version": "0.5.22",
"version": "0.6.0",
"license": "MIT",
"type": "module",
"keywords": [
@@ -33,6 +33,7 @@
"@llamaindex/cloud": "workspace:*",
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*",
"@llamaindex/openai": "workspace:*",
"@mistralai/mistralai": "^1.0.4",
"@mixedbread-ai/sdk": "^2.2.11",
"@pinecone-database/pinecone": "^3.0.2",
@@ -56,11 +57,9 @@
"md-utils-ts": "^2.0.0",
"mongodb": "^6.7.0",
"notion-md-crawler": "^1.0.0",
"openai": "^4.57.0",
"openai": "^4.60.0",
"papaparse": "^5.4.1",
"pathe": "^1.1.2",
"pg": "^8.12.0",
"pgvector": "^0.2.0",
"portkey-ai": "0.1.16",
"rake-modified": "^1.0.8",
"string-strip-html": "^13.4.8",
@@ -72,11 +71,19 @@
"zod": "^3.23.8"
},
"peerDependencies": {
"@notionhq/client": "^2.2.15"
"@notionhq/client": "^2.2.15",
"pg": "^8.12.0",
"pgvector": "0.2.0"
},
"peerDependenciesMeta": {
"@notionhq/client": {
"optional": true
},
"pg": {
"optional": true
},
"pgvector": {
"optional": true
}
},
"devDependencies": {
@@ -85,6 +92,8 @@
"@swc/core": "^1.7.22",
"concurrently": "^8.2.2",
"glob": "^11.0.0",
"pg": "^8.12.0",
"pgvector": "0.2.0",
"typescript": "^5.5.4"
},
"engines": {
+6 -6
View File
@@ -5,7 +5,7 @@ import {
} from "@llamaindex/core/prompts";
import { extractText, messagesToHistory } from "@llamaindex/core/utils";
import { tokenizers, type Tokenizer } from "@llamaindex/env";
import { OpenAI } from "./llm/openai.js";
import { OpenAI } from "@llamaindex/openai";
/**
* A ChatHistory is used to keep the state of back and forth chat messages
@@ -42,7 +42,7 @@ export class SimpleChatHistory extends ChatHistory {
messages: ChatMessage[];
private messagesBefore: number;
constructor(init?: Partial<SimpleChatHistory>) {
constructor(init?: { messages?: ChatMessage[] | undefined }) {
super();
this.messages = init?.messages ?? [];
this.messagesBefore = this.messages.length;
@@ -118,7 +118,7 @@ export class SummaryChatHistory extends ChatHistory {
// remove oldest message until the chat history is short enough for the context window
messagesToSummarize.shift();
} while (
this.tokenizer.encode(promptMessages[0].content).length >
this.tokenizer.encode(promptMessages[0]!.content).length >
this.tokensToSummarize
);
@@ -146,7 +146,7 @@ export class SummaryChatHistory extends ChatHistory {
public getLastSummary(): ChatMessage | null {
const lastSummaryIndex = this.getLastSummaryIndex();
return lastSummaryIndex ? this.messages[lastSummaryIndex] : null;
return lastSummaryIndex ? this.messages[lastSummaryIndex]! : null;
}
private get systemMessages() {
@@ -174,10 +174,10 @@ export class SummaryChatHistory extends ChatHistory {
// and convert summary message so it can be send to the LLM
const summaryMessage: ChatMessage = transformSummary
? {
content: `Summary of the conversation so far: ${this.messages[lastSummaryIndex].content}`,
content: `Summary of the conversation so far: ${this.messages[lastSummaryIndex]!.content}`,
role: "system",
}
: this.messages[lastSummaryIndex];
: this.messages[lastSummaryIndex]!;
return [summaryMessage, ...this.messages.slice(lastSummaryIndex + 1)];
}
}
+1 -1
View File
@@ -8,12 +8,12 @@ import {
import type { QueryType } from "@llamaindex/core/query-engine";
import type { BaseOutputParser } from "@llamaindex/core/schema";
import { extractText, toToolDescriptions } from "@llamaindex/core/utils";
import { OpenAI } from "@llamaindex/openai";
import { SubQuestionOutputParser } from "./OutputParser.js";
import type {
BaseQuestionGenerator,
SubQuestion,
} from "./engines/query/types.js";
import { OpenAI } from "./llm/openai.js";
import type { StructuredOutput } from "./types.js";
/**
+4 -2
View File
@@ -13,6 +13,8 @@ export type RetrieveParams = {
export interface BaseRetriever {
retrieve(params: RetrieveParams): Promise<NodeWithScore[]>;
// to be deprecated soon
serviceContext?: ServiceContext;
/**
* @deprecated to be deprecated soon
*/
serviceContext?: ServiceContext | undefined;
}
+2 -3
View File
@@ -1,12 +1,11 @@
import type { BaseEmbedding } from "@llamaindex/core/embeddings";
import { PromptHelper } from "@llamaindex/core/indices";
import type { LLM } from "@llamaindex/core/llms";
import {
type NodeParser,
SentenceSplitter,
} from "@llamaindex/core/node-parser";
import { PromptHelper } from "./PromptHelper.js";
import { OpenAIEmbedding } from "./embeddings/OpenAIEmbedding.js";
import { OpenAI } from "./llm/openai.js";
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
/**
* The ServiceContext is a collection of components that are used in different parts of the application.
+15 -14
View File
@@ -2,9 +2,9 @@ import {
type CallbackManager,
Settings as CoreSettings,
} from "@llamaindex/core/global";
import { OpenAI } from "./llm/openai.js";
import { OpenAI } from "@llamaindex/openai";
import { PromptHelper } from "./PromptHelper.js";
import { PromptHelper } from "@llamaindex/core/indices";
import type { BaseEmbedding } from "@llamaindex/core/embeddings";
import type { LLM } from "@llamaindex/core/llms";
@@ -27,13 +27,12 @@ export type PromptConfig = {
export interface Config {
prompt: PromptConfig;
llm: LLM | null;
promptHelper: PromptHelper | null;
embedModel: BaseEmbedding | null;
nodeParser: NodeParser | null;
callbackManager: CallbackManager | null;
chunkSize?: number;
chunkOverlap?: number;
chunkSize: number | undefined;
chunkOverlap: number | undefined;
}
/**
@@ -41,12 +40,10 @@ export interface Config {
*/
class GlobalSettings implements Config {
#prompt: PromptConfig = {};
#llm: LLM | null = null;
#promptHelper: PromptHelper | null = null;
#nodeParser: NodeParser | null = null;
#chunkOverlap?: number;
#llmAsyncLocalStorage = new AsyncLocalStorage<LLM>();
#promptHelperAsyncLocalStorage = new AsyncLocalStorage<PromptHelper>();
#nodeParserAsyncLocalStorage = new AsyncLocalStorage<NodeParser>();
#chunkOverlapAsyncLocalStorage = new AsyncLocalStorage<number>();
@@ -62,19 +59,21 @@ class GlobalSettings implements Config {
}
get llm(): LLM {
if (this.#llm === null) {
this.#llm = new OpenAI();
// fixme: we might need check internal error instead of try-catch here
try {
CoreSettings.llm;
} catch (error) {
CoreSettings.llm = new OpenAI();
}
return this.#llmAsyncLocalStorage.getStore() ?? this.#llm;
return CoreSettings.llm;
}
set llm(llm: LLM) {
this.#llm = llm;
CoreSettings.llm = llm;
}
withLLM<Result>(llm: LLM, fn: () => Result): Result {
return this.#llmAsyncLocalStorage.run(llm, fn);
return CoreSettings.withLLM(llm, fn);
}
get promptHelper(): PromptHelper {
@@ -159,7 +158,9 @@ class GlobalSettings implements Config {
}
set chunkOverlap(chunkOverlap: number | undefined) {
this.#chunkOverlap = chunkOverlap;
if (typeof chunkOverlap === "number") {
this.#chunkOverlap = chunkOverlap;
}
}
withChunkOverlap<Result>(chunkOverlap: number, fn: () => Result): Result {
+2 -2
View File
@@ -53,7 +53,7 @@ export function createTaskOutputStream<
nextSteps: new Set(),
};
if (steps.length > 0) {
step.prevStep = steps[steps.length - 1];
step.prevStep = steps[steps.length - 1]!;
}
const taskOutputs: TaskStepOutput<
Model,
@@ -77,7 +77,7 @@ export function createTaskOutputStream<
context.logger.log("Finished step(id, %s).", step.id);
// fixme: support multi-thread when there are multiple outputs
// todo: for now we pretend there is only one task output
const { isLast, taskStep } = taskOutputs[0];
const { isLast, taskStep } = taskOutputs[0]!;
context = {
...taskStep.context,
store: {
+1 -1
View File
@@ -1,5 +1,5 @@
import { OpenAI } from "@llamaindex/openai";
import { Settings } from "../Settings.js";
import { OpenAI } from "../llm/openai.js";
import { LLMAgent, LLMAgentWorker, type LLMAgentParams } from "./llm.js";
// This is likely not necessary anymore but leaving it here just incase it's in use elsewhere
+5 -5
View File
@@ -89,8 +89,8 @@ function extractFinalResponse(
);
}
const thought = match[1].trim();
const answer = match[2].trim();
const thought = match[1]!.trim();
const answer = match[2]!.trim();
return [thought, answer];
}
@@ -108,9 +108,9 @@ function extractToolUse(
);
}
const thought = match[1].trim();
const action = match[2].trim();
const actionInput = match[3].trim();
const thought = match[1]!.trim();
const action = match[2]!.trim();
const actionInput = match[3]!.trim();
return [thought, action, actionInput];
}
@@ -12,9 +12,14 @@ export class LLamaCloudFileService {
public static async getAllProjectsWithPipelines() {
initService();
try {
const projects = await ProjectsService.listProjectsApiV1ProjectsGet();
const pipelines =
await PipelinesService.searchPipelinesApiV1PipelinesGet();
const { data: projects } =
await ProjectsService.listProjectsApiV1ProjectsGet({
throwOnError: true,
});
const { data: pipelines } =
await PipelinesService.searchPipelinesApiV1PipelinesGet({
throwOnError: true,
});
return projects.map((project) => ({
...project,
pipelines: pipelines.filter((p) => p.project_id === project.id),
@@ -35,11 +40,12 @@ export class LLamaCloudFileService {
customMetadata: Record<string, any> = {},
) {
initService();
const file = await FilesService.uploadFileApiV1FilesPost({
projectId,
formData: {
const { data: file } = await FilesService.uploadFileApiV1FilesPost({
path: { project_id: projectId },
body: {
upload_file: uploadFile,
},
throwOnError: true,
});
const files = [
{
@@ -48,19 +54,24 @@ export class LLamaCloudFileService {
},
];
await PipelinesService.addFilesToPipelineApiV1PipelinesPipelineIdFilesPut({
pipelineId,
requestBody: files,
path: {
pipeline_id: pipelineId,
},
body: files,
});
// Wait 2s for the file to be processed
const maxAttempts = 20;
let attempt = 0;
while (attempt < maxAttempts) {
const result =
const { data: result } =
await PipelinesService.getPipelineFileStatusApiV1PipelinesPipelineIdFilesFileIdStatusGet(
{
pipelineId,
fileId: file.id,
path: {
pipeline_id: pipelineId,
file_id: file.id,
},
throwOnError: true,
},
);
if (result.status === "ERROR") {
@@ -83,16 +94,24 @@ export class LLamaCloudFileService {
*/
public static async getFileUrl(pipelineId: string, filename: string) {
initService();
const allPipelineFiles =
const { data: allPipelineFiles } =
await PipelinesService.listPipelineFilesApiV1PipelinesPipelineIdFilesGet({
pipelineId,
path: {
pipeline_id: pipelineId,
},
throwOnError: true,
});
const file = allPipelineFiles.find((file) => file.name === filename);
if (!file?.file_id) return null;
const fileContent =
const { data: fileContent } =
await FilesService.readFileContentApiV1FilesIdContentGet({
id: file.file_id,
projectId: file.project_id,
path: {
id: file.file_id,
},
query: {
project_id: file.project_id,
},
throwOnError: true,
});
return fileContent.url;
}
@@ -13,8 +13,8 @@ import { getAppBaseUrl, getProjectId, initService } from "./utils.js";
import { PipelinesService, ProjectsService } from "@llamaindex/cloud/api";
import { SentenceSplitter } from "@llamaindex/core/node-parser";
import { getEnv } from "@llamaindex/env";
import { OpenAIEmbedding } from "@llamaindex/openai";
import { Settings } from "../Settings.js";
import { OpenAIEmbedding } from "../embeddings/OpenAIEmbedding.js";
export class LlamaCloudIndex {
params: CloudConstructorParams;
@@ -38,10 +38,13 @@ export class LlamaCloudIndex {
}
while (true) {
const pipelineStatus =
const { data: pipelineStatus } =
await PipelinesService.getPipelineStatusApiV1PipelinesPipelineIdStatusGet(
{
pipelineId,
path: {
pipeline_id: pipelineId,
},
throwOnError: true,
},
);
@@ -90,9 +93,14 @@ export class LlamaCloudIndex {
const docsToRemove = new Set<string>();
for (const doc of pendingDocs) {
const { status } =
const {
data: { status },
} =
await PipelinesService.getPipelineDocumentStatusApiV1PipelinesPipelineIdDocumentsDocumentIdStatusGet(
{ pipelineId, documentId: doc },
{
path: { pipeline_id: pipelineId, document_id: doc },
throwOnError: true,
},
);
if (status === "NOT_STARTED" || status === "IN_PROGRESS") {
@@ -136,12 +144,16 @@ export class LlamaCloudIndex {
name?: string,
projectName?: string,
): Promise<string> {
const pipelines = await PipelinesService.searchPipelinesApiV1PipelinesGet({
projectId: await this.getProjectId(projectName),
pipelineName: name ?? this.params.name,
});
const { data: pipelines } =
await PipelinesService.searchPipelinesApiV1PipelinesGet({
path: {
project_id: await this.getProjectId(projectName),
project_name: name ?? this.params.name,
},
throwOnError: true,
});
return pipelines[0].id;
return pipelines[0]!.id;
}
public async getProjectId(
@@ -177,26 +189,37 @@ export class LlamaCloudIndex {
transformations: params.transformations ?? defaultTransformations,
});
const project = await ProjectsService.upsertProjectApiV1ProjectsPut({
organizationId: params.organizationId,
requestBody: {
name: params.projectName ?? "default",
},
});
const { data: project } =
await ProjectsService.upsertProjectApiV1ProjectsPut({
path: {
organization_id: params.organizationId,
},
body: {
name: params.projectName ?? "default",
},
throwOnError: true,
});
if (!project.id) {
throw new Error("Project ID should be defined");
}
const pipeline = await PipelinesService.upsertPipelineApiV1PipelinesPut({
projectId: project.id,
requestBody: {
name: params.name,
configured_transformations:
pipelineCreateParams.configured_transformations,
pipeline_type: pipelineCreateParams.pipeline_type,
},
});
const { data: pipeline } =
await PipelinesService.upsertPipelineApiV1PipelinesPut({
path: {
project_id: project.id,
},
body: pipelineCreateParams.configured_transformations
? {
name: params.name,
configured_transformations:
pipelineCreateParams.configured_transformations,
}
: {
name: params.name,
},
throwOnError: true,
});
if (!pipeline.id) {
throw new Error("Pipeline ID must be defined");
@@ -208,8 +231,10 @@ export class LlamaCloudIndex {
await PipelinesService.upsertBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPut(
{
pipelineId: pipeline.id,
requestBody: params.documents.map((doc) => ({
path: {
pipeline_id: pipeline.id,
},
body: params.documents.map((doc) => ({
metadata: doc.metadata,
text: doc.text,
excluded_embed_metadata_keys: doc.excludedEmbedMetadataKeys,
@@ -220,10 +245,11 @@ export class LlamaCloudIndex {
);
while (true) {
const pipelineStatus =
const { data: pipelineStatus } =
await PipelinesService.getPipelineStatusApiV1PipelinesPipelineIdStatusGet(
{
pipelineId: pipeline.id,
path: { pipeline_id: pipeline.id },
throwOnError: true,
},
);
@@ -299,8 +325,10 @@ export class LlamaCloudIndex {
await PipelinesService.createBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPost(
{
pipelineId: pipelineId,
requestBody: [
path: {
pipeline_id: pipelineId,
},
body: [
{
metadata: document.metadata,
text: document.text,
@@ -327,8 +355,10 @@ export class LlamaCloudIndex {
await PipelinesService.deletePipelineDocumentApiV1PipelinesPipelineIdDocumentsDocumentIdDelete(
{
pipelineId,
documentId: document.id_,
path: {
pipeline_id: pipelineId,
document_id: document.id_,
},
},
);
@@ -347,8 +377,10 @@ export class LlamaCloudIndex {
await PipelinesService.upsertBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPut(
{
pipelineId,
requestBody: [
path: {
pipeline_id: pipelineId,
},
body: [
{
metadata: document.metadata,
text: document.text,
@@ -59,20 +59,27 @@ export class LlamaCloudRetriever implements BaseRetriever {
query,
preFilters,
}: RetrieveParams): Promise<NodeWithScore[]> {
const pipelines = await PipelinesService.searchPipelinesApiV1PipelinesGet({
projectId: await getProjectId(this.projectName, this.organizationId),
pipelineName: this.pipelineName,
});
const { data: pipelines } =
await PipelinesService.searchPipelinesApiV1PipelinesGet({
query: {
project_id: await getProjectId(this.projectName, this.organizationId),
project_name: this.pipelineName,
},
throwOnError: true,
});
if (pipelines.length === 0 || !pipelines[0].id) {
if (pipelines.length === 0 || !pipelines[0]!.id) {
throw new Error(
`No pipeline found with name ${this.pipelineName} in project ${this.projectName}`,
);
}
const pipeline =
const { data: pipeline } =
await PipelinesService.getPipelineApiV1PipelinesPipelineIdGet({
pipelineId: pipelines[0].id,
path: {
pipeline_id: pipelines[0]!.id,
},
throwOnError: true,
});
if (!pipeline) {
@@ -81,15 +88,18 @@ export class LlamaCloudRetriever implements BaseRetriever {
);
}
const results =
const { data: results } =
await PipelinesService.runSearchApiV1PipelinesPipelineIdRetrievePost({
pipelineId: pipeline.id,
requestBody: {
throwOnError: true,
path: {
pipeline_id: pipeline.id,
},
body: {
...this.retrieveParams,
query: extractText(query),
search_filters:
this.retrieveParams.filters ?? (preFilters as MetadataFilters),
dense_similarity_top_k: this.retrieveParams.similarityTopK,
dense_similarity_top_k: this.retrieveParams.similarityTopK!,
},
});
+1 -1
View File
@@ -5,7 +5,7 @@ import type {
} from "@llamaindex/cloud/api";
import { SentenceSplitter } from "@llamaindex/core/node-parser";
import { BaseNode, type TransformComponent } from "@llamaindex/core/schema";
import { OpenAIEmbedding } from "../embeddings/OpenAIEmbedding.js";
import { OpenAIEmbedding } from "@llamaindex/openai";
export type GetPipelineCreateParams = {
pipelineName: string;
+6 -3
View File
@@ -1,10 +1,13 @@
import type { ServiceContext } from "../ServiceContext.js";
export type ClientParams = { apiKey?: string; baseUrl?: string };
export type ClientParams = {
apiKey?: string | undefined;
baseUrl?: string | undefined;
};
export type CloudConstructorParams = {
name: string;
projectName: string;
organizationId?: string;
serviceContext?: ServiceContext;
organizationId?: string | undefined;
serviceContext?: ServiceContext | undefined;
} & ClientParams;
+28 -10
View File
@@ -1,4 +1,4 @@
import { OpenAPI, ProjectsService } from "@llamaindex/cloud/api";
import { client, ProjectsService } from "@llamaindex/cloud/api";
import { DEFAULT_BASE_URL } from "@llamaindex/core/global";
import { getEnv } from "@llamaindex/env";
import type { ClientParams } from "./type.js";
@@ -8,13 +8,26 @@ function getBaseUrl(baseUrl?: string): string {
}
export function getAppBaseUrl(): string {
return OpenAPI.BASE.replace(/api\./, "");
return client.getConfig().baseUrl?.replace(/api\./, "") ?? "";
}
// fixme: refactor this to init at the top level or module level
let initOnce = false;
export function initService({ apiKey, baseUrl }: ClientParams = {}) {
OpenAPI.TOKEN = apiKey ?? getEnv("LLAMA_CLOUD_API_KEY");
OpenAPI.BASE = getBaseUrl(baseUrl);
if (!OpenAPI.TOKEN) {
if (initOnce) {
return;
}
initOnce = true;
client.setConfig({
baseUrl: getBaseUrl(baseUrl),
throwOnError: true,
});
const token = apiKey ?? getEnv("LLAMA_CLOUD_API_KEY");
client.interceptors.request.use((request) => {
request.headers.set("Authorization", `Bearer ${token}`);
return request;
});
if (!token) {
throw new Error(
"API Key is required for LlamaCloudIndex. Please pass the apiKey parameter",
);
@@ -25,10 +38,15 @@ export async function getProjectId(
projectName: string,
organizationId?: string,
): Promise<string> {
const projects = await ProjectsService.listProjectsApiV1ProjectsGet({
projectName: projectName,
organizationId: organizationId,
});
const { data: projects } = await ProjectsService.listProjectsApiV1ProjectsGet(
{
path: {
project_name: projectName,
organization_id: organizationId,
},
throwOnError: true,
},
);
if (projects.length === 0) {
throw new Error(
@@ -40,7 +58,7 @@ export async function getProjectId(
);
}
const project = projects[0];
const project = projects[0]!;
if (!project.id) {
throw new Error(`No project found with name ${projectName}`);
@@ -87,7 +87,7 @@ export class DeepInfraEmbedding extends BaseEmbedding {
async getTextEmbedding(text: string): Promise<number[]> {
const texts = mapPrefixWithInputs(this.textPrefix, [text]);
const embeddings = await this.getDeepInfraEmbedding(texts);
return embeddings[0];
return embeddings[0]!;
}
async getQueryEmbedding(
@@ -97,7 +97,7 @@ export class DeepInfraEmbedding extends BaseEmbedding {
if (text) {
const queries = mapPrefixWithInputs(this.queryPrefix, [text]);
const embeddings = await this.getDeepInfraEmbedding(queries);
return embeddings[0];
return embeddings[0]!;
} else {
return null;
}

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