npc0 commited on
Commit
f3d8d40
·
verified ·
1 Parent(s): 6caf7dd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -225
app.py CHANGED
@@ -1,232 +1,67 @@
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
- # --- Configuration and Constants ---
13
- MODEL_NAME = os.getenv("POE_MODEL", "GPT-5-mini")
14
- PROMPT = os.getenv("prompt", "Summarize the following text:")
15
- print(PROMPT)
16
- KEYS_FILE = "user_keys.json"
17
-
18
- # --- Helper Functions for Security and User Management ---
19
- # By moving logic out of a class, we avoid complex state issues.
20
-
21
- def get_user_id(username):
22
- """Generates a unique, stable ID from a username."""
23
- return hashlib.sha256(username.encode()).hexdigest()
24
-
25
- def generate_fernet_key(user_id):
26
- """Generates a deterministic encryption key from a user ID."""
27
- salt = user_id.encode()[:16].ljust(16, b'\0') # Salt must be 16 bytes
28
- kdf = PBKDF2HMAC(
29
- algorithm=hashes.SHA256(),
30
- length=32,
31
- salt=salt,
32
- iterations=100000,
33
- )
34
- key = base64.urlsafe_b64encode(kdf.derive(user_id.encode()))
35
- return Fernet(key)
36
-
37
- def load_user_keys():
38
- """Loads the entire user key database."""
39
- if os.path.exists(KEYS_FILE):
40
- with open(KEYS_FILE, 'r') as f:
41
- return json.load(f)
42
- return {}
43
-
44
- def save_user_keys(keys_data):
45
- """Saves the entire user key database."""
46
- with open(KEYS_FILE, 'w') as f:
47
- json.dump(keys_data, f)
48
-
49
- def get_decrypted_api_key(user_id):
50
- """Retrieves and decrypts a single user's API key."""
51
- keys_data = load_user_keys()
52
- encrypted_key_b64 = keys_data.get(user_id)
53
- if encrypted_key_b64:
54
- try:
55
- cipher = generate_fernet_key(user_id)
56
- encrypted_key = base64.urlsafe_b64decode(encrypted_key_b64.encode())
57
- return cipher.decrypt(encrypted_key).decode()
58
- except Exception as e:
59
- print(f"Decryption failed for user {user_id}: {e}")
60
- return None
61
-
62
- def set_encrypted_api_key(user_id, api_key):
63
- """Encrypts and saves a single user's API key."""
64
- keys_data = load_user_keys()
65
- cipher = generate_fernet_key(user_id)
66
- encrypted_key = cipher.encrypt(api_key.encode())
67
- keys_data[user_id] = base64.urlsafe_b64encode(encrypted_key).decode()
68
- save_user_keys(keys_data)
69
-
70
- def delete_api_key(user_id):
71
- """Deletes a user's API key."""
72
- keys_data = load_user_keys()
73
- if user_id in keys_data:
74
- del keys_data[user_id]
75
- save_user_keys(keys_data)
76
 
77
- # --- Gradio Event Handlers ---
 
 
 
78
 
79
- def initialize_client(api_key):
80
- """Initializes and returns an OpenAI client or None on failure."""
81
- if not api_key:
82
- return None
83
- try:
84
- client = openai.OpenAI(api_key=api_key, base_url="https://api.poe.com/v1")
85
- # Test connection
86
- client.models.list()
87
- return client
88
- except Exception as e:
89
- print(f"Failed to initialize client: {e}")
90
- return None
91
 
