npc0 commited on
Commit
4967799
·
verified ·
1 Parent(s): 908e2be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +199 -419
app.py CHANGED
@@ -1,436 +1,216 @@
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
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") as demo:
23
- with gr.Row():
24
- with gr.Column(scale=2):
25
- welcome_md = gr.Markdown()
26
- with gr.Column(scale=1):
27
- login_btn = gr.LoginButton()
28
-
29
- # API Key input section (shown after login)
30
- api_key_section = gr.Column(visible=False)
31
- with api_key_section:
32
- gr.Markdown("""
33
- ### Poe API Key Setup
34
-
35
- To use this tool, you need a Poe API key:
36
-
37
- 1. Visit [https://poe.com/api_key](https://poe.com/api_key)
38
- 2. If you don't have an account, create one first
39
- 3. Generate a new API key or copy your existing one
40
- 4. Paste it in the field below
41
-
42
- **Security**: Your API key will be encrypted and tied to your login account.
43
- """)
44
- api_key_input = gr.Textbox(
45
- label="Poe API Key",
46
- placeholder="Enter your Poe API key here...",
47
- type="password"
48
- )
49
- with gr.Row():
50
- remember_key = gr.Checkbox(
51
- label="Remember my API key (encrypted storage)",
52
- value=True,
53
- info="Your key will be encrypted and saved for future sessions"
54
- )
55
- api_key_btn = gr.Button("Set API Key", variant="primary")
56
- clear_key_btn = gr.Button("Clear Saved Key", variant="secondary", visible=False)
57
 
58
- out = gr.Markdown()
59
- inp = gr.File(file_types=['.epub'], visible=False)
 
 
 
 
 
60
 
61
- # Event handlers
62
- demo.load(self.on_load, None, [welcome_md, api_key_section, inp, clear_key_btn])
63
- # Note: Login/logout events are handled automatically by Gradio
64
- # We use demo.load to check login status on page load/refresh
65
- api_key_btn.click(
66
- self.set_api_key,
67
- inputs=[api_key_input, remember_key],
68
- outputs=[out, inp, api_key_section, clear_key_btn]
69
- )
70
- clear_key_btn.click(
71
- self.clear_saved_key,
72
- outputs=[out, api_key_section, clear_key_btn, api_key_input]
73
- )
74
- inp.change(self.process, inp, out)
75
 
