cryogenic22 commited on
Commit
8db68e1
·
verified ·
1 Parent(s): 2996e1f

Update selfapi_writer.py

Browse files
Files changed (1) hide show
  1. selfapi_writer.py +227 -188
selfapi_writer.py CHANGED
@@ -3,7 +3,7 @@ import streamlit as st
3
  import json
4
  import os
5
  import tiktoken
6
- from typing import Dict, Any, Optional, List, Tuple
7
 
8
  class SelfApiWriter:
9
  def __init__(self):
@@ -31,6 +31,55 @@ class SelfApiWriter:
31
  # Token encoding
32
  self.tokenizer = tiktoken.encoding_for_model("gpt-4")
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  def _truncate_blueprint(self, blueprint: str, max_tokens: int = 15000) -> Tuple[str, str]:
35
  """
36
  Intelligently truncate the blueprint to fit within token limits
@@ -83,79 +132,81 @@ class SelfApiWriter:
83
  def process_blueprint(self, blueprint: str) -> Dict[str, Any]:
84
  """Process blueprint to extract complete writing guidelines and structure"""
85
  try:
86
- # Truncate blueprint if too long
87
- truncated_blueprint, overview_summary = self._truncate_blueprint(blueprint)
 
 
88
 
89
- # Prepare system prompt for blueprint processing
90
- system_prompt = """You are an expert book planner analyzing a blueprint.
91
- Extract ALL relevant information and return it in a structured format.
92
- Include:
93
- 1. Book title and high-level information
94
- 2. Complete structure (introduction, parts, chapters)
95
- 3. All writing style guidelines
96
- 4. Content requirements and constraints
97
- 5. Target audience details
98
- 6. Chapter structure requirements
99
- 7. Tone and voice requirements
100
- 8. Any other relevant guidelines or requirements
101
-
102
- Return a JSON structure with the following format:
103
- {
104
- "book_info": {
105
- "title": "Book title",
106
- "vision": "Core vision/purpose",
107
- "target_audience": "Detailed audience description"
108
- },
109
- "structure": {
110
- "introduction": "Introduction title",
111
- "parts": [
112
- {
113
- "title": "Part title",
114
- "chapters": ["Chapter 1 title", "Chapter 2 title", ...]
115
- }
116
- ]
117
- },
118
- "guidelines": {
119
- "style": "Writing style description",
120
- "tone": "Tone requirements",
121
- "chapter_structure": ["Required chapter components"],
122
- "content_requirements": ["Specific content requirements"],
123
- "practical_elements": ["Required practical elements"]
124
- },
125
- "overview_summary": "Summary of truncated sections"
126
- }"""
127
 
128
- prompt = f"""Analyze this book blueprint and extract ALL information:
129
 
130
- {truncated_blueprint}
131
 
132
- {overview_summary}
133
 
