agentsclimarketplace

Migrate sonar to agent api

Skill perplexityai/api-platform-developers/skills/migrate-sonar-to-agent-api

Migrate code from Perplexity's Sonar API (chat completions) to the Agent API (/v1/agent, responses.create). Maps endpoints, request params, model slugs (sonar, sonar-pro, sonar-reasoning, sonar-deep-research), response parsing, streaming, and citations. Use when migrating from Sonar, moving chat completions to the Agent API, replacing deprecated sonar-pro or sonar-reasoning models, or fixing 400 unknown-field errors and missing citations after a migration attempt.From its SKILL.md

Install
npx -y skills add perplexityai/api-platform-developers --skill migrate-sonar-to-agent-api

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

11.3 KB, as published. Nobody here has run it

Migrate Sonar chat completions to the Perplexity Agent API

What this skill does

Migrates a codebase from Perplexity's Sonar API (POST /chat/completions, chat.completions.create()) to the Agent API (POST /v1/agent, responses.create()). Both APIs are live; this is a contract migration - endpoint, request body, response parsing, streaming - not a rewrite. Start by inventorying Sonar usage:

python scripts/scan_sonar_usage.py [path-to-code]

Run it from this skill's directory (the directory containing this SKILL.md), or pass the script's full path. It prints a file-by-file migration checklist with line numbers and the reference file that covers each finding.

Canonical human guide: https://docs.perplexity.ai/docs/agent-api/migrate-from-sonar - when this skill and the live docs disagree on an API fact, the docs win; cross-check there if something looks off. If a perplexity-docs MCP server is available in your session (the Claude Code plugin ships one; other clients can add https://docs.perplexity.ai/mcp), prefer its search tools for that cross-check. Never invent model availability, limits, defaults, or API behavior: if a fact is in neither this skill nor the docs, verify it against the live API - or state that you could not.

Step 1: classify each call site by integration style

