# Job Tracker Agent - First Version with Gradio + JSON Memory + CSV Export (No OpenAI Key) # - Uses Hugging Face Inference API (Mistral-7B) # - Extracts job info from text # - Saves to JSON + allows CSV export import gradio as gr import os import json import csv import requests from datetime import datetime JOB_LOG_FILE = "job_memory.json" CSV_EXPORT_FILE = "job_memory.csv" HF_API_URL = "https://api-inference.huggingface.co/models/HuggingFaceH4/zephyr-7b-beta" # Optional: use your Hugging Face token for more reliability HF_API_TOKEN = os.getenv("HUG_API_TOKEN") # or leave None for free/public use headers = {"Authorization": f"Bearer {HF_API_TOKEN}"} if HF_API_TOKEN else {} # Initialize memory file if not os.path.exists(JOB_LOG_FILE): with open(JOB_LOG_FILE, "w") as f: json.dump([], f) # Extract job info using Hugging Face Inference API def extract_job_details(text): prompt = f""" Extract structured JSON with the following fields from the input text: - company - role - location - source - status - link (if present) - follow_up_date (in YYYY-MM-DD if mentioned, else null) Input: {text} Output: """ payload = { "inputs": prompt, "parameters": {"max_new_tokens": 500}, "options": {"wait_for_model": True} } try: response = requests.post(HF_API_URL, headers=headers, json=payload) response.raise_for_status() result = response.json() if isinstance(result, dict) and "error" in result: return json.dumps(result) if isinstance(result, list) and "generated_text" in result[0]: return result[0]["generated_text"].split("Output:")[-1].strip() return json.dumps({"error": "Unexpected response format"}) except Exception as e: return json.dumps({"error": str(e)}) # Store to JSON file def store_job_data(parsed_json): try: job_data = json.loads(parsed_json) job_data["logged_at"] = datetime.now().isoformat() with open(JOB_LOG_FILE, "r+") as f: existing = json.load(f) existing.append(job_data) f.seek(0) json.dump(existing, f, indent=2) return "✅ Job logged successfully." except Exception as e: return f"❌ Error logging job: {e}" # Read memory safely with missing key handling def get_job_history(): with open(JOB_LOG_FILE, "r") as f: entries = json.load(f) formatted = "\n\n".join([ f"{e.get('role', '[Missing Role]')} at {e.get('company', '[Missing Company]')} ({e.get('status', 'Unknown')}) - {e.get('follow_up_date', 'N/A')}" for e in entries ]) return formatted or "No jobs tracked yet." # Export to CSV def export_to_csv(): try: with open(JOB_LOG_FILE, "r") as f: entries = json.load(f) if entries: keys = set() for entry in entries: keys.update(entry.keys()) keys = sorted(keys) with open(CSV_EXPORT_FILE, "w", newline='') as f: writer = csv.DictWriter(f, fieldnames=keys) writer.writeheader() writer.writerows(entries) return f"✅ Exported to {CSV_EXPORT_FILE}" return "❌ No job data to export." except Exception as e: return f"❌ Export failed: {e}" # Gradio logic def job_tracker_interface(user_input): parsed_json = extract_job_details(user_input) if "error" in parsed_json: return parsed_json, "❌ Failed to log job.", get_job_history() store_status = store_job_data(parsed_json) memory_state = get_job_history() return parsed_json, store_status, memory_state def export_interface(): return export_to_csv() # Gradio app demo = gr.Interface( fn=job_tracker_interface, inputs=gr.Textbox(lines=10, label="Paste a job description or job-related email"), outputs=[ gr.Textbox(label="🧠 Extracted Job Info (JSON)"), gr.Textbox(label="📥 Save Status"), gr.Textbox(label="📂 Job Tracker Memory (Current Session)") ], title="Job Tracker AI (v1) - No OpenAI Key", description="Paste job descriptions or emails. This AI agent uses Mistral-7B to extract job data, save it in memory, and export to CSV." ) export_demo = gr.Interface( fn=export_interface, inputs=[], outputs=gr.Textbox(label="📤 Export Result"), title="Export to CSV", description="Click the button below to export your job data to a CSV file." ) demo.launch() # Optionally launch CSV export in a separate tab # export_demo.launch()