cryogenic22 commited on
Commit
b308c3d
·
verified ·
1 Parent(s): 7905285

Update selfapi_writer.py

Browse files
Files changed (1) hide show
  1. selfapi_writer.py +194 -73
selfapi_writer.py CHANGED
@@ -1,21 +1,20 @@
1
- # File: selfapi_writer.py
2
-
3
  from anthropic import Anthropic
4
  import streamlit as st
5
  import json
6
  import os
7
- from typing import Dict, Any, Optional
 
8
 
9
  class SelfApiWriter:
10
  def __init__(self):
11
  """Initialize the Self.api writer"""
12
  # Try to get API key from environment variables first, then from secrets
13
-
14
  ANTHROPIC_API_KEY = os.getenv('api_key')
15
 
16
- """Initialize Claude service with API key from HuggingFace secrets"""
17
  if not ANTHROPIC_API_KEY:
18
- raise ValueError("Anthropic API key not found in HuggingFace secrets. Please ensure ANTHROPIC_API_KEY is set in your space's secrets.")
 
19
  self.client = Anthropic(api_key=ANTHROPIC_API_KEY)
20
  self.model = "claude-3-opus-20240229"
21
  self.context = {}
@@ -23,9 +22,73 @@ class SelfApiWriter:
23
  self.writing_guidelines = None
24
  self.initialized = False
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  def process_blueprint(self, blueprint: str) -> Dict[str, Any]:
27
  """Process blueprint to extract complete writing guidelines and structure"""
28
  try:
 
 
 
29
  system_prompt = """You are an expert book planner analyzing a blueprint.
30
  Extract ALL relevant information and return it in a structured format.
31
  Include:
@@ -60,17 +123,19 @@ class SelfApiWriter:
60
  "chapter_structure": ["Required chapter components"],
61
  "content_requirements": ["Specific content requirements"],
62
  "practical_elements": ["Required practical elements"]
63
- }
 
64
  }"""
65
 
66
  prompt = f"""Analyze this book blueprint and extract ALL information:
67
 
68
- {blueprint}
 
 
69
 
70
  Return only the JSON structure without any additional text."""
71
 
