From 1cd62a75c93d17a0670487874eabc9f4b8ad840f Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 4 Feb 2026 22:57:38 -0500 Subject: [PATCH] Flow improvements. --- modules/story.py | 18 ++++++++++++------ modules/web_app.py | 8 +++++++- templates/project.html | 18 +++++++++++++++--- templates/run_details.html | 13 ++++++++++++- 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/modules/story.py b/modules/story.py index 99f4060..01f4084 100644 --- a/modules/story.py +++ b/modules/story.py @@ -373,7 +373,7 @@ def update_tracking(folder, chapter_num, chapter_text, current_tracking): utils.log("TRACKER", f"Failed to update tracking: {e}") return current_tracking -def evaluate_chapter_quality(text, chapter_title, model, folder): +def evaluate_chapter_quality(text, chapter_title, genre, model, folder): guidelines = get_style_guidelines() ai_isms = "', '".join(guidelines['ai_isms']) fw_examples = ", ".join([f"'He {w}'" for w in guidelines['filter_words'][:5]]) @@ -381,9 +381,10 @@ def evaluate_chapter_quality(text, chapter_title, model, folder): prompt = f""" Act as a World-Class Literary Editor (e.g., Maxwell Perkins). Analyze this chapter draft with extreme scrutiny. CHAPTER TITLE: {chapter_title} + GENRE: {genre} STRICT PROHIBITIONS (Automatic deduction): - - "AI-isms": '{ai_isms}'. + - "AI-isms": '{ai_isms}'. (Evaluate in context of {genre}. Allow genre-appropriate tropes, but penalize robotic clichés). - Filter Words: {fw_examples}, etc. (Show the sensation/action, don't state the internal process). - Stilted Dialogue: Characters speaking in perfect paragraphs without interruptions, slang, or subtext. - White Room Syndrome: Dialogue occurring in a void without interaction with the setting/props. @@ -532,6 +533,7 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None): ls = bp['length_settings'] meta = bp.get('book_metadata', {}) style = meta.get('style', {}) + genre = meta.get('genre', 'Fiction') pov_char = chap.get('pov_character', '') @@ -593,6 +595,7 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None): prompt = f""" Write Chapter {chap['chapter_number']}: {chap['title']} + GENRE: {genre} PACING GUIDE: - Format: {ls.get('label', 'Story')} @@ -651,7 +654,7 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None): for attempt in range(1, max_attempts + 1): utils.log("WRITER", f" -> Evaluating Ch {chap['chapter_number']} (Attempt {attempt}/{max_attempts})...") - score, critique = evaluate_chapter_quality(current_text, chap['title'], ai.model_logic, folder) + score, critique = evaluate_chapter_quality(current_text, chap['title'], meta.get('genre', 'Fiction'), ai.model_logic, folder) past_critiques.append(f"Attempt {attempt}: {critique}") @@ -686,7 +689,7 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None): history_str = "\n".join(past_critiques) refine_prompt = f""" - Act as a Senior Editor. Rewrite this chapter to fix the issues identified below. + Act as a Senior Editor. Rewrite this chapter to fix the issues identified below and ELEVATE the writing quality. CRITIQUE TO ADDRESS (MANDATORY): {critique} @@ -701,6 +704,8 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None): 4. GENRE CONSISTENCY: Ensure the tone matches {meta.get('genre', 'Fiction')}. 5. SETTING INTERACTION: Ensure characters interact with their environment (props, weather, lighting) during dialogue. 6. DRAMATIZE, DON'T SUMMARIZE: Expand summarized moments into full scenes with dialogue and action. Ensure the scene feels "full" and immersive. + 7. STRONG VERBS: Avoid "was/were" constructions. Use active, specific verbs to drive the prose. + 8. EMOTIONAL RESONANCE: Ensure the POV character's internal state is clear and drives the narrative. STORY SO FAR: {prev_sum} @@ -712,8 +717,9 @@ def write_chapter(chap, bp, folder, prev_sum, tracking=None, prev_content=None): Return the polished, final version of the chapter in Markdown. """ try: - resp_refine = ai.model_writer.generate_content(refine_prompt) - utils.log_usage(folder, "writer-flash", resp_refine.usage_metadata) + # Use Logic model (Pro) for refinement to ensure higher quality prose + resp_refine = ai.model_logic.generate_content(refine_prompt) + utils.log_usage(folder, "logic-pro", resp_refine.usage_metadata) current_text = resp_refine.text except Exception as e: utils.log("WRITER", f"Refinement failed: {e}") diff --git a/modules/web_app.py b/modules/web_app.py index 46d5a18..0bee9ad 100644 --- a/modules/web_app.py +++ b/modules/web_app.py @@ -977,7 +977,13 @@ def run_status(id): if os.path.exists(temp_log): with open(temp_log, 'r') as f: log_content = f.read() - response = {"status": run.status, "log": log_content, "cost": run.cost, "percent": run.progress} + response = { + "status": run.status, + "log": log_content, + "cost": run.cost, + "percent": run.progress, + "start_time": run.start_time.timestamp() if run.start_time else None + } if last_log: response["progress"] = { diff --git a/templates/project.html b/templates/project.html index 446470b..4fa1d61 100644 --- a/templates/project.html +++ b/templates/project.html @@ -109,7 +109,7 @@ Initializing...
Preparing environment...
-
+
@@ -164,8 +164,8 @@ ${{ "%.4f"|format(r.cost) }} - - {{ 'View Active' if active_run and r.id == active_run.id else 'View' }} + + {{ 'View Active' if active_run and r.id == active_run.id and active_run.status in ['running', 'queued'] else 'View' }} {% if r.status in ['failed', 'cancelled', 'interrupted'] %}
@@ -562,6 +562,18 @@ const progBar = document.getElementById('progressBar'); if (progBar && data.percent !== undefined) { progBar.style.width = data.percent + "%"; + + let label = data.percent + "%"; + if (data.status === 'running' && data.percent > 2 && data.start_time) { + const elapsed = (Date.now() / 1000) - data.start_time; + if (elapsed > 5) { + const remaining = (elapsed / (data.percent / 100)) - elapsed; + const m = Math.floor(remaining / 60); + const s = Math.floor(remaining % 60); + if (remaining > 0 && remaining < 86400) label += ` (~${m}m ${s}s)`; + } + } + progBar.innerText = label; } // Update Status Bar diff --git a/templates/run_details.html b/templates/run_details.html index bb9cd39..a6e3852 100644 --- a/templates/run_details.html +++ b/templates/run_details.html @@ -324,7 +324,18 @@ if (data.status === 'running' || data.status === 'queued') { statusBar.className = "progress-bar progress-bar-striped progress-bar-animated"; statusBar.style.width = (data.percent || 5) + "%"; - statusBar.innerText = (data.percent || 0) + "%"; + + let label = (data.percent || 0) + "%"; + if (data.status === 'running' && data.percent > 2 && data.start_time) { + const elapsed = (Date.now() / 1000) - data.start_time; + if (elapsed > 5) { + const remaining = (elapsed / (data.percent / 100)) - elapsed; + const m = Math.floor(remaining / 60); + const s = Math.floor(remaining % 60); + if (remaining > 0 && remaining < 86400) label += ` (~${m}m ${s}s)`; + } + } + statusBar.innerText = label; } else if (data.status === 'failed') { statusBar.className = "progress-bar bg-danger"; statusBar.style.width = "100%";