134
- Return only the JSON structure without any additional text."""
135
 
136
- response = self.client.messages.create(
137
- model=self.model,
138
- max_tokens=4000,
139
- temperature=0,
140
- system=system_prompt,
141
- messages=[{"role": "user", "content": prompt}]
142
- )
143
-
144
- extracted_info = json.loads(response.content[0].text)
145
-
146
- # Store full original blueprint for reference
147
- extracted_info['full_original_blueprint'] = blueprint
148
-
149
- # Store extracted information
150
- self.book_info = extracted_info["book_info"]
151
- self.book_structure = extracted_info["structure"]
152
- self.writing_guidelines = extracted_info["guidelines"]
153
- self.initialized = True
154
-
155
- # Store full blueprint in context
156
- self.context['full_original_blueprint'] = blueprint
157
-
158
- return extracted_info
159
 
160
  except Exception as e:
161
  st.error(f"Error processing blueprint: {str(e)}")
@@ -166,149 +217,137 @@ class SelfApiWriter:
166
  if not self.initialized:
167
  raise ValueError("Writer not initialized. Process blueprint first.")
168
 
169
- try:
 
170
  # Retrieve full original blueprint
171
  full_blueprint = self.context.get('full_original_blueprint', '')
172
 
173
- # Iterative generation for introduction
174
- full_intro_content = ""
175
- for iteration in range(1, self.max_iterations + 1):
176
- # Prepare system prompt with full context
177
- system_prompt = f"""You are writing the introduction for '{self.book_info.get('title', 'Untitled Book')}'
178
- Full Blueprint Context:
179
- {full_blueprint}
180
-
181
- Core Vision: {self.book_info.get('vision', '')}
182
- Target Audience: {self.book_info.get('target_audience', '')}
183
-
184
- Writing Style: {self.writing_guidelines.get('style', 'Academic and clear')}
185
- Tone: {self.writing_guidelines.get('tone', 'Professional')}
186
- """
187
-
188
- response = self.client.messages.create(
189
- model=self.model,
190
- max_tokens=2000,
191
- temperature=0.7,
192
- system=system_prompt,
193
- messages=[
194
- {
195
- "role": "user",
196
- "content": f"""Write the introduction: "{self.book_structure.get('introduction', 'Book Introduction')}"
197
-
198
- Iteration {iteration} of {self.max_iterations}
199
-
200
- Create an engaging opening that:
201
- 1. Introduces the book's core concept
202
- 2. Speaks directly to the target audience
203
- 3. Outlines the book's approach and structure
204
- 4. Sets the tone for the entire book
205
-
206
- Follow ALL provided guidelines for style, tone, and content."""
207
- }
208
- ]
209
- )
210
-
211
- new_content = response.content[0].text
212
- full_intro_content += new_content
213
-
214
- # Stopping criteria
215
- if (len(full_intro_content.split()) > self.pages_per_chapter * self.words_per_page
216
- or iteration == self.max_iterations):
217
- break
218
 
219
- # Store and return
220
- self.context['introduction'] = full_intro_content
221
- return full_intro_content
222
 
223
- except Exception as e:
224
- st.error(f"Error generating introduction: {str(e)}")
225
- return f"Error generating introduction: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
  def write_chapter(self, part_idx: int, chapter_idx: int) -> str:
228
  """Generate a chapter using extracted guidelines"""
229
  if not self.initialized:
230
  raise ValueError("Writer not initialized. Process blueprint first.")
231
 
232
- try:
 
233
  # Retrieve full original blueprint
234
  full_blueprint = self.context.get('full_original_blueprint', '')
235
 
236
  part = self.book_structure["parts"][part_idx]
237
  chapter_title = part["chapters"][chapter_idx]
238
  part_title = part["title"]
239
-
240
- # Iterative generation for chapter
241
- full_chapter_content = ""
242
- for iteration in range(1, self.max_iterations + 1):
243
- # Prepare system prompt with full context
244
- system_prompt = f"""You are writing '{self.book_info.get('title', 'Untitled Book')}'
245
- Full Blueprint Context:
246
- {full_blueprint}
247
-
248
- Chapter: {chapter_title}
249
- Part: {part_title}
250
-
251
- Target Audience: {self.book_info.get('target_audience', '')}
252
- Writing Style: {self.writing_guidelines.get('style', 'Academic and clear')}
253
- Tone: {self.writing_guidelines.get('tone', 'Professional')}
254
-
255
- Chapter Structure Requirements:
256
- {', '.join(self.writing_guidelines.get('chapter_structure', []))}
257
-
258
- Content Requirements:
259
- {', '.join(self.writing_guidelines.get('content_requirements', []))}"""
260
 
261
- response = self.client.messages.create(
262
- model=self.model,
263
- max_tokens=2000,
264
- temperature=0.7,
265
- system=system_prompt,
266
- messages=[
267
- {
268
- "role": "user",
269
- "content": f"""Write Chapter: "{chapter_title}" in Part {part_idx + 1}: "{part_title}"
270
-
271
- Iteration {iteration} of {self.max_iterations}
272
-
273
- Follow ALL provided guidelines for:
274
- 1. Structure and organization
275
- 2. Style and tone
276
- 3. Practical elements and exercises
277
- 4. Content depth and requirements
278
-
279
- Begin writing the complete chapter now."""
280
- }
281
- ]
282
- )
283
-
284
- new_content = response.content[0].text
285
- full_chapter_content += new_content
286
-
287
- # Stopping criteria
288
- if (len(full_chapter_content.split()) > self.pages_per_chapter * self.words_per_page
289
- or iteration == self.max_iterations):
290
- break
291
-
292
- # Store in context
293
- if 'parts' not in self.context:
294
- self.context['parts'] = []
295
 
296
- while len(self.context['parts']) <= part_idx:
297
- self.context['parts'].append({'chapters': []})
298
 
299
- while len(self.context['parts'][part_idx]['chapters']) <= chapter_idx:
300
- self.context['parts'][part_idx]['chapters'].append({})
 
301
 
302
- self.context['parts'][part_idx]['chapters'][chapter_idx] = {
303
- 'title': chapter_title,
304
- 'content': full_chapter_content
305
- }
306
 
307
- return full_chapter_content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
- except Exception as e:
310
- st.error(f"Error generating chapter: {str(e)}")
311
- return f"Error generating chapter {chapter_title}: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
 
313
  def get_current_structure(self) -> Optional[Dict[str, Any]]:
314
  """Get current book structure and guidelines"""
 
3
  import json
4
  import os
5
  import tiktoken
6
+ from typing import Dict, Any, Optional, List, Tuple, Generator
7
 
8
  class SelfApiWriter:
9
  def __init__(self):
 
31
  # Token encoding
32
  self.tokenizer = tiktoken.encoding_for_model("gpt-4")
33
 
34
+ def _generate_with_progress(self,
35
+ generate_func: callable,
36
+ title: str,
37
+ total_steps: int = 20) -> str:
38
+ """
39
+ Generate content with progress tracking
40
+
41
+ :param generate_func: Function to generate content
42
+ :param title: Title for progress display
43
+ :param total_steps: Total number of generation steps
44
+ :return: Generated content
45
+ """
46
+ # Create Streamlit progress bar
47
+ progress_bar = st.progress(0, text=f"Generating {title}...")
48
+
49
+ # Tracking variables
50
+ full_content = ""
51
+
52
+ try:
53
+ for iteration in range(1, total_steps + 1):
54
+ # Update progress
55
+ progress = iteration / total_steps
56
+ progress_bar.progress(
57
+ min(int(progress * 100), 100),
58
+ text=f"Generating {title}... (Iteration {iteration}/{total_steps})"
59
+ )
60
+
61
+ # Generate content
62
+ new_content = generate_func(iteration)
63
+ full_content += new_content
64
+
65
+ # Stopping criteria
66
+ if (len(full_content.split()) > self.pages_per_chapter * self.words_per_page
67
+ or iteration == total_steps):
68
+ break
69
+
70
+ # Complete progress
71
+ progress_bar.progress(100, text=f"Finished generating {title}")
72
+
73
+ return full_content
74
+
75
+ except Exception as e:
76
+ st.error(f"Error generating {title}: {e}")
77
+ progress_bar.empty()
78
+ return f"Error generating {title}: {e}"
79
+ finally:
80
+ # Ensure progress bar is cleared
81
+ progress_bar.empty()
82
+
83
  def _truncate_blueprint(self, blueprint: str, max_tokens: int = 15000) -> Tuple[str, str]:
84
  """
