Compare commits

...

8 Commits

Author SHA1 Message Date
github-actions[bot] 9c5ff164ac Release 0.5.27 (#1195)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-12 13:47:11 -07:00
Alex Yang 7edeb1c2d7 feat: decouple openai from llamaindex module (#1194) 2024-09-12 13:36:08 -07:00
github-actions[bot] 8b95abdc85 Release 0.5.26 (#1193)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-09-12 11:38:07 -07:00
Alex Yang ffe0cd1ef1 chore: update changelog 2024-09-12 11:33:32 -07:00
Alex Yang 5d2111a19f feat: init support openai o1 model (#1192) 2024-09-12 11:31:05 -07:00
Alex Yang 68ac7fd57f ci: fix syntax (#1186) 2024-09-11 16:54:39 -07:00
Alex Yang 7320d96a36 fix: waku build (#1185) 2024-09-11 15:36:39 -07:00
Goran ee17fb475b feat: add PostgreSQL storage (#1180) 2024-09-11 12:31:04 -07:00
65 changed files with 1461 additions and 856 deletions
+18 -19
View File
@@ -12,6 +12,10 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
POSTGRES_USER: runneradmin
POSTGRES_HOST_AUTH_METHOD: trust
jobs:
e2e:
strategy:
@@ -19,17 +23,20 @@ jobs:
matrix:
node-version: [18.x, 20.x, 22.x]
name: E2E on Node.js ${{ matrix.node-version }}
env: POSTGRES_DB=vectordb
POSTGRES_USER=testuser
POSTGRES_PASSWORD=testpwd
POSTGRES_HOST_AUTH_METHOD=trust
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:
@@ -47,19 +54,8 @@ 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: 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
@@ -107,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:
@@ -146,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
+15
View File
@@ -189,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
+15
View File
@@ -1,5 +1,20 @@
# docs
## 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
+37 -3
View File
@@ -4,12 +4,19 @@ sidebar_position: 7
# Storage
Storage in LlamaIndex.TS works automatically once you've configured a `StorageContext` object. Just configure the `persistDir` and attach it to an index.
Storage in LlamaIndex.TS works automatically once you've configured a
`StorageContext` object.
Right now, only saving and loading from disk is supported, with future integrations planned!
## Local Storage
You can configure the `persistDir` and attach it to an index.
```typescript
import { Document, VectorStoreIndex, storageContextFromDefaults } from "./src";
import {
Document,
VectorStoreIndex,
storageContextFromDefaults,
} from "llamaindex";
const storageContext = await storageContextFromDefaults({
persistDir: "./storage",
@@ -21,6 +28,33 @@ const index = await VectorStoreIndex.fromDocuments([document], {
});
```
## PostgreSQL Storage
You can configure the `schemaName`, `tableName`, `namespace`, and
`connectionString`. If a `connectionString` is not
provided, it will use the environment variables `PGHOST`, `PGUSER`,
`PGPASSWORD`, `PGDATABASE` and `PGPORT`.
```typescript
import {
Document,
VectorStoreIndex,
PostgresDocumentStore,
PostgresIndexStore,
storageContextFromDefaults,
} from "llamaindex";
const storageContext = await storageContextFromDefaults({
docStore: new PostgresDocumentStore(),
indexStore: new PostgresIndexStore(),
});
const document = new Document({ text: "Test Text" });
const index = await VectorStoreIndex.fromDocuments([document], {
storageContext,
});
```
## API Reference
- [StorageContext](../api/interfaces/StorageContext.md)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "docs",
"version": "0.0.66",
"version": "0.0.68",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
@@ -1,5 +1,22 @@
# @llamaindex/autotool-01-node-example
## 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
@@ -13,5 +13,5 @@
"scripts": {
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
},
"version": "0.0.6"
"version": "0.0.8"
}
@@ -1,5 +1,22 @@
# @llamaindex/autotool-02-next-example
## 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
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/autotool-02-next-example",
"private": true,
"version": "0.1.50",
"version": "0.1.52",
"scripts": {
"dev": "next dev",
"build": "next build",
+1 -1
View File
@@ -51,7 +51,7 @@
"unplugin": "^1.12.2"
},
"peerDependencies": {
"llamaindex": "^0.5.25",
"llamaindex": "^0.5.27",
"openai": "^4",
"typescript": "^4"
},
+15
View File
@@ -1,5 +1,20 @@
# @llamaindex/experimental
## 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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/experimental",
"description": "Experimental package for LlamaIndexTS",
"version": "0.0.75",
"version": "0.0.77",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+18
View File
@@ -1,5 +1,23 @@
# llamaindex
## 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
@@ -1,5 +1,20 @@
# @llamaindex/cloudflare-worker-agent-test
## 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloudflare-worker-agent-test",
"version": "0.0.59",
"version": "0.0.61",
"type": "module",
"private": true,
"scripts": {
@@ -1,5 +1,20 @@
# @llamaindex/next-agent-test
## 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-agent-test",
"version": "0.1.59",
"version": "0.1.61",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,20 @@
# test-edge-runtime
## 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/nextjs-edge-runtime-test",
"version": "0.1.58",
"version": "0.1.60",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,20 @@
# @llamaindex/next-node-runtime
## 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-node-runtime-test",
"version": "0.0.40",
"version": "0.0.42",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,20 @@
# @llamaindex/waku-query-engine-test
## 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/waku-query-engine-test",
"version": "0.0.59",
"version": "0.0.61",
"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;
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>>;
@@ -77,6 +88,7 @@ export class OpenAI implements LLM {
}
throw new Error("Method not implemented.");
}
complete(
params: LLMCompletionParamsStreaming,
): Promise<AsyncIterable<CompletionResponse>>;
@@ -103,3 +115,46 @@ export class OpenAI implements LLM {
throw new Error("Method not implemented.");
}
}
export class OpenAIEmbedding
extends TransformComponent
implements BaseEmbedding
{
embedInfo?: EmbeddingInfo;
embedBatchSize = 512;
constructor() {
super(async (nodes: BaseNode[], _options?: any): Promise<BaseNode[]> => {
nodes.forEach((node) => (node.embedding = [0]));
return nodes;
});
}
async getQueryEmbedding(query: MessageContentDetail) {
return [0];
}
async getTextEmbedding(text: string) {
return [0];
}
async getTextEmbeddings(texts: string[]) {
return [[0]];
}
async getTextEmbeddingsBatch(texts: string[]) {
return [[0]];
}
similarity(
embedding1: number[],
embedding2: number[],
mode?: SimilarityType,
) {
return 1;
}
truncateMaxTokens(input: string[]): string[] {
return input;
}
}
+8 -2
View File
@@ -13,8 +13,14 @@ export async function resolve(specifier, context, nextResolve) {
return result;
}
const targetUrl = fileURLToPath(result.url).replace(/\.js$/, ".ts");
const relativePath = relative(packageDistDir, targetUrl);
if (relativePath.startsWith(".") || relativePath.startsWith("/")) {
let relativePath = relative(packageDistDir, targetUrl);
// todo: make it more generic if we have more sub modules fixtures in the future
if (relativePath.startsWith("../../llm/openai")) {
relativePath = relativePath.replace(
"../../llm/openai/dist/index.ts",
"llm/openai.ts",
);
} else if (relativePath.startsWith(".") || relativePath.startsWith("/")) {
return result;
}
const url = pathToFileURL(join(fixturesDir, relativePath)).toString();
@@ -0,0 +1,90 @@
import { Document, VectorStoreQueryMode } from "llamaindex";
import { PGVectorStore } from "llamaindex/vector-store/PGVectorStore";
import assert from "node:assert";
import { test } from "node:test";
import pg from "pg";
import { registerTypes } from "pgvector/pg";
let pgClient: pg.Client | pg.Pool;
test.afterEach(async () => {
await pgClient.end();
});
await test("init with client", async () => {
pgClient = new pg.Client({
database: "llamaindex_node_test",
});
await pgClient.connect();
await pgClient.query("CREATE EXTENSION IF NOT EXISTS vector");
await registerTypes(pgClient);
const vectorStore = new PGVectorStore(pgClient);
assert.deepStrictEqual(await vectorStore.client(), pgClient);
});
await test("init with pool", async () => {
pgClient = new pg.Pool({
database: "llamaindex_node_test",
});
await pgClient.query("CREATE EXTENSION IF NOT EXISTS vector");
const client = await pgClient.connect();
await registerTypes(client);
const vectorStore = new PGVectorStore(client);
assert.deepStrictEqual(await vectorStore.client(), client);
client.release();
});
await test("init without client", async () => {
const vectorStore = new PGVectorStore({
database: "llamaindex_node_test",
});
pgClient = (await vectorStore.client()) as pg.Client;
assert.notDeepStrictEqual(pgClient, undefined);
});
await test("simple node", async () => {
const dimensions = 3;
const schemaName =
"llamaindex_vector_store_test_" + Math.random().toString(36).substring(7);
const nodeId = "5bb16627-f6c0-459c-bb18-71642813ef21";
const node = new Document({
text: "hello world",
id_: nodeId,
embedding: [0.1, 0.2, 0.3],
});
const vectorStore = new PGVectorStore({
database: "llamaindex_node_test",
dimensions,
schemaName,
});
await vectorStore.add([node]);
{
const result = await vectorStore.query({
mode: VectorStoreQueryMode.DEFAULT,
similarityTopK: 1,
queryEmbedding: [1, 2, 3],
});
const actualJSON = result.nodes![0]!.toJSON();
assert.deepStrictEqual(actualJSON, {
...node.toJSON(),
hash: actualJSON.hash,
metadata: actualJSON.metadata,
});
assert.deepStrictEqual(result.ids, [nodeId]);
assert.deepStrictEqual(result.similarities, [1]);
}
await vectorStore.delete(nodeId);
{
const result = await vectorStore.query({
mode: VectorStoreQueryMode.DEFAULT,
similarityTopK: 1,
queryEmbedding: [1, 2, 3],
});
assert.deepStrictEqual(result.nodes, []);
}
pgClient = (await vectorStore.client()) as pg.Client;
});
-1
View File
@@ -10,7 +10,6 @@
},
"devDependencies": {
"@faker-js/faker": "^8.4.1",
"@llamaindex/core": "workspace:*",
"@types/node": "^22.5.1",
"consola": "^3.2.3",
"llamaindex": "workspace:*",
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "llamaindex",
"version": "0.5.25",
"version": "0.5.27",
"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,7 +57,7 @@
"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",
"portkey-ai": "0.1.16",
+1 -1
View File
@@ -5,7 +5,7 @@ import {
} from "@llamaindex/core/prompts";
import { extractText, messagesToHistory } from "@llamaindex/core/utils";
import { tokenizers, type Tokenizer } from "@llamaindex/env";
import { OpenAI } from "./llm/openai.js";
import { OpenAI } from "@llamaindex/openai";
/**
* A ChatHistory is used to keep the state of back and forth chat messages
+1 -1
View File
@@ -8,12 +8,12 @@ import {
import type { QueryType } from "@llamaindex/core/query-engine";
import type { BaseOutputParser } from "@llamaindex/core/schema";
import { extractText, toToolDescriptions } from "@llamaindex/core/utils";
import { OpenAI } from "@llamaindex/openai";
import { SubQuestionOutputParser } from "./OutputParser.js";
import type {
BaseQuestionGenerator,
SubQuestion,
} from "./engines/query/types.js";
import { OpenAI } from "./llm/openai.js";
import type { StructuredOutput } from "./types.js";
/**
+1 -2
View File
@@ -5,8 +5,7 @@ import {
type NodeParser,
SentenceSplitter,
} from "@llamaindex/core/node-parser";
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.
+1 -1
View File
@@ -2,7 +2,7 @@ import {
type CallbackManager,
Settings as CoreSettings,
} from "@llamaindex/core/global";
import { OpenAI } from "./llm/openai.js";
import { OpenAI } from "@llamaindex/openai";
import { PromptHelper } from "@llamaindex/core/indices";
+1 -1
View File
@@ -1,5 +1,5 @@
import { OpenAI } from "@llamaindex/openai";
import { Settings } from "../Settings.js";
import { OpenAI } from "../llm/openai.js";
import { LLMAgent, LLMAgentWorker, type LLMAgentParams } from "./llm.js";
// This is likely not necessary anymore but leaving it here just incase it's in use elsewhere
@@ -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;
+1 -1
View File
@@ -5,7 +5,7 @@ import type {
} from "@llamaindex/cloud/api";
import { SentenceSplitter } from "@llamaindex/core/node-parser";
import { BaseNode, type TransformComponent } from "@llamaindex/core/schema";
import { OpenAIEmbedding } from "../embeddings/OpenAIEmbedding.js";
import { OpenAIEmbedding } from "@llamaindex/openai";
export type GetPipelineCreateParams = {
pipelineName: string;
@@ -1,152 +1 @@
import { BaseEmbedding } from "@llamaindex/core/embeddings";
import { Tokenizers } from "@llamaindex/env";
import type { ClientOptions as OpenAIClientOptions } from "openai";
import type { AzureOpenAIConfig } from "../llm/azure.js";
import {
getAzureConfigFromEnv,
getAzureModel,
shouldUseAzure,
} from "../llm/azure.js";
import type { OpenAISession } from "../llm/openai.js";
import { getOpenAISession } from "../llm/openai.js";
export const ALL_OPENAI_EMBEDDING_MODELS = {
"text-embedding-ada-002": {
dimensions: 1536,
maxTokens: 8192,
tokenizer: Tokenizers.CL100K_BASE,
},
"text-embedding-3-small": {
dimensions: 1536,
dimensionOptions: [512, 1536],
maxTokens: 8192,
tokenizer: Tokenizers.CL100K_BASE,
},
"text-embedding-3-large": {
dimensions: 3072,
dimensionOptions: [256, 1024, 3072],
maxTokens: 8192,
tokenizer: Tokenizers.CL100K_BASE,
},
};
type ModelKeys = keyof typeof ALL_OPENAI_EMBEDDING_MODELS;
export class OpenAIEmbedding extends BaseEmbedding {
/** embeddding model. defaults to "text-embedding-ada-002" */
model: string;
/** number of dimensions of the resulting vector, for models that support choosing fewer dimensions. undefined will default to model default */
dimensions?: number | undefined;
// OpenAI session params
/** api key */
apiKey?: string | undefined = undefined;
/** maximum number of retries, default 10 */
maxRetries: number;
/** timeout in ms, default 60 seconds */
timeout?: number | undefined;
/** other session options for OpenAI */
additionalSessionOptions?:
| Omit<Partial<OpenAIClientOptions>, "apiKey" | "maxRetries" | "timeout">
| undefined;
/** session object */
session: OpenAISession;
/**
* OpenAI Embedding
* @param init - initial parameters
*/
constructor(init?: Partial<OpenAIEmbedding> & { azure?: AzureOpenAIConfig }) {
super();
this.model = init?.model ?? "text-embedding-ada-002";
this.dimensions = init?.dimensions; // if no dimensions provided, will be undefined/not sent to OpenAI
this.embedBatchSize = init?.embedBatchSize ?? 10;
this.maxRetries = init?.maxRetries ?? 10;
this.timeout = init?.timeout ?? 60 * 1000; // Default is 60 seconds
this.additionalSessionOptions = init?.additionalSessionOptions;
// find metadata for model
const key = Object.keys(ALL_OPENAI_EMBEDDING_MODELS).find(
(key) => key === this.model,
) as ModelKeys | undefined;
if (key) {
this.embedInfo = ALL_OPENAI_EMBEDDING_MODELS[key];
}
if (init?.azure || shouldUseAzure()) {
const azureConfig = {
...getAzureConfigFromEnv({
model: getAzureModel(this.model),
}),
...init?.azure,
};
this.apiKey = azureConfig.apiKey;
this.session =
init?.session ??
getOpenAISession({
azure: true,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
...azureConfig,
});
} else {
this.apiKey = init?.apiKey ?? undefined;
this.session =
init?.session ??
getOpenAISession({
apiKey: this.apiKey,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
});
}
}
/**
* Get embeddings for a batch of texts
* @param texts
* @param options
*/
private async getOpenAIEmbedding(input: string[]): Promise<number[][]> {
// TODO: ensure this for every sub class by calling it in the base class
input = this.truncateMaxTokens(input);
const { data } = await this.session.openai.embeddings.create(
this.dimensions
? {
model: this.model,
dimensions: this.dimensions, // only sent to OpenAI if set by user
input,
}
: {
model: this.model,
input,
},
);
return data.map((d) => d.embedding);
}
/**
* Get embeddings for a batch of texts
* @param texts
*/
getTextEmbeddings = async (texts: string[]): Promise<number[][]> => {
return this.getOpenAIEmbedding(texts);
};
/**
* Get embeddings for a single text
* @param texts
*/
async getTextEmbedding(text: string): Promise<number[]> {
return (await this.getOpenAIEmbedding([text]))[0]!;
}
}
export * from "@llamaindex/openai";
@@ -1,5 +1,5 @@
import { getEnv } from "@llamaindex/env";
import { OpenAIEmbedding } from "./OpenAIEmbedding.js";
import { OpenAIEmbedding } from "@llamaindex/openai";
export class FireworksEmbedding extends OpenAIEmbedding {
constructor(init?: Partial<OpenAIEmbedding>) {
@@ -1,5 +1,5 @@
import { getEnv } from "@llamaindex/env";
import { OpenAIEmbedding } from "./OpenAIEmbedding.js";
import { OpenAIEmbedding } from "@llamaindex/openai";
export class TogetherEmbedding extends OpenAIEmbedding {
constructor(init?: Partial<OpenAIEmbedding>) {
@@ -1,7 +1,7 @@
import type { LLM } from "@llamaindex/core/llms";
import type { BaseNode } from "@llamaindex/core/schema";
import { MetadataMode, TextNode } from "@llamaindex/core/schema";
import { OpenAI } from "../llm/index.js";
import { OpenAI } from "@llamaindex/openai";
import {
defaultKeywordExtractorPromptTemplate,
defaultQuestionAnswerPromptTemplate,
@@ -1,6 +1,6 @@
import type { BaseEmbedding } from "@llamaindex/core/embeddings";
import { AsyncLocalStorage } from "@llamaindex/env";
import { OpenAIEmbedding } from "../../embeddings/OpenAIEmbedding.js";
import { OpenAIEmbedding } from "@llamaindex/openai";
const embeddedModelAsyncLocalStorage = new AsyncLocalStorage<BaseEmbedding>();
let globalEmbeddedModel: BaseEmbedding | null = null;
+1 -1
View File
@@ -1,5 +1,5 @@
import { getEnv } from "@llamaindex/env";
import { OpenAI } from "./openai.js";
import { OpenAI } from "@llamaindex/openai";
const ENV_VARIABLE_NAME = "DEEPINFRA_API_TOKEN";
const DEFAULT_MODEL = "mistralai/Mixtral-8x22B-Instruct-v0.1";
+1 -1
View File
@@ -1,5 +1,5 @@
import { getEnv } from "@llamaindex/env";
import { OpenAI } from "./openai.js";
import { OpenAI } from "@llamaindex/openai";
export const DEEPSEEK_MODELS = {
"deepseek-coder": { contextWindow: 128000 },
+1 -1
View File
@@ -1,5 +1,5 @@
import { getEnv } from "@llamaindex/env";
import { OpenAI } from "./openai.js";
import { OpenAI } from "@llamaindex/openai";
export class FireworksLLM extends OpenAI {
constructor(init?: Partial<OpenAI>) {
+1 -1
View File
@@ -1,6 +1,6 @@
import { getEnv } from "@llamaindex/env";
import { OpenAI } from "@llamaindex/openai";
import GroqSDK, { type ClientOptions } from "groq-sdk";
import { OpenAI } from "./openai.js";
export class Groq extends OpenAI {
constructor(
-1
View File
@@ -11,7 +11,6 @@ export {
GEMINI_MODEL,
type GoogleGeminiSessionOptions,
} from "./gemini/types.js";
export { Groq } from "./groq.js";
export { HuggingFaceInferenceAPI, HuggingFaceLLM } from "./huggingface.js";
export {
+1 -482
View File
@@ -1,482 +1 @@
import { getEnv } from "@llamaindex/env";
import _ from "lodash";
import type OpenAILLM from "openai";
import type {
ClientOptions,
ClientOptions as OpenAIClientOptions,
} from "openai";
import { AzureOpenAI, OpenAI as OrigOpenAI } from "openai";
import type { ChatModel } from "openai/resources/chat/chat";
import {
type BaseTool,
type ChatMessage,
type ChatResponse,
type ChatResponseChunk,
type LLM,
type LLMChatParamsNonStreaming,
type LLMChatParamsStreaming,
type LLMMetadata,
type MessageType,
type PartialToolCall,
ToolCallLLM,
type ToolCallLLMMessageOptions,
} from "@llamaindex/core/llms";
import {
extractText,
wrapEventCaller,
wrapLLMEvent,
} from "@llamaindex/core/utils";
import { Tokenizers } from "@llamaindex/env";
import type {
ChatCompletionAssistantMessageParam,
ChatCompletionMessageToolCall,
ChatCompletionRole,
ChatCompletionSystemMessageParam,
ChatCompletionTool,
ChatCompletionToolMessageParam,
ChatCompletionUserMessageParam,
} from "openai/resources/chat/completions";
import type { ChatCompletionMessageParam } from "openai/resources/index.js";
import type { AzureOpenAIConfig } from "./azure.js";
import {
getAzureConfigFromEnv,
getAzureModel,
shouldUseAzure,
} from "./azure.js";
export class OpenAISession {
openai: Pick<OrigOpenAI, "chat" | "embeddings">;
constructor(options: ClientOptions & { azure?: boolean } = {}) {
if (options.azure) {
this.openai = new AzureOpenAI(options as AzureOpenAIConfig);
} else {
if (!options.apiKey) {
options.apiKey = getEnv("OPENAI_API_KEY");
}
if (!options.apiKey) {
throw new Error("Set OpenAI Key in OPENAI_API_KEY env variable"); // Overriding OpenAI package's error message
}
this.openai = new OrigOpenAI({
...options,
});
}
}
}
// I'm not 100% sure this is necessary vs. just starting a new session
// every time we make a call. They say they try to reuse connections
// so in theory this is more efficient, but we should test it in the future.
const defaultOpenAISession: {
session: OpenAISession;
options: ClientOptions;
}[] = [];
/**
* Get a session for the OpenAI API. If one already exists with the same options,
* it will be returned. Otherwise, a new session will be created.
* @param options
* @returns
*/
export function getOpenAISession(
options: ClientOptions & { azure?: boolean } = {},
) {
let session = defaultOpenAISession.find((session) => {
return _.isEqual(session.options, options);
})?.session;
if (!session) {
session = new OpenAISession(options);
defaultOpenAISession.push({ session, options });
}
return session;
}
export const GPT4_MODELS = {
"gpt-4": { contextWindow: 8192 },
"gpt-4-32k": { contextWindow: 32768 },
"gpt-4-32k-0613": { contextWindow: 32768 },
"gpt-4-turbo": { contextWindow: 128000 },
"gpt-4-turbo-preview": { contextWindow: 128000 },
"gpt-4-1106-preview": { contextWindow: 128000 },
"gpt-4-0125-preview": { contextWindow: 128000 },
"gpt-4-vision-preview": { contextWindow: 128000 },
"gpt-4o": { contextWindow: 128000 },
"gpt-4o-2024-05-13": { contextWindow: 128000 },
"gpt-4o-mini": { contextWindow: 128000 },
"gpt-4o-mini-2024-07-18": { contextWindow: 128000 },
"gpt-4o-2024-08-06": { contextWindow: 128000 },
"gpt-4o-2024-09-14": { contextWindow: 128000 },
"gpt-4o-2024-10-14": { contextWindow: 128000 },
"gpt-4-0613": { contextWindow: 128000 },
"gpt-4-turbo-2024-04-09": { contextWindow: 128000 },
"gpt-4-0314": { contextWindow: 128000 },
"gpt-4-32k-0314": { contextWindow: 32768 },
};
// NOTE we don't currently support gpt-3.5-turbo-instruct and don't plan to in the near future
export const GPT35_MODELS = {
"gpt-3.5-turbo": { contextWindow: 16385 },
"gpt-3.5-turbo-0613": { contextWindow: 4096 },
"gpt-3.5-turbo-16k": { contextWindow: 16385 },
"gpt-3.5-turbo-16k-0613": { contextWindow: 16385 },
"gpt-3.5-turbo-1106": { contextWindow: 16385 },
"gpt-3.5-turbo-0125": { contextWindow: 16385 },
"gpt-3.5-turbo-0301": { contextWindow: 16385 },
};
/**
* We currently support GPT-3.5 and GPT-4 models
*/
export const ALL_AVAILABLE_OPENAI_MODELS = {
...GPT4_MODELS,
...GPT35_MODELS,
} satisfies Record<ChatModel, { contextWindow: number }>;
export function isFunctionCallingModel(llm: LLM): llm is OpenAI {
let model: string;
if (llm instanceof OpenAI) {
model = llm.model;
} else if ("model" in llm && typeof llm.model === "string") {
model = llm.model;
} else {
return false;
}
const isChatModel = Object.keys(ALL_AVAILABLE_OPENAI_MODELS).includes(model);
const isOld = model.includes("0314") || model.includes("0301");
return isChatModel && !isOld;
}
export type OpenAIAdditionalMetadata = {};
export type OpenAIAdditionalChatOptions = Omit<
Partial<OpenAILLM.Chat.ChatCompletionCreateParams>,
| "max_tokens"
| "messages"
| "model"
| "temperature"
| "top_p"
| "stream"
| "tools"
| "toolChoice"
>;
export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> {
model:
| ChatModel
// string & {} is a hack to allow any string, but still give autocomplete
| (string & {});
temperature: number;
topP: number;
maxTokens?: number | undefined;
additionalChatOptions?: OpenAIAdditionalChatOptions | undefined;
// OpenAI session params
apiKey?: string | undefined = undefined;
maxRetries: number;
timeout?: number;
session: OpenAISession;
additionalSessionOptions?:
| undefined
| Omit<Partial<OpenAIClientOptions>, "apiKey" | "maxRetries" | "timeout">;
constructor(
init?: Partial<OpenAI> & {
azure?: AzureOpenAIConfig;
},
) {
super();
this.model = init?.model ?? "gpt-4o";
this.temperature = init?.temperature ?? 0.1;
this.topP = init?.topP ?? 1;
this.maxTokens = init?.maxTokens ?? undefined;
this.maxRetries = init?.maxRetries ?? 10;
this.timeout = init?.timeout ?? 60 * 1000; // Default is 60 seconds
this.additionalChatOptions = init?.additionalChatOptions;
this.additionalSessionOptions = init?.additionalSessionOptions;
if (init?.azure || shouldUseAzure()) {
const azureConfig = {
...getAzureConfigFromEnv({
model: getAzureModel(this.model),
}),
...init?.azure,
};
this.apiKey = azureConfig.apiKey;
this.session =
init?.session ??
getOpenAISession({
azure: true,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
...azureConfig,
});
} else {
this.apiKey = init?.apiKey ?? undefined;
this.session =
init?.session ??
getOpenAISession({
apiKey: this.apiKey,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
});
}
}
get supportToolCall() {
return isFunctionCallingModel(this);
}
get metadata(): LLMMetadata & OpenAIAdditionalMetadata {
const contextWindow =
ALL_AVAILABLE_OPENAI_MODELS[
this.model as keyof typeof ALL_AVAILABLE_OPENAI_MODELS
]?.contextWindow ?? 1024;
return {
model: this.model,
temperature: this.temperature,
topP: this.topP,
maxTokens: this.maxTokens,
contextWindow,
tokenizer: Tokenizers.CL100K_BASE,
};
}
static toOpenAIRole(messageType: MessageType): ChatCompletionRole {
switch (messageType) {
case "user":
return "user";
case "assistant":
return "assistant";
case "system":
return "system";
default:
return "user";
}
}
static toOpenAIMessage(
messages: ChatMessage<ToolCallLLMMessageOptions>[],
): ChatCompletionMessageParam[] {
return messages.map((message) => {
const options = message.options ?? {};
if ("toolResult" in options) {
return {
tool_call_id: options.toolResult.id,
role: "tool",
content: extractText(message.content),
} satisfies ChatCompletionToolMessageParam;
} else if ("toolCall" in options) {
return {
role: "assistant",
content: extractText(message.content),
tool_calls: options.toolCall.map((toolCall) => {
return {
id: toolCall.id,
type: "function",
function: {
name: toolCall.name,
arguments:
typeof toolCall.input === "string"
? toolCall.input
: JSON.stringify(toolCall.input),
},
};
}),
} satisfies ChatCompletionAssistantMessageParam;
} else if (message.role === "user") {
return {
role: "user",
content: message.content,
} satisfies ChatCompletionUserMessageParam;
}
const response:
| ChatCompletionSystemMessageParam
| ChatCompletionUserMessageParam
| ChatCompletionMessageToolCall = {
// fixme(alex): type assertion
role: OpenAI.toOpenAIRole(message.role) as never,
// fixme: should not extract text, but assert content is string
content: extractText(message.content),
};
return response;
});
}
chat(
params: LLMChatParamsStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>,
): Promise<AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>>>;
chat(
params: LLMChatParamsNonStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>,
): Promise<ChatResponse<ToolCallLLMMessageOptions>>;
@wrapEventCaller
@wrapLLMEvent
async chat(
params:
| LLMChatParamsNonStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>
| LLMChatParamsStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>,
): Promise<
| ChatResponse<ToolCallLLMMessageOptions>
| AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>>
> {
const { messages, stream, tools, additionalChatOptions } = params;
const baseRequestParams = <OpenAILLM.Chat.ChatCompletionCreateParams>{
model: this.model,
temperature: this.temperature,
max_tokens: this.maxTokens,
tools: tools?.map(OpenAI.toTool),
messages: OpenAI.toOpenAIMessage(messages),
top_p: this.topP,
...Object.assign({}, this.additionalChatOptions, additionalChatOptions),
};
if (
Array.isArray(baseRequestParams.tools) &&
baseRequestParams.tools.length === 0
) {
// remove empty tools array to avoid OpenAI error
delete baseRequestParams.tools;
}
// Streaming
if (stream) {
return this.streamChat(baseRequestParams);
}
// Non-streaming
const response = await this.session.openai.chat.completions.create({
...baseRequestParams,
stream: false,
});
const content = response.choices[0]!.message?.content ?? "";
return {
raw: response,
message: {
content,
role: response.choices[0]!.message.role,
options: response.choices[0]!.message?.tool_calls
? {
toolCall: response.choices[0]!.message.tool_calls.map(
(toolCall) => ({
id: toolCall.id,
name: toolCall.function.name,
input: toolCall.function.arguments,
}),
),
}
: {},
},
};
}
// todo: this wrapper is ugly, refactor it
@wrapEventCaller
protected async *streamChat(
baseRequestParams: OpenAILLM.Chat.ChatCompletionCreateParams,
): AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>> {
const stream: AsyncIterable<OpenAILLM.Chat.ChatCompletionChunk> =
await this.session.openai.chat.completions.create({
...baseRequestParams,
stream: true,
});
// TODO: add callback to streamConverter and use streamConverter here
// this will be used to keep track of the current tool call, make sure input are valid json object.
let currentToolCall: PartialToolCall | null = null;
const toolCallMap = new Map<string, PartialToolCall>();
for await (const part of stream) {
if (part.choices.length === 0) continue;
const choice = part.choices[0]!;
// skip parts that don't have any content
if (!(choice.delta.content || choice.delta.tool_calls)) continue;
let shouldEmitToolCall: PartialToolCall | null = null;
if (
choice.delta.tool_calls?.[0]!.id &&
currentToolCall &&
choice.delta.tool_calls?.[0].id !== currentToolCall.id
) {
shouldEmitToolCall = {
...currentToolCall,
input: JSON.parse(currentToolCall.input),
};
}
if (choice.delta.tool_calls?.[0]!.id) {
currentToolCall = {
name: choice.delta.tool_calls[0].function!.name!,
id: choice.delta.tool_calls[0].id,
input: choice.delta.tool_calls[0].function!.arguments!,
};
toolCallMap.set(choice.delta.tool_calls[0].id, currentToolCall);
} else {
if (choice.delta.tool_calls?.[0]!.function?.arguments) {
currentToolCall!.input +=
choice.delta.tool_calls[0].function.arguments;
}
}
const isDone: boolean = choice.finish_reason !== null;
if (isDone && currentToolCall) {
// for the last one, we need to emit the tool call
shouldEmitToolCall = {
...currentToolCall,
input: JSON.parse(currentToolCall.input),
};
}
yield {
raw: part,
options: shouldEmitToolCall
? { toolCall: [shouldEmitToolCall] }
: currentToolCall
? {
toolCall: [currentToolCall],
}
: {},
delta: choice.delta.content ?? "",
};
}
toolCallMap.clear();
return;
}
static toTool(tool: BaseTool): ChatCompletionTool {
return {
type: "function",
function: tool.metadata.parameters
? {
name: tool.metadata.name,
description: tool.metadata.description,
parameters: tool.metadata.parameters,
}
: {
name: tool.metadata.name,
description: tool.metadata.description,
},
};
}
}
export * from "@llamaindex/openai";
+1 -1
View File
@@ -1,5 +1,5 @@
import { getEnv } from "@llamaindex/env";
import { OpenAI } from "./openai.js";
import { OpenAI } from "@llamaindex/openai";
export class TogetherLLM extends OpenAI {
constructor(init?: Partial<OpenAI>) {
@@ -0,0 +1,21 @@
import { DEFAULT_NAMESPACE } from "@llamaindex/core/global";
import { PostgresKVStore } from "../kvStore/PostgresKVStore.js";
import { KVDocumentStore } from "./KVDocumentStore.js";
const DEFAULT_TABLE_NAME = "llamaindex_doc_store";
export class PostgresDocumentStore extends KVDocumentStore {
constructor(config?: {
schemaName?: string;
tableName?: string;
connectionString?: string;
namespace?: string;
}) {
const kvStore = new PostgresKVStore({
schemaName: config?.schemaName,
tableName: config?.tableName || DEFAULT_TABLE_NAME,
});
const namespace = config?.namespace || DEFAULT_NAMESPACE;
super(kvStore, namespace);
}
}
+3
View File
@@ -1,10 +1,13 @@
export { SimpleChatStore } from "./chatStore/SimpleChatStore.js";
export * from "./chatStore/types.js";
export { PostgresDocumentStore } from "./docStore/PostgresDocumentStore.js";
export { SimpleDocumentStore } from "./docStore/SimpleDocumentStore.js";
export * from "./docStore/types.js";
export * from "./FileSystem.js";
export { PostgresIndexStore } from "./indexStore/PostgresIndexStore.js";
export { SimpleIndexStore } from "./indexStore/SimpleIndexStore.js";
export * from "./indexStore/types.js";
export { PostgresKVStore } from "./kvStore/PostgresKVStore.js";
export { SimpleKVStore } from "./kvStore/SimpleKVStore.js";
export * from "./kvStore/types.js";
export * from "./StorageContext.js";
@@ -0,0 +1,21 @@
import { DEFAULT_NAMESPACE } from "@llamaindex/core/global";
import { PostgresKVStore } from "../kvStore/PostgresKVStore.js";
import { KVIndexStore } from "./KVIndexStore.js";
const DEFAULT_TABLE_NAME = "llamaindex_index_store";
export class PostgresIndexStore extends KVIndexStore {
constructor(config?: {
schemaName?: string;
tableName?: string;
connectionString?: string;
namespace?: string;
}) {
const kvStore = new PostgresKVStore({
schemaName: config?.schemaName,
tableName: config?.tableName || DEFAULT_TABLE_NAME,
});
const namespace = config?.namespace || DEFAULT_NAMESPACE;
super(kvStore, namespace);
}
}
@@ -0,0 +1,140 @@
import { DEFAULT_COLLECTION } from "@llamaindex/core/global";
import type pg from "pg";
import { BaseKVStore } from "./types.js";
export type DataType = Record<string, Record<string, any>>;
const DEFAULT_SCHEMA_NAME = "public";
const DEFAULT_TABLE_NAME = "llamaindex_kv_store";
export class PostgresKVStore extends BaseKVStore {
private schemaName: string;
private tableName: string;
private connectionString: string | undefined = undefined;
private db?: pg.Client;
constructor(config?: {
schemaName?: string | undefined;
tableName?: string | undefined;
connectionString?: string | undefined;
}) {
super();
this.schemaName = config?.schemaName || DEFAULT_SCHEMA_NAME;
this.tableName = config?.tableName || DEFAULT_TABLE_NAME;
this.connectionString = config?.connectionString;
}
private async getDb(): Promise<pg.Client> {
if (!this.db) {
try {
const pg = await import("pg");
const { Client } = pg.default ? pg.default : pg;
const db = new Client({ connectionString: this.connectionString });
await db.connect();
await this.checkSchema(db);
this.db = db;
} catch (err) {
console.error(err);
return Promise.reject(err instanceof Error ? err : new Error(`${err}`));
}
}
return Promise.resolve(this.db);
}
private async checkSchema(db: pg.Client) {
await db.query(`CREATE SCHEMA IF NOT EXISTS ${this.schemaName}`);
const tbl = `CREATE TABLE IF NOT EXISTS ${this.schemaName}.${this.tableName} (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
collection VARCHAR,
key VARCHAR,
value JSONB DEFAULT '{}'
)`;
await db.query(tbl);
const idxs = `CREATE INDEX IF NOT EXISTS idx_${this.tableName}_collection ON ${this.schemaName}.${this.tableName} (collection);
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_key ON ${this.schemaName}.${this.tableName} (key);`;
await db.query(idxs);
return db;
}
client() {
return this.getDb();
}
async put(
key: string,
val: any,
collection: string = DEFAULT_COLLECTION,
): Promise<void> {
const db = await this.getDb();
try {
await db.query("BEGIN");
const sql = `
INSERT INTO ${this.schemaName}.${this.tableName}
(collection, key, value)
VALUES ($1, $2, $3)
ON CONFLICT (id) DO UPDATE SET
collection = EXCLUDED.collection,
key = EXCLUDED.key,
value = EXCLUDED.value
RETURNING id
`;
const values = [collection, key, val];
await db.query(sql, values);
await db.query("COMMIT");
} catch (error) {
await db.query("ROLLBACK");
throw error;
}
}
async get(
key: string,
collection: string = DEFAULT_COLLECTION,
): Promise<any> {
const db = await this.getDb();
try {
await db.query("BEGIN");
const sql = `SELECT * FROM ${this.schemaName}.${this.tableName} WHERE key = $1 AND collection = $2`;
const result = await db.query(sql, [key, collection]);
await db.query("COMMIT");
return result.rows[0].value;
} catch (error) {
await db.query("ROLLBACK");
throw error;
}
}
async getAll(collection: string = DEFAULT_COLLECTION): Promise<DataType> {
const db = await this.getDb();
try {
await db.query("BEGIN");
const sql = `SELECT * FROM ${this.schemaName}.${this.tableName} WHERE collection = $1`;
const result = await db.query(sql, [collection]);
await db.query("COMMIT");
return result.rows.reduce((acc, row) => {
acc[row.key] = row.value;
return acc;
}, {});
} catch (error) {
await db.query("ROLLBACK");
throw error;
}
}
async delete(
key: string,
collection: string = DEFAULT_COLLECTION,
): Promise<boolean> {
const db = await this.getDb();
try {
await db.query("BEGIN");
const sql = `DELETE FROM ${this.schemaName}.${this.tableName} WHERE key = $1 AND collection = $2`;
const result = await db.query(sql, [key, collection]);
await db.query("COMMIT");
return !!result.rowCount && result.rowCount > 0;
} catch (error) {
await db.query("ROLLBACK");
throw error;
}
}
}
@@ -1,85 +0,0 @@
import { Document } from "llamaindex";
import { PGVectorStore } from "llamaindex/vector-store/PGVectorStore";
import pg from "pg";
import { registerTypes } from "pgvector/pg";
import { describe, expect, test } from "vitest";
import { VectorStoreQueryMode } from "../../src/index.js";
describe("pg - init", () => {
test("init with client", async () => {
const client = new pg.Client({
database: "llamaindex_node_test",
});
await client.connect();
await client.query("CREATE EXTENSION IF NOT EXISTS vector");
await registerTypes(client);
const vectorStore = new PGVectorStore(client);
expect(await vectorStore.client()).toBe(client);
});
test("init with pool", async () => {
const pool = new pg.Pool({
database: "llamaindex_node_test",
});
await pool.query("CREATE EXTENSION IF NOT EXISTS vector");
const client = await pool.connect();
await registerTypes(client);
const vectorStore = new PGVectorStore(client);
expect(await vectorStore.client()).toBe(client);
});
test("init without client", async () => {
const vectorStore = new PGVectorStore({
database: "llamaindex_node_test",
});
expect(await vectorStore.client()).toBeDefined();
});
});
describe("pg - save data", () => {
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();
expect(actualJSON).toEqual({
...node.toJSON(),
hash: actualJSON.hash,
metadata: actualJSON.metadata,
});
expect(result.ids).toEqual([nodeId]);
expect(result.similarities).toEqual([1]);
}
await vectorStore.delete(nodeId);
{
const result = await vectorStore.query({
mode: VectorStoreQueryMode.DEFAULT,
similarityTopK: 1,
queryEmbedding: [1, 2, 3],
});
expect(result.nodes).toEqual([]);
}
});
});
+9
View File
@@ -0,0 +1,9 @@
# @llamaindex/openai
## 0.1.1
### 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
+42
View File
@@ -0,0 +1,42 @@
{
"name": "@llamaindex/openai",
"description": "OpenAI Adapter for LlamaIndex",
"version": "0.1.1",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
},
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/run-llama/LlamaIndexTS.git",
"directory": "packages/llm/openai"
},
"private": true,
"scripts": {
"build": "bunchee",
"dev": "bunchee --watch"
},
"devDependencies": {
"bunchee": "5.3.2"
},
"dependencies": {
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*",
"openai": "^4.60.0",
"remeda": "^2.12.0"
}
}
+152
View File
@@ -0,0 +1,152 @@
import { BaseEmbedding } from "@llamaindex/core/embeddings";
import { Tokenizers } from "@llamaindex/env";
import type { ClientOptions as OpenAIClientOptions } from "openai";
import type { AzureOpenAIConfig } from "./azure.js";
import {
getAzureConfigFromEnv,
getAzureModel,
shouldUseAzure,
} from "./azure.js";
import type { OpenAISession } from "./llm.js";
import { getOpenAISession } from "./llm.js";
export const ALL_OPENAI_EMBEDDING_MODELS = {
"text-embedding-ada-002": {
dimensions: 1536,
maxTokens: 8192,
tokenizer: Tokenizers.CL100K_BASE,
},
"text-embedding-3-small": {
dimensions: 1536,
dimensionOptions: [512, 1536],
maxTokens: 8192,
tokenizer: Tokenizers.CL100K_BASE,
},
"text-embedding-3-large": {
dimensions: 3072,
dimensionOptions: [256, 1024, 3072],
maxTokens: 8192,
tokenizer: Tokenizers.CL100K_BASE,
},
};
type ModelKeys = keyof typeof ALL_OPENAI_EMBEDDING_MODELS;
export class OpenAIEmbedding extends BaseEmbedding {
/** embeddding model. defaults to "text-embedding-ada-002" */
model: string;
/** number of dimensions of the resulting vector, for models that support choosing fewer dimensions. undefined will default to model default */
dimensions?: number | undefined;
// OpenAI session params
/** api key */
apiKey?: string | undefined = undefined;
/** maximum number of retries, default 10 */
maxRetries: number;
/** timeout in ms, default 60 seconds */
timeout?: number | undefined;
/** other session options for OpenAI */
additionalSessionOptions?:
| Omit<Partial<OpenAIClientOptions>, "apiKey" | "maxRetries" | "timeout">
| undefined;
/** session object */
session: OpenAISession;
/**
* OpenAI Embedding
* @param init - initial parameters
*/
constructor(init?: Partial<OpenAIEmbedding> & { azure?: AzureOpenAIConfig }) {
super();
this.model = init?.model ?? "text-embedding-ada-002";
this.dimensions = init?.dimensions; // if no dimensions provided, will be undefined/not sent to OpenAI
this.embedBatchSize = init?.embedBatchSize ?? 10;
this.maxRetries = init?.maxRetries ?? 10;
this.timeout = init?.timeout ?? 60 * 1000; // Default is 60 seconds
this.additionalSessionOptions = init?.additionalSessionOptions;
// find metadata for model
const key = Object.keys(ALL_OPENAI_EMBEDDING_MODELS).find(
(key) => key === this.model,
) as ModelKeys | undefined;
if (key) {
this.embedInfo = ALL_OPENAI_EMBEDDING_MODELS[key];
}
if (init?.azure || shouldUseAzure()) {
const azureConfig = {
...getAzureConfigFromEnv({
model: getAzureModel(this.model),
}),
...init?.azure,
};
this.apiKey = azureConfig.apiKey;
this.session =
init?.session ??
getOpenAISession({
azure: true,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
...azureConfig,
});
} else {
this.apiKey = init?.apiKey ?? undefined;
this.session =
init?.session ??
getOpenAISession({
apiKey: this.apiKey,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
});
}
}
/**
* Get embeddings for a batch of texts
* @param texts
* @param options
*/
private async getOpenAIEmbedding(input: string[]): Promise<number[][]> {
// TODO: ensure this for every sub class by calling it in the base class
input = this.truncateMaxTokens(input);
const { data } = await this.session.openai.embeddings.create(
this.dimensions
? {
model: this.model,
dimensions: this.dimensions, // only sent to OpenAI if set by user
input,
}
: {
model: this.model,
input,
},
);
return data.map((d) => d.embedding);
}
/**
* Get embeddings for a batch of texts
* @param texts
*/
getTextEmbeddings = async (texts: string[]): Promise<number[][]> => {
return this.getOpenAIEmbedding(texts);
};
/**
* Get embeddings for a single text
* @param texts
*/
async getTextEmbedding(text: string): Promise<number[]> {
return (await this.getOpenAIEmbedding([text]))[0]!;
}
}
+13
View File
@@ -0,0 +1,13 @@
export { ALL_OPENAI_EMBEDDING_MODELS, OpenAIEmbedding } from "./embedding";
export {
ALL_AVAILABLE_OPENAI_MODELS,
GPT35_MODELS,
GPT4_MODELS,
O1_MODELS,
OpenAI,
OpenAISession,
type OpenAIAdditionalChatOptions,
type OpenAIAdditionalMetadata,
} from "./llm";
export { type AzureOpenAIConfig } from "./azure";
+502
View File
@@ -0,0 +1,502 @@
import { getEnv } from "@llamaindex/env";
import type OpenAILLM from "openai";
import type {
ClientOptions,
ClientOptions as OpenAIClientOptions,
} from "openai";
import { AzureOpenAI, OpenAI as OrigOpenAI } from "openai";
import type { ChatModel } from "openai/resources/chat/chat";
import { isDeepEqual } from "remeda";
import {
type BaseTool,
type ChatMessage,
type ChatResponse,
type ChatResponseChunk,
type LLM,
type LLMChatParamsNonStreaming,
type LLMChatParamsStreaming,
type LLMMetadata,
type MessageType,
type PartialToolCall,
ToolCallLLM,
type ToolCallLLMMessageOptions,
} from "@llamaindex/core/llms";
import {
extractText,
wrapEventCaller,
wrapLLMEvent,
} from "@llamaindex/core/utils";
import { Tokenizers } from "@llamaindex/env";
import type {
ChatCompletionAssistantMessageParam,
ChatCompletionMessageToolCall,
ChatCompletionRole,
ChatCompletionSystemMessageParam,
ChatCompletionTool,
ChatCompletionToolMessageParam,
ChatCompletionUserMessageParam,
} from "openai/resources/chat/completions";
import type { ChatCompletionMessageParam } from "openai/resources/index.js";
import type { AzureOpenAIConfig } from "./azure.js";
import {
getAzureConfigFromEnv,
getAzureModel,
shouldUseAzure,
} from "./azure.js";
export class OpenAISession {
openai: Pick<OrigOpenAI, "chat" | "embeddings">;
constructor(options: ClientOptions & { azure?: boolean } = {}) {
if (options.azure) {
this.openai = new AzureOpenAI(options as AzureOpenAIConfig);
} else {
if (!options.apiKey) {
options.apiKey = getEnv("OPENAI_API_KEY");
}
if (!options.apiKey) {
throw new Error("Set OpenAI Key in OPENAI_API_KEY env variable"); // Overriding OpenAI package's error message
}
this.openai = new OrigOpenAI({
...options,
});
}
}
}
// I'm not 100% sure this is necessary vs. just starting a new session
// every time we make a call. They say they try to reuse connections
// so in theory this is more efficient, but we should test it in the future.
const defaultOpenAISession: {
session: OpenAISession;
options: ClientOptions;
}[] = [];
/**
* Get a session for the OpenAI API. If one already exists with the same options,
* it will be returned. Otherwise, a new session will be created.
* @param options
* @returns
*/
export function getOpenAISession(
options: ClientOptions & { azure?: boolean } = {},
) {
let session = defaultOpenAISession.find((session) => {
return isDeepEqual(session.options, options);
})?.session;
if (!session) {
session = new OpenAISession(options);
defaultOpenAISession.push({ session, options });
}
return session;
}
export const GPT4_MODELS = {
"chatgpt-4o-latest": {
contextWindow: 128000,
},
"gpt-4": { contextWindow: 8192 },
"gpt-4-32k": { contextWindow: 32768 },
"gpt-4-32k-0613": { contextWindow: 32768 },
"gpt-4-turbo": { contextWindow: 128000 },
"gpt-4-turbo-preview": { contextWindow: 128000 },
"gpt-4-1106-preview": { contextWindow: 128000 },
"gpt-4-0125-preview": { contextWindow: 128000 },
"gpt-4-vision-preview": { contextWindow: 128000 },
"gpt-4o": { contextWindow: 128000 },
"gpt-4o-2024-05-13": { contextWindow: 128000 },
"gpt-4o-mini": { contextWindow: 128000 },
"gpt-4o-mini-2024-07-18": { contextWindow: 128000 },
"gpt-4o-2024-08-06": { contextWindow: 128000 },
"gpt-4o-2024-09-14": { contextWindow: 128000 },
"gpt-4o-2024-10-14": { contextWindow: 128000 },
"gpt-4-0613": { contextWindow: 128000 },
"gpt-4-turbo-2024-04-09": { contextWindow: 128000 },
"gpt-4-0314": { contextWindow: 128000 },
"gpt-4-32k-0314": { contextWindow: 32768 },
};
// NOTE we don't currently support gpt-3.5-turbo-instruct and don't plan to in the near future
export const GPT35_MODELS = {
"gpt-3.5-turbo": { contextWindow: 16385 },
"gpt-3.5-turbo-0613": { contextWindow: 4096 },
"gpt-3.5-turbo-16k": { contextWindow: 16385 },
"gpt-3.5-turbo-16k-0613": { contextWindow: 16385 },
"gpt-3.5-turbo-1106": { contextWindow: 16385 },
"gpt-3.5-turbo-0125": { contextWindow: 16385 },
"gpt-3.5-turbo-0301": { contextWindow: 16385 },
};
export const O1_MODELS = {
"o1-preview": {
contextWindow: 128000,
},
"o1-preview-2024-09-12": {
contextWindow: 128000,
},
"o1-mini": {
contextWindow: 128000,
},
"o1-mini-2024-09-12": {
contextWindow: 128000,
},
};
/**
* We currently support GPT-3.5 and GPT-4 models
*/
export const ALL_AVAILABLE_OPENAI_MODELS = {
...GPT4_MODELS,
...GPT35_MODELS,
...O1_MODELS,
} satisfies Record<ChatModel, { contextWindow: number }>;
export function isFunctionCallingModel(llm: LLM): llm is OpenAI {
let model: string;
if (llm instanceof OpenAI) {
model = llm.model;
} else if ("model" in llm && typeof llm.model === "string") {
model = llm.model;
} else {
return false;
}
const isChatModel = Object.keys(ALL_AVAILABLE_OPENAI_MODELS).includes(model);
const isOld = model.includes("0314") || model.includes("0301");
const isO1 = model.startsWith("o1");
return isChatModel && !isOld && !isO1;
}
export type OpenAIAdditionalMetadata = {};
export type OpenAIAdditionalChatOptions = Omit<
Partial<OpenAILLM.Chat.ChatCompletionCreateParams>,
| "max_tokens"
| "messages"
| "model"
| "temperature"
| "top_p"
| "stream"
| "tools"
| "toolChoice"
>;
export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> {
model:
| ChatModel
// string & {} is a hack to allow any string, but still give autocomplete
| (string & {});
temperature: number;
topP: number;
maxTokens?: number | undefined;
additionalChatOptions?: OpenAIAdditionalChatOptions | undefined;
// OpenAI session params
apiKey?: string | undefined = undefined;
maxRetries: number;
timeout?: number;
session: OpenAISession;
additionalSessionOptions?:
| undefined
| Omit<Partial<OpenAIClientOptions>, "apiKey" | "maxRetries" | "timeout">;
constructor(
init?: Partial<OpenAI> & {
azure?: AzureOpenAIConfig;
},
) {
super();
this.model = init?.model ?? "gpt-4o";
this.temperature = init?.temperature ?? 0.1;
this.topP = init?.topP ?? 1;
this.maxTokens = init?.maxTokens ?? undefined;
this.maxRetries = init?.maxRetries ?? 10;
this.timeout = init?.timeout ?? 60 * 1000; // Default is 60 seconds
this.additionalChatOptions = init?.additionalChatOptions;
this.additionalSessionOptions = init?.additionalSessionOptions;
if (init?.azure || shouldUseAzure()) {
const azureConfig = {
...getAzureConfigFromEnv({
model: getAzureModel(this.model),
}),
...init?.azure,
};
this.apiKey = azureConfig.apiKey;
this.session =
init?.session ??
getOpenAISession({
azure: true,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
...azureConfig,
});
} else {
this.apiKey = init?.apiKey ?? undefined;
this.session =
init?.session ??
getOpenAISession({
apiKey: this.apiKey,
maxRetries: this.maxRetries,
timeout: this.timeout,
...this.additionalSessionOptions,
});
}
}
get supportToolCall() {
return isFunctionCallingModel(this);
}
get metadata(): LLMMetadata & OpenAIAdditionalMetadata {
const contextWindow =
ALL_AVAILABLE_OPENAI_MODELS[
this.model as keyof typeof ALL_AVAILABLE_OPENAI_MODELS
]?.contextWindow ?? 1024;
return {
model: this.model,
temperature: this.temperature,
topP: this.topP,
maxTokens: this.maxTokens,
contextWindow,
tokenizer: Tokenizers.CL100K_BASE,
};
}
static toOpenAIRole(messageType: MessageType): ChatCompletionRole {
switch (messageType) {
case "user":
return "user";
case "assistant":
return "assistant";
case "system":
return "system";
default:
return "user";
}
}
static toOpenAIMessage(
messages: ChatMessage<ToolCallLLMMessageOptions>[],
): ChatCompletionMessageParam[] {
return messages.map((message) => {
const options = message.options ?? {};
if ("toolResult" in options) {
return {
tool_call_id: options.toolResult.id,
role: "tool",
content: extractText(message.content),
} satisfies ChatCompletionToolMessageParam;
} else if ("toolCall" in options) {
return {
role: "assistant",
content: extractText(message.content),
tool_calls: options.toolCall.map((toolCall) => {
return {
id: toolCall.id,
type: "function",
function: {
name: toolCall.name,
arguments:
typeof toolCall.input === "string"
? toolCall.input
: JSON.stringify(toolCall.input),
},
};
}),
} satisfies ChatCompletionAssistantMessageParam;
} else if (message.role === "user") {
return {
role: "user",
content: message.content,
} satisfies ChatCompletionUserMessageParam;
}
const response:
| ChatCompletionSystemMessageParam
| ChatCompletionUserMessageParam
| ChatCompletionMessageToolCall = {
// fixme(alex): type assertion
role: OpenAI.toOpenAIRole(message.role) as never,
// fixme: should not extract text, but assert content is string
content: extractText(message.content),
};
return response;
});
}
chat(
params: LLMChatParamsStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>,
): Promise<AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>>>;
chat(
params: LLMChatParamsNonStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>,
): Promise<ChatResponse<ToolCallLLMMessageOptions>>;
@wrapEventCaller
@wrapLLMEvent
async chat(
params:
| LLMChatParamsNonStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>
| LLMChatParamsStreaming<
OpenAIAdditionalChatOptions,
ToolCallLLMMessageOptions
>,
): Promise<
| ChatResponse<ToolCallLLMMessageOptions>
| AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>>
> {
const { messages, stream, tools, additionalChatOptions } = params;
const baseRequestParams = <OpenAILLM.Chat.ChatCompletionCreateParams>{
model: this.model,
temperature: this.temperature,
max_tokens: this.maxTokens,
tools: tools?.map(OpenAI.toTool),
messages: OpenAI.toOpenAIMessage(messages),
top_p: this.topP,
...Object.assign({}, this.additionalChatOptions, additionalChatOptions),
};
if (
Array.isArray(baseRequestParams.tools) &&
baseRequestParams.tools.length === 0
) {
// remove empty tools array to avoid OpenAI error
delete baseRequestParams.tools;
}
// Streaming
if (stream) {
return this.streamChat(baseRequestParams);
}
// Non-streaming
const response = await this.session.openai.chat.completions.create({
...baseRequestParams,
stream: false,
});
const content = response.choices[0]!.message?.content ?? "";
return {
raw: response,
message: {
content,
role: response.choices[0]!.message.role,
options: response.choices[0]!.message?.tool_calls
? {
toolCall: response.choices[0]!.message.tool_calls.map(
(toolCall) => ({
id: toolCall.id,
name: toolCall.function.name,
input: toolCall.function.arguments,
}),
),
}
: {},
},
};
}
// todo: this wrapper is ugly, refactor it
@wrapEventCaller
protected async *streamChat(
baseRequestParams: OpenAILLM.Chat.ChatCompletionCreateParams,
): AsyncIterable<ChatResponseChunk<ToolCallLLMMessageOptions>> {
const stream: AsyncIterable<OpenAILLM.Chat.ChatCompletionChunk> =
await this.session.openai.chat.completions.create({
...baseRequestParams,
stream: true,
});
// TODO: add callback to streamConverter and use streamConverter here
// this will be used to keep track of the current tool call, make sure input are valid json object.
let currentToolCall: PartialToolCall | null = null;
const toolCallMap = new Map<string, PartialToolCall>();
for await (const part of stream) {
if (part.choices.length === 0) continue;
const choice = part.choices[0]!;
// skip parts that don't have any content
if (!(choice.delta.content || choice.delta.tool_calls)) continue;
let shouldEmitToolCall: PartialToolCall | null = null;
if (
choice.delta.tool_calls?.[0]!.id &&
currentToolCall &&
choice.delta.tool_calls?.[0].id !== currentToolCall.id
) {
shouldEmitToolCall = {
...currentToolCall,
input: JSON.parse(currentToolCall.input),
};
}
if (choice.delta.tool_calls?.[0]!.id) {
currentToolCall = {
name: choice.delta.tool_calls[0].function!.name!,
id: choice.delta.tool_calls[0].id,
input: choice.delta.tool_calls[0].function!.arguments!,
};
toolCallMap.set(choice.delta.tool_calls[0].id, currentToolCall);
} else {
if (choice.delta.tool_calls?.[0]!.function?.arguments) {
currentToolCall!.input +=
choice.delta.tool_calls[0].function.arguments;
}
}
const isDone: boolean = choice.finish_reason !== null;
if (isDone && currentToolCall) {
// for the last one, we need to emit the tool call
shouldEmitToolCall = {
...currentToolCall,
input: JSON.parse(currentToolCall.input),
};
}
yield {
raw: part,
options: shouldEmitToolCall
? { toolCall: [shouldEmitToolCall] }
: currentToolCall
? {
toolCall: [currentToolCall],
}
: {},
delta: choice.delta.content ?? "",
};
}
toolCallMap.clear();
return;
}
static toTool(tool: BaseTool): ChatCompletionTool {
return {
type: "function",
function: tool.metadata.parameters
? {
name: tool.metadata.name,
description: tool.metadata.description,
parameters: tool.metadata.parameters,
}
: {
name: tool.metadata.name,
description: tool.metadata.description,
},
};
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./lib"
},
"include": ["./src"],
"references": [
{
"path": "../../llamaindex/tsconfig.json"
},
{
"path": "../../env/tsconfig.json"
}
]
}
+99 -20
View File
@@ -152,7 +152,7 @@ importers:
version: 2.4.6
chromadb:
specifier: ^1.8.1
version: 1.8.1(@google/generative-ai@0.12.0)(cohere-ai@7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13))(encoding@0.1.13)(openai@4.57.0(encoding@0.1.13)(zod@3.23.8))
version: 1.8.1(@google/generative-ai@0.12.0)(cohere-ai@7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13))(encoding@0.1.13)(openai@4.60.0(encoding@0.1.13)(zod@3.23.8))
commander:
specifier: ^12.1.0
version: 12.1.0
@@ -167,7 +167,7 @@ importers:
version: link:../packages/llamaindex
mongodb:
specifier: ^6.7.0
version: 6.8.0(@aws-sdk/credential-providers@3.637.0)
version: 6.8.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))
pathe:
specifier: ^1.1.2
version: 1.1.2
@@ -276,7 +276,7 @@ importers:
version: 1.1.0(@types/react@18.3.5)(react@18.3.1)
ai:
specifier: ^3.3.21
version: 3.3.21(openai@4.57.0(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.4.38(typescript@5.5.4))(zod@3.23.8)
version: 3.3.21(openai@4.60.0(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.4.38(typescript@5.5.4))(zod@3.23.8)
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -516,6 +516,9 @@ importers:
'@llamaindex/env':
specifier: workspace:*
version: link:../env
'@llamaindex/openai':
specifier: workspace:*
version: link:../llm/openai
'@mistralai/mistralai':
specifier: ^1.0.4
version: 1.0.4(zod@3.23.8)
@@ -554,7 +557,7 @@ importers:
version: 4.7.0
chromadb:
specifier: 1.8.1
version: 1.8.1(@google/generative-ai@0.12.0)(cohere-ai@7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13))(encoding@0.1.13)(openai@4.57.0(encoding@0.1.13)(zod@3.23.8))
version: 1.8.1(@google/generative-ai@0.12.0)(cohere-ai@7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13))(encoding@0.1.13)(openai@4.60.0(encoding@0.1.13)(zod@3.23.8))
cohere-ai:
specifier: 7.13.0
version: 7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13)
@@ -581,13 +584,13 @@ importers:
version: 2.0.0
mongodb:
specifier: ^6.7.0
version: 6.8.0(@aws-sdk/credential-providers@3.637.0)
version: 6.8.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))
notion-md-crawler:
specifier: ^1.0.0
version: 1.0.0(encoding@0.1.13)
openai:
specifier: ^4.57.0
version: 4.57.0(encoding@0.1.13)(zod@3.23.8)
specifier: ^4.60.0
version: 4.60.0(encoding@0.1.13)(zod@3.23.8)
papaparse:
specifier: ^5.4.1
version: 5.4.1
@@ -652,9 +655,6 @@ importers:
'@faker-js/faker':
specifier: ^8.4.1
version: 8.4.1
'@llamaindex/core':
specifier: workspace:*
version: link:../../core
'@types/node':
specifier: ^22.5.1
version: 22.5.1
@@ -700,7 +700,7 @@ importers:
dependencies:
ai:
specifier: ^3.3.21
version: 3.3.21(openai@4.57.0(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.4.38(typescript@5.5.4))(zod@3.23.8)
version: 3.3.21(openai@4.60.0(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.4.38(typescript@5.5.4))(zod@3.23.8)
llamaindex:
specifier: workspace:*
version: link:../../..
@@ -840,6 +840,9 @@ importers:
typescript:
specifier: 5.5.4
version: 5.5.4
vite-plugin-wasm:
specifier: ^3.3.0
version: 3.3.0(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6))
packages/llamaindex/tests:
devDependencies:
@@ -850,6 +853,25 @@ importers:
specifier: ^2.0.5
version: 2.0.5(@types/node@22.5.1)(terser@5.31.6)
packages/llm/openai:
dependencies:
'@llamaindex/core':
specifier: workspace:*
version: link:../../core
'@llamaindex/env':
specifier: workspace:*
version: link:../../env
openai:
specifier: ^4.60.0
version: 4.60.0(encoding@0.1.13)(zod@3.23.8)
remeda:
specifier: ^2.12.0
version: 2.12.0
devDependencies:
bunchee:
specifier: 5.3.2
version: 5.3.2(typescript@5.5.4)
packages/wasm-tools:
dependencies:
'@assemblyscript/loader':
@@ -8385,6 +8407,15 @@ packages:
zod:
optional: true
openai@4.60.0:
resolution: {integrity: sha512-U/wNmrUPdfsvU1GrKRP5mY5YHR3ev6vtdfNID6Sauz+oquWD8r+cXPL1xiUlYniosPKajy33muVHhGS/9/t6KA==}
hasBin: true
peerDependencies:
zod: ^3.23.8
peerDependenciesMeta:
zod:
optional: true
opener@1.5.2:
resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
hasBin: true
@@ -9460,6 +9491,9 @@ packages:
remark-stringify@11.0.0:
resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==}
remeda@2.12.0:
resolution: {integrity: sha512-VAlyhh1os8boCA9/7yN9sXzo0tfCeOwScGXztwBspS0DXQmbIN8xTBfEABvbAW8rMJMPzqxQ1UymHquuESh/pg==}
renderkid@3.0.0:
resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==}
@@ -10444,6 +10478,10 @@ packages:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
type-fest@4.26.1:
resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==}
engines: {node: '>=16'}
type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
@@ -10689,6 +10727,11 @@ packages:
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
vite-plugin-wasm@3.3.0:
resolution: {integrity: sha512-tVhz6w+W9MVsOCHzxo6SSMSswCeIw4HTrXEi6qL3IRzATl83jl09JVO1djBqPSwfjgnpVHNLYcaMbaDX5WB/pg==}
peerDependencies:
vite: ^2 || ^3 || ^4 || ^5
vite@5.4.2:
resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -14746,7 +14789,7 @@ snapshots:
'@smithy/is-array-buffer@2.2.0':
dependencies:
tslib: 2.6.3
tslib: 2.7.0
'@smithy/is-array-buffer@3.0.0':
dependencies:
@@ -15958,7 +16001,7 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
ai@3.3.21(openai@4.57.0(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.4.38(typescript@5.5.4))(zod@3.23.8):
ai@3.3.21(openai@4.60.0(zod@3.23.8))(react@18.3.1)(sswr@2.1.0(svelte@4.2.19))(svelte@4.2.19)(vue@3.4.38(typescript@5.5.4))(zod@3.23.8):
dependencies:
'@ai-sdk/provider': 0.0.22
'@ai-sdk/provider-utils': 1.0.17(zod@3.23.8)
@@ -15975,7 +16018,7 @@ snapshots:
secure-json-parse: 2.7.0
zod-to-json-schema: 3.23.2(zod@3.23.8)
optionalDependencies:
openai: 4.57.0(zod@3.23.8)
openai: 4.60.0(zod@3.23.8)
react: 18.3.1
sswr: 2.1.0(svelte@4.2.19)
svelte: 4.2.19
@@ -16652,14 +16695,14 @@ snapshots:
chownr@2.0.0: {}
chromadb@1.8.1(@google/generative-ai@0.12.0)(cohere-ai@7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13))(encoding@0.1.13)(openai@4.57.0(encoding@0.1.13)(zod@3.23.8)):
chromadb@1.8.1(@google/generative-ai@0.12.0)(cohere-ai@7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13))(encoding@0.1.13)(openai@4.60.0(encoding@0.1.13)(zod@3.23.8)):
dependencies:
cliui: 8.0.1
isomorphic-fetch: 3.0.0(encoding@0.1.13)
optionalDependencies:
'@google/generative-ai': 0.12.0
cohere-ai: 7.13.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(encoding@0.1.13)
openai: 4.57.0(encoding@0.1.13)(zod@3.23.8)
openai: 4.60.0(encoding@0.1.13)(zod@3.23.8)
transitivePeerDependencies:
- encoding
@@ -17747,6 +17790,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.8.2(@typescript-eslint/parser@8.3.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.3.0(eslint@8.57.0)(typescript@5.5.4)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.3.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0):
dependencies:
array-includes: 3.1.8
@@ -17757,7 +17810,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.2(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-module-utils: 2.8.2(@typescript-eslint/parser@8.3.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3
@@ -20485,7 +20538,7 @@ snapshots:
optionalDependencies:
'@aws-sdk/credential-providers': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))
mongodb@6.8.0(@aws-sdk/credential-providers@3.637.0):
mongodb@6.8.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))):
dependencies:
'@mongodb-js/saslprep': 1.1.7
bson: 6.8.0
@@ -20915,7 +20968,23 @@ snapshots:
transitivePeerDependencies:
- encoding
openai@4.57.0(zod@3.23.8):
openai@4.60.0(encoding@0.1.13)(zod@3.23.8):
dependencies:
'@types/node': 18.19.47
'@types/node-fetch': 2.6.11
'@types/qs': 6.9.15
abort-controller: 3.0.0
agentkeepalive: 4.5.0
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0(encoding@0.1.13)
qs: 6.13.0
optionalDependencies:
zod: 3.23.8
transitivePeerDependencies:
- encoding
openai@4.60.0(zod@3.23.8):
dependencies:
'@types/node': 18.19.47
'@types/node-fetch': 2.6.11
@@ -22123,6 +22192,10 @@ snapshots:
mdast-util-to-markdown: 2.1.0
unified: 11.0.5
remeda@2.12.0:
dependencies:
type-fest: 4.26.1
renderkid@3.0.0:
dependencies:
css-select: 4.3.0
@@ -23220,6 +23293,8 @@ snapshots:
type-fest@2.19.0: {}
type-fest@4.26.1: {}
type-is@1.6.18:
dependencies:
media-typer: 0.3.0
@@ -23340,7 +23415,7 @@ snapshots:
union@0.5.0:
dependencies:
qs: 6.11.2
qs: 6.13.0
unique-string@3.0.0:
dependencies:
@@ -23522,6 +23597,10 @@ snapshots:
- supports-color
- terser
vite-plugin-wasm@3.3.0(vite@5.4.2(@types/node@22.5.1)(terser@5.31.6)):
dependencies:
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.6)
vite@5.4.2(@types/node@22.5.1)(terser@5.31.6):
dependencies:
esbuild: 0.21.5
+1
View File
@@ -1,6 +1,7 @@
packages:
- "apps/*"
- "packages/*"
- "packages/llm/*"
- "packages/core/tests"
- "packages/llamaindex/tests"
- "packages/llamaindex/e2e"
+3
View File
@@ -26,6 +26,9 @@
{
"path": "./packages/community/tsconfig.json"
},
{
"path": "./packages/llm/openai/tsconfig.json"
},
{
"path": "./packages/cloud/tsconfig.json"
},