ryanshelley commited on
Commit
fcff236
Β·
verified Β·
1 Parent(s): 34ed6ed

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -559
app.py CHANGED
@@ -1,580 +1,131 @@
1
- import gradio as gr
2
- import os
3
- from typing import Dict, List, Tuple
4
  import time
 
 
 
5
 
6
- class GlossaryGenerator:
7
- def __init__(self):
8
- self.template = """
9
- **Glossary Page Template**
10
- Use this template to create individual glossary pages for specific terms. Fill in each section with relevant information.
11
-
12
- **[TERM NAME]**
13
-
14
- **1. Introduction / Brief Definition (AI Overview)**
15
- * **Purpose:** Provide the absolute clearest, most concise, and direct answer to "What is [TERM NAME]?" This should be a short, no-fluff definition, similar to an AI overview or a quick dictionary entry.
16
- * **Content:** Start immediately with the core definition. Get straight to the point.
17
-
18
- **2. Detailed Explanation**
19
- * **Purpose:** Expand on the brief definition, offering a comprehensive explanation of the term. This section should follow a "reverse pyramid" structure, meaning the most critical information is presented first, followed by supporting details.
20
- * **Content:**
21
- * **Elaborate on the core concept:** Build upon the initial definition, providing more depth and context.
22
- * **Explore related questions (PAA / Query Fans):** Anticipate what users might ask next or what related topics they might search for. Integrate answers to "People Also Ask" (PAA) type questions or expand into "query fan" concepts that naturally branch off the main term.
23
- * Provide context, background, or the purpose of the term.
24
- * Include key characteristics, functions, or processes associated with the term.
25
- * Use examples to illustrate the concept clearly.
26
- * Break down complex ideas into simpler parts.
27
-
28
- **3. Key Concepts / Components (Optional)**
29
- * **Purpose:** If the term has distinct sub-sections, components, or related key ideas that warrant separate discussion, list and explain them here.
30
- * **Content:**
31
- * Use bullet points or sub-headings for each key concept.
32
- * Briefly define and explain each component.
33
-
34
- **4. Importance / Application (Optional)**
35
- * **Purpose:** Explain why the term is significant, its impact, or how it is applied in real-world scenarios.
36
- * **Content:** Discuss the relevance, benefits, challenges, or practical uses of the term.
37
-
38
- **5. Related Terms / Concepts**
39
- * **Purpose:** Link to other relevant terms within your glossary or related concepts that readers might find useful for further understanding.
40
- * **Content:**
41
- * List terms that are closely associated or often discussed alongside the current term.
42
-
43
- **6. Sources / References**
44
- * **Purpose:** Cite the sources from which the information was gathered. This adds credibility and allows readers to explore further.
45
- * **Content:**
46
- * List URLs, book titles, or other references.
47
- """
48
-
49
- def _test_api_key(self, api_key: str) -> bool:
50
- """Test OpenAI API key with minimal request"""
51
- if not api_key or not api_key.strip():
52
- return False
53
-
54
- try:
55
- # Import here to avoid import-time issues
56
- from openai import OpenAI
57
-
58
- client = OpenAI(api_key=api_key.strip())
59
-
60
- # Test with minimal request
61
- response = client.chat.completions.create(
62
- model="gpt-3.5-turbo",
63
- messages=[{"role": "user", "content": "Hi"}],
64
- max_tokens=1
65
- )
66
- return True
67
-
68
- except Exception as e:
69
- print(f"API test error: {e}")
70
- return False
71
-
72
- def _call_openai_raw(self, api_key: str, prompt: str, model: str = "gpt-4", max_tokens: int = 2000) -> str:
73
- """Direct OpenAI API call using requests"""
74
- import requests
75
- import json
76
-
77
- if not api_key or not api_key.strip():
78
- return "πŸ”‘ Please enter your OpenAI API key above to generate content."
79
-
80
- clean_key = api_key.strip()
81
-
82
- # Direct API call to avoid client issues
83
- url = "https://api.openai.com/v1/chat/completions"
84
- headers = {
85
- "Authorization": f"Bearer {clean_key}",
86
- "Content-Type": "application/json"
87
- }
88
 
