Spaces:
Running
Running
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from langchain import PromptTemplate, LLMChain
|
3 |
+
from langchain.llms import OpenAI
|
4 |
+
from transformers import pipeline, GPT2TokenizerFast
|
5 |
+
from stability_sdk import client
|
6 |
+
import stability_sdk.interfaces.gooseai.generation.generation_pb2 as generation
|
7 |
+
import openai
|
8 |
+
import torch
|
9 |
+
from fpdf import FPDF
|
10 |
+
from PIL import Image, ImageDraw, ImageFont
|
11 |
+
import numpy as np
|
12 |
+
import pandas as pd
|
13 |
+
import json
|
14 |
+
import datetime
|
15 |
+
import os
|
16 |
+
import re
|
17 |
+
import requests
|
18 |
+
import yaml
|
19 |
+
from typing import List, Dict, Any
|
20 |
+
import logging
|
21 |
+
from tqdm import tqdm
|
22 |
+
import arxiv
|
23 |
+
import wikipedia
|
24 |
+
from scholarly import scholarly
|
25 |
+
import researcher
|
26 |
+
from dotenv import load_dotenv
|
27 |
+
import tiktoken
|
28 |
+
import concurrent.futures
|
29 |
+
from pathlib import Path
|
30 |
+
|
31 |
+
# Load environment variables
|
32 |
+
load_dotenv()
|
33 |
+
|
34 |
+
# Configure logging
|
35 |
+
logging.basicConfig(
|
36 |
+
level=logging.INFO,
|
37 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
38 |
+
)
|
39 |
+
logger = logging.getLogger(__name__)
|
40 |
+
|
41 |
+
class BookMetadata:
|
42 |
+
def __init__(self, title: str, author: str, genre: str, isbn: str = None):
|
43 |
+
self.title = title
|
44 |
+
self.author = author
|
45 |
+
self.genre = genre
|
46 |
+
self.isbn = isbn or self._generate_isbn()
|
47 |
+
self.creation_date = datetime.datetime.now()
|
48 |
+
|
49 |
+
def _generate_isbn(self) -> str:
|
50 |
+
"""Generate a valid ISBN-13 number"""
|
51 |
+
# Implement ISBN generation logic
|
52 |
+
return f"978-{random.randint(1000000000, 9999999999)}"
|
53 |
+
|
54 |
+
def to_dict(self) -> dict:
|
55 |
+
return {
|
56 |
+
"title": self.title,
|
57 |
+
"author": self.author,
|
58 |
+
"genre": self.genre,
|
59 |
+
"isbn": self.isbn,
|
60 |
+
"creation_date": self.creation_date.isoformat()
|
61 |
+
}
|
62 |
+
|
63 |
+
class ResearchEngine:
|
64 |
+
def __init__(self):
|
65 |
+
self.arxiv_client = arxiv.Client()
|
66 |
+
self.wikipedia = wikipedia
|
67 |
+
self.scholarly = scholarly
|
68 |
+
|
69 |
+
async def gather_research(self, topic: str, depth: int = 3) -> Dict[str, Any]:
|
70 |
+
"""Gather research from multiple sources"""
|
71 |
+
research_data = {}
|
72 |
+
|
73 |
+
# Gather academic papers
|
74 |
+
search = arxiv.Search(
|
75 |
+
query=topic,
|
76 |
+
max_results=depth,
|
77 |
+
sort_by=arxiv.SortCriterion.Relevance
|
78 |
+
)
|
79 |
+
|
80 |
+
papers = await self.arxiv_client.results(search)
|
81 |
+
research_data["academic"] = [
|
82 |
+
{"title": paper.title, "summary": paper.summary}
|
83 |
+
for paper in papers
|
84 |
+
]
|
85 |
+
|
86 |
+
# Gather Wikipedia information
|
87 |
+
try:
|
88 |
+
wiki_content = self.wikipedia.summary(topic, sentences=depth*3)
|
89 |
+
research_data["wikipedia"] = wiki_content
|
90 |
+
except:
|
91 |
+
logger.warning(f"Could not fetch Wikipedia content for {topic}")
|
92 |
+
|
93 |
+
return research_data
|
94 |
+
|
95 |
+
class ContentGenerator:
|
96 |
+
def __init__(self, api_key: str):
|
97 |
+
self.openai = OpenAI(api_key=api_key)
|
98 |
+
self.tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
|
99 |
+
|
100 |
+
def generate_chapter_outline(self, topic: str, research_data: Dict[str, Any]) -> List[str]:
|
101 |
+
"""Generate detailed chapter outline based on research"""
|
102 |
+
prompt = self._create_outline_prompt(topic, research_data)
|
103 |
+
response = self.openai.complete(prompt)
|
104 |
+
return self._parse_outline(response)
|
105 |
+
|
106 |
+
def generate_chapter_content(self, chapter_title: str, outline: str, style: str) -> str:
|
107 |
+
"""Generate detailed chapter content"""
|
108 |
+
prompt = self._create_chapter_prompt(chapter_title, outline, style)
|
109 |
+
return self.openai.complete(prompt)
|
110 |
+
|
111 |
+
def _create_outline_prompt(self, topic: str, research_data: Dict[str, Any]) -> str:
|
112 |
+
# Implement sophisticated prompt engineering
|
113 |
+
pass
|
114 |
+
|
115 |
+
def _parse_outline(self, response: str) -> List[str]:
|
116 |
+
# Implement outline parsing logic
|
117 |
+
pass
|
118 |
+
|
119 |
+
class BookFormatter:
|
120 |
+
def __init__(self, metadata: BookMetadata):
|
121 |
+
self.metadata = metadata
|
122 |
+
self.pdf = FPDF()
|
123 |
+
self.styles = self._load_styles()
|
124 |
+
|
125 |
+
def _load_styles(self) -> dict:
|
126 |
+
"""Load book styling configurations"""
|
127 |
+
with open("styles/book_styles.yaml", "r") as f:
|
128 |
+
return yaml.safe_load(f)
|
129 |
+
|
130 |
+
def create_cover(self, prompt: str) -> Image:
|
131 |
+
"""Create AI-generated book cover"""
|
132 |
+
stability_api = client.StabilityInference(
|
133 |
+
key=os.environ['STABILITY_KEY'],
|
134 |
+
engine="stable-diffusion-xl-1024-v1-0"
|
135 |
+
)
|
136 |
+
|
137 |
+
answers = stability_api.generate(
|
138 |
+
prompt=prompt,
|
139 |
+
height=1024,
|
140 |
+
width=768,
|
141 |
+
samples=1,
|
142 |
+
)
|
143 |
+
|
144 |
+
for resp in answers:
|
145 |
+
for artifact in resp.artifacts:
|
146 |
+
if artifact.finish_reason == generation.FILTER:
|
147 |
+
logger.warning("Cover generation was filtered")
|
148 |
+
continue
|
149 |
+
|
150 |
+
if artifact.type == generation.ARTIFACT_IMAGE:
|
151 |
+
img = Image.open(io.BytesIO(artifact.binary))
|
152 |
+
return self._enhance_cover(img)
|
153 |
+
|
154 |
+
def _enhance_cover(self, cover_image: Image) -> Image:
|
155 |
+
"""Add professional finishing touches to the cover"""
|
156 |
+
# Implement cover enhancement logic
|
157 |
+
pass
|
158 |
+
|
159 |
+
def format_book(self, content: Dict[str, str]) -> None:
|
160 |
+
"""Format the book content into a professional PDF"""
|
161 |
+
self._add_cover_page()
|
162 |
+
self._add_copyright_page()
|
163 |
+
self._add_table_of_contents(content)
|
164 |
+
self._add_chapters(content)
|
165 |
+
self._add_bibliography()
|
166 |
+
|
167 |
+
def _add_table_of_contents(self, content: Dict[str, str]) -> None:
|
168 |
+
"""Create detailed table of contents"""
|
169 |
+
pass
|
170 |
+
|
171 |
+
def export_for_kindle(self) -> bytes:
|
172 |
+
"""Export the book in Kindle format"""
|
173 |
+
# Implement Kindle export logic
|
174 |
+
pass
|
175 |
+
|
176 |
+
def export_for_epub(self) -> bytes:
|
177 |
+
"""Export the book in EPUB format"""
|
178 |
+
# Implement EPUB export logic
|
179 |
+
pass
|
180 |
+
|
181 |
+
class AmazonKDPInterface:
|
182 |
+
def __init__(self, credentials: Dict[str, str]):
|
183 |
+
self.credentials = credentials
|
184 |
+
|
185 |
+
def publish_book(self, book_file: bytes, metadata: BookMetadata) -> str:
|
186 |
+
"""Publish book to Amazon KDP"""
|
187 |
+
# Implement Amazon KDP publishing logic
|
188 |
+
pass
|
189 |
+
|
190 |
+
def update_pricing(self, isbn: str, price: float) -> None:
|
191 |
+
"""Update book pricing"""
|
192 |
+
pass
|
193 |
+
|
194 |
+
def main():
|
195 |
+
st.set_page_config(page_title="Professional Book Generator", layout="wide")
|
196 |
+
|
197 |
+
# Apply custom CSS
|
198 |
+
st.markdown("""
|
199 |
+
<style>
|
200 |
+
.main {
|
201 |
+
background-color: #f5f5f5;
|
202 |
+
}
|
203 |
+
.stButton button {
|
204 |
+
background-color: #007bff;
|
205 |
+
color: white;
|
206 |
+
border-radius: 5px;
|
207 |
+
padding: 10px 20px;
|
208 |
+
}
|
209 |
+
</style>
|
210 |
+
""", unsafe_allow_html=True)
|
211 |
+
|
212 |
+
st.title("Professional AI Book Generator")
|
213 |
+
|
214 |
+
# Initialize session state
|
215 |
+
if 'book_metadata' not in st.session_state:
|
216 |
+
st.session_state.book_metadata = None
|
217 |
+
|
218 |
+
# Sidebar for configuration
|
219 |
+
with st.sidebar:
|
220 |
+
st.header("Configuration")
|
221 |
+
api_key = st.text_input("OpenAI API Key", type="password")
|
222 |
+
stability_key = st.text_input("Stability API Key", type="password")
|
223 |
+
|
224 |
+
# Main workflow
|
225 |
+
tabs = st.tabs(["Book Setup", "Content Generation", "Export & Publish"])
|
226 |
+
|
227 |
+
with tabs[0]:
|
228 |
+
st.header("Book Setup")
|
229 |
+
col1, col2 = st.columns(2)
|
230 |
+
|
231 |
+
with col1:
|
232 |
+
title = st.text_input("Book Title")
|
233 |
+
author = st.text_input("Author Name")
|
234 |
+
genre = st.selectbox("Genre", [
|
235 |
+
"Business & Leadership",
|
236 |
+
"Self-Help & Personal Development",
|
237 |
+
"Psychology & Social Sciences",
|
238 |
+
"History & Politics",
|
239 |
+
"Philosophy & Wisdom",
|
240 |
+
"Strategy & Power Dynamics"
|
241 |
+
])
|
242 |
+
|
243 |
+
with col2:
|
244 |
+
writing_style = st.selectbox("Writing Style", [
|
245 |
+
"Academic & Research-based",
|
246 |
+
"Narrative & Engaging",
|
247 |
+
"Professional & Authoritative",
|
248 |
+
"Philosophical & Thought-provoking"
|
249 |
+
])
|
250 |
+
target_audience = st.selectbox("Target Audience", [
|
251 |
+
"Business Professionals",
|
252 |
+
"Academic Readers",
|
253 |
+
"General Adult Audience",
|
254 |
+
"Leaders & Executives"
|
255 |
+
])
|
256 |
+
|
257 |
+
if st.button("Initialize Book Project"):
|
258 |
+
st.session_state.book_metadata = BookMetadata(title, author, genre)
|
259 |
+
st.success("Book project initialized!")
|
260 |
+
|
261 |
+
with tabs[1]:
|
262 |
+
if st.session_state.book_metadata:
|
263 |
+
st.header("Content Generation")
|
264 |
+
|
265 |
+
research_depth = st.slider("Research Depth", 1, 5, 3)
|
266 |
+
|
267 |
+
if st.button("Generate Content"):
|
268 |
+
with st.spinner("Researching and generating content..."):
|
269 |
+
# Initialize components
|
270 |
+
research_engine = ResearchEngine()
|
271 |
+
content_generator = ContentGenerator(api_key)
|
272 |
+
|
273 |
+
# Generate content
|
274 |
+
research_data = research_engine.gather_research(
|
275 |
+
st.session_state.book_metadata.title,
|
276 |
+
research_depth
|
277 |
+
)
|
278 |
+
|
279 |
+
outline = content_generator.generate_chapter_outline(
|
280 |
+
st.session_state.book_metadata.title,
|
281 |
+
research_data
|
282 |
+
)
|
283 |
+
|
284 |
+
st.session_state.book_content = {}
|
285 |
+
|
286 |
+
progress_bar = st.progress(0)
|
287 |
+
for i, chapter in enumerate(outline):
|
288 |
+
content = content_generator.generate_chapter_content(
|
289 |
+
chapter,
|
290 |
+
outline[i],
|
291 |
+
writing_style
|
292 |
+
)
|
293 |
+
st.session_state.book_content[chapter] = content
|
294 |
+
progress_bar.progress((i + 1) / len(outline))
|
295 |
+
|
296 |
+
st.success("Content generation complete!")
|
297 |
+
|
298 |
+
with tabs[2]:
|
299 |
+
if st.session_state.book_metadata and hasattr(st.session_state, 'book_content'):
|
300 |
+
st.header("Export & Publish")
|
301 |
+
|
302 |
+
formatter = BookFormatter(st.session_state.book_metadata)
|
303 |
+
|
304 |
+
col1, col2 = st.columns(2)
|
305 |
+
|
306 |
+
with col1:
|
307 |
+
if st.button("Generate PDF"):
|
308 |
+
with st.spinner("Formatting PDF..."):
|
309 |
+
formatter.format_book(st.session_state.book_content)
|
310 |
+
st.download_button(
|
311 |
+
"Download PDF",
|
312 |
+
formatter.pdf.output(dest='S').encode('latin-1'),
|
313 |
+
f"{st.session_state.book_metadata.title}.pdf",
|
314 |
+
"application/pdf"
|
315 |
+
)
|
316 |
+
|
317 |
+
if st.button("Generate EPUB"):
|
318 |
+
with st.spinner("Formatting EPUB..."):
|
319 |
+
epub_data = formatter.export_for_epub()
|
320 |
+
st.download_button(
|
321 |
+
"Download EPUB",
|
322 |
+
epub_data,
|
323 |
+
f"{st.session_state.book_metadata.title}.epub",
|
324 |
+
"application/epub+zip"
|
325 |
+
)
|
326 |
+
|
327 |
+
with col2:
|
328 |
+
if st.button("Publish to Amazon KDP"):
|
329 |
+
with st.spinner("Publishing..."):
|
330 |
+
kdp = AmazonKDPInterface({
|
331 |
+
"access_key": st.secrets["amazon_access_key"],
|
332 |
+
"secret_key": st.secrets["amazon_secret_key"]
|
333 |
+
})
|
334 |
+
|
335 |
+
publication_url = kdp.publish_book(
|
336 |
+
formatter.export_for_kindle(),
|
337 |
+
st.session_state.book_metadata
|
338 |
+
)
|
339 |
+
|
340 |
+
st.success(f"Published! View your book at {publication_url}")
|
341 |
+
|
342 |
+
if __name__ == "__main__":
|
343 |
+
main()
|