diff --git a/templates/project_setup.html b/templates/project_setup.html
index 092b991..9b7c07b 100644
--- a/templates/project_setup.html
+++ b/templates/project_setup.html
@@ -121,12 +121,12 @@
-
+
-
+
-
+
diff --git a/web/routes/project.py b/web/routes/project.py
index 9e20d57..c8a073d 100644
--- a/web/routes/project.py
+++ b/web/routes/project.py
@@ -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)