76
- demo.queue().launch()
77
-
78
- def _get_user_id(self, profile):
79
- """Generate a unique user ID from profile"""
80
- if not profile:
81
- return None
82
- # Use email as primary identifier, fallback to username
83
- identifier = profile.email if hasattr(profile, 'email') and profile.email else profile.username
84
- return hashlib.sha256(identifier.encode()).hexdigest()
85
-
86
- def _generate_key_from_user(self, user_id):
87
- """Generate encryption key from user ID"""
88
- # Use user ID as salt for key derivation
89
- salt = user_id.encode()[:32].ljust(32, b'0') # Ensure 32 bytes
90
- kdf = PBKDF2HMAC(
91
- algorithm=hashes.SHA256(),
92
- length=32,
93
- salt=salt,
94
- iterations=100000,
95
- )
96
- key = base64.urlsafe_b64encode(kdf.derive(user_id.encode()))
97
- return Fernet(key)
98
-
99
- def _load_user_keys(self):
100
- """Load encrypted user keys from file"""
101
- try:
102
- if os.path.exists(self.keys_file):
103
- with open(self.keys_file, 'r') as f:
104
- return json.load(f)
105
- except Exception as e:
106
- print(f"Error loading user keys: {e}")
107
- return {}
108
-
109
- def _save_user_keys(self, keys_data):
110
- """Save encrypted user keys to file"""
111
- try:
112
- with open(self.keys_file, 'w') as f:
113
- json.dump(keys_data, f)
114
- except Exception as e:
115
- print(f"Error saving user keys: {e}")
116
-
117
- def _get_saved_key(self, user_id):
118
- """Retrieve and decrypt user's API key"""
119
- try:
120
- keys_data = self._load_user_keys()
121
- if user_id in keys_data:
122
- cipher_suite = self._generate_key_from_user(user_id)
123
- encrypted_key = base64.urlsafe_b64decode(keys_data[user_id].encode())
124
- return cipher_suite.decrypt(encrypted_key).decode()
125
- except Exception as e:
126
- print(f"Error retrieving saved key: {e}")
127
- return None
128
-
129
- def _save_encrypted_key(self, user_id, api_key):
130
- """Encrypt and save user's API key"""
131
- try:
132
- keys_data = self._load_user_keys()
133
- cipher_suite = self._generate_key_from_user(user_id)
134
- encrypted_key = cipher_suite.encrypt(api_key.encode())
135
- keys_data[user_id] = base64.urlsafe_b64encode(encrypted_key).decode()
136
- self._save_user_keys(keys_data)
137
- return True
138
- except Exception as e:
139
- print(f"Error saving encrypted key: {e}")
140
- return False
141
-
142
- def _delete_saved_key(self, user_id):
143
- """Delete user's saved API key"""
144
- try:
145
- keys_data = self._load_user_keys()
146
- if user_id in keys_data:
147
- del keys_data[user_id]
148
- self._save_user_keys(keys_data)
149
- return True
150
- except Exception as e:
151
- print(f"Error deleting saved key: {e}")
152
- return False
153
-
154
- def on_load(self, request: gr.Request = None):
155
- """Handle initial page load and login status check"""
156
- # Get user info from request if available
157
- profile = getattr(request, 'username', None) if request else None
158
-
159
- if not profile:
160
- return (
161
- gr.update(value='# ePub Summarization Tool\n\nPlease login to access the tool.'),
162
- gr.update(visible=False), # api_key_section
163
- gr.update(visible=False), # inp
164
- gr.update(visible=False) # clear_key_btn
165
- )
166
 
167
- # Create a mock profile object for compatibility
168
- class MockProfile:
169
- def __init__(self, username):
170
- self.username = username
171
- self.name = username
172
- self.email = f"{username}@example.com" # Fallback email
173
-
174
- mock_profile = MockProfile(profile)
175
- return self.handle_user_login(mock_profile)
176
-
177
- def handle_user_login(self, profile):
178
- """Handle user login logic"""
179
- if not profile:
180
- return self.handle_user_logout()
181
 
182
- self.current_user = self._get_user_id(profile)
183
- user_name = profile.name if hasattr(profile, 'name') else profile.username
 
 
 
 
 
 
 
184
 