Classify before touching code, and KEEP the style - do not "upgrade" raw HTTP to an SDK or swap SDKs.

  • (a) Raw HTTP (requests, fetch, curl to https://api.perplexity.ai/chat/completions): migrate to POST https://api.perplexity.ai/v1/agent with the same HTTP library.
  • (b) OpenAI SDK pointed at Perplexity: set base_url="https://api.perplexity.ai/v1" - the /v1 is required, the SDK appends /responses - and switch to client.responses.create(). Perplexity-only fields (preset, max_steps, response_format, Perplexity tool types) go through extra_body={...} in Python or a type cast in TypeScript. Requires a recent OpenAI SDK (openai>=1.66 for Python).
  • (c) Perplexity SDK (from perplexity import Perplexity / @perplexity-ai/perplexity_ai): switch client.chat.completions.create() to client.responses.create(). Same client construction, same PERPLEXITY_API_KEY env var.
  • (d) Framework bridge (LangChain ChatPerplexity, LlamaIndex Perplexity LLM, OpenAI Agents SDK): read integration-styles.md - it has the concrete Agents SDK class swap and the ranked options for LangChain/LlamaIndex.

Classification trap: the Perplexity call may live OUTSIDE the framework as a sibling SDK client. Find which client actually hits api.perplexity.ai and migrate that call site by its own style. Leave framework-internal LLMs pointed at other providers alone.

Do NOT migrate:

  • Search API code (client.search.create(), POST /search) - a different product, unaffected.
  • Code already on the Agent API (responses.create, /v1/agent, /v1/responses).
  • Historical or factual references to "the Sonar API" as a legacy platform in prose.
  • Third-party showcase code that merely describes what someone built.

The strict-mode warning (read this first)

THE #1 MIGRATION FAILURE: the Agent API rejects ANY unknown or leftover field - top-level or nested - with HTTP 400: {"error":{"message":"invalid request body: json: unknown field \"X\"","type":"invalid_request","code":400,"param":"X"}}

Sonar silently dropped many params; the Agent API does not. Strip every field not explicitly mapped in request-mapping.md, including everything on its REMOVE list.

Top 10 pitfalls

  1. Leftover Sonar params -> 400 unknown field. Strict mode (above). Check every request body against the param table and REMOVE list in request-mapping.md.

  2. Web search is NOT automatic - and offering the tool does not guarantee the model calls it. A bare model request does no search and answers ungrounded. Add "tools": [{"type": "web_search"}] (or use a preset, which bundles it). For citation-critical flows, force the search with "tool_choice": {"type": "web_search"} or use a preset. Even forced, an occasional run returns zero sources - handle empty search_results gracefully. Do not force tool_choice universally; it adds cost and latency where grounding is optional.

  3. Citations moved. There is no top-level citations or search_results on Agent responses. Read the search_results OUTPUT ITEM inside output[]. Message annotations are often an empty array - do not rely on them for citations.

  4. 200-wrapped failures. Failed and cancelled runs return HTTP 200 with status: "failed" or "cancelled" and a populated error field. Always branch on response.status, never only on the HTTP status code.

  5. Streaming consumers must handle ALL terminals. Exactly one of response.completed, response.failed, response.incomplete, response.cancelled arrives, plus a bare error event for transport failures. A consumer that only exits on response.completed hangs on failed runs.

  6. No perplexity/sonar-pro slug exists. Map sonar-pro per the two-option rule (preset "low" vs perplexity/sonar + web_search) in models-and-presets.md.

  7. max_output_tokens is REQUIRED for anthropic/* models (400 otherwise); values under 16 floor to 16. On presets and reasoning models, tight caps can be consumed by reasoning tokens before any text or search appears - size generously or omit. incomplete_details.reason: "max_output_tokens" is the truncation signal (the old finish_reason: "length").

  8. OpenAI SDK traps. text={"format": ...} is rejected - send top-level response_format via extra_body. Perplexity-only output items deserialize as raw dicts: use result.get("url"), not result.url, and avoid model_dump() on responses containing them. Prefer response.output_text for the plain-text case.

  9. Reserved custom-function names. Custom tool names colliding with built-in tool names (web_search, search_web, people_search, search_people, fetch_url, finance_search, sandbox, and others) are rejected with a 400. Sonar allowed shadowing; rename during migration (e.g. web_search -> my_web_search). If any custom tool name 400s, rename it - other names may also be reserved.

  10. Preset mechanics. instructions alongside a preset REPLACES the preset's system prompt (empty string clears it). Preset tools cannot be disabled (tools: [] does not clear them; max_tool_calls: 0 is the blunt all-tools off-switch). Request fields override preset values. Presets may resolve to third-party models.

Step-by-step procedure

  1. Scan. Run python scripts/scan_sonar_usage.py [path-to-code] from this skill's directory (the directory containing this SKILL.md), or pass the script's full path, and turn its output into a file-by-file checklist.
  2. Classify each call site by integration style (Step 1 above).
  3. Rewrite the request per request-mapping.md: endpoint to /v1/agent; messages -> input (system prompt to instructions or a system input item); model slug or preset per models-and-presets.md; search filters into tools[web_search].filters; strip everything on the REMOVE list.
  4. Rewrite response handling per response-and-streaming.md: output[] walk or output_text; citations from the search_results item; usage key renames; status branching.
  5. Rewrite streaming consumers per response-and-streaming.md: typed SSE events instead of delta.content; handle all terminals.
  6. Migrate function calling: Responses-style tool shape (top-level name/description/parameters), reserved-name renames, function_call -> function_call_output loop. Details in integration-styles.md.
  7. Verify per verification.md: live smoke test each distinct call shape, then grep for leftovers. The live run is MANDATORY when an API key is available - compile-clean SDK code can still crash on call (see the typed-kwarg trap in integration-styles.md); if you truly cannot run it, say so explicitly in your summary.

Minimal-diff rule: keep the author's structure, naming, and voice; do not refactor unrelated code. When a Sonar feature has no Agent equivalent (images, related questions, academic search mode), apply the documented workaround from request-mapping.md if it is small. For return_related_questions specifically, use the lightweight prompt-delimiter recipe in request-mapping.md - it fits free-text flows too. If no workaround fits, remove the feature AND call the removal out in your migration notes; never drop user-facing output silently.

Verification loop

  • Compile or typecheck every changed file.
  • Run one real request per distinct call shape against production, with a small max_output_tokens and a cheap model (except on presets and reasoning models - see pitfall 7).
  • Capture real response JSON for any printed samples; never fabricate response JSON.
  • Grep for leftovers: chat.completions, choices[0], max_tokens (as a request field), top-level search_recency_filter, citations, delta.content, sonar- model slugs. Every hit must be intentional legacy prose.
  • Full checklist in verification.md.

Reference index

  • request-mapping.md - read when rewriting a request body: full param table, the REMOVE list with workarounds, limits.
  • response-and-streaming.md - read when rewriting response parsing or a streaming consumer: output[] walk, search_results shape, full SSE event set, error catalogue.
  • models-and-presets.md - read when choosing a target model or preset: slug mapping, preset mechanics.
  • integration-styles.md - read when the code uses the OpenAI SDK, Perplexity SDK, LangChain, LlamaIndex, or the OpenAI Agents SDK.
  • verification.md - read before declaring the migration done: checklist, live smoke pattern, leftover greps.

Keep looking

Skills are one crate of 628,971. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.