Improve function registration for SillyBubble extension
- 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 <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										158
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								index.js
									
									
									
									
									
								
							| @@ -1,31 +1,42 @@ | |||||||
| // SillyBubble - Dynamic Chat Bubble Image Generation Extension | // SillyBubble - Dynamic Chat Bubble Image Generation Extension | ||||||
| import { extension_settings, getContext, loadExtensionSettings, registerCustomFunction } from "../../../extensions.js"; | import { extension_settings, getContext } from "../../../extensions.js"; | ||||||
| import { saveSettingsDebounced, callPopup, substituteParams } from "../../../../script.js"; | import { saveSettingsDebounced, callPopup } from "../../../../script.js"; | ||||||
|  |  | ||||||
| // Extension configuration | // Extension configuration | ||||||
| const extensionName = "SillyBubble"; | const extensionName = "SillyBubble"; | ||||||
| const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`; | const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`; | ||||||
| const extensionSettings = extension_settings[extensionName]; |  | ||||||
|  | // Default settings | ||||||
| const defaultSettings = { | const defaultSettings = { | ||||||
|   image_service_url: "image.php", |   image_service_url: "image.php", | ||||||
|   default_style: "default", |   default_style: "default", | ||||||
|   enabled: true |   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 for AI to call - generates markdown image with URL-encoded text | ||||||
| function generateChatBubbleImage(text, style) { | function generateChatBubbleImage(text, style) { | ||||||
|   if (!extensionSettings.enabled) { |   if (!extension_settings[extensionName].enabled) { | ||||||
|     return text; |     return text; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Use default style if not specified |   // 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 |   // URL encode the text parameter | ||||||
|   const encodedText = encodeURIComponent(text); |   const encodedText = encodeURIComponent(text); | ||||||
|    |    | ||||||
|   // Construct the URL with the encoded 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 |   // Add style parameter if specified | ||||||
|   const fullUrl = bubbleStyle !== "default"  |   const fullUrl = bubbleStyle !== "default"  | ||||||
| @@ -36,37 +47,32 @@ function generateChatBubbleImage(text, style) { | |||||||
|   return ``; |   return ``; | ||||||
| } | } | ||||||
|  |  | ||||||
| // Load extension settings | // Load extension settings into UI | ||||||
| async function loadSettings() { | function loadSettings() { | ||||||
|   extension_settings[extensionName] = extension_settings[extensionName] || {}; |  | ||||||
|   if (Object.keys(extension_settings[extensionName]).length === 0) { |  | ||||||
|     Object.assign(extension_settings[extensionName], defaultSettings); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Update UI with current settings |   // Update UI with current settings | ||||||
|   $("#sillybubble_enabled").prop("checked", extensionSettings.enabled).trigger("input"); |   $("#sillybubble_enabled").prop("checked", extension_settings[extensionName].enabled).trigger("input"); | ||||||
|   $("#sillybubble_image_url").val(extensionSettings.image_service_url).trigger("input"); |   $("#sillybubble_image_url").val(extension_settings[extensionName].image_service_url).trigger("input"); | ||||||
|   $("#sillybubble_default_style").val(extensionSettings.default_style).trigger("input"); |   $("#sillybubble_default_style").val(extension_settings[extensionName].default_style).trigger("input"); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Handle enable/disable toggle | // Handle enable/disable toggle | ||||||
| function onEnabledInput(event) { | function onEnabledInput(event) { | ||||||
|   const value = Boolean($(event.target).prop("checked")); |   const value = Boolean($(event.target).prop("checked")); | ||||||
|   extensionSettings.enabled = value; |   extension_settings[extensionName].enabled = value; | ||||||
|   saveSettingsDebounced(); |   saveSettingsDebounced(); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Handle image service URL changes | // Handle image service URL changes | ||||||
| function onImageUrlInput(event) { | function onImageUrlInput(event) { | ||||||
|   const value = $(event.target).val(); |   const value = $(event.target).val(); | ||||||
|   extensionSettings.image_service_url = value; |   extension_settings[extensionName].image_service_url = value; | ||||||
|   saveSettingsDebounced(); |   saveSettingsDebounced(); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Handle default style changes | // Handle default style changes | ||||||
| function onDefaultStyleInput(event) { | function onDefaultStyleInput(event) { | ||||||
|   const value = $(event.target).val(); |   const value = $(event.target).val(); | ||||||
|   extensionSettings.default_style = value; |   extension_settings[extensionName].default_style = value; | ||||||
|   saveSettingsDebounced(); |   saveSettingsDebounced(); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -82,11 +88,77 @@ function onTestButtonClick() { | |||||||
|       <div>${markdown}</div> |       <div>${markdown}</div> | ||||||
|     </div> |     </div> | ||||||
|   `; |   `; | ||||||
|  |    | ||||||
|   callPopup(testPopup, 'text'); |   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 | // Initialize extension | ||||||
| jQuery(async () => { | jQuery(async () => { | ||||||
|  |   console.log(`[${extensionName}] Initializing...`); | ||||||
|  |    | ||||||
|  |   try { | ||||||
|     // Load settings HTML |     // Load settings HTML | ||||||
|     const settingsHtml = await $.get(`${extensionFolderPath}/example.html`); |     const settingsHtml = await $.get(`${extensionFolderPath}/example.html`); | ||||||
|     // Visual extensions go in the right column (extensions_settings2) |     // Visual extensions go in the right column (extensions_settings2) | ||||||
| @@ -98,47 +170,31 @@ jQuery(async () => { | |||||||
|     $("#sillybubble_default_style").on("input", onDefaultStyleInput); |     $("#sillybubble_default_style").on("input", onDefaultStyleInput); | ||||||
|     $("#sillybubble_test_button").on("click", onTestButtonClick); |     $("#sillybubble_test_button").on("click", onTestButtonClick); | ||||||
|      |      | ||||||
|   // Register the custom function for AI to call |     // Wait for the document to fully load before trying to register tools | ||||||
|   registerCustomFunction({ |     $(document).ready(() => { | ||||||
|     name: 'generateChatBubbleImage', |       setTimeout(() => { | ||||||
|     displayName: 'Generate Chat Bubble Image', |         // Try to register the function tool after a short delay | ||||||
|     description: 'Creates a markdown image link with URL-encoded text parameter for dynamic chat bubbles', |         // to ensure all SillyTavern systems are initialized | ||||||
|     parameters: [ |         registerFunctionTool(); | ||||||
|       { |  | ||||||
|         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 |         // Also add the function to extension context | ||||||
|   console.log(`[${extensionName}] Function registered: generateChatBubbleImage`); |  | ||||||
|    |  | ||||||
|   // Add to context if needed - this might be required to expose to AI |  | ||||||
|         try { |         try { | ||||||
|           const context = getContext(); |           const context = getContext(); | ||||||
|     if (context && typeof context.addExtensionFunction === 'function') { |           if (context) { | ||||||
|       context.addExtensionFunction('generateChatBubbleImage', generateChatBubbleImage); |             // Make the function available to the AI | ||||||
|  |             context.generateChatBubbleImage = generateChatBubbleImage; | ||||||
|             console.log(`[${extensionName}] Function added to context: generateChatBubbleImage`); |             console.log(`[${extensionName}] Function added to context: generateChatBubbleImage`); | ||||||
|           } |           } | ||||||
|         } catch (error) { |         } catch (error) { | ||||||
|           console.error(`[${extensionName}] Error adding function to context:`, error); |           console.error(`[${extensionName}] Error adding function to context:`, error); | ||||||
|         } |         } | ||||||
|  |       }, 2000); // Wait 2 seconds for SillyTavern to initialize | ||||||
|  |     }); | ||||||
|      |      | ||||||
|     // Load settings |     // Load settings | ||||||
|     loadSettings(); |     loadSettings(); | ||||||
|  |     console.log(`[${extensionName}] Initialization complete`); | ||||||
|  |   } catch (error) { | ||||||
|  |     console.error(`[${extensionName}] Initialization error:`, error); | ||||||
|  |   } | ||||||
| }); | }); | ||||||
		Reference in New Issue
	
	Block a user