85
  Intelligently truncate the blueprint to fit within token limits
 
132
  def process_blueprint(self, blueprint: str) -> Dict[str, Any]:
133
  """Process blueprint to extract complete writing guidelines and structure"""
134
  try:
135
+ # Add a spinner during blueprint processing
136
+ with st.spinner("Processing blueprint..."):
137
+ # Truncate blueprint if too long
138
+ truncated_blueprint, overview_summary = self._truncate_blueprint(blueprint)
139
 
140
+ # Prepare system prompt for blueprint processing
141
+ system_prompt = """You are an expert book planner analyzing a blueprint.
142
+ Extract ALL relevant information and return it in a structured format.
143
+ Include:
144
+ 1. Book title and high-level information
145
+ 2. Complete structure (introduction, parts, chapters)
146
+ 3. All writing style guidelines
147
+ 4. Content requirements and constraints
148
+ 5. Target audience details
149
+ 6. Chapter structure requirements
150
+ 7. Tone and voice requirements
151
+ 8. Any other relevant guidelines or requirements
152
+
153
+ Return a JSON structure with the following format:
154
+ {
155
+ "book_info": {
156
+ "title": "Book title",
157
+ "vision": "Core vision/purpose",
158
+ "target_audience": "Detailed audience description"
159
+ },
160
+ "structure": {
161
+ "introduction": "Introduction title",
162
+ "parts": [
163
+ {
164
+ "title": "Part title",
165
+ "chapters": ["Chapter 1 title", "Chapter 2 title", ...]
166
+ }
167
+ ]
168
+ },
169
+ "guidelines": {
170
+ "style": "Writing style description",
171
+ "tone": "Tone requirements",
172
+ "chapter_structure": ["Required chapter components"],
173
+ "content_requirements": ["Specific content requirements"],
174
+ "practical_elements": ["Required practical elements"]
175
+ },
176
+ "overview_summary": "Summary of truncated sections"
177
+ }"""
178
 
