@ai-sdk/perplexity 4.0.52 → 5.0.1

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.
package/dist/index.js CHANGED
@@ -45,20 +45,22 @@ var perplexityEmbeddingModelOptions = z.object({
45
45
  });
46
46
 
47
47
  // src/perplexity-language-model.ts
48
+ import {
49
+ APICallError
50
+ } from "@ai-sdk/provider";
48
51
  import {
49
52
  combineHeaders,
50
53
  createEventSourceResponseHandler,
51
54
  createJsonErrorResponseHandler,
52
55
  createJsonResponseHandler,
53
- createLanguageModelResponseMetadata as getResponseMetadata,
56
+ createLanguageModelResponseMetadata,
54
57
  isCustomReasoning,
55
58
  parseProviderOptions,
56
59
  postJsonToApi,
57
60
  serializeModelOptions,
58
- WORKFLOW_SERIALIZE,
59
- WORKFLOW_DESERIALIZE
61
+ WORKFLOW_DESERIALIZE,
62
+ WORKFLOW_SERIALIZE
60
63
  } from "@ai-sdk/provider-utils";
61
- import { z as z3 } from "zod/v4";
62
64
 
63
65
  // src/convert-perplexity-usage.ts
64
66
  import { createNullLanguageModelUsage } from "@ai-sdk/provider-utils";
@@ -66,27 +68,28 @@ function convertPerplexityUsage(usage) {
66
68
  if (usage == null) {
67
69
  return createNullLanguageModelUsage();
68
70
  }
69
- const promptTokens = usage.prompt_tokens ?? 0;
70
- const completionTokens = usage.completion_tokens ?? 0;
71
- const reasoningTokens = usage.reasoning_tokens ?? 0;
71
+ const inputTokens = usage.input_tokens ?? 0;
72
+ const outputTokens = usage.output_tokens ?? 0;
73
+ const cacheReadTokens = usage.input_tokens_details?.cache_read_input_tokens ?? usage.input_tokens_details?.cached_tokens ?? 0;
74
+ const cacheWriteTokens = usage.input_tokens_details?.cache_creation_input_tokens ?? 0;
75
+ const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ?? 0;
72
76
  return {
73
77
  inputTokens: {
74
- total: promptTokens,
75
- noCache: promptTokens,
76
- cacheRead: void 0,
77
- cacheWrite: void 0
78
+ total: inputTokens,
79
+ noCache: Math.max(0, inputTokens - cacheReadTokens - cacheWriteTokens),
80
+ cacheRead: cacheReadTokens,
81
+ cacheWrite: cacheWriteTokens
78
82
  },
79
83
  outputTokens: {
80
- // Perplexity reports reasoning tokens separately from completion tokens.
81
- total: completionTokens + reasoningTokens,
82
- text: completionTokens,
84
+ total: outputTokens,
85
+ text: Math.max(0, outputTokens - reasoningTokens),
83
86
  reasoning: reasoningTokens
84
87
  },
85
88
  raw: usage
86
89
  };
87
90
  }
88
91
 
89
- // src/convert-to-perplexity-messages.ts
92
+ // src/convert-to-perplexity-input.ts
90
93
  import {
91
94
  UnsupportedFunctionalityError
92
95
  } from "@ai-sdk/provider";
@@ -95,95 +98,154 @@ import {
95
98
  getTopLevelMediaType,
96
99
  resolveFullMediaType
97
100
  } from "@ai-sdk/provider-utils";
