Skip to content

Commit 2ad7b7f

Browse files
dmikushinclaude
andcommitted
feat: add greenlet dep, incremental SQLite saves during Phase 3 generation
Add greenlet>=3.0 to pyproject.toml to fix SQLAlchemy async engine on Python 3.11 (greenlet._greenlet C extension was missing for 3.11). Add incremental per-page SQLite persistence during Phase 3 so that a crash in Phase 4 (persist step) does not lose all generated content: - page_generator.generate_all(): accept optional async on_page_saved callback, called immediately after each page is generated - orchestrator.run_generation(): accept page_persister and wire it through to generate_all(on_page_saved=page_persister) - init_cmd._page_persister(): async function that upserts each page into SQLite via the session factory already created for the CostTracker; errors are logged but do not abort generation The final batch persist (Phase 4) is idempotent (upsert), so pages saved incrementally are simply no-ops on the second write. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a9295df commit 2ad7b7f

4 files changed

Lines changed: 26 additions & 0 deletions

File tree

packages/cli/src/repowise/cli/commands/init_cmd.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,18 @@ async def _make_cost_tracker() -> CostTracker:
578578
# accept _cost_tracker as an attribute)
579579
provider._cost_tracker = cost_tracker
580580

581+
# Persist each page immediately after generation so a crash in
582+
# Phase 4 does not lose all generated content.
583+
_incremental_sf = getattr(cost_tracker, "_session_factory", None)
584+
_incremental_repo_id = getattr(cost_tracker, "_repo_id", None)
585+
586+
async def _page_persister(page: Any) -> None:
587+
if _incremental_sf is None or _incremental_repo_id is None:
588+
return
589+
from repowise.core.persistence import get_session, upsert_page_from_generated
590+
async with get_session(_incremental_sf) as _sess:
591+
await upsert_page_from_generated(_sess, page, _incremental_repo_id)
592+
581593
generated_pages = run_async(
582594
run_generation(
583595
repo_path=repo_path,
@@ -593,6 +605,7 @@ async def _make_cost_tracker() -> CostTracker:
593605
progress=gen_callback,
594606
resume=resume,
595607
cost_tracker=cost_tracker,
608+
page_persister=_page_persister,
596609
)
597610
)
598611

packages/core/src/repowise/core/generation/page_generator.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ async def generate_all(
409409
repo_name: str,
410410
job_system: Any | None = None, # JobSystem | None
411411
on_page_done: Callable[[str], None] | None = None,
412+
on_page_saved: Callable[[Any], Any] | None = None,
412413
on_total_known: Callable[[int], None] | None = None,
413414
git_meta_map: dict[str, dict] | None = None,
414415
resume: bool = False,
@@ -503,6 +504,15 @@ async def guarded_named(page_id: str, coro: Any) -> Any:
503504
# Report progress immediately (not batched after gather)
504505
if on_page_done is not None:
505506
on_page_done(result.page_type)
507+
if on_page_saved is not None:
508+
try:
509+
await on_page_saved(result)
510+
except Exception as _save_err:
511+
log.error(
512+
"page_incremental_save_failed",
513+
page_id=result.page_id,
514+
error=str(_save_err),
515+
)
506516
return result
507517
except Exception as exc:
508518
if job_system is not None and job_id is not None:

packages/core/src/repowise/core/pipeline/orchestrator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ async def run_generation(
641641
progress: ProgressCallback | None,
642642
resume: bool = False,
643643
cost_tracker: Any | None = None,
644+
page_persister: Any | None = None,
644645
) -> list[Any]:
645646
"""Run LLM-powered page generation.
646647
@@ -704,6 +705,7 @@ def on_total_known(total: int) -> None:
704705
repo_name,
705706
job_system=job_system,
706707
on_page_done=on_page_done,
708+
on_page_saved=page_persister,
707709
on_total_known=on_total_known,
708710
git_meta_map=git_meta_map if git_meta_map else None,
709711
resume=resume,

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dependencies = [
5454
# Database (async)
5555
"sqlalchemy[asyncio]>=2.0,<3",
5656
"aiosqlite>=0.20,<1",
57+
"greenlet>=3.0",
5758
"alembic>=1.13,<2",
5859
# Data validation
5960
"pydantic>=2.8,<3",

0 commit comments

Comments
 (0)