Spaces:
Sleeping
Sleeping
| 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() |