Tool
v0.1.1
Story Element Generator Tool
This tool is designed to generate a wide array of story elements, from individual components to comprehensive story outlines.
Tool ID
story_element_generator_tool
Creator
@iamg30
Downloads
612+

Tool Content
python
"""
title: Story Element Generator Tool
description: Generate a wide array of story elements, from individual components to comprehensive story outlines.
author: @iamg30
author_url: https://openwebui.com/u/iamg30/
funding_url: https://github.com/open-webui
version: 0.1.1
license: MIT
"""

import random
import json
from pydantic import BaseModel, Field
from typing import List, Dict, Optional


class Tools:
    class Valves(BaseModel):
        CHARACTER_TRAITS: List[str] = Field(
            default=[
                "brave",
                "shy",
                "intelligent",
                "clumsy",
                "charismatic",
                "mysterious",
                "loyal",
                "ambitious",
                "compassionate",
                "arrogant",
                "creative",
                "patient",
                "quirky",
                "pessimistic",
                "optimistic",
                "sarcastic",
                "determined",
                "naive",
                "wise",
                "impulsive",
                "methodical",
                "eccentric",
                "stoic",
                "flamboyant",
            ],
            description="List of character traits",
        )
        OCCUPATIONS: List[str] = Field(
            default=[
                "teacher",
                "detective",
                "chef",
                "astronaut",
                "artist",
                "doctor",
                "farmer",
                "librarian",
                "scientist",
                "athlete",
                "musician",
                "entrepreneur",
                "spy",
                "archaeologist",
                "pilot",
                "hacker",
                "diplomat",
                "magician",
                "journalist",
                "bounty hunter",
                "inventor",
                "marine biologist",
                "stunt performer",
                "cryptozoologist",
            ],
            description="List of occupations",
        )
        SETTINGS: List[str] = Field(
            default=[
                "medieval castle",
                "futuristic city",
                "tropical island",
                "haunted house",
                "underground bunker",
                "space station",
                "ancient ruins",
                "bustling marketplace",
                "quiet village",
                "cyberpunk metropolis",
                "enchanted forest",
                "desert oasis",
                "underwater city",
                "steampunk airship",
                "post-apocalyptic wasteland",
                "floating sky kingdom",
                "prehistoric jungle",
                "arctic research station",
                "dimensional nexus",
                "virtual reality world",
            ],
            description="List of story settings",
        )
        PLOT_ELEMENTS: List[str] = Field(
            default=[
                "unexpected inheritance",
                "mysterious disappearance",
                "time travel accident",
                "alien invasion",
                "forbidden romance",
                "hidden treasure",
                "epic quest",
                "political intrigue",
                "magical transformation",
                "natural disaster",
                "artificial intelligence rebellion",
                "parallel universe discovery",
                "ancient prophecy fulfillment",
                "memory manipulation conspiracy",
                "supernatural ability awakening",
                "interspecies diplomatic crisis",
                "reality-altering experiment",
                "cosmic entity encounter",
                "historical figure resurrection",
                "sentient planet exploration",
            ],
            description="List of plot elements",
        )
        GENRES: List[str] = Field(
            default=[
                "science fiction",
                "fantasy",
                "mystery",
                "romance",
                "thriller",
                "horror",
                "adventure",
                "historical fiction",
                "dystopian",
                "comedy",
                "drama",
                "action",
                "magical realism",
                "cyberpunk",
                "steampunk",
                "supernatural",
                "psychological",
                "crime",
                "post-apocalyptic",
                "alternate history",
            ],
            description="List of story genres",
        )
        THEMES: List[str] = Field(
            default=[
                "redemption",
                "coming of age",
                "power corrupts",
                "love conquers all",
                "man vs. nature",
                "technology gone wrong",
                "identity crisis",
                "good vs. evil",
                "sacrifice for greater good",
                "the human condition",
                "moral ambiguity",
                "cycle of violence",
                "triumph over adversity",
                "loss of innocence",
                "beauty in imperfection",
                "consequences of ambition",
                "illusion vs. reality",
                "power of friendship",
                "nature of consciousness",
                "impact of choices",
            ],
            description="List of story themes",
        )

    def __init__(self):
        self.valves = self.Valves()

    def generate_character(self, trait_count: int = 2) -> str:
        """
        Generate a character with random traits and occupation.
        :param trait_count: Number of traits to assign to the character.
        :return: A JSON string containing character details.
        """
        traits = random.sample(
            self.valves.CHARACTER_TRAITS,
            min(trait_count, len(self.valves.CHARACTER_TRAITS)),
        )
        occupation = random.choice(self.valves.OCCUPATIONS)

        character = {"traits": traits, "occupation": occupation}

        return json.dumps(character, indent=2)

    def generate_setting(self) -> str:
        """
        Generate a random story setting.
        :return: A JSON string containing setting details.
        """
        setting = random.choice(self.valves.SETTINGS)
        return json.dumps({"setting": setting}, indent=2)

    def generate_plot_point(self) -> str:
        """
        Generate a random plot point or story element.
        :return: A JSON string containing a plot point.
        """
        plot_point = random.choice(self.valves.PLOT_ELEMENTS)
        return json.dumps({"plot_point": plot_point}, indent=2)

    def generate_genre(self) -> str:
        """
        Generate a random story genre.
        :return: A JSON string containing a genre.
        """
        genre = random.choice(self.valves.GENRES)
        return json.dumps({"genre": genre}, indent=2)

    def generate_theme(self) -> str:
        """
        Generate a random story theme.
        :return: A JSON string containing a theme.
        """
        theme = random.choice(self.valves.THEMES)
        return json.dumps({"theme": theme}, indent=2)

    def generate_story_starter(
        self,
        trait_count: int = 2,
        include_genre: bool = True,
        include_theme: bool = True,
    ) -> str:
        """
        Generate a complete story starter with character, setting, plot point, and optionally genre and theme.
        :param trait_count: Number of traits to assign to the character.
        :param include_genre: Whether to include a genre in the story starter.
        :param include_theme: Whether to include a theme in the story starter.
        :return: A JSON string containing a story starter.
        """
        character = json.loads(self.generate_character(trait_count))
        setting = json.loads(self.generate_setting())
        plot_point = json.loads(self.generate_plot_point())

        story_starter = {
            "character": character,
            "setting": setting["setting"],
            "plot_point": plot_point["plot_point"],
        }

        if include_genre:
            genre = json.loads(self.generate_genre())
            story_starter["genre"] = genre["genre"]

        if include_theme:
            theme = json.loads(self.generate_theme())
            story_starter["theme"] = theme["theme"]

        return json.dumps(story_starter, indent=2)

    def add_custom_element(self, element_type: str, new_element: str) -> str:
        """
        Add a custom element to one of the predefined lists.
        :param element_type: The type of element to add (trait, occupation, setting, plot_element, genre, or theme).
        :param new_element: The new element to add.
        :return: A JSON string confirming the addition or an error message.
        """
        element_lists = {
            "trait": self.valves.CHARACTER_TRAITS,
            "occupation": self.valves.OCCUPATIONS,
            "setting": self.valves.SETTINGS,
            "plot_element": self.valves.PLOT_ELEMENTS,
            "genre": self.valves.GENRES,
            "theme": self.valves.THEMES,
        }

        if element_type in element_lists:
            if new_element not in element_lists[element_type]:
                element_lists[element_type].append(new_element)
                return json.dumps(
                    {"message": f"Added '{new_element}' to {element_type}s."}, indent=2
                )
            else:
                return json.dumps(
                    {"error": f"'{new_element}' already exists in {element_type}s."},
                    indent=2,
                )
        else:
            return json.dumps(
                {
                    "error": "Invalid element type. Choose from: trait, occupation, setting, plot_element, genre, or theme."
                },
                indent=2,
            )

    def generate_story_outline(self, num_scenes: int = 3) -> str:
        """
        Generate a basic story outline with multiple scenes.
        :param num_scenes: Number of scenes to generate for the outline.
        :return: A JSON string containing a story outline.
        """
        story_starter = json.loads(self.generate_story_starter())
        scenes = []

        for i in range(num_scenes):
            scene = {
                "setting": json.loads(self.generate_setting())["setting"],
                "plot_point": json.loads(self.generate_plot_point())["plot_point"],
            }
            scenes.append(scene)

        outline = {
            "main_character": story_starter["character"],
            "genre": story_starter.get("genre", "Not specified"),
            "theme": story_starter.get("theme", "Not specified"),
            "opening_scene": {
                "setting": story_starter["setting"],
                "plot_point": story_starter["plot_point"],
            },
            "additional_scenes": scenes,
        }

        return json.dumps(outline, indent=2)

    def generate_character_relationship(self) -> str:
        """
        Generate a random character relationship.
        :return: A JSON string containing a character relationship.
        """
        relationships = [
            "siblings",
            "best friends",
            "rivals",
            "mentor and student",
            "lovers",
            "partners in crime",
            "parent and child",
            "nemeses",
            "coworkers",
            "childhood friends",
            "teacher and pupil",
            "hero and sidekick",
            "captor and prisoner",
            "ruler and subject",
            "guardian and ward",
        ]
        relationship = random.choice(relationships)
        return json.dumps({"relationship": relationship}, indent=2)

    def generate_conflict(self) -> str:
        """
        Generate a random conflict for the story.
        :return: A JSON string containing a conflict.
        """
        conflicts = [
            "internal struggle",
            "person vs. person",
            "person vs. nature",
            "person vs. society",
            "person vs. technology",
            "person vs. supernatural",
            "moral dilemma",
            "unrequited love",
            "betrayal",
            "race against time",
            "clash of ideologies",
            "power struggle",
            "identity crisis",
            "revenge plot",
            "quest for redemption",
        ]
        conflict = random.choice(conflicts)
        return json.dumps({"conflict": conflict}, indent=2)

    def generate_complete_story_concept(self) -> str:
        """
        Generate a complete story concept with multiple elements.
        :return: A JSON string containing a comprehensive story concept.
        """
        outline = json.loads(self.generate_story_outline(num_scenes=3))
        secondary_character = json.loads(self.generate_character())
        relationship = json.loads(self.generate_character_relationship())
        conflict = json.loads(self.generate_conflict())

        story_concept = {
            "main_character": outline["main_character"],
            "secondary_character": secondary_character,
            "character_relationship": relationship["relationship"],
            "genre": outline["genre"],
            "theme": outline["theme"],
            "central_conflict": conflict["conflict"],
            "setting": outline["opening_scene"]["setting"],
            "plot_outline": [outline["opening_scene"]] + outline["additional_scenes"],
        }

        return json.dumps(story_concept, indent=2)