npc0 commited on
Commit
212347a
Β·
verified Β·
1 Parent(s): b512500

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +429 -199
app.py CHANGED
@@ -1,215 +1,445 @@
1
- import gradio as gr
2
- import requests
3
- import json
4
  import os
5
- from datetime import datetime
6
-
7
- # API configuration
8
- GROQ_API_KEY = os.getenv('GROQ_API_KEY')
9
- headers = {
10
- "Authorization": f"Bearer {GROQ_API_KEY}",
11
- "Content-Type": "application/json"
12
- }
13
-
14
- def get_summary(user_input, conversation_id, request: gr.Request):
15
- """Generate summary using GROQ API"""
16
- if not user_input.strip():
17
- return "Please enter some text to summarize.", conversation_id
18
-
19
- try:
20
- # GROQ API call
21
- url = "https://api.groq.com/openai/v1/chat/completions"
22
-
23
- data = {
24
- "model": "mixtral-8x7b-32768",
25
- "messages": [
26
- {
27
- "role": "system",
28
- "content": "You are a helpful assistant that provides concise and informative summaries of text."
29
- },
30
- {
31
- "role": "user",
32
- "content": f"Please provide a comprehensive summary of the following text:\n\n{user_input}"
33
- }
34
- ],
35
- "max_tokens": 1000,
36
- "temperature": 0.3
37
- }
38
-
39
- response = requests.post(url, headers=headers, json=data)
40
 
41
- if response.status_code == 200:
42
- result = response.json()
43
- summary = result['choices'][0]['message']['content']
 
 
44
 
