{"slug": "show-hn-tutor-a-self-hosted-llm-tutor-that-teaches-you-your-books-or-docs", "title": "Show HN: Tutor, a self-hosted LLM tutor that teaches you your books or docs", "summary": "Developer Demetrius Edelin released Tutor, a self-hosted LLM tutoring application that ingests EPUB, PDF, or online books and documentation to build a concept map and teach subjects one concept at a time. Tutor requires Node.js 22 or later and an API key from Anthropic, OpenAI, or OpenRouter, with setup via git clone, npm install, and npm run ingest, then npm start at http://localhost:3000. The tool tracks concept status as known, learning, or mastered and lets users select which concepts to test, learn, or skip.", "body_md": "See also my other project, [Tapas Habit & Goal Tracker](https://tapastracker.app), a full-featured habit tracker for iPhone and Android. Also, **I am looking for a full-time role** as a software developer. Contact me on [LinkedIn](https://www.linkedin.com/in/demetrius-edelin/).\n\nA personal tutor that teaches you from your books or from online documentation. You make a subject of study, for example \"SQL\", and add books to it. A book can be an EPUB file, a PDF file, an online book, or online documentation. The tutor finds the concepts in the books and checks which concepts you know. Then it teaches the other concepts one at a time, with references to the books, and tests each one.\n\nThe tutor runs on your computer. It uses one large language model (LLM) from Anthropic, OpenAI, or OpenRouter. You select the model.\n\n| Concept map | Lesson | Test | \n|---|---|---|\n\nA chat with a book answers the questions that you think of. The tutor turns your books into a course:\n\n- **Structure.** The tutor puts the concepts of a subject into modules on a concept map. It teaches them one at a time, from a study queue.\n- **All the concepts.** Ingest finds the concepts in each chapter. Then it makes sure that the concepts cover the bold and italic terms of the book.\n- **Visible progress.** The concept map and the review board show the status of each concept, for example known, learning, or mastered.\n- **Your control.** You select the concepts to test, to learn, and to skip. You also set the order of the study queue. The tutor only suggests.\n\nYou need Node.js 22 or later and an API key for Anthropic, OpenAI, or OpenRouter. We test the tutor on macOS.\n\n```\ngit clone https://github.com/demetrius-edelin/tutor.git\ncd tutor\nnpm install\ncp .env.example .env                       # then set the provider, the model, and the API key\nnpm run llm:check                          # optional: send 3 small test requests to the model\nnpm run ingest -- SQL /path/to/book.epub   # add a book to the subject \"SQL\"\nnpm run fetch -- https://doc.rust-lang.org/book/   # optional: download an online book into an EPUB file\nnpm start                                  # then open http://localhost:3000\n```\n\nIn the Windows command prompt, use `copy` instead of `cp`.\n\nThe `ingest` command shows the number of model requests and asks before it starts. To start with some chapters of the book only, see [Step 3 of the usage guide](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#step-3-add-a-book-to-a-subject).\n\nYou use the tutor in four steps. Steps 1 to 3 are commands in the terminal. Step 4 is the app in the browser.\n\n``` php\nflowchart LR\n    S1[\"1. Set up<br/>npm install<br/>.env\"] --> S2[\"2. Check (optional)<br/>npm run llm:check<br/>npm run parse\"]\n    S2 --> S3[\"3. Add a book<br/>or some chapters<br/>npm run ingest\"]\n    S3 --> S4[\"4. Study<br/>npm start\"]\n    S3 -->|\"more chapters or books\"| S3\n    style S2 stroke-dasharray: 5 5\n```\n\n1. [Set up the tutor](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#step-1-set-up-the-tutor) . Do this one time.\n2. [Check the model and the book](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#step-2-check-the-model-and-the-book-optional) . This step is optional. It saves no data.\n3. [Add a book to a subject](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#step-3-add-a-book-to-a-subject) . Add the full book, or only the chapters that you want to study now. Only this step puts books into the tutor. For an online book,[download it first](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#add-an-online-book) .\n4. [Study in the browser](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#step-4-study-in-the-browser) . Start the app and learn. The app shows the next action at each stage.\n\nThe [usage guide](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md) explains each step and each command.\n\nIn the app, you open a subject and go through these stages:\n\n- Choose: on the concept map, test, learn, or skip each concept. To do this for many concepts in one step, select their checkboxes and use the bar at the bottom of the page.\n- Diagnosis: the tutor asks 2 questions about each concept that you selected for a test. To start it, click \"Test it\" next to a concept, or select concepts and click \"Test them\".\n- Study queue: the concepts to learn, in an order that you can change.\n- Lesson and test: the tutor teaches one concept from your books, with references. Then it tests the concept with the questions that fit it: one question for a simple concept, at most 5 for a larger one. To pass, answer each question correctly. A pass makes the concept mastered.\n- Review board: a list of all concepts with their status and their stars.\n\nThe model writes the questions and grades the open answers. If you think that a grade is wrong, use \"Dispute the grade\". A disputed answer counts as correct.\n\n| Command | Use | Uses the model | What it writes | \n|---|---|---|---|\n| `npm run llm:check` | Optional. After a change to `.env` . | Yes, 3 small requests. | Nothing. | \n| `npm run parse -- <book>` | Optional. To check a book and to find the chapter numbers for `--chapters` . | No. | A report in `data/parse/<book>/` , for you to read. | \n| `npm run fetch -- <url>` | To study an online book or online documentation. Then use the EPUB file as the book. | Only for a site with no `llms.txt` file: one small request. | An EPUB file in `data/web/` . | \n| `npm run ingest -- <subject> <book> --chapters <list> --preview` | Recommended. To check the concepts of some chapters before you save them. | Yes. | Preview files in the folder of the book. The database does not change. | \n| `npm run ingest -- <subject> <book> --chapters <list>` | To study some chapters of a book. | Yes. | The folder of the book and the database. | \n| `npm run ingest -- <subject> <book>` | To study all the chapters of a book. | Yes. | The folder of the book and the database. | \n| `npm start` | Each time that you want to study. | Yes, for the diagnosis, the lessons, and the tests. | Your progress in the database. | \n| `npm run refresh -- <subject> <book>` | Only after an update of the tutor that changes the parser. | Only for new images. | The section files of the book. | \n\nThe folder of the book is `data/subjects/<subject>/books/<book>/`. The database is `data/tutor.db`.\n\nThe tutor reads its configuration from the `.env` file. Copy `.env.example` to `.env`. The file explains each value.\n\n| Variable | Value | \n|---|---|\n| `LLM_PROVIDER` | `anthropic` ,`openai` , or`openrouter` . | \n| `LLM_MODEL` | The model name, exactly as the provider writes it. For OpenRouter, use the form `vendor/model` . | \n| `LLM_REASONING` | Optional. `none` ,`minimal` ,`low` ,`medium` ,`high` ,`xhigh` , or`max` . Empty means the default of the model. | \n| `OPENROUTER_PROVIDERS` | Optional, for OpenRouter only. The providers that can serve the model, in order. | \n| `ANTHROPIC_API_KEY` ,`OPENAI_API_KEY` ,`OPENROUTER_API_KEY` | The API key of the selected provider. | \n\nThe tutor has no default model. After a change to `.env`, run `npm run llm:check`.\n\nWe tested the tutor with different models. GPT-6 Luna from OpenAI, at the reasoning level `high`, gave the best results. It was also the cheapest model in our tests, and it is very smart for a model in its price range.\n**Update, October 2026:** Claude Haiku 5.5 from Anthropic has now the same price as GPT-6 Luna, but it gives much better results!\n\n```\nLLM_PROVIDER=anthropic\nLLM_MODEL=claude-haiku-5-5\nLLM_REASONING=high\n```\n\nIf you use OpenAI, use these values:\n\n```\nLLM_PROVIDER=openai\nLLM_MODEL=gpt-6-luna\nLLM_REASONING=high\n```\n\nThe tutor uses your API key, so your provider charges you for each request.\n\n- Ingest makes the most requests, because the model reads each chapter and each image of the book. The command shows the number of requests and asks before it starts.\n- The tutor keeps the model results of each chapter and each image in a cache. A later run does not pay again for the chapters in the cache.\n- In the app, the diagnosis, the lessons, the questions about a lesson, and the tests use the model.\n- A high reasoning level makes each request slower.\n\nThe app uses port 3000. To use a different port, set `PORT` in the shell. The tutor does not read `PORT` from `.env`.\n\n```\nPORT=8080 npm start\n```\n\n- EPUB files work best. The parser reads the publisher CSS for headings, bold, and italic.\n- PDF files must be tagged. Word and many publishing tools make tagged PDF files. The parser stops with a clear message for an untagged or a scanned PDF.\n- Some books show code or tables as images. Ingest uses the model to read them. If the model does not accept images, the images stay as placeholders.\n- Online books and online documentation: `npm run fetch` downloads the pages into an EPUB file. The command cannot read pages behind a login, or pages that show their text only after JavaScript runs. See[Add an online book](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#add-an-online-book) .\n\n- **Must I run `parse` before `ingest`?** No. Ingest parses the book by itself. The` parse` command only prints a report in the terminal, at no cost. See[Check a book](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#check-a-book) .\n- **How do I find the number of a chapter for `--chapters`?** Run` npm run parse` and read the`#` column. This number can be different from the number in the title of the chapter.\n- **How do I find the id of a section for `--sections`?** Run` npm run parse` and read the file names in`data/parse/<book>/sections/` . The file`01-10-pointers.md` is section`1.10` .\n- **Must I ingest the full book?** No. With`--chapters` or`--sections` , you can study a book one part at a time. Later, you can add more parts.\n- **Does `ingest` always save to the database?** Yes. Only`--preview` keeps the database as it is. With`--chapters` , the app shows the concepts of these chapters immediately.\n- **Do I pay two times for the chapters of a preview?** No. The tutor keeps the model results of each run, so a later run does not pay for these chapters again.\n- **How do I add more chapters later?** Run`ingest` again with a list of the new chapters or sections. See[Add more chapters later](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#add-more-chapters-later) .\n- **Can I move or delete the book file after ingest?** Yes. Ingest keeps a copy of the book file in the folder of the book.\n- **Can the tutor teach from online documentation?** Yes. Run`npm run fetch` with the address of the documentation. Then ingest the EPUB file. See[Add an online book](https://github.com/demetrius-edelin/tutor/blob/master/docs/usage.md#add-an-online-book) .\n- **How do I add a second book to a subject?** Run`npm run ingest` again with the same subject name. The tutor adds the concepts of the new book to the concept map of the subject.\n- **How do I make a new subject?** Run`npm run ingest` with a new subject name. The first book makes the subject.\n- **How do I delete a subject?** On the list of subjects, click \"Delete\" below the subject. Then click the red button in the panel that opens. The tutor deletes the books, the concepts, your progress, and the folder of the subject. You cannot undo this.\n- **Can I delete `data/parse/`?** Yes. Nothing else uses it.\n\nThe tutor is at version 0.1.0. All the commands and app stages in this README and in the usage guide work. The tutor is for one person on one computer.\n\nPlanned:\n\n- Add a book from the app, with the progress of the ingest. Now you add books with a command in the terminal.\n- A container image, to run the tutor on your own server.\n\n| Command | What it does | \n|---|---|\n| `npm run dev:server` and`npm run dev:app` | Run the server and the app with live reload. Use two terminals. | \n| `npm test` | Run the tests. The tests use a fake model client, so they do not call a model. | \n| `npm run test:watch` | Run the tests again after each change. | \n| `npm run typecheck` | Check the TypeScript types of the server and of the app. | \n\n| Part | Tools | \n|---|---|\n| Language | TypeScript on Node.js 22. | \n| Server | Fastify. | \n| App | React 19 and Vite. | \n| Database | SQLite, with better-sqlite3. | \n| Book parser | JSZip and cheerio for EPUB, pdf.js for PDF, and Turndown for Markdown. | \n| Online books | The fetch function of Node.js, cheerio, marked for Markdown pages, and JSZip for the EPUB file. | \n| Model clients | The Anthropic SDK and the OpenAI SDK. OpenRouter uses the OpenAI SDK. | \n| Tests | Vitest. | \n\n```\nsrc/\n  cli/          command-line scripts\n  config.ts     reads .env\n  db/           SQLite schema\n  ingest/\n    core/       blocks, sections, Markdown, checklist (all formats)\n    epub/       EPUB reader\n    pdf/        tagged PDF reader\n    stages/     extract, review, and merge (the model stages)\n    web/        online books: table of contents, pages, and the EPUB writer\n  llm/          model client for Anthropic, OpenAI, and OpenRouter\n  tutor/        diagnosis, lessons, questions, grader, and study queue\n  server/       HTTP API\n  app/          browser app (React)\ndocs/           usage guide\ntest/           tests, a fake model client, and a builder for test EPUB files\ndata/           your books, the results, and the database (not in git)\n```\n\nI built Tutor with Claude Code. All the product and design decisions are mine, and I tested each milestone carefully with real books. Claude wrote the code and most of the documentation.\n\nPull requests are welcome. For a large change, open an issue first, so that we can agree on the approach.\n\nBefore you send a pull request, do these steps:\n\n1. Run `npm test` . All tests must pass.\n2. Run `npm run typecheck` . It must show no errors.\n\n- Do not commit `data/` or`.env` . Git ignores them.\n- The `data/` folder contains the text of books that you bought, and of the online books that you downloaded.\n- The `fetch` command obeys`robots.txt` and waits one second between two requests.\n- The repository contains no book text. The tests build their own small EPUB file in code.\n- When the tutor sends a request, it sends book text to the provider that you selected. With OpenRouter, the text goes to the vendor of the selected model.", "url": "https://wpnews.pro/news/show-hn-tutor-a-self-hosted-llm-tutor-that-teaches-you-your-books-or-docs", "canonical_source": "https://github.com/demetrius-edelin/tutor", "published_at": "2026-10-10 20:23:47+00:00", "updated_at": "2026-10-10 20:47:31.452058+00:00", "lang": "en", "topics": ["ai-tools", "large-language-models", "ai-products", "developer-tools"], "entities": ["Demetrius Edelin", "Tutor", "Anthropic", "OpenAI", "OpenRouter", "Node.js", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-tutor-a-self-hosted-llm-tutor-that-teaches-you-your-books-or-docs", "markdown": "https://wpnews.pro/news/show-hn-tutor-a-self-hosted-llm-tutor-that-teaches-you-your-books-or-docs.md", "text": "https://wpnews.pro/news/show-hn-tutor-a-self-hosted-llm-tutor-that-teaches-you-your-books-or-docs.txt", "jsonld": "https://wpnews.pro/news/show-hn-tutor-a-self-hosted-llm-tutor-that-teaches-you-your-books-or-docs.jsonld"}}