File size: 9,782 Bytes
eb93f4c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b962757
 
 
 
 
 
 
 
 
 
eb93f4c
 
9d90398
3bdf77b
 
eb93f4c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
import os
import queue
from threading import Thread
from dotenv import load_dotenv

print(f"Start loading .env")
load_dotenv()
print(f"Finish loading .env")

from langchain.callbacks.base import BaseCallbackHandler
print(f"Start importing from rag_func")
from rag_func import prepare_RAG, retrieve_RAG, generate_RAG
print(f"Finish importing from rag_func")
import gradio as gr

# ----------------- Context Setup -----------------
# For local execution, where the user can select the directory
# user_input = input("Enter a subfolder inside 'context' (press Enter for full 'context'): ").strip()
# if user_input:
#     user_dir = os.path.join("context", user_input)
# else:
#     user_dir = "context"
# print(f"[Info] Using context directory: {user_dir}")

# For gradio deploy. The user cannot choose the directory
user_dir = "context"

pinecone_API = os.getenv("PINECONE_API")
index_name = "regulatorik"
#llm_model = os.getenv("MODELNAME")
llm_model="gpt-5-nano"

index, pc, llm = prepare_RAG(pinecone_API, index_name, llm_model=llm_model, dir_name=user_dir)

# ----------------- Chat Functions -----------------
def add_user_message(message, history):
    history = history or []
    history.append({"role": "user", "content": message})
    return "", history, history

import time

# ----------------- Streaming Handler -----------------
class StreamHandler(BaseCallbackHandler):
    def __init__(self, q: queue.Queue):
        self.q = q
        self.first_token_received = False
        self.ttft = None  # time to first token

    def on_llm_new_token(self, token: str, **kwargs):
        if not self.first_token_received:
            self.ttft = time.time() - self.start_time
            self.first_token_received = True
        self.q.put(token)

    def on_llm_end(self, *args, **kwargs):
        self.total_time = time.time() - self.start_time
        self.q.put("[[END]]")

# ----------------- Chat Functions with timing -----------------
def generate_bot_response(history):
    if not history or history[-1]["role"] != "user":
        yield history, history
        return

    user_msg = history[-1]["content"]
    retrieved_chunks = retrieve_RAG(user_msg, pc, index)

    q = queue.Queue()
    handler = StreamHandler(q)
    handler.start_time = time.time()

    model_name = getattr(llm, "model_name", getattr(llm, "model", None))
    streaming_llm = llm.__class__(model=model_name, streaming=True, callbacks=[handler])

    def run_llm():
        try:
            generate_RAG(user_msg, streaming_llm, retrieved_chunks)
        finally:
            q.put("[[END]]")

    Thread(target=run_llm, daemon=True).start()

    partial = ""
    history.append({"role": "assistant", "content": ""})

    while True:
        token = q.get()
        if token == "[[END]]":
            print(f"[Timing] TTFT: {handler.ttft:.3f} s, Total: {handler.total_time:.3f} s")
            break
        partial += token
        history[-1]["content"] = partial
        yield history, history

