Skip to content
Snippets Groups Projects
Unverified Commit 27746816 authored by Julius Lipp's avatar Julius Lipp Committed by GitHub
Browse files

feat: add mixedbread ai integration (#953)

parent a0f424e5
No related branches found
No related merge requests found
---
"llamaindex": patch
"docs": patch
---
Add mixedbread's embeddings and reranking API
# MixedbreadAI
Welcome to the mixedbread embeddings guide! This guide will help you use the mixedbread ai's API to generate embeddings for your text documents, ensuring you get the most relevant information, just like picking the freshest bread from the bakery.
To find out more about the latest features, updates, and available models, visit [mixedbread.ai](https://mixedbread-ai.com/).
## Table of Contents
1. [Setup](#setup)
2. [Usage with LlamaIndex](#integration-with-llamaindex)
3. [Embeddings with Custom Parameters](#embeddings-with-custom-parameters)
## Setup
First, you will need to install the `llamaindex` package.
```bash
pnpm install llamaindex
```
Next, sign up for an API key at [mixedbread.ai](https://mixedbread.ai/). Once you have your API key, you can import the necessary modules and create a new instance of the `MixedbreadAIEmbeddings` class.
```ts
import { MixedbreadAIEmbeddings, Document, Settings } from "llamaindex";
```
## Usage with LlamaIndex
This section will guide you through integrating mixedbread embeddings with LlamaIndex for more advanced usage.
### Step 1: Load and Index Documents
For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index, like a variety of breads in a bakery.
```ts
Settings.embedModel = new MixedbreadAIEmbeddings({
apiKey: "<MIXEDBREAD_API_KEY>",
model: "mixedbread-ai/mxbai-embed-large-v1",
});
const document = new Document({
text: "The true source of happiness.",
id_: "bread",
});
const index = await VectorStoreIndex.fromDocuments([document]);
```
### Step 2: Create a Query Engine
Combine the retriever and the embed model to create a query engine. This setup ensures that your queries are processed to provide the best results, like arranging the bread in the order of freshness and quality.
Models can require prompts to generate embeddings for queries, in the 'mixedbread-ai/mxbai-embed-large-v1' model's case, the prompt is `Represent this sentence for searching relevant passages:`.
```ts
const queryEngine = index.asQueryEngine();
const query =
"Represent this sentence for searching relevant passages: What is bread?";
// Log the response
const results = await queryEngine.query(query);
console.log(results); // Serving up the freshest, most relevant results.
```
## Embeddings with Custom Parameters
This section will guide you through generating embeddings with custom parameters and usage with f.e. matryoshka and binary embeddings.
### Step 1: Create an Instance of MixedbreadAIEmbeddings
Create a new instance of the `MixedbreadAIEmbeddings` class with custom parameters. For example, to use the `mixedbread-ai/mxbai-embed-large-v1` model with a batch size of 64, normalized embeddings, and binary encoding format:
```ts
const embeddings = new MixedbreadAIEmbeddings({
apiKey: "<MIXEDBREAD_API_KEY>",
model: "mixedbread-ai/mxbai-embed-large-v1",
batchSize: 64,
normalized: true,
dimensions: 512,
encodingFormat: MixedbreadAI.EncodingFormat.Binary,
});
```
### Step 2: Define Texts
Define the texts you want to generate embeddings for.
```ts
const texts = ["Bread is life", "Bread is love"];
```
### Step 3: Generate Embeddings
Use the `embedDocuments` method to generate embeddings for the texts.
```ts
const result = await embeddings.embedDocuments(texts);
console.log(result); // Perfectly customized embeddings, ready to serve.
```
# MixedbreadAI
Welcome to the mixedbread ai reranker guide! This guide will help you use mixedbread ai's API to rerank search query results, ensuring you get the most relevant information, just like picking the freshest bread from the bakery.
To find out more about the latest features and updates, visit the [mixedbread.ai](https://mixedbread.ai/).
## Table of Contents
1. [Setup](#setup)
2. [Usage with LlamaIndex](#integration-with-llamaindex)
3. [Simple Reranking Guide](#simple-reranking-guide)
4. [Reranking with Objects](#reranking-with-objects)
## Setup
First, you will need to install the `llamaindex` package.
```bash
pnpm install llamaindex
```
Next, sign up for an API key at [mixedbread.ai](https://mixedbread.ai/). Once you have your API key, you can import the necessary modules and create a new instance of the `MixedbreadAIReranker` class.
```ts
import {
MixedbreadAIReranker,
Document,
OpenAI,
VectorStoreIndex,
Settings,
} from "llamaindex";
```
## Usage with LlamaIndex
This section will guide you through integrating mixedbread's reranker with LlamaIndex.
### Step 1: Load and Index Documents
For this example, we will use a single document. In a real-world scenario, you would have multiple documents to index, like a variety of breads in a bakery.
```ts
const document = new Document({
text: "This is a sample document.",
id_: "sampleDoc",
});
Settings.llm = new OpenAI({ model: "gpt-3.5-turbo", temperature: 0.1 });
const index = await VectorStoreIndex.fromDocuments([document]);
```
### Step 2: Increase Similarity TopK
The default value for `similarityTopK` is 2, which means only the most similar document will be returned. To get more results, like picking a variety of fresh breads, you can increase the value of `similarityTopK`.
```ts
const retriever = index.asRetriever();
retriever.similarityTopK = 5;
```
### Step 3: Create a MixedbreadAIReranker Instance
Create a new instance of the `MixedbreadAIReranker` class.
```ts
const nodePostprocessor = new MixedbreadAIReranker({
apiKey: "<MIXEDBREAD_API_KEY>",
topN: 4,
});
```
### Step 4: Create a Query Engine
Combine the retriever and node postprocessor to create a query engine. This setup ensures that your queries are processed and reranked to provide the best results, like arranging the bread in the order of freshness and quality.
```ts
const queryEngine = index.asQueryEngine({
retriever,
nodePostprocessors: [nodePostprocessor],
});
// Log the response
const response = await queryEngine.query("Where did the author grow up?");
console.log(response);
```
With mixedbread's Reranker, you're all set to serve up the most relevant and well-ordered results, just like a skilled baker arranging their best breads for eager customers. Enjoy the perfect blend of technology and culinary delight!
## Simple Reranking Guide
This section will guide you through a simple reranking process using mixedbread ai.
### Step 1: Create an Instance of MixedbreadAIReranker
Create a new instance of the `MixedbreadAIReranker` class, passing in your API key and the number of results you want to return. It's like setting up your bakery to offer a specific number of freshly baked items.
```ts
const reranker = new MixedbreadAIReranker({
apiKey: "<MIXEDBREAD_API_KEY>",
topN: 4,
});
```
### Step 2: Define Nodes and Query
Define the nodes (documents) you want to rerank and the query.
```ts
const nodes = [
{ node: new BaseNode("To bake bread you need flour") },
{ node: new BaseNode("To bake bread you need yeast") },
];
const query = "What do you need to bake bread?";
```
### Step 3: Perform Reranking
Use the `postprocessNodes` method to rerank the nodes based on the query.
```ts
const result = await reranker.postprocessNodes(nodes, query);
console.log(result); // Like pulling freshly baked nodes out of the oven.
```
## Reranking with Objects
This section will guide you through reranking when working with objects.
### Step 1: Create an Instance of MixedbreadAIReranker
Create a new instance of the `MixedbreadAIReranker` class, just like before.
```ts
const reranker = new MixedbreadAIReranker({
apiKey: "<MIXEDBREAD_API_KEY>",
model: "mixedbread-ai/mxbai-rerank-large-v1",
topK: 5,
rankFields: ["title", "content"],
returnInput: true,
maxRetries: 5,
});
```
### Step 2: Define Documents and Query
Define the documents (objects) you want to rerank and the query.
```ts
const documents = [
{ title: "Bread Recipe", content: "To bake bread you need flour" },
{ title: "Bread Recipe", content: "To bake bread you need yeast" },
];
const query = "What do you need to bake bread?";
```
### Step 3: Perform Reranking
Use the `rerank` method to reorder the documents based on the query.
```ts
const result = await reranker.rerank(documents, query);
console.log(result); // Perfectly customized results, ready to serve.
```
......@@ -45,6 +45,7 @@
"chromadb": "1.8.1",
"cohere-ai": "7.9.5",
"groq-sdk": "^0.5.0",
"@mixedbread-ai/sdk": "^2.2.11",
"js-tiktoken": "^1.0.12",
"lodash": "^4.17.21",
"magic-bytes.js": "^1.10.0",
......
import { getEnv } from "@llamaindex/env";
import { MixedbreadAI, MixedbreadAIClient } from "@mixedbread-ai/sdk";
import { BaseEmbedding, type EmbeddingInfo } from "./types.js";
type EmbeddingsRequestWithoutInput = Omit<
MixedbreadAI.EmbeddingsRequest,
"input"
>;
/**
* Interface extending EmbeddingsParams with additional
* parameters specific to the MixedbreadAIEmbeddings class.
*/
export interface MixedbreadAIEmbeddingsParams
extends Omit<EmbeddingsRequestWithoutInput, "model"> {
/**
* The model to use for generating embeddings.
* @default {"mixedbread-ai/mxbai-embed-large-v1"}
*/
model?: string;
/**
* The API key to use.
* @default {process.env.MXBAI_API_KEY}
*/
apiKey?: string;
/**
* The base URL for the API.
*/
baseUrl?: string;
/**
* The maximum number of documents to embed in a single request.
* @default {128}
*/
embedBatchSize?: number;
/**
* The embed info for the model.
*/
embedInfo?: EmbeddingInfo;
/**
* The maximum number of retries to attempt.
* @default {3}
*/
maxRetries?: number;
/**
* Timeouts for the request.
*/
timeoutInSeconds?: number;
}
/**
* Class for generating embeddings using the mixedbread ai API.
*
* This class leverages the model "mixedbread-ai/mxbai-embed-large-v1" to generate
* embeddings for text documents. The embeddings can be used for various NLP tasks
* such as similarity comparison, clustering, or as features in machine learning models.
*
* @example
* const mxbai = new MixedbreadAIEmbeddings({ apiKey: 'your-api-key' });
* const texts = ["Baking bread is fun", "I love baking"];
* const result = await mxbai.getTextEmbeddings(texts);
* console.log(result);
*
* @example
* const mxbai = new MixedbreadAIEmbeddings({
* apiKey: 'your-api-key',
* model: 'mixedbread-ai/mxbai-embed-large-v1',
* encodingFormat: MixedbreadAI.EncodingFormat.Binary,
* dimensions: 512,
* normalized: true,
* });
* const query = "Represent this sentence for searching relevant passages: Is baking bread fun?";
* const result = await mxbai.getTextEmbedding(query);
* console.log(result);
*/
export class MixedbreadAIEmbeddings extends BaseEmbedding {
requestParams: EmbeddingsRequestWithoutInput;
requestOptions: MixedbreadAIClient.RequestOptions;
private client: MixedbreadAIClient;
/**
* Constructor for MixedbreadAIEmbeddings.
* @param {Partial<MixedbreadAIEmbeddingsParams>} params - An optional object with properties to configure the instance.
* @throws {Error} If the API key is not provided or found in the environment variables.
* @throws {Error} If the batch size exceeds 256.
*/
constructor(params?: Partial<MixedbreadAIEmbeddingsParams>) {
super();
const apiKey = params?.apiKey ?? getEnv("MXBAI_API_KEY");
if (!apiKey) {
throw new Error(
"mixedbread ai API key not found. Either provide it in the constructor or set the 'MXBAI_API_KEY' environment variable.",
);
}
if (params?.embedBatchSize && params?.embedBatchSize > 256) {
throw new Error(
"The maximum batch size for mixedbread ai embeddings API is 256.",
);
}
this.embedBatchSize = params?.embedBatchSize ?? 128;
this.embedInfo = params?.embedInfo;
this.requestParams = {
model: params?.model ?? "mixedbread-ai/mxbai-embed-large-v1",
normalized: params?.normalized,
dimensions: params?.dimensions,
encodingFormat: params?.encodingFormat,
truncationStrategy: params?.truncationStrategy,
prompt: params?.prompt,
};
this.requestOptions = {
timeoutInSeconds: params?.timeoutInSeconds,
maxRetries: params?.maxRetries ?? 3,
// Support for this already exists in the python sdk and will be added to the js sdk soon
// @ts-ignore
additionalHeaders: {
"user-agent": "@mixedbread-ai/llamaindex-ts-sdk",
},
};
this.client = new MixedbreadAIClient({
apiKey,
environment: params?.baseUrl,
});
}
/**
* Generates an embedding for a single text.
* @param {string} text - A string to generate an embedding for.
* @returns {Promise<number[]>} A Promise that resolves to an array of numbers representing the embedding.
*
* @example
* const query = "Represent this sentence for searching relevant passages: Is baking bread fun?";
* const result = await mxbai.getTextEmbedding(text);
* console.log(result);
*/
async getTextEmbedding(text: string): Promise<number[]> {
return (await this.getTextEmbeddings([text]))[0];
}
/**
* Generates embeddings for an array of texts.
* @param {string[]} texts - An array of strings to generate embeddings for.
* @returns {Promise<Array<number[]>>} A Promise that resolves to an array of embeddings.
*
* @example
* const texts = ["Baking bread is fun", "I love baking"];
* const result = await mxbai.getTextEmbeddings(texts);
* console.log(result);
*/
async getTextEmbeddings(texts: string[]): Promise<Array<number[]>> {
if (texts.length === 0) {
return [];
}
const response = await this.client.embeddings(
{
...this.requestParams,
input: texts,
},
this.requestOptions,
);
return response.data.map((d) => d.embedding as number[]);
}
}
......@@ -4,6 +4,7 @@ export * from "./GeminiEmbedding.js";
export { HuggingFaceInferenceAPIEmbedding } from "./HuggingFaceEmbedding.js";
export * from "./JinaAIEmbedding.js";
export * from "./MistralAIEmbedding.js";
export * from "./MixedbreadAIEmbeddings.js";
export * from "./MultiModalEmbedding.js";
export { OllamaEmbedding } from "./OllamaEmbedding.js";
export * from "./OpenAIEmbedding.js";
......
import { getEnv } from "@llamaindex/env";
import { MixedbreadAI, MixedbreadAIClient } from "@mixedbread-ai/sdk";
import { MetadataMode } from "@llamaindex/core/schema";
import { extractText } from "@llamaindex/core/utils";
import type { MessageContent } from "@llamaindex/core/llms";
import type { BaseNode, NodeWithScore } from "@llamaindex/core/schema";
import type { BaseNodePostprocessor } from "../types.js";
type RerankingRequestWithoutInput = Omit<
MixedbreadAI.RerankingRequest,
"query" | "input"
>;
/**
* Interface extending RerankingRequestWithoutInput with additional
* parameters specific to the MixedbreadRerank class.
*/
export interface MixedbreadAIRerankerParams
extends Omit<RerankingRequestWithoutInput, "model"> {
/**
* The model to use for reranking. For example "default" or "mixedbread-ai/mxbai-rerank-large-v1".
* @default {"default"}
*/
model?: string;
/**
* The API key to use.
* @default {process.env.MXBAI_API_KEY}
*/
apiKey?: string;
/**
* The base URL of the MixedbreadAI API.
*/
baseUrl?: string;
/**
* The maximum number of retries to attempt.
* @default {3}
*/
maxRetries?: number;
/**
* Timeouts for the request.
*/
timeoutInSeconds?: number;
}
/**
* Node postprocessor that uses MixedbreadAI's rerank API.
*
* This class utilizes MixedbreadAI's rerank model to reorder a set of nodes based on their relevance
* to a given query. The reranked nodes are then used for various applications like search results refinement.
*
* @example
* const reranker = new MixedbreadAIReranker({ apiKey: 'your-api-key' });
* const nodes = [{ node: new BaseNode('To bake bread you need flour') }, { node: new BaseNode('To bake bread you need yeast') }];
* const query = "What do you need to bake bread?";
* const result = await reranker.postprocessNodes(nodes, query);
* console.log(result);
*
* @example
* const reranker = new MixedbreadAIReranker({
* apiKey: 'your-api-key',
* model: 'mixedbread-ai/mxbai-rerank-large-v1',
* topK: 5,
* rankFields: ["title", "content"],
* returnInput: true,
* maxRetries: 5
* });
* const documents = [{ title: "Bread Recipe", content: "To bake bread you need flour" }, { title: "Bread Recipe", content: "To bake bread you need yeast" }];
* const query = "What do you need to bake bread?";
* const result = await reranker.rerank(documents, query);
* console.log(result);
*/
export class MixedbreadAIReranker implements BaseNodePostprocessor {
requestParams: RerankingRequestWithoutInput;
requestOptions: MixedbreadAIClient.RequestOptions;
private readonly client: MixedbreadAIClient;
/**
* Constructor for MixedbreadRerank.
* @param {Partial<MixedbreadAIRerankerParams>} params - An optional object with properties to configure the instance.
* @throws {Error} If the API key is not provided or found in the environment variables.
*/
constructor(params: Partial<MixedbreadAIRerankerParams>) {
const apiKey = params?.apiKey ?? getEnv("MXBAI_API_KEY");
if (!apiKey) {
throw new Error(
"MixedbreadAI API key not found. Either provide it in the constructor or set the 'MXBAI_API_KEY' environment variable.",
);
}
this.requestOptions = {
maxRetries: params?.maxRetries ?? 3,
timeoutInSeconds: params?.timeoutInSeconds,
// Support for this already exists in the python sdk and will be added to the js sdk soon
// @ts-ignore
additionalHeaders: {
"user-agent": "@mixedbread-ai/llamaindex-ts-sdk",
},
};
this.client = new MixedbreadAIClient({
apiKey: apiKey,
environment: params?.baseUrl,
});
this.requestParams = {
model: params?.model ?? "default",
returnInput: params?.returnInput ?? false,
topK: params?.topK,
rankFields: params?.rankFields,
};
}
/**
* Reranks the nodes using the mixedbread.ai API.
* @param {NodeWithScore[]} nodes - Array of nodes with scores.
* @param {MessageContent} [query] - Query string.
* @throws {Error} If query is undefined.
*
* @returns {Promise<NodeWithScore[]>} A Promise that resolves to an ordered list of nodes with relevance scores.
*
* @example
* const nodes = [{ node: new BaseNode('To bake bread you need flour') }, { node: new BaseNode('To bake bread you need yeast') }];
* const query = "What do you need to bake bread?";
* const result = await reranker.postprocessNodes(nodes, query);
* console.log(result);
*/
async postprocessNodes(
nodes: NodeWithScore[],
query?: MessageContent,
): Promise<NodeWithScore[]> {
if (query === undefined) {
throw new Error("MixedbreadAIReranker requires a query");
}
if (nodes.length === 0) {
return [];
}
const input = nodes.map((n) => n.node.getContent(MetadataMode.ALL));
const result = await this.client.reranking(
{
query: extractText(query),
input,
...this.requestParams,
},
this.requestOptions,
);
const newNodes: NodeWithScore[] = [];
for (const document of result.data) {
const node = nodes[document.index];
node.score = document.score;
newNodes.push(node);
}
return newNodes;
}
/**
* Returns an ordered list of documents sorted by their relevance to the provided query.
* @param {(Array<string> | Array<BaseNode> | Array<Record<string, unknown>>)} nodes - A list of documents as strings, DocumentInterfaces, or objects with a `pageContent` key.
* @param {string} query - The query to use for reranking the documents.
* @param {RerankingRequestWithoutInput} [options] - Optional parameters for reranking.
*
* @returns {Promise<Array<MixedbreadAI.RankedDocument>>} A Promise that resolves to an ordered list of documents with relevance scores.
*
* @example
* const nodes = ["To bake bread you need flour", "To bake bread you need yeast"];
* const query = "What do you need to bake bread?";
* const result = await reranker.rerank(nodes, query);
* console.log(result);
*/
async rerank(
nodes: Array<string> | Array<BaseNode> | Array<Record<string, unknown>>,
query: string,
options?: RerankingRequestWithoutInput,
): Promise<Array<MixedbreadAI.RankedDocument>> {
if (nodes.length === 0) {
return [];
}
const input =
typeof nodes[0] === "object" && "node" in nodes[0]
? (nodes as BaseNode[]).map((n) => n.getContent(MetadataMode.ALL))
: (nodes as string[]);
const result = await this.client.reranking(
{
query,
input,
...this.requestParams,
...options,
},
this.requestOptions,
);
return result.data;
}
}
export * from "./CohereRerank.js";
export * from "./JinaAIReranker.js";
export * from "./MixedbreadAIReranker.js";
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment