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()