import { z } from "zod";
import {
GenerateResearchIdeasSchema,
GenerateResearchIdeasSchemaBase,
} from "../schemas/index.js";
import { makeInfraNodusRequest } from "../api/client.js";
import { fetchUrlContentAsText } from "../utils/urlContent.js";
import {
generateResearchIdeas,
generateResponses,
} from "../utils/transformers.js";
function errorContent(message: string) {
return {
content: [
{ type: "text" as const, text: JSON.stringify({ error: message }) },
],
isError: true,
};
}
export const generateResearchIdeasTool = {
name: "generate_research_ideas",
definition: {
title: "Generate Research Ideas from Text or Graph",
description:
"Analyze text or an existing graph and generate innovative research ideas based on the content gaps identified between the topical clusters inside the text that can be used to improve the text and the discourse it relates to.",
inputSchema: GenerateResearchIdeasSchemaBase.shape,
annotations: {
readOnlyHint: true,
idempotentHint: true,
destructiveHint: false,
},
},
handler: async (params: z.infer<typeof GenerateResearchIdeasSchema>) => {
try {
const responseType = params.responseType === "idea" ? "idea" : "response";
const queryParams = new URLSearchParams({
doNotSave: "true",
addStats: "true",
optimize: "gap",
includeStatements: "false",
includeGraphSummary: "false",
extendedGraphSummary: "false",
includeGraph: "false",
aiTopics: "true",
extendedAdvice: params.useSeveralGaps ? "true" : "false",
gapDepth: params.gapDepth ? params.gapDepth.toString() : "0",
});
const endpoint = `/graphAndAdvice?${queryParams.toString()}`;
let requestBody: {
text?: string;
name?: string;
aiTopics: string;
requestMode: string;
modelToUse: string;
};
if (params.graphName?.trim()) {
requestBody = {
name: params.graphName,
aiTopics: "true",
requestMode: params.shouldTranscend ? "transcend" : "response",
modelToUse: params.modelToUse ?? "gpt-4o",
};
} else {
let contentText: string;
if (params.url) {
const result = await fetchUrlContentAsText(params.url);
if (!result.ok) return errorContent(result.error);
contentText = result.contentText;
if (!contentText?.trim())
return errorContent("URL did not return any text content");
} else if (params.text?.trim()) {
contentText = params.text;
} else {
return errorContent(
"Provide either text, url, or graphName for analysis"
);
}
requestBody = {
text: contentText,
aiTopics: "true",
requestMode: params.shouldTranscend
? "transcend"
: responseType === "idea"
? "idea"
: "response",
modelToUse: params.modelToUse ?? "gpt-4o",
};
}
const response = await makeInfraNodusRequest(endpoint, requestBody);
const researchIdeas =
responseType === "idea"
? generateResearchIdeas(response)
: generateResponses(response);
if (response.error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${response.error}`,
},
],
isError: true,
};
}
return {
content: [
{
type: "text" as const,
text: JSON.stringify(researchIdeas, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: JSON.stringify({
error: error instanceof Error ? error.message : String(error),
}),
},
],
isError: true,
};
}
},
};