185
- # Check if user has a saved API key
186
- saved_key = self._get_saved_key(self.current_user)
187
- if saved_key:
188
- self.api_key = saved_key
189
- if self._initialize_client():
190
- return (
191
- gr.update(value=f'# ePub Summarization Tool\n\nWelcome back {user_name}! ✅ Your saved API key is loaded and ready.'),
192
- gr.update(visible=False), # api_key_section
193
- gr.update(visible=True), # inp
194
- gr.update(visible=True) # clear_key_btn
195
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
- # No saved key or failed to initialize
198
- return (
199
- gr.update(value=f'# ePub Summarization Tool\n\nWelcome {user_name}! Please set up your Poe API key below.'),
200
- gr.update(visible=True), # api_key_section
201
- gr.update(visible=False), # inp
202
- gr.update(visible=False) # clear_key_btn
203
  )
204
-
205
- def handle_user_logout(self):
206
- """Handle user logout logic"""
207
- self.current_user = None
208
- self.api_key = None
209
- self.client = None
210
 
211
- return (
212
- gr.update(value='# ePub Summarization Tool\n\nPlease login to access the tool.'),
213
- gr.update(visible=False), # api_key_section
214
- gr.update(visible=False), # inp
215
- gr.update(visible=False) # clear_key_btn
 
 
 
 
216
  )
217
-
218
- def _initialize_client(self):
219
- """Initialize the Poe API client"""
220
- try:
221
- self.client = openai.OpenAI(
222
- api_key=self.api_key,
223
- base_url="https://api.poe.com/v1",
224
- )
225
- return True
226
- except Exception as e:
227
- print(f"Error initializing Poe client: {e}")
228
- return False
229
-
230
- def set_api_key(self, api_key, remember_key):
231
- """Set and validate the API key"""
232
- if not self.current_user:
233
- return (
234
- gr.update(value="❌ Please login first."),
235
- gr.update(visible=False),
236
- gr.update(visible=True),
237
- gr.update(visible=False)
238
- )
239
-
240
- if not api_key or not api_key.strip():
241
- return (
242
- gr.update(value="⚠️ Please enter a valid API key."),
243
- gr.update(visible=False),
244
- gr.update(visible=True),
245
- gr.update(visible=False)
246
- )
247
 
248
- self.api_key = api_key.strip()
249
-
250
- if self._initialize_client():
251
- # Test the API key with a simple request
252
- try:
253
- test_chat = self.client.chat.completions.create(
254
- model=self.model_name,
255
- messages=[{"role": "user", "content": "Hello"}],
256
- max_tokens=10
257
- )
258
-
259
- # Save key if user wants to remember it
260
- if remember_key:
261
- if self._save_encrypted_key(self.current_user, self.api_key):
262
- success_msg = "✅ API key validated and saved successfully! You can now upload an ePub file."
263
- else:
264
- success_msg = "✅ API key validated successfully! (Note: Failed to save for future use)"
265
- else:
266
- success_msg = "✅ API key validated successfully! You can now upload an ePub file."
267
-
268
- return (
269
- gr.update(value=success_msg),
270
- gr.update(visible=True), # inp
271
- gr.update(visible=False), # api_key_section
272
- gr.update(visible=remember_key) # clear_key_btn
273
- )
274
- except Exception as e:
275
- error_msg = str(e)
276
- if "401" in error_msg or "unauthorized" in error_msg.lower():
277
- return (
278
- gr.update(value="❌ Invalid API key. Please check your key and try again."),
279
- gr.update(visible=False),
280
- gr.update(visible=True),
281
- gr.update(visible=False)
282
- )
283
- elif "quota" in error_msg.lower() or "limit" in error_msg.lower():
284
- return (
285
- gr.update(value="⚠️ API key valid but quota exceeded. Please check your Poe account."),
286
- gr.update(visible=False),
287
- gr.update(visible=True),
288
- gr.update(visible=False)
289
- )
290
- else:
291
- return (
292
- gr.update(value=f"❌ API connection error: {error_msg}"),
293
- gr.update(visible=False),
294
- gr.update(visible=True),
295
- gr.update(visible=False)
296
- )
297
- else:
298
- return (
299
- gr.update(value="❌ Failed to initialize API client."),
300
- gr.update(visible=False),
301
- gr.update(visible=True),
302
- gr.update(visible=False)
303
- )
304
-
305
- def clear_saved_key(self):
306
- """Clear the user's saved API key"""
307
- if not self.current_user:
308
- return (
309
- gr.update(value="❌ No user logged in."),
310
- gr.update(visible=False),
311
- gr.update(visible=False),
312
- gr.update(value="")
313
- )
314
 
315
- if self._delete_saved_key(self.current_user):
316
- self.api_key = None
317
- self.client = None
318
- return (
319
- gr.update(value="✅ Saved API key cleared. Please enter your API key below."),
320
- gr.update(visible=True), # api_key_section
321
- gr.update(visible=False), # clear_key_btn
322
- gr.update(value="") # clear input
323
- )
324
- else:
325
- return (
326
- gr.update(value="⚠️ Failed to clear saved key."),
327
- gr.update(visible=True),
328
- gr.update(visible=True),
329
- gr.update(value="")
330
- )
331
-
332
- def get_model_response(self, text: str) -> str:
333
- """
334
- Get response from Poe API for the given text
335
- """
336
- if not self.client:
337
- return "Error: API client not initialized"
338
-
339
- try:
340
- chat = self.client.chat.completions.create(
341
- model=self.model_name,
342
- messages=[{"role": "user", "content": text}],
343
- )
344
- return chat.choices[0].message.content
345
- except Exception as e:
346
- print(f"Error calling Poe API: {e}")
347
- return f"Error processing text: {str(e)}"
348
-
349
- def process(self, file, request: gr.Request = None):
350
- # Get user info from request if available
351
- profile = getattr(request, 'username', None) if request else None
352
 
353
- if profile is None:
354
- return gr.update(value='⚠️ Please login to access the tool.')
355
-
356
- if not self.client:
357
- return gr.update(value='⚠️ Please set your Poe API key first.')
358
-
359
- if file is None:
360
- return gr.update(value='Please upload an ePub file.')
361
-
362
- try:
363
- # Extract content from ePub
364
- ch_list = epub2txt(file.name, outputlist=True)
365
- chapter_titles = epub2txt.content_titles
366
- title = epub2txt.title
367
-
368
- yield gr.update(value=f"# {title}\n\nProcessing ePub file...")
369
-
370
- sm_list = []
371
-
372
- # Process each chapter (skip first 2 as they're usually metadata)
373
- for idx, text in enumerate(ch_list[2:], 1):
374
- if not text.strip():
375
- continue
376
-
377
- yield gr.update(value=f"# {title}\n\nProcessing chapter {idx}...")
378
-
379
- docs = []
380
- # Split chapter into chunks for processing
381
- chunk_size = 2000
382
- for i in range(0, len(text), chunk_size):
383
- chunk = text[i:i+2048] # Slight overlap for context
384
- if len(chunk.strip()) > 0:
385
- response = self.get_model_response(self.prompt + "\n\n" + chunk)
386
- docs.append(response)
387
-
388
- # Update UI with current progress
389
- current_summaries = "\n\n".join([
390
- f"## {ct}\n\n{sm}"
391
- for ct, sm in zip(chapter_titles[2:idx+1], sm_list + [f"Processing chunk {len(docs)}..."])
392
- ])
393
- yield gr.update(value=f"# {title}\n\n{current_summaries}")
394
-
395
- # Combine chunk summaries into chapter summary
396
- if docs:
397
- if len(docs) == 1:
398
- hist = docs[0]
399
- else:
400
- hist = docs[0]
401
- for doc in docs[1:]:
402
- combined_text = f"{self.prompt}\n\nCombine these summaries:\n\n{hist}\n\n{doc}"
403
- hist = self.get_model_response(combined_text)
404
-
405
- # Update UI with draft summary
406
- current_summaries = "\n\n".join([
407
- f"## {ct}\n\n{sm}"
408
- for ct, sm in zip(chapter_titles[2:idx+1], sm_list + [f"Draft: {hist}"])
409
- ])
410
- yield gr.update(value=f"# {title}\n\n{current_summaries}")
411
-
412
- sm_list.append(hist)
413
-
414
- # Update final output for this chapter
415
- final_summaries = "\n\n".join([
416
- f"## {ct}\n\n{sm}"
417
- for ct, sm in zip(chapter_titles[2:idx+1], sm_list)
418
- ])
419
- yield gr.update(value=f"# {title}\n\n{final_summaries}")
420
-
421
- # Final complete summary
422
- if sm_list:
423
- complete_summary = f"# {title}\n\n" + "\n\n".join([
424
- f"## {ct}\n\n{sm}"
425
- for ct, sm in zip(chapter_titles[2:len(sm_list)+2], sm_list)
426
- ])
427
- yield gr.update(value=complete_summary)
428
- else:
429
- yield gr.update(value=f"# {title}\n\nNo content found to summarize.")
430
-
431
- except Exception as e:
432
- yield gr.update(value=f"Error processing file: {str(e)}")
433
 
434
- # Run the application
435
  if __name__ == "__main__":
436
- GUI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ server_name="0.0.0.0",
214
+ server_port=7860,
215
+ show_error=True
216
+ )