Sign inSign up

jncchds/abook

By jncchds

•Updated about 14 hours ago

Image
0

6.2K

jncchds/abook repository overview

⁠ABook — Agentic AI Book Writing

License: MIT Docker Hub .NET

A self-hosted web application that uses AI agents to collaboratively write books. Seven specialized agents — Story Bible, Characters, Plot Threads, Chapter Outlines, Writer, Checker, and Editor — work together under your direction, streaming their progress in real time and pausing to ask clarifying questions when needed.


⁠Features

⁠Writing pipeline
  • Seven specialized agents across two phases: a 4-phase Planner (Story Bible → Characters → Plot Threads → Chapter Outlines) and a per-chapter writing pipeline (Pre-write Check → Write → Check → Edit)
  • Checker → mechanical patch apply — the Checker flags continuity, grammar, repetition, and style issues as structured JSON patches; the Editor applies them mechanically (no LLM call) using indexed text matching with whitespace normalization and position hints
  • Full synopsis spine — every prior chapter's title and outline is injected into Writer and Editor messages so agents stay aware of the whole narrative and avoid recycling beats or re-introducing established characters
  • RAG context retrieval — Writer runs 3 targeted queries (characters, locations, plot threads) and Editor runs 4 (same + repeated-phrase detection) against pgvector embeddings
