hari-huynh commited on
Commit
b0747e4
0 Parent(s):

First Commit

Browse files
.github/workflows/hfspace_cicd.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ push:
4
+ branches: [main]
5
+
6
+ # to run this workflow manually from the Actions tab
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ sync-to-hub:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+ with:
15
+ fetch-depth: 0
16
+ lfs: true
17
+ - name: Push to hub
18
+ env:
19
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
+ run: git push https://haihuynh:[email protected]/spaces/haihuynh/jobs-qa-kg main
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .venv
2
+ .idea
Dockerfile ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:latest
5
+
6
+ RUN apt-get update
7
+
8
+ WORKDIR /code
9
+
10
+ COPY ./requirements.txt /code/requirements.txt
11
+
12
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
13
+
14
+ # Set up a new user named "user" with user ID 1000
15
+ RUN useradd -m -u 1000 user
16
+
17
+ # Switch to the "user" user
18
+ USER user
19
+
20
+ # Set home to the user's home directory
21
+ ENV HOME=/home/user \
22
+ PATH=/home/user/.local/bin:$PATH
23
+
24
+ # Set the working directory to the user's home directory
25
+ WORKDIR $HOME/app
26
+
27
+ # Try and run pip command after setting the user with `USER user` to avoid permission issues with Python
28
+ RUN pip install --no-cache-dir --upgrade pip
29
+
30
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
31
+ COPY --chown=user . $HOME/app
32
+
33
+ CMD ["python", "main.py"]
README.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: JobsQA KnowledgeGraph
3
+ sdk: docker
4
+ emoji: 👀
5
+ colorFrom: yellow
6
+ colorTo: indigo
7
+ ---
main.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from utils import llm_answer
3
+
4
+ # demo = gr.ChatInterface(
5
+ # fn = echo,
6
+ # multimodal= True, # Allow upload CV
7
+ # examples = ["hello", "hola", "merhaba"],
8
+ # title= "Echo Bot",
9
+ # description= "Chatbot to answer about job postings & recommend jobs based on your CV.",
10
+ # theme= None,
11
+ # autofocus= True,
12
+ # fill_height= False,
13
+ # )
14
+
15
+
16
+ demo = gr.ChatInterface(
17
+ fn= llm_answer,
18
+ examples=[],
19
+ title="Jobs QA & Recommendations Chatbot",
20
+ multimodal=True,
21
+ )
22
+
23
+ if __name__ == "__main__":
24
+ demo.launch(server_name = "0.0.0.0", server_port = 7860)
prompts/cypher_prompt.yaml ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ input_variables: [schema, question]
2
+ output_parser: null
3
+ template: |
4
+ Task:Generate Cypher statement to query a graph database.
5
+ Instructions:
6
+ Use only the provided relationship types and properties in the schema.
7
+ Do not use any other relationship types or properties that are not provided.
8
+ Schema:
9
+ {schema}
10
+ Note: Do not include any explanations or apologies in your responses.
11
+ Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.
12
+ Do not include any text except the generated Cypher statement.
13
+
14
+ Examples:
15
+ Find all jobs in the 'Software Engineering' industry that offer remote work options and require 'Python' skills?
16
+ MATCH (j:Job)
17
+ WHERE j.name CONTAINS 'Software Engineer'
18
+ AND j.work_mode = 'Remote'
19
+ AND (j)-[:REQUIRES]->(:Skill {{name: "Python"}})
20
+ RETURN j AS job
21
+
22
+ Which companies located in 'San Francisco' are hiring for 'Data Scientist' roles with a 'Master's Degree' requirement?
23
+ MATCH (c:Company)-[:LOCATES_IN]->(l:Location {{name: "San Francisco"}})
24
+ WHERE (c)-[:RECRUITES]->(j:Job {{name: "Data Scientist"}})
25
+ AND (j)-[:REQUIRES]->(e:Education {{name: "Master's Degree"}})
26
+ RETURN DISTINCT c.name AS company
27
+
28
+ What are the most common skills required for 'Product Manager' jobs across different industries?
29
+ MATCH (j:Job {{name: "Product Manager"}})-[:REQUIRES]->(s:Skill)
30
+ RETURN s.name, count(*) AS skill_count
31
+ ORDER BY skill_count DESC
32
+ LIMIT 10
33
+
34
+ Find all jobs that require at least 5 years of experience and a 'Bachelor's Degree' in 'Computer Science':
35
+ MATCH (j:Job)-[:REQUIRES]->(e:Education {{name: "Bachelor's Degree", fields: "Computer Science"}})
36
+ WHERE (j)-[:REQUIRES]->(we:Work_Exper {{duration: "5 years"}})
37
+ RETURN j AS job
38
+
39
+ Identify companies that are subsidiaries of 'Google' and are recruiting for 'Software Engineer' roles with 'Senior' level.
40
+ MATCH (g:Company {{name: "Google"}})<-[:SUBDIARY]-(c:Company)
41
+ WHERE (c)-[:RECRUITES]->(j:Job {{name: "Software Engineer"}})
42
+ AND (j)-[:AT_LEVEL]->(wl:Work_LV {{name: "Senior"}})
43
+ RETURN DISTINCT c.name AS company
44
+
45
+ Find companies recruiting "Machine Learning" jobs and their corresponding job titles.
46
+ MATCH (company: Company)-[:RECRUITES]->(job: Job)
47
+ WHERE job.name CONTAINS "Machine Learning"
48
+ RETURN company.name as company_name, job.name as job_title
49
+
50
+ The question is:
51
+ {question}
52
+
53
+ template_format: f-string
prompts/qa_prompt.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ input_variables: [context, question]
2
+ output_parser: null
3
+ template: |
4
+ Task: answer the question you are given based on the context provided.
5
+ Instructions:
6
+ You are an assistant that helps to form nice and human understandable answers.
7
+ Use the context information provided to generate a well organized and comprehensive answer to the user's question.
8
+ When the provided information contains multiple elements, structure your answer as a bulleted or numbered list to enhance clarity and readability.
9
+ You must use the information to construct your answer.
10
+ The provided information is authoritative; do not doubt it or try to use your internal knowledge to correct it.
11
+ Make the answer sound like a response to the question without mentioning that you based the result on the given information.
12
+ If there is no information provided, say that the knowledge base returned empty results.
13
+ You should answer result in Vietnamese.
14
+
15
+ Here's the information:
16
+ {context}
17
+
18
+ Question: {question}
19
+ Answer:
20
+ template_format: f-string
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ langchain
3
+ langchain-community
4
+ langchain-google-genai
5
+ neo4j
utils.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import yaml
3
+ from dotenv import load_dotenv
4
+ from langchain_google_genai import ChatGoogleGenerativeAI
5
+ from langchain_community.graphs import Neo4jGraph
6
+ from langchain_core.prompts.prompt import PromptTemplate
7
+ from langchain.chains import GraphCypherQAChain
8
+ from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
9
+
10
+ def config():
11
+ # load_dotenv()
12
+
13
+ # Set up Neo4J & Gemini API
14
+ os.environ["NEO4J_URI"] = os.getenv("NEO4J_URI")
15
+ os.environ["NEO4J_USERNAME"] = os.getenv("NEO4J_USERNAME")
16
+ os.environ["NEO4J_PASSWORD"] = os.getenv("NEO4J_PASSWORD")
17
+ os.environ["GOOGLE_API_KEY"] = os.getenv("GEMINI_API_KEY")
18
+
19
+ def load_prompt(filepath):
20
+ with open(filepath, "r") as file:
21
+ prompt = yaml.safe_load(file)
22
+
23
+ return prompt
24
+
25
+ def init_():
26
+ config()
27
+ knowledge_graph = Neo4jGraph()
28
+ llm_chat = ChatGoogleGenerativeAI(
29
+ model= "gemini-1.5-flash-latest"
30
+ )
31
+
32
+ # Connect to Neo4J Knowledge Graph
33
+ cypher_prompt = load_prompt("hf_space/prompts/cypher_prompt.yaml")
34
+ qa_prompt = load_prompt("hf_space/prompts/qa_prompt.yaml")
35
+
36
+ CYPHER_GENERATION_PROMPT = PromptTemplate(**cypher_prompt)
37
+ QA_GENERATION_PROMPT = PromptTemplate(**qa_prompt)
38
+
39
+ chain = GraphCypherQAChain.from_llm(
40
+ llm_chat, graph=knowledge_graph, verbose=True,
41
+ cypher_prompt= CYPHER_GENERATION_PROMPT,
42
+ qa_prompt= QA_GENERATION_PROMPT
43
+ )
44
+
45
+ return chain
46
+
47
+ # Init GraphQA Chain
48
+ chain = init_()
49
+
50
+ def get_llm_response(query):
51
+ return chain.invoke({"query": query})["result"]
52
+
53
+
54
+ def llm_answer(message, history):
55
+ # history_langchain_format = []
56
+ #
57
+ # for human, ai in history:
58
+ # history_langchain_format.append(HumanMessage(content= human))
59
+ # history_langchain_format.append(AIMessage(content= ai))
60
+ #
61
+ # history_langchain_format.append(HumanMessage(content= message["text"]))
62
+
63
+ try:
64
+ response = get_llm_response(message["text"])
65
+ except Exception:
66
+ response = "Exception"
67
+ except Error:
68
+ response = "Error"
69
+ return response
70
+
71
+ # if __name__ == "__main__":
72
+ # message = "Have any company recruiting jobs about Machine Learning and coresponding job titles?"
73
+ # history = [("What's your name?", "My name is Gemini")]
74
+ # resp = llm_answer(message, history)
75
+ # print(resp)
76
+