From 4aacd6b63aa90df1ffbb63fbc51b1b9b033552ad Mon Sep 17 00:00:00 2001 From: Saurabh Gupta Date: Sun, 21 Sep 2025 17:56:31 -0400 Subject: [PATCH] Week 2, Day 4 - OpenAI + Gradio + Tools Project --- .../day4_linkedin_job_search_assistant.ipynb | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 week2/community-contributions/day4_linkedin_job_search_assistant.ipynb diff --git a/week2/community-contributions/day4_linkedin_job_search_assistant.ipynb b/week2/community-contributions/day4_linkedin_job_search_assistant.ipynb new file mode 100644 index 0000000..d1cadc9 --- /dev/null +++ b/week2/community-contributions/day4_linkedin_job_search_assistant.ipynb @@ -0,0 +1,245 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2a8071da", + "metadata": {}, + "source": [ + "Week 2, Day 4 - Use of Tools with openAI - Saurabh Gupta. For any feedback, please mail it to srbmisc@gmail.com, I would love connecting with you to discuss more on this, potentially improving it. Thank you for your read.\n" + ] + }, + { + "cell_type": "markdown", + "id": "08f50ff5", + "metadata": {}, + "source": [ + "# πŸ” LinkedIn Job Search Conversationally with OpenAI and JobSpy\n", + "\n", + "Looking for a job on LinkedIn but tired of manually scrolling through endless listings? This Python project simplifies your job hunt by automating the process of searching LinkedIn job postings based on your **desired role** and **location**.\n", + "\n", + "## πŸš€ What It Does\n", + "\n", + "Using the powerful Python scraping library **`jobspy`** [Github Link](https://github.com/speedyapply/JobSpy), this tool fetches **real-time job listings from LinkedIn** tailored to your input. Just provide:\n", + "\n", + "\n", + "- πŸ§‘β€πŸ’» **Job Title** (e.g., *Data Scientist*, *Frontend Developer*)\n", + "- πŸ“ **Location** (e.g., *New York*, *Remote*, *Berlin*)\n", + "\n", + "…and the script will return up to **3 curated job listings** matching your criteria β€” complete with **direct links to the job posts** on LinkedIn.\n", + "\n", + "## βœ… Key Features\n", + "\n", + "- πŸ”Ž Real-time LinkedIn job scraping \n", + "- ⚑ Fast and minimalistic – only the top 3 results \n", + "- πŸ”— Includes clickable links to the job listings \n", + "- πŸ’¬ Simple CLI input β€” no setup hassles\n", + "\n", + "## πŸ“Œ Use Case\n", + "\n", + "Whether you're actively job hunting or just exploring opportunities in your field, this tool helps you **stay updated** with fresh job listings β€” without opening a browser.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af6d6650", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from jobspy import scrape_jobs\n", + "except:\n", + " %pip install -U python-jobspy\n", + " from jobspy import scrape_jobs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a08eb7af", + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "\n", + "import os\n", + "import json\n", + "from dotenv import load_dotenv\n", + "from openai import OpenAI\n", + "import gradio as gr\n", + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11a280b1", + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv(override=True)\n", + "\n", + "openai_api_key = os.getenv('OPENAI_API_KEY')\n", + "if openai_api_key:\n", + " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n", + "else:\n", + " print(\"OpenAI API Key not set\")\n", + " \n", + "MODEL = \"gpt-4.1-mini\"\n", + "openai = OpenAI()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbac4cb6", + "metadata": {}, + "outputs": [], + "source": [ + "def callExternalAPI(title, location) -> str:\n", + " jobs = pd.DataFrame(scrape_jobs(\n", + " site_name=[\"linkedin\"],\n", + " search_term=title,\n", + " location=location,\n", + " results_wanted=3,\n", + " hours_old=48\n", + " ))\n", + " print(f\"Found {len(jobs)} jobs\")\n", + " if len(jobs) > 0:\n", + " df = pd.DataFrame(jobs.loc[:,['title','company','location','date_posted','job_url']])\n", + " df = df.fillna('N/A')\n", + " return df.to_json()\n", + " return 'No results found'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b44a92f6", + "metadata": {}, + "outputs": [], + "source": [ + "system_message = ''' You are a helpful assistant for a Jobs Agency who helps users with job listings available.\n", + "Give short, courteous answers to the users. Tell the user you can bring max 3 job listings at once. Present the job listings to the user in nice Markdown.\n", + "Always be accurate. If you don't know the answer, say so.'''" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "339ac21b", + "metadata": {}, + "outputs": [], + "source": [ + "getData_function = {\n", + " \"name\": \"callExternalAPI\",\n", + " \"description\": '''Get the Job listings for the provided job title and/or location. Call this whenever the user needs specific job listings data for the title or location that they provide. Following are few examples - \n", + " 1. Give me the jobs available for Software Engineer at Gurugram, India. - In such case, you can call this function by passing the job_title as Software Engineer and job_location as Gurugram, India.\n", + " 2. Give me the jobs available for Software Engineer. - In such case, you can call this function by passing the job_title as Software Engineer and job_location as blank string.\n", + " 3. Give me the jobs available at Gurugram, India. - In such case, you can call this function by passing the job_title as blank string and job_location as Gurugram, India.\n", + " ''',\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"job_title\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The title or position or designation of the job the user is interested in\",\n", + " },\n", + " \"job_location\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"The location of the job the user is interested in\",\n", + " }\n", + " },\n", + " \"required\": [\"job_title\",\"job_location\"],\n", + " \"additionalProperties\": False\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "892250a4", + "metadata": {}, + "outputs": [], + "source": [ + "tools = [{\"type\": \"function\", \"function\": getData_function}]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4be6fd80", + "metadata": {}, + "outputs": [], + "source": [ + "# We have to write that function handle_tool_call:\n", + "\n", + "def handle_tool_call(message):\n", + " tool_call = message.tool_calls[0]\n", + " arguments = json.loads(tool_call.function.arguments)\n", + " job_title = arguments.get('job_title')\n", + " job_location = arguments.get('job_location')\n", + " jobs = callExternalAPI(job_title,job_location)\n", + " response = {\n", + " \"role\": \"tool\",\n", + " \"content\": json.dumps({\"job_title\": job_title,\"job_location\": job_location,\"job_listings\": jobs}),\n", + " \"tool_call_id\": tool_call.id\n", + " }\n", + " return response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d0b5b08", + "metadata": {}, + "outputs": [], + "source": [ + "def chat(message, history):\n", + " messages = [{\"role\": \"system\", \"content\": system_message}] + history + [{\"role\": \"user\", \"content\": message}]\n", + " response = openai.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n", + "\n", + " if response.choices[0].finish_reason==\"tool_calls\":\n", + " message = response.choices[0].message\n", + " response = handle_tool_call(message)\n", + " messages.append(message)\n", + " messages.append(response)\n", + " response = openai.chat.completions.create(model=MODEL, messages=messages)\n", + " \n", + " return response.choices[0].message.content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "085b4aae", + "metadata": {}, + "outputs": [], + "source": [ + "gr.ChatInterface(fn=chat, type=\"messages\").launch()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}