v1.3.0: Improve all AI prompts, refinement loops, and cover generation accuracy
story.py — write_chapter():
- Added POSITION context ("Chapter N of Total") so the AI calibrates narrative
tension correctly (setup vs escalation vs climax/payoff)
- Moved PACING_GUIDE to sit directly after PACING metadata instead of being
buried after 13 quality criteria items where the AI rarely reads it
- Removed duplicate pacing descriptions that appeared after QUALITY_CRITERIA
story.py — refinement loop:
- Capped critique history to last 2 entries (was accumulating all previous
attempts, wasting tokens and confusing the model on attempt 4-5)
- Added TARGET_WORDS and BEATS constraints to the refinement prompt to prevent
chapters from shrinking or losing plot beats during editing passes
- Restructured refinement prompt with explicit HARD_CONSTRAINTS section
story.py — check_and_propagate():
- Increased chapter context from 5000 to 12000 chars for continuity rewrites
(was asking for a full chapter rewrite but only providing a fragment)
- Added explicit word count target to rewrite so chapters are not truncated
- Added conservative decision bias: only rewrite on genuine contradictions
story.py — plan_structure():
- Now passes TARGET_CHAPTERS, TARGET_WORDS, GENRE, and CHARACTERS to the
structure AI — it was planning blindly without knowing the book's scale
marketing.py — generate_blurb():
- Rewrote prompt with 4-part structure: Hook → Stakes → Tension → Close
- Formats plot beats as a readable list instead of raw JSON array
- Extracts protagonist automatically for personalised blurb copy
- Added genre-tone matching, present-tense voice, and no-spoiler rule
marketing.py — generate_cover():
- Added genre-to-visual-style mapping (thriller → cinematic, fantasy → epic
digital painting, romance → painterly, etc.)
- Art prompt instructions now enforce: no text/letters/watermarks, rule-of-thirds
composition, explicit focal point, lighting description, colour palette
- Replaced generic image evaluation with a 5-criteria book-cover rubric:
visual impact, genre fit, composition, quality, and clean image (no text)
- Score penalties: -3 for visible text/watermarks, -2 for blur/deformed anatomy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,19 +89,41 @@ def evaluate_image_quality(image_path, prompt, model, folder=None):
|
||||
def generate_blurb(bp, folder):
|
||||
utils.log("MARKETING", "Generating blurb...")
|
||||
meta = bp.get('book_metadata', {})
|
||||
|
||||
|
||||
# Format beats as a readable list, not raw JSON
|
||||
beats = bp.get('plot_beats', [])
|
||||
beats_text = "\n".join(f" - {b}" for b in beats[:6]) if beats else " - (no beats provided)"
|
||||
|
||||
# Format protagonist for the blurb
|
||||
chars = bp.get('characters', [])
|
||||
protagonist = next((c for c in chars if 'protagonist' in c.get('role', '').lower()), None)
|
||||
protagonist_desc = f"{protagonist['name']} — {protagonist.get('description', '')}" if protagonist else "the protagonist"
|
||||
|
||||
prompt = f"""
|
||||
ROLE: Marketing Copywriter
|
||||
TASK: Write a back-cover blurb (150-200 words).
|
||||
|
||||
INPUT_DATA:
|
||||
TASK: Write a compelling back-cover blurb for a {meta.get('genre', 'fiction')} novel.
|
||||
|
||||
BOOK DETAILS:
|
||||
- TITLE: {meta.get('title')}
|
||||
- GENRE: {meta.get('genre')}
|
||||
- LOGLINE: {bp.get('manual_instruction')}
|
||||
- PLOT: {json.dumps(bp.get('plot_beats', []))}
|
||||
- CHARACTERS: {json.dumps(bp.get('characters', []))}
|
||||
|
||||
OUTPUT: Text only.
|
||||
- AUDIENCE: {meta.get('target_audience', 'General')}
|
||||
- PROTAGONIST: {protagonist_desc}
|
||||
- LOGLINE: {bp.get('manual_instruction', '(none)')}
|
||||
- KEY PLOT BEATS:
|
||||
{beats_text}
|
||||
|
||||
BLURB STRUCTURE:
|
||||
1. HOOK (1-2 sentences): Open with the protagonist's world and the inciting disruption. Make it urgent.
|
||||
2. STAKES (2-3 sentences): Raise the central conflict. What does the protagonist stand to lose?
|
||||
3. TENSION (1-2 sentences): Hint at the impossible choice or escalating danger without revealing the resolution.
|
||||
4. HOOK CLOSE (1 sentence): End with a tantalising question or statement that demands the reader open the book.
|
||||
|
||||
RULES:
|
||||
- 150-200 words total.
|
||||
- DO NOT reveal the ending or resolution.
|
||||
- Match the genre's marketing tone ({meta.get('genre', 'fiction')}: e.g. thriller = urgent/terse, romance = emotionally charged, fantasy = epic/wondrous, horror = dread-laden).
|
||||
- Use present tense for the blurb voice.
|
||||
- No "Blurb:", no title prefix, no labels — marketing copy only.
|
||||
"""
|
||||
try:
|
||||
response = ai.model_writer.generate_content(prompt)
|
||||
@@ -167,30 +189,51 @@ def generate_cover(bp, folder, tracking=None, feedback=None, interactive=False):
|
||||
except:
|
||||
utils.log("MARKETING", "Feedback analysis failed. Defaulting to full regeneration.")
|
||||
|
||||
genre = meta.get('genre', 'Fiction')
|
||||
tone = meta.get('style', {}).get('tone', 'Balanced')
|
||||
# Map genre to visual style suggestion
|
||||
genre_style_map = {
|
||||
'thriller': 'dark, cinematic, high-contrast photography style',
|
||||
'mystery': 'moody, atmospheric, noir-inspired painting',
|
||||
'romance': 'warm, painterly, soft-focus illustration',
|
||||
'fantasy': 'epic digital painting, rich colours, mythic scale',
|
||||
'science fiction': 'sharp digital art, cool palette, futuristic',
|
||||
'horror': 'unsettling, dark atmospheric painting, desaturated',
|
||||
'historical fiction': 'classical oil painting style, period-accurate',
|
||||
'young adult': 'vibrant illustrated style, bold colours',
|
||||
}
|
||||
suggested_style = genre_style_map.get(genre.lower(), 'professional digital illustration or photography')
|
||||
|
||||
design_prompt = f"""
|
||||
ROLE: Art Director
|
||||
TASK: Design a book cover.
|
||||
|
||||
METADATA:
|
||||
- TITLE: {meta.get('title')}
|
||||
- GENRE: {meta.get('genre')}
|
||||
- TONE: {meta.get('style', {}).get('tone', 'Balanced')}
|
||||
TASK: Design a professional book cover for an AI image generator.
|
||||
|
||||
VISUAL_CONTEXT:
|
||||
{visual_context}
|
||||
|
||||
USER_FEEDBACK:
|
||||
{f"{feedback}" if feedback else "None"}
|
||||
|
||||
INSTRUCTION:
|
||||
{f"{design_instruction}" if design_instruction else "Create a compelling, genre-appropriate cover."}
|
||||
|
||||
OUTPUT_FORMAT (JSON):
|
||||
BOOK:
|
||||
- TITLE: {meta.get('title')}
|
||||
- GENRE: {genre}
|
||||
- TONE: {tone}
|
||||
- SUGGESTED_VISUAL_STYLE: {suggested_style}
|
||||
|
||||
VISUAL_CONTEXT (characters and key themes from the story):
|
||||
{visual_context if visual_context else "Use genre conventions."}
|
||||
|
||||
USER_FEEDBACK: {feedback if feedback else "None"}
|
||||
DESIGN_INSTRUCTION: {design_instruction if design_instruction else "Create a compelling, genre-appropriate cover."}
|
||||
|
||||
COVER_ART_RULES:
|
||||
- The art_prompt must produce an image with NO text, no letters, no numbers, no watermarks, no UI elements, no logos.
|
||||
- Describe a clear FOCAL POINT (e.g. the protagonist, a dramatic scene, a symbolic object).
|
||||
- Use RULE OF THIRDS composition — leave visual space at top and/or bottom for the title and author text to be overlaid.
|
||||
- Describe LIGHTING that reinforces the tone (e.g. "harsh neon backlight" for thriller, "golden hour" for romance).
|
||||
- Describe the COLOUR PALETTE explicitly (e.g. "deep crimson and shadow-black", "soft rose gold and cream").
|
||||
- Characters must match their descriptions from VISUAL_CONTEXT if present.
|
||||
|
||||
OUTPUT_FORMAT (JSON only, no markdown):
|
||||
{{
|
||||
"font_name": "Name of a popular Google Font (e.g. Roboto, Cinzel, Oswald, Playfair Display)",
|
||||
"primary_color": "#HexCode (Background)",
|
||||
"text_color": "#HexCode (Contrast)",
|
||||
"art_prompt": "A detailed description of the cover art for an image generator. Explicitly describe characters based on the visual context."
|
||||
"font_name": "Name of a Google Font suited to the genre (e.g. Cinzel for fantasy, Oswald for thriller, Playfair Display for romance)",
|
||||
"primary_color": "#HexCode (dominant background/cover colour)",
|
||||
"text_color": "#HexCode (high contrast against primary_color)",
|
||||
"art_prompt": "Detailed {suggested_style} image generation prompt. Begin with the style. Describe composition, focal point, lighting, colour palette, and any characters. End with: No text, no letters, no watermarks, photorealistic/painted quality, 8k detail."
|
||||
}}
|
||||
"""
|
||||
try:
|
||||
@@ -243,7 +286,18 @@ def generate_cover(bp, folder, tracking=None, feedback=None, interactive=False):
|
||||
result.images[0].save(attempt_path)
|
||||
utils.log_usage(folder, "imagen", image_count=1)
|
||||
|
||||
score, critique = evaluate_image_quality(attempt_path, art_prompt, ai.model_writer, folder)
|
||||
cover_eval_criteria = (
|
||||
f"Book cover art for a {genre} novel titled '{meta.get('title')}'.\n\n"
|
||||
f"Evaluate STRICTLY as a professional book cover on these criteria:\n"
|
||||
f"1. VISUAL IMPACT: Is the image immediately arresting and compelling?\n"
|
||||
f"2. GENRE FIT: Does the visual style, mood, and palette match {genre}?\n"
|
||||
f"3. COMPOSITION: Is there a clear focal point? Are top/bottom areas usable for title/author text?\n"
|
||||
f"4. QUALITY: Is the image sharp, detailed, and free of deformities or blurring?\n"
|
||||
f"5. CLEAN IMAGE: Are there absolutely NO text, watermarks, letters, or UI artifacts?\n"
|
||||
f"Score 1-10. Deduct 3 points if any text/watermarks are visible. "
|
||||
f"Deduct 2 if the image is blurry or has deformed anatomy."
|
||||
)
|
||||
score, critique = evaluate_image_quality(attempt_path, cover_eval_criteria, ai.model_writer, folder)
|
||||
if score is None: score = 0
|
||||
|
||||
utils.log("MARKETING", f" -> Image Score: {score}/10. Critique: {critique}")
|
||||
|
||||
Reference in New Issue
Block a user