fix: Pipeline hardening — error handling, token efficiency, and robustness

core/utils.py:
- estimate_tokens: improved heuristic 4 chars/token → 3.5 chars/token (more accurate)
- truncate_to_tokens: added keep_head=True mode for head+tail truncation (better
  context retention for story summaries that need both opening and recent content)
- load_json: explicit exception handling (json.JSONDecodeError, OSError) with log
  instead of silent returns; added utf-8 encoding with error replacement
- log_image_attempt: replaced bare except with (json.JSONDecodeError, OSError);
  added utf-8 encoding to output write
- log_usage: replaced bare except with AttributeError for token count extraction

story/bible_tracker.py:
- merge_selected_changes: wrapped all int() key casts (char idx, book num, beat idx)
  in try/except with meaningful log warning instead of crashing on malformed keys
- harvest_metadata: replaced bare except:pass with except Exception as e + log message

cli/engine.py:
- Persona validation: added warning when all 3 attempts fail and substandard persona
  is accepted — flags elevated voice-drift risk for the run
- Lore index updates: throttled from every chapter to every 3 chapters; lore is
  stable after the first few chapters (~10% token saving per book)
- Mid-gen consistency check: now samples first 2 + last 8 chapters instead of passing
  full manuscript — caps token cost regardless of book length

story/writer.py:
- Two-pass polish: added local filter-word density check (no API call); skips the
  Pro polish if density < 1 per 83 words — saves ~8K tokens on already-clean drafts
- Polish prompt: added prev_context_block for continuity — polished chapter now
  maintains seamless flow from the previous chapter's ending

marketing/fonts.py:
- Separated requests.exceptions.Timeout with specific log message vs generic failure
- Added explicit log message when Roboto fallback also fails (returns None)

marketing/blurb.py:
- Added word count trim: blurbs > 220 words trimmed to last sentence within 220 words
- Changed bare except to except Exception as e with log message
- Added utf-8 encoding to file writes; logs final word count

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 22:31:22 -05:00
parent 3a42d1a339
commit ff5093a5f9
6 changed files with 106 additions and 36 deletions

View File

@@ -19,7 +19,11 @@ def merge_selected_changes(original, draft, selected_keys):
original['project_metadata'][field] = draft['project_metadata'][field]
elif parts[0] == 'char' and len(parts) >= 2:
idx = int(parts[1])
try:
idx = int(parts[1])
except (ValueError, IndexError):
utils.log("SYSTEM", f"⚠️ Skipping malformed bible merge key: '{key}'")
continue
if idx < len(draft['characters']):
if idx < len(original['characters']):
original['characters'][idx] = draft['characters'][idx]
@@ -27,7 +31,11 @@ def merge_selected_changes(original, draft, selected_keys):
original['characters'].append(draft['characters'][idx])
elif parts[0] == 'book' and len(parts) >= 2:
book_num = int(parts[1])
try:
book_num = int(parts[1])
except (ValueError, IndexError):
utils.log("SYSTEM", f"⚠️ Skipping malformed bible merge key: '{key}'")
continue
orig_book = next((b for b in original['books'] if b['book_number'] == book_num), None)
draft_book = next((b for b in draft['books'] if b['book_number'] == book_num), None)
@@ -42,7 +50,11 @@ def merge_selected_changes(original, draft, selected_keys):
orig_book['manual_instruction'] = draft_book['manual_instruction']
elif len(parts) == 4 and parts[2] == 'beat':
beat_idx = int(parts[3])
try:
beat_idx = int(parts[3])
except (ValueError, IndexError):
utils.log("SYSTEM", f"⚠️ Skipping malformed beat merge key: '{key}'")
continue
if beat_idx < len(draft_book['plot_beats']):
while len(orig_book['plot_beats']) <= beat_idx:
orig_book['plot_beats'].append("")
@@ -153,7 +165,8 @@ def harvest_metadata(bp, folder, full_manuscript):
if valid_chars:
utils.log("HARVESTER", f"Found {len(valid_chars)} new chars.")
bp['characters'].extend(valid_chars)
except: pass
except Exception as e:
utils.log("HARVESTER", f"⚠️ Metadata harvest failed: {e}")
return bp