Skip to content
phaethixPublic

Latest commit

 

History

491 Commits

Folders and files

Repository files navigation

Inkstone — novel to series comic generator

Release License CI Python 3.10+ PRs welcome

"Grind your novel into comics."

Inkstone reads a local txt novel and, through the free Agnes multimodal API, produces comic pages with cross-panel character consistency — exported as PDF / CBZ / PNG. No GPU, no paid plan: just one free API key.


Gallery — see what Inkstone makes

Generated comic panel 1 Generated comic panel 4 Generated comic panel 7

These panels were generated from the bundled sample (examples/scene1.txt) with the default L1+L2 pipeline — no L3, straight from the model. Consistency here is "medium" by design: the honest ceiling of a free, no-GPU model. More samples, including the full webtoon strip, are at:

phaethix.github.io/inkstone

What Inkstone does

  • Free & local-first — one AGNES_API_KEY, no GPU, no paid plan.
  • Agnes-native multimodal — agnes-3.0-flash for scripting, agnes-image-2.5-flash for t2i and i2i consistency.
  • Pluggable ImageProvider — Agnes by default; PROVIDER=openai_compat for Agnes-protocol-compatible /images/generations gateways (i2i uses extra_body.image).
  • Character consistency engine — L1 prompt hard-description + L2 reference img2img, the robust path under no-GPU. An optional L3 PIL/OpenCV face overlay is off by default (opt-in via INKSTONE_L3=1).
  • Reliability layer — token-bucket rate limiting, exponential-backoff retries, and error collection against 429/503.
  • Resumable long-form runs — chapter-split generation with a persisted state.json checkpoint.

Why Inkstone

Every novel-to-comic tool we surveyed in 2026 runs on a paid model — Gemini, OpenAI, Doubao, Wenxin, or Claude. Inkstone is built differently: it is Agnes-native and zero-cost, the one combination no other open-source generator occupies.

Agnes-native Zero-cost (no GPU / no paid key)
Other open-source comic generators — — (all paid APIs)
Inkstone ✓ ✓

Inkstone is an independent implementation, not a fork — inspired by lcy362/agnes-video-generator (MIT) but carrying none of its source tree. Attribution is recorded in NOTICE.

An honest trade-off. Free, cloud-only Agnes with no GPU caps how far character consistency can reach. The strongest approaches (IP-Adapter / InsightFace) need a local GPU running SDXL/Flux — incompatible with Inkstone's zero-cost premise. So Inkstone trades perfect consistency for zero-cost + no-GPU + out-of-the-box, using L1+L2. L3 is opt-in and off by default. Stated plainly, not hidden.

Panel-compose mode (default) plans comic pages, generates each panel as its own image, and composes pages locally with the LayoutEngine; caption/dialogue/sfx are overlaid with real fonts (CJK-capable), never model-painted. Set INKSTONE_RENDER_MODE=finished_page for the whole-page fallback (one image per page, empty lettering chrome overlaid the same way); set INKSTONE_RENDER_MODE=storyboard for the legacy per-panel grid path. Unlettered art is cached under pages/blank/ so resume can re-letter without another image API call.

How it works

Pipeline: Split → Extract → Board → Paint → Export

A txt novel is split into segments → characters & scenes are extracted with agnes-3.0-flash → panel-compose mode (default) plans one comic page layout per chunk, paints each panel as its own image, and composes the page locally → Inkstone overlays text with real fonts → pages are bound to PDF, zipped as CBZ, or stacked into a webtoon PNG. Set INKSTONE_RENDER_MODE=finished_page for the whole-page fallback (one image per page). The legacy path (INKSTONE_RENDER_MODE=storyboard) runs storyboard prompts → per-panel generation → LayoutEngine grid layout → export.

The core challenge — cross-panel character consistency without a GPU — is handled by a layered strategy:

Layer Strategy Default
L1 Appearance-derived prompt hard-description ✅ on
L2 Reference img2img from the character portrait ✅ on
L3 PIL/OpenCV face overlay ❌ off (opt-in)

Backed by a reliability layer (rate limiting, retries, and state.json resumption). Alias variants are flagged for human merge/dismiss (never silent); merges mark affected panels stale for selective redraw.

Quick Start

Prerequisites: Python 3.10+, uv, and a free Agnes API key (Free Access tier).

# 1. Install uv (https://docs.astral.sh/uv/getting-started/installation/)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Install Inkstone (runtime + dev/test tooling; creates .venv automatically)
uv sync --extra dev

# 3. Provide your API key
cp .env.example .env
#    then open .env and set AGNES_API_KEY=sk-xxx