89
  data = {
90
- "model": model,
91
  "messages": [
92
- {"role": "system", "content": "You are a professional content writer specializing in creating high-quality glossary entries. You follow templates precisely and create comprehensive, well-structured content."},
93
  {"role": "user", "content": prompt}
94
  ],
95
  "max_tokens": max_tokens,
96
- "temperature": 0.7,
97
- "top_p": 1,
98
- "frequency_penalty": 0,
99
- "presence_penalty": 0
100
  }
101
 
102
- try:
103
- print(f"πŸš€ Making direct API call to OpenAI...")
104
- response = requests.post(url, headers=headers, json=data, timeout=60)
105
- print(f"πŸ“Š Response status: {response.status_code}")
106
-
107
- if response.status_code == 200:
108
- result = response.json()
109
- content = result['choices'][0]['message']['content'].strip()
110
- print("βœ… API call successful")
111
- return content
112
-
113
- else:
114
- error_data = response.json() if response.content else {}
115
- error_msg = error_data.get('error', {}).get('message', 'Unknown error')
116
- error_code = error_data.get('error', {}).get('code', 'unknown')
117
 
118
- print(f"❌ API call failed: {response.status_code} - {error_msg}")
 
 
 
 
119
 
120
- if response.status_code == 401:
121
- return "❌ Invalid API key. Please check your OpenAI API key."
122
  elif response.status_code == 429:
123
- return "❌ Rate limit exceeded. Please wait a moment and try again."
124
- elif response.status_code == 404 and model == "gpt-4":
125
- # Try GPT-3.5 fallback
126
- print("πŸ”„ Trying GPT-3.5-turbo fallback...")
127
- return self._call_openai_raw(api_key, prompt, "gpt-3.5-turbo", max_tokens)
128
- elif "insufficient_quota" in error_msg.lower():
129
- return "❌ OpenAI API quota exceeded. Please check your billing at https://platform.openai.com/usage"
130
- else:
131
- return f"❌ OpenAI API Error ({response.status_code}): {error_msg}"
132
 
