pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog
LLM

Perplexity

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

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 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.

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.

Installation

bun add @pipedog/perplexity

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

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

Prerequisites

  • A Perplexity account and an API key from Perplexity.
  • 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

Configuration

Prop

Type

The other options (organization, project, defaultHeaders, serviceTier, retryOnTimeout, retryTimeoutSecs) are OpenAIResponsesHttpLLMService's. See OpenAI Responses.

Base URLhttps://api.perplexity.ai/v1
Default modelopenai/gpt-5.6-luna

Settings

Runtime-updatable settings, passed as settings: new PerplexityAgentHttpLLMService.Settings({...}) (PerplexityAgentLLMSettings). They are the OpenAI Responses settings, with these defaults and additions:

SettingTypeDefaultDescription
modelstring | 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.
presetstring | nullnullPerplexity 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.
webSearchbooleantrueOffer the web_search tool on every request, alongside any tools from the context. A preset searches the web whatever this is set to.
maxStepsnumber | nullnullMaximum number of search and reasoning steps per response, sent as max_steps. null leaves Perplexity's default.
reasoningOpenAIResponsesReasoningConfig | nullnullReasoning 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

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:

// 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:

// 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:

// 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

  • Runs a response for each LLMContextFrame, between an LLMFullResponseStartFrame and an LLMFullResponseEndFrame, and streams the reply as LLMTextFrames. 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

RuntimeWorksWhat it needs
Bun✅ yesNothing.
Node.js✅ yesNothing.
Deno✅ yesNothing.
Cloudflare Workers✅ yesNothing.

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.

pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.

Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).

On this page