diff --git a/storycraftr/agent/agents.py b/storycraftr/agent/agents.py index 0298f2b..cc1fade 100644 --- a/storycraftr/agent/agents.py +++ b/storycraftr/agent/agents.py @@ -1,6 +1,7 @@ import os import glob import time +from datetime import datetime from dotenv import load_dotenv from openai import OpenAI from rich.console import Console @@ -229,7 +230,8 @@ def create_message( try: # Send prompt to OpenAI API avoid_cache_content = generate_prompt_with_hash( - f"content\n\n{FORMAT_OUTPUT.format(reference_author=config.reference_author, language=config.primary_language)}" + f"content\n\n{FORMAT_OUTPUT.format(reference_author=config.reference_author, language=config.primary_language)}", + datetime.now().strftime("%B %d, %Y"), ) client.beta.threads.messages.create( thread_id=thread_id, role="user", content=avoid_cache_content diff --git a/storycraftr/agent/iterate.py b/storycraftr/agent/iterate.py index 327a9ca..8252ad2 100644 --- a/storycraftr/agent/iterate.py +++ b/storycraftr/agent/iterate.py @@ -8,6 +8,8 @@ STRENGTHEN_ARGUMENT_PROMPT, INSERT_CHAPTER_PROMPT, REWRITE_SURROUNDING_CHAPTERS_PROMPT, + INSERT_FLASHBACK_CHAPTER_PROMPT, + REWRITE_SURROUNDING_CHAPTERS_FOR_FLASHBACK_PROMPT, ) from storycraftr.agent.agents import ( update_agent_files, @@ -321,7 +323,7 @@ def strengthen_core_argument(book_path, argument): return -def insert_new_chapter(book_path, position, prompt): +def insert_new_chapter(book_path, position, prompt, flashback=False): """ Function to insert a new chapter at the specified position, renaming chapters and adjusting content accordingly. """ @@ -368,8 +370,10 @@ def insert_new_chapter(book_path, position, prompt): assistant = create_or_get_assistant(book_path) thread = get_thread() + prompt = INSERT_FLASHBACK_CHAPTER_PROMPT if flashback else INSERT_CHAPTER_PROMPT + # Generate new chapter content using context - prompt_text = INSERT_CHAPTER_PROMPT.format(prompt=prompt, position=position) + prompt_text = prompt.format(prompt=prompt, position=position) new_chapter_text = create_message( book_path, thread_id=thread.id, @@ -423,6 +427,7 @@ def insert_new_chapter(book_path, position, prompt): prompt, progress, task_openai, + flashback, ) if next_chapter: @@ -435,10 +440,13 @@ def insert_new_chapter(book_path, position, prompt): prompt, progress, task_openai, + flashback, ) -def rewrite_chapters(book_path, path, num, position, prompt, progress, task_chapters): +def rewrite_chapters( + book_path, path, num, position, prompt, progress, task_chapters, flashback=False +): """ Function to rewrite the chapters before and after the inserted chapter to ensure consistency with the new chapter. Utilizes the retrieval system to access the full context of the book without loading chapter contents directly. @@ -447,11 +455,15 @@ def rewrite_chapters(book_path, path, num, position, prompt, progress, task_chap assistant = create_or_get_assistant(book_path) thread = get_thread() - # Rewrite chapters using retrieval context - rewrite_prompt = REWRITE_SURROUNDING_CHAPTERS_PROMPT.format( - prompt=prompt, position=position, chapter=num + prompt = ( + REWRITE_SURROUNDING_CHAPTERS_FOR_FLASHBACK_PROMPT + if flashback + else REWRITE_SURROUNDING_CHAPTERS_PROMPT ) + # Rewrite chapters using retrieval context + rewrite_prompt = prompt.format(prompt=prompt, position=position, chapter=num) + updated_chapters = create_message( book_path, thread_id=thread.id, diff --git a/storycraftr/cmd/iterate.py b/storycraftr/cmd/iterate.py index 1108179..bf5fce2 100644 --- a/storycraftr/cmd/iterate.py +++ b/storycraftr/cmd/iterate.py @@ -166,11 +166,13 @@ def insert_chapter(position, prompt, book_path=None): @iterate.command() -@click.option("--book-path", type=click.Path(), help="Path to the book directory") +@click.option( + "--book-path", type=click.Path(), help="Path to the book directory", required=False +) +@click.argument("position", type=int) @click.argument("prompt") -@click.argument("chapter_number", type=int) -def split_chapter(prompt, chapter_number, book_path): - """Split a chapter and adjust the numbering of subsequent chapters.""" +def add_flashback(position, prompt, book_path=None): + """Add a flashback scene between two chapters.""" if not book_path: book_path = os.getcwd() @@ -178,17 +180,24 @@ def split_chapter(prompt, chapter_number, book_path): return None console.print( - f"[yellow]The command 'split-chapter' is not yet implemented.[/yellow]" + f"[bold blue]Inserting a new flashback chapter at position {position} in the book: {book_path}[/bold blue]" + ) + + # Call the function to insert the new chapter and adjust the surrounding chapters + insert_new_chapter(book_path, position, prompt, flashback=True) + + # Success log + console.print( + f"[green bold] Flashback Chapter insertion completed at position {position}, and chapters renumbered accordingly![/green bold]" ) - console.print(f"Prompt: {prompt}, Split chapter: {chapter_number}") @iterate.command() @click.option("--book-path", type=click.Path(), help="Path to the book directory") @click.argument("prompt") -@click.argument("chapter_position", type=int) -def add_flashback(prompt, chapter_position, book_path): - """Add a flashback scene between two chapters.""" +@click.argument("chapter_number", type=int) +def split_chapter(prompt, chapter_number, book_path): + """Split a chapter and adjust the numbering of subsequent chapters.""" if not book_path: book_path = os.getcwd() @@ -196,11 +205,9 @@ def add_flashback(prompt, chapter_position, book_path): return None console.print( - f"[yellow]The command 'add-flashback' is not yet implemented.[/yellow]" - ) - console.print( - f"Prompt: {prompt}, Flashback between chapters: {chapter_position} and {chapter_position + 1}" + f"[yellow]The command 'split-chapter' is not yet implemented.[/yellow]" ) + console.print(f"Prompt: {prompt}, Split chapter: {chapter_number}") @iterate.command() diff --git a/storycraftr/prompts/iterate.py b/storycraftr/prompts/iterate.py index a776d25..377f9c1 100644 --- a/storycraftr/prompts/iterate.py +++ b/storycraftr/prompts/iterate.py @@ -28,11 +28,33 @@ all subsequent chapters will be renumbered accordingly. For example, if a new chapter is inserted at position 3, the current chapter 3 will become chapter 4, chapter 4 will become chapter 5, and so on. Use the retrieval system to access the chapters before and after this position, ensuring that the new chapter fits seamlessly with the narrative, themes, and character arcs. +Generate content only for the newly inserted chapter, not for the surrounding chapters. Use this prompt for context: {prompt}. """ REWRITE_SURROUNDING_CHAPTERS_PROMPT = """ Write the chapters {chapter}, ensuring that they fit seamlessly with the previous and next chapter. Utilize the retrieval system to gather context from the full book, making sure the tone, style, and character arcs remain consistent. +Generate content only for the requested chapter, without altering or including content from other chapters. +Use this prompt for context: {prompt}. +""" + +INSERT_FLASHBACK_CHAPTER_PROMPT = """ +Insert a new chapter at position {position} in the book, ensuring that the inserted chapter serves as a meaningful flashback. +The flashback should provide essential backstory or context that deepens the reader's understanding of the characters, themes, or events. +All subsequent chapters will be renumbered accordingly. +For example, if a new chapter is inserted at position 3, the current chapter 3 will become chapter 4, chapter 4 will become chapter 5, and so on. +Use the retrieval system to access the chapters before and after this position, ensuring that the new flashback fits seamlessly with the narrative, +maintaining the tone and character arcs established in the book. +Generate content only for the newly inserted flashback chapter, not for the surrounding chapters. +Use this prompt for context: {prompt}. +""" + +REWRITE_SURROUNDING_CHAPTERS_FOR_FLASHBACK_PROMPT = """ +Write the chapters {chapter}, ensuring that they fit seamlessly with the previous and next chapter, +and that the newly inserted flashback enhances the overall narrative flow. +Utilize the retrieval system to gather context from the full book, ensuring that the tone, style, and character arcs remain consistent. +The flashback should feel integral to the story, adding depth without disrupting the pacing or narrative. +Generate content only for the requested chapter, without altering or including content from other chapters. Use this prompt for context: {prompt}. """ diff --git a/storycraftr/utils/core.py b/storycraftr/utils/core.py index e96c5b2..bca0a87 100644 --- a/storycraftr/utils/core.py +++ b/storycraftr/utils/core.py @@ -1,21 +1,25 @@ import os +import secrets # Para generar números aleatorios seguros import hashlib import json from typing import NamedTuple from rich.console import Console +from storycraftr.prompts.permute import longer_date_formats console = Console() -def generate_prompt_with_hash(original_prompt): +def generate_prompt_with_hash(original_prompt, date): # Genera un hash basado en el contenido del prompt hash_object = hashlib.sha256(original_prompt.encode()) hash_hex = hash_object.hexdigest() - # Combina el hash con el prompt original (puedes limitar el hash a los primeros caracteres si prefieres) - modified_prompt = ( - f"{hash_hex}: {original_prompt}" # Usa los primeros 10 caracteres del hash - ) + # Selecciona una frase aleatoria de la lista usando secrets.choice para mayor seguridad + random_phrase = secrets.choice(longer_date_formats).format(date=date) + + # Combina la frase seleccionada, un salto de línea, el hash y el prompt original + modified_prompt = f"{random_phrase}\n\n{hash_hex[:10]}: {original_prompt}" # Usa los primeros 10 caracteres del hash + return modified_prompt