133
- except requests.exceptions.Timeout:
134
- return "❌ Request timeout. Please try again."
135
- except requests.exceptions.RequestException as e:
136
- return f"❌ Network error: {str(e)}"
137
- except Exception as e:
138
- return f"❌ Unexpected error: {str(e)}"
139
-
140
- def generate_new_content(self, api_key: str, term: str, context: str = "", target_audience: str = "general") -> str:
141
- """Generate new glossary content for a given term"""
142
-
143
- if not term.strip():
144
- return "Please enter a term to generate content for."
145
-
146
- prompt = f"""
147
- Create a comprehensive glossary entry for the term "{term}" following this EXACT template structure:
148
-
149
- {self.template}
150
-
151
- **Requirements:**
152
- - Replace [TERM NAME] with "{term}"
153
- - Target Audience: {target_audience}
154
- - Additional Context: {context if context else "No additional context provided"}
155
- - Fill in ALL sections with relevant, accurate information
156
- - Use the "reverse pyramid" structure - most important info first
157
- - Include relevant PAA (People Also Ask) questions in section 2
158
- - Remove optional sections only if truly not applicable
159
- - Maintain clear, concise language
160
- - Provide at least 3 related terms in section 5
161
- - Include credible sources/references in section 6
162
-
163
- **Focus Areas:**
164
- - Make the brief definition crystal clear and direct
165
- - Expand thoroughly in the detailed explanation
166
- - Include practical examples and use cases
167
- - Address common questions people might have
168
- - Ensure professional, authoritative tone
169
-
170
- Generate the complete glossary entry now:
171
- """
172
-
173
- return self._call_openai_raw(api_key, prompt, max_tokens=2500)
174
-
175
- def update_existing_content(self, api_key: str, term: str, existing_content: str, update_instructions: str = "") -> Tuple[str, str]:
176
- """Analyze existing content and provide update recommendations"""
177
-
178
- if not term.strip() or not existing_content.strip():
179
- return "Please provide both term and existing content.", ""
180
-
181
- # First, analyze the content
182
- analysis_prompt = f"""
183
- Analyze this existing glossary content for "{term}" against the template standard and provide specific improvement recommendations.
184
-
185
- **EXISTING CONTENT:**
186
- {existing_content}
187
-
188
- **TEMPLATE STANDARD:**
189
- {self.template}
190
-
191
- **UPDATE INSTRUCTIONS:** {update_instructions if update_instructions else "General content improvement"}
192
-
193
- **Provide a detailed analysis covering:**
194
-
195
- 1. **STRUCTURAL ANALYSIS:**
196
- - Does it follow the template structure?
197
- - Which sections are missing or incomplete?
198
- - Is the reverse pyramid structure implemented?
199
-
200
- 2. **CONTENT QUALITY ASSESSMENT:**
201
- - Clarity and conciseness of the brief definition
202
- - Depth and comprehensiveness of detailed explanation
203
- - Relevance and usefulness of examples
204
- - Quality of related terms and references
205
-
206
- 3. **SPECIFIC RECOMMENDATIONS (prioritized):**
207
- - HIGH PRIORITY: Critical improvements needed
208
- - MEDIUM PRIORITY: Important enhancements
209
- - LOW PRIORITY: Nice-to-have improvements
210
-
211
- 4. **SEO & USER EXPERIENCE:**
212
- - Missing PAA questions to address
213
- - Keyword opportunities
214
- - Cross-linking possibilities
215
- - Readability improvements
216
-
217
- 5. **SOURCES & CREDIBILITY:**
218
- - Quality of current references
219
- - Missing authoritative sources
220
- - Fact-checking requirements
221
-
222
- Format as a professional content analysis report.
223
- """
224
-
225
- recommendations = self._call_openai_raw(api_key, analysis_prompt, max_tokens=1500)
226
-
227
- # Then generate updated content
228
- update_prompt = f"""
229
- Create an improved version of the glossary entry for "{term}" based on the analysis and recommendations.
230
-
231
- **ORIGINAL CONTENT:**
232
- {existing_content}
233
-
234
- **ANALYSIS & RECOMMENDATIONS:**
235
- {recommendations}
236
-
237
- **TEMPLATE TO FOLLOW:**
238
- {self.template}
239
-
240
- **UPDATE INSTRUCTIONS:** {update_instructions if update_instructions else "Apply the key recommendations from the analysis"}
241
-
242
- **Create the improved glossary entry that:**
243
- 1. Follows the template structure exactly
244
- 2. Implements the high and medium priority recommendations
245
- 3. Maintains the best elements from the original
246
- 4. Adds missing sections or information
247
- 5. Improves clarity, structure, and usefulness
248
- 6. Includes better examples and explanations
249
- 7. Enhances SEO and user experience
250
-
251
- Generate the complete, improved glossary entry:
252
- """
253
-
254
- updated_content = self._call_openai_raw(api_key, update_prompt, max_tokens=2500)
255
-
256
- return recommendations, updated_content
257
-
258
- def create_outline_brief(self, api_key: str, topic: str, scope: str = "comprehensive") -> str:
259
- """Create an outline or brief for new glossary content"""
260
-
261
- if not topic.strip():
262
- return "Please enter a topic for the outline."
263
-
264
- prompt = f"""
265
- Create a comprehensive content brief for developing a glossary focused on "{topic}".
266
-
267
- **Scope:** {scope}
268
- **Template Standard:** Follow the 6-section template structure provided
269
-
270
- **Create a detailed brief covering:**
271
-
272
- **1. TOPIC OVERVIEW & STRATEGY**
273
- - Comprehensive topic definition and boundaries
274
- - Target audience analysis and segmentation
275
- - Content complexity and depth recommendations
276
- - Competitive landscape and differentiation opportunities
277
-
278
- **2. TERM IDENTIFICATION & PRIORITIZATION**
279
- - **Primary Terms (10-15 key terms):** Most important, high-search volume terms
280
- - **Secondary Terms (8-12 supporting terms):** Important supporting concepts
281
- - **Long-tail Terms (5-10 specific terms):** Niche but valuable terms
282
- - **Priority Matrix:** High/Medium/Low priority for each term with reasoning
283
-
284
- **3. CONTENT ARCHITECTURE**
285
- - Template section recommendations for each term type
286
- - Suggested content depth and length for each priority level
287
- - Cross-linking strategy between terms
288
- - Information hierarchy and user journey mapping
289
-
290
- **4. RESEARCH & DEVELOPMENT PLAN**
291
- - **Primary Sources:** Authoritative websites, publications, studies
292
- - **Expert Sources:** Industry leaders, academic researchers, practitioners
293
- - **User Research:** Common questions, search patterns, knowledge gaps
294
- - **Competitive Analysis:** What others are doing well/poorly
295
-
296
- **5. SEO & DISCOVERABILITY STRATEGY**
297
- - **Primary Keywords:** Main search terms for each priority level
298
- - **Long-tail Keywords:** Specific phrases users search for
299
- - **PAA Questions:** "People Also Ask" questions to address
300
- - **Content Gap Analysis:** Opportunities competitors are missing
301
- - **Internal Linking Strategy:** How terms connect to each other
302
-
303
- **6. PRODUCTION ROADMAP**
304
- - **Phase 1:** High-priority terms (timeline and resource allocation)
305
- - **Phase 2:** Secondary terms and enhancements
306
- - **Phase 3:** Long-tail terms and optimization
307
- - **Resource Requirements:** Estimated hours per term type
308
- - **Quality Assurance:** Review process and standards
309
- - **Maintenance Plan:** Update frequency and monitoring
310
-
311
- **7. SUCCESS METRICS & KPIs**
312
- - Content quality indicators
313
- - User engagement metrics
314
- - SEO performance targets
315
- - Conversion and utility measurements
316
-
317
- Create a comprehensive, actionable brief that will guide the entire glossary development process.
318
- """
319
-
320
- return self._call_openai_raw(api_key, prompt, max_tokens=3000)
321
-
322
- def create_gradio_interface():
323
- """Create the Gradio interface for the glossary generator"""
324
-
325
- generator = GlossaryGenerator()
326
-
327
- def test_api_key(api_key):
328
- """Test if the API key is valid"""
329
- if not api_key or not api_key.strip():
330
- return "❌ Please enter your OpenAI API key"
331
-
332
- # Basic format check
333
- clean_key = api_key.strip()
334
- if not clean_key.startswith('sk-'):
335
- return "❌ Invalid format - OpenAI API keys should start with 'sk-'"
336
-
337
- if len(clean_key) < 20:
338
- return "❌ API key too short - check if you copied the full key"
339
-
340
- # Test the key with direct API call
341
- test_result = generator._call_openai_raw(clean_key, "Test", "gpt-3.5-turbo", 1)
342
-
343
- if "❌" in test_result:
344
- return test_result
345
- else:
346
- return "βœ… API key is valid and connected!"
347
-
348
- def generate_new_wrapper(api_key, term, context, audience):
349
- if not term.strip():
350
- return "Please enter a term to generate content for."
351
- return generator.generate_new_content(api_key, term, context, audience)
352
-
353
- def update_existing_wrapper(api_key, term, existing_content, update_instructions):
354
- if not term.strip() or not existing_content.strip():
355
- return "Please provide both term and existing content.", ""
356
- recommendations, updated_content = generator.update_existing_content(api_key, term, existing_content, update_instructions)
357
- return recommendations, updated_content
358
-
359
- def create_outline_wrapper(api_key, topic, scope):
360
- if not topic.strip():
361
- return "Please enter a topic for the outline."
362
- return generator.create_outline_brief(api_key, topic, scope)
363
-
364
- # Create the Gradio interface
365
- with gr.Blocks(title="Glossary Content Generator", theme=gr.themes.Soft()) as demo:
366
- gr.Markdown("""
367
- # πŸ“š Glossary Content Generator
368
-
369
- **Powered by OpenAI GPT-4** - Professional glossary content creation and optimization tool.
370
-
371
- πŸ”‘ **Enter your OpenAI API key below to get started!**
372
- """)
373
-
374
- # API Key Section
375
- with gr.Row():
376
- with gr.Column(scale=2):
377
- api_key_input = gr.Textbox(
378
- label="πŸ”‘ OpenAI API Key",
379
- placeholder="sk-proj-... or sk-...",
380
- type="password",
381
- info="Get your API key at https://platform.openai.com/api-keys"
382
- )
383
- with gr.Column(scale=1):
384
- test_key_btn = gr.Button("Test Key", variant="secondary")
385
- manual_test_btn = gr.Button("Skip Test & Try Anyway", variant="outline", size="sm")
386
- with gr.Column(scale=1):
387
- key_status = gr.Textbox(
388
- label="Status",
389
- value="❌ No API key provided",
390
- interactive=False,
391
- max_lines=2
392
- )
393
-
394
- def manual_override(api_key):
395
- if api_key and api_key.strip():
396
- return "⚠️ Test skipped - trying anyway..."
397
- return "❌ Please enter an API key first"
398
-
399
- test_key_btn.click(
400
- test_api_key,
401
- inputs=[api_key_input],
402
- outputs=[key_status]
403
- )
404
-
405
- manual_test_btn.click(
406
- manual_override,
407
- inputs=[api_key_input],
408
- outputs=[key_status]
409
- )
410
-
411
- gr.Markdown("---")
412
-
413
- with gr.Tabs():
414
- # Tab 1: Generate New Content
415
- with gr.TabItem("πŸ†• Generate New Content"):
416
- gr.Markdown("### Create a new glossary entry from scratch using GPT-4")
417
-
418
- with gr.Row():
419
- with gr.Column(scale=1):
420
- new_term = gr.Textbox(
421
- label="Term to Define",
422
- placeholder="e.g., Machine Learning, CPQ, SEO, API",
423
- lines=1
424
- )
425
- new_context = gr.Textbox(
426
- label="Additional Context (Optional)",
427
- placeholder="Provide industry context, specific use cases, or background information",
428
- lines=3
429
- )
430
- new_audience = gr.Dropdown(
431
- label="Target Audience",
432
- choices=["general", "technical", "business", "beginner", "expert"],
433
- value="general"
434
- )
435
- generate_btn = gr.Button("πŸš€ Generate Content", variant="primary", size="lg")
436
 
