Auto-commit: Fix 'create new book not showing anything' — 3 root causes
1. templates/project_setup.html: s.tropes|join and s.formatting_rules|join
raised Jinja2 UndefinedError when AI failed and fallback dict lacked those
keys → 500 blank page. Fixed with (s.tropes or [])|join(', ').
2. web/routes/project.py (project_setup_wizard): Removed silent redirect-to-
dashboard when model_logic is None. Now renders the setup form with a
complete default suggestions dict (all fields present, lists as []) plus a
clear warning flash so the user can fill it in manually.
3. web/routes/project.py (create_project_final): planner.enrich() was called
with the full bible dict — enrich() reads manual_instruction from the top
level (got 'A generic story' fallback) and wrote results into book_metadata
instead of the bible's books[0]. Fixed to build a proper per-book blueprint,
call enrich, and merge characters/plot_beats back into the correct locations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -121,12 +121,12 @@
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Tropes (comma separated)</label>
|
||||
<input type="text" name="tropes" class="form-control" value="{{ s.tropes|join(', ') }}">
|
||||
<input type="text" name="tropes" class="form-control" value="{{ (s.tropes or [])|join(', ') }}">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Formatting Rules (comma separated)</label>
|
||||
<input type="text" name="formatting_rules" class="form-control" value="{{ s.formatting_rules|join(', ') }}">
|
||||
<input type="text" name="formatting_rules" class="form-control" value="{{ (s.formatting_rules or [])|join(', ') }}">
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2">
|
||||
|
||||
@@ -30,10 +30,6 @@ def project_setup_wizard():
|
||||
try: ai_setup.init_models()
|
||||
except: pass
|
||||
|
||||
if not ai_models.model_logic:
|
||||
flash("AI models not initialized.")
|
||||
return redirect(url_for('project.index'))
|
||||
|
||||
prompt = f"""
|
||||
ROLE: Publishing Analyst
|
||||
TASK: Suggest metadata for a story concept.
|
||||
@@ -67,13 +63,46 @@ def project_setup_wizard():
|
||||
}}
|
||||
"""
|
||||
|
||||
_default_suggestions = {
|
||||
"title": concept[:60] if concept else "New Project",
|
||||
"genre": "Fiction",
|
||||
"target_audience": "",
|
||||
"tone": "",
|
||||
"length_category": "4",
|
||||
"estimated_chapters": 20,
|
||||
"estimated_word_count": "75,000",
|
||||
"include_prologue": False,
|
||||
"include_epilogue": False,
|
||||
"tropes": [],
|
||||
"pov_style": "",
|
||||
"time_period": "Modern",
|
||||
"spice": "",
|
||||
"violence": "",
|
||||
"is_series": False,
|
||||
"series_title": "",
|
||||
"narrative_tense": "",
|
||||
"language_style": "",
|
||||
"dialogue_style": "",
|
||||
"page_orientation": "Portrait",
|
||||
"formatting_rules": [],
|
||||
"author_bio": ""
|
||||
}
|
||||
|
||||
suggestions = {}
|
||||
try:
|
||||
response = ai_models.model_logic.generate_content(prompt)
|
||||
suggestions = json.loads(utils.clean_json(response.text))
|
||||
except Exception as e:
|
||||
flash(f"AI Analysis failed: {e}")
|
||||
suggestions = {"title": "New Project", "genre": "Fiction"}
|
||||
if not ai_models.model_logic:
|
||||
flash("AI models not initialized — fill in the details manually.", "warning")
|
||||
suggestions = _default_suggestions
|
||||
else:
|
||||
try:
|
||||
response = ai_models.model_logic.generate_content(prompt)
|
||||
suggestions = json.loads(utils.clean_json(response.text))
|
||||
# Ensure list fields are always lists
|
||||
for list_field in ("tropes", "formatting_rules"):
|
||||
if not isinstance(suggestions.get(list_field), list):
|
||||
suggestions[list_field] = []
|
||||
except Exception as e:
|
||||
flash(f"AI Analysis failed — fill in the details manually. ({e})", "warning")
|
||||
suggestions = _default_suggestions
|
||||
|
||||
personas = {}
|
||||
if os.path.exists(config.PERSONAS_FILE):
|
||||
@@ -201,8 +230,33 @@ def create_project_final():
|
||||
|
||||
try:
|
||||
ai_setup.init_models()
|
||||
bible = planner.enrich(bible, proj_path)
|
||||
except: pass
|
||||
# Build a per-book blueprint matching what enrich() expects
|
||||
first_book = bible['books'][0] if bible.get('books') else {}
|
||||
bp = {
|
||||
'manual_instruction': first_book.get('manual_instruction', concept),
|
||||
'book_metadata': {
|
||||
'title': bible['project_metadata']['title'],
|
||||
'genre': bible['project_metadata']['genre'],
|
||||
'style': dict(bible['project_metadata'].get('style', {})),
|
||||
},
|
||||
'length_settings': dict(bible['project_metadata'].get('length_settings', {})),
|
||||
'characters': [],
|
||||
'plot_beats': [],
|
||||
}
|
||||
bp = planner.enrich(bp, proj_path)
|
||||
# Merge enriched characters and plot_beats back into the bible
|
||||
if bp.get('characters'):
|
||||
bible['characters'] = bp['characters']
|
||||
if bp.get('plot_beats') and bible.get('books'):
|
||||
bible['books'][0]['plot_beats'] = bp['plot_beats']
|
||||
# Merge enriched style fields back (structure_prompt, content_warnings)
|
||||
bm = bp.get('book_metadata', {})
|
||||
if bm.get('structure_prompt') and bible.get('books'):
|
||||
bible['books'][0]['structure_prompt'] = bm['structure_prompt']
|
||||
if bm.get('content_warnings'):
|
||||
bible['project_metadata']['content_warnings'] = bm['content_warnings']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with open(os.path.join(proj_path, "bible.json"), 'w') as f:
|
||||
json.dump(bible, f, indent=2)
|
||||
|
||||
Reference in New Issue
Block a user