BookSumBeta / app.py
npc0's picture
Update app.py
b8becf7 verified
raw
history blame
9.22 kB
import os
import openai
import gradio as gr
from epub2txt import epub2txt
class GUI:
def __init__(self, *args, **kwargs):
# Configuration
self.model_name = os.getenv("POE_MODEL", "GPT-5-mini")
self.prompt = os.getenv("prompt", "Summarize the following text:")
self.client = None
self.api_key = os.getenv("POE_API_KEY")
with gr.Blocks(title="ePub Summarizer") as demo:
with gr.Row():
gr.Markdown(scale=2).attach_load_event(self.hello, None)
gr.LoginButton()
gr.LogoutButton()
# API Key input section
with gr.Row(visible=not self.api_key):
with gr.Column():
gr.Markdown("""
### Poe API Key Required
To use this tool, you need a Poe API key:
1. Visit [https://poe.com/api_key](https://poe.com/api_key)
2. If you don't have an account, create one first
3. Generate a new API key or copy your existing one
4. Paste it in the field below
**Note**: Your API key is only stored temporarily during this session for security.
""")
api_key_input = gr.Textbox(
label="Poe API Key",
placeholder="Enter your Poe API key here...",
type="password"
)
api_key_btn = gr.Button("Set API Key", variant="primary")
out = gr.Markdown()
inp = gr.File(file_types=['.epub'], visible=bool(self.api_key))
# Set up event handlers
if not self.api_key:
api_key_btn.click(
self.set_api_key,
inputs=[api_key_input],
outputs=[out, inp]
)
else:
self._initialize_client()
inp.change(self.process, inp, out)
demo.queue(concurrency_count=2).launch()
def _initialize_client(self):
"""Initialize the Poe API client"""
try:
self.client = openai.OpenAI(
api_key=self.api_key,
base_url="https://api.poe.com/v1",
)
return True
except Exception as e:
print(f"Error initializing Poe client: {e}")
return False
def set_api_key(self, api_key):
"""Set and validate the API key"""
if not api_key or not api_key.strip():
return gr.update(value="⚠️ Please enter a valid API key."), gr.update(visible=False)
self.api_key = api_key.strip()
if self._initialize_client():
# Test the API key with a simple request
try:
test_chat = self.client.chat.completions.create(
model=self.model_name,
messages=[{"role": "user", "content": "Hello"}],
max_tokens=10
)
return (
gr.update(value="✅ API key validated successfully! You can now upload an ePub file."),
gr.update(visible=True)
)
except Exception as e:
error_msg = str(e)
if "401" in error_msg or "unauthorized" in error_msg.lower():
return (
gr.update(value="❌ Invalid API key. Please check your key and try again."),
gr.update(visible=False)
)
elif "quota" in error_msg.lower() or "limit" in error_msg.lower():
return (
gr.update(value="⚠️ API key valid but quota exceeded. Please check your Poe account."),
gr.update(visible=False)
)
else:
return (
gr.update(value=f"❌ API connection error: {error_msg}"),
gr.update(visible=False)
)
else:
return (
gr.update(value="❌ Failed to initialize API client."),
gr.update(visible=False)
)
def get_model_response(self, text: str) -> str:
"""
Get response from Poe API for the given text
"""
if not self.client:
return "Error: API client not initialized"
try:
chat = self.client.chat.completions.create(
model=self.model_name,
messages=[{"role": "user", "content": text}],
)
return chat.choices[0].message.content
except Exception as e:
print(f"Error calling Poe API: {e}")
return f"Error processing text: {str(e)}"
def process(self, file, profile: gr.OAuthProfile | None = None):
if profile is None:
return gr.update(value='Login to access the tool.')
if not self.client:
return gr.update(value='⚠️ Please set your Poe API key first.')
if file is None:
return gr.update(value='Please upload an ePub file.')
try:
# Extract content from ePub
ch_list = epub2txt(file.name, outputlist=True)
chapter_titles = epub2txt.content_titles
title = epub2txt.title
yield gr.update(value=f"# {title}\n\nProcessing ePub file...")
sm_list = []
# Process each chapter (skip first 2 as they're usually metadata)
for idx, text in enumerate(ch_list[2:], 1):
if not text.strip():
continue
yield gr.update(value=f"# {title}\n\nProcessing chapter {idx}...")
docs = []
# Split chapter into chunks for processing
chunk_size = 2000
for i in range(0, len(text), chunk_size):
chunk = text[i:i+2048] # Slight overlap for context
if len(chunk.strip()) > 0:
response = self.get_model_response(self.prompt + "\n\n" + chunk)
docs.append(response)
# Update UI with current progress
current_summaries = "\n\n".join([
f"## {ct}\n\n{sm}"
for ct, sm in zip(chapter_titles[2:idx+1], sm_list + [f"Processing chunk {len(docs)}..."])
])
yield gr.update(value=f"# {title}\n\n{current_summaries}")
# Combine chunk summaries into chapter summary
if docs:
if len(docs) == 1:
hist = docs[0]
else:
hist = docs[0]
for doc in docs[1:]:
combined_text = f"{self.prompt}\n\nCombine these summaries:\n\n{hist}\n\n{doc}"
hist = self.get_model_response(combined_text)
# Update UI with draft summary
current_summaries = "\n\n".join([
f"## {ct}\n\n{sm}"
for ct, sm in zip(chapter_titles[2:idx+1], sm_list + [f"Draft: {hist}"])
])
yield gr.update(value=f"# {title}\n\n{current_summaries}")
sm_list.append(hist)
# Update final output for this chapter
final_summaries = "\n\n".join([
f"## {ct}\n\n{sm}"
for ct, sm in zip(chapter_titles[2:idx+1], sm_list)
])
yield gr.update(value=f"# {title}\n\n{final_summaries}")
# Final complete summary
if sm_list:
complete_summary = f"# {title}\n\n" + "\n\n".join([
f"## {ct}\n\n{sm}"
for ct, sm in zip(chapter_titles[2:len(sm_list)+2], sm_list)
])
yield gr.update(value=complete_summary)
else:
yield gr.update(value=f"# {title}\n\nNo content found to summarize.")
except Exception as e:
yield gr.update(value=f"Error processing file: {str(e)}")
def hello(self, profile: gr.OAuthProfile | None = None):
if profile is None:
return '# ePub Summarization Tool\n\nLogin to access the tool.'
if not self.api_key:
return f"# ePub Summarization Tool\n\nWelcome {profile.name}!\n\nPlease set your Poe API key below to get started."
return f"# ePub Summarization Tool\n\nWelcome {profile.name}! Ready to summarize ePub files."
# Run the application
if __name__ == "__main__":
GUI()