72
  response = self.client.messages.create(
73
- #model="claude-3-sonnet-20240229",
74
  model=self.model,
75
  max_tokens=4000,
76
  temperature=0,
@@ -80,6 +145,9 @@ class SelfApiWriter:
80
 
81
  extracted_info = json.loads(response.content[0].text)
82
 
 
 
 
83
  # Store extracted information
84
  self.book_info = extracted_info["book_info"]
85
  self.book_structure = extracted_info["structure"]
@@ -93,42 +161,63 @@ class SelfApiWriter:
93
  return None
94
 
95
  def write_introduction(self) -> str:
96
- """Generate the book's introduction based on extracted guidelines"""
97
  if not self.initialized:
98
  raise ValueError("Writer not initialized. Process blueprint first.")
99
 
100
  try:
101
- system_prompt = f"""You are writing the introduction for '{self.book_info["title"]}'
102
- Core Vision: {self.book_info["vision"]}
103
- Target Audience: {self.book_info["target_audience"]}
104
-
105
- Writing Style: {self.writing_guidelines["style"]}
106
- Tone: {self.writing_guidelines["tone"]}
107
-
108
- Content Requirements:
109
- {', '.join(self.writing_guidelines["content_requirements"])}"""
110
 
111
- intro_prompt = f"""Write the introduction: "{self.book_structure['introduction']}"
112
-
113
- Create an engaging opening that:
114
- 1. Introduces the book's core concept
115
- 2. Speaks directly to the target audience
116
- 3. Outlines the book's approach and structure
117
- 4. Sets the tone for the entire book
118
-
119
- Follow ALL provided guidelines for style, tone, and content."""
120
-
121
- response = self.client.messages.create(
122
- model=self.model,
123
- max_tokens=20000,
124
- temperature=0.7,
125
- system=system_prompt,
126
- messages=[{"role": "user", "content": intro_prompt}]
127
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- intro_content = response.content[0].text
130
- self.context['introduction'] = intro_content
131
- return intro_content
132
 
133
  except Exception as e:
134
  st.error(f"Error generating introduction: {str(e)}")
@@ -140,49 +229,81 @@ class SelfApiWriter:
140
  raise ValueError("Writer not initialized. Process blueprint first.")
141
 
142
  try:
 
 
 
143
  part = self.book_structure["parts"][part_idx]
144
  chapter_title = part["chapters"][chapter_idx]
145
  part_title = part["title"]
146
- previous_chapter = self.context.get(f'part_{part_idx}_chapter_{chapter_idx-1}', '')
147
-
148
- system_prompt = f"""You are writing '{self.book_info["title"]}'
149
- Target Audience: {self.book_info["target_audience"]}
150
- Writing Style: {self.writing_guidelines["style"]}
151
- Tone: {self.writing_guidelines["tone"]}
152
 
153
- Chapter Structure Requirements:
154
- {', '.join(self.writing_guidelines["chapter_structure"])}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
- Content Requirements:
157
- {', '.join(self.writing_guidelines["content_requirements"])}
 
158
 
159
- Practical Elements to Include:
160
- {', '.join(self.writing_guidelines["practical_elements"])}"""
161
-
162
- chapter_prompt = f"""
163
- Write Chapter: "{chapter_title}" in Part {part_idx + 1}: "{part_title}"
164
 
165
- Previous Chapter Context: {previous_chapter[:1000] if previous_chapter else 'Starting new part'}
166
-
167
- Follow ALL provided guidelines for:
168
- 1. Structure and organization
169
- 2. Style and tone
170
- 3. Practical elements and exercises
171
- 4. Content depth and requirements
172
 
173
- Begin writing the complete chapter now."""
174
-
175
- response = self.client.messages.create(
176
- model=self.model,
177
- max_tokens=50000,
178
- temperature=0.7,
179
- system=system_prompt,
180
- messages=[{"role": "user", "content": chapter_prompt}]
181
- )
182
 
183
- chapter_content = response.content[0].text
184
- self.context[f'part_{part_idx}_chapter_{chapter_idx}'] = chapter_content
185
- return chapter_content
186
 
187
  except Exception as e:
188
  st.error(f"Error generating chapter: {str(e)}")
 
 
 
1
  from anthropic import Anthropic
2
  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):
10
  """Initialize the Self.api writer"""
11
  # Try to get API key from environment variables first, then from secrets
 
12
  ANTHROPIC_API_KEY = os.getenv('api_key')
13
 
14
+ """Initialize Claude service with API key"""
15
  if not ANTHROPIC_API_KEY:
16
+ raise ValueError("Anthropic API key not found. Please ensure ANTHROPIC_API_KEY is set.")
17
+
18
  self.client = Anthropic(api_key=ANTHROPIC_API_KEY)
19
  self.model = "claude-3-opus-20240229"
20
  self.context = {}
 
22
  self.writing_guidelines = None
23
  self.initialized = False
24
 
25
+ # Configuration for generation
26
+ self.pages_per_chapter = 70 # Approximately 35,000 words
27
+ self.words_per_page = 500
28
+ self.max_iterations = 20
29
+ self.max_tokens = 40000
30
+
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
37
+
38
+ :param blueprint: Full blueprint text
39
+ :param max_tokens: Maximum tokens to keep
40
+ :return: Tuple of (truncated_blueprint, overview_summary)
41
+ """
42
+ # Tokenize the blueprint
43
+ tokens = self.tokenizer.encode(blueprint)
44
+
45
+ # If within token limit, return full blueprint
46
+ if len(tokens) <= max_tokens:
47
+ return blueprint, ""
48
+
49
+ # Try to preserve key sections intelligently
50
+ truncated_tokens = tokens[:max_tokens]
51
+ truncated_blueprint = self.tokenizer.decode(truncated_tokens)
52
+
53
+ # Generate a summary of the truncated sections
54
+ try:
55
+ overview_response = self.client.messages.create(
56
+ model=self.model,
57
+ max_tokens=1000,
58
+ messages=[
59
+ {
60
+ "role": "system",
61
+ "content": "You are an expert at creating concise summaries of book blueprints."
62
+ },
63
+ {
64
+ "role": "user",
65
+ "content": f"""The following blueprint was truncated due to length constraints.
66
+ Please create a comprehensive overview that captures the essence of the
67
+ truncated sections:
68
+
69
+ Truncated Blueprint Ending:
70
+ {blueprint[len(truncated_blueprint):]}
71
+
72
+ Provide a summary that:
73
+ 1. Captures key themes and intentions
74
+ 2. Highlights main sections that were cut off
75
+ 3. Ensures no critical information is lost
76
+ 4. Is concise but comprehensive"""
77
+ }
78
+ ]
79
+ )
80
+ overview_summary = overview_response.content[0].text
81
+ except Exception as e:
82
+ overview_summary = f"Note: Some blueprint content was truncated. Original blueprint exceeded {max_tokens} tokens."
83
+
84
+ return truncated_blueprint, overview_summary
85
+
86
  def process_blueprint(self, blueprint: str) -> Dict[str, Any]:
87
  """Process blueprint to extract complete writing guidelines and structure"""
88
  try:
89
+ # Truncate blueprint if too long
90
+ truncated_blueprint, overview_summary = self._truncate_blueprint(blueprint)
91
+
92
  system_prompt = """You are an expert book planner analyzing a blueprint.
93
  Extract ALL relevant information and return it in a structured format.
94
  Include:
 
123
  "chapter_structure": ["Required chapter components"],
124
  "content_requirements": ["Specific content requirements"],
125
  "practical_elements": ["Required practical elements"]
126
+ },
127
+ "overview_summary": "Summary of truncated sections"
128
  }"""
129
 
130
  prompt = f"""Analyze this book blueprint and extract ALL information:
