Skip to content

Instantly share code, notes, and snippets.

@ruvnet
Last active May 3, 2024 07:06
Show Gist options
  • Save ruvnet/aeb4fb6a0260a922f037a476ffded72d to your computer and use it in GitHub Desktop.
Save ruvnet/aeb4fb6a0260a922f037a476ffded72d to your computer and use it in GitHub Desktop.
progressive-story-ai.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/ruvnet/aeb4fb6a0260a922f037a476ffded72d/progressive-story-ai.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "SkBkDqS0oFYf"
},
"source": [
"# Introduction to the Story Development Toolkit\n",
"### created by @rUv, cause i could.\n",
"\n",
"Welcome to an innovative way of crafting narratives! This toolkit, comprising several Python libraries and custom functionalities, is designed to empower writers, creators, and enthusiasts in generating engaging and coherent stories with ease.\n",
"\n",
"At the heart of this toolkit are `dspy`, a library for integrating machine learning models into storytelling, and other essential Python libraries like `random`, `numpy`, `re`, and `logging`.\n",
"\n",
"## Key Features:\n",
"\n",
"- **Dynamic Story Generation**: Create stories dynamically, using a range of parameters such as genre, theme, setting, character depth, and more, to tailor the narrative to your preferences.\n",
"- **Customization and Flexibility**: Customize your story with detailed configurations including story length, writing style, tone, pacing, and plot complexity. Whether you're crafting a short tale or an epic, the toolkit adjusts to your creative vision.\n",
"- **Interactive Development**: Through an interactive interface, tweak your story’s direction on-the-fly. Incorporate illustrations, poems, and mature themes as desired.\n",
"- **AI-Powered Insights**: Utilize OpenAI’s language model to generate story introductions, developments, and conclusions, ensuring your narrative is engaging and cohesive from start to finish.\n",
"- **Feedback Mechanism**: Employ built-in feedback loops to validate story coherence, engagement, and adherence to guidelines, refining your narrative with precision.\n",
"\n",
"## Capabilities:\n",
"\n",
"- **Generate story introductions** that set the stage for your narrative, capturing the reader's attention with compelling settings and characters.\n",
"- **Develop your story progressively**, with options to adjust complexity, emotional tone, and pacing, ensuring a satisfying narrative arc.\n",
"-**Conclude your story:** witha meaningful resolution, tying together all thematic elements and character arcs.\n",
"- **Training with DSPy** not only enhances AI content creation but also provides robust training and testing frameworks for AI models.\n",
"## Customization Options:\n",
"\n",
"- **Story Configurator**: A user-friendly interface allowing you to define your story’s core elements—genre, theme, characters, and more.\n",
"- **Development Tweaks**: Adjust the narrative’s development phase with controls over dialogue, character depth, and plot twists.\n",
"- **Conclusion Crafting**: Specify the length and tone of your story’s conclusion, ensuring a fitting end to your narrative journey.\n",
"\n",
"This toolkit is perfect for writers looking for an assistive tool to explore new narratives, for educators integrating creative writing with technology, or for anyone passionate about storytelling. With its blend of customization, AI-powered tools, and interactive features, the Story Development Toolkit offers a unique approach to storytelling in the digital age.\n",
"\n",
"## Parameters\n",
"\n",
"- `output_length` (int): The desired length of the generated story in words.\n",
"- `story_context` (str): The initial context or setting for the story.\n",
"- `broad_guidelines` (list): A list of guidelines to shape the story's direction and themes.\n",
"- `concluding_sentence` (str, optional): An optional concluding sentence to wrap up the story.\n",
"\n",
"## Example Usage\n",
"\n",
"Here's an example of how to use ProgressiveStory:\n",
"\n",
"```python\n",
"from google.colab import userdata\n",
"\n",
"# Retrieve the API key\n",
"api_key = userdata.get('OPENAI_API_KEY')\n",
"openai.api_key = api_key\n",
"\n",
"# Example settings and characters to generate a story chunk\n",
"setting = \"A forgotten office city\"\n",
"characters = \"An adventurous archaeologist; a mysterious guardian of the city\"\n",
"plot_point = \"The archaeologist discovers a hidden temple\"\n",
"\n",
"# Initialize the story generator\n",
"story_gen = StoryGenerator()\n",
"\n",
"# Generate a story chunk\n",
"story_chunk = story_gen(setting=setting, characters=characters, plot_point=plot_point)\n",
"\n",
"# Print the generated story chunk\n",
"print(f\"Generated Story Chunk:\\n{story_chunk.story_chunk}\")\n",
"\n",
"```\n",
"\n",
"Output:\n",
"```\n",
"Generated Story Chunk:\n",
"As the archaeologist enters the hidden temple, they are met with intricate carvings and ancient artifacts. The guardian's warnings echo in their mind as they navigate through dark corridors and booby traps. Finally, they reach the inner chamber where a powerful artifact is said to be hidden. Just as they are about to reach for it, the guardian appears, revealing their true identity as\n",
"```\n",
"\n",
"## How's it built?\n",
"DSPy differentiates from traditional AI content creation tools by focusing on a programming-first approach with large language models (LLMs), offering several innovative aspects:\n",
"\n",
"- **Modular Design**: It introduces composable, declarative modules for task instruction, moving beyond manual prompt engineering to a structured, Pythonic syntax. This allows for more deterministic, programmatic control over AI behaviors\n",
"\n",
"- **Automatic Compilation**: Features an automatic compiler that optimizes prompts based on execution, fine-tuning AI models to understand tasks without manual intermediary labels. This improves AI application effectiveness and simplifies development.\n",
"\n",
"- **Signatures and Teleprompters**: Utilizes Signatures for specific behavior expression in LLM tasks and Teleprompters for optimizing prompt formulation, reducing labeling requirements and enhancing pipeline optimization.\n",
"\n",
"Compared to tools like LangChain and LlamaIndex, DSPy offers a richer programming model and automatic pipeline optimization, making it a unique tool for AI-driven storytelling and content creation.\n",
"\n",
"\n",
"## License\n",
"\n",
"ProgressiveStory is open-source software licensed under the [MIT License](https://opensource.org/licenses/MIT)."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "opxYHr7qoFYg"
},
"source": [
"# Getting Set-Up\n",
"\n",
"As we'll start to see below, **DSPy** can routinely teach powerful models like `GPT-3.5` and local models like `T5-base` or `Llama2-13b` to be much more reliable at complex tasks. **DSPy** will compile the _same program_ into different few-shot prompts and/or finetunes for each LM.\n",
"\n",
"Let's begin by setting things up.\n",
"\n",
"## The snippet below will also install **DSPy** if it's not there already."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "sJW5o0r9oFYh"
},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2\n",
"\n",
"import sys\n",
"import os\n",
"\n",
"try: # When on google Colab, let's clone the notebook so we download the cache.\n",
" import google.colab\n",
" repo_path = 'dspy'\n",
" !git -C $repo_path pull origin || git clone https://github.com/stanfordnlp/dspy $repo_path\n",
"except:\n",
" repo_path = '.'\n",
"\n",
"if repo_path not in sys.path:\n",
" sys.path.append(repo_path)\n",
"\n",
"# Set up the cache for this notebook\n",
"os.environ[\"DSP_NOTEBOOK_CACHEDIR\"] = os.path.join(repo_path, 'cache')\n",
"\n",
"import pkg_resources # Install the package if it's not installed\n",
"if not \"dspy-ai\" in {pkg.key for pkg in pkg_resources.working_set}:\n",
" !pip install -U pip\n",
" !pip install dspy-ai\n",
" !pip install openai~=0.28.1\n",
" # !pip install -e $repo_path\n",
"\n",
"import dspy"
]
},
{
"cell_type": "markdown",
"source": [
"## Configure your OpenAi Key or use another LLM::"
],
"metadata": {
"id": "4J_jlmwtZ4lx"
}
},
{
"cell_type": "code",
"source": [
"# Import necessary libraries\n",
"from IPython.display import display, clear_output\n",
"from ipywidgets import widgets, Layout\n",
"\n",
"# Function to save the API key and display a success message\n",
"def save_api_key(b):\n",
" # Retrieve the API key from the text widget\n",
" api_key = text.value\n",
"\n",
" # Save the API key to an environment variable (simulating saving functionality)\n",
" os.environ['OPENAI_API_KEY'] = api_key\n",
"\n",
" # Clear previous outputs to keep the UI clean\n",
" clear_output(wait=True)\n",
"\n",
" # Display the UI elements again for further inputs\n",
" display(text, button)\n",
"\n",
" # Display a success message\n",
" print(\"API Key saved successfully! ✅\")\n",
"\n",
"# Create a text widget for input\n",
"text = widgets.Text(\n",
" value='',\n",
" placeholder='Enter your OPENAI API Key',\n",
" description='API Key:',\n",
" disabled=False,\n",
" layout=Layout(width='50%')\n",
")\n",
"\n",
"# Create a button widget\n",
"button = widgets.Button(\n",
" description='Save',\n",
" button_style='success', # 'success', 'info', 'warning', 'danger' or ''\n",
" tooltip='Click to save the API Key',\n",
" icon='check' # FontAwesome icon name (without 'fa-')\n",
")\n",
"\n",
"# Display the widgets\n",
"display(text, button)\n",
"\n",
"# Set the button's on_click event to call the save_api_key function\n",
"button.on_click(save_api_key)"
],
"metadata": {
"id": "mgHq-XiYsC6z"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"### 1] Getting Started\n",
"\n",
"We'll start by setting up the language model (LM) and retrieval model (RM). **DSPy** supports multiple API and local models. In this notebook, we'll work with GPT-3.5 (`gpt-3.5-turbo`) and the retriever `ColBERTv2`.\n",
"\n",
"To make things easy, we've set up a ColBERTv2 server hosting a Wikipedia 2017 \"abstracts\" search index (i.e., containing first paragraph of each article from this [2017 dump](https://hotpotqa.github.io/wiki-readme.html)), so you don't need to worry about setting one up! It's free.\n",
"\n",
"**Note:** _If you want to run this notebook without changing the examples, you don't need an API key. All examples are already cached internally so you can inspect them!_"
],
"metadata": {
"id": "dVnyqY4o88Hb"
}
},
{
"cell_type": "code",
"source": [
"turbo = dspy.OpenAI(model='gpt-3.5-turbo')\n",
"colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')\n",
"\n",
"dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)"
],
"metadata": {
"id": "tiRb9e1Nr2VZ"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class StoryElement(dspy.Signature):\n",
" \"\"\"Generate elements of a story based on inputs.\"\"\"\n",
" setting = dspy.InputField(desc=\"General setting or mood of the story\")\n",
" characters = dspy.InputField(desc=\"Main characters and their traits\")\n",
" plot_point = dspy.InputField(desc=\"A key plot point to develop\")\n",
" story_chunk = dspy.OutputField(desc=\"A narrative chunk developing the plot point within the setting and involving the characters\")\n"
],
"metadata": {
"id": "MDQw-eBCr3-N"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"class StoryGenerator(dspy.Module):\n",
" def __init__(self):\n",
" super().__init__()\n",
" self.story_element_generator = dspy.ChainOfThought(StoryElement)\n",
"\n",
" def forward(self, setting, characters, plot_point):\n",
" return self.story_element_generator(setting=setting, characters=characters, plot_point=plot_point)\n"
],
"metadata": {
"id": "JqjyO-RQr5bc"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Install the OpenAI library (uncomment if needed)\n",
"# !pip install openai\n",
"\n",
"# Import necessary libraries\n",
"import openai\n",
"from google.colab import userdata\n",
"\n",
"# Retrieve and set the API key\n",
"api_key = userdata.get('OPENAI_API_KEY')\n",
"openai.api_key = api_key\n",
"\n",
"# Verify the API key is set (this is just for demonstration and should not be used in production code)\n",
"if openai.api_key:\n",
" print(\"OpenAI API key is set. Ready to proceed!\")\n",
"else:\n",
" print(\"OpenAI API key is not set. Please check your setup.\")\n"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "g02hSVPguLHk",
"outputId": "05710f3b-e873-4290-c8eb-8b6dca8e2e18"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"OpenAI API key is set. Ready to proceed!\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## Test the LLM Settings"
],
"metadata": {
"id": "e-OUqDqxqcx0"
}
},
{
"cell_type": "code",
"source": [
"from google.colab import userdata\n",
"\n",
"# Retrieve the API key\n",
"api_key = userdata.get('OPENAI_API_KEY')\n",
"openai.api_key = api_key\n",
"\n",
"# Example settings and characters to generate a story chunk\n",
"setting = \"A forgotten office city\"\n",
"characters = \"An adventurous archaeologist; a mysterious guardian of the city\"\n",
"plot_point = \"The archaeologist discovers a hidden temple\"\n",
"\n",
"# Initialize the story generator\n",
"story_gen = StoryGenerator()\n",
"\n",
"# Generate a story chunk\n",
"story_chunk = story_gen(setting=setting, characters=characters, plot_point=plot_point)\n",
"\n",
"# Print the generated story chunk\n",
"print(f\"Generated Story Chunk:\\n{story_chunk.story_chunk}\")\n"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "vm2gpr77r7QO",
"outputId": "2a526343-6eba-4e8c-9a04-2083e33cd6d7"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Generated Story Chunk:\n",
"As the archaeologist enters the hidden temple, they are met with intricate carvings and ancient artifacts. The guardian's warnings echo in their mind as they navigate through dark corridors and booby traps. Finally, they reach the inner chamber where a powerful artifact is said to be hidden. Just as they are about to reach for it, the guardian appears, revealing their true identity as\n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"## Story Time UI"
],
"metadata": {
"id": "UEesDm35ruM9"
}
},
{
"cell_type": "markdown",
"source": [
"# Training and Testing Your AI with DSPy\n",
"\n",
"DSPy not only enhances AI content creation but also provides robust training and testing frameworks for AI models. Here's a brief overview focused on training and testing your AI for storytelling with DSPy, featuring the `ProgressiveStory` class as a foundational example.\n",
"\n",
"## Training Your AI\n",
"\n",
"The `ProgressiveStory` class encapsulates the essence of a story, including its length, context, development guidelines, and a concluding sentence. This class structure is pivotal for training AI models on generating cohesive and engaging narratives based on specified parameters."
],
"metadata": {
"id": "wj5EY4YL-LKt"
}
},
{
"cell_type": "code",
"source": [
"class ProgressiveStory:\n",
" def __init__(self, output_length, story_context, broad_guidelines, concluding_sentence=\"\"):\n",
" self.output_length = output_length\n",
" self.story_context = story_context\n",
" self.developments = []\n",
" self.prompts = []\n",
" self.broad_guidelines = broad_guidelines\n",
" self.concluding_sentence = concluding_sentence\n",
"\n",
" def __repr__(self):\n",
" return f\"ProgressiveStory(output_length={self.output_length}, story_context='{self.story_context}', broad_guidelines={self.broad_guidelines}, concluding_sentence='{self.concluding_sentence}')\"\n",
"\n",
"trainset = [\n",
" ProgressiveStory(\n",
" output_length=200,\n",
" story_context=\"In a world where magic and technology coexist...\",\n",
" broad_guidelines=[\"Unique world-building\", \"Complex characters\", \"Thought-provoking themes\"],\n",
" concluding_sentence=\"The end of one journey is the beginning of another.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=300,\n",
" story_context=\"The ancient prophecy spoke of a chosen one...\",\n",
" broad_guidelines=[\"Epic adventure\", \"Unexpected twists\", \"Emotional depth\"],\n",
" concluding_sentence=\"The fate of the world hung in the balance.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=250,\n",
" story_context=\"In a small town nestled in the mountains...\",\n",
" broad_guidelines=[\"Mystery and suspense\", \"Rich atmosphere\", \"Compelling characters\"],\n",
" concluding_sentence=\"The truth was finally revealed.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=400,\n",
" story_context=\"In a galaxy far, far away...\",\n",
" broad_guidelines=[\"Space opera\", \"Iconic characters\", \"Epic battles\"],\n",
" concluding_sentence=\"The force will be with you, always.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=350,\n",
" story_context=\"In a dystopian future where humanity is on the brink of extinction...\",\n",
" broad_guidelines=[\"Post-apocalyptic setting\", \"Survival themes\", \"Moral dilemmas\"],\n",
" concluding_sentence=\"In the end, hope was the only thing that kept them going.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=280,\n",
" story_context=\"In a world where dreams become reality...\",\n",
" broad_guidelines=[\"Surreal imagery\", \"Psychological exploration\", \"Blurred lines between reality and fantasy\"],\n",
" concluding_sentence=\"And so, the dreamer awoke, forever changed by the experience.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=320,\n",
" story_context=\"In a city where crime runs rampant...\",\n",
" broad_guidelines=[\"Gritty atmosphere\", \"Flawed protagonists\", \"Moral ambiguity\"],\n",
" concluding_sentence=\"In a world of corruption, one must choose their allies carefully.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=240,\n",
" story_context=\"In a realm where mythical creatures roam...\",\n",
" broad_guidelines=[\"Rich folklore\", \"Hero's journey\", \"Magical encounters\"],\n",
" concluding_sentence=\"And thus, the legend was born, to be passed down through generations.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=380,\n",
" story_context=\"In a time when the lines between man and machine blur...\",\n",
" broad_guidelines=[\"Philosophical questions\", \"Technological advancements\", \"Identity crisis\"],\n",
" concluding_sentence=\"In the end, it was not about being human or machine, but about being true to oneself.\"\n",
" ),\n",
" ProgressiveStory(\n",
" output_length=220,\n",
" story_context=\"In a world where time travel is a reality...\",\n",
" broad_guidelines=[\"Paradoxes and consequences\", \"Historical settings\", \"Butterfly effect\"],\n",
" concluding_sentence=\"The traveler realized that the past, present, and future are all interconnected.\"\n",
" )\n",
"]\n",
"\n",
"print(trainset)"
],
"metadata": {
"id": "mysE3R8cSVqp"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import dspy\n",
"import random\n",
"import numpy as np\n",
"import re\n",
"import logging\n",
"import time\n",
"\n",
"from IPython.display import display, Markdown, clear_output\n",
"from dspy.teleprompt import BootstrapFewShot\n",
"\n",
"conclusion_min_length = 150 # Minimum number of words in the conclusion\n",
"conclusion_max_length = 250 # Maximum number of words in the conclusion\n",
"\n",
"#@title Story Configuration { run: \"none\", display-mode: \"form\" }\n",
"story_genre = \"Historical Fiction\" #@param [\"Fantasy\", \"Science Fiction\", \"Mystery\", \"Romance\", \"Thriller\", \"Historical Fiction\", \"Horror\", \"Adventure\", \"Literary Fiction\", \"Young Adult\", \"Dystopian\", \"Paranormal\", \"Crime\", \"Western\", \"Magical Realism\"]\n",
"story_theme = \"A movie script for a horror films using a movie format and output. \" #@param {type:\"string\"}\n",
"story_setting = \"In a distant star wars vs star trek\" #@param {type:\"string\"}\n",
"main_character = \"Luke Skywalker jr\" #@param {type:\"string\"}\n",
"\n",
"story_length = 800 #@param {type:\"slider\", min:200, max:3000, step:50}\n",
"writing_style = \"Journalistic\" #@param [\"Concise\", \"Descriptive\", \"Humorous\", \"Serious\", \"Poetic\", \"Journalistic\", \"Conversational\", \"Expository\", \"Persuasive\", \"Stream of Consciousness\"]\n",
"tone = \"Mysterious\" #@param [\"Lighthearted\", \"Dark\", \"Adventurous\", \"Mysterious\", \"Romantic\", \"Humorous\", \"Serious\", \"Whimsical\", \"Nostalgic\", \"Inspirational\", \"Suspenseful\", \"Melancholic\"]\n",
"tense = \"Past\" #@param [\"Past\", \"Present\", \"Future\", \"Mixed\"]\n",
"\n",
"num_characters = 1 #@param {type:\"slider\", min:1, max:25, step:1}\n",
"character_depth = \"Evolving arcs\" #@param [\"Brief descriptions\", \"Detailed backstories\", \"Complex personalities\", \"Archetypal roles\", \"Evolving arcs\", \"Inner conflicts\", \"Unique motivations\", \"Mysterious pasts\"]\n",
"dialogue_amount = \"Realistic conversations\" #@param [\"Minimal\", \"Moderate\", \"Extensive\", \"Realistic conversations\", \"Witty banter\", \"Philosophical discussions\", \"Emotional exchanges\", \"Plot-driven dialogue\"]\n",
"\n",
"plot_complexity = \"Unreliable narrator\" #@param [\"Straightforward\", \"Some twists\", \"Multiple subplots\", \"Non-linear narrative\", \"Parallel storylines\", \"Ensemble cast\", \"Unreliable narrator\", \"Nested stories\", \"Philosophical undertones\", \"Allegorical elements\"]\n",
"pacing = \"Gradual reveal\" #@param [\"Fast-paced action\", \"Steady buildup\", \"Slow and reflective\", \"Alternating tempo\", \"Cliffhanger chapters\", \"Time jumps\", \"Foreshadowing\", \"In media res\", \"Gradual reveal\", \"Slice of life\"]\n",
"chapter_length = \"Long\" #@param [\"Short\", \"Medium\", \"Long\", \"Varying lengths\", \"Vignette style\", \"Novella-like\", \"Episodic\", \"Unconventional structure\"]\n",
"\n",
"theme_emphasis = \"Subtle underlying message\" #@param [\"Subtle underlying message\", \"Clearly stated moral\", \"Open to interpretation\", \"Allegory\", \"Symbolism\", \"Social commentary\", \"Philosophical questions\", \"Ethical dilemmas\", \"Political satire\", \"Religious undertones\"]\n",
"emotional_tone = \"Unsettling\" #@param [\"Uplifting\", \"Thought-provoking\", \"Tragic\", \"Inspiring\", \"Unsettling\", \"Heartwarming\", \"Bittersweet\", \"Suspenseful\", \"Comedic\", \"Nostalgic\", \"Cathartic\", \"Melancholic\", \"Hopeful\", \"Satirical\", \"Provocative\"]\n",
"ending_type = \"Open-ended\" #@param [\"Open-ended\", \"Resolved\", \"Twist\", \"Ambiguous\", \"Cliffhanger\", \"Epilogue\", \"Circular\", \"Deus ex Machina\", \"Tragic\", \"Happy\", \"Bittersweet\", \"Ironic\", \"Metaphorical\", \"Surreal\", \"Metafictional\"]\n",
"\n",
"include_illustrations = False #@param {type:\"boolean\"}\n",
"include_poems = False #@param {type:\"boolean\"}\n",
"allow_mature_themes = False #@param {type:\"boolean\"}\n",
"\n",
"temperature = 0.1 #@param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
"top_k = 10 #@param {type:\"slider\", min:0, max:100, step:10}\n",
"min_iterations = 16 #@param {type:\"slider\", min:1, max:100, step:1}\n",
"\n",
"# Combine all guideline inputs into a list\n",
"broad_guidelines = [\n",
" f\"Genre: {story_genre}\",\n",
" f\"Theme: {story_theme}\",\n",
" f\"Setting: {story_setting}\",\n",
" f\"Main Character: {main_character}\",\n",
" f\"Writing Style: {writing_style}\",\n",
" f\"Tone: {tone}\",\n",
" f\"Tense: {tense}\",\n",
" f\"Number of Characters: {num_characters}\",\n",
" f\"Character Depth: {character_depth}\",\n",
" f\"Dialogue Amount: {dialogue_amount}\",\n",
" f\"Plot Complexity: {plot_complexity}\",\n",
" f\"Pacing: {pacing}\",\n",
" f\"Chapter Length: {chapter_length}\",\n",
" f\"Theme Emphasis: {theme_emphasis}\",\n",
" f\"Emotional Tone: {emotional_tone}\",\n",
" f\"Ending Type: {ending_type}\",\n",
" f\"Include Illustrations: {'Yes' if include_illustrations else 'No'}\",\n",
" f\"Include Poems: {'Yes' if include_poems else 'No'}\",\n",
" f\"Allow Mature Themes: {'Yes' if allow_mature_themes else 'No'}\"\n",
"]\n",
"\n",
"class StoryDevelopment(dspy.Signature):\n",
" \"\"\"Generate story developments using OpenAI's language model.\"\"\"\n",
" context = dspy.InputField(desc=\"The current story context\")\n",
" prompt = dspy.InputField(desc=\"A prompt to guide the language model\")\n",
" max_tokens = dspy.InputField(desc=\"Maximum number of tokens to generate\", default=\"500\")\n",
" temperature = dspy.InputField(desc=\"Temperature for sampling\", default=str(temperature))\n",
" top_k = dspy.InputField(desc=\"Top K words to sample from\", default=str(top_k))\n",
" development = dspy.OutputField(desc=\"The generated story development\")\n",
" teleprompter = dspy.InputField(desc=\"Additional context or instructions for the language model\", default=\"matter of fact\")\n",
"\n",
"class StoryReview(dspy.Signature):\n",
" \"\"\"Assess story completeness and coherence using OpenAI's language model.\"\"\"\n",
" story = dspy.InputField(desc=\"The current story text\")\n",
" feedback = dspy.OutputField(desc=\"Feedback on whether the story needs more development or is complete\")\n",
" teleprompter = dspy.InputField(desc=\"Additional context or instructions for the language model\", default=\"\")\n",
"\n",
"def validate_story(example, pred, trace=True):\n",
" # Check if the story is coherent and engaging\n",
" coherence = True # Placeholder implementation, replace with actual coherence check\n",
" engagement = True # Placeholder implementation, replace with actual engagement check\n",
"\n",
" # Check if the story follows the broad guidelines\n",
" follows_guidelines = all(guideline.lower() in pred.lower() for guideline in broad_guidelines)\n",
"\n",
" return coherence and engagement and follows_guidelines\n",
"\n",
"teleprompter = BootstrapFewShot(metric=validate_story)\n",
"\n",
"class ProgressiveStory:\n",
" def __init__(self, story_length, story_context, broad_guidelines, min_iterations=1):\n",
" self.story_length = story_length\n",
" self.story_context = story_context\n",
" self.developments = []\n",
" self.prompts = []\n",
" self.broad_guidelines = broad_guidelines\n",
" self.min_iterations = min_iterations\n",
"\n",
" def develop_story(self):\n",
" print(\"Generating story...\")\n",
"\n",
" generate_development = dspy.Predict(StoryDevelopment)\n",
" review_story = dspy.Predict(StoryReview)\n",
" iteration = 0\n",
"\n",
" # Generate the main story content\n",
" while len(self.story_context.split()) < self.story_length:\n",
" # Update loading indicator\n",
" clear_output(wait=True)\n",
" print(f\"Generating story... {len(self.story_context.split())}/{self.story_length} words\")\n",
"\n",
" if iteration < len(self.broad_guidelines):\n",
" prompt = self.generate_dynamic_prompt(iteration)\n",
" else:\n",
" prompt = \"Continue the story.\"\n",
"\n",
" iteration += 1\n",
" self.prompts.append(prompt)\n",
" remaining_words = self.story_length - len(self.story_context.split())\n",
" max_tokens = self.calculate_max_tokens(remaining_words)\n",
" development_teleprompter = \"Focus on vivid descriptions and engaging dialogue.\"\n",
"\n",
" unique_identifier = str(iteration) + \"_\" + str(time.time())\n",
" modified_prompt = prompt + \" \" + unique_identifier\n",
"\n",
" development = generate_development(context=self.story_context, prompt=modified_prompt, max_tokens=str(max_tokens), temperature=\"0.9\", top_k=\"50\", teleprompter=development_teleprompter).development\n",
"\n",
" self.story_context += \" \" + development.strip()\n",
" self.developments.append(development)\n",
"\n",
" # Generate the conclusion\n",
" conclusion_prompt = f\"Given the following story:\\n\\n{self.story_context}\\n\\nWrite a brief word satisfying conclusion that ties together the main themes, resolves the main conflicts, and provides a sense of closure. Avoid any abrupt or incomplete sentences.\"\n",
" conclusion_max_tokens = self.calculate_max_tokens(12200)\n",
" conclusion_teleprompter = \"Focus on creating a a brief 100 words, coherent and satisfying conclusion that ties together the main themes, resolves the main conflicts, and provides a sense of closure. Avoid ending the story abruptly or in the middle of a sentence.\"\n",
"\n",
" unique_identifier = str(iteration + 1) + \"_\" + str(time.time())\n",
" modified_conclusion_prompt = conclusion_prompt + \" \" + unique_identifier\n",
"\n",
" conclusion = generate_development(context=modified_conclusion_prompt, prompt=modified_conclusion_prompt, max_tokens=str(conclusion_max_tokens), temperature=\"0.7\", top_k=\"50\", teleprompter=conclusion_teleprompter).development\n",
" self.story_context += \" \" + conclusion.strip()\n",
" self.developments.append(conclusion)\n",
"\n",
" clear_output(wait=True)\n",
" print(\"Story generation complete!\")\n",
"\n",
" def calculate_max_tokens(self, remaining_words):\n",
" average_word_length = sum(len(word) for word in self.story_context.split()) / len(self.story_context.split())\n",
" estimated_tokens_per_word = average_word_length + 1\n",
" max_tokens = int(remaining_words * estimated_tokens_per_word)\n",
" return min(max_tokens, 1024)\n",
"\n",
"\n",
" def generate_dynamic_prompt(self, iteration):\n",
" last_sentence = self.story_context.split('.')[-1]\n",
" theme = self.broad_guidelines[iteration % len(self.broad_guidelines)]\n",
" remaining_words = max(self.story_length - len(self.story_context.split()), 250) # Ensure a minimum of 150 words\n",
" specific_prompt = f\"{last_sentence}. Now, considering the guideline '{theme}', continue the story. Aim for around {remaining_words} words.\"\n",
" logging.info(f\"Generated dynamic prompt for iteration {iteration}: {specific_prompt}\")\n",
" return specific_prompt\n",
"\n",
" def compile_story(self):\n",
" return self.story_context\n",
"\n",
" def verbose_display(self):\n",
" word_count = len(self.story_context.split())\n",
" context_info = f\"**Initial Context:** {self.story_context.split('.')[0]}.\"\n",
" prompt_info = \"**Prompts Used:**\\n\"\n",
" for i, prompt in enumerate(self.prompts, start=1):\n",
" prompt_info += f\"Iteration {i}: {prompt}\\n\"\n",
" story = self.format_into_paragraphs(self.compile_story())\n",
" iteration_info = f\"**Total Iterations:** {len(self.prompts)}\"\n",
" verbose_output = f\"\"\"\n",
" **Word Count:** {word_count}\n",
"\n",
" {context_info}\n",
"\n",
" {prompt_info}\n",
"\n",
" {iteration_info}\n",
"\n",
" **Final Story:**\n",
"\n",
" {story}\n",
" \"\"\"\n",
" display(Markdown(verbose_output))\n",
"\n",
" def format_into_paragraphs(self, text, min_paragraph_length=100):\n",
" sentences = re.split('(?<=[.!?]) +', text)\n",
" paragraph = \"\"\n",
" formatted_text = \"\"\n",
" for sentence in sentences:\n",
" if len(paragraph) + len(sentence) < min_paragraph_length:\n",
" paragraph += sentence + \" \"\n",
" else:\n",
" formatted_text += paragraph.strip() + \"\\n\\n\"\n",
" paragraph = sentence + \" \"\n",
" if paragraph:\n",
" formatted_text += paragraph.strip()\n",
" return formatted_text\n",
"\n",
"# Configure logging level (set to INFO or DEBUG as needed)\n",
"logging.basicConfig(level=logging.INFO)\n",
"\n",
"# Usage\n",
"# Remove the original story_context assignment\n",
"# story_context = f\"In the {story_setting}, {main_character} embarks on a quest.\"\n",
"\n",
"# Create a StoryIntroduction signature\n",
"class StoryIntroduction(dspy.Signature):\n",
" \"\"\"Generate story introduction using OpenAI's language model.\"\"\"\n",
" setting = dspy.InputField(desc=\"The story setting\")\n",
" main_character = dspy.InputField(desc=\"The main character\")\n",
" genre = dspy.InputField(desc=\"The story genre\")\n",
" theme = dspy.InputField(desc=\"The story theme\")\n",
" introduction = dspy.OutputField(desc=\"The generated story introduction\")\n",
" teleprompter = dspy.InputField(desc=\"Additional context or instructions for the language model\", default=\"Write an engaging and dynamic story introduction that sets the stage for the adventure and captures the reader's attention.\")\n",
"\n",
"# Generate the story introduction using the LLM\n",
"generate_introduction = dspy.Predict(StoryIntroduction)\n",
"introduction_teleprompter = \"Write a compelling and immersive introduction to the story, highlighting the main character, setting, and the central conflict or quest. Use vivid descriptions and engaging language to draw the reader into the story world. Be verbose.\"\n",
"story_context = generate_introduction(setting=story_setting, main_character=main_character, genre=story_genre, theme=story_theme, teleprompter=introduction_teleprompter).introduction\n",
"\n",
"# Create the ProgressiveStory instance with the generated story_context\n",
"# progressive_story = ProgressiveStory(story_length=story_length, story_context=story_context, broad_guidelines=broad_guidelines, min_iterations=min_iterations)\n",
"\n",
"progressive_story = ProgressiveStory(story_length=story_length, story_context=story_context, broad_guidelines=broad_guidelines, min_iterations=min_iterations)\n",
"progressive_story.develop_story()\n",
"progressive_story.verbose_display()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"id": "ka-9QEAhu4I6",
"outputId": "59f48cb0-24ef-41bb-f9c7-ad1bc43ad5a2"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Story generation complete!\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<IPython.core.display.Markdown object>"
],
"text/markdown": "\n **Word Count:** 943\n\n **Initial Context:** In a distant galaxy where the epic battle between Star Wars and Star Trek rages on, a new hero emerges to take center stage.\n\n **Prompts Used:**\nIteration 1: Will Luke prove himself worthy of his father's legacy, or. Now, considering the guideline 'Genre: Historical Fiction', continue the story. Aim for around 671 words.\nIteration 2: But Luke's courage never wavered, and his determination only grew stronger with each obstacle he faced. Now, considering the guideline 'Theme: A movie script for a horror films using a movie format and output. ', continue the story. Aim for around 539 words.\nIteration 3: \n\nThe Sith Lord chuckled dark. Now, considering the guideline 'Setting: In a distant star wars vs star trek', continue the story. Aim for around 419 words.\nIteration 4: With. Now, considering the guideline 'Main Character: Luke Skywalker jr', continue the story. Aim for around 308 words.\nIteration 5: \n\nAs the duel raged on, Luke felt the weight of his father's legacy urging him on, giving. Now, considering the guideline 'Writing Style: Journalistic', continue the story. Aim for around 250 words.\nIteration 6: \n\nWith a swift motion, Luke disarmed Darth Mal. Now, considering the guideline 'Tone: Mysterious', continue the story. Aim for around 250 words.\n\n\n **Total Iterations:** 6\n\n **Final Story:**\n\n \n\nIn a distant galaxy where the epic battle between Star Wars and Star Trek rages on, a new hero emerges to take center stage.\n\nLuke Skywalker Jr, the son of the legendary Jedi Knight, finds himself thrust into a world of danger and intrigue as he navigates the treacherous waters of intergalactic warfare.\n\nWith his father's lightsaber in hand and a heart full of courage, Luke sets out on a quest to uncover the truth behind a dark conspiracy that threatens to tear the galaxy apart.\n\nAs he delves deeper into the shadows of the universe, he must confront his own fears and doubts, facing enemies both old and new in a battle for the fate of the galaxy.\n\nWill Luke prove himself worthy of his father's legacy, or As Luke Skywalker Jr.\n\ncontinued his quest to uncover the truth behind the dark conspiracy threatening the galaxy, he found himself facing challenges unlike any he had encountered before.\n\nThe path ahead was fraught with danger, and the weight of his father's legacy bore heavily on his shoulders.\n\nBut Luke was determined to prove himself worthy, to honor the memory of the great Jedi Knight who had come before him.\n\nAs he traveled from one corner of the galaxy to the next, Luke encountered allies and enemies alike.\n\nSome were old friends of his father, eager to help him in his quest.\n\nOthers were agents of the dark conspiracy, determined to stop him at any cost.\n\nBut Luke's courage never wavered, and his determination only grew stronger with each obstacle he faced As Luke's journey continued, he found himself on a desolate planet shrouded in darkness.\n\nThe air was thick with an eerie silence, broken only by the distant howl of a creature unknown.\n\nLuke gripped his father's lightsaber tightly, his heart pounding in his chest as he ventured deeper into the shadows.\n\nSuddenly, a figure emerged from the darkness, its eyes glowing with a malevolent light.\n\nIt was a Sith Lord, a servant of the dark conspiracy that threatened the galaxy.\n\nLuke knew he had to face this enemy head-on, for the fate of the galaxy hung in the balance.\n\n\"Who are you?\" Luke demanded, his voice steady despite the fear that gnawed at his insides.\n\nThe Sith Lord chuckled dark The Sith Lord chuckled darkly, his voice echoing through the desolate landscape.\n\n\"I am Darth Malice, servant of the dark side and harbinger of destruction,\" he hissed, his red lightsaber igniting with a menacing hum.\n\nLuke's grip tightened on his own lightsaber, his resolve steeling as he prepared to face this formidable foe.\n\n\"You will not succeed in your twisted plans, Darth Malice,\" Luke declared, his voice unwavering despite the fear that threatened to consume him.\n\nThe Sith Lord's laughter filled the air, a chilling sound that sent shivers down Luke's spine.\n\n\"You underestimate the power of the dark side, young Jedi,\" Darth Malice taunted, his eyes gleaming with malice.\n\nWith With a swift motion, Luke lunged forward, his lightsaber clashing against Darth Malice's with a shower of sparks.\n\nThe two adversaries circled each other, their blades a blur of light and shadow in the darkness.\n\nLuke's heart raced as he fought with all his strength, drawing on the teachings of his father and the Force to guide him.\n\n\"You cannot defeat me, young Jedi,\" Darth Malice sneered, his attacks relentless and powerful.\n\nBut Luke refused to back down, his determination burning bright within him.\n\nWith a fierce battle cry, he unleashed a flurry of strikes, each one aimed at bringing down the Sith Lord.\n\nAs the duel raged on, Luke felt the weight of his father's legacy urging him on, giving As the duel raged on, Luke felt the weight of his father's legacy urging him on, giving him strength he never knew he had.\n\nThe clash of lightsabers echoed through the desolate landscape, casting eerie shadows on the ground.\n\nLuke's eyes blazed with determination as he faced off against Darth Malice, his every move calculated and precise.\n\n\"Your reign of darkness ends here, Darth Malice,\" Luke declared, his voice ringing out with conviction.\n\nThe Sith Lord snarled in response, his attacks growing more ferocious as he sought to overpower the young Jedi.\n\nBut Luke held his ground, his resolve unshakable as he pushed back against the tide of darkness.\n\nWith a swift motion, Luke disarmed Darth Mal With a swift motion, Luke disarmed Darth Mal, sending the Sith Lord's lightsaber flying into the darkness.\n\nThe red blade sputtered and died, leaving Darth Malice defenseless before the young Jedi.\n\nLuke's eyes blazed with determination as he advanced on his fallen enemy, his father's lightsaber humming with power in his hand.\n\n\"You have underestimated the power of the light side, Darth Malice,\" Luke said, his voice filled with a quiet intensity.\n\n\"Your reign of darkness ends here.\"\n\nDarth Malice scrambled to his feet, his eyes wide with disbelief and rage.\n\n\"You cannot defeat me, Jedi scum!\" he spat, his voice laced with venom.\n\nBut Luke was undeterred, his every movement With a final, decisive strike, Luke Skywalker Jr.\n\ndefeated Darth Malice, vanquishing the darkness that threatened the galaxy.\n\nAs the Sith Lord fell, the shadows lifted, and a sense of peace washed over the desolate planet.\n\nLuke stood victorious, his father's lightsaber glowing with the light of the Force.\n\nThe galaxy was safe once more, and Luke had proven himself worthy of his father's legacy.\n\nWith a heart full of courage and a spirit unbroken, he knew that his journey was far from over, but he faced the future with hope and determination, ready to continue the fight for justice and peace.\n "
},
"metadata": {}
}
]
},
{
"cell_type": "markdown",
"source": [
"#Inspect Output"
],
"metadata": {
"id": "kyXuTPqbLNTj"
}
},
{
"cell_type": "code",
"source": [
"turbo.inspect_history(n=5)"
],
"metadata": {
"id": "IHcx3AOzLPnY"
},
"execution_count": null,
"outputs": []
}
],
"metadata": {
"kernelspec": {
"display_name": "py39",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.17"
},
"orig_nbformat": 4,
"colab": {
"provenance": [],
"include_colab_link": true
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment