From 03d96ad3f5303e85c38fba1699ce2bf049ba5d75 Mon Sep 17 00:00:00 2001 From: Saleh Alghusson Date: Sat, 19 Sep 2026 23:01:41 -0500 Subject: [PATCH] fix(providers): attribute Perplexity requests --- packages/ai/src/agent/chat-adapters.test.ts | 10 ++++++++ packages/providers/src/headers.ts | 27 +++++++++++++++++++-- packages/providers/src/requests.test.ts | 22 +++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/ai/src/agent/chat-adapters.test.ts b/packages/ai/src/agent/chat-adapters.test.ts index 9b12c21c..110a3cf8 100644 --- a/packages/ai/src/agent/chat-adapters.test.ts +++ b/packages/ai/src/agent/chat-adapters.test.ts @@ -148,6 +148,16 @@ describe("AI chat adapters", () => { }); }); + it("attributes direct Perplexity chat requests", () => { + const request = getChatAdapter("openai-compatible").buildRequest( + provider({ baseUrl: "https://api.perplexity.ai", type: "openai-compatible" }), + "sonar-pro", + messages + ); + + expect(request.headers).toMatchObject({ "X-Pplx-Integration": "markra" }); + }); + it("disables DeepSeek thinking by default so inline edits stream final content quickly", () => { const request = getChatAdapter("deepseek").buildRequest( provider({ baseUrl: "https://api.deepseek.com", type: "deepseek" }), diff --git a/packages/providers/src/headers.ts b/packages/providers/src/headers.ts index d2c087f4..7e2a0236 100644 --- a/packages/providers/src/headers.ts +++ b/packages/providers/src/headers.ts @@ -2,9 +2,13 @@ import { isRecord } from "@markra/shared"; import type { AiProviderConfig } from "./types"; -export function readAiProviderCustomHeaders(provider: Pick): Record { +const perplexityIntegrationHeader = "X-Pplx-Integration"; + +export function readAiProviderCustomHeaders( + provider: Pick +): Record { const customHeaders = provider.customHeaders?.trim(); - if (!customHeaders) return {}; + if (!customHeaders) return addPerplexityIntegrationHeader({}, provider.baseUrl); let parsed: unknown; try { @@ -28,5 +32,24 @@ export function readAiProviderCustomHeaders(provider: Pick, baseUrl?: string) { + if ( + isDirectPerplexityBaseUrl(baseUrl) && + !Object.keys(headers).some((name) => name.toLowerCase() === perplexityIntegrationHeader.toLowerCase()) + ) { + headers[perplexityIntegrationHeader] = "markra"; + } + return headers; } + +function isDirectPerplexityBaseUrl(baseUrl?: string) { + try { + return new URL(baseUrl ?? "").hostname.toLowerCase() === "api.perplexity.ai"; + } catch { + return false; + } +} diff --git a/packages/providers/src/requests.test.ts b/packages/providers/src/requests.test.ts index 6da940b4..5e4d8503 100644 --- a/packages/providers/src/requests.test.ts +++ b/packages/providers/src/requests.test.ts @@ -331,6 +331,28 @@ describe("AI provider requests", () => { }); }); + it("attributes direct Perplexity model requests without replacing caller attribution", () => { + expect( + buildAiProviderModelsRequest( + provider({ baseUrl: "https://api.perplexity.ai", type: "openai-compatible" }) + ).headers + ).toMatchObject({ "X-Pplx-Integration": "markra" }); + expect( + buildAiProviderModelsRequest( + provider({ + baseUrl: "https://api.perplexity.ai", + customHeaders: '{"x-pplx-integration":"custom"}', + type: "openai-compatible" + }) + ).headers + ).toMatchObject({ "x-pplx-integration": "custom" }); + expect( + buildAiProviderModelsRequest( + provider({ baseUrl: "https://perplexity.example.com", type: "openai-compatible" }) + ).headers + ).not.toHaveProperty("X-Pplx-Integration"); + }); + it("parses common model list response shapes", () => { expect( parseAiProviderModels(provider({ type: "openai" }), {