novelforge 1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
novelforge-1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 StoryForge Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: novelforge
3
+ Version: 1.0
4
+ Summary: AI-powered novel writer with vector memory, RAG, and a PyQt6 desktop UI
5
+ Author-email: Kshitij Budholiya <kshitijbudholiya2006@gmail.com>
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/Kshitijbudholiya/NovelForge
8
+ Project-URL: Issues, https://github.com/Kshitijbudholiya/NovelForge/issues
9
+ Keywords: novel,ai,writing,ollama,pyqt6,rag,vector-database
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Games/Entertainment
18
+ Classifier: Topic :: Text Editors
19
+ Classifier: Environment :: X11 Applications
20
+ Classifier: Environment :: Win32 (MS Windows)
21
+ Classifier: Environment :: MacOS X
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: PyQt6>=6.4.0
26
+ Requires-Dist: ollama>=0.2.0
27
+ Requires-Dist: endee>=0.1.27
28
+ Dynamic: license-file
29
+
30
+ # NovelForge AI
31
+
32
+ > ⚠️ **Work in Progress** — NovelForge is actively under development. Features may change, break, or be incomplete. Not production-ready.
33
+
34
+ **NovelForge** is an AI-powered desktop novel writer that runs entirely on your local machine. Give it a title, genre, and premise — it writes Chapter 1, remembers everything, and keeps writing with full narrative continuity across as many chapters as you want. Ask it questions about your story and it searches its vector memory to answer accurately.
35
+
36
+ Built on three local services: **Ollama** for LLM inference, **Endee** for vector memory, and **PyQt6** for the desktop UI.
37
+
38
+ ---
39
+
40
+ ## What Makes This Different
41
+
42
+ Most AI writing tools forget everything after each message. NovelForge doesn't. Every chapter you generate is broken into chunks, embedded, and stored across four dedicated Endee vector indexes — story, characters, lore, and summaries. When the next chapter is generated, the RAG layer retrieves the most semantically relevant chunks from all four indexes and feeds them into the prompt. Characters stay consistent. Locations don't move. Plot threads don't get dropped.
43
+
44
+ ---
45
+
46
+ ## Powered by Endee
47
+
48
+ [Endee](https://endee.io) is the vector database that makes NovelForge's memory work. It runs locally via Docker, stores all embeddings on disk, and serves queries over HTTP — no cloud, no API keys, no data leaving your machine.
49
+
50
+ **Why Endee for this project:**
51
+
52
+ - **Multi-index architecture** — NovelForge uses four separate indexes (`story_memory`, `character_memory`, `lore_memory`, `summary_memory`). Endee makes it trivial to create, manage, and query multiple indexes independently, so character recall and lore recall don't interfere with each other.
53
+ - **Filtered queries** — Every vector is tagged with a `novel_id` filter. Endee's `$eq` filter operator means queries are always scoped to the active novel — no cross-contamination between your different stories.
54
+ - **INT8 precision** — All indexes use `Precision.INT8` quantization. This halves the memory footprint of stored vectors with negligible quality loss, which matters when you're embedding hundreds of chunks across long novels.
55
+ - **Cosine similarity** — The embedding model (`nomic-embed-text`) and cosine space type are a natural fit; semantic similarity is what you want when searching story memory by meaning, not distance.
56
+ - **Persistent Docker volume** — Everything written to Endee survives restarts. Your novel memory is durable.
57
+ - **Zero-latency cold start** — Indexes are lazily initialized; the first query creates the index if it doesn't exist, so there's no setup ceremony.
58
+
59
+ **Endee docs:** https://docs.endee.io/quick-start
60
+
61
+ ---
62
+
63
+ ## Features
64
+
65
+ - **Create novels** — title, genre, and premise is all you need; Chapter 1 is written and saved automatically
66
+ - **Continue stories** — type a chapter instruction; the RAG layer retrieves relevant memory before writing
67
+ - **Ask questions** — query any detail about your story; answers are grounded in stored chapter memory
68
+ - **Interactive / Read-only toggle** — switch between writing mode and read-only browsing at any time
69
+ - **Collapsible sidebar** — novel tree with per-chapter navigation; click any chapter to load its full text and summary
70
+ - **Per-context chat history** — separate conversation thread per novel and per chapter
71
+ - **Story Bible** — characters, locations, factions, and lore auto-extracted from every chapter and saved to the novel's JSON metadata
72
+ - **GPU-accelerated inference** — all Ollama calls (generation + embeddings) are configured to use full GPU offload
73
+ - **Fully local** — Ollama, Endee, and the app all run on your machine; no data leaves it
74
+
75
+ ---
76
+
77
+ ## Minimum Requirements
78
+
79
+ NovelForge runs heavy local inference. These are the **minimum tested specs**:
80
+
81
+ | Component | Minimum |
82
+ |---|---|
83
+ | GPU | NVIDIA RTX 4050 6 GB VRAM (or equivalent) |
84
+ | RAM | 16 GB |
85
+ | CPU | AMD Ryzen 7 (or equivalent 8-core) |
86
+ | Storage | ~10 GB free (models + novel data) |
87
+ | OS | Windows 10/11, Ubuntu 20.04+, macOS 12+ |
88
+
89
+ > **Expected generation time:** 1–3 minutes per chapter or query, depending on GPU and chapter length. The `qwen3:8b` model runs at roughly 20–40 tokens/sec on an RTX 4050 with full GPU offload.
90
+
91
+ ---
92
+
93
+ ## Prerequisites
94
+
95
+ You need **Ollama** and **Endee** running locally before launching NovelForge.
96
+
97
+ ### 1. Start Ollama
98
+
99
+ Download and install Ollama from https://ollama.ai, then pull the two required models:
100
+
101
+ ```bash
102
+ ollama pull qwen3:8b
103
+ ollama pull nomic-embed-text
104
+ ```
105
+
106
+ Ollama starts automatically on install and runs at `http://localhost:11434`. Verify it's running:
107
+
108
+ ```bash
109
+ ollama list
110
+ ```
111
+
112
+ You should see both `qwen3:8b` and `nomic-embed-text` listed.
113
+
114
+ ### 2. Start Endee (Docker)
115
+
116
+ Endee runs as a Docker container. Install Docker Desktop from https://www.docker.com/products/docker-desktop first, then run:
117
+
118
+ ```bash
119
+ docker run \
120
+ --ulimit nofile=100000:100000 \
121
+ -p 8080:8080 \
122
+ -v ./endee-data:/data \
123
+ --name endee-server \
124
+ --restart unless-stopped \
125
+ endeeio/endee-server:latest
126
+ ```
127
+
128
+ What each flag does:
129
+
130
+ | Flag | Purpose |
131
+ |---|---|
132
+ | `--ulimit nofile=100000:100000` | Raises the file descriptor limit — required for Endee's index files under load |
133
+ | `-p 8080:8080` | Exposes Endee's HTTP API on localhost port 8080 |
134
+ | `-v ./endee-data:/data` | Mounts a local folder so all vector data persists across restarts |
135
+ | `--name endee-server` | Names the container so you can stop/start it easily |
136
+ | `--restart unless-stopped` | Auto-restarts Endee if Docker restarts |
137
+
138
+ Verify Endee is running by visiting http://localhost:8080 in your browser — you should see the Endee API response.
139
+
140
+ To stop and restart later:
141
+
142
+ ```bash
143
+ docker stop endee-server
144
+ docker start endee-server
145
+ ```
146
+
147
+ Full Endee documentation: https://docs.endee.io/quick-start
148
+
149
+ ---
150
+
151
+ ## Installation
152
+
153
+ Once Ollama and Endee are both running:
154
+
155
+ ```bash
156
+ pip install novelforge
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Launch
162
+
163
+ ```bash
164
+ novelforge
165
+ ```
166
+
167
+ Or from Python:
168
+
169
+ ```python
170
+ from novelforge import launch
171
+ launch()
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Usage
177
+
178
+ 1. Click **+ New Novel** in the top bar
179
+ 2. Enter a title, genre, and premise — then click **Generate Chapter 1**
180
+ 3. Wait 1–3 minutes while the chapter is generated, embedded, and saved
181
+ 4. The chapter appears in the chat area and the sidebar updates with Chapter 1
182
+ 5. Type a chapter instruction (e.g. *"The lion and rat discover an abandoned village"*) and press **Send** or **Ctrl+Enter** to generate the next chapter
183
+ 6. Ask questions about your story at any time (e.g. *"What does the rat look like?"*)
184
+ 7. Use the **Interactive / Read-only toggle** in the top-right to switch to browse mode
185
+ 8. Click any chapter in the sidebar to read its full text and summary
186
+
187
+ ---
188
+
189
+ ## Project Layout
190
+
191
+ ```
192
+ novelforge/
193
+ ├── pyproject.toml
194
+ ├── README.md
195
+ ├── LICENSE
196
+
197
+ ├── novelforge/
198
+ │ ├── __init__.py # public API — exposes launch()
199
+ │ ├── __main__.py # python -m novelforge entry point
200
+ │ │
201
+ │ ├── core/ # backend — no UI dependency
202
+ │ │ ├── __init__.py
203
+ │ │ ├── prompts.py # all LLM prompt templates
204
+ │ │ ├── generators.py # Ollama calls + GPU options
205
+ │ │ ├── memory.py # Endee vector DB layer (lazy init)
206
+ │ │ ├── novel_manager.py # JSON metadata + flat-file chapter storage
207
+ │ │ └── rag.py # RAG orchestration layer
208
+ │ │
209
+ │ └── ui/ # PyQt6 frontend
210
+ │ ├── __init__.py
211
+ │ ├── style.py # full QSS stylesheet + colour palette
212
+ │ ├── widgets.py # ToggleSwitch, Spinner, LoaderOverlay, MessageBubble
213
+ │ ├── workers.py # QThread workers (CreateNovelWorker, SendMessageWorker)
214
+ │ ├── dialogs.py # New Novel dialog
215
+ │ ├── sidebar.py # collapsible novel/chapter tree
216
+ │ ├── chat_panel.py # chat area (header, scroll, bubbles, input bar)
217
+ │ └── app.py # MainWindow + run() entry point
218
+ ```
219
+
220
+ ---
221
+
222
+ ## How the Memory System Works
223
+
224
+ ```
225
+ Chapter generated
226
+
227
+
228
+ chunk_text() splits chapter into 400-word chunks
229
+
230
+
231
+ get_embedding() nomic-embed-text via Ollama (GPU accelerated)
232
+
233
+ ├──▶ story_memory index full chapter chunks
234
+ ├──▶ summary_memory index compressed continuity summary
235
+ ├──▶ character_memory index extracted character data
236
+ └──▶ lore_memory index locations, factions, world rules
237
+
238
+ Next chapter generation:
239
+ query = user instruction
240
+
241
+
242
+ retrieve_all_memory()
243
+ ├── story_memory.query(top_k=8)
244
+ ├── character_memory.query(top_k=6)
245
+ ├── lore_memory.query(top_k=6)
246
+ └── summary_memory.query(top_k=8)
247
+
248
+
249
+ context injected into LLM prompt
250
+
251
+
252
+ consistent chapter generated
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Known Limitations
258
+
259
+ - Generation time is 1–3 minutes — there is no streaming output yet; the full chapter appears when complete
260
+ - The app has been tested on Windows 11 with an RTX 4050; other configurations may need tuning
261
+ - Endee must be running before the app starts; there is no auto-start or connection retry yet
262
+ - Chapter deletion is not yet implemented in the UI
263
+ - Export to EPUB/PDF is not yet implemented
264
+
265
+ ---
266
+
267
+ ## License
268
+
269
+ MIT — see `LICENSE` for details.
@@ -0,0 +1,240 @@
1
+ # NovelForge AI
2
+
3
+ > ⚠️ **Work in Progress** — NovelForge is actively under development. Features may change, break, or be incomplete. Not production-ready.
4
+
5
+ **NovelForge** is an AI-powered desktop novel writer that runs entirely on your local machine. Give it a title, genre, and premise — it writes Chapter 1, remembers everything, and keeps writing with full narrative continuity across as many chapters as you want. Ask it questions about your story and it searches its vector memory to answer accurately.
6
+
7
+ Built on three local services: **Ollama** for LLM inference, **Endee** for vector memory, and **PyQt6** for the desktop UI.
8
+
9
+ ---
10
+
11
+ ## What Makes This Different
12
+
13
+ Most AI writing tools forget everything after each message. NovelForge doesn't. Every chapter you generate is broken into chunks, embedded, and stored across four dedicated Endee vector indexes — story, characters, lore, and summaries. When the next chapter is generated, the RAG layer retrieves the most semantically relevant chunks from all four indexes and feeds them into the prompt. Characters stay consistent. Locations don't move. Plot threads don't get dropped.
14
+
15
+ ---
16
+
17
+ ## Powered by Endee
18
+
19
+ [Endee](https://endee.io) is the vector database that makes NovelForge's memory work. It runs locally via Docker, stores all embeddings on disk, and serves queries over HTTP — no cloud, no API keys, no data leaving your machine.
20
+
21
+ **Why Endee for this project:**
22
+
23
+ - **Multi-index architecture** — NovelForge uses four separate indexes (`story_memory`, `character_memory`, `lore_memory`, `summary_memory`). Endee makes it trivial to create, manage, and query multiple indexes independently, so character recall and lore recall don't interfere with each other.
24
+ - **Filtered queries** — Every vector is tagged with a `novel_id` filter. Endee's `$eq` filter operator means queries are always scoped to the active novel — no cross-contamination between your different stories.
25
+ - **INT8 precision** — All indexes use `Precision.INT8` quantization. This halves the memory footprint of stored vectors with negligible quality loss, which matters when you're embedding hundreds of chunks across long novels.
26
+ - **Cosine similarity** — The embedding model (`nomic-embed-text`) and cosine space type are a natural fit; semantic similarity is what you want when searching story memory by meaning, not distance.
27
+ - **Persistent Docker volume** — Everything written to Endee survives restarts. Your novel memory is durable.
28
+ - **Zero-latency cold start** — Indexes are lazily initialized; the first query creates the index if it doesn't exist, so there's no setup ceremony.
29
+
30
+ **Endee docs:** https://docs.endee.io/quick-start
31
+
32
+ ---
33
+
34
+ ## Features
35
+
36
+ - **Create novels** — title, genre, and premise is all you need; Chapter 1 is written and saved automatically
37
+ - **Continue stories** — type a chapter instruction; the RAG layer retrieves relevant memory before writing
38
+ - **Ask questions** — query any detail about your story; answers are grounded in stored chapter memory
39
+ - **Interactive / Read-only toggle** — switch between writing mode and read-only browsing at any time
40
+ - **Collapsible sidebar** — novel tree with per-chapter navigation; click any chapter to load its full text and summary
41
+ - **Per-context chat history** — separate conversation thread per novel and per chapter
42
+ - **Story Bible** — characters, locations, factions, and lore auto-extracted from every chapter and saved to the novel's JSON metadata
43
+ - **GPU-accelerated inference** — all Ollama calls (generation + embeddings) are configured to use full GPU offload
44
+ - **Fully local** — Ollama, Endee, and the app all run on your machine; no data leaves it
45
+
46
+ ---
47
+
48
+ ## Minimum Requirements
49
+
50
+ NovelForge runs heavy local inference. These are the **minimum tested specs**:
51
+
52
+ | Component | Minimum |
53
+ |---|---|
54
+ | GPU | NVIDIA RTX 4050 6 GB VRAM (or equivalent) |
55
+ | RAM | 16 GB |
56
+ | CPU | AMD Ryzen 7 (or equivalent 8-core) |
57
+ | Storage | ~10 GB free (models + novel data) |
58
+ | OS | Windows 10/11, Ubuntu 20.04+, macOS 12+ |
59
+
60
+ > **Expected generation time:** 1–3 minutes per chapter or query, depending on GPU and chapter length. The `qwen3:8b` model runs at roughly 20–40 tokens/sec on an RTX 4050 with full GPU offload.
61
+
62
+ ---
63
+
64
+ ## Prerequisites
65
+
66
+ You need **Ollama** and **Endee** running locally before launching NovelForge.
67
+
68
+ ### 1. Start Ollama
69
+
70
+ Download and install Ollama from https://ollama.ai, then pull the two required models:
71
+
72
+ ```bash
73
+ ollama pull qwen3:8b
74
+ ollama pull nomic-embed-text
75
+ ```
76
+
77
+ Ollama starts automatically on install and runs at `http://localhost:11434`. Verify it's running:
78
+
79
+ ```bash
80
+ ollama list
81
+ ```
82
+
83
+ You should see both `qwen3:8b` and `nomic-embed-text` listed.
84
+
85
+ ### 2. Start Endee (Docker)
86
+
87
+ Endee runs as a Docker container. Install Docker Desktop from https://www.docker.com/products/docker-desktop first, then run:
88
+
89
+ ```bash
90
+ docker run \
91
+ --ulimit nofile=100000:100000 \
92
+ -p 8080:8080 \
93
+ -v ./endee-data:/data \
94
+ --name endee-server \
95
+ --restart unless-stopped \
96
+ endeeio/endee-server:latest
97
+ ```
98
+
99
+ What each flag does:
100
+
101
+ | Flag | Purpose |
102
+ |---|---|
103
+ | `--ulimit nofile=100000:100000` | Raises the file descriptor limit — required for Endee's index files under load |
104
+ | `-p 8080:8080` | Exposes Endee's HTTP API on localhost port 8080 |
105
+ | `-v ./endee-data:/data` | Mounts a local folder so all vector data persists across restarts |
106
+ | `--name endee-server` | Names the container so you can stop/start it easily |
107
+ | `--restart unless-stopped` | Auto-restarts Endee if Docker restarts |
108
+
109
+ Verify Endee is running by visiting http://localhost:8080 in your browser — you should see the Endee API response.
110
+
111
+ To stop and restart later:
112
+
113
+ ```bash
114
+ docker stop endee-server
115
+ docker start endee-server
116
+ ```
117
+
118
+ Full Endee documentation: https://docs.endee.io/quick-start
119
+
120
+ ---
121
+
122
+ ## Installation
123
+
124
+ Once Ollama and Endee are both running:
125
+
126
+ ```bash
127
+ pip install novelforge
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Launch
133
+
134
+ ```bash
135
+ novelforge
136
+ ```
137
+
138
+ Or from Python:
139
+
140
+ ```python
141
+ from novelforge import launch
142
+ launch()
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Usage
148
+
149
+ 1. Click **+ New Novel** in the top bar
150
+ 2. Enter a title, genre, and premise — then click **Generate Chapter 1**
151
+ 3. Wait 1–3 minutes while the chapter is generated, embedded, and saved
152
+ 4. The chapter appears in the chat area and the sidebar updates with Chapter 1
153
+ 5. Type a chapter instruction (e.g. *"The lion and rat discover an abandoned village"*) and press **Send** or **Ctrl+Enter** to generate the next chapter
154
+ 6. Ask questions about your story at any time (e.g. *"What does the rat look like?"*)
155
+ 7. Use the **Interactive / Read-only toggle** in the top-right to switch to browse mode
156
+ 8. Click any chapter in the sidebar to read its full text and summary
157
+
158
+ ---
159
+
160
+ ## Project Layout
161
+
162
+ ```
163
+ novelforge/
164
+ ├── pyproject.toml
165
+ ├── README.md
166
+ ├── LICENSE
167
+
168
+ ├── novelforge/
169
+ │ ├── __init__.py # public API — exposes launch()
170
+ │ ├── __main__.py # python -m novelforge entry point
171
+ │ │
172
+ │ ├── core/ # backend — no UI dependency
173
+ │ │ ├── __init__.py
174
+ │ │ ├── prompts.py # all LLM prompt templates
175
+ │ │ ├── generators.py # Ollama calls + GPU options
176
+ │ │ ├── memory.py # Endee vector DB layer (lazy init)
177
+ │ │ ├── novel_manager.py # JSON metadata + flat-file chapter storage
178
+ │ │ └── rag.py # RAG orchestration layer
179
+ │ │
180
+ │ └── ui/ # PyQt6 frontend
181
+ │ ├── __init__.py
182
+ │ ├── style.py # full QSS stylesheet + colour palette
183
+ │ ├── widgets.py # ToggleSwitch, Spinner, LoaderOverlay, MessageBubble
184
+ │ ├── workers.py # QThread workers (CreateNovelWorker, SendMessageWorker)
185
+ │ ├── dialogs.py # New Novel dialog
186
+ │ ├── sidebar.py # collapsible novel/chapter tree
187
+ │ ├── chat_panel.py # chat area (header, scroll, bubbles, input bar)
188
+ │ └── app.py # MainWindow + run() entry point
189
+ ```
190
+
191
+ ---
192
+
193
+ ## How the Memory System Works
194
+
195
+ ```
196
+ Chapter generated
197
+
198
+
199
+ chunk_text() splits chapter into 400-word chunks
200
+
201
+
202
+ get_embedding() nomic-embed-text via Ollama (GPU accelerated)
203
+
204
+ ├──▶ story_memory index full chapter chunks
205
+ ├──▶ summary_memory index compressed continuity summary
206
+ ├──▶ character_memory index extracted character data
207
+ └──▶ lore_memory index locations, factions, world rules
208
+
209
+ Next chapter generation:
210
+ query = user instruction
211
+
212
+
213
+ retrieve_all_memory()
214
+ ├── story_memory.query(top_k=8)
215
+ ├── character_memory.query(top_k=6)
216
+ ├── lore_memory.query(top_k=6)
217
+ └── summary_memory.query(top_k=8)
218
+
219
+
220
+ context injected into LLM prompt
221
+
222
+
223
+ consistent chapter generated
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Known Limitations
229
+
230
+ - Generation time is 1–3 minutes — there is no streaming output yet; the full chapter appears when complete
231
+ - The app has been tested on Windows 11 with an RTX 4050; other configurations may need tuning
232
+ - Endee must be running before the app starts; there is no auto-start or connection retry yet
233
+ - Chapter deletion is not yet implemented in the UI
234
+ - Export to EPUB/PDF is not yet implemented
235
+
236
+ ---
237
+
238
+ ## License
239
+
240
+ MIT — see `LICENSE` for details.
@@ -0,0 +1,10 @@
1
+ """
2
+ storyforge
3
+ ~~~~~~~~~~
4
+ AI-powered novel writer — public API.
5
+ """
6
+
7
+ from novelforge.ui.app import run as launch
8
+
9
+ __all__ = ["launch"]
10
+ __version__ = "0.2.0"
@@ -0,0 +1,6 @@
1
+ """Allow `python -m storyforge` and the `storyforge` console script."""
2
+
3
+ from novelforge.ui.app import run as main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,129 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ import ollama
6
+
7
+ from novelforge.core.prompts import (
8
+ NOVEL_SYSTEM_PROMPT,
9
+ CREATE_NOVEL_PROMPT,
10
+ CHAPTER_PLAN_PROMPT,
11
+ CONTINUE_NOVEL_PROMPT,
12
+ MEMORY_COMPRESSION_PROMPT,
13
+ CHARACTER_EXTRACTION_PROMPT,
14
+ LORE_EXTRACTION_PROMPT,
15
+ QA_PROMPT,
16
+ )
17
+
18
+ STORY_MODEL = "qwen3:8b"
19
+
20
+ GPU_OPTIONS = {
21
+ "num_gpu": -1,
22
+ "main_gpu": 0,
23
+ "num_batch": 512,
24
+ "num_ctx": 4096,
25
+ "f16_kv": True,
26
+ "use_mmap": True,
27
+ "use_mlock": False,
28
+ "num_thread": 0,
29
+ }
30
+
31
+
32
+ def llm(
33
+ prompt: str,
34
+ system: str = NOVEL_SYSTEM_PROMPT,
35
+ temperature: float = 0.8,
36
+ ) -> str:
37
+ response = ollama.chat(
38
+ model=STORY_MODEL,
39
+ messages=[
40
+ {"role": "system", "content": system},
41
+ {"role": "user", "content": prompt},
42
+ ],
43
+ options={**GPU_OPTIONS, "temperature": temperature},
44
+ )
45
+ return response["message"]["content"]
46
+
47
+
48
+ def classify_intent(user_input: str) -> str:
49
+ text = user_input.lower().strip()
50
+ new_keywords = [
51
+ "new novel",
52
+ "new story",
53
+ "write a novel",
54
+ "create a novel",
55
+ "start a novel",
56
+ "novel idea",
57
+ ]
58
+ question_keywords = ["who", "what", "when", "where", "why", "how"]
59
+ if any(k in text for k in new_keywords):
60
+ return "NEW_NOVEL"
61
+ if "?" in text:
62
+ return "QUESTION"
63
+ if any(text.startswith(k) for k in question_keywords):
64
+ return "QUESTION"
65
+ return "CONTINUE"
66
+
67
+
68
+ def create_first_chapter(idea: str) -> str:
69
+ return llm(CREATE_NOVEL_PROMPT.format(idea=idea))
70
+
71
+
72
+ def create_chapter_plan(memory: str, instruction: str) -> str:
73
+ return llm(
74
+ CHAPTER_PLAN_PROMPT.format(memory=memory, instruction=instruction),
75
+ temperature=0.4,
76
+ )
77
+
78
+
79
+ def generate_chapter(memory: str, plan: str, instruction: str) -> str:
80
+ return llm(
81
+ CONTINUE_NOVEL_PROMPT.format(memory=memory, plan=plan, instruction=instruction)
82
+ )
83
+
84
+
85
+ def compress_memory(chapter: str) -> str:
86
+ return llm(
87
+ MEMORY_COMPRESSION_PROMPT.format(chapter=chapter),
88
+ temperature=0.2,
89
+ )
90
+
91
+
92
+ def extract_characters(chapter: str) -> list[dict]:
93
+ result = llm(
94
+ CHARACTER_EXTRACTION_PROMPT.format(chapter=chapter),
95
+ temperature=0.1,
96
+ )
97
+ try:
98
+ return json.loads(result)
99
+ except Exception:
100
+ return []
101
+
102
+
103
+ def extract_lore(chapter: str) -> str:
104
+ return llm(
105
+ LORE_EXTRACTION_PROMPT.format(chapter=chapter),
106
+ temperature=0.2,
107
+ )
108
+
109
+
110
+ def answer_story_question(context: str, question: str) -> str:
111
+ return llm(
112
+ QA_PROMPT.format(context=context, question=question),
113
+ temperature=0.1,
114
+ )
115
+
116
+
117
+ def generate_story_package(memory: str, instruction: str) -> dict:
118
+ plan = create_chapter_plan(memory, instruction)
119
+ chapter = generate_chapter(memory, plan, instruction)
120
+ summary = compress_memory(chapter)
121
+ characters = extract_characters(chapter)
122
+ lore = extract_lore(chapter)
123
+ return {
124
+ "plan": plan,
125
+ "chapter": chapter,
126
+ "summary": summary,
127
+ "characters": characters,
128
+ "lore": lore,
129
+ }