repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/web/parallel.ts
Rawimport type { SearchResult } from "./constants.js";
import { callMcpTool, McpError } from "./mcp.js";
/** Anonymous hosted MCP; preserve all excerpts, applying only the result-count limit. */
export async function parallelSearch(
query: string,
signal?: AbortSignal,
count = 5,
timeout = 30_000,
): Promise<SearchResult[]> {
const deadline = AbortSignal.timeout(timeout);
const requestSignal = signal ? AbortSignal.any([signal, deadline]) : deadline;
const result = await callMcpTool({
url: "https://search.parallel.ai/mcp",
protocolVersion: "2024-11-05",
signal: requestSignal,
name: "web_search",
arguments: { objective: query, search_queries: [query] },
}).catch(async (error: unknown) => {
if (!(error instanceof McpError)) throw error;
if (error.response) {
await error.response.body?.cancel();
throw new Error(`Parallel ${error.message}`);
}
throw new Error(`Parallel MCP: ${error.message}`);
});
const text = result?.content
?.filter((block) => block.type === "text")
.map((block) => block.text ?? "")
.join("\n");
if (result?.isError)
throw new Error(`Parallel search: ${text || "tool failed"}`);
if (!text) throw new Error("Parallel search returned no content");
const data = JSON.parse(text) as {
results?: Array<{
title?: string;
url?: string;
excerpts?: string[];
publish_date?: string | null;
}>;
};
if (!Array.isArray(data.results))
throw new Error("Parallel search returned invalid results");
return data.results
.map((result) => {
if (
typeof result.url !== "string" ||
!Array.isArray(result.excerpts) ||
!result.excerpts.every((excerpt) => typeof excerpt === "string")
) {
throw new Error("Parallel search returned a malformed result");
}
return {
title: result.title ?? result.url,
url: result.url,
snippet: result.excerpts.join("\n\n"),
...(result.publish_date ? { date: result.publish_date } : {}),
};
})
.slice(0, count);
}