diff --git a/lib/artist/createKnowledgeBase.ts b/lib/artist/createKnowledgeBase.ts new file mode 100644 index 000000000..145614dcf --- /dev/null +++ b/lib/artist/createKnowledgeBase.ts @@ -0,0 +1,70 @@ +import { uploadToArweave } from "@/lib/arweave/uploadToArweave"; +import { arweaveGatewayUrl } from "@/lib/arweave/arweaveGatewayUrl"; +import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo"; +import { updateAccountInfo } from "@/lib/supabase/account_info/updateAccountInfo"; +import { insertAccountInfo } from "@/lib/supabase/account_info/insertAccountInfo"; +import type { Knowledge } from "./updateArtistProfile"; + +/** + * Creates a knowledge base entry for an artist by uploading text to Arweave + * and appending it to the artist's knowledges array. + * + * @param artistId - The artist account ID + * @param knowledgeBaseText - The text content to add to the knowledge base + * @returns The created knowledge entry with Arweave URL + */ +export async function createKnowledgeBase( + artistId: string, + knowledgeBaseText: string, +): Promise { + if (!knowledgeBaseText || knowledgeBaseText.trim().length === 0) { + throw new Error("Knowledge base text is required"); + } + + // Upload text to Arweave + const textBuffer = Buffer.from(knowledgeBaseText, "utf-8"); + const transaction = await uploadToArweave(textBuffer, "text/plain"); + const arweaveUrl = `ar://${transaction.id}`; + const fetchableUrl = arweaveGatewayUrl(arweaveUrl); + + // Generate a name from the first line or a default name + const firstLine = knowledgeBaseText.split("\n")[0].trim(); + const name = firstLine.length > 0 && firstLine.length <= 100 ? firstLine : "Knowledge Base Entry"; + + // Create the knowledge entry + const newKnowledge: Knowledge = { + url: fetchableUrl || arweaveUrl, + name, + type: "text/plain", + }; + + // Get current account_info + const accountInfo = await selectAccountInfo(artistId); + + // Get existing knowledges or initialize empty array + const existingKnowledges: Knowledge[] = accountInfo?.knowledges + ? (accountInfo.knowledges as unknown as Knowledge[]) + : []; + + // Append new knowledge (deduplicate by URL) + const knowledgeMap = new Map(); + existingKnowledges.forEach(k => { + knowledgeMap.set(k.url, k); + }); + knowledgeMap.set(newKnowledge.url, newKnowledge); + const updatedKnowledges = Array.from(knowledgeMap.values()); + + // Update account_info + if (accountInfo) { + await updateAccountInfo(artistId, { + knowledges: updatedKnowledges as unknown as typeof accountInfo.knowledges, + }); + } else { + await insertAccountInfo({ + account_id: artistId, + knowledges: updatedKnowledges as unknown as typeof accountInfo.knowledges, + }); + } + + return newKnowledge; +} diff --git a/lib/mcp/tools/index.ts b/lib/mcp/tools/index.ts index 006f6bccd..d39463de9 100644 --- a/lib/mcp/tools/index.ts +++ b/lib/mcp/tools/index.ts @@ -9,6 +9,7 @@ import { registerContactTeamTool } from "./registerContactTeamTool"; import { registerUpdateAccountInfoTool } from "./registerUpdateAccountInfoTool"; import { registerAllArtistSocialsTools } from "./artistSocials"; import { registerSearchWebTool } from "./registerSearchWebTool"; +import { registerCreateKnowledgeBaseTool } from "./registerCreateKnowledgeBaseTool"; /** * Registers all MCP tools on the server. @@ -27,4 +28,5 @@ export const registerAllTools = (server: McpServer): void => { registerContactTeamTool(server); registerUpdateAccountInfoTool(server); registerSearchWebTool(server); + registerCreateKnowledgeBaseTool(server); }; diff --git a/lib/mcp/tools/registerCreateKnowledgeBaseTool.ts b/lib/mcp/tools/registerCreateKnowledgeBaseTool.ts new file mode 100644 index 000000000..cfe182852 --- /dev/null +++ b/lib/mcp/tools/registerCreateKnowledgeBaseTool.ts @@ -0,0 +1,54 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { createKnowledgeBase } from "@/lib/artist/createKnowledgeBase"; +import { getToolResultSuccess } from "@/lib/mcp/getToolResultSuccess"; +import { getToolResultError } from "@/lib/mcp/getToolResultError"; + +const createKnowledgeBaseSchema = z.object({ + artistId: z + .string() + .min(1, "Artist account ID is required") + .describe( + "The artist_account_id to add the knowledge base to. If not provided, check system prompt for the active artist_account_id.", + ), + knowledgeBaseText: z + .string() + .min(1, "Knowledge base text is required") + .describe("Text to add to the knowledge base"), +}); + +type CreateKnowledgeBaseArgs = z.infer; + +/** + * Registers the "create_knowledge_base" tool on the MCP server. + * Adds a knowledge base file to an artist by uploading text to Arweave and appending it to the artist's knowledges. + * + * @param server - The MCP server instance to register the tool on. + */ +export function registerCreateKnowledgeBaseTool(server: McpServer): void { + server.registerTool( + "create_knowledge_base", + { + description: `Adds a knowledge base file to the active artist by generating a text file and appending it to the list of existing knowledge bases. The text will be uploaded to Arweave for permanent storage.`, + inputSchema: createKnowledgeBaseSchema, + }, + async (args: CreateKnowledgeBaseArgs) => { + try { + const knowledge = await createKnowledgeBase(args.artistId, args.knowledgeBaseText); + + const response = { + success: true, + knowledge, + message: "Knowledge base text prepared for creation and storage.", + }; + + return getToolResultSuccess(response); + } catch (error) { + console.error("Error creating knowledge base:", error); + return getToolResultError( + error instanceof Error ? error.message : "Failed to create knowledge base", + ); + } + }, + ); +}