mokshak commited on
Commit
03757d1
Β·
1 Parent(s): 6fcbff1

Add Gradio UI with dark theme and professional card-based results

Browse files
Files changed (4) hide show
  1. Dockerfile +6 -3
  2. README.md +35 -23
  3. app.py +206 -186
  4. requirements.txt +5 -9
Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
- # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
  # SHL Assessment Recommendation Engine
 
3
 
4
- FROM python:3.9
5
 
6
  RUN useradd -m -u 1000 user
7
  USER user
@@ -13,4 +13,7 @@ COPY --chown=user ./requirements.txt requirements.txt
13
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
 
15
  COPY --chown=user . /app
16
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
 
1
  # SHL Assessment Recommendation Engine
2
+ # HuggingFace Spaces with Gradio UI
3
 
4
+ FROM python:3.10-slim
5
 
6
  RUN useradd -m -u 1000 user
7
  USER user
 
13
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
 
15
  COPY --chown=user . /app
16
+
17
+ EXPOSE 7860
18
+
19
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -3,41 +3,53 @@ title: SHL Assessment Recommendation Engine
3
  emoji: 🎯
4
  colorFrom: blue
5
  colorTo: purple
6
- sdk: docker
 
 
7
  pinned: false
8
  license: mit
 
9
  ---
10
 
11
- # SHL Assessment Recommendation Engine
12
 
13
- AI-powered assessment recommendations using 2-Stage RAG Pipeline:
14
 
15
- **Architecture**: Query β†’ SBERT β†’ FAISS β†’ Top-K β†’ Cross-Encoder Reranker β†’ Results
16
 
17
- ## Performance
18
- - **P@1**: 1.000 (Top result always relevant)
19
- - **MRR**: 1.000 (First relevant at rank 1)
20
- - **NDCG@5**: 0.964 (Near-optimal ranking)
21
 
22
- ## API Endpoints
23
- - `GET /` - API info
24
- - `GET /health` - Health check
25
- - `GET /recommend?query=...` - Get recommendations
26
- - `GET /evaluate` - Run evaluation metrics
27
- - `GET /docs` - Interactive API documentation
28
 
29
- ## Models Used
30
- | Component | Model |
31
- |-----------|-------|
32
- | Retriever | `all-MiniLM-L6-v2` |
33
- | Reranker | `ms-marco-MiniLM-L-6-v2` |
34
- | Index | FAISS (IndexFlatIP) |
35
 
36
- ## Data
37
- 74 SHL assessments covering:
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  - Job Focused Assessments
39
  - Cognitive Assessments
40
  - Personality Assessments
41
  - Skills & Simulations
42
 
43
- Built for SHL Research Internship - December 2024
 
 
 
3
  emoji: 🎯
4
  colorFrom: blue
5
  colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
  pinned: false
10
  license: mit
11
+ short_description: AI-powered SHL assessment recommendations using 2-Stage RAG
12
  ---
13
 
14
+ # 🎯 SHL Assessment Recommendation Engine
15
 
16
+ AI-powered recommendations for SHL assessments using a novel **2-Stage RAG Pipeline**.
17
 
18
+ ## πŸ—οΈ Architecture
19
 
20
+ ```
21
+ Query β†’ SBERT Encoder β†’ FAISS Index β†’ Top-K β†’ Cross-Encoder Reranker β†’ Results
22
+ ```
 
23
 
24
+ | Component | Model | Purpose |
25
+ |-----------|-------|---------|
26
+ | **Retriever** | all-MiniLM-L6-v2 | Fast semantic embedding |
27
+ | **Index** | FAISS IndexFlatIP | Efficient similarity search |
28
+ | **Reranker** | ms-marco-MiniLM-L-6-v2 | Accurate pairwise scoring |
29
+ | **Scoring** | Hybrid | 30% retrieval + 70% reranking |
30
 
31
+ ## πŸ“Š Performance
 
 
 
 
 
32
 
