Files
pi-perplexity/src/render/result.ts
T
Ivan Pereira 63eb22316a Replace brittle error text detection with isError flag
renderPerplexityResult was pattern-matching content text prefixes
to decide whether to render in error style. This was tightly coupled
to exact message strings in index.ts — a wording change would
silently break the error styling.

Set isError: true in details on every error return path in execute,
and check details.isError in the renderer instead.
2026-02-23 10:49:33 +00:00

106 lines
2.8 KiB
TypeScript

import type { AgentToolResult, Theme, ToolRenderResultOptions } from "@mariozechner/pi-coding-agent";
import { Text } from "@mariozechner/pi-tui";
import { asString, asNumber, truncate } from "./util.js";
interface PerplexityResultDetails {
model?: unknown;
sourceCount?: unknown;
queryMs?: unknown;
uuid?: unknown;
toolCallId?: unknown;
error?: unknown;
isError?: unknown;
}
function extractTextContent(result: AgentToolResult<PerplexityResultDetails>): string | undefined {
if (!Array.isArray(result?.content)) {
return undefined;
}
for (const item of result.content) {
if (
item &&
typeof item === "object" &&
"type" in item &&
item.type === "text" &&
"text" in item &&
typeof item.text === "string"
) {
const text = item.text.trim();
if (text.length > 0) {
return text;
}
}
}
return undefined;
}
export function renderPerplexityResult(
result: AgentToolResult<PerplexityResultDetails>,
options: ToolRenderResultOptions,
theme: Theme,
): Text {
const raw = result?.details;
const details: PerplexityResultDetails =
raw && typeof raw === "object" && !Array.isArray(raw)
? (raw as PerplexityResultDetails)
: {};
const contentText = extractTextContent(result);
if (options?.isPartial) {
let partial = theme.fg("warning", "Perplexity: searching…");
if (contentText) {
partial += `\n${theme.fg("dim", truncate(contentText.replace(/\s+/g, " "), 140))}`;
}
return new Text(partial, 0, 0);
}
const error = asString(details.error)?.trim();
if (error) {
return new Text(theme.fg("error", `Perplexity error: ${error}`), 0, 0);
}
if (details.isError === true) {
return new Text(theme.fg("error", truncate(contentText ?? "Perplexity request failed", 200)), 0, 0);
}
const sourceCount = asNumber(details.sourceCount);
const queryMs = asNumber(details.queryMs);
const model = asString(details.model)?.trim();
const uuid = asString(details.uuid)?.trim();
let text = theme.fg("success", "✓ Perplexity");
if (typeof sourceCount === "number") {
text += theme.fg("muted", ` • ${sourceCount} source${sourceCount === 1 ? "" : "s"}`);
}
if (typeof queryMs === "number") {
text += theme.fg("dim", ` • ${(queryMs / 1000).toFixed(queryMs < 1000 ? 2 : 1)}s`);
}
if (!options?.expanded) {
if (contentText) {
const oneLine = contentText.replace(/\s+/g, " ").trim();
if (oneLine.length > 0) {
text += `\n${theme.fg("dim", truncate(oneLine, 160))}`;
}
}
return new Text(text, 0, 0);
}
if (model) {
text += `\n${theme.fg("dim", `model: ${model}`)}`;
}
if (uuid) {
text += `\n${theme.fg("dim", `id: ${uuid}`)}`;
}
if (contentText) {
text += `\n\n${truncate(contentText, 2400)}`;
}
return new Text(text, 0, 0);
}