92
- def update_ui_on_load(request: gr.Request):
93
  """
94
- This function runs after the user logs in and the page loads.
95
- It configures the UI based on whether the user has a saved API key.
96
- """
97
- user_id = get_user_id(request.username)
98
- api_key = get_decrypted_api_key(user_id)
99
- client = initialize_client(api_key)
100
-
101
- if client:
102
- # Key is valid and loaded
103
- welcome_msg = f"Welcome back, **{request.username}**! Your saved API key is loaded."
104
- return {
105
- welcome_md: gr.update(value=welcome_msg),
106
- api_key_section: gr.update(visible=False),
107
- inp: gr.update(visible=True),
108
- clear_key_btn: gr.update(visible=True)
109
- }
110
- else:
111
- # No key or invalid key found
112
- welcome_msg = f"Welcome, **{request.username}**! Please provide your Poe API key to begin."
113
- return {
114
- welcome_md: gr.update(value=welcome_msg),
115
- api_key_section: gr.update(visible=True),
116
- inp: gr.update(visible=False),
117
- clear_key_btn: gr.update(visible=False)
118
- }
119
-
120
- def set_api_key(api_key, remember_key, request: gr.Request):
121
- """Handles the 'Set API Key' button click."""
122
- user_id = get_user_id(request.username)
123
- client = initialize_client(api_key)
124
-
125
- if not client:
126
- return {out: gr.update(value="❌ Invalid or incorrect API key. Please check it and try again.")}
127
-
128
- if remember_key:
129
- set_encrypted_api_key(user_id, api_key)
130
- msg = "✅ API key validated and saved! You can now upload an ePub."
131
- else:
132
- delete_api_key(user_id) # Ensure no old key is stored if user unchecks
133
- msg = "✅ API key validated for this session. You can now upload an ePub."
134
-
135
- return {
136
- out: gr.update(value=msg),
137
- inp: gr.update(visible=True),
138
- api_key_section: gr.update(visible=False),
139
- clear_key_btn: gr.update(visible=remember_key)
140
- }
141
-
142
- def clear_saved_key(request: gr.Request):
143
- """Handles the 'Clear Saved Key' button click."""
144
- user_id = get_user_id(request.username)
145
- delete_api_key(user_id)
146
- return {
147
- out: gr.update(value="✅ Saved API key cleared. Please enter an API key to continue."),
148
- api_key_section: gr.update(visible=True),
149
- inp: gr.update(visible=False),
150
- clear_key_btn: gr.update(visible=False),
151
- api_key_input: gr.update(value="")
152
- }
153
-
154
- def process_epub(file, request: gr.Request):
155
- """Processes the uploaded ePub file. This is a generator function."""
156
- user_id = get_user_id(request.username)
157
- api_key = get_decrypted_api_key(user_id)
158
-
159
- # Re-initialize client to ensure API key is available for this long-running task
160
- client = initialize_client(api_key)
161
- if not client:
162
- yield "⚠️ Your API key is missing or invalid. Please clear and set your key again."
163
- return
164
-
165
- # ... (The rest of your ePub processing logic is identical) ...
166
- try:
167
- ch_list = epub2txt(file.name, outputlist=True)
168
- title = epub2txt.title
169
- yield f"# {title}\n\nProcessing ePub file..."
170
- sm_list = []
171
- for text in ch_list[2:]:
172
- if text.strip():
173
- response = client.chat.completions.create(
174
- model=MODEL_NAME,
175
- messages=[{"role": "user", "content": PROMPT + "\n\n" + text[:4000]}], # Simple chunking
176
- ).choices[0].message.content
177
- sm_list.append(response)
178
-
179
- yield f"# {title}\n\n" + "\n\n---\n\n".join(sm_list)
180
-
181
- except Exception as e:
182
- yield f"An error occurred: {e}"
183
-
184
-
185
- # --- Build the Gradio Interface ---
186
-
187
- with gr.Blocks(title="ePub Summarizer") as demo:
188
- welcome_md = gr.Markdown("Welcome! Please log in to continue.")
189
-
190
- # API Key Section (initially hidden, made visible by update_ui_on_load)
191
- with gr.Column(visible=False) as api_key_section:
192
- api_key_input = gr.Textbox(label="Poe API Key", type="password")
193
- remember_key = gr.Checkbox(label="Remember my API key", value=True)
194
- api_key_btn = gr.Button("Set API Key", variant="primary")
195
-
196
- # Main App Section
197
- out = gr.Markdown()
198
- inp = gr.File(file_types=['.epub'], visible=False, label="Upload ePub File")
199
- clear_key_btn = gr.Button("Clear Saved Key", variant="secondary", visible=False)
200
-
201
- # --- Event Wiring ---
202
-
203
- # 1. After login, the 'load' event fires, calling update_ui_on_load
204
- demo.load(
205
- fn=update_ui_on_load,
206
- outputs=[welcome_md, api_key_section, inp, clear_key_btn]
207
- )
208
-
209
- # 2. User interactions trigger other handlers
210
- api_key_btn.click(
211
- fn=set_api_key,
212
- inputs=[api_key_input, remember_key],
213
- outputs=[out, inp, api_key_section, clear_key_btn]
214
- )
215
- clear_key_btn.click(
216
- fn=clear_saved_key,
217
- outputs=[out, api_key_section, inp, clear_key_btn, api_key_input]
218
- )
219
- inp.change(fn=process_epub, inputs=[inp], outputs=[out])
220
-
221
- # --- Main Execution ---
222
-
223
- if __name__ == "__main__":
224
- # This dummy function is used for local testing.
225
- # On Hugging Face Spaces, their login system is used automatically.
226
- def auth(username, password):
227
- return True
228
 
