Blueprint v2.4-2.6: Style Rules UI, Lore RAG, Thread Tracking, Redo Book

v2.4 — Item 7: Refresh Style Guidelines
- web/routes/admin.py: Added /admin/refresh-style-guidelines route (AJAX-aware)
- templates/system_status.html: Added 'Refresh Style Rules' button with spinner

v2.5 — Item 8: Lore & Location RAG-Lite
- story/bible_tracker.py: Added update_lore_index() — extracts location/item
  descriptions from chapters into tracking_lore.json
- story/writer.py: Reads chapter locations/key_items, builds LORE_CONTEXT block
  injected into the prompt (graceful degradation if no tags)
- cli/engine.py: Loads tracking_lore.json on resume, calls update_lore_index
  after each chapter, saves tracking_lore.json

v2.5 — Item 9: Structured Story State (Thread Tracking)
- story/state.py (new): load_story_state, update_story_state (extracts
  active_threads, immediate_handoff, resolved_threads via model_logic),
  format_for_prompt (structured context replacing the prev_sum blob)
- cli/engine.py: Loads story_state.json on resume, uses format_for_prompt as
  summary_ctx for write_chapter, updates state after each chapter accepted

v2.6 — Item 10: Redo Book
- templates/consistency_report.html: Added 'Redo Book' form with instruction
  input and confirmation dialog
- web/routes/run.py: Added revise_book route — creates new Run, queues
  generate_book_task with user instruction as feedback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 01:35:43 -05:00
parent 2db7a35a66
commit 83a6a4315b
9 changed files with 291 additions and 27 deletions

View File

@@ -1,6 +1,7 @@
import os
import json
import markdown
from datetime import datetime
from flask import Blueprint, render_template, request, redirect, url_for, flash, session, send_from_directory
from flask_login import login_required, current_user
from web.db import db, Run, LogEntry
@@ -315,6 +316,39 @@ def get_task_status(task_id):
return {"status": "completed", "success": task_result}
@run_bp.route('/project/<int:run_id>/revise_book/<string:book_folder>', methods=['POST'])
@login_required
def revise_book(run_id, book_folder):
run = db.session.get(Run, run_id) or Run.query.get_or_404(run_id)
if run.project.user_id != current_user.id:
flash("Unauthorized.")
return redirect(url_for('run.view_run', id=run_id))
if run.status == 'running':
flash("A run is already active. Please wait for it to finish.")
return redirect(url_for('run.view_run', id=run_id))
instruction = request.form.get('instruction', '').strip()
if not instruction:
flash("Please provide an instruction describing what to fix.")
return redirect(url_for('run.check_consistency', run_id=run_id, book_folder=book_folder))
bible_path = os.path.join(run.project.folder_path, "bible.json")
if not os.path.exists(bible_path):
flash("Bible file not found. Cannot start revision.")
return redirect(url_for('run.view_run', id=run_id))
new_run = Run(project_id=run.project_id, status='queued', start_time=datetime.utcnow())
db.session.add(new_run)
db.session.commit()
from web.tasks import generate_book_task
generate_book_task(new_run.id, run.project.folder_path, bible_path, feedback=instruction, source_run_id=run.id)
flash(f"Book revision queued. Instruction: '{instruction[:80]}...' — a new run has been started.")
return redirect(url_for('run.view_run', id=new_run.id))
@run_bp.route('/project/<int:run_id>/regenerate_artifacts', methods=['POST'])
@login_required
def regenerate_artifacts(run_id):