Generate your first panel:

$ uv run python examples/first_panel.py   # single-panel probe, run from the repo root
saved -> panel.png

Verify the install — the test suite runs fully offline:

$ uv run pytest
# validates providers, schemas, layout/export, consistency, screenwriter,
# pipeline resume and content-safety behavior without real API calls

Running Inkstone

Every way to run Inkstone goes through one command: uv run inkstone <command> [flags]. There is no separate server script to launch; the CLI loads the repo-root .env automatically, so once step 3 above is done you never need to export a key again.

For the complete usage guide — quick-start examples, every flag (--book, --out / --project, --fresh, --format, --density, --budget, the model overrides), the command list, and colors — run:

uv run inkstone --help

Run uv run inkstone <command> --help for the full flag list of any single command.

Web UI

uv run inkstone web starts the zero-dependency local UI (Tailwind SPA + an http.server backend — no new pip package) around the same pipeline. Paste a novel and hit Generate; artifacts land under comic_out/<project_id>/.

The same web/index.html is also deployed to GitHub Pages: locally it generates your comic, on the static site it runs in demo mode with the identical interface.

Status: M1–M4 (provider foundation, comic pipeline, long-form hardening and open-source release) have shipped. See the Roadmap for released capability, local prototypes and the approved long-form plan.

inkstone coverage reads legacy PageScript fields and is not a quality gate. Those fields are written only when INKSTONE_PAGE_SCRIPT=1. inkstone structure checks stored source units, beats, lettering, causal order, and blocked chunks. A check with nothing to read is absent, not a pass. A chapter is validated only when every one of those checks passes and a page is stored.

The long-form sample is the public-domain excerpt examples/showcase/journey-west-ch1/source.txt. inkstone plan on that file is an estimate. After pages are stored under comic_out/journey-west-ch1, inkstone structure --out comic_out/journey-west-ch1 is the check. Lettering is kept only when the whole string is a contiguous span of the source. The page images and PDF stay in comic_out/ and are not part of the repository. A local run of that excerpt passes the five checks and has 8 pages.

Harness A/B

A harness is the editable prompt tree under core/harness/data/ (system prompt, six stage templates, genre packs). The planning-only A/B command compares a baseline with a candidate tree without spending image calls:

inkstone harness validate --dir path/to/candidate
inkstone experiment plan \
  --book examples/showcase/journey-west-ch1/source.txt \
  --chunk 0 \
  --candidate base \
  --candidate path/to/candidate \
  --out experiments/wuxia-a
inkstone experiment pick --run experiments/wuxia-a --choice A --why "clearer staging"
inkstone experiment compose --run experiments/wuxia-a --choice A --out harness/wuxia-a
inkstone generate examples/scene1.txt --out comic_out/wuxia --harness-dir harness/wuxia-a

The experiment writes an anonymised candidates/A|B/ packet and a selection.json. --candidate can be repeated; base means the builtin tree. By default each candidate runs extract + key beats + page plan (no image calls); pass --shared-evidence for the cheaper page_plan-only probe. compose copies the picked tree, optionally merging named components from another candidate (--merge-from B --component stage.page_plan), validates it, and never overwrites the builtin tree. generate --harness-dir (or INKSTONE_HARNESS_DIR) runs all six chat stages with that tree; the tree is validated before the first billable call and its digest is recorded in the review manifest. The image renderer and export keys are unchanged. Set INKSTONE_HARNESS=0 to force the builtin tree and general genre and disable craft lessons for one release.

The local Web UI has an A/B tab for the same experiments: it lists the packets under INKSTONE_EXPERIMENTS_DIR (default comic_out/experiments), shows both plans and structure reports with the labels hidden, records your pick plus a required why, and can compose the picked harness into <experiment>/adopted/ (optionally merging stage.page_plan from the other candidate). The server fixes that output path; the browser never chooses it.

To refine across several rounds, use sequential recursion:

inkstone experiment sequence plan \
  --book examples/showcase/journey-west-ch1/source.txt \
  --out experiments/sequence \
  --candidate path/to/candidate --rounds 2
inkstone experiment pick --run experiments/sequence/rounds/1 --choice A --why "..."
inkstone experiment sequence advance --run experiments/sequence
inkstone experiment sequence status --run experiments/sequence

Each round probes the current checkpoint plus the same candidates; advance composes the pick into rounds/<n>/checkpoint and plans the next round. The last advance marks the sequence done.

The Web A/B tab lists sequences above standalone experiments. Opening one shows the current round, the same anonymous plan cards, and an Advance sequence action after a pick; the APIs are GET /api/sequences, GET /api/sequence/<id>, and POST /api/sequence/<id>/pick|advance.