45
- # Add to conversation history
46
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
47
- conversation_id.append({
48
- "timestamp": timestamp,
49
- "input": user_input[:100] + "..." if len(user_input) > 100 else user_input,
50
- "summary": summary
51
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- return summary, conversation_id
54
- else:
55
- return f"Error: {response.status_code} - {response.text}", conversation_id
56
 
57
- except Exception as e:
58
- return f"An error occurred: {str(e)}", conversation_id
59
-
60
- def format_history(conversation_id):
61
- """Format conversation history for display"""
62
- if not conversation_id:
63
- return "No summaries generated yet."
64
-
65
- history = ""
66
- for i, item in enumerate(conversation_id, 1):
67
- history += f"**Summary {i}** ({item['timestamp']})\n"
68
- history += f"*Input:* {item['input']}\n"
69
- history += f"*Summary:* {item['summary']}\n\n---\n\n"
70
-
71
- return history
72
-
73
- # Custom CSS for better styling
74
- css = """
75
- .gradio-container {
76
- max-width: 1200px !important;
77
- }
78
- .summary-input {
79
- min-height: 200px;
80
- }
81
- .summary-output {
82
- min-height: 150px;
83
- }
84
- .history-panel {
85
- max-height: 400px;
86
- overflow-y: auto;
87
- }
88
- """
89
-
90
- # Create the main interface
91
- def create_app():
92
- with gr.Blocks(
93
- css=css,
94
- title="πŸ“š BookSum Beta - AI Text Summarizer",
95
- theme=gr.themes.Soft()
96
- ) as app:
97
-
98
- # Initialize conversation state
99
- conversation_state = gr.State([])
100
-
101
- # Header
102
- gr.Markdown(
103
- """
104
- # πŸ“š BookSum Beta
105
- ## AI-Powered Text Summarization Tool
106
 
107
- Enter any text below and get an intelligent, comprehensive summary powered by advanced AI.
108
- """
 
 
 
109
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- with gr.Row():
112
- with gr.Column(scale=2):
113
- # Input section
114
- with gr.Group():
115
- gr.Markdown("### πŸ“ Input Text")
116
- text_input = gr.Textbox(
117
- label="Enter text to summarize",
118
- placeholder="Paste your text, article, or document here...",
119
- lines=10,
120
- elem_classes=["summary-input"]
121
- )
122
-
123
- with gr.Row():
124
- summarize_btn = gr.Button(
125
- "✨ Generate Summary",
126
- variant="primary",
127
- size="lg"
128
- )
129
- clear_btn = gr.Button("πŸ—‘οΈ Clear", variant="secondary")
130
-
131
- # Output section
132
- with gr.Group():
133
- gr.Markdown("### πŸ“„ Summary")
134
- summary_output = gr.Textbox(
135
- label="Generated Summary",
136
- lines=8,
137
- elem_classes=["summary-output"],
138
- interactive=False
139
- )
140
-
141
- with gr.Column(scale=1):
142
- # History sidebar
143
- with gr.Group():
144
- gr.Markdown("### πŸ“œ History")
145
- history_display = gr.Markdown(
146
- "No summaries generated yet.",
147
- elem_classes=["history-panel"]
148
- )
149
- refresh_history_btn = gr.Button("πŸ”„ Refresh History", size="sm")
150
 
151
- # Footer
152
- gr.Markdown(
153
- """
154
- ---
155
- *Powered by GROQ API | Built with Gradio 5*
156
- """
157
- )
 
 
 
 
158
 
159
- # Event handlers
160
- summarize_btn.click(
161
- fn=get_summary,
162
- inputs=[text_input, conversation_state],
163
- outputs=[summary_output, conversation_state]
164
- ).then(
165
- fn=format_history,
166
- inputs=[conversation_state],
167
- outputs=[history_display]
168
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
- clear_btn.click(
171
- lambda: ("", ""),
172
- outputs=[text_input, summary_output]
173
- )
 
 
 
174
 
175
- refresh_history_btn.click(
176
- fn=format_history,
177
- inputs=[conversation_state],
178
- outputs=[history_display]
179
- )
180
 
181
- # Allow Enter key to submit (but only if text is not empty)
182
- text_input.submit(
183
- fn=get_summary,
184
- inputs=[text_input, conversation_state],
185
- outputs=[summary_output, conversation_state]
186
- ).then(
187
- fn=format_history,
188
- inputs=[conversation_state],
189
- outputs=[history_display]
190
- )
191
-
192
- return app
193
-
194
- # Authentication function
195
- def authenticate(username, password):
196
- """Simple authentication - replace with your logic"""
197
- # For demo purposes - replace with your actual authentication
198
- valid_users = {
199
- "admin": "password123",
200
- "user": "userpass",
201
- "demo": "demo"
202
- }
203
- return username in valid_users and valid_users[username] == password
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
 
205
  if __name__ == "__main__":
206
- app = create_app()
207
-
208
- # Launch with authentication
209
- app.launch(
210
- auth=authenticate,
211
- auth_message="Please log in to access BookSum Beta",
212
- share=False,
213
- ssr_mode=False,
214
- show_error=True
215
- )
 
 
 
 
1
  import os
2
+ import json
3
+ import hashlib
4
+ from cryptography.fernet import Fernet
5
+ from cryptography.hazmat.primitives import hashes
6
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
7
+ import base64
8
+ import openai
9
+ import gradio as gr
10
+ from epub2txt import epub2txt
11
+
12
+ class GUI:
13
+ def __init__(self, *args, **kwargs):
14
+ # Configuration - restored your original settings
15
+ self.model_name = os.getenv("POE_MODEL", "GPT-5-mini")
16
+ self.prompt = os.getenv("prompt", "Summarize the following text:")
17
+ self.client = None
18
+ self.api_key = os.getenv("POE_API_KEY")
19
+ self.current_user = None
20
+ self.keys_file = "user_keys.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ with gr.Blocks(title="ePub Summarizer", css=self._get_css()) as demo:
23
+ with gr.Row():
24
+ welcome_md = gr.Markdown(scale=2)
25
+ # Updated for Gradio 5 - removed LogoutButton (LoginButton transforms automatically)
26
+ login_btn = gr.LoginButton()
27
 
28
+ # API Key input section (shown after login)
29
+ api_key_section = gr.Column(visible=False)
30
+ with api_key_section:
31
+ gr.Markdown("""
32
+ ### Poe API Key Setup
33
+
34
+ To use this tool, you need a Poe API key:
35
+
36
+ 1. Visit [https://poe.com/api_key](https://poe.com/api_key)
37
+ 2. If you don't have an account, create one first
38
+ 3. Generate a new API key or copy your existing one
39
+ 4. Paste it in the field below
40
+
41
+ **Security**: Your API key will be encrypted and tied to your login account.
42
+ """)
43
+ api_key_input = gr.Textbox(
44
+ label="Poe API Key",
45
+ placeholder="Enter your Poe API key here...",
46
+ type="password"
47
+ )
48
+ with gr.Row():
49
+ remember_key = gr.Checkbox(
50
+ label="Remember my API key (encrypted storage)",
51
+ value=True,
52
+ info="Your key will be encrypted and saved for future sessions"
53
+ )
54
+ api_key_btn = gr.Button("Set API Key", variant="primary")
55
+ clear_key_btn = gr.Button("Clear Saved Key", variant="secondary", visible=False)
56
 
57
+ out = gr.Markdown()
58
+ inp = gr.File(file_types=['.epub'], visible=False, label="Upload ePub File")
 
59
 
60
+ # Event handlers - Updated for Gradio 5
61
+ demo.load(
62
+ fn=self.on_load,
63
+ outputs=[welcome_md, api_key_section, inp, clear_key_btn]
64
+ )
65
+
66
+ # Updated login handling for Gradio 5
67
+ login_btn.click(
68
+ fn=self.handle_login_click,
69
+ outputs=[welcome_md, api_key_section, inp, clear_key_btn]
70
+ )
71
+
72
+ api_key_btn.click(
73
+ self.set_api_key,
74
+ inputs=[api_key_input, remember_key],
75
+ outputs=[out, inp, api_key_section, clear_key_btn]
76
+ )
77
+ clear_key_btn.click(
78
+ self.clear_saved_key,
79
+ outputs=[out, api_key_section, clear_key_btn, api_key_input]
80
+ )
81
+ # Your original ePub processing
82
+ inp.change(self.process, inputs=[inp], outputs=[out])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ # Updated launch for Gradio 5
85
+ demo.queue().launch(
86
+ share=True,
87
+ ssr_mode=False, # Disable SSR to avoid i18n issues
88
+ show_error=True
89
  )
90
+
91
+ def _get_css(self):
92
+ """Custom CSS for better styling"""
93
+ return """
94
+ .gradio-container {
95
+ max-width: 1200px !important;
96
+ }
97
+ .epub-processor {
98
+ border: 2px dashed #ccc;
99
+ border-radius: 10px;
100
+ padding: 20px;
101
+ text-align: center;
102
+ }
103
+ .progress-display {
104
+ background: #f8f9fa;
105
+ padding: 15px;
106
+ border-radius: 8px;
107
+ margin: 10px 0;
108
+ }
109
+ """
110
+
111
+ def _get_user_id(self, request):
112
+ """Generate a unique user ID from request - Updated for Gradio 5"""
113
+ if not hasattr(request, 'username') or not request.username:
114
+ return None
115
+ # Use username as identifier
116
+ identifier = request.username
117
+ return hashlib.sha256(identifier.encode()).hexdigest()
118
+
119
+ def _generate_key_from_user(self, user_id):
120
+ """Generate encryption key from user ID"""
121
+ # Use user ID as salt for key derivation
122
+ salt = user_id.encode()[:32].ljust(32, b'0') # Ensure 32 bytes
123
+ kdf = PBKDF2HMAC(
124
+ algorithm=hashes.SHA256(),
125
+ length=32,
126
+ salt=salt,
127
+ iterations=100000,
128
+ )
129
+ key = base64.urlsafe_b64encode(kdf.derive(user_id.encode()))
130
+ return Fernet(key)
131
+
132
+ def _load_user_keys(self):
133
+ """Load encrypted user keys from file"""
134
+ try:
135
+ if os.path.exists(self.keys_file):
136
+ with open(self.keys_file, 'r') as f:
137
+ return json.load(f)
138
+ except Exception as e:
139
+ print(f"Error loading user keys: {e}")
140
+ return {}
141
+
142
+ def _save_user_keys(self, keys_data):
143
+ """Save encrypted user keys to file"""
144
+ try:
145
+ with open(self.keys_file, 'w') as f:
146
+ json.dump(keys_data, f)
147
+ except Exception as e:
148
+ print(f"Error saving user keys: {e}")
149
+
150
+ def _get_saved_key(self, user_id):
151
+ """Retrieve and decrypt user's API key"""
152
+ try:
153
+ keys_data = self._load_user_keys()
154
+ if user_id in keys_data:
155
+ cipher_suite = self._generate_key_from_user(user_id)
156
+ encrypted_key = base64.urlsafe_b64decode(keys_data[user_id].encode())
157
+ return cipher_suite.decrypt(encrypted_key).decode()
158
+ except Exception as e:
159
+ print(f"Error retrieving saved key: {e}")
160
+ return None
161
+
162
+ def _save_encrypted_key(self, user_id, api_key):
163
+ """Encrypt and save user's API key"""
164
+ try:
165
+ keys_data = self._load_user_keys()
166
+ cipher_suite = self._generate_key_from_user(user_id)
167
+ encrypted_key = cipher_suite.encrypt(api_key.encode())
168
+ keys_data[user_id] = base64.urlsafe_b64encode(encrypted_key).decode()
169
+ self._save_user_keys(keys_data)
170
+ return True
171
+ except Exception as e:
172
+ print(f"Error saving encrypted key: {e}")
173
+ return False
174
+
175
+ def _delete_saved_key(self, user_id):
176
+ """Delete user's saved API key"""
177
+ try:
178
+ keys_data = self._load_user_keys()
179
+ if user_id in keys_data:
180
+ del keys_data[user_id]
181
+ self._save_user_keys(keys_data)
182
+ return True
183
+ except Exception as e:
184
+ print(f"Error deleting saved key: {e}")
185
+ return False
186
+
187
+ def on_load(self, request: gr.Request = None):
188
+ """Handle initial page load - Updated for Gradio 5"""
189
+ if not hasattr(request, 'username') or not request.username:
190
+ return (
191
+ gr.update(value='# ePub Summarization Tool\n\nPlease login to access the tool.'),
192
+ gr.update(visible=False), # api_key_section
193
+ gr.update(visible=False), # inp
194
+ gr.update(visible=False) # clear_key_btn
195
+ )
196
+ return self.handle_login_click(request)
197
+
198
+ def handle_login_click(self, request: gr.Request = None):
199
+ """Handle user login - Updated for Gradio 5"""
200
+ if not hasattr(request, 'username') or not request.username:
201
+ return (
202
+ gr.update(value='# ePub Summarization Tool\n\nPlease login to access the tool.'),
203
+ gr.update(visible=False), # api_key_section
204
+ gr.update(visible=False), # inp
205
+ gr.update(visible=False) # clear_key_btn
206
+ )
207
 
208
+ self.current_user = self._get_user_id(request)
209
+ user_name = request.username
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
+ # Check if user has a saved API key
212
+ saved_key = self._get_saved_key(self.current_user)
213
+ if saved_key:
214
+ self.api_key = saved_key
215
+ if self._initialize_client():
216
+ return (
217
+ gr.update(value=f'# ePub Summarization Tool\n\nWelcome back {user_name}! βœ… Your saved API key is loaded and ready.'),
218
+ gr.update(visible=False), # api_key_section
219
+ gr.update(visible=True), # inp
220
+ gr.update(visible=True) # clear_key_btn
221
+ )
222
 
223
+ # No saved key or failed to initialize
224
+ return (
225
+ gr.update(value=f'# ePub Summarization Tool\n\nWelcome {user_name}! Please set up your Poe API key below.'),
226
+ gr.update(visible=True), # api_key_section
227
+ gr.update(visible=False), # inp
228
+ gr.update(visible=False) # clear_key_btn
 
 
 
229
  )
230
+
231
+ def _initialize_client(self):
232
+ """Initialize the Poe API client"""
233
+ try:
234
+ self.client = openai.OpenAI(
235
+ api_key=self.api_key,
236
+ base_url="https://api.poe.com/v1",
237
+ )
238
+ return True
239
+ except Exception as e:
240
+ print(f"Error initializing Poe client: {e}")
241
+ return False
242
+
243
+ def set_api_key(self, api_key, remember_key):
244
+ """Set and validate the API key"""
245
+ if not self.current_user:
246
+ return (
247
+ gr.update(value="❌ Please login first."),
248
+ gr.update(visible=False),
249
+ gr.update(visible=True),
250
+ gr.update(visible=False)
251
+ )
252
 
253
+ if not api_key or not api_key.strip():
254
+ return (
255
+ gr.update(value="⚠️ Please enter a valid API key."),
256
+ gr.update(visible=False),
257
+ gr.update(visible=True),
258
+ gr.update(visible=False)
259
+ )
260
 
261
+ self.api_key = api_key.strip()
 
 
 
 
262
 
263
+ if self._initialize_client():
264
+ # Test the API key with a simple request
265
+ try:
266
+ test_chat = self.client.chat.completions.create(
267
+ model=self.model_name,
268
+ messages=[{"role": "user", "content": "Hello"}],
269
+ max_tokens=10
270
+ )
271
+
272
+ # Save key if user wants to remember it
273
+ if remember_key:
274
+ if self._save_encrypted_key(self.current_user, self.api_key):
275
+ success_msg = "βœ… API key validated and saved successfully! You can now upload an ePub file."
276
+ else:
277
+ success_msg = "βœ… API key validated successfully! (Note: Failed to save for future use)"
278
+ else:
279
+ success_msg = "βœ… API key validated successfully! You can now upload an ePub file."
280
+
281
+ return (
282
+ gr.update(value=success_msg),
283
+ gr.update(visible=True), # inp
284
+ gr.update(visible=False), # api_key_section
285
+ gr.update(visible=remember_key) # clear_key_btn
286
+ )
287
+ except Exception as e:
288
+ error_msg = str(e)
289
+ if "401" in error_msg or "unauthorized" in error_msg.lower():
290
+ return (
291
+ gr.update(value="❌ Invalid API key. Please check your key and try again."),
292
+ gr.update(visible=False),
293
+ gr.update(visible=True),
294
+ gr.update(visible=False)
295
+ )
296
+ elif "quota" in error_msg.lower() or "limit" in error_msg.lower():
297
+ return (
298
+ gr.update(value="⚠️ API key valid but quota exceeded. Please check your Poe account."),
299
+ gr.update(visible=False),
300
+ gr.update(visible=True),
301
+ gr.update(visible=False)
302
+ )
303
+ else:
304
+ return (
305
+ gr.update(value=f"❌ API connection error: {error_msg}"),
306
+ gr.update(visible=False),
307
+ gr.update(visible=True),
308
+ gr.update(visible=False)
309
+ )
310
+ else:
311
+ return (
312
+ gr.update(value="❌ Failed to initialize API client."),
313
+ gr.update(visible=False),
314
+ gr.update(visible=True),
315
+ gr.update(visible=False)
316
+ )
317
+
318
+ def clear_saved_key(self):
319
+ """Clear the user's saved API key"""
320
+ if not self.current_user:
321
+ return (
322
+ gr.update(value="❌ No user logged in."),
323
+ gr.update(visible=False),
324
+ gr.update(visible=False),
325
+ gr.update(value="")
326
+ )
327
+
328
+ if self._delete_saved_key(self.current_user):
329
+ self.api_key = None
330
+ self.client = None
331
+ return (
332
+ gr.update(value="βœ… Saved API key cleared. Please enter your API key below."),
333
+ gr.update(visible=True), # api_key_section
334
+ gr.update(visible=False), # clear_key_btn
335
+ gr.update(value="") # clear input
336
+ )
337
+ else:
338
+ return (
339
+ gr.update(value="⚠️ Failed to clear saved key."),
340
+ gr.update(visible=True),
341
+ gr.update(visible=True),
342
+ gr.update(value="")
343
+ )
344
+
345
+ def get_model_response(self, text: str) -> str:
346
+ """Get response from Poe API for the given text - Your original function"""
347
+ if not self.client:
348
+ return "Error: API client not initialized"
349
+
350
+ try:
351
+ chat = self.client.chat.completions.create(
352
+ model=self.model_name,
353
+ messages=[{"role": "user", "content": text}],
354
+ )
355
+ return chat.choices[0].message.content
356
+ except Exception as e:
357
+ print(f"Error calling Poe API: {e}")
358
+ return f"Error processing text: {str(e)}"
359
+
360
+ def process(self, file, request: gr.Request = None):
361
+ """Your original ePub processing workflow - restored completely"""
362
+ if not hasattr(request, 'username') or not request.username:
363
+ return gr.update(value='⚠️ Please login to access the tool.')
364
+
365
+ if not self.client:
366
+ return gr.update(value='⚠️ Please set your Poe API key first.')
367
+
368
+ if file is None:
369
+ return gr.update(value='Please upload an ePub file.')
370
+
371
+ try:
372
+ # Extract content from ePub - Your original workflow
373
+ ch_list = epub2txt(file.name, outputlist=True)
374
+ chapter_titles = epub2txt.content_titles
375
+ title = epub2txt.title
376
+
377
+ yield gr.update(value=f"# {title}\n\nProcessing ePub file...")
378
+
379
+ sm_list = []
380
+
381
+ # Process each chapter (skip first 2 as they're usually metadata) - Your original logic
382
+ for idx, text in enumerate(ch_list[2:], 1):
383
+ if not text.strip():
384
+ continue
385
+
386
+ yield gr.update(value=f"# {title}\n\nProcessing chapter {idx}...")
387
+
388
+ docs = []
389
+ # Split chapter into chunks for processing - Your original chunking strategy
390
+ chunk_size = 2000
391
+ for i in range(0, len(text), chunk_size):
392
+ chunk = text[i:i+2048] # Slight overlap for context
393
+ if len(chunk.strip()) > 0:
394
+ response = self.get_model_response(self.prompt + "\n\n" + chunk)
395
+ docs.append(response)
396
+
397
+ # Update UI with current progress
398
+ current_summaries = "\n\n".join([
399
+ f"## {ct}\n\n{sm}"
400
+ for ct, sm in zip(chapter_titles[2:idx+1], sm_list + [f"Processing chunk {len(docs)}..."])
401
+ ])
402
+ yield gr.update(value=f"# {title}\n\n{current_summaries}")
403
+
404
+ # Combine chunk summaries into chapter summary - Your original combining logic
405
+ if docs:
406
+ if len(docs) == 1:
407
+ hist = docs[0]
408
+ else:
409
+ hist = docs[0]
410
+ for doc in docs[1:]:
411
+ combined_text = f"{self.prompt}\n\nCombine these summaries:\n\n{hist}\n\n{doc}"
412
+ hist = self.get_model_response(combined_text)
413
+
414
+ # Update UI with draft summary
415
+ current_summaries = "\n\n".join([
416
+ f"## {ct}\n\n{sm}"
417
+ for ct, sm in zip(chapter_titles[2:idx+1], sm_list + [f"Draft: {hist}"])
418
+ ])
419
+ yield gr.update(value=f"# {title}\n\n{current_summaries}")
420
+
421
+ sm_list.append(hist)
422
+
423
+ # Update final output for this chapter
424
+ final_summaries = "\n\n".join([
425
+ f"## {ct}\n\n{sm}"
426
+ for ct, sm in zip(chapter_titles[2:idx+1], sm_list)
427
+ ])
428
+ yield gr.update(value=f"# {title}\n\n{final_summaries}")
429
+
430
+ # Final complete summary - Your original final output
431
+ if sm_list:
432
+ complete_summary = f"# {title}\n\n" + "\n\n".join([
433
+ f"## {ct}\n\n{sm}"
434
+ for ct, sm in zip(chapter_titles[2:len(sm_list)+2], sm_list)
435
+ ])
436
+ yield gr.update(value=complete_summary)
437
+ else:
438
+ yield gr.update(value=f"# {title}\n\nNo content found to summarize.")
439
+
440
+ except Exception as e:
441
+ yield gr.update(value=f"Error processing file: {str(e)}")
442
 
443
+ # Run the application
444
  if __name__ == "__main__":
445
+ GUI()