Skip to content

Commit a2a55e2

Browse files
ovuruskajacoblee93
andauthored
community[minor]: DeepInfra embeddings integration #1 (langchain-ai#5382)
* Init * fix(type errors) * feat(deepinfra embeddings) * fix(default model) * fix(deepinfra): axios is removed * ref(deepinfra): remove redundant cast * format(deepinfra) * doc(deepinfra) * doc(deepinfra) * Update deepinfra.mdx * Format --------- Co-authored-by: Jacob Lee <[email protected]>
1 parent cc80b12 commit a2a55e2

File tree

8 files changed

+384
-0
lines changed

8 files changed

+384
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
sidebar_label: DeepInfra
3+
---
4+
5+
# DeepInfra Embeddings
6+
7+
The `DeepInfraEmbeddings` class utilizes the DeepInfra API to generate embeddings for given text inputs. This guide will walk you through the setup and usage of the `DeepInfraEmbeddings` class, helping you integrate it into your project seamlessly.
8+
9+
## Installation
10+
11+
Install the `@langchain/community` package as shown below:
12+
13+
import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx";
14+
15+
<IntegrationInstallTooltip></IntegrationInstallTooltip>
16+
17+
```bash npm2yarn
18+
npm i @langchain/community
19+
```
20+
21+
## Initialization
22+
23+
With this integration, you can use the DeepInfra embeddings model to get embeddings for your text data. Here is the [link](https://deepinfra.com/models/embeddings) to the embeddings models.
24+
25+
First, you need to sign up on the DeepInfra website and get the API token from [here](https://deepinfra.com/dash/api_keys). You can copy names from the model cards and start using them in your code.
26+
27+
To use the `DeepInfraEmbeddings` class, you need an API token from DeepInfra. You can pass this token directly to the constructor or set it as an environment variable (`DEEPINFRA_API_TOKEN`).
28+
29+
### Basic Usage
30+
31+
Here’s how to create an instance of `DeepInfraEmbeddings`:
32+
33+
```typescript
34+
import { DeepInfraEmbeddings } from "@langchain/community/embeddings/deepinfra";
35+
36+
const embeddings = new DeepInfraEmbeddings({
37+
apiToken: "YOUR_API_TOKEN",
38+
modelName: "sentence-transformers/clip-ViT-B-32", // Optional, defaults to "sentence-transformers/clip-ViT-B-32"
39+
batchSize: 1024, // Optional, defaults to 1024
40+
});
41+
```
42+
43+
If the `apiToken` is not provided, it will be read from the `DEEPINFRA_API_TOKEN` environment variable.
44+
45+
## Generating Embeddings
46+
47+
### Embedding a Single Query
48+
49+
To generate embeddings for a single text query, use the `embedQuery` method:
50+
51+
```typescript
52+
const embedding = await embeddings.embedQuery(
53+
"What would be a good company name for a company that makes colorful socks?"
54+
);
55+
console.log(embedding);
56+
```
57+
58+
### Embedding Multiple Documents
59+
60+
To generate embeddings for multiple documents, use the `embedDocuments` method. This method will handle batching automatically based on the `batchSize` parameter:
61+
62+
```typescript
63+
const documents = [
64+
"Document 1 text...",
65+
"Document 2 text...",
66+
"Document 3 text...",
67+
];
68+
69+
const embeddingsArray = await embeddings.embedDocuments(documents);
70+
console.log(embeddingsArray);
71+
```
72+
73+
## Customizing Requests
74+
75+
You can customize the base URL the SDK sends requests to by passing a `configuration` parameter:
76+
77+
```typescript
78+
const customEmbeddings = new DeepInfraEmbeddings({
79+
apiToken: "YOUR_API_TOKEN",
80+
configuration: {
81+
baseURL: "https://your_custom_url.com",
82+
},
83+
});
84+
```
85+
86+
This allows you to route requests through a custom endpoint if needed.
87+
88+
## Error Handling
89+
90+
If the API token is not provided and cannot be found in the environment variables, an error will be thrown:
91+
92+
```typescript
93+
try {
94+
const embeddings = new DeepInfraEmbeddings();
95+
} catch (error) {
96+
console.error("DeepInfra API token not found");
97+
}
98+
```
99+
100+
## Example
101+
102+
Here’s a complete example of how to set up and use the `DeepInfraEmbeddings` class:
103+
104+
```typescript
105+
import { DeepInfraEmbeddings } from "@langchain/community/embeddings/deepinfra";
106+
107+
const embeddings = new DeepInfraEmbeddings({
108+
apiToken: "YOUR_API_TOKEN",
109+
modelName: "sentence-transformers/clip-ViT-B-32",
110+
batchSize: 512,
111+
});
112+
113+
async function runExample() {
114+
const queryEmbedding = await embeddings.embedQuery("Example query text.");
115+
console.log("Query Embedding:", queryEmbedding);
116+
117+
const documents = ["Text 1", "Text 2", "Text 3"];
118+
const documentEmbeddings = await embeddings.embedDocuments(documents);
119+
console.log("Document Embeddings:", documentEmbeddings);
120+
}
121+
122+
runExample();
123+
```
124+
125+
## Feedback and Support
126+
127+
For feedback or questions, please contact [[email protected]](mailto:[email protected]).

examples/src/embeddings/deepinfra.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DeepInfraEmbeddings } from "@langchain/community/embeddings/deepinfra";
2+
3+
const model = new DeepInfraEmbeddings({
4+
apiToken: process.env.DEEPINFRA_API_TOKEN,
5+
batchSize: 1024, // Default value
6+
modelName: "sentence-transformers/clip-ViT-B-32", // Default value
7+
});
8+
9+
const embeddings = await model.embedQuery(
10+
"Tell me a story about a dragon and a princess."
11+
);
12+
console.log(embeddings);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { DeepInfraEmbeddings } from "@langchain/community/embeddings/deepinfra";
2+
3+
const model = new DeepInfraEmbeddings({
4+
apiToken: process.env.DEEPINFRA_API_TOKEN,
5+
batchSize: 1024, // Default value
6+
modelName: "sentence-transformers/clip-ViT-B-32", // Default value
7+
});
8+
9+
const embeddings = await model.embedQuery(
10+
"Tell me a story about a dragon and a princess."
11+
);
12+
console.log(embeddings);

libs/langchain-community/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ embeddings/cohere.cjs
142142
embeddings/cohere.js
143143
embeddings/cohere.d.ts
144144
embeddings/cohere.d.cts
145+
embeddings/deepinfra.cjs
146+
embeddings/deepinfra.js
147+
embeddings/deepinfra.d.ts
148+
embeddings/deepinfra.d.cts
145149
embeddings/fireworks.cjs
146150
embeddings/fireworks.js
147151
embeddings/fireworks.d.ts

libs/langchain-community/langchain.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const config = {
6969
"embeddings/bedrock": "embeddings/bedrock",
7070
"embeddings/cloudflare_workersai": "embeddings/cloudflare_workersai",
7171
"embeddings/cohere": "embeddings/cohere",
72+
"embeddings/deepinfra": "embeddings/deepinfra",
7273
"embeddings/fireworks": "embeddings/fireworks",
7374
"embeddings/googlepalm": "embeddings/googlepalm",
7475
"embeddings/googlevertexai": "embeddings/googlevertexai",

libs/langchain-community/package.json

+13
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,15 @@
10221022
"import": "./embeddings/cohere.js",
10231023
"require": "./embeddings/cohere.cjs"
10241024
},
1025+
"./embeddings/deepinfra": {
1026+
"types": {
1027+
"import": "./embeddings/deepinfra.d.ts",
1028+
"require": "./embeddings/deepinfra.d.cts",
1029+
"default": "./embeddings/deepinfra.d.ts"
1030+
},
1031+
"import": "./embeddings/deepinfra.js",
1032+
"require": "./embeddings/deepinfra.cjs"
1033+
},
10251034
"./embeddings/fireworks": {
10261035
"types": {
10271036
"import": "./embeddings/fireworks.d.ts",
@@ -3096,6 +3105,10 @@
30963105
"embeddings/cohere.js",
30973106
"embeddings/cohere.d.ts",
30983107
"embeddings/cohere.d.cts",
3108+
"embeddings/deepinfra.cjs",
3109+
"embeddings/deepinfra.js",
3110+
"embeddings/deepinfra.d.ts",
3111+
"embeddings/deepinfra.d.cts",
30993112
"embeddings/fireworks.cjs",
31003113
"embeddings/fireworks.js",
31013114
"embeddings/fireworks.d.ts",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { getEnvironmentVariable } from "@langchain/core/utils/env";
2+
import { Embeddings, EmbeddingsParams } from "@langchain/core/embeddings";
3+
import { chunkArray } from "@langchain/core/utils/chunk_array";
4+
5+
/**
6+
* The default model name to use for generating embeddings.
7+
*/
8+
const DEFAULT_MODEL_NAME = "sentence-transformers/clip-ViT-B-32";
9+
10+
/**
11+
* The default batch size to use for generating embeddings.
12+
* This is limited by the DeepInfra API to a maximum of 1024.
13+
*/
14+
const DEFAULT_BATCH_SIZE = 1024;
15+
16+
/**
17+
* Environment variable name for the DeepInfra API token.
18+
*/
19+
const API_TOKEN_ENV_VAR = "DEEPINFRA_API_TOKEN";
20+
21+
export interface DeepInfraEmbeddingsRequest {
22+
inputs: string[];
23+
normalize?: boolean;
24+
image?: string;
25+
webhook?: string;
26+
}
27+
28+
/**
29+
* Input parameters for the DeepInfra embeddings
30+
*/
31+
export interface DeepInfraEmbeddingsParams extends EmbeddingsParams {
32+
/**
33+
* The API token to use for authentication.
34+
* If not provided, it will be read from the `DEEPINFRA_API_TOKEN` environment variable.
35+
*/
36+
apiToken?: string;
37+
38+
/**
39+
* The model ID to use for generating completions.
40+
* Default: `sentence-transformers/clip-ViT-B-32`
41+
*/
42+
modelName?: string;
43+
44+
/**
45+
* The maximum number of texts to embed in a single request. This is
46+
* limited by the DeepInfra API to a maximum of 1024.
47+
*/
48+
batchSize?: number;
49+
}
50+
51+
/**
52+
* Response from the DeepInfra embeddings API.
53+
*/
54+
export interface DeepInfraEmbeddingsResponse {
55+
/**
56+
* The embeddings generated for the input texts.
57+
*/
58+
embeddings: number[][];
59+
/**
60+
* The number of tokens in the input texts.
61+
*/
62+
input_tokens: number;
63+
/**
64+
* The status of the inference.
65+
*/
66+
request_id?: string;
67+
}
68+
69+
/**
70+
* A class for generating embeddings using the DeepInfra API.
71+
* @example
72+
* ```typescript
73+
* // Embed a query using the DeepInfraEmbeddings class
74+
* const model = new DeepInfraEmbeddings();
75+
* const res = await model.embedQuery(
76+
* "What would be a good company name for a company that makes colorful socks?",
77+
* );
78+
* console.log({ res });
79+
* ```
80+
*/
81+
export class DeepInfraEmbeddings
82+
extends Embeddings
83+
implements DeepInfraEmbeddingsParams
84+
{
85+
apiToken: string;
86+
87+
batchSize: number;
88+
89+
modelName: string;
90+
91+
/**
92+
* Constructor for the DeepInfraEmbeddings class.
93+
* @param fields - An optional object with properties to configure the instance.
94+
*/
95+
constructor(
96+
fields?: Partial<DeepInfraEmbeddingsParams> & {
97+
verbose?: boolean;
98+
}
99+
) {
100+
const fieldsWithDefaults = {
101+
modelName: DEFAULT_MODEL_NAME,
102+
batchSize: DEFAULT_BATCH_SIZE,
103+
...fields,
104+
};
105+
106+
super(fieldsWithDefaults);
107+
108+
const apiKey =
109+
fieldsWithDefaults?.apiToken || getEnvironmentVariable(API_TOKEN_ENV_VAR);
110+
111+
if (!apiKey) {
112+
throw new Error("DeepInfra API token not found");
113+
}
114+
115+
this.modelName = fieldsWithDefaults?.modelName ?? this.modelName;
116+
this.batchSize = fieldsWithDefaults?.batchSize ?? this.batchSize;
117+
this.apiToken = apiKey;
118+
}
119+
120+
/**
121+
* Generates embeddings for an array of texts.
122+
* @param inputs - An array of strings to generate embeddings for.
123+
* @returns A Promise that resolves to an array of embeddings.
124+
*/
125+
async embedDocuments(inputs: string[]): Promise<number[][]> {
126+
const batches = chunkArray(inputs, this.batchSize);
127+
128+
const batchRequests = batches.map((batch: string[]) =>
129+
this.embeddingWithRetry({
130+
inputs: batch,
131+
})
132+
);
133+
134+
const batchResponses = await Promise.all(batchRequests);
135+
136+
const out: number[][] = [];
137+
138+
for (let i = 0; i < batchResponses.length; i += 1) {
139+
const batch = batches[i];
140+
const { embeddings } = batchResponses[i];
141+
for (let j = 0; j < batch.length; j += 1) {
142+
out.push(embeddings[j]);
143+
}
144+
}
145+
146+
return out;
147+
}
148+
149+
/**
150+
* Generates an embedding for a single text.
151+
* @param text - A string to generate an embedding for.
152+
* @returns A Promise that resolves to an array of numbers representing the embedding.
153+
*/
154+
async embedQuery(text: string): Promise<number[]> {
155+
const { embeddings } = await this.embeddingWithRetry({
156+
inputs: [text],
157+
});
158+
return embeddings[0];
159+
}
160+
161+
/**
162+
* Generates embeddings with retry capabilities.
163+
* @param request - An object containing the request parameters for generating embeddings.
164+
* @returns A Promise that resolves to the API response.
165+
*/
166+
private async embeddingWithRetry(
167+
request: DeepInfraEmbeddingsRequest
168+
): Promise<DeepInfraEmbeddingsResponse> {
169+
const response = await this.caller.call(() =>
170+
fetch(`https://api.deepinfra.com/v1/inference/${this.modelName}`, {
171+
method: "POST",
172+
headers: {
173+
Authorization: `Bearer ${this.apiToken}`,
174+
"Content-Type": "application/json",
175+
},
176+
body: JSON.stringify(request),
177+
}).then((res) => res.json())
178+
);
179+
return response as DeepInfraEmbeddingsResponse;
180+
}
181+
}

0 commit comments

Comments
 (0)