437
- with gr.Column(scale=2):
438
- new_output = gr.Textbox(
439
- label="Generated Glossary Entry",
440
- lines=25,
441
- max_lines=30,
442
- show_copy_button=True
443
- )
 
 
 
 
 
 
 
444
 
445
- generate_btn.click(
446
- generate_new_wrapper,
447
- inputs=[api_key_input, new_term, new_context, new_audience],
448
- outputs=[new_output]
449
- )
450
 
451
- # Add examples
452
- gr.Markdown("**πŸ’‘ Example Terms:** API, Machine Learning, Blockchain, SaaS, Customer Journey, A/B Testing")
453
-
454
- # Tab 2: Update Existing Content
455
- with gr.TabItem("πŸ”„ Update Existing Content"):
456
- gr.Markdown("### Analyze and improve existing glossary entries with AI-powered recommendations")
457
 
458
- with gr.Row():
459
- with gr.Column(scale=1):
460
- update_term = gr.Textbox(
461
- label="Term Name",
462
- placeholder="Name of the term being updated",
463
- lines=1
464
- )
465
- existing_content = gr.Textbox(
466
- label="Existing Content",
467
- placeholder="Paste your current glossary entry here",
468
- lines=10
469
- )
470
- update_instructions = gr.Textbox(
471
- label="Update Instructions (Optional)",
472
- placeholder="e.g., 'Add more technical details', 'Include recent developments', 'Improve SEO focus'",
473
- lines=3
474
- )
475
- update_btn = gr.Button("πŸ” Analyze & Update", variant="primary", size="lg")
476
 