131
 
132
+ {truncated_blueprint}
133
+
134
+ {overview_summary}
135
 
136
  Return only the JSON structure without any additional text."""
137
 
138
  response = self.client.messages.create(
 
139
  model=self.model,
140
  max_tokens=4000,
141
  temperature=0,
 
145
 
146
  extracted_info = json.loads(response.content[0].text)
147
 
148
+ # Store full original blueprint for reference
149
+ extracted_info['full_original_blueprint'] = blueprint
150
+
151
  # Store extracted information
152
  self.book_info = extracted_info["book_info"]
153
  self.book_structure = extracted_info["structure"]
 
161
  return None
162
 
163
  def write_introduction(self) -> str:
164
+ """Generate the book's introduction"""
165
  if not self.initialized:
166
  raise ValueError("Writer not initialized. Process blueprint first.")
167
 
168
  try:
169
+ # Retrieve full original blueprint
170
+ full_blueprint = self.context.get('full_original_blueprint', '')
 
 
 
 
 
 
 
171
 
172
+ # Iterative generation for introduction
173
+ full_intro_content = ""
174
+ for iteration in range(1, self.max_iterations + 1):
175
+ response = self.client.messages.create(
176
+ model=self.model,
177
+ max_tokens=2000,
178
+ temperature=0.7,
179
+ messages=[
180
+ {
181
+ "role": "system",
182
+ "content": f"""You are writing the introduction for '{self.book_info.get('title', 'Untitled Book')}'
183
+ Full Blueprint Context:
184
+ {full_blueprint}
185
+
186
+ Core Vision: {self.book_info.get('vision', '')}
187
+ Target Audience: {self.book_info.get('target_audience', '')}
188
+
189
+ Writing Style: {self.writing_guidelines.get('style', 'Academic and clear')}
190
+ Tone: {self.writing_guidelines.get('tone', 'Professional')}
191
+ """
192
+ },
193
+ {
194
+ "role": "user",
195
+ "content": f"""Write the introduction: "{self.book_structure.get('introduction', 'Book Introduction')}"
196
+
197
+ Iteration {iteration} of {self.max_iterations}
198
+
199
+ Create an engaging opening that:
200
+ 1. Introduces the book's core concept
201
+ 2. Speaks directly to the target audience
202
+ 3. Outlines the book's approach and structure
203
+ 4. Sets the tone for the entire book
204
+
205
+ Follow ALL provided guidelines for style, tone, and content."""
206
+ }
207
+ ]
208
+ )
209
+
210
+ new_content = response.content[0].text
211
+ full_intro_content += new_content
212
+
213
+ # Stopping criteria
214
+ if (len(full_intro_content.split()) > self.pages_per_chapter * self.words_per_page
215
+ or iteration == self.max_iterations):
216
+ break
217
 
