@ai-sdk/perplexity 3.0.48 → 3.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,165 @@
1
+ import {
2
+ TooManyEmbeddingValuesForCallError,
3
+ type EmbeddingModelV3,
4
+ } from '@ai-sdk/provider';
5
+ import {
6
+ combineHeaders,
7
+ convertBase64ToUint8Array,
8
+ createJsonErrorResponseHandler,
9
+ createJsonResponseHandler,
10
+ parseProviderOptions,
11
+ postJsonToApi,
12
+ type FetchFunction,
13
+ } from '@ai-sdk/provider-utils';
14
+ import { z } from 'zod/v4';
15
+ import {
16
+ perplexityEmbeddingModelOptions,
17
+ type PerplexityEmbeddingModelId,
18
+ } from './perplexity-embedding-model-options';
19
+ import { perplexityErrorSchema } from './perplexity-language-model';
20
+
21
+ type PerplexityEmbeddingConfig = {
22
+ provider: string;
23
+ baseURL: string;
24
+ headers: () => Record<string, string | undefined>;
25
+ fetch?: FetchFunction;
26
+ };
27
+
28
+ export class PerplexityEmbeddingModel implements EmbeddingModelV3 {
29
+ readonly specificationVersion = 'v3';
30
+ readonly modelId: PerplexityEmbeddingModelId;
31
+ // https://docs.perplexity.ai/docs/embeddings/standard-embeddings
32
+ readonly maxEmbeddingsPerCall = 512;
33
+ readonly supportsParallelCalls = true;
34
+
35
+ private readonly config: PerplexityEmbeddingConfig;
36
+
37
+ get provider(): string {
38
+ return this.config.provider;
39
+ }
40
+
41
+ constructor(
42
+ modelId: PerplexityEmbeddingModelId,
43
+ config: PerplexityEmbeddingConfig,
44
+ ) {
45
+ this.modelId = modelId;
46
+ this.config = config;
47
+ }
48
+
49
+ async doEmbed({
50
+ values,
51
+ abortSignal,
52
+ headers,
53
+ providerOptions,
54
+ }: Parameters<EmbeddingModelV3['doEmbed']>[0]): Promise<
55
+ Awaited<ReturnType<EmbeddingModelV3['doEmbed']>>
56
+ > {
57
+ if (values.length > this.maxEmbeddingsPerCall) {
58
+ throw new TooManyEmbeddingValuesForCallError({
59
+ provider: this.provider,
60
+ modelId: this.modelId,
61
+ maxEmbeddingsPerCall: this.maxEmbeddingsPerCall,
62
+ values,
63
+ });
64
+ }
65
+
66
+ const perplexityOptions =
67
+ (await parseProviderOptions({
68
+ provider: 'perplexity',
69
+ providerOptions,
70
+ schema: perplexityEmbeddingModelOptions,
71
+ })) ?? {};
72
+
73
+ // Perplexity only returns quantized (base64-encoded) embeddings.
74
+ const encodingFormat = perplexityOptions.encodingFormat ?? 'base64_int8';
75
+
76
+ const {
77
+ responseHeaders,
78
+ value: response,
79
+ rawValue,
80
+ } = await postJsonToApi({
81
+ url: `${this.config.baseURL}/v1/embeddings`,
82
+ headers: combineHeaders(this.config.headers(), headers),
83
+ body: {
84
+ model: this.modelId,
85
+ input: values,
86
+ dimensions: perplexityOptions.dimensions,
87
+ encoding_format: encodingFormat,
88
+ },
89
+ failedResponseHandler: createJsonErrorResponseHandler({
90
+ errorSchema: perplexityErrorSchema,
91
+ errorToMessage: data => data.error.message ?? 'Unknown error',
92
+ }),
93
+ successfulResponseHandler: createJsonResponseHandler(
94
+ perplexityEmbeddingResponseSchema,
95
+ ),
96
+ abortSignal,
97
+ fetch: this.config.fetch,
98
+ });
99
+
100
+ const cost = response.usage?.cost;
101
+
102
+ return {
103
+ embeddings: response.data.map(item =>
104
+ decodeEmbedding(item.embedding, encodingFormat),
105
+ ),
106
+ usage: response.usage
107
+ ? { tokens: response.usage.prompt_tokens }
108
+ : undefined,
109
+ providerMetadata: cost
110
+ ? {
111
+ perplexity: {
112
+ cost: {
113
+ inputCost: cost.input_cost ?? null,
114
+ totalCost: cost.total_cost ?? null,
115
+ currency: cost.currency ?? null,
116
+ },
117
+ },
118
+ }
119
+ : undefined,
120
+ response: { headers: responseHeaders, body: rawValue },
121
+ warnings: [],
122
+ };
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Decodes a base64-encoded Perplexity embedding into a numeric vector.
128
+ * For `base64_int8` the bytes are reinterpreted as signed int8 values;
129
+ * for `base64_binary` the raw unsigned bytes (packed bits) are returned.
130
+ */
131
+ function decodeEmbedding(
132
+ base64: string,
133
+ encodingFormat: 'base64_int8' | 'base64_binary',
134
+ ): number[] {
135
+ const bytes = convertBase64ToUint8Array(base64);
136
+
137
+ if (encodingFormat === 'base64_int8') {
138
+ const int8 = new Int8Array(
139
+ bytes.buffer,
140
+ bytes.byteOffset,
141
+ bytes.byteLength,
142
+ );
143
+ return Array.from(int8);
144
+ }
145
+
146
+ return Array.from(bytes);
147
+ }
148
+
149
+ // minimal version of the schema, focussed on what is needed for the implementation
150
+ // this approach limits breakages when the API changes and increases efficiency
151
+ const perplexityEmbeddingResponseSchema = z.object({
152
+ data: z.array(z.object({ embedding: z.string() })),
153
+ usage: z
154
+ .object({
155
+ prompt_tokens: z.number(),
156
+ cost: z
157
+ .object({
158
+ input_cost: z.number().nullish(),
159
+ total_cost: z.number().nullish(),
160
+ currency: z.string().nullish(),
161
+ })
162
+ .nullish(),
163
+ })
164
+ .nullish(),
165
+ });
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  NoSuchModelError,
3
3
  type LanguageModelV3,
4
+ type EmbeddingModelV3,
4
5
  type ProviderV3,
5
6
  } from '@ai-sdk/provider';
6
7
  import {
@@ -10,6 +11,8 @@ import {
10
11
  withUserAgentSuffix,
11
12
  type FetchFunction,
12
13
  } from '@ai-sdk/provider-utils';
14
+ import { PerplexityEmbeddingModel } from './perplexity-embedding-model';
15
+ import type { PerplexityEmbeddingModelId } from './perplexity-embedding-model-options';
13
16
  import { PerplexityLanguageModel } from './perplexity-language-model';
14
17
  import type { PerplexityLanguageModelId } from './perplexity-language-model-options';
15
18
  import { VERSION } from './version';
@@ -25,10 +28,20 @@ export interface PerplexityProvider extends ProviderV3 {
25
28
  */
26
29
  languageModel(modelId: PerplexityLanguageModelId): LanguageModelV3;
27
30
 
31
+ /**
32
+ * Creates a Perplexity model for text embeddings.
33
+ */
34
+ embedding(modelId: PerplexityEmbeddingModelId): EmbeddingModelV3;
35
+
36
+ /**
37
+ * Creates a Perplexity model for text embeddings.
38
+ */
39
+ embeddingModel(modelId: PerplexityEmbeddingModelId): EmbeddingModelV3;
40
+
28
41
  /**
29
42
  * @deprecated Use `embeddingModel` instead.
30
43
  */
31
- textEmbeddingModel(modelId: string): never;
44
+ textEmbeddingModel(modelId: PerplexityEmbeddingModelId): EmbeddingModelV3;
32
45
  }
33
46
 
34
47
  export interface PerplexityProviderSettings {
@@ -70,27 +83,36 @@ export function createPerplexity(
70
83
  `ai-sdk/perplexity/${VERSION}`,
71
84
  );
72
85
 
86
+ const baseURL = withoutTrailingSlash(
87
+ options.baseURL ?? 'https://api.perplexity.ai',
88
+ )!;
89
+
73
90
  const createLanguageModel = (modelId: PerplexityLanguageModelId) => {
74
91
  return new PerplexityLanguageModel(modelId, {
75
- baseURL: withoutTrailingSlash(
76
- options.baseURL ?? 'https://api.perplexity.ai',
77
- )!,
92
+ baseURL,
78
93
  headers: getHeaders,
79
94
  generateId,
80
95
  fetch: options.fetch,
81
96
  });
82
97
  };
83
98
 
99
+ const createEmbeddingModel = (modelId: PerplexityEmbeddingModelId) =>
100
+ new PerplexityEmbeddingModel(modelId, {
101
+ provider: 'perplexity.embedding',
102
+ baseURL,
103
+ headers: getHeaders,
104
+ fetch: options.fetch,
105
+ });
106
+
84
107
  const provider = (modelId: PerplexityLanguageModelId) =>
85
108
  createLanguageModel(modelId);
86
109
 
87
110
  provider.specificationVersion = 'v3' as const;
88
111
  provider.languageModel = createLanguageModel;
89
112
 
90
- provider.embeddingModel = (modelId: string) => {
91
- throw new NoSuchModelError({ modelId, modelType: 'embeddingModel' });
92
- };
93
- provider.textEmbeddingModel = provider.embeddingModel;
113
+ provider.embedding = createEmbeddingModel;
114
+ provider.embeddingModel = createEmbeddingModel;
115
+ provider.textEmbeddingModel = createEmbeddingModel;
94
116
  provider.imageModel = (modelId: string) => {
95
117
  throw new NoSuchModelError({ modelId, modelType: 'imageModel' });
96
118
  };