⁠Planning & guidance
  • Guided planning Q&A — before planning, the planner posts its clarifying questions to the book's Clarifications page and waits; answer each one there (or skip it), and submitting the last answer completes the round and planning carries on. Answers are kept as clarifications and added to the premise in every prompt, so nothing is lost if planning is interrupted, and the round isn't repeated once it's done. Edit, add, archive or restore them on the same page, or reopen the round to be asked again
  • Two ways to write a book — Interactive: write it in a chat with the LLM, in any order, approving every change; Autonomous: the LLM plans the whole book and writes it on its own
  • Book chat (Interactive books) — say what you want (a character, a plot thread, a chapter, a change); the LLM answers, flags conflicts with what the book already establishes, and proposes changes as cards with a diff — nothing changes until you Apply them, and every applied change can be Undone. No chapter count, no up-front outline: chapters and sections are added as you go, and each written chapter is summarized and checked, with the fixes proposed back in the chat. The premise and Story Bible are settled first and then locked while you write (only you can unlock them). Works on a phone; also available over MCP
  • Flexible workflow controls — Plan Book and Write Book (both pick up where the book left off), Illustrate Book, per-section runs on the Chapters page (▶ Write section writes a section's unfinished chapters the way Write Book does, breaking it down first when the planner expects several chapters; 🔍 Check checks each written chapter of it) and per-chapter runs on the chapter page (✍️ Write / Rewrite, 🖋 Edit prose, 🔍 Check); Stop cancels any running agent cleanly
  • Refresh-safe progress — reloading the page (or losing the connection) mid-run keeps the current step, the latest progress message and everything streamed so far; new text continues where it left off
  • Interrupted runs keep their output — if a Characters, Plot Threads, or Chapter Outlines run times out, drops, or is stopped part-way, everything that finished streaming is saved instead of discarded; run the phase again to pick up where it left off
  • Re-runs build on what exists — regenerating a planning phase sends the current characters, plot threads, or outlines back to the model to refine and extend, rather than starting from a blank page; archived items stay out of it and are never overwritten
⁠Content management
  • Story Bible, Characters & Plot Threads — generated by the Planner, fully editable, with per-item version history and snapshot restore
  • Inline editing — edit book metadata, chapter titles/outlines, and add chapters manually without leaving the detail page
  • Sections and chapter order — the plan is made of sections, each starting as one chapter. Insert anywhere (+ After adds a section, + Chapter here adds a chapter to the same section, + Insert at the start), move chapters and sections up and down, and ✂ Split a chapter that needs more room: divide its outline between it and a new chapter right after it in the same section, and both are written again (the old text stays in the version history). A section with several chapters keeps an overall plan of its own (✎ Plan), and the sidebar numbers its chapters "3.1", "3.2". Plan Book outlines sections (the target count is a number of sections) and notes which ones need more than one chapter; ⋔ Break down has the planner divide an unwritten section into chapters, and Autonomous runs do it on their own before writing such a section. The Writer gets the section's overall plan and its other chapters, so each chapter tells its own share. Numbers follow the order, and plot threads' and characters' references (which count sections) move with them. Archived chapters and sections keep their place — Show archived in place shows them between their neighbours, and restoring puts them back there. Turn on Show sections in the book (Book Settings) to give each section a part heading ("I. Title") in the downloads and the reader
  • Version history — chapters, characters, plot threads and clarifications all track history with preview and restore; soft-archive instead of delete. Every edit is kept as a version; a run of your own edits (web or Telegram) updates one version instead of piling up new ones, while any change by an agent or the book chat in between starts a new one
  • Archived means archived — an archived chapter, character, or plot thread is kept purely so you can look at it or restore it. It is never sent to a model, never counted in planning or continuity checks, never included in an HTML/FB2/EPUB export or on the public reader page, and agents refuse to write to it
  • Book continuation — create a sequel that copies all settings and inherits ancestor context; RAG and planning reference the full base-book chain
  • Rewind — bring the whole book and its chat back to an earlier moment (before a chat message, after a chapter version, before or after a run). Every part gets the state it had then as a new version, so nothing in between is lost; later chat messages are hidden — see Rewind⁠
⁠Illustrations
  • AI illustrations with Z-Image or Unsloth Studio — turn on Illustrate this book and the LLM plans illustrations for every finished chapter (what each image shows, where it goes, and its positive and negative prompts, chosen to suit the genre), plus the cover once the book is written. Review and edit the plan, then press Illustrate book to render everything in one image-model run — no LLM calls, so you can unload the LLM first (automatic for Ollama)
  • The same objects, clothes and places in every image — the illustrator keeps a registry of recurring assets (props, outfits, places, creatures, vehicles) and repeats each one's description word for word whenever it appears; characters keep their usual outfit unless the story changes it. Review and edit the registry on the Illustrations page — an edit updates every prompt that shows the asset
  • One style for the whole book — the illustrator writes an art direction once (style, a portrait composition shared by the whole cast, a book-wide negative prompt) from your visual style; it is added word for word to every image and can be edited on the Illustrations page
  • Consistent characters — each character gets a visual description written from their profile and a reference portrait; every image showing them repeats that description, and scenes centred on them reuse the portrait's seed. Re-roll a portrait until the look is right, then re-render their scenes
  • Configurable — image endpoint, steps, CFG, sizes and visual style live in the LLM configuration and presets, so applying a preset switches the text and image models together
⁠Exports & sharing
  • Multiple export formats — HTML (6 colour themes, adjustable font size), EPUB, FB2, and a Metadata document (book info and stats, then the current Story Bible, clarifications, characters, plot threads and chapter plans, then the full timeline of planning, edits, agent messages and LLM calls). Rendered illustrations and the cover are embedded in all of them, compressed to JPEG to keep the download small
  • Public Library — browse and read published books without logging in (when public mode is enabled); each chapter has its own URL for bookmarking and sharing
  • Telegram bot — connect a book to a Telegram chat, group topic or channel: a pinned status card with Start / Continue / Stop, the planner's questions (reply to answer), a file after each chapter edit, the finished book, illustrations, live drafts while a chapter is written, and the book chat of Interactive books. Whoever can post in the chat gets the access you choose — see Telegram Bot⁠
⁠Infrastructure
  • Pluggable LLM backend — Ollama (default, local), OpenAI (or any OpenAI-compatible API), or Google AI Studio; settings kept as presets (your default is copied into each new book) and per book
  • Real-time streaming — watch chapters being written token by token via SignalR; planning phases stream with live progressive JSON previews. Only book content streams — a reasoning model's thinking is saved to the chat as one "💭 Thinking" message when each step finishes (even when it fails), and the live view reconnects by itself after a network drop. Live updates are numbered, so after a drop the page gets exactly the updates it missed replayed; a heartbeat every 15 seconds notices a connection that died silently and reconnects it
  • Fail-safe runs — a stalled LLM connection times out instead of hanging the book; double-clicking a start button can't launch two runs; runs interrupted by a server restart are closed out with a note telling you how to pick up (Plan Book / Write Book, or /plan / /continue in Telegram)
  • Health endpoint — GET /health reports whether the database is reachable (for Docker/uptime checks)
  • Token usage statistics — per-agent prompt and completion token counts, persisted to the database and displayed in a collapsible panel. Calls that error, time out, or are cancelled are recorded too, with a Status column and the failure reason shown next to their partial counts
  • MCP server — built-in Model Context Protocol server at /mcp; connect Claude Desktop, VS Code Copilot, or any MCP client using a per-user API token
  • Multi-user — JWT sign-in (short-lived access token, httpOnly refresh cookie) with an admin role for user management
  • Ollama model management — browse installed models, pull new ones with live progress
  • Global concurrency limit — cap simultaneous agent runs across all books/users with AgentSettings__MaxConcurrentRuns
  • PWA support — installable, works offline for cached content

⁠Architecture

[React SPA — served as static files from ASP.NET wwwroot]
        ↕ REST API + SignalR
[ASP.NET Core 10 API]
        ↕ Direct provider SDKs      ↕ EF Core 10 + Npgsql
[LLM (Ollama / OpenAI / Google)]   [PostgreSQL 16 (pgvector in-DB)]

React is built at image-build time and served from wwwroot/ — there is no separate frontend container at runtime.


⁠Quick Start

⁠Prerequisites
⁠Run with Docker Compose
docker-compose up -d

# App is available at
open http://localhost:5000

On first launch the app shows a Create Admin Account setup screen — the first account registered automatically becomes admin. After signing in, go to Settings to configure your LLM provider and pull an Ollama model.

⁠Run from Docker Hub
docker run -d \
  -p 5000:8080 \
  -e ConnectionStrings__DefaultConnection="Host=<postgres-host>;Port=5432;Database=abook;Username=abook;Password=abook" \
  --add-host host.docker.internal:host-gateway \
  jncchds/abook:latest

PostgreSQL with the pgvector extension must be reachable. The compose file below starts it automatically.

Full docker-compose.yml
services:
  abook-api:
    image: jncchds/abook:latest
    ports:
      - "5000:8080"
    environment:
      - ConnectionStrings__DefaultConnection=Host=postgres;Port=5432;Database=abook;Username=abook;Password=abook
      - ASPNETCORE_ENVIRONMENT=Production
    depends_on:
      postgres:
        condition: service_healthy
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

  postgres:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_DB: abook
      POSTGRES_USER: abook
      POSTGRES_PASSWORD: abook
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U abook -d abook"]
      interval: 5s
      timeout: 5s
      retries: 10
    restart: unless-stopped

volumes:
  postgres_data:

⁠Configuration

⁠Environment Variables
VariableDefaultDescription
ConnectionStrings__DefaultConnection—PostgreSQL connection string
ASPNETCORE_ENVIRONMENTDevelopmentProduction disables Swagger
LlmDefaults__ProviderOllamaProvider of the "Default" preset each new user gets (Ollama, OpenAI, OpenAICompatible, GoogleAIStudio); unset = new users start without a preset. The LlmDefaults__* values only seed that preset — books never fall back to them
LlmDefaults__ModelNamellama3Default model name
LlmDefaults__Endpointhttp://host.docker.internal:11434Default LLM endpoint
LlmDefaults__ApiKey—API key (required for OpenAI / GoogleAIStudio; optional for Ollama)
LlmDefaults__EmbeddingModelName—Embedding model for RAG (optional; falls back to chat model)
LlmDefaults__ImageEndpoint—Image server URL for illustrations (e.g. http://gpu-box:8000); applied with LlmDefaults__Provider
LlmDefaults__ImageProviderZImageZImage or UnslothStudio
LlmDefaults__ImageApiKey—Image server API key (Unsloth Studio sk-unsloth-…)
LlmDefaults__ImageModel—Unsloth Studio: image model to load when none is loaded
LlmDefaults__ImageVisualStyle—Default visual style for illustrations
LlmDefaults__ImagesPerChapter2Base number of illustrations per chapter (the illustrator may plan more when a chapter needs them)
Jwt__SigningKeygeneratedKey that signs sign-in tokens — any random string of 32+ characters (e.g. openssl rand -base64 48). If unset, ABook generates one and stores it in the database, logs a ⚠️ warning at startup and shows admins a banner; see Sign-in⁠
Jwt__AccessTokenMinutes15Lifetime of an access token
Jwt__RefreshTokenDays30How long a browser stays signed in without using ABook
AgentSettings__MaxConcurrentRuns3Max simultaneous agent runs across all books/users
PublicModefalseEnable public library (anonymous access to published books)
TELEGRAM_BOT_TOKEN—Token from @BotFather; turns the Telegram bot⁠ on
ABOOK_HOSTNAME—Public host name (e.g. book.example.com): the bot gets updates by webhook at https://{ABOOK_HOSTNAME}/api/telegram/webhook. Unset = long polling
TELEGRAM_WEBHOOK_SECRETrandomSecret Telegram sends with each webhook call (A-Z a-z 0-9 _ -); unset = a new random one at each start
TELEGRAM_API_URLhttps://api.telegram.orgA self-hosted Bot API server⁠ instead — raises the file limit from 50 MB to 2 GB

Changing AgentSettings__MaxConcurrentRuns requires restarting the API process/container.

For local development, copy src/ABook.Api/appsettings.Local.example.json → appsettings.Local.json and fill in your values.

⁠Sign-in

⚠️ Since v0.3.0 ABook uses JWT sign-in instead of cookie sessions. Everyone has to sign in once after updating; books, presets and MCP API tokens are not affected.

The browser gets a short-lived access token (kept in memory, never in storage) and a refresh token in an httpOnly cookie that is only sent to /api/auth, rotated on every use and revocable (sign-out, password change). A browser stays signed in for Jwt__RefreshTokenDays after its last visit.

Tokens are signed with Jwt__SigningKey. You don't have to set it — if it is missing, ABook generates a random key on first start and keeps it in its database, so sessions survive restarts and updates. It does log a ⚠️ warning at every startup and shows admins a banner, because anyone with a copy of the database could then forge sign-ins. To move the key into your configuration:

docker compose exec abook-api dotnet ABook.Api.dll secrets show

Copy the printed Jwt__SigningKey=… line into the environment: section of abook-api, then docker compose up -d (any other random 32+ character value works just as well). Changing the key never signs anyone out: it only invalidates access tokens, and browsers fetch a new one with their refresh cookie.

⁠LLM Providers

LLM settings are kept as presets (the 🔑 Presets page) and per book (each book's Settings page):

ProviderNotes
OllamaDefault. Runs locally; host.docker.internal resolves to the host from inside Docker.
OpenAIProvide an API key and model name (e.g. gpt-4o). Leave endpoint blank for the real OpenAI API; set a custom endpoint for any OpenAI-compatible API (Groq, Together, LM Studio at http://host.docker.internal:1234/v1, etc.).
Google AI StudioNative Gemini connector. Requires an API key from aistudio.google.com⁠. Suggested models: gemini-2.0-flash, gemini-2.5-pro. Embedding model: text-embedding-004.
OpenAI CompatibleFor LM Studio, OpenRouter, vLLM and similar. Reads the stream directly, so non-standard fields such as reasoning_content are captured; reasoning_effort is never sent. API key optional.

How the settings are resolved:

  • Every book has its own LLM settings. When you create a book it gets a copy of your default preset (a sequel copies its base book instead). Later changes to the preset — or picking another default — never affect existing books; change those on the book's Settings page (where Apply Preset copies a preset in).
  • Your default preset is chosen under Settings → Default LLM Preset or with Make default on the Presets page. New users get a private "Default" preset built from the LlmDefaults__* environment variables, if those are set.
  • Shared presets are available to every user (including as their default) but only admins can create, edit or delete them. Admins share a preset with the Shared with everyone toggle; sharing cannot be undone. Anyone who can use a shared preset can see its API keys once it is copied into their books.
  • There is no global or per-user fallback configuration any more (removed in v0.3.0).

Local models often ignore the JSON schema the planning agents send — returning a chapter number as 1.0 or "3", or a text field as a list. The parsers coerce those instead of failing, drop only the entries they genuinely cannot read, and post a ⚠️ note in the book's chat naming each dropped entry and the JSON behind it. If a planning phase does fail outright, the error in the chat quotes what the model actually returned.

⁠Image Generation (Illustrations)

Illustrations are rendered by one of two image providers, chosen under 🎨 Image Generation in a book's LLM settings or in a preset; use Test connection after setting the URL.

  • Z-Image — a self-hosted Z-Image Turbo⁠ server: the FastAPI server in the adate project's compose/zimage folder (POST /generate, GET /images/{id}, GET /health).
  • Unsloth Studio — the image models of an Unsloth Studio⁠ server. Enter the Studio address (with or without /v1) and an API key from Studio's Settings → API. Studio keeps only one model on the GPU, so if it also serves your text model (as an OpenAI Compatible provider at http://<host>:<port>/v1), set Image model too — e.g. unsloth/Z-Image-Turbo-GGUF:z-image-turbo-Q8_0.gguf (a Hub repo id, optionally : plus a checkpoint file). ABook then loads it before rendering whenever no image model is loaded, and Studio unloads the text model to make room. Leave it blank to use whatever is loaded on Studio's Images page.
SettingDefaultNotes
Endpoint—Blank disables rendering (illustrations can still be planned)
Image API key—Unsloth Studio only
Image model—Unsloth Studio only — loaded when Studio has no image model loaded
Visual style—Written into every prompt by the LLM; blank lets it choose one for the genre
Steps / CFG8 / 1.0 (Unsloth Studio: 9 / 0)Z-Image Turbo values; blank on Unsloth Studio lets Studio choose, so set both for other Studio models (e.g. FLUX). Negative prompts only take effect with CFG above 1.0
Portrait / landscape size768×1152 / 1152×768Square images use the portrait area
Timeout300000 msPer image
Unload the LLM before renderingoffOllama only (keep_alive: 0); Unsloth Studio swaps models by itself

Workflow: enable Illustrate this book in the book settings → write the book → open 🎨 Illustrations, review or edit the plan → Render portraits and re-roll any you don't like → Illustrate book.

⁠MCP Access

ABook includes a built-in Model Context Protocol⁠ server at /mcp. Any MCP-compatible client can connect to read and write book content and trigger agent workflows.

Tools (each tool's description says exactly what it does):

AreaTools
Account & LLMget_current_user, get_llm_config, set_llm_config, list_presets, apply_preset
Bookslist_books, get_book, create_book, update_book, delete_book, generate_book, export_book (html / fb2 / epub / metadata, or one chapter), get_agent_messages, get_agent_status, get_token_usage
Planninglist_clarifications, add_clarification, update_clarification, complete_planning_phase, reopen_planning_phase, clear_planning_phase
Contentget_story_bible, update_story_bible, list_characters, create_character, update_character, delete_character, list_plot_threads, create_plot_thread, update_plot_thread, delete_plot_thread
Sections & chapterslist_sections, update_section, move_section, archive_section, restore_section, list_chapters, get_chapter, create_chapter, update_chapter, move_chapter, move_chapter_to_section, split_chapter, archive_chapter, restore_chapter
Historylist_chapter_versions, get_chapter_version, activate_chapter_version, archive_character, restore_character, list_character_versions, restore_character_version, archive_plot_thread, restore_plot_thread, list_plot_thread_versions, restore_plot_thread_version, list_clarification_versions, restore_clarification_version, list_story_bible_versions, restore_story_bible_version
Runs (Autonomous books)start_planning, continue_planning, start_workflow, continue_workflow, stop_workflow, break_down_section, write_section, write_chapter, edit_chapter, run_continuity_check (book, section or chapter), answer_agent_question
Book chat (Interactive books)get_book_chat, start_book_chat, send_book_chat_message, revise_book_chat_suggestions, retry_book_chat_message, apply_book_chat_proposals, reject_book_chat_proposals, undo_book_chat_proposal, `l

Tag summary

Content type

Image

Digest

sha256:b62da7398…

Size

250 MB

Last updated

about 14 hours ago

docker pull jncchds/abook