File size: 8,870 Bytes
b142561 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | import os
import logging
import sys
import gradio as gr
from pinecone import Pinecone
from llama_index.core import VectorStoreIndex, Settings, StorageContext
from llama_index.vector_stores.pinecone import PineconeVectorStore
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
# --- Logging ---
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
# --- Secrets from Hugging Face Spaces ---
PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
if not PINECONE_API_KEY:
raise ValueError("Missing PINECONE_API_KEY in Hugging Face Space secrets.")
if not OPENAI_API_KEY:
raise ValueError("Missing OPENAI_API_KEY in Hugging Face Space secrets.")
# --- LlamaIndex Settings ---
Settings.llm = OpenAI(
model="gpt-4o-mini",
temperature=0.2,
api_key=OPENAI_API_KEY
)
Settings.embed_model = OpenAIEmbedding(
model="text-embedding-ada-002",
api_key=OPENAI_API_KEY
)
Settings.chunk_size = 600
Settings.chunk_overlap = 200
# --- System Prompt ---
system_prompt = '''
You are Ayesha, the Decoding Data Science (DDS) Enterprise HR Chatbot. Your objective is to interact politely and professionally with employees, answering only HR-related questions. Use only information directly from the connected HR documents to provide your answers. Always provide an explicit citation indicating the document source for every answer. Do not offer information or suggestions beyond what is present in these documents.
If the requested information cannot be found in the connected documents, politely instruct the user to email connect@decodingdatascience.com for further assistance. For questions outside of HR, inform the user that you can only answer HR-related questions. If a question is unclear or possibly HR-related but ambiguous, ask the user to rephrase. Never attempt to answer non-HR, personal, or unrelated questions.
- Remain polite and professional in all interactions.
- Respond exclusively to HR-related topics using only the connected HR documents.
- Always include an explicit citation to the relevant document(s) for every answer.
- If a question is off-topic, state that you can only address HR questions.
- If you are unsure whether the question is HR-related, politely ask for clarification or rephrasing.
- For HR-related questions where the answer is not in the connected documents, kindly direct the user to email connect@decodingdatascience.com.
- Use a friendly and formal tone.
Respond in short, clear paragraphs (2-4 sentences). Do not use markdown or code blocks. Every answer must include the citation showing which document(s) the information is sourced from.
'''
# --- Connect to existing Pinecone index ---
index_name = "quickstart"
pc = Pinecone(api_key=PINECONE_API_KEY)
pinecone_index = pc.Index(index_name)
# --- Connect LlamaIndex to existing Pinecone vector store ---
vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_vector_store(
vector_store=vector_store,
storage_context=storage_context
)
# --- Query Engine ---
query_engine = index.as_query_engine(
system_prompt=system_prompt,
similarity_top_k=2
)
# --- App Logic (unchanged) ---
def query_doc(prompt):
try:
if not prompt or not prompt.strip():
return "Please enter an HR-related question."
response = query_engine.query(prompt)
return str(response)
except Exception as e:
return f"Error: {str(e)}"
def fill_example(example_text):
return example_text
# IMPORTANT:
# GitHub "blob" links do not directly render as images in many apps.
# Use the RAW version of your logo URL instead.
LOGO_URL = "https://raw.githubusercontent.com/Decoding-Data-Science/airesidency/main/dds-logo-removebg-preview.png"
faq_questions = [
"What is the leave policy?",
"How many annual leave days do employees get?",
"What is the probation period policy?",
"What is the work from home policy?",
"How is sick leave handled?",
"What is the process for reimbursement claims?",
"What are the office working hours?",
"How does the company handle public holidays?"
]
custom_css = """
body {
font-family: Arial, sans-serif;
}
.gradio-container {
max-width: 1250px !important;
margin: auto !important;
}
.hero-card, .faq-card, .chat-card, .note-card {
border-radius: 16px;
padding: 18px;
border: 1px solid #e5e7eb;
background: #ffffff;
box-shadow: 0 4px 14px rgba(0,0,0,0.06);
}
.brand-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 6px;
color: #111827;
}
.brand-subtitle {
font-size: 15px;
color: #4b5563;
line-height: 1.6;
}
.section-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 10px;
color: #111827;
}
.small-note {
font-size: 13px;
color: #6b7280;
line-height: 1.5;
}
.logo-wrap {
display: flex;
align-items: center;
gap: 14px;
margin-bottom: 10px;
}
.logo-wrap img {
max-height: 72px;
width: auto;
}
.footer-note {
font-size: 12px;
color: #6b7280;
margin-top: 8px;
}
"""
with gr.Blocks(css=custom_css, title="DDS Enterprise HR Chatbot") as demo:
gr.HTML(
f"""
<div class="hero-card">
<div class="logo-wrap">
<img src="{LOGO_URL}" alt="DDS Logo">
<div>
<div class="brand-title">DDS Enterprise HR Chatbot</div>
<div class="brand-subtitle">
A professional HR assistant powered by LlamaIndex, Pinecone, and OpenAI.
Ask policy-related questions based only on indexed HR documents.
</div>
</div>
</div>
</div>
"""
)
with gr.Row(equal_height=True):
with gr.Column(scale=1, min_width=320):
gr.HTML('<div class="faq-card">')
gr.Markdown("### HR FAQ / Quick Questions")
gr.Markdown(
"Use the examples below to quickly test common HR policy questions."
)
example_box = gr.Textbox(
label="Selected FAQ Question",
placeholder="Click an example below or type your own question on the right."
)
with gr.Column():
for q in faq_questions:
btn = gr.Button(q, variant="secondary")
btn.click(fn=fill_example, inputs=gr.State(q), outputs=example_box)
gr.Markdown(
"### Suggested Topics\n"
"- Leave policy\n"
"- Sick leave\n"
"- Reimbursements\n"
"- Working hours\n"
"- Probation period\n"
"- Public holidays"
)
gr.HTML(
"""
<div class="footer-note">
This assistant only answers HR-related questions grounded in connected HR documents.
</div>
"""
)
gr.HTML("</div>")
with gr.Column(scale=2, min_width=500):
gr.HTML('<div class="chat-card">')
gr.Markdown("### Ask Your HR Question")
prompt = gr.Textbox(
label="Your Question",
placeholder="Example: What is the annual leave policy for full-time employees?",
lines=4
)
answer = gr.Textbox(
label="Assistant Response",
lines=12,
interactive=False
)
with gr.Row():
ask_btn = gr.Button("Ask HR Assistant", variant="primary")
clear_btn = gr.Button("Clear")
gr.Markdown(
"### Notes\n"
"- Responses are based only on indexed HR documents.\n"
"- Every answer should include a document citation.\n"
"- If the answer is not found, users are directed to connect@decodingdatascience.com."
)
gr.HTML("</div>")
gr.HTML('<div class="note-card" style="margin-top:12px;">')
gr.Markdown("### Copy Selected FAQ into the Question Box")
use_example_btn = gr.Button("Use Selected FAQ Question", variant="secondary")
gr.HTML("</div>")
# Button actions
ask_btn.click(fn=query_doc, inputs=prompt, outputs=answer)
prompt.submit(fn=query_doc, inputs=prompt, outputs=answer)
use_example_btn.click(fn=fill_example, inputs=example_box, outputs=prompt)
clear_btn.click(
fn=lambda: ("", "", ""),
inputs=[],
outputs=[prompt, answer, example_box]
)
if __name__ == "__main__":
demo.launch() |