477
- with gr.Column(scale=2):
478
- with gr.Row():
479
- recommendations_output = gr.Textbox(
480
- label="πŸ“Š Analysis & Recommendations",
481
- lines=12,
482
- max_lines=15,
483
- show_copy_button=True
484
- )
485
- with gr.Row():
486
- updated_content_output = gr.Textbox(
487
- label="✨ Updated Content",
488
- lines=12,
489
- max_lines=15,
490
- show_copy_button=True
491
- )
492
-
493
- update_btn.click(
494
- update_existing_wrapper,
495
- inputs=[api_key_input, update_term, existing_content, update_instructions],
496
- outputs=[recommendations_output, updated_content_output]
497
- )
498
-
499
- # Tab 3: Create Outline/Brief
500
- with gr.TabItem("πŸ“‹ Create Content Brief"):
501
- gr.Markdown("### Generate a comprehensive strategy brief for glossary development")
502
-
503
- with gr.Row():
504
- with gr.Column(scale=1):
505
- outline_topic = gr.Textbox(
506
- label="Topic/Subject Area",
507
- placeholder="e.g., Digital Marketing, Cloud Computing, Artificial Intelligence, E-commerce",
508
- lines=1
509
- )
510
- outline_scope = gr.Dropdown(
511
- label="Scope & Depth",
512
- choices=["comprehensive", "focused", "basic", "advanced", "specialized"],
513
- value="comprehensive"
514
- )
515
- outline_btn = gr.Button("πŸ“‹ Create Strategic Brief", variant="primary", size="lg")
516
 
