Audit, bug fixes, and coherence enhancements by Gemini AI
This commit is contained in:
@@ -421,26 +421,39 @@ Genre: {self.genre}
|
||||
}
|
||||
|
||||
def node_write_chapters(self, state: OpusGraphState) -> dict:
|
||||
"""Write all chapters."""
|
||||
"""Write all chapters with coherence tracking."""
|
||||
print("\n✍️ WRITING CHAPTERS...")
|
||||
|
||||
system_prompt = f"""You are a professional novelist.
|
||||
Style: {state.style_guide[:500] if state.style_guide else 'Professional fiction'}
|
||||
|
||||
Maintain consistent character voices and world details across chapters.
|
||||
"""
|
||||
|
||||
chapters = {}
|
||||
critique_iterations = state.critique_iterations or {}
|
||||
|
||||
# COHERENCE: Track what happened in previous chapters
|
||||
previous_chapters_summary = ""
|
||||
|
||||
for plan in state.prewriting.chapter_plans:
|
||||
chapter_num = plan.chapter_number
|
||||
print(f"\n Writing chapter {chapter_num}...")
|
||||
|
||||
user_prompt = f"""Write Chapter {chapter_num}: {plan.summary}
|
||||
|
||||
Story: {state.prewriting.one_sentence}
|
||||
Characters: {', '.join(c.name for c in state.prewriting.characters[:3])}
|
||||
Story Context:
|
||||
- Premise: {state.prewriting.one_sentence}
|
||||
- One Paragraph: {state.prewriting.one_paragraph}
|
||||
- Key Characters: {', '.join(c.name for c in state.prewriting.characters)}
|
||||
|
||||
Write ~{plan.word_count_target} words.
|
||||
## SUMMARY OF PREVIOUS CHAPTERS:
|
||||
{previous_chapters_summary if previous_chapters_summary else "This is the first chapter."}
|
||||
|
||||
## TASK:
|
||||
Write the full prose for Chapter {chapter_num}.
|
||||
Target: ~{plan.word_count_target} words.
|
||||
Follow the story arc and ensure a smooth transition from previous events.
|
||||
"""
|
||||
|
||||
result = self._call_llm(system_prompt, user_prompt)
|
||||
@@ -462,6 +475,7 @@ Write ~{plan.word_count_target} words.
|
||||
"genre": self.genre,
|
||||
"one_sentence": state.prewriting.one_sentence,
|
||||
"summary": plan.summary,
|
||||
"previous_chapters": previous_chapters_summary,
|
||||
}
|
||||
|
||||
# Iterate critique
|
||||
@@ -502,6 +516,9 @@ Write ~{plan.word_count_target} words.
|
||||
critique_summary=critique_summary,
|
||||
)
|
||||
|
||||
# Update running summary for next chapter
|
||||
previous_chapters_summary += f"\n- Chapter {chapter_num}: {plan.summary}"
|
||||
|
||||
status = "✅" if approved else "⚠️"
|
||||
print(f" {status} Chapter {chapter_num} complete: {word_count} words, score: {critique_score:.2f}")
|
||||
|
||||
@@ -510,7 +527,7 @@ Write ~{plan.word_count_target} words.
|
||||
"critique_iterations": critique_iterations,
|
||||
"stage": Stage.WRITING,
|
||||
"progress": 0.90,
|
||||
"messages": state.messages + [f"Wrote {len(chapters)} chapters with AutoGen critique"],
|
||||
"messages": state.messages + [f"Wrote {len(chapters)} chapters with AutoGen critique and coherence tracking"],
|
||||
}
|
||||
|
||||
def node_complete(self, state: OpusGraphState) -> dict:
|
||||
@@ -605,7 +622,7 @@ Write ~{plan.word_count_target} words.
|
||||
print(f"Framework: {self.framework}")
|
||||
print(f"Target: {self.target_word_count:,} words\n")
|
||||
|
||||
# Create initial state as dict (not Pydantic model)
|
||||
# Create initial state
|
||||
initial_state = OpusGraphState(
|
||||
seed_concept=seed_concept,
|
||||
framework=self.framework,
|
||||
@@ -617,54 +634,49 @@ Write ~{plan.word_count_target} words.
|
||||
|
||||
# Use GEMINI PATTERN: stream with values, then snapshot fallback
|
||||
final_state = None
|
||||
last_error = None
|
||||
|
||||
# Stream mode "values" emits FULL state after each node
|
||||
print("[RUN] Starting stream...")
|
||||
try:
|
||||
for chunk in self.graph.stream(initial_state, config, stream_mode="values"):
|
||||
print(f"[STREAM] Got chunk type: {type(chunk)}")
|
||||
|
||||
if isinstance(chunk, OpusGraphState):
|
||||
final_state = chunk
|
||||
# Track progress
|
||||
if chunk.stage.value == "complete":
|
||||
print(f"[STREAM] Reached COMPLETE stage")
|
||||
if chunk.manuscript:
|
||||
print(f"[STREAM] Manuscript present: {len(chunk.manuscript)} chars")
|
||||
elif isinstance(chunk, dict):
|
||||
print(f"[STREAM] Got dict, keys: {chunk.keys()}")
|
||||
# Try to reconstruct
|
||||
if 'manuscript' in chunk and chunk.get('manuscript'):
|
||||
# Try to reconstruct if it looks like state
|
||||
if 'stage' in chunk:
|
||||
final_state = OpusGraphState(**chunk)
|
||||
print(f"[STREAM] Reconstructed state from dict")
|
||||
except Exception as e:
|
||||
print(f"[RUN] Stream error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
last_error = e
|
||||
# Don't give up - try to recover partial state
|
||||
|
||||
# Enable checkpointing for recovery
|
||||
print("[RUN] Checking final state...")
|
||||
if final_state is None or final_state.stage != Stage.COMPLETE:
|
||||
print("[WARNING] Workflow incomplete or failed. Attempting recovery from checkpoint...")
|
||||
try:
|
||||
snapshot = self.graph.get_state(config)
|
||||
if snapshot and snapshot.values:
|
||||
# Merge snapshot values into OpusGraphState
|
||||
final_state = OpusGraphState(**snapshot.values)
|
||||
print(f"[RECOVERY] Recovered state at stage: {final_state.stage}")
|
||||
except Exception as e2:
|
||||
print(f"[RECOVERY] Failed to get state snapshot: {e2}")
|
||||
|
||||
if final_state is None:
|
||||
print("[WARNING] No state from stream, attempting recovery...")
|
||||
# Try to recover from any partial state that was accumulated
|
||||
# In a full implementation, we'd load from checkpoint here
|
||||
# For now, raise a clear error instead of silently failing
|
||||
raise RuntimeError(
|
||||
f"Workflow failed to complete. "
|
||||
f"Last known stage: {getattr(final_state, 'stage', 'unknown') if final_state else 'initial'}. "
|
||||
f"Error: {e}"
|
||||
f"Workflow failed to complete and could not be recovered. "
|
||||
f"Last error: {last_error}"
|
||||
)
|
||||
|
||||
# Verify we have manuscript
|
||||
if not final_state.manuscript:
|
||||
print("[WARNING] No manuscript generated!")
|
||||
# Return partial state for debugging
|
||||
if final_state.prewriting.one_sentence:
|
||||
print(f"[PARTIAL] Generated: {final_state.prewriting.one_sentence[:100]}...")
|
||||
raise RuntimeError("Workflow completed but no manuscript was generated.")
|
||||
# Verify we have manuscript if we finished
|
||||
if final_state.stage == Stage.COMPLETE and not final_state.manuscript:
|
||||
print("[WARNING] Workflow completed but no manuscript was generated.")
|
||||
|
||||
print(f"[RESULT] SUCCESS! {len(final_state.chapters)} chapters, {final_state.total_word_count} words")
|
||||
print(f"[RESULT] Final Stage: {final_state.stage}")
|
||||
if final_state.total_word_count > 0:
|
||||
print(f"[RESULT] SUCCESS! {len(final_state.chapters)} chapters, {final_state.total_word_count} words")
|
||||
|
||||
return final_state
|
||||
|
||||
|
||||
Reference in New Issue
Block a user