179
+ prompt = f"""Analyze this book blueprint and extract ALL information:
180
 
181
+ {truncated_blueprint}
182
 
183
+ {overview_summary}
184
 
185
+ Return only the JSON structure without any additional text."""
186
 
187
+ response = self.client.messages.create(
188
+ model=self.model,
189
+ max_tokens=4000,
190
+ temperature=0,
191
+ system=system_prompt,
192
+ messages=[{"role": "user", "content": prompt}]
193
+ )
194
+
195
+ extracted_info = json.loads(response.content[0].text)
196
+
197
+ # Store full original blueprint for reference
198
+ extracted_info['full_original_blueprint'] = blueprint
199
+
200
+ # Store extracted information
201
+ self.book_info = extracted_info["book_info"]
202
+ self.book_structure = extracted_info["structure"]
203
+ self.writing_guidelines = extracted_info["guidelines"]
204
+ self.initialized = True
205
+
206
+ # Store full blueprint in context
207
+ self.context['full_original_blueprint'] = blueprint
208
+
209
+ return extracted_info
210
 
211
  except Exception as e:
212
  st.error(f"Error processing blueprint: {str(e)}")
 
217
  if not self.initialized:
218
  raise ValueError("Writer not initialized. Process blueprint first.")
219
 
220
+ def generate_intro_iteration(iteration: int) -> str:
221
+ """Generate a single iteration of the introduction"""
222
  # Retrieve full original blueprint
223
  full_blueprint = self.context.get('full_original_blueprint', '')
224
 
225
+ # Prepare system prompt with full context
226
+ system_prompt = f"""You are writing the introduction for '{self.book_info.get('title', 'Untitled Book')}'
227
+ Full Blueprint Context:
228
+ {full_blueprint}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ Core Vision: {self.book_info.get('vision', '')}
231
+ Target Audience: {self.book_info.get('target_audience', '')}
 
232
 
233
+ Writing Style: {self.writing_guidelines.get('style', 'Academic and clear')}
234
+ Tone: {self.writing_guidelines.get('tone', 'Professional')}
235
+ """
236
+
237
+ response = self.client.messages.create(
238
+ model=self.model,
239
+ max_tokens=2000,
240
+ temperature=0.7,
241
+ system=system_prompt,
242
+ messages=[
243
+ {
244
+ "role": "user",
245
+ "content": f"""Write the introduction: "{self.book_structure.get('introduction', 'Book Introduction')}"
246
+
247
+ Iteration {iteration} of {self.max_iterations}
248
+
249
+ Create an engaging opening that:
250
+ 1. Introduces the book's core concept
251
+ 2. Speaks directly to the target audience
252
+ 3. Outlines the book's approach and structure
253
+ 4. Sets the tone for the entire book
254
+
255
+ Follow ALL provided guidelines for style, tone, and content."""
256
+ }
257
+ ]
258
+ )
259
+
260
+ return response.content[0].text
261
+
262
+ # Generate with progress tracking
263
+ full_intro_content = self._generate_with_progress(
264
+ generate_intro_iteration,
265
+ "Introduction"
266
+ )
267
+
268
+ # Store and return
269
+ self.context['introduction'] = full_intro_content
270
+ return full_intro_content
271
 