517
- with gr.Column(scale=2):
518
- outline_output = gr.Textbox(
519
- label="πŸ“ˆ Content Strategy Brief",
520
- lines=25,
521
- max_lines=30,
522
- show_copy_button=True
523
- )
524
-
525
- outline_btn.click(
526
- create_outline_wrapper,
527
- inputs=[api_key_input, outline_topic, outline_scope],
528
- outputs=[outline_output]
529
- )
530
-
531
- gr.Markdown("**πŸ’‘ Example Topics:** Digital Marketing, FinTech, SaaS Operations, Data Science, Cybersecurity")
532
-
533
- # Tab 4: Template Reference
534
- with gr.TabItem("πŸ“„ Template Reference"):
535
- gr.Markdown("### Official Glossary Template Structure")
536
- template_display = gr.Textbox(
537
- label="Template Guidelines",
538
- value=generator.template,
539
- lines=35,
540
- max_lines=40,
541
- interactive=False,
542
- show_copy_button=True
543
- )
544
-
545
- gr.Markdown("""
546
- ---
547
- ## πŸ”§ How to Get Your OpenAI API Key:
548
-
549
- 1. **Visit** [platform.openai.com/api-keys](https://platform.openai.com/api-keys)
550
- 2. **Sign up** or log in to your OpenAI account
551
- 3. **Add billing information** (GPT-4 requires a paid account)
552
- 4. **Create a new secret key**
553
- 5. **Copy and paste** it into the field above
554
-
555
- ## πŸ’° **Cost Information:**
556
- - **GPT-4 pricing**: ~$0.03 per 1K input tokens, ~$0.06 per 1K output tokens
557
- - **Per generation**: Approximately $0.02-0.10 depending on content length
558
- - **Very cost-effective** for professional content creation
559
-
560
- ## ✨ **Features:**
561
- - πŸ€– **GPT-4 Powered**: High-quality, professional content generation
562
- - πŸ“ **Template Consistency**: Follows your exact 6-section structure
563
- - πŸ” **Content Analysis**: Detailed improvement recommendations
564
- - πŸ“Š **Strategic Planning**: Comprehensive content briefs and roadmaps
565
- - 🎯 **SEO Optimized**: Includes PAA questions and keyword strategies
566
- - πŸ“‹ **Copy-Friendly**: Easy copy buttons for all outputs
567
- - πŸ”‘ **No Setup Required**: Just enter your API key and start generating!
568
- - πŸ› οΈ **Direct API**: Uses raw HTTP requests to avoid library conflicts
569
- """)
570
 
571
- return demo
572
-
573
- # Launch the application
574
- if __name__ == "__main__":
575
- app = create_gradio_interface()
576
- app.launch(
577
- share=False,
578
- server_name="0.0.0.0",
579
- server_port=7860
580
- )
 
 
 
 
 
 
 
 
 
 
 
 
1
  import time
2
+ import requests
3
+ import json
4
+ from typing import Optional
5
 
6
+ def _call_openai_with_retry(self, api_key: str, prompt: str, model: str = "gpt-4", max_tokens: int = 2000, max_retries: int = 3) -> str:
7
+ """OpenAI API call with exponential backoff retry logic"""
8
+
9
+ if not api_key or not api_key.strip():
10
+ return "πŸ”‘ Please enter your OpenAI API key above to generate content."
11
+
12
+ clean_key = api_key.strip()
13
+ url = "https://api.openai.com/v1/chat/completions"
14
+ headers = {
15
+ "Authorization": f"Bearer {clean_key}",
16
+ "Content-Type": "application/json"
17
+ }
18
+
19
+ # Start with GPT-3.5-turbo if GPT-4 is not available
20
+ models_to_try = [model]
21
+ if model == "gpt-4":
22
+ models_to_try = ["gpt-4", "gpt-3.5-turbo"]
23
+
24
+ for current_model in models_to_try:
25
+ print(f"πŸ€– Trying model: {current_model}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  data = {
28
+ "model": current_model,
29
  "messages": [
30
+ {"role": "system", "content": "You are a professional content writer specializing in creating high-quality glossary entries."},
31
  {"role": "user", "content": prompt}
32
  ],
33
  "max_tokens": max_tokens,
34
+ "temperature": 0.7
 
 
 
35
  }