98
- function convertToPerplexityMessages(prompt) {
99
- const messages = [];
101
+ function serializeToolOutput(output) {
102
+ switch (output.type) {
103
+ case "text":
104
+ case "error-text":
105
+ return output.value;
106
+ case "json":
107
+ case "error-json":
108
+ return JSON.stringify(output.value);
109
+ case "execution-denied":
110
+ return output.reason ?? "Tool call execution denied.";
111
+ case "content": {
112
+ if (output.value.some((part) => part.type !== "text")) {
113
+ throw new UnsupportedFunctionalityError({
114
+ functionality: "file and custom tool result content"
115
+ });
116
+ }
117
+ return output.value.map((part) => part.type === "text" ? part.text : "").join("");
118
+ }
119
+ default: {
120
+ const _exhaustiveCheck = output;
121
+ throw new Error(`Unsupported tool output: ${_exhaustiveCheck}`);
122
+ }
123
+ }
124
+ }
125
+ function getThoughtSignature(providerOptions) {
126
+ const thoughtSignature = providerOptions?.perplexity?.thoughtSignature;
127
+ return typeof thoughtSignature === "string" ? thoughtSignature : void 0;
128
+ }
129
+ function convertToPerplexityInput(prompt) {
130
+ const input = [];
131
+ const warnings = [];
100
132
  for (const { role, content } of prompt) {
101
133
  switch (role) {
102
134
  case "system": {
103
- messages.push({ role: "system", content });
135
+ input.push({ type: "message", role: "system", content });
104
136
  break;
105
137
  }
106
- case "user":
107
- case "assistant": {
108
- const hasMultipartContent = content.some(
109
- (part) => part.type === "file" && getTopLevelMediaType(part.mediaType) === "image" || part.type === "file" && getTopLevelMediaType(part.mediaType) === "application"
110
- );
111
- const messageContent = content.map((part, index) => {
112
- switch (part.type) {
113
- case "text": {
114
- return {
115
- type: "text",
116
- text: part.text
117
- };
118
- }
119
- case "file": {
120
- switch (part.data.type) {
121
- case "reference": {
138
+ case "user": {
139
+ const convertedContent = content.map(
140
+ (part) => {
141
+ switch (part.type) {
142
+ case "text":
143
+ return { type: "input_text", text: part.text };
144
+ case "file": {
145
+ if (getTopLevelMediaType(part.mediaType) !== "image") {
122
146
  throw new UnsupportedFunctionalityError({
123
- functionality: "file parts with provider references"
124
- });
125
- }
126
- case "text": {
127
- throw new UnsupportedFunctionalityError({
128
- functionality: "text file parts"
147
+ functionality: `file part media type ${part.mediaType}`
129
148
  });
130
149
  }
131
- case "url":
132
- case "data": {
133
- const topLevelMediaType = getTopLevelMediaType(
134
- part.mediaType
135
- );
136
- if (topLevelMediaType === "application") {
137
- const fullMediaType = resolveFullMediaType({ part });
138
- if (fullMediaType !== "application/pdf") {
139
- throw new UnsupportedFunctionalityError({
140
- functionality: `file part media type ${fullMediaType}`
141
- });
142
- }
143
- return part.data.type === "url" ? {
144
- type: "file_url",
145
- file_url: {
146
- url: part.data.url.toString()
147
- },
148
- file_name: part.filename
149
- } : {
150
- type: "file_url",
151
- file_url: {
152
- url: typeof part.data.data === "string" ? part.data.data : convertUint8ArrayToBase64(part.data.data)
153
- },
154
- file_name: part.filename || `document-${index}.pdf`
150
+ switch (part.data.type) {
151
+ case "url":
152
+ return {
153
+ type: "input_image",
154
+ image_url: part.data.url.toString()
155
155
  };
156
- } else if (topLevelMediaType === "image") {
157
- return part.data.type === "url" ? {
158
- type: "image_url",
159
- image_url: {
160
- url: part.data.url.toString()
161
- }
162
- } : {
163
- type: "image_url",
164
- image_url: {
165
- url: `data:${resolveFullMediaType({ part })};base64,${typeof part.data.data === "string" ? part.data.data : convertUint8ArrayToBase64(part.data.data)}`
166
- }
156
+ case "data":
157
+ return {
158
+ type: "input_image",
159
+ image_url: `data:${resolveFullMediaType({ part })};base64,${typeof part.data.data === "string" ? part.data.data : convertUint8ArrayToBase64(part.data.data)}`
167
160
  };
168
- }
169
- throw new UnsupportedFunctionalityError({
170
- functionality: `file part media type ${part.mediaType}`
171
- });
161
+ case "reference":
162
+ throw new UnsupportedFunctionalityError({
163
+ functionality: "file parts with provider references"
164
+ });
165
+ case "text":
166
+ throw new UnsupportedFunctionalityError({
167
+ functionality: "text file parts"
168
+ });
172
169
  }
173
170
  }
174
171
  }
175
172
  }
176
- }).filter(Boolean);
177
- messages.push({
178
- role,
179
- content: hasMultipartContent ? messageContent : messageContent.filter((part) => part.type === "text").map((part) => part.text).join("")
173
+ );
174
+ const isTextOnly = convertedContent.every(
175
+ (part) => part.type === "input_text"
176
+ );
177
+ input.push({
178
+ type: "message",
179
+ role: "user",
180
+ content: isTextOnly ? convertedContent.map((part) => part.type === "input_text" ? part.text : "").join("") : convertedContent
180
181
  });
181
182
  break;
182
183
  }
184
+ case "assistant": {
185
+ const text = content.filter((part) => part.type === "text").map((part) => part.text).join("");
186
+ if (text.length > 0) {
187
+ input.push({ type: "message", role: "assistant", content: text });
188
+ }
189
+ for (const part of content) {
190
+ switch (part.type) {
191
+ case "text":
192
+ break;
193
+ case "tool-call":
194
+ input.push({
195
+ type: "function_call",
196
+ call_id: part.toolCallId,
197
+ name: part.toolName,
198
+ arguments: JSON.stringify(part.input ?? {}),
199
+ thought_signature: getThoughtSignature(part.providerOptions)
200
+ });
201
+ break;
202
+ case "tool-result":
203
+ input.push({
204
+ type: "function_call_output",
205
+ call_id: part.toolCallId,
206
+ name: part.toolName,
207
+ output: serializeToolOutput(part.output),
208
+ thought_signature: getThoughtSignature(part.providerOptions)
209
+ });
210
+ break;
211
+ case "reasoning":
212
+ warnings.push({
213
+ type: "unsupported",
214
+ feature: "reasoning content in prompt"
215
+ });
216
+ break;
217
+ case "file":
218
+ case "reasoning-file":
219
+ case "custom":
220
+ throw new UnsupportedFunctionalityError({
221
+ functionality: `assistant ${part.type} parts`
222
+ });
223
+ default: {
224
+ const _exhaustiveCheck = part;
225
+ throw new Error(
226
+ `Unsupported assistant part: ${_exhaustiveCheck}`
227
+ );
228
+ }
229
+ }
230
+ }
231
+ break;
232
+ }
183
233
  case "tool": {
184
- throw new UnsupportedFunctionalityError({
185
- functionality: "Tool messages"
186
- });
234
+ for (const part of content) {
235
+ if (part.type === "tool-approval-response") {
236
+ throw new UnsupportedFunctionalityError({
237
+ functionality: "tool approval responses"
238
+ });
239
+ }
240
+ input.push({
241
+ type: "function_call_output",
242
+ call_id: part.toolCallId,
243
+ name: part.toolName,
244
+ output: serializeToolOutput(part.output),
245
+ thought_signature: getThoughtSignature(part.providerOptions)
246
+ });
247
+ }
248
+ break;
187
249
  }
188
250
  default: {
189
251
  const _exhaustiveCheck = role;
@@ -191,141 +253,420 @@ function convertToPerplexityMessages(prompt) {
191
253
  }
192
254
  }
193
255
  }
194
- return messages;
256
+ return { input, warnings };
195
257
  }
196
258
 
197
259
  // src/map-perplexity-finish-reason.ts
198
- function mapPerplexityFinishReason(finishReason) {
199
- switch (finishReason) {
200
- case "stop":
201
- case "length":
202
- return finishReason;
260
+ function mapPerplexityFinishReason({
261
+ status,
262
+ incompleteReason,
263
+ hasFunctionCall
264
+ }) {
265
+ switch (incompleteReason) {
266
+ case "max_output_tokens":
267
+ return "length";
268
+ case "content_filter":
269
+ return "content-filter";
270
+ }
271
+ switch (status) {
272
+ case "completed":
273
+ return hasFunctionCall ? "tool-calls" : "stop";
274
+ case "requires_action":
275
+ return "tool-calls";
276
+ case "failed":
277
+ return "error";
278
+ case "cancelled":
279
+ case "queued":
280
+ case "in_progress":
203
281
  default:
204
- return "other";
282
+ return hasFunctionCall ? "tool-calls" : "other";
205
283
  }
206
284
  }
207
285
 
208
- // src/perplexity-language-model-options.ts
286
+ // src/perplexity-agent-api.ts
209
287
  import { z as z2 } from "zod/v4";
210
- var perplexityLanguageModelOptions = z2.looseObject({
211
- /**
212
- * Filters search results to those published within the specified time window.
213
- * Cannot be combined with other date filters.
214
- */
215
- search_recency_filter: z2.enum(["hour", "day", "week", "month", "year"]).optional(),
216
- /**
217
- * Restrict web search results to specific domains or URLs.
218
- * Prefix a domain with "-" to exclude it.
219
- */
220
- search_domain_filter: z2.array(z2.string()).optional(),
221
- /**
222
- * Filter search results by language using ISO 639-1 codes.
223
- */
224
- search_language_filter: z2.array(z2.string()).optional(),
225
- /**
226
- * Return search results published after this date.
227
- */
228
- search_after_date_filter: z2.string().optional(),
229
- /**
230
- * Return search results published before this date.
231
- */
232
- search_before_date_filter: z2.string().optional(),
233
- /**
234
- * Return search results last updated after this date.
235
- */
236
- last_updated_after_filter: z2.string().optional(),
237
- /**
238
- * Return search results last updated before this date.
239
- */
240
- last_updated_before_filter: z2.string().optional(),
241
- /**
242
- * Source of search results.
243
- */
244
- search_mode: z2.enum(["web", "academic", "sec"]).optional(),
245
- /**
246
- * If true, the model decides whether web search is needed.
247
- */
248
- enable_search_classifier: z2.boolean().optional(),
249
- /**
250
- * If true, disables web search.
251
- */
252
- disable_search: z2.boolean().optional(),
253
- /**
254
- * If true, a list of related questions is included in the response.
255
- */
256
- return_related_questions: z2.boolean().optional(),
257
- /**
258
- * If true, image search results are included in the response.
259
- */
260
- return_images: z2.boolean().optional(),
261
- /**
262
- * Restrict image results to specific domains.
263
- * Prefix a domain with "-" to exclude it.
264
- */
265
- image_domain_filter: z2.array(z2.string()).optional(),
288
+ var perplexitySearchResultSchema = z2.looseObject({
289
+ id: z2.number().optional(),
290
+ title: z2.string(),
291
+ url: z2.string(),
292
+ snippet: z2.string().optional(),
293
+ date: z2.string().nullish(),
294
+ last_updated: z2.string().nullish(),
295
+ source: z2.string().optional()
296
+ });
297
+ var perplexityFetchedContentSchema = z2.looseObject({
298
+ title: z2.string(),
299
+ url: z2.string(),
300
+ snippet: z2.string().optional()
301
+ });
302
+ var perplexityContentPartSchema = z2.looseObject({
303
+ type: z2.string(),
304
+ text: z2.string().optional(),
305
+ annotations: z2.array(
306
+ z2.looseObject({
307
+ type: z2.string().optional(),
308
+ url: z2.string().optional(),
309
+ title: z2.string().optional()
310
+ })
311
+ ).nullish()
312
+ });
313
+ var handledOutputTypes = [
314
+ "message",
315
+ "search_results",
316
+ "fetch_url_results",
317
+ "function_call"
318
+ ];
319
+ var perplexityOutputItemSchema = z2.union([
320
+ z2.looseObject({
321
+ type: z2.literal("message"),
322
+ id: z2.string().nullish(),
323
+ content: z2.array(perplexityContentPartSchema).nullish()
324
+ }),
325
+ z2.looseObject({
326
+ type: z2.literal("search_results"),
327
+ results: z2.array(perplexitySearchResultSchema).nullish()
328
+ }),
329
+ z2.looseObject({
330
+ type: z2.literal("fetch_url_results"),
331
+ contents: z2.array(perplexityFetchedContentSchema).nullish()
332
+ }),
333
+ z2.looseObject({
334
+ type: z2.literal("function_call"),
335
+ id: z2.string().nullish(),
336
+ call_id: z2.string().nullish(),
337
+ name: z2.string().nullish(),
338
+ arguments: z2.string().nullish(),
339
+ thought_signature: z2.string().nullish()
340
+ }),
341
+ // Native tool traces remain available in raw responses and raw stream chunks.
342
+ // Their fields (e.g. finance_results.results) have different shapes. Do not
343
+ // validate them as search results, or let malformed handled items fall back.
344
+ z2.object({
345
+ type: z2.string().refine((type) => !handledOutputTypes.includes(type))
346
+ }).transform(() => ({ type: "unhandled" }))
347
+ ]);
348
+ var perplexityCostSchema = z2.looseObject({
349
+ currency: z2.string().optional(),
350
+ input_cost: z2.number().optional(),
351
+ output_cost: z2.number().optional(),
352
+ total_cost: z2.number().optional(),
353
+ cache_creation_cost: z2.number().nullish(),
354
+ cache_read_cost: z2.number().nullish(),
355
+ tool_calls_cost: z2.number().nullish()
356
+ });
357
+ var perplexityUsageSchema = z2.looseObject({
358
+ input_tokens: z2.number(),
359
+ output_tokens: z2.number(),
360
+ total_tokens: z2.number(),
361
+ input_tokens_details: z2.looseObject({
362
+ cached_tokens: z2.number().optional(),
363
+ cache_creation_input_tokens: z2.number().optional(),
364
+ cache_read_input_tokens: z2.number().optional()
365
+ }).nullish(),
366
+ output_tokens_details: z2.looseObject({
367
+ reasoning_tokens: z2.number().optional()
368
+ }).nullish(),
369
+ tool_calls_details: z2.record(
370
+ z2.string(),
371
+ z2.looseObject({
372
+ invocation: z2.number().optional()
373
+ })
374
+ ).nullish(),
375
+ cost: perplexityCostSchema.nullish()
376
+ });
377
+ var perplexityAgentResponseSchema = z2.looseObject({
378
+ id: z2.string(),
379
+ created_at: z2.number(),
380
+ model: z2.string(),
381
+ object: z2.literal("response"),
382
+ output: z2.array(perplexityOutputItemSchema),
383
+ status: z2.string(),
384
+ incomplete_details: z2.looseObject({
385
+ reason: z2.string()
386
+ }).nullish(),
387
+ error: z2.looseObject({
388
+ code: z2.string().optional(),
389
+ message: z2.string(),
390
+ type: z2.string().optional()
391
+ }).nullish(),
392
+ usage: perplexityUsageSchema.nullish()
393
+ });
394
+ var perplexityAgentChunkSchema = z2.looseObject({
395
+ type: z2.string(),
396
+ sequence_number: z2.number().nullish(),
397
+ response: perplexityAgentResponseSchema.nullish(),
398
+ item: perplexityOutputItemSchema.nullish(),
399
+ output_index: z2.number().nullish(),
400
+ item_id: z2.string().nullish(),
401
+ content_index: z2.number().nullish(),
402
+ delta: z2.string().nullish(),
403
+ text: z2.string().nullish(),
404
+ thought: z2.string().nullish(),
405
+ queries: z2.array(z2.string()).nullish(),
406
+ urls: z2.array(z2.string()).nullish(),
407
+ results: z2.array(perplexitySearchResultSchema).nullish(),
408
+ contents: z2.array(perplexityFetchedContentSchema).nullish(),
409
+ error: z2.looseObject({
410
+ code: z2.string().nullish(),
411
+ message: z2.string(),
412
+ type: z2.string().nullish()
413
+ }).nullish()
414
+ });
415
+ var perplexityErrorSchema = z2.looseObject({
416
+ error: z2.union([
417
+ z2.string(),
418
+ z2.looseObject({
419
+ code: z2.union([z2.string(), z2.number()]).optional(),
420
+ message: z2.string().nullish(),
421
+ type: z2.string().nullish()
422
+ })
423
+ ]).optional(),
424
+ detail: z2.union([
425
+ z2.string(),
426
+ z2.array(
427
+ z2.looseObject({
428
+ msg: z2.string()
429
+ })
430
+ )
431
+ ]).optional(),
432
+ message: z2.string().optional()
433
+ });
434
+ function perplexityErrorToMessage(data) {
435
+ if (typeof data.error === "string") {
436
+ return data.error;
437
+ }
438
+ if (data.error != null) {
439
+ return data.error.message ?? data.error.type ?? "unknown error";
440
+ }
441
+ if (typeof data.detail === "string") {
442
+ return data.detail;
443
+ }
444
+ if (Array.isArray(data.detail)) {
445
+ return data.detail.map((detail) => detail.msg).join(", ");
446
+ }
447
+ return data.message ?? "unknown error";
448
+ }
449
+
450
+ // src/perplexity-language-model-options.ts
451
+ import { z as z3 } from "zod/v4";
452
+ var userLocationSchema = z3.looseObject({
453
+ latitude: z3.number().optional(),
454
+ longitude: z3.number().optional(),
455
+ country: z3.string().optional(),
456
+ city: z3.string().optional(),
457
+ region: z3.string().optional()
458
+ });
459
+ var webSearchFiltersSchema = z3.looseObject({
460
+ search_domain_filter: z3.array(z3.string()).optional(),
461
+ search_recency_filter: z3.enum(["hour", "day", "week", "month", "year"]).optional(),
462
+ search_after_date_filter: z3.string().optional(),
463
+ search_before_date_filter: z3.string().optional(),
464
+ last_updated_after_filter: z3.string().optional(),
465
+ last_updated_before_filter: z3.string().optional()
466
+ });
467
+ var webSearchToolSchema = z3.looseObject({
468
+ type: z3.literal("web_search"),
469
+ filters: webSearchFiltersSchema.optional(),
470
+ max_results: z3.number().int().positive().optional(),
471
+ max_tokens: z3.number().optional(),
472
+ max_tokens_per_page: z3.number().optional(),
473
+ search_context_size: z3.enum(["low", "medium", "high"]).optional(),
474
+ user_location: userLocationSchema.optional()
475
+ });
476
+ var nativeToolSchema = z3.union([
477
+ webSearchToolSchema,
478
+ z3.looseObject({
479
+ type: z3.literal("fetch_url"),
480
+ max_urls: z3.number().optional()
481
+ }),
482
+ z3.looseObject({ type: z3.literal("people_search") }),
483
+ z3.looseObject({ type: z3.literal("finance_search") }),
484
+ z3.looseObject({ type: z3.literal("sandbox") }),
485
+ z3.looseObject({
486
+ type: z3.literal("mcp"),
487
+ server_label: z3.string(),
488
+ server_url: z3.string(),
489
+ allowed_tools: z3.array(z3.string()).optional(),
490
+ authorization: z3.string().optional(),
491
+ defer_loading: z3.boolean().optional(),
492
+ headers: z3.record(z3.string(), z3.string()).optional()
493
+ }),
494
+ z3.looseObject({
495
+ type: z3.literal("connector"),
496
+ id: z3.string(),
497
+ server_label: z3.string(),
498
+ server_description: z3.string().optional(),
499
+ allowed_tools: z3.array(z3.string()).optional()
500
+ })
501
+ ]);
502
+ var perplexityLanguageModelOptions = z3.looseObject({
503
+ /** Top-level Agent API instructions. */
504
+ instructions: z3.string().optional(),
266
505
  /**
267
- * Restrict image results to specific file formats.
506
+ * Native Agent API tools. AI SDK function tools can also be supplied through
507
+ * the top-level `tools` option on `generateText` and `streamText`.
268
508
  */
269
- image_format_filter: z2.array(z2.string()).optional(),
509
+ tools: z3.array(nativeToolSchema).optional(),
510
+ /** A fallback model list for Agent API routing. */
511
+ models: z3.array(z3.string()).optional(),
512
+ /** Maximum number of agentic steps. */
513
+ max_steps: z3.number().int().positive().optional(),
514
+ /** Maximum number of native tool calls. */
515
+ max_tool_calls: z3.number().int().nonnegative().optional(),
516
+ /** Continue a conversation from an earlier Agent API response. */
517
+ previous_response_id: z3.string().optional(),
270
518
  /**
271
- * Additional media response configuration.
519
+ * Whether the response can be retrieved later. Setting this to false does
520
+ * not disable persistence or prevent previous_response_id continuations.
272
521
  */
273
- media_response: z2.looseObject({
274
- overrides: z2.looseObject({
275
- /**
276
- * If true, video results are included in the response.
277
- */
278
- return_videos: z2.boolean().optional()
279
- }).optional()
522
+ store: z3.boolean().optional(),
523
+ /** Preferred response language as an ISO 639-1 language code. */
524
+ language_preference: z3.string().optional(),
525
+ /** Agent API reasoning configuration. */
526
+ reasoning: z3.looseObject({
527
+ effort: z3.enum(["minimal", "low", "medium", "high", "xhigh"]).optional()
280
528
  }).optional(),
281
- /**
282
- * Controls the format of streaming events.
283
- */
284
- stream_mode: z2.enum(["full", "concise"]).optional(),
285
- /**
286
- * Controls how much effort the model spends on reasoning.
287
- */
288
- reasoning_effort: z2.enum(["minimal", "low", "medium", "high"]).optional(),
289
- /**
290
- * Preferred response language as an ISO 639-1 language code.
291
- */
292
- language_preference: z2.string().optional(),
293
- /**
294
- * Additional web search configuration.
295
- */
296
- web_search_options: z2.looseObject({
297
- /**
298
- * Controls the size of search context injected into the model.
299
- */
300
- search_context_size: z2.enum(["low", "medium", "high"]).optional(),
301
- /**
302
- * Controls whether to use fast search, pro search, or automatic routing.
303
- */
304
- search_type: z2.enum(["fast", "pro", "auto"]).optional(),
305
- /**
306
- * User location for search result personalization.
307
- */
308
- user_location: z2.looseObject({
309
- latitude: z2.number().optional(),
310
- longitude: z2.number().optional(),
311
- country: z2.string().optional(),
312
- city: z2.string().optional(),
313
- region: z2.string().optional()
314
- }).optional(),
315
- /**
316
- * If true, applies enhanced relevance filtering to image results.
317
- */
318
- image_results_enhanced_relevance: z2.boolean().optional()
319
- }).optional()
529
+ /** Agent API skill configuration. */
530
+ skills: z3.array(
531
+ z3.union([
532
+ z3.looseObject({
533
+ type: z3.literal("builtin"),
534
+ name: z3.enum([
535
+ "office",
536
+ "office/docx",
537
+ "office/pdf",
538
+ "office/pptx",
539
+ "office/xlsx"
540
+ ])
541
+ }),
542
+ z3.looseObject({
543
+ type: z3.literal("inline"),
544
+ name: z3.string(),
545
+ description: z3.string(),
546
+ instructions: z3.string()
547
+ })
548
+ ])
549
+ ).optional()
320
550
  });
321
551
 
552
+ // src/perplexity-prepare-tools.ts
553
+ function preparePerplexityTools({
554
+ tools,
555
+ toolChoice
556
+ }) {
557
+ const preparedTools = [];
558
+ const warnings = [];
559
+ for (const tool of tools ?? []) {
560
+ if (tool.type === "provider") {
561
+ warnings.push({
562
+ type: "unsupported",
563
+ feature: `provider-defined tool ${tool.name}`
564
+ });
565
+ continue;
566
+ }
567
+ preparedTools.push({
568
+ type: "function",
569
+ name: tool.name,
570
+ description: tool.description,
571
+ parameters: tool.inputSchema,
572
+ strict: tool.strict
573
+ });
574
+ }
575
+ if (toolChoice != null && toolChoice.type !== "auto") {
576
+ warnings.push({
577
+ type: "unsupported",
578
+ feature: "toolChoice",
579
+ details: "The Perplexity Agent API currently selects tools automatically."
580
+ });
581
+ }
582
+ return { tools: preparedTools, warnings };
583
+ }
584
+
322
585
  // src/perplexity-language-model.ts
586
+ var presetIds = /* @__PURE__ */ new Set([
587
+ "fast",
588
+ "low",
589
+ "medium",
590
+ "high",
591
+ "xhigh"
592
+ ]);
593
+ function getModelSelection(modelId) {
594
+ if (presetIds.has(modelId)) {
595
+ return { preset: modelId };
596
+ }
597
+ return { model: modelId };
598
+ }
599
+ function getResponseMetadata(response) {
600
+ return createLanguageModelResponseMetadata({
601
+ id: response.id,
602
+ model: response.model,
603
+ created: response.created_at
604
+ });
605
+ }
606
+ function getProviderMetadata(usage) {
607
+ const cost = usage?.cost;
608
+ const numSearchQueries = usage?.tool_calls_details ? Object.entries(usage.tool_calls_details).filter(([name]) => name.includes("search")).reduce((total, [, details]) => total + (details.invocation ?? 0), 0) : null;
609
+ return {
610
+ perplexity: {
611
+ usage: {
612
+ citationTokens: null,
613
+ numSearchQueries
614
+ },
615
+ images: null,
616
+ cost: cost == null ? null : {
617
+ inputTokensCost: cost.input_cost ?? null,
618
+ outputTokensCost: cost.output_cost ?? null,
619
+ requestCost: null,
620
+ totalCost: cost.total_cost ?? null,
621
+ currency: cost.currency ?? null,
622
+ cacheCreationCost: cost.cache_creation_cost ?? null,
623
+ cacheReadCost: cost.cache_read_cost ?? null,
624
+ toolCallsCost: cost.tool_calls_cost ?? null
625
+ },
626
+ toolCalls: usage?.tool_calls_details == null ? null : Object.fromEntries(
627
+ Object.entries(usage.tool_calls_details).map(
628
+ ([name, details]) => [
629
+ name,
630
+ { invocation: details.invocation ?? null }
631
+ ]
632
+ )
633
+ )
634
+ }
635
+ };
636
+ }
637
+ function createSource(result, generateId2) {
638
+ return {
639
+ type: "source",
640
+ sourceType: "url",
641
+ id: result.id == null ? generateId2() : String(result.id),
642
+ url: result.url,
643
+ title: result.title,
644
+ providerMetadata: {
645
+ perplexity: {
646
+ resultId: result.id ?? null,
647
+ snippet: result.snippet ?? null,
648
+ date: result.date ?? null,
649
+ lastUpdated: result.last_updated ?? null,
650
+ source: result.source ?? null
651
+ }
652
+ }
653
+ };
654
+ }
655
+ function hasSearchResultId(source) {
656
+ return typeof source.providerMetadata?.perplexity?.resultId === "number";
657
+ }
658
+ function getSearchResults(item) {
659
+ return item.type === "search_results" ? item.results ?? [] : [];
660
+ }
661
+ function getFetchedSources(item) {
662
+ return item.type === "fetch_url_results" ? item.contents ?? [] : [];
663
+ }
323
664
  var PerplexityLanguageModel = class _PerplexityLanguageModel {
324
665
  constructor(modelId, config) {
325
666
  this.specificationVersion = "v4";
326
667
  this.provider = "perplexity";
327
668
  this.supportedUrls = {
328
- // No URLs are supported.
669
+ "image/*": [/^https?:\/\/.*$/]
329
670
  };
330
671
  this.modelId = modelId;
331
672
  this.config = config;
@@ -351,7 +692,9 @@ var PerplexityLanguageModel = class _PerplexityLanguageModel {
351
692
  reasoning,
352
693
  responseFormat,
353
694
  seed,
354
- providerOptions
695
+ providerOptions,
696
+ tools,
697
+ toolChoice
355
698
  }) {
356
699
  const warnings = [];
357
700
  const perplexityOptions = await parseProviderOptions({
@@ -362,84 +705,173 @@ var PerplexityLanguageModel = class _PerplexityLanguageModel {
362
705
  if (topK != null) {
363
706
  warnings.push({ type: "unsupported", feature: "topK" });
364
707
  }
708
+ if (frequencyPenalty != null) {
709
+ warnings.push({ type: "unsupported", feature: "frequencyPenalty" });
710
+ }
711
+ if (presencePenalty != null) {
712
+ warnings.push({ type: "unsupported", feature: "presencePenalty" });
713
+ }
365
714
  if (stopSequences != null) {
366
715
  warnings.push({ type: "unsupported", feature: "stopSequences" });
367
716
  }
368
717
  if (seed != null) {
369
718
  warnings.push({ type: "unsupported", feature: "seed" });
370
719
  }
371
- if (isCustomReasoning(reasoning)) {
720
+ const { tools: nativeTools, ...agentOptions } = perplexityOptions;
721
+ const modelSelection = getModelSelection(this.modelId);
722
+ const { input, warnings: inputWarnings } = convertToPerplexityInput(prompt);
723
+ warnings.push(...inputWarnings);
724
+ const { tools: functionTools, warnings: toolWarnings } = preparePerplexityTools({ tools, toolChoice });
725
+ warnings.push(...toolWarnings);
726
+ const agentTools = [
727
+ ...nativeTools ?? [],
728
+ ...functionTools
729
+ ];
730
+ let reasoningConfig = agentOptions.reasoning;
731
+ if (reasoningConfig == null && isCustomReasoning(reasoning)) {
732
+ if (reasoning === "none") {
733
+ warnings.push({
734
+ type: "unsupported",
735
+ feature: 'reasoning "none"'
736
+ });
737
+ } else {
738
+ reasoningConfig = { effort: reasoning };
739
+ }
740
+ }
741
+ const body = {
742
+ ...agentOptions,
743
+ ...modelSelection,
744
+ input,
745
+ max_output_tokens: maxOutputTokens,
746
+ temperature,
747
+ top_p: topP,
748
+ reasoning: reasoningConfig,
749
+ response_format: responseFormat?.type === "json" && responseFormat.schema != null ? {
750
+ type: "json_schema",
751
+ json_schema: {
752
+ name: responseFormat.name ?? "response",
753
+ description: responseFormat.description,
754
+ schema: responseFormat.schema,
755
+ strict: true
756
+ }
757
+ } : void 0,
758
+ tools: agentTools.length > 0 ? agentTools : void 0
759
+ };
760
+ if (responseFormat?.type === "json" && responseFormat.schema == null) {
372
761
  warnings.push({
373
762
  type: "unsupported",
374
- feature: "reasoning",
375
- details: "This provider does not support reasoning configuration."
763
+ feature: "JSON response format without a schema"
376
764
  });
377
765
  }
378
- return {
379
- args: {
380
- // model id:
381
- model: this.modelId,
382
- // standardized settings:
383
- frequency_penalty: frequencyPenalty,
384
- max_tokens: maxOutputTokens,
385
- presence_penalty: presencePenalty,
386
- temperature,
387
- top_k: topK,
388
- top_p: topP,
389
- // response format:
390
- response_format: responseFormat?.type === "json" ? {
391
- type: "json_schema",
392
- json_schema: { schema: responseFormat.schema }
393
- } : void 0,
394
- // provider extensions
395
- ...perplexityOptions,
396
- // messages:
397
- messages: convertToPerplexityMessages(prompt)
398
- },
399
- warnings
400
- };
766
+ return { args: body, warnings };
401
767
  }
402
768
  async doGenerate(options) {
403
769
  const { args: body, warnings } = await this.getArgs(options);
770
+ const url = `${this.config.baseURL}/v1/agent`;
404
771
  const {
405
772
  responseHeaders,
406
773
  value: response,
407
774
  rawValue: rawResponse
408
775
  } = await postJsonToApi({
409
- url: `${this.config.baseURL}/chat/completions`,
776
+ url,
410
777
  headers: combineHeaders(this.config.headers?.(), options.headers),
411
778
  body,
412
779
  failedResponseHandler: createJsonErrorResponseHandler({
413
780
  errorSchema: perplexityErrorSchema,
414
- errorToMessage
781
+ errorToMessage: perplexityErrorToMessage
415
782
  }),
416
783
  successfulResponseHandler: createJsonResponseHandler(
417
- perplexityResponseSchema
784
+ perplexityAgentResponseSchema
418
785
  ),
419
786
  abortSignal: options.abortSignal,
420
787
  fetch: this.config.fetch
421
788
  });
422
- const choice = response.choices[0];
423
- const content = [];
424
- const text = choice.message.content;
425
- if (text.length > 0) {
426
- content.push({ type: "text", text });
789
+ if (response.error != null || response.status === "failed") {
790
+ throw new APICallError({
791
+ message: response.error?.message ?? "Perplexity response failed",
792
+ url,
793
+ requestBodyValues: body,
794
+ statusCode: 400,
795
+ responseHeaders,
796
+ responseBody: rawResponse,
797
+ isRetryable: false
798
+ });
427
799
  }
428
- if (response.citations != null) {
429
- for (const url of response.citations) {
800
+ const content = [];
801
+ const sourceIndexesByUrl = /* @__PURE__ */ new Map();
802
+ let hasFunctionCall = false;
803
+ const addSource = (source) => {
804
+ const existingIndex = sourceIndexesByUrl.get(source.url);
805
+ if (existingIndex == null) {
806
+ sourceIndexesByUrl.set(source.url, content.length);
807
+ content.push(source);
808
+ } else if (hasSearchResultId(source) && !hasSearchResultId(content[existingIndex])) {
809
+ content[existingIndex] = source;
810
+ }
811
+ };
812
+ for (const item of response.output) {
813
+ if (item.type === "message") {
814
+ for (const part of item.content ?? []) {
815
+ if (part.type === "output_text" && part.text != null) {
816
+ content.push({ type: "text", text: part.text });
817
+ }
818
+ for (const annotation of part.annotations ?? []) {
819
+ if (annotation.url != null) {
820
+ addSource({
821
+ type: "source",
822
+ sourceType: "url",
823
+ id: this.config.generateId(),
824
+ url: annotation.url,
825
+ title: annotation.title
826
+ });
827
+ }
828
+ }
829
+ }
830
+ } else if (item.type === "search_results") {
831
+ for (const result of getSearchResults(item)) {
832
+ addSource(createSource(result, this.config.generateId));
833
+ }
834
+ } else if (item.type === "fetch_url_results") {
835
+ for (const result of getFetchedSources(item)) {
836
+ addSource({
837
+ type: "source",
838
+ sourceType: "url",
839
+ id: this.config.generateId(),
840
+ url: result.url,
841
+ title: result.title,
842
+ providerMetadata: {
843
+ perplexity: { snippet: result.snippet ?? null }
844
+ }
845
+ });
846
+ }
847
+ } else if (item.type === "function_call" && item.call_id != null && item.name != null && item.arguments != null) {
848
+ hasFunctionCall = true;
430
849
  content.push({
431
- type: "source",
432
- sourceType: "url",
433
- id: this.config.generateId(),
434
- url
850
+ type: "tool-call",
851
+ toolCallId: item.call_id,
852
+ toolName: item.name,
853
+ input: item.arguments,
854
+ providerMetadata: {
855
+ perplexity: {
856
+ itemId: item.id ?? null,
857
+ ...item.thought_signature != null && {
858
+ thoughtSignature: item.thought_signature
859
+ }
860
+ }
861
+ }
435
862
  });
436
863
  }
437
864
  }
865
+ const finishReason = response.incomplete_details?.reason ?? response.status;
438
866
  return {
439
867
  content,
440
868
  finishReason: {
441
- unified: mapPerplexityFinishReason(choice.finish_reason),
442
- raw: choice.finish_reason ?? void 0
869
+ unified: mapPerplexityFinishReason({
870
+ status: response.status,
871
+ incompleteReason: response.incomplete_details?.reason,
872
+ hasFunctionCall
873
+ }),
874
+ raw: finishReason
443
875
  },
444
876
  usage: convertPerplexityUsage(response.usage),
445
877
  request: { body },
@@ -449,41 +881,22 @@ var PerplexityLanguageModel = class _PerplexityLanguageModel {
449
881
  body: rawResponse
450
882
  },
451
883
  warnings,
452
- providerMetadata: {
453
- perplexity: {
454
- images: response.images?.map((image) => ({
455
- imageUrl: image.image_url,
456
- originUrl: image.origin_url,
457
- height: image.height,
458
- width: image.width
459
- })) ?? null,
460
- usage: {
461
- citationTokens: response.usage?.citation_tokens ?? null,
462
- numSearchQueries: response.usage?.num_search_queries ?? null
463
- },
464
- cost: response.usage?.cost ? {
465
- inputTokensCost: response.usage.cost.input_tokens_cost ?? null,
466
- outputTokensCost: response.usage.cost.output_tokens_cost ?? null,
467
- requestCost: response.usage.cost.request_cost ?? null,
468
- totalCost: response.usage.cost.total_cost ?? null
469
- } : null
470
- }
471
- }
884
+ providerMetadata: getProviderMetadata(response.usage)
472
885
  };
473
886
  }
474
887
  async doStream(options) {
475
888
  const { args, warnings } = await this.getArgs(options);
476
889
  const body = { ...args, stream: true };
477
890
  const { responseHeaders, value: response } = await postJsonToApi({
478
- url: `${this.config.baseURL}/chat/completions`,
891
+ url: `${this.config.baseURL}/v1/agent`,
479
892
  headers: combineHeaders(this.config.headers?.(), options.headers),
480
893
  body,
481
894
  failedResponseHandler: createJsonErrorResponseHandler({
482
895
  errorSchema: perplexityErrorSchema,
483
- errorToMessage
896
+ errorToMessage: perplexityErrorToMessage
484
897
  }),
485
898
  successfulResponseHandler: createEventSourceResponseHandler(
486
- perplexityChunkSchema
899
+ perplexityAgentChunkSchema
487
900
  ),
488
901
  abortSignal: options.abortSignal,
489
902
  fetch: this.config.fetch
@@ -492,20 +905,15 @@ var PerplexityLanguageModel = class _PerplexityLanguageModel {
492
905
  unified: "other",
493
906
  raw: void 0
494
907
  };
495
- let usage = void 0;
496
- const providerMetadata = {
497
- perplexity: {
498
- usage: {
499
- citationTokens: null,
500
- numSearchQueries: null
501
- },
502
- cost: null,
503
- images: null
504
- }
505
- };
506
- let isFirstChunk = true;
507
- let isActive = false;
508
- const self = this;
908
+ let usage;
909
+ let hasFunctionCall = false;
910
+ let hasResponseMetadata = false;
911
+ let activeReasoningId;
912
+ const textStates = /* @__PURE__ */ new Map();
913
+ const emittedSourceUrls = /* @__PURE__ */ new Set();
914
+ const pendingSourcesByUrl = /* @__PURE__ */ new Map();
915
+ const seenFunctionCalls = /* @__PURE__ */ new Set();
916
+ const generateId2 = this.config.generateId;
509
917
  return {
510
918
  stream: response.pipeThrough(
511
919
  new TransformStream({
@@ -517,79 +925,299 @@ var PerplexityLanguageModel = class _PerplexityLanguageModel {
517
925
  controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
518
926
  }
519
927
  if (!chunk.success) {
928
+ finishReason = { unified: "error", raw: void 0 };
520
929
  controller.enqueue({ type: "error", error: chunk.error });
521
930
  return;
522
931
  }
523
932
  const value = chunk.value;
524
- if (isFirstChunk) {
933
+ const getTextId = (itemId, outputIndex, contentIndex = 0) => {
934
+ const id = itemId ?? String(outputIndex ?? "text");
935
+ return contentIndex == null || contentIndex === 0 ? id : `${id}:${contentIndex}`;
936
+ };
937
+ const emitTextDelta = (id, delta) => {
938
+ let state = textStates.get(id);
939
+ if (state?.ended) {
940
+ return;
941
+ }
942
+ if (state == null) {
943
+ state = { text: "", ended: false };
944
+ textStates.set(id, state);
945
+ controller.enqueue({ type: "text-start", id });
946
+ }
947
+ state.text += delta;
948
+ controller.enqueue({ type: "text-delta", id, delta });
949
+ };
950
+ const finishText = (id, text) => {
951
+ const state = textStates.get(id);
952
+ if (state?.ended) {
953
+ return;
954
+ }
955
+ const emittedText = state?.text ?? "";
956
+ if (text != null && text.startsWith(emittedText) && text.length > emittedText.length) {
957
+ emitTextDelta(id, text.slice(emittedText.length));
958
+ }
959
+ const finalState = textStates.get(id);
960
+ if (finalState != null) {
961
+ finalState.ended = true;
962
+ controller.enqueue({ type: "text-end", id });
963
+ }
964
+ };
965
+ const finishOutputText = (item, outputIndex) => {
966
+ if (item.type === "message") {
967
+ for (const [contentIndex, part] of (item.content ?? []).entries()) {
968
+ if (part.type === "output_text") {
969
+ finishText(
970
+ getTextId(item.id, outputIndex, contentIndex),
971
+ part.text
972
+ );
973
+ }
974
+ }
975
+ }
976
+ };
977
+ const emitSource = (source) => {
978
+ if (emittedSourceUrls.has(source.url)) {
979
+ return;
980
+ }
981
+ if (hasSearchResultId(source)) {
982
+ pendingSourcesByUrl.delete(source.url);
983
+ emittedSourceUrls.add(source.url);
984
+ controller.enqueue(source);
985
+ } else if (!pendingSourcesByUrl.has(source.url)) {
986
+ pendingSourcesByUrl.set(source.url, source);
987
+ }
988
+ };
989
+ const emitReasoningThought = (thought) => {
990
+ if (activeReasoningId != null && thought != null) {
991
+ controller.enqueue({
992
+ type: "reasoning-delta",
993
+ id: activeReasoningId,
994
+ delta: thought
995
+ });
996
+ }
997
+ };
998
+ const emitFunctionCall = (item) => {
999
+ if (item.type !== "function_call" || item.call_id == null || item.name == null || item.arguments == null || seenFunctionCalls.has(item.call_id)) {
1000
+ return;
1001
+ }
1002
+ seenFunctionCalls.add(item.call_id);
1003
+ hasFunctionCall = true;
525
1004
  controller.enqueue({
526
- type: "response-metadata",
527
- ...getResponseMetadata(value)
1005
+ type: "tool-input-start",
1006
+ id: item.call_id,
1007
+ toolName: item.name
528
1008
  });
529
- value.citations?.forEach((url) => {
530
- controller.enqueue({
1009
+ controller.enqueue({
1010
+ type: "tool-input-delta",
1011
+ id: item.call_id,
1012
+ delta: item.arguments
1013
+ });
1014
+ controller.enqueue({ type: "tool-input-end", id: item.call_id });
1015
+ controller.enqueue({
1016
+ type: "tool-call",
1017
+ toolCallId: item.call_id,
1018
+ toolName: item.name,
1019
+ input: item.arguments,
1020
+ providerMetadata: {
1021
+ perplexity: {
1022
+ itemId: item.id ?? null,
1023
+ ...item.thought_signature != null && {
1024
+ thoughtSignature: item.thought_signature
1025
+ }
1026
+ }
1027
+ }
1028
+ });
1029
+ };
1030
+ const emitOutputSources = (item) => {
1031
+ if (item.type === "message") {
1032
+ for (const part of item.content ?? []) {
1033
+ for (const annotation of part.annotations ?? []) {
1034
+ if (annotation.url != null) {
1035
+ emitSource({
1036
+ type: "source",
1037
+ sourceType: "url",
1038
+ id: generateId2(),
1039
+ url: annotation.url,
1040
+ title: annotation.title
1041
+ });
1042
+ }
1043
+ }
1044
+ }
1045
+ }
1046
+ for (const result of getSearchResults(item)) {
1047
+ emitSource(createSource(result, generateId2));
1048
+ }
1049
+ for (const result of getFetchedSources(item)) {
1050
+ emitSource({
531
1051
  type: "source",
532
1052
  sourceType: "url",
533
- id: self.config.generateId(),
534
- url
1053
+ id: generateId2(),
1054
+ url: result.url,
1055
+ title: result.title,
1056
+ providerMetadata: {
1057
+ perplexity: { snippet: result.snippet ?? null }
1058
+ }
535
1059
  });
536
- });
537
- isFirstChunk = false;
538
- }
539
- if (value.usage != null) {
540
- usage = value.usage;
541
- providerMetadata.perplexity.usage = {
542
- citationTokens: value.usage.citation_tokens ?? null,
543
- numSearchQueries: value.usage.num_search_queries ?? null
544
- };
545
- providerMetadata.perplexity.cost = value.usage.cost ? {
546
- inputTokensCost: value.usage.cost.input_tokens_cost ?? null,
547
- outputTokensCost: value.usage.cost.output_tokens_cost ?? null,
548
- requestCost: value.usage.cost.request_cost ?? null,
549
- totalCost: value.usage.cost.total_cost ?? null
550
- } : null;
551
- }
552
- if (value.images != null) {
553
- providerMetadata.perplexity.images = value.images.map((image) => ({
554
- imageUrl: image.image_url,
555
- originUrl: image.origin_url,
556
- height: image.height,
557
- width: image.width
558
- }));
559
- }
560
- const choice = value.choices[0];
561
- if (choice?.finish_reason != null) {
562
- finishReason = {
563
- unified: mapPerplexityFinishReason(choice.finish_reason),
564
- raw: choice.finish_reason
565
- };
1060
+ }
1061
+ };
1062
+ switch (value.type) {
1063
+ case "response.created":
1064
+ case "response.in_progress": {
1065
+ if (!hasResponseMetadata && value.response != null) {
1066
+ controller.enqueue({
1067
+ type: "response-metadata",
1068
+ ...getResponseMetadata(value.response)
1069
+ });
1070
+ hasResponseMetadata = true;
1071
+ }
1072
+ break;
1073
+ }
1074
+ case "response.output_text.delta": {
1075
+ if (value.delta != null) {
1076
+ emitTextDelta(
1077
+ getTextId(
1078
+ value.item_id,
1079
+ value.output_index,
1080
+ value.content_index
1081
+ ),
1082
+ value.delta
1083
+ );
1084
+ }
1085
+ break;
1086
+ }
1087
+ case "response.output_text.done": {
1088
+ finishText(
1089
+ getTextId(
1090
+ value.item_id,
1091
+ value.output_index,
1092
+ value.content_index
1093
+ ),
1094
+ value.text
1095
+ );
1096
+ break;
1097
+ }
1098
+ case "response.reasoning.started": {
1099
+ if (activeReasoningId != null) {
1100
+ controller.enqueue({
1101
+ type: "reasoning-end",
1102
+ id: activeReasoningId
1103
+ });
1104
+ }
1105
+ activeReasoningId = `reasoning-${value.sequence_number ?? generateId2()}`;
1106
+ controller.enqueue({
1107
+ type: "reasoning-start",
1108
+ id: activeReasoningId
1109
+ });
1110
+ emitReasoningThought(value.thought);
1111
+ break;
1112
+ }
1113
+ case "response.reasoning.search_queries":
1114
+ case "response.reasoning.fetch_url_queries": {
1115
+ emitReasoningThought(value.thought);
1116
+ break;
1117
+ }
1118
+ case "response.reasoning.search_results": {
1119
+ emitReasoningThought(value.thought);
1120
+ for (const result of value.results ?? []) {
1121
+ emitSource(createSource(result, generateId2));
1122
+ }
1123
+ break;
1124
+ }
1125
+ case "response.reasoning.fetch_url_results": {
1126
+ emitReasoningThought(value.thought);
1127
+ for (const result of value.contents ?? []) {
1128
+ emitSource({
1129
+ type: "source",
1130
+ sourceType: "url",
1131
+ id: generateId2(),
1132
+ url: result.url,
1133
+ title: result.title,
1134
+ providerMetadata: {
1135
+ perplexity: { snippet: result.snippet ?? null }
1136
+ }
1137
+ });
1138
+ }
1139
+ break;
1140
+ }
1141
+ case "response.reasoning.stopped": {
1142
+ emitReasoningThought(value.thought);
1143
+ if (activeReasoningId != null) {
1144
+ controller.enqueue({
1145
+ type: "reasoning-end",
1146
+ id: activeReasoningId
1147
+ });
1148
+ activeReasoningId = void 0;
1149
+ }
1150
+ break;
1151
+ }
1152
+ case "response.output_item.done": {
1153
+ if (value.item != null) {
1154
+ finishOutputText(value.item, value.output_index);
1155
+ emitOutputSources(value.item);
1156
+ emitFunctionCall(value.item);
1157
+ }
1158
+ break;
1159
+ }
1160
+ case "response.completed":
1161
+ case "response.incomplete": {
1162
+ if (value.response != null) {
1163
+ if (!hasResponseMetadata) {
1164
+ controller.enqueue({
1165
+ type: "response-metadata",
1166
+ ...getResponseMetadata(value.response)
1167
+ });
1168
+ hasResponseMetadata = true;
1169
+ }
1170
+ for (const [
1171
+ outputIndex,
1172
+ item
1173
+ ] of value.response.output.entries()) {
1174
+ finishOutputText(item, outputIndex);
1175
+ emitOutputSources(item);
1176
+ emitFunctionCall(item);
1177
+ }
1178
+ usage = value.response.usage ?? void 0;
1179
+ const rawFinishReason = value.response.incomplete_details?.reason ?? value.response.status;
1180
+ finishReason = {
1181
+ unified: mapPerplexityFinishReason({
1182
+ status: value.response.status,
1183
+ incompleteReason: value.response.incomplete_details?.reason,
1184
+ hasFunctionCall
1185
+ }),
1186
+ raw: rawFinishReason
1187
+ };
1188
+ }
1189
+ break;
1190
+ }
1191
+ case "response.failed": {
1192
+ finishReason = { unified: "error", raw: "failed" };
1193
+ controller.enqueue({
1194
+ type: "error",
1195
+ error: value.error ?? new Error("Perplexity response failed")
1196
+ });
1197
+ break;
1198
+ }
566
1199
  }
567
- if (choice?.delta == null) {
568
- return;
1200
+ },
1201
+ flush(controller) {
1202
+ for (const source of pendingSourcesByUrl.values()) {
1203
+ controller.enqueue(source);
569
1204
  }
570
- const delta = choice.delta;
571
- const textContent = delta.content;
572
- if (textContent != null) {
573
- if (!isActive) {
574
- controller.enqueue({ type: "text-start", id: "0" });
575
- isActive = true;
576
- }
1205
+ if (activeReasoningId != null) {
577
1206
  controller.enqueue({
578
- type: "text-delta",
579
- id: "0",
580
- delta: textContent
1207
+ type: "reasoning-end",
1208
+ id: activeReasoningId
581
1209
  });
582
1210
  }
583
- },
584
- flush(controller) {
585
- if (isActive) {
586
- controller.enqueue({ type: "text-end", id: "0" });
1211
+ for (const [id, state] of textStates) {
1212
+ if (!state.ended) {
1213
+ controller.enqueue({ type: "text-end", id });
1214
+ }
587
1215
  }
588
1216
  controller.enqueue({
589
1217
  type: "finish",
590
1218
  finishReason,
591
1219
  usage: convertPerplexityUsage(usage),
592
- providerMetadata
1220
+ providerMetadata: getProviderMetadata(usage)
593
1221
  });
594
1222
  }
595
1223
  })
@@ -599,75 +1227,6 @@ var PerplexityLanguageModel = class _PerplexityLanguageModel {
599
1227
  };
600
1228
  }
601
1229
  };
602
- var perplexityCostSchema = z3.object({
603
- input_tokens_cost: z3.number().nullish(),
604
- output_tokens_cost: z3.number().nullish(),
605
- reasoning_tokens_cost: z3.number().nullish(),
606
- request_cost: z3.number().nullish(),
607
- citation_tokens_cost: z3.number().nullish(),
608
- search_queries_cost: z3.number().nullish(),
609
- total_cost: z3.number().nullish()
610
- }).catchall(z3.json());
611
- var perplexityUsageSchema = z3.object({
612
- prompt_tokens: z3.number(),
613
- completion_tokens: z3.number(),
614
- total_tokens: z3.number().nullish(),
615
- search_context_size: z3.enum(["low", "medium", "high"]).nullish(),
616
- citation_tokens: z3.number().nullish(),
617
- num_search_queries: z3.number().nullish(),
618
- reasoning_tokens: z3.number().nullish(),
619
- cost: perplexityCostSchema.nullish()
620
- }).catchall(z3.json());
621
- var perplexityImageSchema = z3.object({
622
- image_url: z3.string(),
623
- origin_url: z3.string(),
624
- height: z3.number(),
625
- width: z3.number()
626
- });
627
- var perplexityResponseSchema = z3.object({
628
- id: z3.string(),
629
- created: z3.number(),
630
- model: z3.string(),
631
- choices: z3.array(
632
- z3.object({
633
- message: z3.object({
634
- role: z3.literal("assistant"),
635
- content: z3.string()
636
- }),
637
- finish_reason: z3.string().nullish()
638
- })
639
- ),
640
- citations: z3.array(z3.string()).nullish(),
641
- images: z3.array(perplexityImageSchema).nullish(),
642
- usage: perplexityUsageSchema.nullish()
643
- });
644
- var perplexityChunkSchema = z3.object({
645
- id: z3.string(),
646
- created: z3.number(),
647
- model: z3.string(),
648
- choices: z3.array(
649
- z3.object({
650
- delta: z3.object({
651
- role: z3.literal("assistant").optional(),
652
- content: z3.string().nullish()
653
- }),
654
- finish_reason: z3.string().nullish()
655
- })
656
- ),
657
- citations: z3.array(z3.string()).nullish(),
658
- images: z3.array(perplexityImageSchema).nullish(),
659
- usage: perplexityUsageSchema.nullish()
660
- });
661
- var perplexityErrorSchema = z3.object({
662
- error: z3.object({
663
- code: z3.number(),
664
- message: z3.string().nullish(),
665
- type: z3.string().nullish()
666
- })
667
- });
668
- var errorToMessage = (data) => {
669
- return data.error.message ?? data.error.type ?? "unknown error";
670
- };
671
1230
 
672
1231
  // src/perplexity-embedding-model.ts
673
1232
  var PerplexityEmbeddingModel = class _PerplexityEmbeddingModel {
@@ -726,7 +1285,7 @@ var PerplexityEmbeddingModel = class _PerplexityEmbeddingModel {
726
1285
  },
727
1286
  failedResponseHandler: createJsonErrorResponseHandler2({
728
1287
  errorSchema: perplexityErrorSchema,
729
- errorToMessage: (data) => data.error.message ?? "Unknown error"
1288
+ errorToMessage: perplexityErrorToMessage
730
1289
  }),
731
1290
  successfulResponseHandler: createJsonResponseHandler2(
732
1291
  perplexityEmbeddingResponseSchema
@@ -779,7 +1338,7 @@ var perplexityEmbeddingResponseSchema = z4.object({
779
1338
  });
780
1339
 
781
1340
  // src/version.ts
782
- var VERSION = true ? "4.0.52" : "0.0.0-test";
1341
+ var VERSION = true ? "5.0.1" : "0.0.0-test";
783
1342
 
784
1343
  // src/perplexity-provider.ts
785
1344
  function createPerplexity(options = {}) {