33
+ | Metric | Score | Interpretation |
34
+ |--------|-------|----------------|
35
+ | **P@1** | 0.90 | 90% top-1 accuracy |
36
+ | **MRR** | 0.95 | First relevant at rank ~1 |
37
+ | **NDCG@5** | 0.944 | Near-optimal ranking |
38
+
39
+ ## πŸš€ Usage
40
+
41
+ 1. Enter a job description or role requirements
42
+ 2. Click "Get Recommendations"
43
+ 3. View matching SHL assessments with scores
44
+
45
+ ## πŸ“š Dataset
46
+
47
+ 74 SHL assessments from the [SHL Product Catalog](https://www.shl.com/products/product-catalog/) including:
48
  - Job Focused Assessments
49
  - Cognitive Assessments
50
  - Personality Assessments
51
  - Skills & Simulations
52
 
53
+ ---
54
+
55
+ Built for SHL Research Internship Assessment β€’ December 2024
app.py CHANGED
@@ -1,17 +1,15 @@
1
- # SHL Assessment Recommendation Engine - HuggingFace Spaces
2
  # 2-Stage RAG Pipeline: SBERT + Cross-Encoder Reranking
3
- # Performance: P@1=1.0, MRR=1.0, NDCG@5=0.964
4
 
5
  import json
6
  import math
7
  import os
8
  import statistics
9
  import numpy as np
 
10
  from dataclasses import dataclass
11
  from typing import List, Optional, Dict, Any
12
- from fastapi import FastAPI, Query, HTTPException
13
- from fastapi.middleware.cors import CORSMiddleware
14
- from pydantic import BaseModel
15
  from sentence_transformers import CrossEncoder, SentenceTransformer
16
  import faiss
17
 
@@ -154,7 +152,7 @@ class SHLRecommendationEngine:
154
 
155
  Pipeline: Query -> SBERT -> FAISS -> Top-K -> Cross-Encoder -> Results
156
 
157
- Performance: P@1=1.0, MRR=1.0, NDCG@5=0.964
158
  """
159
 
160
  def __init__(self):
@@ -245,209 +243,231 @@ class SHLRecommendationEngine:
245
  # Sort by hybrid score and return top results
246
  results.sort(key=lambda x: x["similarity_score"], reverse=True)
247
  return results[:max_results]
248
-
249
- def get_architecture_info(self) -> Dict[str, Any]:
250
- return {
251
- "name": "2-Stage RAG Pipeline",
252
- "retriever": "SBERT (all-MiniLM-L6-v2)",
253
- "reranker": "Cross-Encoder (ms-marco-MiniLM-L-6-v2)",
254
- "index": "FAISS IndexFlatIP",
255
- "scoring": "Hybrid (30% retrieval + 70% reranking)",
256
- "assessments": len(self.assessments),
257
- "performance": {"P@1": 1.0, "MRR": 1.0, "NDCG@5": 0.964}
258
- }
259
 
260
 
261
  # ============================================================================
262
- # EVALUATION METRICS
263
  # ============================================================================
264
 
265
- class EvaluationMetrics:
266
- """Standard IR (Information Retrieval) Evaluation Metrics"""
267
-
268
- @staticmethod
269
- def precision_at_k(retrieved: List[str], relevant: List[str], k: int) -> float:
270
- """Precision@K: Fraction of top-K results that are relevant"""
271
- hits = sum(1 for r in retrieved[:k] if any(rel.lower() in r.lower() for rel in relevant))
272
- return hits / k if k > 0 else 0.0
273
-
274
- @staticmethod
275
- def mean_reciprocal_rank(retrieved: List[str], relevant: List[str]) -> float:
276
- """MRR: 1/rank of first relevant result"""
277
- for i, r in enumerate(retrieved, 1):
278
- if any(rel.lower() in r.lower() for rel in relevant):
279
- return 1.0 / i
280
- return 0.0
281
-
282
- @staticmethod
283
- def ndcg_at_k(retrieved: List[str], relevant: List[str], k: int) -> float:
284
- """NDCG@K: Normalized Discounted Cumulative Gain (0-1)"""
285
- hits = [i for i, r in enumerate(retrieved[:k], 1) if any(rel.lower() in r.lower() for rel in relevant)]
286
- if not hits:
287
- return 0.0
288
- dcg = sum(1.0 / math.log2(pos + 1) for pos in hits)
289
- ideal = sum(1.0 / math.log2(i + 1) for i in range(1, len(hits) + 1))
290
- return min(dcg / ideal, 1.0) if ideal > 0 else 0.0
291
 
292
 
293
  # ============================================================================
294
- # FASTAPI APPLICATION
295
  # ============================================================================
296
 
297
- # Initialize engine on startup
298
- engine = SHLRecommendationEngine()
299
-
300
- # Create FastAPI app
301
- app = FastAPI(
302
- title="SHL Assessment Recommendation API",
303
- description="""
304
- AI-powered assessment recommendations using 2-Stage RAG Pipeline.
305
 
306
- ## Architecture
307
- - **Stage 1**: SBERT retrieval with FAISS index (fast)
308
- - **Stage 2**: Cross-Encoder reranking (accurate)
309
- - **Hybrid Scoring**: 30% retrieval + 70% reranking
310
 
311
- ## Performance
312
- - **P@1**: 1.000 (Top result always relevant)
313
- - **MRR**: 1.000 (First relevant at rank 1)
314
- - **NDCG@5**: 0.964 (Near-optimal ranking)
315
- """,
316
- version="2.0.0"
317
- )
318
-
319
- app.add_middleware(
320
- CORSMiddleware,
321
- allow_origins=["*"],
322
- allow_credentials=True,
323
- allow_methods=["*"],
324
- allow_headers=["*"],
325
- )
326
-
327
-
328
- # Response models
329
- class AssessmentResponse(BaseModel):
330
- name: str
331
- url: str
332
- category: str
333
- job_levels: List[str]
334
- focus_areas: List[str]
335
- test_type: List[str]
336
- duration_minutes: Optional[int]
337
- remote_testing: bool
338
- adaptive: bool
339
- similarity_score: float
340
- rerank_score: float
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
 
343
- class RecommendationResponse(BaseModel):
344
- query: str
345
- recommendations: List[AssessmentResponse]
 
 
 
 
 
 
 
346
 
347
 
348
  # ============================================================================
349
- # API ENDPOINTS
350
  # ============================================================================
351
 
352
- @app.get("/")
353
- def root():
354
- """API information and health status"""
355
- return {
356
- "name": "SHL Assessment Recommendation API",
357
- "version": "2.0.0",
358
- "architecture": "2-Stage RAG: SBERT + Cross-Encoder Reranking",
359
- "assessments": len(engine.assessments),
360
- "status": "healthy",
361
- "endpoints": {
362
- "recommend": "/recommend?query=...",
363
- "health": "/health",
364
- "evaluate": "/evaluate",
365
- "architecture": "/architecture",
366
- "docs": "/docs"
367
- }
368
  }
369
-
370
-
371
- @app.get("/health")
372
- def health():
373
- """Health check endpoint"""
374
- return {
375
- "status": "healthy",
376
- "assessments": len(engine.assessments),
377
- "models_loaded": True
378
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
 
380
-
381
- @app.get("/recommend", response_model=RecommendationResponse)
382
- def recommend(
383
- query: str = Query(..., min_length=3, description="Job description or query"),
384
- max_results: int = Query(default=10, ge=1, le=10, description="Number of results")
385
- ):
386
- """Get assessment recommendations for a query using 2-Stage RAG pipeline"""
387
- recommendations = engine.recommend(query, max_results)
388
- return {"query": query, "recommendations": recommendations}
389
-
390
-
391
- @app.get("/architecture")
392
- def architecture():
393
- """Get detailed architecture information"""
394
- return engine.get_architecture_info()
395
-
396
-
397
- @app.get("/evaluate")
398
- def evaluate():
399
- """Run evaluation suite with standard IR metrics"""
400
- metrics = EvaluationMetrics()
401
 
402
- test_cases = [
403
- {"query": "Python developer backend role", "relevant": ["Software Developer", "Coding", "IT Professional"]},
404
- {"query": "customer service representative", "relevant": ["Customer Service", "Contact Center", "Front Desk"]},
405
- {"query": "manager leadership position", "relevant": ["Manager", "Supervisor", "Virtual Assessment"]},
406
- {"query": "graduate trainee program", "relevant": ["Graduate", "Apprentice", "Entry Level"]},
407
- {"query": "sales account executive", "relevant": ["Sales Professional", "Account Manager", "Sales Representative"]},
408
- {"query": "banking branch associate", "relevant": ["Teller", "Banker", "Financial"]},
409
- {"query": "healthcare nursing assistant", "relevant": ["Healthcare", "Nursing", "Home Health"]},
410
- {"query": "numerical reasoning cognitive test", "relevant": ["Verify Numerical", "Calculation", "Cognitive"]},
411
- {"query": "personality work style assessment", "relevant": ["OPQ", "MQ", "Personality"]},
412
- {"query": "problem solving logical thinking", "relevant": ["Verify G+", "Deductive", "Inductive"]},
413
- ]
 
 
 
 
 
 
414
 
415
- all_p1, all_p3, all_p5, all_mrr, all_ndcg = [], [], [], [], []
416
- results = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
 
418
- for tc in test_cases:
419
- recs = engine.recommend(tc["query"], 10)
420
- names = [r["name"] for r in recs]
421
-
422
- p1 = metrics.precision_at_k(names, tc["relevant"], 1)
423
- p3 = metrics.precision_at_k(names, tc["relevant"], 3)
424
- p5 = metrics.precision_at_k(names, tc["relevant"], 5)
425
- mrr = metrics.mean_reciprocal_rank(names, tc["relevant"])
426
- ndcg = metrics.ndcg_at_k(names, tc["relevant"], 5)
427
-
428
- all_p1.append(p1)
429
- all_p3.append(p3)
430
- all_p5.append(p5)
431
- all_mrr.append(mrr)
432
- all_ndcg.append(ndcg)
433
-
434
- results.append({
435
- "query": tc["query"],
436
- "P@1": round(p1, 3),
437
- "MRR": round(mrr, 3),
438
- "NDCG@5": round(ndcg, 3),
439
- "top_result": names[0] if names else None
440
- })
441
 
442
- return {
443
- "aggregate": {
444
- "P@1": round(statistics.mean(all_p1), 3),
445
- "P@3": round(statistics.mean(all_p3), 3),
446
- "P@5": round(statistics.mean(all_p5), 3),
447
- "MRR": round(statistics.mean(all_mrr), 3),
448
- "NDCG@5": round(statistics.mean(all_ndcg), 3)
449
- },
450
- "per_query": results,
451
- "test_cases": len(test_cases),
452
- "assessments": len(engine.assessments)
453
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SHL Assessment Recommendation Engine - HuggingFace Spaces with Gradio UI
2
  # 2-Stage RAG Pipeline: SBERT + Cross-Encoder Reranking
3
+ # Performance: P@1=0.9, MRR=0.95, NDCG@5=0.944
4
 
5
  import json
6
  import math
7
  import os
8
  import statistics
9
  import numpy as np
10
+ import gradio as gr
11
  from dataclasses import dataclass
12
  from typing import List, Optional, Dict, Any
 
 
 
13
  from sentence_transformers import CrossEncoder, SentenceTransformer
14
  import faiss
15
 
 
152
 
153
  Pipeline: Query -> SBERT -> FAISS -> Top-K -> Cross-Encoder -> Results
154
 
155
+ Performance: P@1=0.9, MRR=0.95, NDCG@5=0.944
156
  """
157
 
158
  def __init__(self):
 
243
  # Sort by hybrid score and return top results
244
  results.sort(key=lambda x: x["similarity_score"], reverse=True)
245
  return results[:max_results]
 
 
 
 
 
 
 
 
 
 
 
246
 
247
 
248
  # ============================================================================
249
+ # INITIALIZE ENGINE (Global singleton)
250
  # ============================================================================
251
 
252
+ print("Initializing SHL Recommendation Engine...")
253
+ engine = SHLRecommendationEngine()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
 
256
  # ============================================================================
257
+ # GRADIO UI FUNCTIONS
258
  # ============================================================================
259
 
260
+ def format_recommendations_html(results: List[Dict]) -> str:
261
+ """Format recommendations as beautiful HTML cards"""
262
+ if not results:
263
+ return "<div style='text-align: center; padding: 40px; color: #666;'>No recommendations found. Try a different query.</div>"
 
 
 
 
264
 
265
+ html = "<div style='display: flex; flex-direction: column; gap: 16px;'>"
 
 
 
266
 
267
+ for i, r in enumerate(results, 1):
268
+ # Determine badge colors
269
+ score = r.get('similarity_score', 0)
270
+ if score >= 0.3:
271
+ score_color = "#10b981" # Green
272
+ elif score >= 0.15:
273
+ score_color = "#f59e0b" # Yellow
274
+ else:
275
+ score_color = "#6b7280" # Gray
276
+
277
+ duration = f"{r['duration_minutes']} min" if r['duration_minutes'] else "Variable"
278
+ adaptive = "βœ“ Adaptive" if r['adaptive'] else ""
279
+ remote = "βœ“ Remote" if r['remote_testing'] else ""
280
+
281
+ html += f"""
282
+ <div style='background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
283
+ border-radius: 12px; padding: 20px;
284
+ border-left: 4px solid {score_color};
285
+ box-shadow: 0 4px 6px rgba(0,0,0,0.3);'>
286
+ <div style='display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;'>
287
+ <div>
288
+ <span style='background: {score_color}; color: white; padding: 4px 10px;
289
+ border-radius: 20px; font-size: 12px; font-weight: 600;'>
290
+ #{i} β€’ Score: {score:.1%}
291
+ </span>
292
+ </div>
293
+ <div style='display: flex; gap: 8px;'>
294
+ {f"<span style='background: #3b82f6; color: white; padding: 3px 8px; border-radius: 4px; font-size: 11px;'>{adaptive}</span>" if adaptive else ""}
295
+ {f"<span style='background: #8b5cf6; color: white; padding: 3px 8px; border-radius: 4px; font-size: 11px;'>{remote}</span>" if remote else ""}
296
+ </div>
297
+ </div>
298
+
299
+ <h3 style='margin: 0 0 8px 0; color: #f8fafc; font-size: 18px;'>
300
+ <a href="{r['url']}" target="_blank" style='color: #60a5fa; text-decoration: none;'>
301
+ {r['name']} β†—
302
+ </a>
303
+ </h3>
304
+
305
+ <div style='color: #94a3b8; font-size: 13px; margin-bottom: 12px;'>
306
+ <strong style='color: #cbd5e1;'>Category:</strong> {r['category']} β€’
307
+ <strong style='color: #cbd5e1;'>Duration:</strong> {duration}
308
+ </div>
309
+
310
+ <div style='display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px;'>
311
+ {' '.join([f"<span style='background: #475569; color: #e2e8f0; padding: 3px 10px; border-radius: 15px; font-size: 11px;'>{level}</span>" for level in r['job_levels']])}
312
+ </div>
313
+
314
+ <div style='color: #94a3b8; font-size: 12px;'>
315
+ <strong style='color: #cbd5e1;'>Focus Areas:</strong> {', '.join(r['focus_areas'][:4])}
316
+ </div>
317
+
318
+ <div style='color: #64748b; font-size: 11px; margin-top: 8px;'>
319
+ Test Types: {', '.join(r['test_type'])}
320
+ </div>
321
+ </div>
322
+ """
323
+
324
+ html += "</div>"
325
+ return html
326
 
327
 
328
+ def get_recommendations(query: str, num_results: int) -> str:
329
+ """Get recommendations and format as HTML"""
330
+ if not query or len(query.strip()) < 3:
331
+ return "<div style='text-align: center; padding: 40px; color: #fbbf24;'>⚠️ Please enter at least 3 characters to search.</div>"
332
+
333
+ try:
334
+ results = engine.recommend(query.strip(), max_results=int(num_results))
335
+ return format_recommendations_html(results)
336
+ except Exception as e:
337
+ return f"<div style='text-align: center; padding: 40px; color: #ef4444;'>❌ Error: {str(e)}</div>"
338
 
339
 
340
  # ============================================================================
341
+ # GRADIO INTERFACE - Professional Dark Theme
342
  # ============================================================================
343
 
344
+ # Custom CSS for dark theme
345
+ custom_css = """
346
+ .gradio-container {
347
+ max-width: 1200px !important;
348
+ margin: auto !important;
 
 
 
 
 
 
 
 
 
 
 
349
  }
350
+ .main-title {
351
+ text-align: center;
352
+ background: linear-gradient(90deg, #3b82f6, #8b5cf6);
353
+ -webkit-background-clip: text;
354
+ -webkit-text-fill-color: transparent;
355
+ font-size: 2.5rem !important;
356
+ font-weight: 700 !important;
357
+ margin-bottom: 0.5rem !important;
 
358
  }
359
+ .subtitle {
360
+ text-align: center;
361
+ color: #94a3b8 !important;
362
+ font-size: 1rem !important;
363
+ margin-bottom: 1.5rem !important;
364
+ }
365
+ footer {
366
+ display: none !important;
367
+ }
368
+ """
369
+
370
+ # Example queries for users to try
371
+ examples = [
372
+ ["I need to hire a software developer with Python programming skills"],
373
+ ["Looking for customer service representative assessments"],
374
+ ["Manager position requiring leadership and strategic thinking"],
375
+ ["Fresh graduate analyst trainee program"],
376
+ ["Sales account executive for B2B enterprise software"],
377
+ ["Healthcare nursing assistant for patient care"],
378
+ ["Banking teller handling cash transactions"],
379
+ ["Logical reasoning and problem solving test"],
380
+ ]
381
 
382
+ # Build the Gradio interface
383
+ with gr.Blocks(
384
+ title="SHL Assessment Recommendation Engine",
385
+ theme=gr.themes.Soft(
386
+ primary_hue="blue",
387
+ secondary_hue="purple",
388
+ neutral_hue="slate",
389
+ ),
390
+ css=custom_css
391
+ ) as demo:
 
 
 
 
 
 
 
 
 
 
 
392
 
393
+ # Header
394
+ gr.HTML("""
395
+ <div style='text-align: center; padding: 20px 0;'>
396
+ <h1 class='main-title'>🎯 SHL Assessment Recommendation Engine</h1>
397
+ <p class='subtitle'>AI-powered recommendations using 2-Stage RAG: SBERT + Cross-Encoder Reranking</p>
398
+ <div style='display: flex; justify-content: center; gap: 20px; margin-top: 10px;'>
399
+ <span style='background: #1e40af; color: white; padding: 5px 12px; border-radius: 20px; font-size: 12px;'>
400
+ πŸ“Š 74 Assessments
401
+ </span>
402
+ <span style='background: #047857; color: white; padding: 5px 12px; border-radius: 20px; font-size: 12px;'>
403
+ 🎯 P@1: 90%
404
+ </span>
405
+ <span style='background: #7c3aed; color: white; padding: 5px 12px; border-radius: 20px; font-size: 12px;'>
406
+ ⚑ MRR: 0.95
407
+ </span>
408
+ </div>
409
+ </div>
410
+ """)
411
 
412
+ # Main content
413
+ with gr.Row():
414
+ with gr.Column(scale=3):
415
+ query_input = gr.Textbox(
416
+ label="πŸ” Job Description or Requirements",
417
+ placeholder="Enter a job description, role requirements, or skills needed...",
418
+ lines=3,
419
+ max_lines=5
420
+ )
421
+ with gr.Column(scale=1):
422
+ num_results = gr.Slider(
423
+ minimum=1,
424
+ maximum=10,
425
+ value=5,
426
+ step=1,
427
+ label="πŸ“‹ Number of Results"
428
+ )
429
+ search_btn = gr.Button("πŸš€ Get Recommendations", variant="primary", size="lg")
430
 
431
+ # Examples
432
+ gr.Examples(
433
+ examples=examples,
434
+ inputs=query_input,
435
+ label="πŸ’‘ Try these example queries:"
436
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
 
438
+ # Results
439
+ results_html = gr.HTML(
440
+ value="<div style='text-align: center; padding: 60px; color: #64748b;'>Enter a job description above and click 'Get Recommendations' to see matching SHL assessments.</div>",
441
+ label="Recommendations"
442
+ )
443
+
444
+ # Footer info
445
+ gr.HTML("""
446
+ <div style='text-align: center; padding: 20px; margin-top: 20px; border-top: 1px solid #334155;'>
447
+ <p style='color: #64748b; font-size: 12px; margin: 0;'>
448
+ Built with ❀️ using SBERT + Cross-Encoder Architecture |
449
+ <a href='https://www.shl.com/products/product-catalog/' target='_blank' style='color: #60a5fa;'>SHL Product Catalog</a>
450
+ </p>
451
+ </div>
452
+ """)
453
+
454
+ # Event handlers
455
+ search_btn.click(
456
+ fn=get_recommendations,
457
+ inputs=[query_input, num_results],
458
+ outputs=results_html
459
+ )
460
+
461
+ query_input.submit(
462
+ fn=get_recommendations,
463
+ inputs=[query_input, num_results],
464
+ outputs=results_html
465
+ )
466
+
467
+
468
+ # ============================================================================
469
+ # LAUNCH APPLICATION
470
+ # ============================================================================
471
+
472
+ if __name__ == "__main__":
473
+ demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt CHANGED
@@ -1,9 +1,5 @@
1
- # SHL Assessment Recommendation Engine - HuggingFace Spaces
2
- # Dependencies for 2-Stage RAG Pipeline
3
-
4
- sentence-transformers
5
- faiss-cpu
6
- fastapi
7
- uvicorn[standard]
8
- numpy
9
- pydantic
 
1
+ gradio>=4.0.0
2
+ sentence-transformers>=2.2.2
3
+ faiss-cpu>=1.7.4
4
+ numpy>=1.24.0
5
+ torch>=2.0.0