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.
106 lines
2.8 KiB
TypeScript
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);
|
|
}
|