# ----------------- Simplified CSS for Default Gradio Font -----------------
custom_css = """

:root {

    --brand-blue: #17428f;

    --brand-orange: #f39719;

    --text-dark: #111827;   /* very dark grey (near black) */

    --text-gray: #4B5563;   /* medium grey for messages */

    color-scheme: only light;

}



body, .gradio-container {

    /* Default Gradio font will be used */

    background: linear-gradient(135deg, var(--brand-blue) 0%, var(--brand-orange) 100%);

    min-height: 100vh;

    color: var(--text-dark);

}



/* Top bar transparent */

#topbar { background: transparent !important; }



/* Header text over gradient */

#header h1, #header h2, #header h3, #header h4, #header h5, #header h6,

#header p {

    color: #ffffff;

    text-align: center;

}



/* Chatbox container */

#chatbot {

    height: 600px;

    border-radius: 14px;

    border: 2px solid var(--brand-blue);

    background-color: #ffffff;

    padding: 8px;

    overflow-y: auto;

}



/* Chat text */

#chatbot, #chatbot * { color: var(--text-gray) !important; }



/* Bubble styling */

#chatbot .message.user {

    background: #fff4e1;

    border-radius: 10px;

    padding: 6px 12px;

    color: var(--text-gray) !important;

    text-align: right;

}

#chatbot .message.bot {

    background: #f0f0f0;

    border-radius: 10px;

    padding: 6px 12px;

    color: var(--text-gray) !important;

    text-align: left;

}



/* Fallback selectors for other Gradio versions */

#chatbot [data-testid*="message"] { border-radius: 10px; padding: 6px 12px; }

#chatbot [data-testid="user-message"] { background: #fff4e1; color: var(--text-gray) !important; text-align: right; }

#chatbot [data-testid="assistant-message"] { background: #f0f0f0; color: var(--text-gray) !important; text-align: left; }



/* Inputs */

input[type="text"], textarea, .gr-text-input input, .gr-textbox textarea {

    border-radius: 10px;

    padding: 10px;

    font-size: 16px;

    border: 2px solid var(--brand-orange);

}

input:focus, textarea:focus, .gr-text-input input:focus, .gr-textbox textarea:focus {

    border-color: var(--brand-blue);

    outline: none;

    box-shadow: 0 0 6px rgba(23, 66, 143, 0.5);

}



/* Buttons (global gradient) */

.gr-button, button {

    border-radius: 10px;

    font-weight: 600;

    background: linear-gradient(90deg, var(--brand-blue), var(--brand-orange));

    color: white;

    border: none;

}

.gr-button:hover, button:hover {

    transform: translateY(-2px);

    box-shadow: 0 4px 8px rgba(0,0,0,0.2);

}



/* Chat area: icon-only buttons */

#chatbot {

  --icon-light: #9CA3AF;

  --icon-hover: #6B7280;

}



/* Tint SVG icons */

#chatbot button svg,

#chatbot [role="button"] svg,

#chatbot .icon svg,

#chatbot [class*="icon"] svg,

#chatbot [data-testid*="icon"] svg,

#chatbot [data-testid*="message"] .tools svg,

#chatbot .message-tools svg {

  color: var(--icon-light) !important;

  fill: var(--icon-light) !important;

  stroke: var(--icon-light) !important;

  opacity: 0.95;

}



/* Remove gradient background only on small icon-only buttons */

#chatbot :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient) {

  background: transparent !important;

  background-image: none !important;

  border: none !important;

  box-shadow: none !important;

  padding: 6px !important;

  border-radius: 8px !important;

  color: var(--icon-light) !important;

}



/* Hover/focus/active states */

#chatbot :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient):hover {

  background-color: rgba(0,0,0,0.05) !important;

}

#chatbot :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient):focus-visible {

  outline: none !important;

  box-shadow: 0 0 0 2px rgba(23, 66, 143, 0.35) !important;

  background-color: rgba(0,0,0,0.06) !important;

}

#chatbot :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient):active {

  background-color: rgba(0,0,0,0.08) !important;

}



/* Optional 'danger' icons */

#chatbot .danger svg { color: var(--icon-light) !important; fill: var(--icon-light) !important; stroke: var(--icon-light) !important; }

#chatbot .danger:hover svg { color: #ef4444 !important; fill: #ef4444 !important; stroke: #ef4444 !important; }



#topbar .gr-button.keep-gradient,

#topbar .gr-button:not(:has(svg)) {

  background: linear-gradient(90deg, var(--brand-blue), var(--brand-orange)) !important;

  color: #fff !important;

}



/* Icon-only buttons in topbar: transparent */

#topbar :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient) {

  background: transparent !important;

  border: none !important;

  box-shadow: none !important;

  padding: 6px !important;

  border-radius: 8px !important;

  color: var(--icon-light) !important;

}



/* Tint SVGs in topbar */

#topbar :is(button,[role="button"]):has(> svg) > svg {

  color: var(--icon-light) !important;

  fill: var(--icon-light) !important;

  stroke: var(--icon-light) !important;

  opacity: 0.95;

}



/* Hover/focus/active for topbar icons */

#topbar :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient):hover {

  background-color: rgba(0,0,0,0.05) !important;

}

#topbar :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient):focus-visible {

  outline: none !important;

  box-shadow: 0 0 0 2px rgba(23, 66, 143, 0.35) !important;

  background-color: rgba(0,0,0,0.06) !important;

}

#topbar :is(button,[role="button"]):is([aria-label],[title], :has(> svg)):not(.keep-gradient):active {

  background-color: rgba(0,0,0,0.08) !important;

}



"""

js_force_light = """ function refresh() { const url = new URL(window.location); if (url.searchParams.get('__theme') !== 'light') { url.searchParams.set('__theme', 'light'); window.location.replace(url); } } """

# ----------------- Gradio App -----------------
with gr.Blocks(css=custom_css, fill_height=True, js=js_force_light) as demo:

    gr.Markdown(
        "<h1>📚 RAG Chat Assistant</h1>"
        "<p>Ask questions and get accurate answers from your documents.</p>",
        elem_id="header"
    )

    chatbot = gr.Chatbot(type="messages", label="Conversation", elem_id="chatbot", height=600)
    msg = gr.Textbox(label="Your message", placeholder="Type your question here...")
    state = gr.State([])

    msg.submit(add_user_message, inputs=[msg, state], outputs=[msg, chatbot, state]) \
       .then(generate_bot_response, inputs=[state], outputs=[chatbot, state])

# Launch
if __name__ == "__main__":
    demo.launch(share=True)