|
|
|
|
|
|
| HF_MODEL = "Qwen/Qwen2.5-7B-Instruct"
|
|
|
|
|
|
|
| HISTORY_FILE = "interview_history.json"
|
|
|
|
|
| INTERVIEW_MODES = {
|
| "⚡ Quick (3 Questions)": 3,
|
| "📋 Standard (5 Questions)": 5,
|
| "🔬 Deep Dive (7 Questions) ": 7,
|
| }
|
|
|
|
|
|
|
|
|
| QUESTION_PROMPT_TEMPLATES = [
|
| "Generate ONE interview question asking the candidate to describe their most relevant project experience for a {role_level} {industry} role. Focus on: {keywords}. Question:",
|
| "Generate ONE behavioral interview question about how the candidate handles challenges, relevant to {industry}. Question:",
|
| "Generate ONE technical or domain-specific question that tests knowledge of {keywords} in a {industry} context. Question:",
|
| "Generate ONE question asking the candidate about their approach to collaboration, communication, or leadership relevant to a {role_level} role. Question:",
|
| "Generate ONE situational interview question: 'What would you do if...' relevant to {industry} and {keywords}. Question:",
|
| "Generate ONE question about the candidate's long-term career goals and how this {industry} role aligns with them. Question:",
|
| "Generate ONE challenging follow-up question that digs deeper into technical expertise or past achievements relevant to {keywords}. Question:",
|
| ]
|
|
|
|
|
| TIPS_DB = {
|
| "python": {
|
| "label": "Python / Backend",
|
| "leetcode": [
|
| ("Two Sum", "https://leetcode.com/problems/two-sum/", "Easy"),
|
| ("LRU Cache", "https://leetcode.com/problems/lru-cache/", "Medium"),
|
| ("Word Search II", "https://leetcode.com/problems/word-search-ii/", "Hard"),
|
| ],
|
| "concepts": ["OOP principles", "Decorators & generators", "Async / await", "REST API design"],
|
| },
|
| "react": {
|
| "label": "React / Frontend",
|
| "leetcode": [
|
| ("Valid Parentheses", "https://leetcode.com/problems/valid-parentheses/", "Easy"),
|
| ("Flatten Nested List", "https://leetcode.com/problems/flatten-nested-list-iterator/", "Medium"),
|
| ],
|
| "concepts": ["Virtual DOM", "Hooks & state management", "Component lifecycle", "Web performance"],
|
| },
|
| "machine learning": {
|
| "label": "Machine Learning",
|
| "leetcode": [
|
| ("Find Peak Element", "https://leetcode.com/problems/find-peak-element/", "Medium"),
|
| ("Kth Largest Element", "https://leetcode.com/problems/kth-largest-element-in-an-array/", "Medium"),
|
| ],
|
| "concepts": ["Bias-variance tradeoff", "Overfitting & regularisation", "Gradient descent", "Model evaluation metrics"],
|
| },
|
| "sql": {
|
| "label": "SQL / Databases",
|
| "leetcode": [
|
| ("Employees Earning More Than Manager", "https://leetcode.com/problems/employees-earning-more-than-their-managers/", "Easy"),
|
| ("Department Top 3 Salaries", "https://leetcode.com/problems/department-top-three-salaries/", "Hard"),
|
| ],
|
| "concepts": ["JOINs & subqueries", "Indexing strategies", "Transactions & ACID", "Query optimisation"],
|
| },
|
| }
|
|
|
| DEFAULT_TIPS = {
|
| "label": "General / Professional",
|
| "leetcode": [
|
| ("Two Sum", "https://leetcode.com/problems/two-sum/", "Easy"),
|
| ("Merge Intervals", "https://leetcode.com/problems/merge-intervals/", "Medium"),
|
| ("Trapping Rain Water", "https://leetcode.com/problems/trapping-rain-water/", "Hard"),
|
| ],
|
| "concepts": ["STAR answer format", "System design basics", "Time & space complexity", "Behavioural questions"],
|
| }
|
|
|
|
|
| CUSTOM_CSS = """
|
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
|
|
|
| /* ── Reset & Base ── */
|
| * { font-family: 'Inter', ui-sans-serif, system-ui, sans-serif !important; box-sizing: border-box; }
|
|
|
| /* ── Root color tokens (our design system) ── */
|
| /* ── ALSO overrides Gradio's internal CSS variables so the loading overlay ── */
|
| /* ── stays dark. The white box during processing comes from ── */
|
| /* ── var(--block-background-fill) defaulting to white in the Soft theme. ── */
|
| :root {
|
| /* Our tokens */
|
| --indigo: #6366f1;
|
| --violet: #8b5cf6;
|
| --indigo-glow: rgba(99,102,241,0.35);
|
| --violet-glow: rgba(139,92,246,0.25);
|
| --surface: rgba(15,15,35,0.6);
|
| --border: rgba(99,102,241,0.18);
|
| --text-muted: #94a3b8;
|
| --text-dim: #64748b;
|
|
|
| /* Gradio's internal variables — override to prevent white loading overlay */
|
| --block-background-fill: rgba(8,8,28,0.85);
|
| --input-background-fill: rgba(8,8,28,0.85);
|
| --background-fill-primary: #080818;
|
| --background-fill-secondary: rgba(15,15,35,0.7);
|
| --body-background-fill: #080818;
|
| --border-color-primary: rgba(99,102,241,0.18);
|
| --body-text-color: #e2e8f0;
|
| --body-text-color-subdued: #94a3b8;
|
| --loader-color: #6366f1;
|
| --block-border-color: rgba(99,102,241,0.18);
|
| --input-border-color: rgba(99,102,241,0.18);
|
| }
|
|
|
| /* ── Background ── */
|
| body { background: #080818 !important; }
|
| gradio-app { background: transparent !important; }
|
| .gradio-container { background: transparent !important; width: 100% !important; max-width: none !important; margin: 0 auto !important; padding: 0 20px !important; }
|
|
|
| /* ── Make the blob background wrapper invisible (blobs are position:fixed, they float fine) ── */
|
| #bg-blobs {
|
| background: none !important;
|
| border: none !important;
|
| backdrop-filter: none !important;
|
| padding: 0 !important;
|
| margin: 0 !important;
|
| overflow: visible !important;
|
| min-height: 0 !important;
|
| height: 0 !important;
|
| pointer-events: none !important;
|
| }
|
| /* ── Make the header wrapper borderless/transparent ── */
|
| #app-header {
|
| background: none !important;
|
| border: none !important;
|
| backdrop-filter: none !important;
|
| padding: 0 !important;
|
| margin: 0 !important;
|
| }
|
|
|
| /* ── Glass panels ── */
|
| .gr-panel, .gr-box, .svelte-panel, .block, .form {
|
| background: var(--surface) !important;
|
| border: 1px solid var(--border) !important;
|
| backdrop-filter: blur(12px) !important;
|
| border-radius: 14px !important;
|
| }
|
|
|
| /* ── Tab nav ── */
|
| .tab-nav { border-bottom: 1px solid var(--border) !important; }
|
| .tab-nav button {
|
| font-weight: 600 !important;
|
| color: var(--text-muted) !important;
|
| padding: 0.6rem 1.2rem !important;
|
| border-radius: 8px 8px 0 0 !important;
|
| transition: all 0.2s !important;
|
| }
|
| .tab-nav button.selected {
|
| color: var(--indigo) !important;
|
| border-bottom: 2px solid var(--indigo) !important;
|
| background: rgba(99,102,241,0.08) !important;
|
| }
|
|
|
| /* ── Primary buttons ── */
|
| .gr-button-primary, button[variant="primary"], .primary-btn {
|
| background: linear-gradient(135deg, #6366f1, #8b5cf6) !important;
|
| border: none !important;
|
| color: white !important;
|
| font-weight: 600 !important;
|
| font-size: 0.9rem !important;
|
| padding: 0.6rem 1.4rem !important;
|
| border-radius: 10px !important;
|
| cursor: pointer !important;
|
| transition: transform 0.15s ease, box-shadow 0.15s ease !important;
|
| box-shadow: 0 0 0 rgba(99,102,241,0) !important;
|
| }
|
| .gr-button-primary:hover, button[variant="primary"]:hover {
|
| transform: translateY(-2px) !important;
|
| box-shadow: 0 8px 24px rgba(99,102,241,0.45) !important;
|
| }
|
| .gr-button-primary:active, button[variant="primary"]:active {
|
| transform: translateY(0px) !important;
|
| }
|
|
|
| /* ── Secondary buttons ── */
|
| .gr-button-secondary, button[variant="secondary"] {
|
| border: 1.5px solid var(--indigo) !important;
|
| color: var(--indigo) !important;
|
| font-weight: 600 !important;
|
| border-radius: 10px !important;
|
| background: transparent !important;
|
| transition: all 0.15s !important;
|
| }
|
| .gr-button-secondary:hover, button[variant="secondary"]:hover {
|
| background: var(--indigo) !important;
|
| color: white !important;
|
| }
|
|
|
| /* ── Textbox inputs — idle & focus ── */
|
| .gr-textbox textarea, input[type="text"], textarea {
|
| background: rgba(8,8,28,0.7) !important;
|
| border: 1px solid var(--border) !important;
|
| border-radius: 10px !important;
|
| color: #e2e8f0 !important;
|
| font-size: 0.92rem !important;
|
| padding: 0.65rem 0.9rem !important;
|
| transition: border-color 0.2s !important;
|
| }
|
| .gr-textbox textarea:focus, textarea:focus {
|
| border-color: var(--indigo) !important;
|
| outline: none !important;
|
| box-shadow: 0 0 0 3px rgba(99,102,241,0.15) !important;
|
| }
|
|
|
| /* ── Textbox inputs — Gradio loading/processing states ── */
|
| /* Gradio adds .generating / .pending to the .wrap container during LLM calls.
|
| Without these, the dark textarea flips to white during processing. */
|
|
|
| /* Target the wrapper container in every loading state */
|
| .wrap.generating, .wrap.pending, .wrap.processing,
|
| .generating .wrap, .pending .wrap, .processing .wrap {
|
| background: rgba(8,8,28,0.7) !important;
|
| border-color: var(--indigo) !important;
|
| }
|
|
|
| /* Target the textarea itself inside any loading-state wrapper */
|
| .generating textarea, .pending textarea, .processing textarea,
|
| .wrap.generating textarea, .wrap.pending textarea, .wrap.processing textarea,
|
| textarea.generating, textarea.pending {
|
| background: rgba(8,8,28,0.7) !important;
|
| color: #e2e8f0 !important;
|
| }
|
|
|
| /* The loading overlay div Gradio injects on top of the textarea */
|
| .generating .eta-bar, .generating .progress-bar,
|
| .pending .eta-bar, .pending .progress-bar {
|
| background: rgba(99,102,241,0.12) !important;
|
| }
|
|
|
| /* The small "processing | X.Xs" status text Gradio adds */
|
| .wrap .eta-bar, .eta-bar {
|
| color: var(--text-muted) !important;
|
| font-size: 0.78rem !important;
|
| background: transparent !important;
|
| }
|
|
|
| /* The loading spinner icon — keep it indigo, not default grey */
|
| .loader, .generating .loader {
|
| border-top-color: var(--indigo) !important;
|
| }
|
|
|
| /* Gradio 4/5/6 Svelte-generated loading shimmer overlay */
|
| .generating::after, .pending::after {
|
| background: rgba(8,8,28,0.4) !important;
|
| }
|
|
|
| /* ── Labels ── */
|
| /* Custom styling for the textbox label capsules */
|
| #step-1-group label span,
|
| #step-3-group label span,
|
| #step-4-group label span {
|
| background: #322b48 !important; /* Change this to edit the capsule background color */
|
| color: #e2e8f0 !important; /* Change this to edit the text color inside the label */
|
| border: 1px solid #6366f1 !important; /* Optional: adds a border to match the input boxes */
|
| width: 150px !important;
|
| text-align: center !important;
|
| display: inline-block !important;
|
|
|
| }
|
|
|
|
|
| /* ── Progress box ── */
|
| #progress_box textarea {
|
| font-weight: 700 !important;
|
| font-size: 1rem !important;
|
| color: var(--indigo) !important;
|
| text-align: center !important;
|
| background: rgba(99,102,241,0.08) !important;
|
| border-color: var(--indigo) !important;
|
| }
|
|
|
| /* ── Feedback box ── */
|
| #feedback_box textarea {
|
| font-size: 0.93rem !important;
|
| line-height: 1.65 !important;
|
| color: #cbd5e1 !important;
|
| }
|
|
|
| /* ── Markdown text ── */
|
| .gr-markdown, .prose {
|
| color: #cbd5e1 !important;
|
| }
|
|
|
|
|
| .padded-markdown{
|
| padding: 16px 20px !important;
|
| }
|
| .gr-markdown h1, .gr-markdown h2, .gr-markdown h3 {
|
| color: #e2e8f0 !important;
|
| }
|
| .gr-markdown code {
|
| background: rgba(99,102,241,0.15) !important;
|
| color: #a5b4fc !important;
|
| border-radius: 4px !important;
|
| padding: 2px 6px !important;
|
| }
|
| .gr-markdown table {
|
| border-collapse: collapse !important;
|
| width: 100% !important;
|
| }
|
| .gr-markdown th {
|
| background: rgba(99,102,241,0.15) !important;
|
| color: #a5b4fc !important;
|
| padding: 8px 12px !important;
|
| font-weight: 600 !important;
|
| }
|
| .gr-markdown td {
|
| border: 1px solid var(--border) !important;
|
| padding: 7px 12px !important;
|
| color: #cbd5e1 !important;
|
| }
|
|
|
| /* ── Accordion ── */
|
| .gr-accordion summary {
|
| color: #a5b4fc !important;
|
| font-weight: 600 !important;
|
| }
|
|
|
| /* ── Radio group (mode selector) ── */
|
| .gr-radio-group label {
|
| background: rgba(99,102,241,0.06) !important;
|
| border: 1px solid var(--border) !important;
|
| border-radius: 8px !important;
|
| padding: 0.5rem 1rem !important;
|
| cursor: pointer !important;
|
| transition: all 0.15s !important;
|
| color: #94a3b8 !important;
|
| }
|
| .gr-radio-group label:has(input:checked) {
|
| background: rgba(99,102,241,0.2) !important;
|
| border-color: var(--indigo) !important;
|
| color: #a5b4fc !important;
|
| }
|
|
|
| /* ── File upload ── */
|
| .gr-file { border: 1px dashed var(--border) !important; border-radius: 10px !important; }
|
|
|
| /* ── Scrollbars ── */
|
| ::-webkit-scrollbar { width: 6px; }
|
| ::-webkit-scrollbar-track { background: transparent; }
|
| ::-webkit-scrollbar-thumb { background: var(--indigo); border-radius: 3px; }
|
|
|
| /* ── Fix for White File Download Row ── */
|
| .gr-file .file-preview,
|
| .gr-file .file-preview-holder,
|
| .gr-file .file-preview-wrap,
|
| .gr-file tbody tr,
|
| .gr-file table td,
|
| .gr-file .download-link {
|
| background-color: transparent !important;
|
| background: transparent !important;
|
| color: #e2e8f0 !important;
|
| }
|
|
|
| /* Add a subtle hover effect for the file row */
|
| .gr-file tbody tr:hover {
|
| background-color: rgba(99,102,241,0.1) !important;
|
| }
|
|
|
| /* Ensure the file name text and size information are readable */
|
| .gr-file tbody td {
|
| color: #cbd5e1 !important;
|
| border-color: rgba(99,102,241,0.18) !important;
|
| }
|
|
|
| /* Color the download icon to match your purple theme */
|
| .gr-file svg {
|
| stroke: #8b5cf6 !important;
|
| }
|
|
|
| /* ── Gradio footer (settings cog, "Built with Gradio" link) ── */
|
| footer, .gradio-container > footer, .built-with {
|
| color: var(--text-dim) !important;
|
| }
|
| footer a, .built-with a {
|
| color: var(--text-muted) !important;
|
| }
|
| footer svg, .built-with svg {
|
| fill: var(--text-dim) !important;
|
| stroke: var(--text-dim) !important;
|
| }
|
|
|
| /* Remove default borders and backgrounds from columns, rows, and forms to prevent nested double borders */
|
| .gradio-container .vertical,
|
| .gradio-container .row,
|
| .gradio-container .gr-form {
|
| background: transparent !important;
|
| border: none !important;
|
| backdrop-filter: none !important;
|
| box-shadow: none !important;
|
| padding: 0 !important;
|
| }
|
|
|
| /* Style all step group containers as unified glass panels */
|
| #step-1-group, #step-2-group, #step-3-group, #step-4-group {
|
| background: var(--surface) !important;
|
| border: 1px solid var(--border) !important;
|
| backdrop-filter: blur(10px) !important;
|
| border-radius: 15px !important;
|
| padding: 2px !important;
|
| display: flex !important;
|
| flex-direction: column !important;
|
| gap: 1px !important;
|
| margin-bottom: 5px !important;
|
| box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.2) !important;
|
| }
|
|
|
| /* Remove borders, backgrounds, shadows, and extra padding from all nested elements inside the groups to eliminate double-bordering */
|
| #step-1-group div, #step-1-group label,
|
| #step-2-group div,
|
| #step-3-group div, #step-3-group label,
|
| #step-4-group div, #step-4-group label {
|
| background: transparent !important;
|
| border: none !important;
|
| backdrop-filter: none !important;
|
| box-shadow: none !important;
|
| }
|
|
|
| /* Format the inner textareas (Step 1, Step 3, Step 4) to be clean, integrated, and borderless */
|
| #step-1-group textarea,
|
| #step-3-group textarea,
|
| #step-4-group textarea {
|
| background: rgba(8, 8, 28, 0.7) !important;
|
| border: 3px solid steelblue !important;
|
| border-radius: 5px !important;
|
| padding: 10px !important;
|
| box-shadow: 0 0 3px dodgerblue !important;
|
| }
|
|
|
| #step-1-group textarea:focus,
|
| #step-3-group textarea:focus,
|
| #step-4-group textarea:focus {
|
| outline: none !important;
|
| box-shadow: 0 0 5px dodgerblue !important;
|
| }
|
| /*------------------------------------------------------*/
|
| /* Step-2 Normal (unselected) radio button choices */
|
| #step-2-group label {
|
| border: 2px solid rgba(99, 102, 241, 0.2) !important; /* Edit normal border width/color here */
|
| border-radius: 30px !important;
|
| background: #475569 !important;
|
| width: 60%;
|
| }
|
|
|
| /*Step-2 label Hover settings*/
|
| #step-2-group label:hover {
|
| border: 2px solid rgba(99, 102, 241, 0.8) !important; /* Edit selected border width/color here */
|
| background: rgba(99, 102, 241, 0.1) !important; /* Edit selected background here */
|
| width: 60%;
|
| border-radius: 30px !important;
|
| }
|
|
|
| /* Step-2 Selected/active radio button choice */
|
| #step-2-group label.selected {
|
| border: 2px solid rgba(99, 102, 241, 0.8) !important; /* Edit selected border width/color here */
|
| background: #9333EA !important; /* Edit selected background here */
|
| width: 60%;
|
| border-radius: 30px !important;
|
| }
|
|
|
| /*Step-2 Radio button container's border editing.*/
|
| #step-2-group fieldset{
|
| border: none !important;
|
| background: tranparent !important;
|
| box-shadow: none !important;
|
| backdrop-filter: none !important;
|
| }
|
|
|
| /*TIPS SECTION FORMATTING*/
|
| /*Since we use class tag, we will call this via (.)*/
|
| .session-tips {
|
| background: rgba(15,15,40,0.6);
|
| border: 2px solid rgba(99,102,241,0.2);
|
| border-radius:12px;
|
| padding:14px 14px;
|
| box-shadow: 0 0 10px deepskyblue !important;
|
| }
|
|
|
| .session-tips:hover{
|
| border-color: blue !important;
|
| box-shadow: 0 0 20px blue !important;
|
| }
|
|
|
| /*Step-3 button formartting*/
|
| /*we edit the row settings as the button render inside a row within the right column*/
|
| #step-3-group .row {
|
| display: flex !important;
|
| flex-direction: row !important; /* Force horizontal layout */
|
| flex-wrap: nowrap !important; /* Prevent wrapping/stacking */
|
| justify-content: center !important; /* Center both buttons */
|
| margin: 0px auto !important;
|
| padding: 0px !important;
|
| gap: 10px !important;
|
| width: 95% !important;
|
| }
|
|
|
|
|
| /*Now we get into the button formatting directly*/
|
|
|
| /*this is for the Primary button: the Get Feedback button*/
|
| #step-3-group button{
|
| border-radius: 30px !important;
|
| border: 1px solid rgba(99, 102, 241, 0.2) !important;
|
| padding: 5px !important;
|
| width: 49.5% !important;
|
| flex: none !important;
|
| }
|
|
|
| /* Hover (mouse-over) effect: make the button darker with a minor border change*/
|
| #step-3-group button:hover {
|
| padding: 5px !important;
|
| background: #191836 !important; /* Slightly stronger purple */
|
| border: 1px solid rgba(99, 102, 241, 0.8) !important; /* Edit selected border width/color here */
|
| }
|
| /*------------------------------------------------------*/
|
|
|
| /*this is for the secondary button of the gr.Row(), the Next Question button*/
|
| #step-3-group button.secondary{
|
| background: #475569 !important;
|
| color: white !important;
|
| border-radius: 30px !important;
|
| border: 1px solid rgba(99, 102, 241, 0.2) !important;
|
| opacity: 1 !important;
|
| }
|
|
|
|
|
| #step-3-group button.secondary:hover{
|
| padding: 5px !important;
|
| background: #191836 !important;
|
| border: 1px solid rgba(99, 102, 241, 0.8) !important;
|
| color: white !important;
|
| }
|
|
|
| /* ── Tab 2 (History & Report) Button Styling ── */
|
| /* Primary Button (Generate PDF Report) */
|
| #report-actions button.primary {
|
| background: #8b5cf6 !important;
|
| color: white !important;
|
| border: 1px solid #a5b4fc !important;
|
| }
|
| #report-actions button.primary:hover {
|
| background: #7c3aed !important;
|
| }
|
|
|
| /* Secondary Buttons (Refresh Preview & Clear Session) */
|
| #report-actions button.secondary {
|
| background: #322b48 !important;
|
| color: #ffffff !important;
|
| border: 1px solid #6366f1 !important;
|
| }
|
| #report-actions button.secondary:hover {
|
| background: #191836 !important;
|
| border-color: rgba(99, 102, 241, 0.8) !important;
|
| }
|
|
|
| """
|
|
|