jina-ai-provider
Version:
Jina AI Provider for running Jina AI models with Vercel AI SDK
242 lines (237 loc) • 7.31 kB
JavaScript
// src/jina-provider.ts
import {
loadApiKey,
withoutTrailingSlash
} from "@ai-sdk/provider-utils";
// src/jina-embedding-model.ts
import {
TooManyEmbeddingValuesForCallError
} from "@ai-sdk/provider";
import {
combineHeaders,
createJsonResponseHandler,
parseProviderOptions,
postJsonToApi
} from "@ai-sdk/provider-utils";
import { z as z3 } from "zod/v4";
// src/jina-embedding-options.ts
import { z } from "zod/v4";
var jinaEmbeddingOptions = z.object({
/**
* The input type for the embeddings.
*
* Defaults to `retrieval.passage`.
*
* Used to convey intended downstream application to help the model produce better embeddings.
*
* Must be one of the following values:
* - `retrieval.query`: Specifies the given text is a query in a search or retrieval setting.
* - `retrieval.passage`: Specifies the given text is a document in a search or retrieval setting.
* - `text-matching`: Specifies the given text is used for Semantic Textual Similarity.
* - `classification`: Specifies that the embedding is used for classification.
* - `separation`: Specifies that the embedding is used for clustering.
*/
inputType: z.enum([
"text-matching",
"retrieval.query",
"retrieval.passage",
"separation",
"classification"
]).optional(),
/**
* The number of dimensions for the resulting output embeddings.
*
* - `jina-embeddings-v3`:
* - Min Output Dimensions: 32 for better performance
* - Max Output Dimensions: 1,024
*
* - `jina-clip-v2`:
* - Min Output Dimensions: 64
* - Max Output Dimensions: 1,024
*
* - `jina-clip-v1`:
* - Output Dimensions: 768
*
* Please refer to the model documentation for the supported values.
*
* @see https://jina.ai/api-dashboard/embedding
*/
outputDimension: z.number().optional(),
/**
* Late chunking
*
* When enabled, the model will automatically split the input into chunks of 1024 tokens each.
*
* @see https://jina.ai/news/jina-embeddings-v3-a-frontier-multilingual-embedding-model/#parameter-latechunking
*
* Defaults to false.
*
* This is only supported in text embedding models.
*/
lateChunking: z.boolean().optional(),
/**
* The data type for the resulting output embeddings.
*
* Defaults to `float`.
*
* - `float`: 32-bit floating-point numbers
* - `binary`: 8-bit binary values
* - `ubinary`: 8-bit unsigned binary values
* - `base64`: Base64 encoded strings
*/
embeddingType: z.enum(["float", "binary", "ubinary", "base64"]).optional(),
/**
* Whether to normalize the resulting output embeddings.
* Scales the embedding so its Euclidean (L2) norm becomes 1, preserving direction. Useful when downstream involves dot-product, classification, visualization.
* Defaults to true.
*/
normalized: z.boolean().optional(),
/**
* Truncate at Maximum Context Length which is 8k tokens
*
* When enabled, the model will automatically drop the tail that extends beyond the maximum context length allowed by the model instead of throwing an error.
*
* Defaults to false.
*/
truncate: z.boolean().optional()
});
// src/jina-error.ts
import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils";
import { z as z2 } from "zod";
var voyageErrorDataSchema = z2.object({
error: z2.object({
code: z2.string().nullable(),
message: z2.string(),
param: z2.any().nullable(),
type: z2.string()
})
});
var voyageFailedResponseHandler = createJsonErrorResponseHandler({
errorSchema: voyageErrorDataSchema,
errorToMessage: (data) => data.error.message
});
// src/jina-embedding-model.ts
var JinaEmbeddingModel = class {
specificationVersion = "v2";
modelId;
config;
get provider() {
return this.config.provider;
}
get maxEmbeddingsPerCall() {
return 2048;
}
get supportsParallelCalls() {
return false;
}
constructor(modelId, config) {
this.modelId = modelId;
this.config = config;
}
async doEmbed({
abortSignal,
values,
headers,
providerOptions
}) {
const embeddingOptions = await parseProviderOptions({
provider: "jina",
providerOptions,
schema: jinaEmbeddingOptions
});
if (values.length > this.maxEmbeddingsPerCall) {
throw new TooManyEmbeddingValuesForCallError({
maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
modelId: this.modelId,
provider: this.provider,
values
});
}
const { responseHeaders, value: response } = await postJsonToApi({
abortSignal,
body: {
model: this.modelId,
input: values,
task: embeddingOptions?.inputType,
embedding_type: embeddingOptions?.embeddingType,
dimensions: embeddingOptions?.outputDimension,
normalized: embeddingOptions?.normalized ?? true,
late_chunking: embeddingOptions?.lateChunking,
truncate: embeddingOptions?.truncate ?? false
},
failedResponseHandler: voyageFailedResponseHandler,
fetch: this.config.fetch,
headers: combineHeaders(this.config.headers(), headers),
successfulResponseHandler: createJsonResponseHandler(
jinaEmbeddingResponseSchema
),
url: `${this.config.baseURL}/embeddings`
});
return {
embeddings: response.data.map((item) => item.embedding),
usage: response.usage ? { tokens: response.usage.total_tokens } : void 0,
response: { headers: responseHeaders }
};
}
};
var jinaEmbeddingResponseSchema = z3.object({
data: z3.array(
z3.object({
object: z3.literal("embedding"),
embedding: z3.array(z3.number()),
index: z3.number().optional()
})
),
usage: z3.object({
total_tokens: z3.number(),
prompt_tokens: z3.number().optional()
}).nullish(),
model: z3.string().optional()
});
// src/jina-provider.ts
function createJina(options = {}) {
const baseURL = withoutTrailingSlash(options.baseURL) ?? "https://api.jina.ai/v1";
const getHeaders = () => ({
Authorization: `Bearer ${loadApiKey({
apiKey: options.apiKey,
environmentVariableName: "JINA_API_KEY",
description: "Jina"
})}`,
...options.headers
});
const createTextEmbeddingModel = (modelId) => new JinaEmbeddingModel(modelId, {
provider: "jina.text.embedding",
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const createMultiModalEmbeddingModel = (modelId) => new JinaEmbeddingModel(modelId, {
provider: "jina.multimodal.embedding",
baseURL,
headers: getHeaders,
fetch: options.fetch
});
const provider = function(modelId) {
if (new.target) {
throw new Error(
"The Jina model function cannot be called with the new keyword."
);
}
return createTextEmbeddingModel(modelId);
};
provider.textEmbeddingModel = createTextEmbeddingModel;
provider.multiModalEmbeddingModel = createMultiModalEmbeddingModel;
provider.chat = provider.languageModel = () => {
throw new Error("languageModel method is not implemented.");
};
provider.imageModel = () => {
throw new Error("imageModel method is not implemented.");
};
return provider;
}
var jina = createJina();
export {
createJina,
jina
};
//# sourceMappingURL=index.js.map