UNPKG

permamind

Version:

An MCP server that provides an immortal memory layer for AI agents and clients

118 lines (117 loc) 5.29 kB
import { z } from "zod"; import { ToolCommand } from "../../core/index.js"; export class QueryPermawebDocsCommand extends ToolCommand { context; metadata = { description: `Query comprehensive Permaweb ecosystem documentation with intelligent domain detection. Automatically searches through Arweave, AO, AR.IO, HyperBEAM, and Permaweb glossary based on your query. Returns relevant documentation sections with high accuracy scoring. Includes automatic backoff mechanism to reduce results if response size approaches context limits.`, name: "queryPermawebDocs", openWorldHint: false, readOnlyHint: true, title: "Query Permaweb Documentation", }; parametersSchema = z.object({ domains: z .string() .optional() .describe("Comma-separated list of domains to search (arweave,ao,ario,hyperbeam,permaweb-glossary). If not provided, domains are auto-detected."), maxResults: z .number() .min(1) .max(20) .optional() .describe("Maximum number of results to return (default: 10). May be automatically reduced to prevent context limits."), query: z .string() .describe("Your question or search query about the Permaweb ecosystem"), }); constructor(context) { super(); this.context = context; } async execute(args) { try { const { permawebDocs } = await import("../../../services/PermawebDocsService.js"); // Progressive backoff strategy for context limit management const backoffLevels = [ { label: "standard", maxResults: args.maxResults || 10 }, { label: "reduced", maxResults: 5 }, { label: "minimal", maxResults: 3 }, { label: "single", maxResults: 1 }, ]; const maxContextTokens = 8000; // Conservative estimate for context limits let backoffLevel = 0; let docsResults = []; let estimatedTokens = 0; // Try each backoff level until we get a manageable response size for (const level of backoffLevels) { try { const domains = args.domains ? args.domains.split(",").map((d) => d.trim()) : undefined; docsResults = await permawebDocs.query(args.query, domains, level.maxResults); if (docsResults.length === 0) { break; // No results to process } // Estimate token count for the response estimatedTokens = permawebDocs.estimateResponseTokens(docsResults); // Add some overhead for JSON structure and metadata const responseOverhead = 1000; const totalEstimatedTokens = estimatedTokens + responseOverhead; if (totalEstimatedTokens <= maxContextTokens || backoffLevel === backoffLevels.length - 1) { break; // Response size is acceptable or this is our last attempt } backoffLevel++; } catch (queryError) { // If query fails at this level, try the next backoff level if (backoffLevel === backoffLevels.length - 1) { throw queryError; // This was our last attempt } backoffLevel++; } } if (docsResults.length > 0) { const permawebResults = docsResults.map((result) => ({ content: result.content, domain: result.domain, isFullDocument: false, // These are chunked results relevanceScore: result.relevanceScore, type: "permaweb_docs", url: result.url, })); const response = { query: args.query, results: permawebResults, success: true, totalResults: permawebResults.length, ...(backoffLevel > 0 && { backoff: { estimatedTokens: estimatedTokens, level: backoffLevel, message: `Results limited to ${backoffLevels[backoffLevel].maxResults} to stay within context limits`, strategy: backoffLevels[backoffLevel].label, }, }), }; return JSON.stringify(response); } return JSON.stringify({ query: args.query, results: [], success: true, totalResults: 0, }); } catch (error) { return JSON.stringify({ error: `Documentation query failed: ${error instanceof Error ? error.message : "Unknown error"}`, query: args.query, success: false, }); } } }