Configuration

Inkstone is configured through environment variables (copy .env.example → .env). The CLI loads .env automatically, so no manual export is needed. The model variables below (AGNES_CHAT_MODEL, AGNES_IMAGE_I2I_MODEL, PROVIDER, AGNES_BASE_URL, and their OPENAI_COMPAT_* equivalents) can also be overridden per run with --chat-model / --image-model / --provider / --base-url on generate and web — run uv run inkstone --help for the flag guide.

Variable Required Default Description
AGNES_API_KEY ✅ — Free Access tier key; the only thing ordinary users need.
AGNES_BASE_URL https://apihub.agnes-ai.com/v1 Agnes API root (docs).
AGNES_CHAT_MODEL agnes-3.0-flash Text / tool-calling model (docs).
AGNES_RATE_LIMIT 20 Free-tier text + image-1K RPM ceiling (× 0.8 safety factor).
AGNES_IMAGE_2K_RPM 10 Free-tier image 2K RPM (max side ≤2048).
AGNES_IMAGE_3K_RPM 1 Free-tier image 3K/4K RPM.
AGNES_IMAGE_I2I_MODEL agnes-image-2.5-flash Model used for consistency img2img.
AGNES_IMAGE_MAX_RETRIES 5 Image calls retry this many times.
AGNES_IMAGE_RETRY_BASE_DELAY 5.0 Image retry backoff base in seconds.
PROVIDER agnes openai_compat routes to configured OpenAI-style base URLs.
OPENAI_COMPAT_* — Base URL / key / models when PROVIDER=openai_compat.
INKSTONE_L3 0 Enable the experimental L3 PIL/OpenCV face overlay (1 to turn on).
INKSTONE_GENRE general Domain notes for planning: general (none) or wuxia. The genre layer only adds craft guidance; it cannot override the invariant rules.
INKSTONE_REVIEW_PACKET 1 Write a local review/ evidence packet (effective prompts, source, plans, structure report, decisions). 0 disables it; it is never sent to a model.
INKSTONE_LESSONS 1 Inject active craft lessons from <INKSTONE_DATA_DIR>/library/ into page planning. 0 disables retrieval; approved lessons stay on disk.
INKSTONE_EXPERIMENTS_DIR comic_out/experiments Root the Web UI A/B tab lists. Set it to the parent of the --out directories you created.
INKSTONE_HARNESS_DIR — Run all chat stages with this harness tree (same layout as core/harness/data/). generate --harness-dir overrides it. The tree is validated at startup.
INKSTONE_HARNESS 1 One-release fallback. 0 forces the builtin tree, ignores the harness dir, forces general genre, and disables craft-lesson injection. The review manifest records harness_fallback: true.
INKSTONE_PAGE_SCRIPT 0 Write legacy PageScript metadata. Off. Not a quality gate.
INKSTONE_DATA_DIR ~/.inkstone Shared quota, budget, and sample-gate files.
INKSTONE_RENDER_MODE panel_compose Default: page-plan → per-panel images → local page compose, text overlaid with real fonts. finished_page is the whole-page fallback (one image per page). storyboard runs the legacy storyboard → panel → layout grid path.
INKSTONE_PANEL_SIZE 1024x1024 Single-panel image size in panel-compose mode.
INKSTONE_PAGE_SIZE 1024x1536 Finished-page image size. If the provider rejects the size, Inkstone retries once with 1024x1024.
INKSTONE_FONT_PATH (auto) TrueType/OpenType font for dialogue bubbles.
INKSTONE_WEBTOON_MAX_PIXELS 200000000 Refuse single-strip webtoon compose above this pixel budget. 0 disables.
INKSTONE_UI_HOST / INKSTONE_UI_PORT 127.0.0.1 / 8000 Web UI bind address. Values in .env apply at startup. A non-loopback host is refused unless INKSTONE_UI_ALLOW_LAN=1, INKSTONE_UI_TOKEN, and INKSTONE_UI_ORIGIN are all set.
INKSTONE_IMAGE_DOWNLOAD_HOSTS (any public https host) Optional comma-separated allowlist for image URLs returned by a provider. Private, link-local, and metadata addresses are always refused.

PDF export prefers the optional manga2pdf CLI for two-page / right-to-left manga layout (pip install manga2pdf to restore the gallery layout).

Resources

Contributing

See CONTRIBUTING.md. All code, docs, and commit messages are in English; commits follow Conventional Commits.


MIT License — LICENSE

Releases

Packages

Used by

Contributors

Languages