from smolagents.tools import Tool import json import pronouncing import string class ParodyWordSuggestionTool(Tool): name = "parody_word_suggester" description = """Suggests phonetically similar funny words using CMU dictionary pronunciations. Returns similar words from a curated list of funny words, along with their similarity scores.""" inputs = {'target': {'type': 'string', 'description': 'The word you want to find funny alternatives for'}, 'word_list_str': {'type': 'string', 'description': 'JSON string of word list (e.g. \'["word1", "word2"]\')'}, 'min_similarity': {'type': 'string', 'description': 'Minimum similarity threshold (0.0-1.0)', 'nullable': True}} output_type = "string" def forward(self, target: str, word_list_str: str, min_similarity: str = "0.7") -> str: """Get phonetically similar funny word suggestions.""" import pronouncing import string import json target = target.lower().strip(string.punctuation) min_similarity = float(min_similarity) suggestions = [] # Parse JSON string to list try: words = json.loads(word_list_str) except json.JSONDecodeError: return json.dumps({ "error": "Invalid JSON string for word_list_str", "suggestions": [] }, indent=2) # Get target pronunciation target_phones = pronouncing.phones_for_word(target) if not target_phones: return json.dumps({ "error": f"'{target}' not found in CMU dictionary", "suggestions": [] }, indent=2) target_phones = target_phones[0] target_phone_list = target_phones.split() # Check each word for word in words: phones = pronouncing.phones_for_word(word) if phones: # Only process if word is in dictionary word_phones = phones[0] word_phone_list = word_phones.split() # Calculate position-aware similarity matches = 0 length = max(len(word_phone_list), len(target_phone_list)) # Compare phones at each position for i in range(length): if i < len(word_phone_list) and i < len(target_phone_list): # Get current phones current_word_phone = word_phone_list[i] current_target_phone = target_phone_list[i] # Exact match at same position if current_word_phone == current_target_phone: matches += 1 # Partial match (just the consonant or vowel part) elif current_word_phone.rstrip('012') == current_target_phone.rstrip('012'): matches += 0.5 similarity = matches / length if length > 0 else 0.0 if similarity >= min_similarity: suggestions.append({ "word": word, "similarity": round(similarity, 3), "syllables": pronouncing.syllable_count(word_phones), "phones": word_phones, }) # Sort by similarity score descending suggestions.sort(key=lambda x: x["similarity"], reverse=True) result = { "target": target, "target_syllables": pronouncing.syllable_count(target_phones), "target_phones": target_phones, "suggestions": suggestions } return json.dumps(result, indent=2) def __init__(self, *args, **kwargs): self.is_initialized = False