UNPKG

@elizaos/plugin-wolfram

Version:

Wolfram Alpha and Wolfram Cloud integration plugin for ElizaOS

1,596 lines (1,571 loc) 63.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var core = require('@elizaos/core'); var zod = require('zod'); // src/index.ts var wolframEnvSchema = zod.z.object({ // Wolfram Alpha API configuration WOLFRAM_APP_ID: zod.z.string().min(1).describe("Wolfram Alpha App ID"), // Optional: Wolfram Cloud configuration for advanced features WOLFRAM_CLOUD_API_KEY: zod.z.string().optional().describe("Wolfram Cloud API Key for advanced features"), // Optional: Custom endpoint for Wolfram services WOLFRAM_API_ENDPOINT: zod.z.string().url().optional().default("https://api.wolframalpha.com/v2"), // Optional: LLM API endpoint (different host/base than v2 API) WOLFRAM_LLM_API_ENDPOINT: zod.z.string().url().optional().default("https://www.wolframalpha.com/api/v1/llm-api"), // Optional: Conversation API endpoint WOLFRAM_CONVERSATION_ENDPOINT: zod.z.string().url().optional().default("https://www.wolframalpha.com/api/v1/conversation.jsp"), // Optional: Response format preferences WOLFRAM_OUTPUT_FORMAT: zod.z.enum(["plaintext", "image", "mathml", "sound", "wav"]).optional().default("plaintext"), // Optional: Timeout settings WOLFRAM_TIMEOUT: zod.z.number().min(1e3).max(3e4).optional().default(1e4), // Optional: Units preference WOLFRAM_UNITS: zod.z.enum(["metric", "imperial"]).optional().default("metric"), // Optional: Location for location-based queries WOLFRAM_LOCATION: zod.z.string().optional().describe('Location for location-based queries (e.g., "New York, NY")'), // Optional: Scanner types to use WOLFRAM_SCANNERS: zod.z.string().optional().describe("Comma-separated list of scanner types to use"), // Optional: Max results per query WOLFRAM_MAX_RESULTS: zod.z.number().min(1).max(10).optional().default(5) }); async function validateWolframConfig(runtime) { try { const config = { WOLFRAM_APP_ID: runtime.getSetting("WOLFRAM_APP_ID") || runtime.getSetting("WOLFRAM_ALPHA_APP_ID"), WOLFRAM_CLOUD_API_KEY: runtime.getSetting("WOLFRAM_CLOUD_API_KEY"), WOLFRAM_API_ENDPOINT: runtime.getSetting("WOLFRAM_API_ENDPOINT"), WOLFRAM_LLM_API_ENDPOINT: runtime.getSetting( "WOLFRAM_LLM_API_ENDPOINT" ), WOLFRAM_CONVERSATION_ENDPOINT: runtime.getSetting( "WOLFRAM_CONVERSATION_ENDPOINT" ), WOLFRAM_OUTPUT_FORMAT: runtime.getSetting("WOLFRAM_OUTPUT_FORMAT"), WOLFRAM_TIMEOUT: runtime.getSetting("WOLFRAM_TIMEOUT") ? parseInt(runtime.getSetting("WOLFRAM_TIMEOUT"), 10) : void 0, WOLFRAM_UNITS: runtime.getSetting("WOLFRAM_UNITS"), WOLFRAM_LOCATION: runtime.getSetting("WOLFRAM_LOCATION"), WOLFRAM_SCANNERS: runtime.getSetting("WOLFRAM_SCANNERS"), WOLFRAM_MAX_RESULTS: runtime.getSetting("WOLFRAM_MAX_RESULTS") ? parseInt(runtime.getSetting("WOLFRAM_MAX_RESULTS"), 10) : void 0 }; const cleanConfig = Object.fromEntries( Object.entries(config).filter(([_, v]) => v !== void 0 && v !== null) ); return wolframEnvSchema.parse(cleanConfig); } catch (error) { if (error instanceof zod.z.ZodError) { const errorMessages = error.errors.map((err) => `${err.path.join(".")}: ${err.message}`).join("\n"); throw new Error( `Wolfram configuration validation failed: ${errorMessages}` ); } throw error; } } function isWolframConfigured(runtime) { const appId = runtime.getSetting("WOLFRAM_APP_ID") || runtime.getSetting("WOLFRAM_ALPHA_APP_ID"); return !!appId; } // src/types.ts var WolframAPIEndpoint = /* @__PURE__ */ ((WolframAPIEndpoint2) => { WolframAPIEndpoint2["QUERY"] = "/query"; WolframAPIEndpoint2["SIMPLE"] = "/simple"; WolframAPIEndpoint2["SHORT"] = "/short"; WolframAPIEndpoint2["SPOKEN"] = "/spoken"; WolframAPIEndpoint2["RESULT"] = "/result"; WolframAPIEndpoint2["LLM"] = "https://www.wolframalpha.com/api/v1/llm-api"; WolframAPIEndpoint2["CONVERSATION"] = "https://www.wolframalpha.com/api/v1/conversation.jsp"; return WolframAPIEndpoint2; })(WolframAPIEndpoint || {}); // src/service.ts var WOLFRAM_SERVICE_NAME = "wolfram"; var WolframService = class extends core.Service { // Cap cache size to avoid unbounded growth constructor(runtime) { super(); this.capabilityDescription = "Provides Wolfram Alpha computational knowledge and mathematical problem solving"; // userId -> conversationID this.CACHE_TTL = 36e5; // 1 hour in milliseconds this.MAX_CACHE_ENTRIES = 200; this.runtime = runtime; this.cache = /* @__PURE__ */ new Map(); this.conversationCache = /* @__PURE__ */ new Map(); } async initialize() { core.logger.log("\u{1F43A} Initializing Wolfram service..."); try { this.wolframConfig = await validateWolframConfig(this.runtime); const axios = await import('axios'); this.client = axios.default.create({ baseURL: this.wolframConfig.WOLFRAM_API_ENDPOINT || "https://api.wolframalpha.com/v2", timeout: this.wolframConfig.WOLFRAM_TIMEOUT || 1e4, headers: { "User-Agent": "ElizaOS-Wolfram-Plugin/1.0" } }); await this.validateApiKey(); core.logger.log("\u2705 Wolfram service initialized successfully"); } catch (error) { core.logger.error("Failed to initialize Wolfram service:", error); throw error; } } /** * Validates the API key by making a test request */ async validateApiKey() { try { const response = await this.client.get("/short", { params: { appid: this.wolframConfig.WOLFRAM_APP_ID, input: "2+2" } }); if (!response?.data || typeof response.data !== "string") { throw new Error("Unexpected validation response"); } } catch (error) { core.logger.error("Failed to validate Wolfram API key:", error); throw new Error("Invalid or missing Wolfram Alpha App ID"); } } /** * Lightweight retry for transient errors (429/5xx) */ async getWithRetry(client, url, config, maxRetries = 2) { let attempt = 0; let delayMs = 250; while (true) { try { return await client.get(url, config); } catch (err) { const status = err?.response?.status; const retriable = status === 429 || status >= 500 && status < 600; if (!retriable || attempt >= maxRetries) { throw err; } await new Promise((r) => setTimeout(r, delayMs)); attempt += 1; delayMs *= 2; } } } /** * Main query method for full Wolfram Alpha results */ async query(input, options = {}) { const cacheKey = `query:${input}:${JSON.stringify(options)}`; const cached = this.getCached(cacheKey); if (cached) { core.logger.log("\u{1F3AF} Returning cached Wolfram query result"); return cached; } try { core.logger.log(`\u{1F50D} Querying Wolfram Alpha: "${input}"`); const params = { input, appid: this.wolframConfig.WOLFRAM_APP_ID, format: "plaintext,image", output: "json", units: this.wolframConfig.WOLFRAM_UNITS, ...options }; if (this.wolframConfig.WOLFRAM_LOCATION && !params.location) { params.location = this.wolframConfig.WOLFRAM_LOCATION; } if (this.wolframConfig.WOLFRAM_SCANNERS && !params.scanner) { params.scanner = this.wolframConfig.WOLFRAM_SCANNERS; } const response = await this.getWithRetry(this.client, "/query" /* QUERY */, { params }); const result = response.data.queryresult; if (result.success) { this.setCached(cacheKey, result); core.logger.log(`\u2705 Wolfram query successful with ${result.numpods} pods`); } else { core.logger.warn(`\u26A0\uFE0F Wolfram query returned no results for: "${input}"`); } return result; } catch (error) { core.logger.error("\u274C Wolfram query failed:", error); throw new Error( `Wolfram query failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Simple API - returns a single image result */ async getSimpleAnswer(input) { const cacheKey = `simple:${input}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F5BC}\uFE0F Getting simple answer for: "${input}"`); const response = await this.getWithRetry(this.client, "/simple" /* SIMPLE */, { params: { appid: this.wolframConfig.WOLFRAM_APP_ID, input, units: this.wolframConfig.WOLFRAM_UNITS, location: this.wolframConfig.WOLFRAM_LOCATION }, responseType: "arraybuffer" }); const base64 = Buffer.from(response.data).toString("base64"); const imageUrl = `data:image/gif;base64,${base64}`; this.setCached(cacheKey, imageUrl); return imageUrl; } catch (error) { core.logger.error("Failed to get simple answer:", error); throw new Error( `Simple answer failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Short Answer API - returns a single plaintext result */ async getShortAnswer(input) { const cacheKey = `short:${input}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F4DD} Getting short answer for: "${input}"`); const response = await this.getWithRetry(this.client, "/short" /* SHORT */, { params: { appid: this.wolframConfig.WOLFRAM_APP_ID, input, units: this.wolframConfig.WOLFRAM_UNITS, location: this.wolframConfig.WOLFRAM_LOCATION } }); const result = { answer: response.data, success: true }; this.setCached(cacheKey, result); return result; } catch (error) { core.logger.error("Failed to get short answer:", error); return { answer: "", success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Spoken Answer API - returns natural language response */ async getSpokenAnswer(input) { const cacheKey = `spoken:${input}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F5E3}\uFE0F Getting spoken answer for: "${input}"`); const response = await this.getWithRetry(this.client, "/spoken" /* SPOKEN */, { params: { appid: this.wolframConfig.WOLFRAM_APP_ID, input, units: this.wolframConfig.WOLFRAM_UNITS, location: this.wolframConfig.WOLFRAM_LOCATION } }); const result = { spoken: response.data, success: true }; this.setCached(cacheKey, result); return result; } catch (error) { core.logger.error("Failed to get spoken answer:", error); return { spoken: "", success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Conversational API - supports multi-turn conversations */ async conversationalQuery(input, userId, maxChars = 2e3) { try { core.logger.log(`\u{1F4AC} Conversational query from user ${userId}: "${input}"`); let conversationID = this.conversationCache.get(userId); const params = { input, appid: this.wolframConfig.WOLFRAM_APP_ID, maxchars: maxChars }; if (conversationID) { params.conversationID = conversationID; } const llmUrl = this.wolframConfig.WOLFRAM_LLM_API_ENDPOINT || "https://www.wolframalpha.com/api/v1/llm-api" /* LLM */; const headers = this.wolframConfig.WOLFRAM_CLOUD_API_KEY ? { "X-Wolfram-Cloud-Api-Key": this.wolframConfig.WOLFRAM_CLOUD_API_KEY } : void 0; const response = await this.getWithRetry(this.client, llmUrl, { params, headers }); const result = response.data; if (result.conversationID) { this.conversationCache.set(userId, result.conversationID); } core.logger.log(`\u2705 Conversational response received`); return result; } catch (error) { core.logger.error("Error in conversational query:", error); throw new Error( `Conversational query failed: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Clear conversation context for a user */ clearConversation(userId) { this.conversationCache.delete(userId); core.logger.log(`\u{1F504} Cleared conversation for user ${userId}`); } /** * Specialized method for solving mathematical equations */ async solveMath(equation) { const cacheKey = `solve:${equation}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F9EE} Solving equation: "${equation}"`); const result = await this.query(`solve ${equation}`); if (!result.success || !result.pods) { return "Could not solve the equation"; } const solutionPod = result.pods.find( (pod) => pod.title === "Solution" || pod.title === "Result" || pod.title.includes("solution") ); if (solutionPod && solutionPod.subpods && solutionPod.subpods[0]) { const solution = solutionPod.subpods[0].plaintext || "No solution found"; this.setCached(cacheKey, solution); return solution; } return "No solution found"; } catch (error) { core.logger.error("Error solving equation:", error); throw new Error( `Failed to solve equation: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Get step-by-step solutions for problems */ async getStepByStep(problem) { const cacheKey = `steps:${problem}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F4CB} Getting step-by-step solution for: "${problem}"`); const result = await this.query(problem, { podstate: "Step-by-step solution" }); if (!result.success || !result.pods) { return ["Could not generate step-by-step solution"]; } const steps = []; for (const pod of result.pods) { if (pod.title.includes("step") || pod.title.includes("Step") || pod.scanner === "Solve") { for (const subpod of pod.subpods || []) { if (subpod.plaintext) { steps.push(subpod.plaintext); } } } } if (steps.length === 0) { for (const pod of result.pods) { if (pod.subpods) { for (const subpod of pod.subpods) { if (subpod.plaintext) { steps.push(`${pod.title}: ${subpod.plaintext}`); } } } } } this.setCached(cacheKey, steps); return steps.length > 0 ? steps : ["No step-by-step solution available"]; } catch (error) { core.logger.error("Error getting step-by-step solution:", error); throw new Error( `Failed to get step-by-step solution: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Compute mathematical expressions */ async compute(expression) { const cacheKey = `compute:${expression}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F522} Computing: "${expression}"`); const shortAnswer = await this.getShortAnswer(expression); if (shortAnswer.success && shortAnswer.answer) { this.setCached(cacheKey, shortAnswer.answer); return shortAnswer.answer; } const result = await this.query(expression); if (result.success && result.pods && result.pods.length > 0) { const resultPod = result.pods.find( (pod) => pod.title === "Result" || pod.title === "Value" || pod.title === "Decimal approximation" ); if (resultPod && resultPod.subpods && resultPod.subpods[0]) { const answer = resultPod.subpods[0].plaintext || "No result"; this.setCached(cacheKey, answer); return answer; } } return "Could not compute expression"; } catch (error) { core.logger.error("Error computing expression:", error); throw new Error( `Failed to compute: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Get facts about a topic */ async getFacts(topic) { const cacheKey = `facts:${topic}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F4DA} Getting facts about: "${topic}"`); const result = await this.query(topic); if (!result.success || !result.pods) { return [`No facts found about ${topic}`]; } const facts = []; for (const pod of result.pods) { if (pod.subpods) { for (const subpod of pod.subpods) { if (subpod.plaintext && subpod.plaintext.length > 10) { facts.push(`${pod.title}: ${subpod.plaintext}`); } } } } this.setCached(cacheKey, facts); return facts.length > 0 ? facts : [`No facts found about ${topic}`]; } catch (error) { core.logger.error("Error getting facts:", error); throw new Error( `Failed to get facts: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Analyze data and provide statistical insights */ async analyzeData(data) { const cacheKey = `analyze:${data}`; const cached = this.getCached(cacheKey); if (cached) { return cached; } try { core.logger.log(`\u{1F4CA} Analyzing data: "${data}"`); const result = await this.query(`statistics ${data}`); if (!result.success || !result.pods) { return { input: data, results: {}, error: "Could not analyze data" }; } const analysis = { input: data, results: {} }; for (const pod of result.pods) { if (pod.subpods) { const podData = []; for (const subpod of pod.subpods) { if (subpod.plaintext) { podData.push(subpod.plaintext); } } if (podData.length > 0) { analysis.results[pod.title] = podData; } } } this.setCached(cacheKey, analysis); return analysis; } catch (error) { core.logger.error("Error analyzing data:", error); throw new Error( `Failed to analyze data: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Format Wolfram Alpha results for display */ formatResult(result) { if (!result.success) { return "No results found"; } const output = []; if (result.pods) { const pods = result.pods.filter((p) => p.title !== "Input"); const primaryPods = pods.filter((p) => p.primary); const podsToRender = primaryPods.length > 0 ? primaryPods : pods; for (const pod of podsToRender) { if (!pod.subpods || pod.subpods.length === 0) continue; output.push(`**${pod.title}**`); for (const subpod of pod.subpods) { if (subpod.plaintext) { output.push(subpod.plaintext); } } } } if (result.assumptions) { output.push("\n*Assumptions:*"); for (const assumption of result.assumptions) { if (assumption.values) { output.push(`- ${assumption.values.map((v) => v.desc).join(", ")}`); } } } if (result.warnings) { output.push("\n*Warnings:*"); for (const warning of result.warnings) { output.push(`- ${warning.text}`); } } return output.join("\n") || "No results to display"; } /** * Cache management methods */ getCached(key) { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() - entry.timestamp > entry.ttl) { this.cache.delete(key); return null; } return entry.result; } setCached(key, result, ttl = this.CACHE_TTL) { this.cache.set(key, { query: key, result, timestamp: Date.now(), ttl }); this.cleanCache(); if (this.cache.size > this.MAX_CACHE_ENTRIES) { const oldestKey = this.cache.keys().next().value; if (oldestKey) this.cache.delete(oldestKey); } } cleanCache() { const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (now - entry.timestamp > entry.ttl) { this.cache.delete(key); } } } /** * Clear all caches */ clearCache() { this.cache.clear(); this.conversationCache.clear(); core.logger.log("\u{1F5D1}\uFE0F Wolfram cache cleared"); } /** * Get service statistics */ getStats() { return { cacheSize: this.cache.size, activeConversations: this.conversationCache.size, config: { units: this.wolframConfig.WOLFRAM_UNITS, location: this.wolframConfig.WOLFRAM_LOCATION, maxResults: this.wolframConfig.WOLFRAM_MAX_RESULTS } }; } /** * Stop the service and clean up resources */ async stop() { core.logger.log("\u{1F6D1} Stopping Wolfram service..."); this.clearCache(); core.logger.log("\u2705 Wolfram service stopped"); } }; WolframService.serviceType = WOLFRAM_SERVICE_NAME; var queryTemplate = ` You are helping the user with a Wolfram Alpha query. Recent conversation context: {{recentMessages}} The user wants to know: "{{userInput}}" Extract the query that should be sent to Wolfram Alpha. Make it clear and specific. Return ONLY the query text, nothing else. `; var wolframQueryAction = { name: "WOLFRAM_QUERY", description: "Query Wolfram Alpha for comprehensive information about any topic, including mathematics, science, geography, history, and more", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, state, _options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } state = state || await runtime.composeState(message); const queryPrompt = core.composePromptFromState({ state, template: queryTemplate }); const queryResult = await runtime.useModel(core.ModelType.TEXT_SMALL, { prompt: queryPrompt }); const query = queryResult.trim(); core.logger.log(`\u{1F50D} Wolfram query: "${query}"`); if (!query) { const errorMessage = "I couldn't understand your query. Please try rephrasing."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } await callback?.({ text: `Searching Wolfram Alpha for: "${query}"...` }); const result = await service.query(query); const formattedResult = service.formatResult(result); await callback?.({ text: formattedResult, metadata: { query, success: result.success, numpods: result.numpods, source: "Wolfram Alpha" } }); return { success: true, text: formattedResult, values: { lastWolframQuery: query, lastQueryTime: Date.now(), querySuccess: result.success }, data: { actionName: "WOLFRAM_QUERY", query, result, formattedResult, pods: result.pods?.length || 0, success: result.success, numpods: result.numpods } }; } catch (error) { core.logger.error("Error executing Wolfram query:", error); const errorMessage = `Failed to query Wolfram Alpha: ${error instanceof Error ? error.message : String(error)}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "What is the population of Tokyo?" } }, { name: "{{agent}}", content: { text: "I'll look up the population of Tokyo for you.", actions: ["WOLFRAM_QUERY"] } } ], [ { name: "{{user1}}", content: { text: "What is the integral of x^2?" } }, { name: "{{agent}}", content: { text: "Let me calculate the integral of x^2.", actions: ["WOLFRAM_QUERY"] } } ], [ { name: "{{user1}}", content: { text: "What's the weather in New York?" } }, { name: "{{agent}}", content: { text: "I'll check the current weather in New York.", actions: ["WOLFRAM_QUERY"] } } ] ] }; var computeTemplate = ` You are helping the user with a mathematical computation using Wolfram Alpha. Recent conversation context: {{recentMessages}} The user wants to compute: "{{userInput}}" Extract the mathematical expression or calculation that should be computed. Return ONLY the expression, nothing else. Examples: - "2 + 2" - "sqrt(144)" - "integrate x^2 dx" - "derivative of sin(x)" `; var wolframComputeAction = { name: "WOLFRAM_COMPUTE", description: "Perform mathematical computations using Wolfram Alpha, including arithmetic, algebra, calculus, and more", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, state, options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } const context = options?.context; const previousQuery = context?.getPreviousResult?.("WOLFRAM_QUERY"); state = state || await runtime.composeState(message); const computePrompt = core.composePromptFromState({ state, template: computeTemplate }); const expressionResult = await runtime.useModel(core.ModelType.TEXT_SMALL, { prompt: computePrompt }); const expression = expressionResult.trim(); core.logger.log(`\u{1F522} Computing: "${expression}"`); if (!expression) { const errorMessage = "I couldn't understand the expression to compute. Please provide a valid mathematical expression."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } await callback?.({ text: `Computing: "${expression}"...` }); const result = await service.compute(expression); await callback?.({ text: `Result: ${result}`, metadata: { expression, result, source: "Wolfram Alpha" } }); return { success: true, text: `Result: ${result}`, values: { lastComputation: expression, lastResult: result, computationTime: Date.now() }, data: { actionName: "WOLFRAM_COMPUTE", expression, result, previousQueryData: previousQuery?.data || null } }; } catch (error) { core.logger.error("Error computing expression:", error); const errorMessage = `Failed to compute: ${error instanceof Error ? error.message : String(error)}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Calculate 15% of 250" } }, { name: "{{agent}}", content: { text: "I'll calculate 15% of 250 for you.", actions: ["WOLFRAM_COMPUTE"] } } ], [ { name: "{{user1}}", content: { text: "What is the square root of 2024?" } }, { name: "{{agent}}", content: { text: "Let me compute the square root of 2024.", actions: ["WOLFRAM_COMPUTE"] } } ], [ { name: "{{user1}}", content: { text: "Calculate the derivative of x^3 + 2x^2 - 5x + 1" } }, { name: "{{agent}}", content: { text: "I'll calculate the derivative of that polynomial.", actions: ["WOLFRAM_COMPUTE"] } } ] ] }; var solveTemplate = ` You are helping the user solve an equation or system of equations using Wolfram Alpha. Recent conversation context: {{recentMessages}} The user wants to solve: "{{userInput}}" Extract the equation or system that needs to be solved. Return ONLY the equation(s), nothing else. Examples: - "2x + 5 = 15" - "x^2 - 4x + 3 = 0" - "x + y = 10, x - y = 2" `; var wolframSolveAction = { name: "WOLFRAM_SOLVE", description: "Solve equations and systems of equations using Wolfram Alpha", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, state, options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } const context = options?.context; const previousCompute = context?.getPreviousResult?.("WOLFRAM_COMPUTE"); state = state || await runtime.composeState(message); const solvePrompt = core.composePromptFromState({ state, template: solveTemplate }); const equationResult = await runtime.useModel(core.ModelType.TEXT_SMALL, { prompt: solvePrompt }); const equation = equationResult.trim(); core.logger.log(`\u{1F9EE} Solving: "${equation}"`); if (!equation) { const errorMessage = "I couldn't understand the equation to solve. Please provide a valid equation."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } await callback?.({ text: `Solving equation: "${equation}"...` }); const solution = await service.solveMath(equation); await callback?.({ text: `Solution: ${solution}`, metadata: { equation, solution, source: "Wolfram Alpha" } }); return { success: true, text: `Solution: ${solution}`, values: { lastEquation: equation, lastSolution: solution, solveTime: Date.now() }, data: { actionName: "WOLFRAM_SOLVE", equation, solution, previousComputeData: previousCompute?.data || null, hasSolution: solution !== "No solution found" } }; } catch (error) { core.logger.error("Error solving equation:", error); const errorMessage = `Failed to solve equation: ${error instanceof Error ? error.message : String(error)}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Solve x^2 - 4x + 3 = 0" } }, { name: "{{agent}}", content: { text: "I'll solve this quadratic equation for you.", actions: ["WOLFRAM_SOLVE"] } } ], [ { name: "{{user1}}", content: { text: "Find x if 3x + 7 = 22" } }, { name: "{{agent}}", content: { text: "Let me solve for x in this equation.", actions: ["WOLFRAM_SOLVE"] } } ], [ { name: "{{user1}}", content: { text: "Solve the system: x + y = 10 and x - y = 2" } }, { name: "{{agent}}", content: { text: "I'll solve this system of equations.", actions: ["WOLFRAM_SOLVE"] } } ] ] }; var stepByStepTemplate = ` You are helping the user get a step-by-step solution using Wolfram Alpha. Recent conversation context: {{recentMessages}} The user wants step-by-step solution for: "{{userInput}}" Extract the problem that needs step-by-step solution. Return ONLY the problem statement, nothing else. Examples: - "solve x^2 - 5x + 6 = 0" - "integrate sin(x) dx" - "factor x^3 - 8" `; var wolframStepByStepAction = { name: "WOLFRAM_STEP_BY_STEP", description: "Get step-by-step solutions for mathematical problems using Wolfram Alpha", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, state, options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } const context = options?.context; const previousSolve = context?.getPreviousResult?.("WOLFRAM_SOLVE"); const previousCompute = context?.getPreviousResult?.("WOLFRAM_COMPUTE"); state = state || await runtime.composeState(message); const stepPrompt = core.composePromptFromState({ state, template: stepByStepTemplate }); const problemResult = await runtime.useModel(core.ModelType.TEXT_SMALL, { prompt: stepPrompt }); const problem = problemResult.trim(); core.logger.log(`\u{1F4CB} Getting step-by-step solution for: "${problem}"`); if (!problem) { const errorMessage = "I couldn't understand the problem. Please provide a clear problem statement."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } await callback?.({ text: `Getting step-by-step solution for: "${problem}"...` }); const steps = await service.getStepByStep(problem); const formattedSteps = steps.map((step, index) => `Step ${index + 1}: ${step}`).join("\n"); await callback?.({ text: formattedSteps, metadata: { problem, steps: steps.length, source: "Wolfram Alpha" } }); return { success: true, text: formattedSteps, values: { lastProblem: problem, stepsCount: steps.length, stepByStepTime: Date.now() }, data: { actionName: "WOLFRAM_STEP_BY_STEP", problem, steps, stepsCount: steps.length, formattedSteps, previousSolveData: previousSolve?.data || null, previousComputeData: previousCompute?.data || null } }; } catch (error) { core.logger.error("Error getting step-by-step solution:", error); const errorMessage = `Failed to get step-by-step solution: ${error instanceof Error ? error.message : String(error)}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Show me how to solve x^2 - 6x + 8 = 0 step by step" } }, { name: "{{agent}}", content: { text: "I'll show you the step-by-step solution for this quadratic equation.", actions: ["WOLFRAM_STEP_BY_STEP"] } } ], [ { name: "{{user1}}", content: { text: "How do I integrate x * sin(x) dx?" } }, { name: "{{agent}}", content: { text: "Let me show you the step-by-step integration process.", actions: ["WOLFRAM_STEP_BY_STEP"] } } ], [ { name: "{{user1}}", content: { text: "Walk me through factoring x^3 - 27" } }, { name: "{{agent}}", content: { text: "I'll provide step-by-step factorization.", actions: ["WOLFRAM_STEP_BY_STEP"] } } ] ] }; var getFactsTemplate = ` You are helping the user get facts about a topic using Wolfram Alpha. Recent conversation context: {{recentMessages}} The user wants facts about: "{{userInput}}" Extract the topic they want facts about. Return ONLY the topic, nothing else. Examples: - "Jupiter" - "Albert Einstein" - "photosynthesis" - "World War II" `; var wolframGetFactsAction = { name: "WOLFRAM_GET_FACTS", description: "Get facts and information about any topic using Wolfram Alpha", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, state, options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } const context = options?.context; const previousQuery = context?.getPreviousResult?.("WOLFRAM_QUERY"); state = state || await runtime.composeState(message); const factsPrompt = core.composePromptFromState({ state, template: getFactsTemplate }); const topicResult = await runtime.useModel(core.ModelType.TEXT_SMALL, { prompt: factsPrompt }); const topic = topicResult.trim(); core.logger.log(`\u{1F4DA} Getting facts about: "${topic}"`); if (!topic) { const errorMessage = "I couldn't understand the topic. Please specify what you want to learn about."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } await callback?.({ text: `Gathering facts about "${topic}"...` }); const facts = await service.getFacts(topic); const formattedFacts = facts.join("\n\n"); await callback?.({ text: formattedFacts, metadata: { topic, factsCount: facts.length, source: "Wolfram Alpha" } }); return { success: true, text: formattedFacts, values: { lastFactsTopic: topic, factsCount: facts.length, factsTime: Date.now() }, data: { actionName: "WOLFRAM_GET_FACTS", topic, facts, factsCount: facts.length, formattedFacts, previousQueryData: previousQuery?.data || null } }; } catch (error) { core.logger.error("Error getting facts:", error); const errorMessage = `Failed to get facts: ${error instanceof Error ? error.message : String(error)}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Tell me facts about Jupiter" } }, { name: "{{agent}}", content: { text: "I'll get you facts about Jupiter.", actions: ["WOLFRAM_GET_FACTS"] } } ], [ { name: "{{user1}}", content: { text: "What are some facts about Albert Einstein?" } }, { name: "{{agent}}", content: { text: "Let me find facts about Albert Einstein.", actions: ["WOLFRAM_GET_FACTS"] } } ], [ { name: "{{user1}}", content: { text: "Give me information about the Eiffel Tower" } }, { name: "{{agent}}", content: { text: "I'll gather facts about the Eiffel Tower.", actions: ["WOLFRAM_GET_FACTS"] } } ] ] }; var analyzeDataTemplate = ` You are helping the user analyze data using Wolfram Alpha. Recent conversation context: {{recentMessages}} The user wants to analyze: "{{userInput}}" Extract the data or dataset that needs to be analyzed. This could be: - A list of numbers (e.g., "1, 2, 3, 4, 5") - A data description (e.g., "heights: 170cm, 165cm, 180cm, 175cm") - A statistical query (e.g., "mean of 10, 20, 30, 40") Return ONLY the data or analysis request, nothing else. `; var wolframAnalyzeDataAction = { name: "WOLFRAM_ANALYZE_DATA", description: "Analyze data and get statistical insights using Wolfram Alpha", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, state, options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } const context = options?.context; const previousCompute = context?.getPreviousResult?.("WOLFRAM_COMPUTE"); const previousQuery = context?.getPreviousResult?.("WOLFRAM_QUERY"); state = state || await runtime.composeState(message); const dataPrompt = core.composePromptFromState({ state, template: analyzeDataTemplate }); const dataResult = await runtime.useModel(core.ModelType.TEXT_SMALL, { prompt: dataPrompt }); const data = dataResult.trim(); core.logger.log(`\u{1F4CA} Analyzing data: "${data}"`); if (!data) { const errorMessage = "I couldn't understand the data to analyze. Please provide valid data or a clear description."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } await callback?.({ text: `Analyzing data: "${data.substring(0, 50)}${data.length > 50 ? "..." : ""}"` }); const analysis = await service.analyzeData(data); let formattedAnalysis = "Data Analysis Results:\n\n"; if (analysis.error) { formattedAnalysis = `Error: ${analysis.error}`; } else if (analysis.results) { for (const [key, value] of Object.entries(analysis.results)) { formattedAnalysis += `${key}: ${Array.isArray(value) ? value.join("\n") : value} `; } } await callback?.({ text: formattedAnalysis, metadata: { data: data.substring(0, 100), analysisType: Object.keys(analysis.results || {}), source: "Wolfram Alpha" } }); return { success: true, text: formattedAnalysis, values: { lastAnalyzedData: data, analysisTime: Date.now(), analysisResultsCount: Object.keys(analysis.results || {}).length }, data: { actionName: "WOLFRAM_ANALYZE_DATA", data, analysis, formattedAnalysis, resultsCount: Object.keys(analysis.results || {}).length, previousComputeData: previousCompute?.data || null, previousQueryData: previousQuery?.data || null } }; } catch (error) { core.logger.error("Error analyzing data:", error); const errorMessage = `Failed to analyze data: ${error instanceof Error ? error.message : String(error)}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: error instanceof Error ? error : new Error(String(error)) }; } }, examples: [ [ { name: "{{user1}}", content: { text: "Analyze this data: 12, 15, 18, 22, 25, 28, 31" } }, { name: "{{agent}}", content: { text: "I'll analyze this dataset for you.", actions: ["WOLFRAM_ANALYZE_DATA"] } } ], [ { name: "{{user1}}", content: { text: "What's the standard deviation of 100, 105, 110, 115, 120?" } }, { name: "{{agent}}", content: { text: "Let me calculate the statistical analysis for this data.", actions: ["WOLFRAM_ANALYZE_DATA"] } } ], [ { name: "{{user1}}", content: { text: "Find the correlation between (1,2), (2,4), (3,6), (4,8)" } }, { name: "{{agent}}", content: { text: "I'll analyze the correlation in this dataset.", actions: ["WOLFRAM_ANALYZE_DATA"] } } ] ] }; var wolframConversationalAction = { name: "WOLFRAM_CONVERSATIONAL", description: "Have a conversational interaction with Wolfram Alpha that maintains context across queries", validate: async (runtime, _message) => { const service = runtime.getService(WOLFRAM_SERVICE_NAME); return service instanceof WolframService; }, handler: async (runtime, message, _state, options, callback) => { try { const service = runtime.getService( WOLFRAM_SERVICE_NAME ); if (!service) { core.logger.error("Wolfram service not found"); const errorMessage = "Wolfram service is not available. Please check the configuration."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: "Service not available" }; } const context = options?.context; const previousConversation = context?.getPreviousResult?.( "WOLFRAM_CONVERSATIONAL" ); const previousQuery = context?.getPreviousResult?.("WOLFRAM_QUERY"); const userInput = message.content.text || ""; const userId = message.userId; if (!userInput) { const errorMessage = "Please provide a question or topic for discussion."; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage }; } core.logger.log(`\u{1F4AC} Conversational query: "${userInput}"`); await callback?.({ text: "Processing your question..." }); const result = await service.conversationalQuery(userInput, userId); if (result.error) { const errorMessage = `Conversation error: ${result.error}`; await callback?.({ text: errorMessage, error: true }); return { success: false, text: errorMessage, error: result.error }; } const response = result.result || "I couldn't generate a response for that question."; await callback?.({ text: respon