# Perplexity

> PerplexityAgentHttpLLMService answers with web search through Perplexity's Agent API, over the OpenAI Responses protocol.

Source: https://polvallverdu.github.io/pipedog/docs/services/llm/perplexity/



## Overview [#overview]

Perplexity's Agent API answers with web search, using models from Perplexity and from other
providers (`openai/...`, `anthropic/...` and so on). It speaks the OpenAI Responses protocol over
HTTP.

* **`PerplexityAgentHttpLLMService`**: an [`OpenAIResponsesHttpLLMService`](https://polvallverdu.github.io/pipedog/docs/services/llm/openai-responses/#openairesponseshttpllmservice)
  subclass with Perplexity's base URL, a default model, and Perplexity's own settings (`preset`,
  `webSearch`, `maxSteps`). Function calling, multi-turn context, token usage metrics and
  `runInference` work as they do with the OpenAI Responses services.

<Callout type="warn" title="PerplexityLLMService is not ported">
  Upstream's `PerplexityLLMService`, for the Sonar chat completions API, is deprecated since
  Pipecat 1.13.0, as Perplexity no longer supports that API. pipedog doesn't port it. Use
  `PerplexityAgentHttpLLMService`.
</Callout>

<Cards>
  <Card title="Upstream page" href="https://docs.pipecat.ai/api-reference/server/services/llm/perplexity">
    Pipecat's documentation for the same service.
  </Card>

  <Card title="Source" href="https://github.com/polvallverdu/pipedog/tree/main/packages/perplexity/src">
    `packages/perplexity/src/llm.ts`.
  </Card>

  <Card title="Perplexity docs" href="https://docs.perplexity.ai/">
    The Agent API, its presets and its models.
  </Card>
</Cards>

## Installation [#installation]

```bash
bun add @pipedog/perplexity
```

It depends on `@pipedog/openai`, which is installed with it.

```ts
import { PerplexityAgentHttpLLMService } from "@pipedog/perplexity/llm";
```

## Prerequisites [#prerequisites]

* A Perplexity account and an API key from [Perplexity](https://www.perplexity.ai/).
* Pass the key as `apiKey`. The examples read it from `PERPLEXITY_API_KEY`. If you leave it out,
  the OpenAI client falls back to `OPENAI_API_KEY`, as with the OpenAI Responses services, which
  is not a Perplexity key.

## PerplexityAgentHttpLLMService [#perplexityagenthttpllmservice]

### Configuration [#configuration]

| Option | Type | Required | Description |
|---|---|---|---|
| `apiKey` | `string \| null \| undefined` | no | The API key for accessing Perplexity's API. |
| `baseUrl` | `string \| null \| undefined` | no | The base URL for Perplexity's API. Defaults to `"https://api.perplexity.ai/v1"`. |
| `settings` | `PerplexityAgentLLMSettings \| null \| undefined` | no | Runtime-updatable settings. |
| `name` | `string \| null \| undefined` | no | Custom name; defaults to `ClassName#count`. |
| `taskManager` | `BaseTaskManager \| null \| undefined` | no | Task manager for handling tasks. |
| `enableDirectMode` | `boolean \| undefined` | no | Process frames immediately instead of using internal queues. |
| `metrics` | `FrameProcessorMetrics \| null \| undefined` | no | Optional metrics collector for this processor. |
| `runInParallel` | `boolean \| undefined` | no | Whether to run function calls in parallel or sequentially. Defaults to true. |
| `groupParallelTools` | `boolean \| undefined` | no | Whether to group parallel function calls so the LLM is triggered exactly once after all calls in the batch complete. When false, each function call result triggers the LLM independently as it arrives. Defaults to true. |
| `functionCallTimeoutSecs` | `number \| null \| undefined` | no | Optional timeout in seconds for function calls. A call that runs past it is cancelled: its handler is cancelled (it gets a `CancelledError`), the call is settled as cancelled, and inference runs so the LLM can report that it didn't complete. |
| `fileResolver` | `FileResolver \| null \| undefined` | no | Resolves file URLs in the context that this provider can't fetch itself (see `FileResolver`). Pass one configured with a `fileStorage` to resolve uploaded-file URLs (`new FileResolver({ fileStorage: runnerArgs.fileStorage })` when a runner hosts the bot), and share one instance across services that may consume the same files (e.g. services switched between mid-session) so downloads are reused. Defaults to a resolver that handles `data:` and `http(s)` URLs only. |
| `organization` | `string \| null \| undefined` | no | OpenAI organization ID. |
| `project` | `string \| null \| undefined` | no | OpenAI project ID. |
| `defaultHeaders` | `Record<string, string> \| null \| undefined` | no | Additional HTTP headers. |
| `serviceTier` | `string \| null \| undefined` | no | Service tier to use: "auto", "default", "flex", "scale", "fast", "priority" or "ultrafast". "fast" is OpenAI's low-latency tier, the name that replaced "priority"; both values are accepted. "ultrafast" is the access-controlled Ultrafast Processing tier, which shortens the time between output tokens on the models that offer it. |
| `retryTimeoutSecs` | `number \| null \| undefined` | no | How long an inference may go without producing output before it is abandoned and re-issued, when `retryOnTimeout` is set. Defaults to 5.0 seconds. |
| `retryOnTimeout` | `boolean \| null \| undefined` | no | Whether to re-issue the request once if the first attempt produces no output within `retryTimeoutSecs`. The retry is unbounded. |

The other options (`organization`, `project`, `defaultHeaders`, `serviceTier`, `retryOnTimeout`,
`retryTimeoutSecs`) are `OpenAIResponsesHttpLLMService`'s. See
[OpenAI Responses](https://polvallverdu.github.io/pipedog/docs/services/llm/openai-responses/#openairesponseshttpllmservice).

|               |                                |
| ------------- | ------------------------------ |
| Base URL      | `https://api.perplexity.ai/v1` |
| Default model | `openai/gpt-5.6-luna`          |

### Settings [#settings]

Runtime-updatable settings, passed as `settings: new PerplexityAgentHttpLLMService.Settings({...})`
(`PerplexityAgentLLMSettings`). They are the
[OpenAI Responses settings](https://polvallverdu.github.io/pipedog/docs/services/llm/openai-responses/#settings), with these defaults and
additions:

| Setting     | Type                                     | Default                 | Description                                                                                                                                                                                            |
| ----------- | ---------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model`     | `string \| null`                         | `"openai/gpt-5.6-luna"` | Model, as `<provider>/<model>`. With a `preset`, it replaces the preset's model; set it to `null` to use the preset's.                                                                                 |
| `preset`    | `string \| null`                         | `null`                  | Perplexity preset, such as `"fast"`, `"low"`, `"medium"`, `"high"` or `"xhigh"`. It sets the model, its instructions, its search budget and how much it reasons. Sent as `preset` in the request body. |
| `webSearch` | `boolean`                                | `true`                  | Offer the `web_search` tool on every request, alongside any tools from the context. A preset searches the web whatever this is set to.                                                                 |
| `maxSteps`  | `number \| null`                         | `null`                  | Maximum number of search and reasoning steps per response, sent as `max_steps`. `null` leaves Perplexity's default.                                                                                    |
| `reasoning` | `OpenAIResponsesReasoningConfig \| null` | `null`                  | Reasoning config (`PerplexityAgentHttpLLMService.ReasoningConfig`). See the default below.                                                                                                             |

Every other setting (`systemInstruction`, `temperature`, `topP`, `maxCompletionTokens`, `extra`, ...)
keeps its OpenAI Responses default. The settings need a `model`, a `preset`, or both: the
constructor throws when they leave neither, and a settings update that leaves neither pushes an
error.

### Usage [#usage]

```ts title="llm.ts"
import { LLMUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { PerplexityAgentHttpLLMService } from "@pipedog/perplexity/llm";

const llm = new PerplexityAgentHttpLLMService({
  apiKey: process.env.PERPLEXITY_API_KEY ?? "",
  settings: new PerplexityAgentHttpLLMService.Settings({
    systemInstruction: "You are a helpful assistant in a voice conversation. Keep replies short.",
  }),
});
```

Let a preset choose the model:

```ts
// The "high" preset chooses the model, its instructions, its search budget and its reasoning.
// `model: null` clears the default model, so the preset's own is used.
const thorough = new PerplexityAgentHttpLLMService({
  apiKey: process.env.PERPLEXITY_API_KEY ?? "",
  settings: new PerplexityAgentHttpLLMService.Settings({ preset: "high", model: null }),
});
```

Pin a model, cap the search steps and set the reasoning effort yourself:

```ts
// Another provider's model, with web search capped at three steps and a set reasoning effort.
const pinned = new PerplexityAgentHttpLLMService({
  apiKey: process.env.PERPLEXITY_API_KEY ?? "",
  settings: new PerplexityAgentHttpLLMService.Settings({
    model: "openai/gpt-5.5",
    maxSteps: 3,
    reasoning: new PerplexityAgentHttpLLMService.ReasoningConfig({ effort: "low" }),
  }),
});
```

Change settings mid-call with an `LLMUpdateSettingsFrame`. The next request uses them:

```ts
// Queue it on the worker, or push it from a processor upstream of the service.
const update = new LLMUpdateSettingsFrame({
  delta: new PerplexityAgentHttpLLMService.Settings({ webSearch: false, maxSteps: 1 }),
});
```

### Notes [#notes]

* Runs a response for each `LLMContextFrame`, between an `LLMFullResponseStartFrame` and an
  `LLMFullResponseEndFrame`, and streams the reply as `LLMTextFrame`s. Each turn sends the whole
  context, as `OpenAIResponsesHttpLLMService` does.
* When neither `preset` nor `reasoning` is set, the service sends Perplexity's `"fast"` preset,
  its lowest-latency configuration, with your model in place of the preset's own, and logs it on
  the first request for each model. A preset or `reasoning` you set (in the settings, or in
  `extra`) is sent as it is. Set `useProviderThinkingDefault` to send no default and run at
  Perplexity's.
* Perplexity runs its web search on the server. The search shows up in the stream as a function
  call, which the service drops, so only your own functions are run.
* Answers can carry citation markers such as `[1]`. Filter them out before TTS if you don't want
  them read aloud.
* Anthropic models need `maxCompletionTokens` set.
* Tokens written to the prompt cache are reported as `cacheCreationInputTokens` in token usage
  metrics.
* `PerplexityLLMAdapter` (`@pipedog/perplexity/perplexity_adapter`) is still exported, as upstream
  keeps it for the deprecated Sonar service, but `PerplexityAgentHttpLLMService` doesn't use it:
  its messages go through the OpenAI Responses adapter.

## Runtime notes [#runtime-notes]

| Runtime            | Works | What it needs |
| ------------------ | ----- | ------------- |
| Bun                | ✅ yes | Nothing.      |
| Node.js            | ✅ yes | Nothing.      |
| Deno               | ✅ yes | Nothing.      |
| Cloudflare Workers | ✅ yes | Nothing.      |

Generated from `pipecat.runtimes` and `pipecat.compat` in `packages/perplexity/package.json`: ✅ means the compat smoke test passes on that runtime.

* The client is the official `openai` JS SDK (through `@pipedog/openai`), which talks through
  `fetch`. It works on Bun, Node, Deno and Cloudflare Workers.
* The JS SDK has no `extra_body` request option, so the service's client merges the request's
  `extra_body` (where `preset` and `max_steps` go) into the request JSON.
* Unit-tested on the requests it builds and on scripted event streams, not against Perplexity
  itself.