218
+ # Store and return
219
+ self.context['introduction'] = full_intro_content
220
+ return full_intro_content
221
 
222
  except Exception as e:
223
  st.error(f"Error generating introduction: {str(e)}")
 
229
  raise ValueError("Writer not initialized. Process blueprint first.")
230
 
231
  try:
232
+ # Retrieve full original blueprint
233
+ full_blueprint = self.context.get('full_original_blueprint', '')
234
+
235
  part = self.book_structure["parts"][part_idx]
236
  chapter_title = part["chapters"][chapter_idx]
237
  part_title = part["title"]
 
 
 
 
 
 
238
 
239
+ # Iterative generation for chapter
240
+ full_chapter_content = ""
241
+ for iteration in range(1, self.max_iterations + 1):
242
+ response = self.client.messages.create(
243
+ model=self.model,
244
+ max_tokens=2000,
245
+ temperature=0.7,
246
+ messages=[
247
+ {
248
+ "role": "system",
249
+ "content": f"""You are writing '{self.book_info.get('title', 'Untitled Book')}'
250
+ Full Blueprint Context:
251
+ {full_blueprint}
252
+
253
+ Chapter: {chapter_title}
254
+ Part: {part_title}
255
+
256
+ Target Audience: {self.book_info.get('target_audience', '')}
257
+ Writing Style: {self.writing_guidelines.get('style', 'Academic and clear')}
258
+ Tone: {self.writing_guidelines.get('tone', 'Professional')}
259
+
260
+ Chapter Structure Requirements:
261
+ {', '.join(self.writing_guidelines.get('chapter_structure', []))}
262
+
263
+ Content Requirements:
264
+ {', '.join(self.writing_guidelines.get('content_requirements', []))}"""
265
+ },
266
+ {
267
+ "role": "user",
268
+ "content": f"""Write Chapter: "{chapter_title}" in Part {part_idx + 1}: "{part_title}"
269
+
270
+ Iteration {iteration} of {self.max_iterations}
271
+
272
+ Follow ALL provided guidelines for:
273
+ 1. Structure and organization
274
+ 2. Style and tone
275
+ 3. Practical elements and exercises
276
+ 4. Content depth and requirements
277
+
278
+ Begin writing the complete chapter now."""
279
+ }
280
+ ]
281
+ )
282
+
283
+ new_content = response.content[0].text
284
+ full_chapter_content += new_content
285
+
286
+ # Stopping criteria
287
+ if (len(full_chapter_content.split()) > self.pages_per_chapter * self.words_per_page
288
+ or iteration == self.max_iterations):
289
+ break
290
 
291
+ # Store in context
292
+ if 'parts' not in self.context:
293
+ self.context['parts'] = []
294
 
295
+ while len(self.context['parts']) <= part_idx:
296
+ self.context['parts'].append({'chapters': []})
 
 
 
297
 
298
+ while len(self.context['parts'][part_idx]['chapters']) <= chapter_idx:
299
+ self.context['parts'][part_idx]['chapters'].append({})
 
 
 
 
 
300
 
301
+ self.context['parts'][part_idx]['chapters'][chapter_idx] = {
302
+ 'title': chapter_title,
303
+ 'content': full_chapter_content
304
+ }
 
 
 
 
 
305
 
306
+ return full_chapter_content
 
 
307
 
308
  except Exception as e:
309
  st.error(f"Error generating chapter: {str(e)}")