1qwsd commited on
Commit
6036de3
Β·
verified Β·
1 Parent(s): b4608bf

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +572 -0
app.py ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import traceback
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ # Setup logging
8
+ logging.basicConfig(
9
+ level=logging.INFO,
10
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
11
+ )
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Add src to Python path
15
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
16
+
17
+ # Import Gradio
18
+ try:
19
+ import gradio as gr
20
+ logger.info("βœ“ Gradio imported successfully")
21
+ except ImportError:
22
+ logger.error("βœ— Gradio not found. Install with: pip install gradio")
23
+ sys.exit(1)
24
+
25
+ # Import PyTorch
26
+ try:
27
+ import torch
28
+ logger.info(f"βœ“ PyTorch imported successfully (GPU: {torch.cuda.is_available()})")
29
+ except ImportError:
30
+ logger.error("βœ— PyTorch not found. Install with: pip install torch")
31
+ sys.exit(1)
32
+
33
+ # Import application components
34
+ try:
35
+ from src.mayini_model import MAYINIModel, MAYINIVocabulary
36
+ from src.scraper import JobScraper
37
+ from src.customizer import ResumeCustomizer
38
+ from src.classifier import JobRelevanceClassifier
39
+ from src.agent import JobApplicationAgent
40
+ logger.info("βœ“ All application modules imported successfully")
41
+ except ImportError as e:
42
+ logger.error(f"βœ— Failed to import application modules: {e}")
43
+ logger.error("Make sure all files are in the 'src' directory")
44
+ traceback.print_exc()
45
+ sys.exit(1)
46
+
47
+ # ============================================================================
48
+ # INITIALIZATION
49
+ # ============================================================================
50
+
51
+ logger.info("=" * 70)
52
+ logger.info("πŸ€– Initializing Job Application Agent (MAYINI Framework Edition)")
53
+ logger.info("=" * 70)
54
+
55
+ # Global variables to hold model instances
56
+ mayini_model = None
57
+ vocab = None
58
+ customizer = None
59
+ classifier = None
60
+ scraper = None
61
+ agent = None
62
+
63
+ def initialize_components():
64
+ """Initialize all application components"""
65
+ global mayini_model, vocab, customizer, classifier, scraper, agent
66
+
67
+ try:
68
+ logger.info("\nπŸ“¦ Step 1: Initializing MAYINI Vocabulary...")
69
+ vocab = MAYINIVocabulary(vocab_size=5000)
70
+ logger.info("βœ“ MAYINI Vocabulary initialized (5000 tokens)")
71
+
72
+ logger.info("\nπŸ“¦ Step 2: Initializing MAYINI Model...")
73
+ mayini_model = MAYINIModel(
74
+ vocab_size=5000,
75
+ hidden_dim=256,
76
+ num_heads=8,
77
+ num_layers=4,
78
+ max_seq_len=512,
79
+ dropout=0.1
80
+ )
81
+ logger.info("βœ“ MAYINI Model initialized")
82
+ logger.info(f" - Hidden Dimensions: 256")
83
+ logger.info(f" - Attention Heads: 8")
84
+ logger.info(f" - Transformer Layers: 4")
85
+ logger.info(f" - Parameters: ~3.5M")
86
+ logger.info(f" - Max Sequence Length: 512")
87
+
88
+ # Set to evaluation mode
89
+ mayini_model.eval()
90
+ logger.info("βœ“ MAYINI Model set to evaluation mode")
91
+
92
+ logger.info("\nπŸ“¦ Step 3: Initializing Job Scraper...")
93
+ scraper = JobScraper()
94
+ logger.info("βœ“ Job Scraper initialized with sample jobs")
95
+
96
+ logger.info("\nπŸ“¦ Step 4: Initializing Resume Customizer...")
97
+ customizer = ResumeCustomizer(mayini_model, vocab)
98
+ logger.info("βœ“ Resume Customizer initialized")
99
+
100
+ logger.info("\nπŸ“¦ Step 5: Initializing Job Classifier...")
101
+ classifier = JobRelevanceClassifier()
102
+ classifier.mayini_model = mayini_model
103
+ classifier.mayini_vocab = vocab
104
+ logger.info("βœ“ Job Classifier initialized with MAYINI model")
105
+
106
+ logger.info("\nπŸ“¦ Step 6: Initializing Application Agent...")
107
+ agent = JobApplicationAgent(scraper, customizer, classifier)
108
+ logger.info("βœ“ Application Agent initialized")
109
+
110
+ logger.info("\n" + "=" * 70)
111
+ logger.info("βœ… ALL COMPONENTS INITIALIZED SUCCESSFULLY!")
112
+ logger.info("=" * 70 + "\n")
113
+
114
+ return True
115
+
116
+ except Exception as e:
117
+ logger.error(f"\nβœ— INITIALIZATION ERROR: {e}")
118
+ logger.error(traceback.format_exc())
119
+ return False
120
+
121
+ # Initialize on startup
122
+ if not initialize_components():
123
+ logger.error("Failed to initialize components. Check errors above.")
124
+ sys.exit(1)
125
+
126
+ # ============================================================================
127
+ # INTERFACE FUNCTIONS
128
+ # ============================================================================
129
+
130
+ def search_jobs_interface(keywords: str, location: str, num_jobs: int) -> str:
131
+ """
132
+ Search and rank jobs based on keywords and location
133
+ Uses MAYINI embeddings for relevance matching
134
+ """
135
+ try:
136
+ if not keywords or not keywords.strip():
137
+ return "❌ Error: Please enter keywords"
138
+
139
+ if not agent:
140
+ return "❌ Error: Application not initialized"
141
+
142
+ logger.info(f"\nπŸ” Searching for jobs: keywords='{keywords}', location='{location}', num={int(num_jobs)}")
143
+
144
+ results = agent.search_and_apply(
145
+ keywords=keywords.strip(),
146
+ location=location.strip(),
147
+ num_jobs=int(num_jobs)
148
+ )
149
+
150
+ if not results or 'applications' not in results:
151
+ return "❌ No jobs found matching your criteria"
152
+
153
+ output = f"βœ… **Search Results**\n"
154
+ output += f"Found: {results.get('total_jobs_found', 0)} jobs\n"
155
+ output += f"Relevant: {results.get('relevant_jobs', 0)} jobs\n"
156
+ output += f"Pass Rate: {results.get('pass_rate', 0):.1%}\n\n"
157
+ output += "---\n\n"
158
+
159
+ for i, app in enumerate(results.get('applications', [])[:5], 1):
160
+ job = app.get('job', {})
161
+ score = app.get('relevance_score', 0)
162
+
163
+ output += f"**{i}. {job.get('title', 'N/A')}**\n"
164
+ output += f"- Company: {job.get('company', 'N/A')}\n"
165
+ output += f"- Location: {job.get('location', 'N/A')}\n"
166
+ output += f"- 🎯 Relevance Score: **{score:.0%}**\n"
167
+ output += f"- πŸ’° Salary: {job.get('salary_range', 'Not specified')}\n"
168
+ output += f"- πŸ“Š Experience Required: {job.get('experience_required', 'N/A')} years\n"
169
+
170
+ match_details = app.get('match_details', {})
171
+ if match_details:
172
+ output += f"- βœ“ Matching Skills: {', '.join(match_details.get('matching_skills', [])[:3])}\n"
173
+ output += f"- βœ— Missing Skills: {', '.join(match_details.get('missing_skills', [])[:2])}\n"
174
+
175
+ output += "\n"
176
+
177
+ logger.info(f"βœ“ Found and displayed {len(results.get('applications', [])[:5])} top results")
178
+ return output
179
+
180
+ except Exception as e:
181
+ logger.error(f"Error in search_jobs_interface: {e}")
182
+ logger.error(traceback.format_exc())
183
+ return f"❌ Error: {str(e)}\n\nPlease try again with different inputs."
184
+
185
+ def customize_resume_interface(job_title: str, company: str, requirements: str) -> str:
186
+ """
187
+ Customize resume for specific job using MAYINI embeddings
188
+ """
189
+ try:
190
+ if not job_title or not job_title.strip():
191
+ return "❌ Error: Please enter job title"
192
+
193
+ if not customizer:
194
+ return "❌ Error: Customizer not initialized"
195
+
196
+ logger.info(f"\nπŸ“„ Customizing resume for: {job_title} @ {company}")
197
+
198
+ job = {
199
+ 'title': job_title.strip(),
200
+ 'company': company.strip(),
201
+ 'description': f"{job_title} at {company}",
202
+ 'requirements': [req.strip() for req in requirements.split(',') if req.strip()]
203
+ }
204
+
205
+ customized = customizer.customize_for_job(job)
206
+
207
+ output = f"βœ… **Customized Resume**\n\n"
208
+ output += f"**Job:** {job_title} @ {company}\n\n"
209
+
210
+ output += f"**Custom Summary:**\n"
211
+ output += f"{customized.get('summary', 'N/A')}\n\n"
212
+
213
+ output += f"**Prioritized Skills:**\n"
214
+ skills = customized.get('skills', [])[:15]
215
+ for skill in skills:
216
+ output += f"β€’ {skill}\n"
217
+
218
+ if 'customized_for' in customized:
219
+ match_info = customized['customized_for']
220
+ output += f"\n**Match Information:**\n"
221
+ output += f"- Matching Skills: {len(match_info.get('matching_skills', []))} skills\n"
222
+ output += f"- Match Score: {match_info.get('match_score', 0):.0%}\n"
223
+
224
+ logger.info(f"βœ“ Resume customized successfully")
225
+ return output
226
+
227
+ except Exception as e:
228
+ logger.error(f"Error in customize_resume_interface: {e}")
229
+ logger.error(traceback.format_exc())
230
+ return f"❌ Error: {str(e)}\n\nPlease check your inputs and try again."
231
+
232
+ def classify_job_interface(job_title: str, requirements: str) -> str:
233
+ """
234
+ Classify job relevance using MAYINI embeddings
235
+ """
236
+ try:
237
+ if not job_title or not job_title.strip():
238
+ return "❌ Error: Please enter job title"
239
+
240
+ if not classifier:
241
+ return "❌ Error: Classifier not initialized"
242
+
243
+ logger.info(f"\n🎯 Classifying job: {job_title}")
244
+
245
+ job = {
246
+ 'title': job_title.strip(),
247
+ 'description': job_title.strip(),
248
+ 'requirements': [req.strip() for req in requirements.split(',') if req.strip()] if requirements else [],
249
+ 'location': 'Remote',
250
+ 'company': 'Unknown',
251
+ 'experience_required': 5,
252
+ 'salary_range': 'Unknown'
253
+ }
254
+
255
+ # Get sample resume skills
256
+ resume_skills = [
257
+ "Python", "Docker", "AWS", "PostgreSQL", "REST API",
258
+ "Microservices", "Git", "Kubernetes", "Machine Learning",
259
+ "Data Analysis", "SQL", "Linux", "Cloud Computing"
260
+ ]
261
+
262
+ score = classifier.classify_job(job, resume_skills)
263
+ details = classifier.get_match_details(job, resume_skills)
264
+
265
+ output = f"βœ… **Job Classification Results**\n\n"
266
+ output += f"**Job Title:** {job_title}\n\n"
267
+
268
+ # Relevance score
269
+ score_percent = score * 100
270
+ if score >= 0.8:
271
+ emoji = "🟒"
272
+ level = "EXCELLENT"
273
+ elif score >= 0.6:
274
+ emoji = "🟑"
275
+ level = "GOOD"
276
+ elif score >= 0.4:
277
+ emoji = "🟠"
278
+ level = "FAIR"
279
+ else:
280
+ emoji = "πŸ”΄"
281
+ level = "POOR"
282
+
283
+ output += f"{emoji} **Relevance Score:** {score_percent:.1f}% ({level})\n"
284
+ output += f"**Recommendation:** {details.get('recommendation', 'N/A')}\n\n"
285
+
286
+ # Decision
287
+ if score >= 0.6:
288
+ output += f"**Decision:** βœ… **APPLY**\n\n"
289
+ else:
290
+ output += f"**Decision:** ⏸️ **CONSIDER**\n\n"
291
+
292
+ # Skill matching
293
+ output += f"**Matching Skills ({len(details.get('matching_skills', []))}):**\n"
294
+ for skill in details.get('matching_skills', []):
295
+ output += f"βœ“ {skill}\n"
296
+
297
+ output += f"\n**Missing Skills ({len(details.get('missing_skills', []))}):**\n"
298
+ for skill in details.get('missing_skills', []):
299
+ output += f"βœ— {skill}\n"
300
+
301
+ logger.info(f"βœ“ Classification complete: {score_percent:.1f}%")
302
+ return output
303
+
304
+ except Exception as e:
305
+ logger.error(f"Error in classify_job_interface: {e}")
306
+ logger.error(traceback.format_exc())
307
+ return f"❌ Error: {str(e)}\n\nPlease check your inputs and try again."
308
+
309
+ def get_system_info() -> str:
310
+ """Get system information"""
311
+ try:
312
+ info = f"βœ… **System Information**\n\n"
313
+ info += f"**Framework:** MAYINI Transformer Model\n"
314
+ info += f"**Vocabulary Size:** 5,000 tokens\n"
315
+ info += f"**Hidden Dimensions:** 256\n"
316
+ info += f"**Attention Heads:** 8\n"
317
+ info += f"**Transformer Layers:** 4\n"
318
+ info += f"**Total Parameters:** ~3.5M\n"
319
+ info += f"**Max Sequence Length:** 512\n\n"
320
+
321
+ info += f"**Hardware:**\n"
322
+ info += f"- GPU Available: {torch.cuda.is_available()}\n"
323
+ if torch.cuda.is_available():
324
+ info += f"- GPU Name: {torch.cuda.get_device_name(0)}\n"
325
+ info += f"- PyTorch Version: {torch.__version__}\n\n"
326
+
327
+ info += f"**Application Status:**\n"
328
+ info += f"- MAYINI Model: {'βœ“ Loaded' if mayini_model else 'βœ— Not Loaded'}\n"
329
+ info += f"- Vocabulary: {'βœ“ Loaded' if vocab else 'βœ— Not Loaded'}\n"
330
+ info += f"- Scraper: {'βœ“ Loaded' if scraper else 'βœ— Not Loaded'}\n"
331
+ info += f"- Customizer: {'βœ“ Loaded' if customizer else 'βœ— Not Loaded'}\n"
332
+ info += f"- Classifier: {'βœ“ Loaded' if classifier else 'βœ— Not Loaded'}\n"
333
+ info += f"- Agent: {'βœ“ Loaded' if agent else 'βœ— Not Loaded'}\n"
334
+
335
+ return info
336
+ except Exception as e:
337
+ return f"❌ Error: {str(e)}"
338
+
339
+ # ============================================================================
340
+ # GRADIO INTERFACE
341
+ # ============================================================================
342
+
343
+ logger.info("\n🎨 Building Gradio Interface...")
344
+
345
+ with gr.Blocks(
346
+ title="Job Application Agent - MAYINI Framework",
347
+ theme=gr.themes.Soft(),
348
+ css="""
349
+ .gradio-container { max-width: 1200px; margin: auto; }
350
+ .header { text-align: center; margin-bottom: 20px; }
351
+ .tab-content { padding: 20px; }
352
+ """
353
+ ) as demo:
354
+
355
+ # Header
356
+ gr.Markdown("""
357
+ # πŸ€– Job Application Agent
358
+ ### AI-Powered Job Search & Resume Customization
359
+ **Powered by MAYINI Framework** - A Custom Transformer-based ML Model
360
+
361
+ > This application uses advanced machine learning to help you find and apply for the perfect job!
362
+ """)
363
+
364
+ # Search & Match Jobs Tab
365
+ with gr.Tab("πŸ” Search & Match Jobs"):
366
+ gr.Markdown("### Find jobs and get AI-powered relevance matching")
367
+ gr.Markdown("Enter your skills and find the best matching job opportunities using MAYINI embeddings.")
368
+
369
+ with gr.Row():
370
+ with gr.Column():
371
+ search_keywords = gr.Textbox(
372
+ label="Keywords",
373
+ placeholder="python docker aws kubernetes",
374
+ value="python",
375
+ lines=2,
376
+ info="Enter skills and technologies (comma-separated)"
377
+ )
378
+ search_location = gr.Textbox(
379
+ label="Location",
380
+ placeholder="Remote, San Francisco, New York",
381
+ value="Remote",
382
+ info="Enter job location"
383
+ )
384
+ search_num = gr.Slider(
385
+ minimum=1,
386
+ maximum=20,
387
+ value=5,
388
+ step=1,
389
+ label="Number of Jobs",
390
+ info="How many job results to display"
391
+ )
392
+ search_btn = gr.Button("πŸ” Search Jobs", variant="primary", size="lg")
393
+
394
+ with gr.Column():
395
+ search_output = gr.Markdown(
396
+ value="### Results will appear here...",
397
+ label="Search Results"
398
+ )
399
+
400
+ search_btn.click(
401
+ fn=search_jobs_interface,
402
+ inputs=[search_keywords, search_location, search_num],
403
+ outputs=search_output,
404
+ show_progress=True
405
+ )
406
+
407
+ # Customize Resume Tab
408
+ with gr.Tab("πŸ“„ Customize Resume"):
409
+ gr.Markdown("### Tailor your resume for specific job opportunities")
410
+ gr.Markdown("Get a customized resume summary and skill prioritization for any job posting.")
411
+
412
+ with gr.Row():
413
+ with gr.Column():
414
+ customize_title = gr.Textbox(
415
+ label="Job Title",
416
+ placeholder="Senior Python Developer",
417
+ info="The position you're interested in"
418
+ )
419
+ customize_company = gr.Textbox(
420
+ label="Company Name",
421
+ placeholder="Tech Giants Inc",
422
+ info="Company name (optional)"
423
+ )
424
+ customize_req = gr.Textbox(
425
+ label="Job Requirements",
426
+ placeholder="Python, Docker, AWS, Kubernetes, PostgreSQL",
427
+ lines=3,
428
+ info="List requirements (comma-separated)"
429
+ )
430
+ customize_btn = gr.Button("✨ Customize Resume", variant="primary", size="lg")
431
+
432
+ with gr.Column():
433
+ customize_output = gr.Markdown(
434
+ value="### Customized resume will appear here...",
435
+ label="Customized Resume"
436
+ )
437
+
438
+ customize_btn.click(
439
+ fn=customize_resume_interface,
440
+ inputs=[customize_title, customize_company, customize_req],
441
+ outputs=customize_output,
442
+ show_progress=True
443
+ )
444
+
445
+ # Classify Job Tab
446
+ with gr.Tab("🎯 Classify Job Relevance"):
447
+ gr.Markdown("### Check how relevant a job is to your skills")
448
+ gr.Markdown("Get a detailed analysis of job relevance with matching and missing skills.")
449
+
450
+ with gr.Row():
451
+ with gr.Column():
452
+ classify_title = gr.Textbox(
453
+ label="Job Title",
454
+ placeholder="Machine Learning Engineer",
455
+ info="The job position to classify"
456
+ )
457
+ classify_req = gr.Textbox(
458
+ label="Job Requirements",
459
+ placeholder="Python, PyTorch, TensorFlow, Machine Learning, SQL",
460
+ lines=3,
461
+ info="Required skills (comma-separated)"
462
+ )
463
+ classify_btn = gr.Button("🎯 Classify Job", variant="primary", size="lg")
464
+
465
+ with gr.Column():
466
+ classify_output = gr.Markdown(
467
+ value="### Classification results will appear here...",
468
+ label="Classification Results"
469
+ )
470
+
471
+ classify_btn.click(
472
+ fn=classify_job_interface,
473
+ inputs=[classify_title, classify_req],
474
+ outputs=classify_output,
475
+ show_progress=True
476
+ )
477
+
478
+ # System Info Tab
479
+ with gr.Tab("ℹ️ About & System Info"):
480
+ gr.Markdown("""
481
+ ## About Job Application Agent
482
+
483
+ This application uses the **MAYINI Framework** - a custom Transformer-based
484
+ machine learning model specifically designed for semantic job matching and resume customization.
485
+
486
+ ### Key Features
487
+
488
+ πŸ” **Intelligent Job Search**
489
+ - AI-powered job discovery and ranking
490
+ - Real-time relevance scoring
491
+ - Multi-criteria filtering
492
+
493
+ πŸ“„ **Resume Customization**
494
+ - Automatic resume tailoring
495
+ - Skill prioritization
496
+ - Semantic matching with MAYINI embeddings
497
+
498
+ 🎯 **Job Classification**
499
+ - Relevance scoring (0-100%)
500
+ - Skill gap analysis
501
+ - Recommendation engine
502
+
503
+ ### MAYINI Framework Details
504
+
505
+ **Architecture:**
506
+ - Model Type: Transformer Encoder
507
+ - Vocabulary Size: 5,000 tokens
508
+ - Hidden Dimensions: 256
509
+ - Attention Heads: 8
510
+ - Transformer Layers: 4
511
+ - Total Parameters: ~3.5 million
512
+ - Max Sequence Length: 512 tokens
513
+
514
+ **Capabilities:**
515
+ - Text tokenization and encoding
516
+ - 256-dimensional embeddings
517
+ - Multi-head self-attention
518
+ - Semantic understanding
519
+ - Job-resume similarity matching
520
+
521
+ ### Technology Stack
522
+ - **ML Framework**: MAYINI Transformer (Custom)
523
+ - **Interface**: Gradio 4.0+
524
+ - **Deep Learning**: PyTorch 2.0+
525
+ - **Language**: Python 3.8+
526
+ - **Deployment**: Hugging Face Spaces
527
+
528
+ ### Repository & Links
529
+ - **GitHub**: [907-bot/Job-Application-Agent](https://github.com/907-bot/Job-Application-Agent)
530
+ - **License**: Apache 2.0
531
+ - **Status**: Production Ready
532
+
533
+ ---
534
+ """)
535
+
536
+ info_btn = gr.Button("πŸ–₯️ Show System Info", variant="primary")
537
+ info_output = gr.Markdown()
538
+
539
+ info_btn.click(fn=get_system_info, outputs=info_output)
540
+
541
+ logger.info("βœ“ Gradio Interface built successfully\n")
542
+
543
+ # ============================================================================
544
+ # LAUNCH APPLICATION
545
+ # ============================================================================
546
+
547
+ if __name__ == "__main__":
548
+ logger.info("=" * 70)
549
+ logger.info("πŸš€ LAUNCHING JOB APPLICATION AGENT")
550
+ logger.info("=" * 70)
551
+
552
+ logger.info("\nπŸ“ Access the application at:")
553
+ logger.info(" Local: http://127.0.0.1:7860")
554
+ logger.info(" Hugging Face Spaces: Check your Space URL")
555
+ logger.info("\nπŸ’‘ Features:")
556
+ logger.info(" 1. Search & Match Jobs")
557
+ logger.info(" 2. Customize Resume")
558
+ logger.info(" 3. Classify Job Relevance")
559
+ logger.info(" 4. System Information\n")
560
+
561
+ try:
562
+ demo.queue(max_size=32, concurrency_count=4).launch(
563
+ server_name="0.0.0.0",
564
+ server_port=7860,
565
+ show_error=True,
566
+ share=False,
567
+ show_api=True
568
+ )
569
+ except Exception as e:
570
+ logger.error(f"Failed to launch: {e}")
571
+ logger.error(traceback.format_exc())
572
+ sys.exit(1)