Skip to content

Commit 3c9517b

Browse files
jliounisPSI Bot
authored andcommitted
feat: add Perplexity Search integration
Add @tanstack/ai-perplexity with: - Search API tool (POST https://api.perplexity.ai/search) wired as a TanStack AI tool definition. Returns {title, url, snippet, date?} per result and surfaces max_results, search_domain_filter, search_recency_filter, and date filters. - OpenAI-compatible chat client factory pointed at https://api.perplexity.ai so existing openai-SDK code can target Perplexity by swapping baseURL. - API key resolution from PERPLEXITY_API_KEY (falls back to PPLX_API_KEY). - Tests with mocked fetch covering auth, body shape, filter pass-through, domain-allow/deny mixing guard, error surface, and env-var fallback. - README + docs/adapters/perplexity.md page wired into docs/config.json.
1 parent 6a23e80 commit 3c9517b

18 files changed

Lines changed: 1049 additions & 13 deletions

File tree

docs/adapters/perplexity.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
title: Perplexity
3+
id: perplexity-adapter
4+
order: 10
5+
description: "Use the Perplexity Search API and OpenAI-compatible chat completions with TanStack AI via @tanstack/ai-perplexity."
6+
keywords:
7+
- tanstack ai
8+
- perplexity
9+
- search api
10+
- web search
11+
- adapter
12+
---
13+
14+
`@tanstack/ai-perplexity` integrates [Perplexity](https://www.perplexity.ai) with TanStack AI:
15+
16+
- A **Search API tool** that grounds your agent on the live web (`POST https://api.perplexity.ai/search`).
17+
- An **OpenAI-compatible chat client** that points the `openai` SDK at Perplexity's chat-completions endpoint, so existing OpenAI code can target Perplexity by swapping the base URL.
18+
19+
## Installation
20+
21+
```bash
22+
npm install @tanstack/ai-perplexity
23+
```
24+
25+
Set your API key (get one at <https://www.perplexity.ai/account/api/keys>):
26+
27+
```bash
28+
export PERPLEXITY_API_KEY=...
29+
# PPLX_API_KEY is also accepted
30+
```
31+
32+
## Search tool
33+
34+
Wrap the Search API as a TanStack AI tool and pass it to a chat agent so the model can fetch up-to-date web results:
35+
36+
```ts
37+
import { chat } from '@tanstack/ai'
38+
import { perplexitySearchTool } from '@tanstack/ai-perplexity'
39+
40+
const search = perplexitySearchTool({
41+
// optional: applied when the model omits max_results
42+
defaultMaxResults: 5,
43+
})
44+
45+
const stream = chat({
46+
// ... your text adapter ...
47+
tools: [search],
48+
messages: [
49+
{ role: 'user', content: 'What were the top AI papers this week?' },
50+
],
51+
})
52+
```
53+
54+
The tool input schema accepts:
55+
56+
| Field | Type | Notes |
57+
| --------------------------- | ------------------------------------------------- | ------------------------------------------------------------------ |
58+
| `query` | `string` (required) | The search query. |
59+
| `max_results` | `integer` (1–20) | Defaults to API default (10), or `defaultMaxResults` if configured.|
60+
| `search_domain_filter` | `string[]` | Allowlist (`"nytimes.com"`) **or** denylist (`"-pinterest.com"`) — never both. |
61+
| `search_recency_filter` | `"hour" \| "day" \| "week" \| "month" \| "year"` | Recency window. |
62+
| `search_after_date_filter` | `string` | `m/d/yyyy` — only results on/after this date. |
63+
| `search_before_date_filter` | `string` | `m/d/yyyy` — only results on/before this date. |
64+
65+
Each result is `{ title, url, snippet, date? }`.
66+
67+
### Direct client
68+
69+
If you want to call the Search API outside an agent loop:
70+
71+
```ts
72+
import { PerplexitySearchClient } from '@tanstack/ai-perplexity'
73+
74+
const client = new PerplexitySearchClient()
75+
const { results } = await client.search({
76+
query: 'mars sample return mission',
77+
max_results: 5,
78+
search_recency_filter: 'month',
79+
})
80+
```
81+
82+
## Chat (OpenAI-compatible)
83+
84+
Perplexity exposes `POST /v1/chat/completions` with the standard OpenAI Chat Completions shape. `createPerplexityChatClient` returns an `openai` SDK instance pointed at `https://api.perplexity.ai`:
85+
86+
```ts
87+
import { createPerplexityChatClient } from '@tanstack/ai-perplexity/chat'
88+
89+
const client = createPerplexityChatClient()
90+
const completion = await client.chat.completions.create({
91+
model: 'sonar',
92+
messages: [
93+
{ role: 'user', content: 'What is the latest on the Mars rover?' },
94+
],
95+
})
96+
```
97+
98+
## Configuration
99+
100+
```ts
101+
import { PerplexitySearchClient } from '@tanstack/ai-perplexity'
102+
103+
const client = new PerplexitySearchClient({
104+
apiKey: process.env.PERPLEXITY_API_KEY, // explicit key (optional)
105+
baseURL: 'https://api.perplexity.ai', // override (optional)
106+
fetch: globalThis.fetch, // custom fetch (optional)
107+
})
108+
```
109+
110+
## References
111+
112+
- Search quickstart: <https://docs.perplexity.ai/docs/search/quickstart>
113+
- Search API reference: <https://docs.perplexity.ai/api-reference/search-post>
114+
- Domain filters: <https://docs.perplexity.ai/docs/search/filters/domain-filter>
115+
- Date / recency filters: <https://docs.perplexity.ai/docs/search/filters/date-time-filters>

docs/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@
293293
{
294294
"label": "OpenRouter Adapter",
295295
"to": "adapters/openrouter"
296+
},
297+
{
298+
"label": "Perplexity",
299+
"to": "adapters/perplexity"
296300
}
297301
]
298302
},
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# @tanstack/ai-perplexity
2+
3+
[Perplexity](https://www.perplexity.ai) integration for [TanStack AI](https://tanstack.com/ai):
4+
5+
- **Search API tool** — call `POST https://api.perplexity.ai/search` from an LLM agent loop and get back ranked web results (`title`, `url`, `snippet`, `date?`) suitable for grounding/citation.
6+
- **OpenAI-compatible chat client** — a thin factory that points the `openai` SDK at Perplexity's chat-completions endpoint so you can reuse existing OpenAI code paths.
7+
8+
## Install
9+
10+
```bash
11+
pnpm add @tanstack/ai-perplexity
12+
```
13+
14+
Set your API key (get one at <https://www.perplexity.ai/account/api/keys>):
15+
16+
```bash
17+
export PERPLEXITY_API_KEY=...
18+
# PPLX_API_KEY is also accepted
19+
```
20+
21+
## Search tool
22+
23+
Wrap the Search API as a TanStack AI tool and pass it to a chat agent:
24+
25+
```ts
26+
import { perplexitySearchTool } from '@tanstack/ai-perplexity'
27+
28+
const search = perplexitySearchTool({
29+
// optional defaults
30+
defaultMaxResults: 5,
31+
})
32+
33+
// Use directly with chat()
34+
chat({
35+
tools: [search],
36+
// ...
37+
})
38+
```
39+
40+
The tool input schema accepts:
41+
42+
| field | type | notes |
43+
| --------------------------- | --------------------------------------------------- | ------------------------------------------------------------------ |
44+
| `query` | `string` (required) | The search query. |
45+
| `max_results` | `integer` (1–20) | Defaults to API default (10), or `defaultMaxResults` if configured.|
46+
| `search_domain_filter` | `string[]` | Allowlist (`"nytimes.com"`) **or** denylist (`"-pinterest.com"`) — never both. |
47+
| `search_recency_filter` | `"hour" \| "day" \| "week" \| "month" \| "year"` | Recency window. |
48+
| `search_after_date_filter` | `string` | `m/d/yyyy` — only results on/after this date. |
49+
| `search_before_date_filter` | `string` | `m/d/yyyy` — only results on/before this date. |
50+
51+
Output: `{ results: Array<{ title, url, snippet, date? }> }`.
52+
53+
### Direct client usage
54+
55+
If you don't need the tool wrapping, call the Search API directly:
56+
57+
```ts
58+
import { PerplexitySearchClient } from '@tanstack/ai-perplexity'
59+
60+
const client = new PerplexitySearchClient()
61+
const { results } = await client.search({
62+
query: 'mars sample return mission',
63+
max_results: 5,
64+
search_recency_filter: 'month',
65+
})
66+
```
67+
68+
## Chat (OpenAI-compatible)
69+
70+
Perplexity's chat completions endpoint is OpenAI-compatible, so you can target it by swapping the `baseURL`:
71+
72+
```ts
73+
import { createPerplexityChatClient } from '@tanstack/ai-perplexity/chat'
74+
75+
const client = createPerplexityChatClient()
76+
const completion = await client.chat.completions.create({
77+
model: 'sonar',
78+
messages: [
79+
{ role: 'user', content: 'What is the latest on the Mars rover?' },
80+
],
81+
})
82+
```
83+
84+
Env vars: `PERPLEXITY_API_KEY` (preferred) or `PPLX_API_KEY`.
85+
86+
## Docs
87+
88+
- Search quickstart: <https://docs.perplexity.ai/docs/search/quickstart>
89+
- Search API reference: <https://docs.perplexity.ai/api-reference/search-post>
90+
- Domain filters: <https://docs.perplexity.ai/docs/search/filters/domain-filter>
91+
- Date / recency filters: <https://docs.perplexity.ai/docs/search/filters/date-time-filters>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "@tanstack/ai-perplexity",
3+
"version": "0.1.0",
4+
"description": "Perplexity adapter for TanStack AI — Search API and OpenAI-compatible chat",
5+
"author": "",
6+
"license": "MIT",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/TanStack/ai.git",
10+
"directory": "packages/typescript/ai-perplexity"
11+
},
12+
"type": "module",
13+
"module": "./dist/esm/index.js",
14+
"types": "./dist/esm/index.d.ts",
15+
"exports": {
16+
".": {
17+
"types": "./dist/esm/index.d.ts",
18+
"import": "./dist/esm/index.js"
19+
},
20+
"./search": {
21+
"types": "./dist/esm/search/index.d.ts",
22+
"import": "./dist/esm/search/index.js"
23+
},
24+
"./chat": {
25+
"types": "./dist/esm/chat/index.d.ts",
26+
"import": "./dist/esm/chat/index.js"
27+
}
28+
},
29+
"files": [
30+
"dist",
31+
"src"
32+
],
33+
"scripts": {
34+
"build": "vite build",
35+
"clean": "premove ./build ./dist",
36+
"lint:fix": "eslint ./src --fix",
37+
"test:build": "publint --strict",
38+
"test:eslint": "eslint ./src",
39+
"test:lib": "vitest run",
40+
"test:lib:dev": "pnpm test:lib --watch",
41+
"test:types": "tsc"
42+
},
43+
"keywords": [
44+
"ai",
45+
"perplexity",
46+
"search",
47+
"tanstack",
48+
"adapter"
49+
],
50+
"dependencies": {
51+
"openai": "^6.9.1"
52+
},
53+
"devDependencies": {
54+
"@tanstack/ai": "workspace:*",
55+
"@vitest/coverage-v8": "4.0.14",
56+
"vite": "^7.2.7"
57+
},
58+
"peerDependencies": {
59+
"@tanstack/ai": "workspace:^"
60+
}
61+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import OpenAI from 'openai'
2+
import { getPerplexityApiKeyFromEnv } from '../utils/api-key'
3+
import type { ClientOptions } from 'openai'
4+
5+
export interface PerplexityChatClientConfig extends ClientOptions {
6+
/** Perplexity API key. Falls back to `PERPLEXITY_API_KEY` / `PPLX_API_KEY` env vars. */
7+
apiKey?: string
8+
/** Override the API base URL (defaults to https://api.perplexity.ai). */
9+
baseURL?: string
10+
}
11+
12+
const DEFAULT_BASE_URL = 'https://api.perplexity.ai'
13+
14+
/**
15+
* Create an OpenAI SDK client pointed at Perplexity's OpenAI-compatible
16+
* chat-completions endpoint.
17+
*
18+
* Perplexity exposes `POST /v1/chat/completions` with the standard OpenAI
19+
* Chat Completions request/response shape, so any code that consumes the
20+
* `openai` SDK can target Perplexity by swapping the `baseURL`.
21+
*
22+
* @example
23+
* ```ts
24+
* import { createPerplexityChatClient } from '@tanstack/ai-perplexity/chat'
25+
*
26+
* const client = createPerplexityChatClient()
27+
* const completion = await client.chat.completions.create({
28+
* model: 'sonar',
29+
* messages: [{ role: 'user', content: 'What is the latest on the Mars rover?' }],
30+
* })
31+
* ```
32+
*/
33+
export function createPerplexityChatClient(
34+
config: PerplexityChatClientConfig = {},
35+
): OpenAI {
36+
const { apiKey, baseURL, ...rest } = config
37+
return new OpenAI({
38+
...rest,
39+
apiKey: apiKey ?? getPerplexityApiKeyFromEnv(),
40+
baseURL: baseURL ?? DEFAULT_BASE_URL,
41+
})
42+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {
2+
createPerplexityChatClient,
3+
type PerplexityChatClientConfig,
4+
} from './client'
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Search API
2+
export {
3+
PerplexitySearchClient,
4+
perplexitySearchTool,
5+
type PerplexitySearchClientConfig,
6+
type PerplexitySearchRequest,
7+
type PerplexitySearchResponse,
8+
type PerplexitySearchResult,
9+
} from './search'
10+
11+
// OpenAI-compatible chat client (Perplexity chat completions endpoint)
12+
export {
13+
createPerplexityChatClient,
14+
type PerplexityChatClientConfig,
15+
} from './chat'
16+
17+
// Utilities
18+
export { getPerplexityApiKeyFromEnv } from './utils/api-key'

0 commit comments

Comments
 (0)