From 8fbc85ee724f5c0c25b1b5e152f4111e8d208d32 Mon Sep 17 00:00:00 2001 From: John Costa Date: Fri, 18 Sep 2026 07:34:56 -0700 Subject: [PATCH] fix(perplexity-web): keep runs of spaces when cleaning non-streaming answers (#14009) cleanResponse(text, strip = true) replaced every run of two or more spaces with a single space. The non-streaming path (which tool mode always uses, since it buffers the full completion before converting text into tool_calls) runs cleanResponse with strip on, so any code the model wrote through perplexity-web lost its indentation: every nesting level came back as one space, making generated Python unimportable. Tabs were untouched, which is what pointed at this normalization step rather than the model. Fold the space handling into CITATION_RE so the space before a removed [n] marker goes with it ("text [1] more" still cleans to "text more"), and drop MULTI_SPACE. Blank-line squashing and trim are unchanged. Fixes #13968 --- .../14009-perplexity-web-preserve-spaces.md | 1 + open-sse/executors/perplexity-web/protocol.ts | 7 +++-- ...rplexity-web-preserve-spaces-13968.test.ts | 31 +++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 changelog.d/fixes/14009-perplexity-web-preserve-spaces.md create mode 100644 tests/unit/perplexity-web-preserve-spaces-13968.test.ts diff --git a/changelog.d/fixes/14009-perplexity-web-preserve-spaces.md b/changelog.d/fixes/14009-perplexity-web-preserve-spaces.md new file mode 100644 index 0000000000..a27f1d5f23 --- /dev/null +++ b/changelog.d/fixes/14009-perplexity-web-preserve-spaces.md @@ -0,0 +1 @@ +- **fix(providers):** Perplexity Web no longer collapses runs of spaces in non-streaming answers (the path tool mode always takes), which flattened code indentation in `write_file` arguments and plain code blocks; citation markers are still removed with single spacing left behind ([#13968](https://github.com/diegosouzapw/OmniRoute/issues/13968)) diff --git a/open-sse/executors/perplexity-web/protocol.ts b/open-sse/executors/perplexity-web/protocol.ts index 9c8de0f8c0..8c2419f361 100644 --- a/open-sse/executors/perplexity-web/protocol.ts +++ b/open-sse/executors/perplexity-web/protocol.ts @@ -91,12 +91,14 @@ export const THINKING_MAP: Record = { "pplx-grok-4.6": "grok46medium", }; -export const CITATION_RE = /\[\d+\]/g; +// Eats the space before the marker so "text [1] more" cleans to "text more". +// Never squash runs of spaces here: the non-streaming path (tool mode always) +// would flatten code indentation (#13968). +export const CITATION_RE = / ?\[\d+\]/g; export const GROK_TAG_RE = /]*>.*?<\/grok:[^>]*>/gs; export const GROK_SELF_RE = /]*\/>/g; export const XML_DECL_RE = /<[?]xml[^?]*[?]>/g; export const RESPONSE_TAG_RE = /<\/?response\b[^>]*>/gi; -export const MULTI_SPACE = / {2,}/g; export const MULTI_NL = /\n{3,}/g; // ─── Helpers ──────────────────────────────────────────────────────────────── @@ -109,7 +111,6 @@ export function cleanResponse(text: string, strip = true): string { t = t.replace(GROK_SELF_RE, ""); t = t.replace(RESPONSE_TAG_RE, ""); if (strip) { - t = t.replace(MULTI_SPACE, " "); t = t.replace(MULTI_NL, "\n\n"); t = t.trim(); } diff --git a/tests/unit/perplexity-web-preserve-spaces-13968.test.ts b/tests/unit/perplexity-web-preserve-spaces-13968.test.ts new file mode 100644 index 0000000000..9ab672d7ae --- /dev/null +++ b/tests/unit/perplexity-web-preserve-spaces-13968.test.ts @@ -0,0 +1,31 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +import { cleanResponse } from "../../open-sse/executors/perplexity-web/protocol.ts"; + +// #13968: the non-streaming path (which tool mode always uses) collapsed every +// run of spaces to one, so code written through perplexity-web lost its +// indentation. Runs of spaces must survive; citation cleanup must still leave +// single spacing behind. + +const PY = ["```python", "def f(x):", " if x:", " return 1", " return 0", "```"].join( + "\n" +); + +test("cleanResponse keeps leading indentation and internal runs of spaces", () => { + assert.equal(cleanResponse(PY), PY); + assert.equal(cleanResponse("a = 1 # aligned comment"), "a = 1 # aligned comment"); +}); + +test("cleanResponse still removes citations without leaving double spaces", () => { + assert.equal(cleanResponse("text [1] more"), "text more"); + assert.equal(cleanResponse("text [1][2] more"), "text more"); + assert.equal(cleanResponse("text [3]."), "text."); + assert.equal(cleanResponse("text[4] more"), "text more"); +}); + +test("cleanResponse trim and blank-line squashing are unchanged", () => { + assert.equal(cleanResponse(" hello "), "hello"); + assert.equal(cleanResponse("a\n\n\n\nb"), "a\n\nb"); + assert.equal(cleanResponse(" keep ", false), " keep "); +});