272
  def write_chapter(self, part_idx: int, chapter_idx: int) -> str:
273
  """Generate a chapter using extracted guidelines"""
274
  if not self.initialized:
275
  raise ValueError("Writer not initialized. Process blueprint first.")
276
 
277
+ def generate_chapter_iteration(iteration: int) -> str:
278
+ """Generate a single iteration of the chapter"""
279
  # Retrieve full original blueprint
280
  full_blueprint = self.context.get('full_original_blueprint', '')
281
 
282
  part = self.book_structure["parts"][part_idx]
283
  chapter_title = part["chapters"][chapter_idx]
284
  part_title = part["title"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ # Prepare system prompt with full context
287
+ system_prompt = f"""You are writing '{self.book_info.get('title', 'Untitled Book')}'
288
+ Full Blueprint Context:
289
+ {full_blueprint}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
+ Chapter: {chapter_title}
292
+ Part: {part_title}
293
 
294
+ Target Audience: {self.book_info.get('target_audience', '')}
295
+ Writing Style: {self.writing_guidelines.get('style', 'Academic and clear')}
296
+ Tone: {self.writing_guidelines.get('tone', 'Professional')}
297
 
298
+ Chapter Structure Requirements:
299
+ {', '.join(self.writing_guidelines.get('chapter_structure', []))}
 
 
300
 
301
+ Content Requirements:
302
+ {', '.join(self.writing_guidelines.get('content_requirements', []))}"""
303
+
304
+ response = self.client.messages.create(
305
+ model=self.model,
306
+ max_tokens=2000,
307
+ temperature=0.7,
308
+ system=system_prompt,
309
+ messages=[
310
+ {
311
+ "role": "user",
312
+ "content": f"""Write Chapter: "{chapter_title}" in Part {part_idx + 1}: "{part_title}"
313
+
314
+ Iteration {iteration} of {self.max_iterations}
315
+
316
+ Follow ALL provided guidelines for:
317
+ 1. Structure and organization
318
+ 2. Style and tone
319
+ 3. Practical elements and exercises
320
+ 4. Content depth and requirements
321
+
322
+ Begin writing the complete chapter now."""
323
+ }
324
+ ]
325
+ )
326
 
327
+ return response.content[0].text
328
+
329
+ # Generate with progress tracking
330
+ full_chapter_content = self._generate_with_progress(
331
+ generate_chapter_iteration,
332
+ f"Chapter: {self.book_structure['parts'][part_idx]['chapters'][chapter_idx]}"
333
+ )
334
+
335
+ # Store in context
336
+ if 'parts' not in self.context:
337
+ self.context['parts'] = []
338
+
339
+ while len(self.context['parts']) <= part_idx:
340
+ self.context['parts'].append({'chapters': []})
341
+
342
+ while len(self.context['parts'][part_idx]['chapters']) <= chapter_idx:
343
+ self.context['parts'][part_idx]['chapters'].append({})
344
+
345
+ self.context['parts'][part_idx]['chapters'][chapter_idx] = {
346
+ 'title': self.book_structure['parts'][part_idx]['chapters'][chapter_idx],
347
+ 'content': full_chapter_content
348
+ }
349
+
350
+ return full_chapter_content
351
 
352
  def get_current_structure(self) -> Optional[Dict[str, Any]]:
353
  """Get current book structure and guidelines"""