229
- # The 'auth' parameter is passed to launch(), not as a separate method call.
230
- demo.queue().launch(auth=auth,
231
- ssr_mode=False, # Disable SSR to avoid i18n issues
232
- show_error=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
2
 
3
+ # --- Main Execution ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ if __name__ == "__main__":
6
+ # Define the message and URLs for the notice
7
+ notice_message = """
8
+ ## Important Notice: Application Migration
9
 
10
+ This application has been successfully ported to Poe.com!
11
+ We recommend using the new versions for an enhanced experience.
 
 
 
 
 
 
 
 
 
 
12
 
13
+ Please find the updated applications below:
14
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ # Create a Gradio Blocks interface
17
+ with gr.Blocks(title="ePub Summarizer - Migrated") as demo:
18
+ gr.Markdown(notice_message)
19
+
20
+ # Use Markdown with HTML for clickable links that look like buttons
21
+ gr.Markdown(
22
+ """
23
+ <a href="https://poe.com/Help_to_Read_a_Book" target="_blank" style="text-decoration: none;">
24
+ <button style="background-color: #4CAF50; /* Green */
25
+ border: none;
26
+ color: white;
27
+ padding: 15px 32px;
28
+ text-align: center;
29
+ text-decoration: none;
30
+ display: inline-block;
31
+ font-size: 16px;
32
+ margin: 4px 2px;
33
+ cursor: pointer;
34
+ border-radius: 8px;">
35
+ Help to Read a Book (Poe.com)
36
+ </button>
37
+ </a>
38
+ """
39
+ )
40
+ gr.Markdown(
41
+ """
42
+ <a href="https://poe.com/Chat-ePub" target="_blank" style="text-decoration: none;">
43
+ <button style="background-color: #008CBA; /* Blue */
44
+ border: none;
45
+ color: white;
46
+ padding: 15px 32px;
47
+ text-align: center;
48
+ text-decoration: none;
49
+ display: inline-block;
50
+ font-size: 16px;
51
+ margin: 4px 2px;
52
+ cursor: pointer;
53
+ border-radius: 8px;">
54
+ Chat-ePub (Poe.com)
55
+ </button>
56
+ </a>
57
+ """
58
+ )
59
+
60
+ gr.Markdown(
61
+ """
62
+ Thank you for your understanding and continued support!
63
+ """
64
+ )
65
+
66
+ # Launch the Gradio interface
67
+ demo.launch()