From 247abbe91a7e7ed12f27d1cccf02ada1a855a38d Mon Sep 17 00:00:00 2001 From: Sven Olderaan Date: Fri, 14 Mar 2025 21:20:23 +0100 Subject: [PATCH] Improve function registration for SillyBubble extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use the proper ToolManager API when available - Add fallback mechanisms when ToolManager is not available - Fix extension settings handling to use the standard pattern - Implement proper JSON schema for function parameters - Add delayed initialization to ensure SillyTavern is fully loaded - Add better error handling and logging 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- index.js | 196 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 126 insertions(+), 70 deletions(-) diff --git a/index.js b/index.js index f3f8009..4d28814 100644 --- a/index.js +++ b/index.js @@ -1,31 +1,42 @@ // SillyBubble - Dynamic Chat Bubble Image Generation Extension -import { extension_settings, getContext, loadExtensionSettings, registerCustomFunction } from "../../../extensions.js"; -import { saveSettingsDebounced, callPopup, substituteParams } from "../../../../script.js"; +import { extension_settings, getContext } from "../../../extensions.js"; +import { saveSettingsDebounced, callPopup } from "../../../../script.js"; // Extension configuration const extensionName = "SillyBubble"; const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`; -const extensionSettings = extension_settings[extensionName]; + +// Default settings const defaultSettings = { image_service_url: "image.php", default_style: "default", enabled: true }; +// Make sure settings exist +if (!extension_settings[extensionName]) { + extension_settings[extensionName] = {}; +} + +// Apply defaults for any missing settings +if (Object.keys(extension_settings[extensionName]).length === 0) { + Object.assign(extension_settings[extensionName], defaultSettings); +} + // Function for AI to call - generates markdown image with URL-encoded text function generateChatBubbleImage(text, style) { - if (!extensionSettings.enabled) { + if (!extension_settings[extensionName].enabled) { return text; } // Use default style if not specified - const bubbleStyle = style || extensionSettings.default_style; + const bubbleStyle = style || extension_settings[extensionName].default_style; // URL encode the text parameter const encodedText = encodeURIComponent(text); // Construct the URL with the encoded text - const imageUrl = `${extensionSettings.image_service_url}?q=${encodedText}`; + const imageUrl = `${extension_settings[extensionName].image_service_url}?q=${encodedText}`; // Add style parameter if specified const fullUrl = bubbleStyle !== "default" @@ -36,37 +47,32 @@ function generateChatBubbleImage(text, style) { return `![](${fullUrl})`; } -// Load extension settings -async function loadSettings() { - extension_settings[extensionName] = extension_settings[extensionName] || {}; - if (Object.keys(extension_settings[extensionName]).length === 0) { - Object.assign(extension_settings[extensionName], defaultSettings); - } - +// Load extension settings into UI +function loadSettings() { // Update UI with current settings - $("#sillybubble_enabled").prop("checked", extensionSettings.enabled).trigger("input"); - $("#sillybubble_image_url").val(extensionSettings.image_service_url).trigger("input"); - $("#sillybubble_default_style").val(extensionSettings.default_style).trigger("input"); + $("#sillybubble_enabled").prop("checked", extension_settings[extensionName].enabled).trigger("input"); + $("#sillybubble_image_url").val(extension_settings[extensionName].image_service_url).trigger("input"); + $("#sillybubble_default_style").val(extension_settings[extensionName].default_style).trigger("input"); } // Handle enable/disable toggle function onEnabledInput(event) { const value = Boolean($(event.target).prop("checked")); - extensionSettings.enabled = value; + extension_settings[extensionName].enabled = value; saveSettingsDebounced(); } // Handle image service URL changes function onImageUrlInput(event) { const value = $(event.target).val(); - extensionSettings.image_service_url = value; + extension_settings[extensionName].image_service_url = value; saveSettingsDebounced(); } // Handle default style changes function onDefaultStyleInput(event) { const value = $(event.target).val(); - extensionSettings.default_style = value; + extension_settings[extensionName].default_style = value; saveSettingsDebounced(); } @@ -82,63 +88,113 @@ function onTestButtonClick() {
${markdown}
`; + callPopup(testPopup, 'text'); } +// Register function tool +function registerFunctionTool() { + try { + // Check if ToolManager exists in global scope + if (!window.ToolManager) { + console.warn(`[${extensionName}] ToolManager not found. Will try alternative registration methods.`); + + // Method 1: Register directly to window + window.generateChatBubbleImage = generateChatBubbleImage; + console.log(`[${extensionName}] Registered function to window object`); + + // Method 2: Add to extensions_functions if it exists + if (window.extensions_functions) { + window.extensions_functions['generateChatBubbleImage'] = generateChatBubbleImage; + console.log(`[${extensionName}] Added to extensions_functions`); + } + + return; + } + + // Check if tool calling is supported + if (typeof window.ToolManager.isToolCallingSupported !== 'function' || !window.ToolManager.isToolCallingSupported()) { + console.warn(`[${extensionName}] Tool calling is not supported in the current configuration`); + return; + } + + // Define the function parameters schema + const parameters = { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "The text to display in the chat bubble" + }, + "style": { + "type": "string", + "description": "The visual style of the chat bubble (default, modern, retro, minimal)" + } + }, + "required": ["text"] + }; + + // Register the tool + window.ToolManager.registerFunctionTool({ + name: 'generateChatBubbleImage', + displayName: 'Generate Chat Bubble', + description: 'Creates a markdown image link with the text displayed as a chat bubble', + parameters: parameters, + action: async (params) => { + return generateChatBubbleImage(params.text, params.style); + }, + formatMessage: async (params) => { + return `Generating chat bubble with text: "${params.text?.substring(0, 30)}${params.text?.length > 30 ? '...' : ''}"`; + } + }); + + console.log(`[${extensionName}] Function tool registered via ToolManager`); + } catch (error) { + console.error(`[${extensionName}] Error registering function tool:`, error); + } +} + // Initialize extension jQuery(async () => { - // Load settings HTML - const settingsHtml = await $.get(`${extensionFolderPath}/example.html`); - // Visual extensions go in the right column (extensions_settings2) - $("#extensions_settings2").append(settingsHtml); + console.log(`[${extensionName}] Initializing...`); - // Register event listeners - $("#sillybubble_enabled").on("input", onEnabledInput); - $("#sillybubble_image_url").on("input", onImageUrlInput); - $("#sillybubble_default_style").on("input", onDefaultStyleInput); - $("#sillybubble_test_button").on("click", onTestButtonClick); - - // Register the custom function for AI to call - registerCustomFunction({ - name: 'generateChatBubbleImage', - displayName: 'Generate Chat Bubble Image', - description: 'Creates a markdown image link with URL-encoded text parameter for dynamic chat bubbles', - parameters: [ - { - name: 'text', - displayName: 'Text', - description: 'The text to display in the chat bubble', - type: 'string', - required: true, - }, - { - name: 'style', - displayName: 'Style', - description: 'The visual style of the chat bubble (optional)', - type: 'string', - required: false, - } - ], - isPublic: true, // Make sure function is exposed to the AI - run: async function(text, style) { - return generateChatBubbleImage(text, style); - } - }); - - // Log to console to verify registration - console.log(`[${extensionName}] Function registered: generateChatBubbleImage`); - - // Add to context if needed - this might be required to expose to AI try { - const context = getContext(); - if (context && typeof context.addExtensionFunction === 'function') { - context.addExtensionFunction('generateChatBubbleImage', generateChatBubbleImage); - console.log(`[${extensionName}] Function added to context: generateChatBubbleImage`); - } + // Load settings HTML + const settingsHtml = await $.get(`${extensionFolderPath}/example.html`); + // Visual extensions go in the right column (extensions_settings2) + $("#extensions_settings2").append(settingsHtml); + + // Register event listeners + $("#sillybubble_enabled").on("input", onEnabledInput); + $("#sillybubble_image_url").on("input", onImageUrlInput); + $("#sillybubble_default_style").on("input", onDefaultStyleInput); + $("#sillybubble_test_button").on("click", onTestButtonClick); + + // Wait for the document to fully load before trying to register tools + $(document).ready(() => { + setTimeout(() => { + // Try to register the function tool after a short delay + // to ensure all SillyTavern systems are initialized + registerFunctionTool(); + + // Also add the function to extension context + try { + const context = getContext(); + if (context) { + // Make the function available to the AI + context.generateChatBubbleImage = generateChatBubbleImage; + console.log(`[${extensionName}] Function added to context: generateChatBubbleImage`); + } + } catch (error) { + console.error(`[${extensionName}] Error adding function to context:`, error); + } + }, 2000); // Wait 2 seconds for SillyTavern to initialize + }); + + // Load settings + loadSettings(); + console.log(`[${extensionName}] Initialization complete`); } catch (error) { - console.error(`[${extensionName}] Error adding function to context:`, error); + console.error(`[${extensionName}] Initialization error:`, error); } - - // Load settings - loadSettings(); -}); +}); \ No newline at end of file