mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-04 03:40:26 -04:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74fc725f37 | |||
| a0a74aed60 | |||
| 11feef8c82 | |||
| 9c5ff164ac | |||
| 7edeb1c2d7 | |||
| 8b95abdc85 | |||
| ffe0cd1ef1 | |||
| 5d2111a19f | |||
| 68ac7fd57f | |||
| 7320d96a36 | |||
| ee17fb475b | |||
| 28b877e31f | |||
| 4389b80a52 | |||
| d3bc663951 | |||
| 4810364788 | |||
| 2dcad52dd9 | |||
| 0bf8d80b12 | |||
| e4bba02aec | |||
| 1caa0da657 | |||
| 711c814bb2 | |||
| 5b832eb927 | |||
| 49988431f6 | |||
| 72d65dd51a | |||
| 553bc55b19 | |||
| fc6f69833c |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"version": "0.0.63",
|
||||
"version": "0.0.69",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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`.
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export type Info = {
|
||||
* @internal
|
||||
*/
|
||||
export type InfoString = {
|
||||
originalFunction?: string;
|
||||
originalFunction: string | undefined;
|
||||
parameterMapping: Record<string, number>;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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 [
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export {
|
||||
PromptHelper,
|
||||
getBiggestPrompt,
|
||||
type PromptHelperOptions,
|
||||
} from "./prompt-helper";
|
||||
+33
-19
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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]!),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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[] => {
|
||||
|
||||
@@ -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 & {})>
|
||||
> = {};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }> {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./context";
|
||||
export * from "./events";
|
||||
export * from "./workflow";
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
@@ -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:*",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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!,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user