36
 
37
+ for attempt in range(max_retries):
38
+ try:
39
+ print(f"πŸ“‘ Attempt {attempt + 1}/{max_retries} for {current_model}")
40
+ response = requests.post(url, headers=headers, json=data, timeout=60)
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ if response.status_code == 200:
43
+ result = response.json()
44
+ content = result['choices'][0]['message']['content'].strip()
45
+ print(f"βœ… Success with {current_model}")
46
+ return content
47
 
 
 
48
  elif response.status_code == 429:
49
+ # Rate limit hit
50
+ try:
51
+ error_data = response.json()
52
+ error_msg = error_data.get('error', {}).get('message', '')
53
+ except:
54
+ error_msg = "Rate limit exceeded"
 
 
 
55
 
56
+ print(f"⏳ Rate limit hit: {error_msg}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ # Extract wait time from headers or use exponential backoff
59
+ retry_after = response.headers.get('retry-after')
60
+ if retry_after:
61
+ wait_time = int(retry_after)
62
+ print(f"⏰ Waiting {wait_time} seconds as suggested by API...")
63
+ else:
64
+ wait_time = (2 ** attempt) * 2 # Exponential backoff: 2, 4, 8 seconds
65
+ print(f"⏰ Waiting {wait_time} seconds (exponential backoff)...")
66
+
67
+ if attempt < max_retries - 1: # Don't wait on the last attempt
68
+ time.sleep(wait_time)
69
+ continue
70
+ else:
71
+ return f"❌ Rate limit exceeded after {max_retries} attempts. Please wait a few minutes and try again."
72
 
73
+ elif response.status_code == 401:
74
+ return "❌ Invalid API key. Please check your OpenAI API key."
 
 
 
75
 
76
+ elif response.status_code == 404:
77
+ print(f"❌ Model {current_model} not available, trying next...")
78
+ break # Try next model
 
 
 
79
 
80
+ else:
81
+ # Other errors
82
+ try:
83
+ error_data = response.json()
84
+ error_msg = error_data.get('error', {}).get('message', 'Unknown error')
85
+ except:
86
+ error_msg = f"HTTP {response.status_code}"
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ if "insufficient_quota" in error_msg.lower():
89
+ return "❌ OpenAI API quota exceeded. Please check your billing at https://platform.openai.com/usage"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ print(f"❌ API Error: {error_msg}")
92
+ if attempt < max_retries - 1:
93
+ time.sleep(2) # Short wait for other errors
94
+ continue
95
+ else:
96
+ return f"❌ API Error: {error_msg}"
97
+
98
+ except requests.exceptions.Timeout:
99
+ print(f"⏰ Request timeout on attempt {attempt + 1}")
100
+ if attempt < max_retries - 1:
101
+ time.sleep(2)
102
+ continue
103
+ else:
104
+ return "❌ Request timeout after multiple attempts. Please try again."
105
+
106
+ except Exception as e:
107
+ print(f"❌ Unexpected error on attempt {attempt + 1}: {str(e)}")
108
+ if attempt < max_retries - 1:
109
+ time.sleep(2)
110
+ continue
111
+ else:
112
+ return f"❌ Unexpected error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ return "❌ All models failed. Please check your API key and try again."
115
+
116
+ # Also add a rate limit checker
117
+ def check_rate_limits(self, api_key: str) -> str:
118
+ """Check current rate limit status"""
119
+ try:
120
+ # Make a minimal request to check status
121
+ response = self._call_openai_with_retry(api_key, "test", "gpt-3.5-turbo", 1)
122
+
123
+ if "Rate limit" in response:
124
+ return "❌ Currently rate limited"
125
+ elif "❌" in response:
126
+ return response
127
+ else:
128
+ return "βœ… Rate limits OK"
129
+
130
+ except Exception as e:
131
+ return f"❌ Error checking rate limits: {str(e)}"