{message.role === 'user' ? 'User: ' : 'AI: '}
- {message.parts.map((part, index) => {
- if (part.type === 'text') {
- return
{part.text}
;
- }
-
- if (part.type === 'source') {
- return (
-
- [
-
- {part.source.title}
-
- ]
-
- );
- }
- })}
+ {message.parts
+ .filter(part => part.type !== 'source')
+ .map((part, index) => {
+ if (part.type === 'text') {
+ return
{part.text}
;
+ }
+ })}
+ {message.parts
+ .filter(part => part.type === 'source')
+ .map(part => (
+
+ [
+
+ {part.source.title ?? new URL(part.source.url).hostname}
+
+ ]
+
+ ))}
))}
diff --git a/examples/next-openai/package.json b/examples/next-openai/package.json
index c36ba785f840..8524913c2134 100644
--- a/examples/next-openai/package.json
+++ b/examples/next-openai/package.json
@@ -13,6 +13,7 @@
"@ai-sdk/deepseek": "0.1.9",
"@ai-sdk/openai": "1.1.10",
"@ai-sdk/google-vertex": "2.1.13",
+ "@ai-sdk/perplexity": "0.0.8",
"@ai-sdk/ui-utils": "1.1.13",
"@ai-sdk/react": "1.1.13",
"@vercel/blob": "^0.26.0",
diff --git a/packages/ai/core/generate-text/generate-text.test.ts b/packages/ai/core/generate-text/generate-text.test.ts
index 469c824507ca..26e51906591d 100644
--- a/packages/ai/core/generate-text/generate-text.test.ts
+++ b/packages/ai/core/generate-text/generate-text.test.ts
@@ -1,10 +1,10 @@
import { LanguageModelV1CallOptions } from '@ai-sdk/provider';
+import { mockId } from '@ai-sdk/provider-utils/test';
import { jsonSchema } from '@ai-sdk/ui-utils';
import assert from 'node:assert';
import { z } from 'zod';
import { Output } from '.';
import { ToolExecutionError } from '../../errors';
-import { mockId } from '../test/mock-id';
import { MockLanguageModelV1 } from '../test/mock-language-model-v1';
import { MockTracer } from '../test/mock-tracer';
import { tool } from '../tool/tool';
diff --git a/packages/ai/core/generate-text/stream-text.test.ts b/packages/ai/core/generate-text/stream-text.test.ts
index a9bdc9f4dbfb..6e3a82d8a8e2 100644
--- a/packages/ai/core/generate-text/stream-text.test.ts
+++ b/packages/ai/core/generate-text/stream-text.test.ts
@@ -10,6 +10,7 @@ import {
convertAsyncIterableToArray,
convertReadableStreamToArray,
convertResponseStreamToArray,
+ mockId,
} from '@ai-sdk/provider-utils/test';
import { jsonSchema } from '@ai-sdk/ui-utils';
import assert from 'node:assert';
@@ -17,7 +18,6 @@ import { z } from 'zod';
import { ToolExecutionError } from '../../errors/tool-execution-error';
import { StreamData } from '../../streams/stream-data';
import { createDataStream } from '../data-stream/create-data-stream';
-import { mockId } from '../test/mock-id';
import { MockLanguageModelV1 } from '../test/mock-language-model-v1';
import { createMockServerResponse } from '../test/mock-server-response';
import { MockTracer } from '../test/mock-tracer';
diff --git a/packages/ai/core/middleware/extract-reasoning-middleware.test.ts b/packages/ai/core/middleware/extract-reasoning-middleware.test.ts
index 88a137cd7077..0740251410c0 100644
--- a/packages/ai/core/middleware/extract-reasoning-middleware.test.ts
+++ b/packages/ai/core/middleware/extract-reasoning-middleware.test.ts
@@ -1,10 +1,10 @@
import {
convertArrayToReadableStream,
convertAsyncIterableToArray,
+ mockId,
} from '@ai-sdk/provider-utils/test';
import { generateText, streamText } from '../generate-text';
import { wrapLanguageModel } from '../middleware/wrap-language-model';
-import { mockId } from '../test/mock-id';
import { MockLanguageModelV1 } from '../test/mock-language-model-v1';
import { extractReasoningMiddleware } from './extract-reasoning-middleware';
diff --git a/packages/ai/test/index.ts b/packages/ai/test/index.ts
index 19303f530886..9cd5f65fd064 100644
--- a/packages/ai/test/index.ts
+++ b/packages/ai/test/index.ts
@@ -1,6 +1,8 @@
-export { convertArrayToReadableStream } from '@ai-sdk/provider-utils/test';
+export {
+ convertArrayToReadableStream,
+ mockId,
+} from '@ai-sdk/provider-utils/test';
export { MockEmbeddingModelV1 } from '../core/test/mock-embedding-model-v1';
-export { mockId } from '../core/test/mock-id';
export { MockLanguageModelV1 } from '../core/test/mock-language-model-v1';
export { mockValues } from '../core/test/mock-values';
diff --git a/packages/perplexity/package.json b/packages/perplexity/package.json
index f0f480f9174a..87b16d857ae7 100644
--- a/packages/perplexity/package.json
+++ b/packages/perplexity/package.json
@@ -30,7 +30,6 @@
}
},
"dependencies": {
- "@ai-sdk/openai-compatible": "0.1.9",
"@ai-sdk/provider": "1.0.7",
"@ai-sdk/provider-utils": "2.1.7"
},
diff --git a/packages/perplexity/src/__snapshots__/convert-to-perplexity-messages.test.ts.snap b/packages/perplexity/src/__snapshots__/convert-to-perplexity-messages.test.ts.snap
new file mode 100644
index 000000000000..108637d38eed
--- /dev/null
+++ b/packages/perplexity/src/__snapshots__/convert-to-perplexity-messages.test.ts.snap
@@ -0,0 +1,28 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`convertToPerplexityMessages > assistant messages > should convert an assistant message with text content 1`] = `
+[
+ {
+ "content": "Assistant reply",
+ "role": "assistant",
+ },
+]
+`;
+
+exports[`convertToPerplexityMessages > system messages > should convert a system message with text content 1`] = `
+[
+ {
+ "content": "System initialization",
+ "role": "system",
+ },
+]
+`;
+
+exports[`convertToPerplexityMessages > user messages > should convert a user message with text parts 1`] = `
+[
+ {
+ "content": "Hello World",
+ "role": "user",
+ },
+]
+`;
diff --git a/packages/perplexity/src/convert-to-perplexity-messages.test.ts b/packages/perplexity/src/convert-to-perplexity-messages.test.ts
new file mode 100644
index 000000000000..cef836f255a3
--- /dev/null
+++ b/packages/perplexity/src/convert-to-perplexity-messages.test.ts
@@ -0,0 +1,116 @@
+import { convertToPerplexityMessages } from './convert-to-perplexity-messages';
+import { UnsupportedFunctionalityError } from '@ai-sdk/provider';
+
+describe('convertToPerplexityMessages', () => {
+ describe('system messages', () => {
+ it('should convert a system message with text content', () => {
+ expect(
+ convertToPerplexityMessages([
+ {
+ role: 'system',
+ content: 'System initialization',
+ },
+ ]),
+ ).toMatchSnapshot();
+ });
+ });
+
+ describe('user messages', () => {
+ it('should convert a user message with text parts', () => {
+ expect(
+ convertToPerplexityMessages([
+ {
+ role: 'user',
+ content: [
+ { type: 'text', text: 'Hello ' },
+ { type: 'text', text: 'World' },
+ ],
+ },
+ ]),
+ ).toMatchSnapshot();
+ });
+
+ it('should throw an error for user messages with image parts', () => {
+ expect(() => {
+ convertToPerplexityMessages([
+ {
+ role: 'user',
+ content: [
+ { type: 'text', text: 'Hello ' },
+ {
+ type: 'image',
+ image: new Uint8Array([0, 1, 2, 3]),
+ mimeType: 'image/png',
+ },
+ ],
+ },
+ ]);
+ }).toThrow(UnsupportedFunctionalityError);
+ });
+
+ it('should throw an error for user messages with file parts', () => {
+ expect(() => {
+ convertToPerplexityMessages([
+ {
+ role: 'user',
+ content: [
+ { type: 'text', text: 'Document: ' },
+ { type: 'file', data: 'dummy-data', mimeType: 'text/plain' },
+ ],
+ },
+ ]);
+ }).toThrow(UnsupportedFunctionalityError);
+ });
+ });
+
+ describe('assistant messages', () => {
+ it('should convert an assistant message with text content', () => {
+ expect(
+ convertToPerplexityMessages([
+ {
+ role: 'assistant',
+ content: [{ type: 'text', text: 'Assistant reply' }],
+ },
+ ]),
+ ).toMatchSnapshot();
+ });
+
+ it('should throw an error for assistant messages with tool-call parts', () => {
+ expect(() => {
+ convertToPerplexityMessages([
+ {
+ role: 'assistant',
+ content: [
+ {
+ type: 'tool-call',
+ args: { key: 'value' },
+ toolCallId: 'call-1',
+ toolName: 'example-tool',
+ },
+ ],
+ },
+ ]);
+ }).toThrow(UnsupportedFunctionalityError);
+ });
+ });
+
+ describe('tool messages', () => {
+ it('should throw an error for tool messages', () => {
+ expect(() => {
+ convertToPerplexityMessages([
+ {
+ role: 'tool',
+ content: [
+ {
+ type: 'tool-result',
+ toolCallId: 'dummy-tool-call-id',
+ toolName: 'dummy-tool-name',
+ result: 'This should fail',
+ },
+ ],
+ },
+ ]);
+ }).toThrow(UnsupportedFunctionalityError);
+ });
+ });
+});
diff --git a/packages/perplexity/src/convert-to-perplexity-messages.ts b/packages/perplexity/src/convert-to-perplexity-messages.ts
new file mode 100644
index 000000000000..d0282787a246
--- /dev/null
+++ b/packages/perplexity/src/convert-to-perplexity-messages.ts
@@ -0,0 +1,67 @@
+import {
+ LanguageModelV1Prompt,
+ UnsupportedFunctionalityError,
+} from '@ai-sdk/provider';
+import { PerplexityPrompt } from './perplexity-language-model-prompt';
+
+export function convertToPerplexityMessages(
+ prompt: LanguageModelV1Prompt,
+): PerplexityPrompt {
+ const messages: PerplexityPrompt = [];
+
+ for (const { role, content } of prompt) {
+ switch (role) {
+ case 'system': {
+ messages.push({ role: 'system', content });
+ break;
+ }
+
+ case 'user':
+ case 'assistant': {
+ messages.push({
+ role,
+ content: content
+ .map(part => {
+ switch (part.type) {
+ case 'text': {
+ return part.text;
+ }
+ case 'image': {
+ throw new UnsupportedFunctionalityError({
+ functionality: 'Image content parts in user messages',
+ });
+ }
+ case 'file': {
+ throw new UnsupportedFunctionalityError({
+ functionality: 'File content parts in user messages',
+ });
+ }
+ case 'tool-call': {
+ throw new UnsupportedFunctionalityError({
+ functionality: 'Tool calls in assistant messages',
+ });
+ }
+ default: {
+ const _exhaustiveCheck: never = part;
+ throw new Error(`Unsupported part: ${_exhaustiveCheck}`);
+ }
+ }
+ })
+ .join(''),
+ });
+ break;
+ }
+ case 'tool': {
+ throw new UnsupportedFunctionalityError({
+ functionality: 'Tool messages',
+ });
+ }
+ default: {
+ const _exhaustiveCheck: never = role;
+ throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
+ }
+ }
+ }
+
+ return messages;
+}
diff --git a/packages/perplexity/src/index.ts b/packages/perplexity/src/index.ts
index 3a3af86fc98e..c8d0dbdff943 100644
--- a/packages/perplexity/src/index.ts
+++ b/packages/perplexity/src/index.ts
@@ -1,6 +1,5 @@
export { createPerplexity, perplexity } from './perplexity-provider';
export type {
- PerplexityErrorData,
PerplexityProvider,
PerplexityProviderSettings,
} from './perplexity-provider';
diff --git a/packages/perplexity/src/map-perplexity-finish-reason.ts b/packages/perplexity/src/map-perplexity-finish-reason.ts
new file mode 100644
index 000000000000..aee2004d5547
--- /dev/null
+++ b/packages/perplexity/src/map-perplexity-finish-reason.ts
@@ -0,0 +1,13 @@
+import { LanguageModelV1FinishReason } from '@ai-sdk/provider';
+
+export function mapPerplexityFinishReason(
+ finishReason: string | null | undefined,
+): LanguageModelV1FinishReason {
+ switch (finishReason) {
+ case 'stop':
+ case 'length':
+ return finishReason;
+ default:
+ return 'unknown';
+ }
+}
diff --git a/packages/perplexity/src/perplexity-chat-settings.ts b/packages/perplexity/src/perplexity-chat-settings.ts
deleted file mode 100644
index a6dc46254f35..000000000000
--- a/packages/perplexity/src/perplexity-chat-settings.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { OpenAICompatibleChatSettings } from '@ai-sdk/openai-compatible';
-
-// https://docs.perplexity.ai/guides/model-cards
-export type PerplexityChatModelId = 'sonar-pro' | 'sonar' | (string & {});
-
-export interface PerplexityChatSettings extends OpenAICompatibleChatSettings {}
diff --git a/packages/perplexity/src/perplexity-language-model-prompt.ts b/packages/perplexity/src/perplexity-language-model-prompt.ts
new file mode 100644
index 000000000000..7c4f37778d32
--- /dev/null
+++ b/packages/perplexity/src/perplexity-language-model-prompt.ts
@@ -0,0 +1,6 @@
+export type PerplexityPrompt = Array