Isshi14 commited on
Commit
b94e7b3
Β·
verified Β·
1 Parent(s): ecb1165

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -54
app.py CHANGED
@@ -1,70 +1,248 @@
 
 
 
1
  import gradio as gr
2
  from huggingface_hub import InferenceClient
3
 
 
 
4
 
5
- def respond(
6
- message,
7
- history: list[dict[str, str]],
8
- system_message,
9
- max_tokens,
10
- temperature,
11
- top_p,
12
- hf_token: gr.OAuthToken,
13
- ):
14
- """
15
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
16
- """
17
- client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
18
 
19
- messages = [{"role": "system", "content": system_message}]
 
 
 
 
 
 
 
 
20
 
21
- messages.extend(history)
 
 
 
 
 
 
 
 
 
22
 
23
- messages.append({"role": "user", "content": message})
 
 
 
 
24
 
25
- response = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- for message in client.chat_completion(
28
- messages,
29
- max_tokens=max_tokens,
30
- stream=True,
31
- temperature=temperature,
32
- top_p=top_p,
33
- ):
34
- choices = message.choices
35
- token = ""
36
- if len(choices) and choices[0].delta.content:
37
- token = choices[0].delta.content
 
 
 
 
 
 
 
38
 
39
- response += token
40
- yield response
 
 
 
 
 
 
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- chatbot = gr.ChatInterface(
47
- respond,
48
- type="messages",
49
- additional_inputs=[
50
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
51
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
52
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
53
- gr.Slider(
54
- minimum=0.1,
55
- maximum=1.0,
56
- value=0.95,
57
- step=0.05,
58
- label="Top-p (nucleus sampling)",
59
- ),
60
- ],
61
- )
62
-
63
- with gr.Blocks() as demo:
64
- with gr.Sidebar():
65
- gr.LoginButton()
66
- chatbot.render()
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  if __name__ == "__main__":
70
- demo.launch()
 
1
+ import os
2
+ import json
3
+ import numpy as np
4
  import gradio as gr
5
  from huggingface_hub import InferenceClient
6
 
7
+ # --- Configuration ---
8
+ KNOWLEDGE_BASE_DIR = "knowledge_base"
9
 
10
+ # --- Step 1: Load documents ---
11
+ def load_documents():
12
+ documents = []
13
+ filenames = []
14
+ for filename in os.listdir(KNOWLEDGE_BASE_DIR):
15
+ if filename.endswith(".txt"):
16
+ filepath = os.path.join(KNOWLEDGE_BASE_DIR, filename)
17
+ with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
18
+ content = f.read().strip()
19
+ if content:
20
+ documents.append(content)
21
+ filenames.append(filename)
22
+ return documents, filenames
23
 
24
+ # --- Step 2: Chunk documents ---
25
+ def chunk_text(text, chunk_size=500, overlap=100):
26
+ chunks = []
27
+ start = 0
28
+ while start < len(text):
29
+ end = start + chunk_size
30
+ chunks.append(text[start:end])
31
+ start += chunk_size - overlap
32
+ return chunks
33
 
34
+ # --- Step 3: Get embeddings via HF API ---
35
+ def get_embeddings(texts, client):
36
+ embeddings = []
37
+ for text in texts:
38
+ response = client.feature_extraction(text, model="sentence-transformers/all-MiniLM-L6-v2")
39
+ emb = np.array(response)
40
+ if emb.ndim == 2:
41
+ emb = emb.mean(axis=0)
42
+ embeddings.append(emb)
43
+ return np.array(embeddings)
44
 
45
+ # --- Step 4: Simple vector search with numpy ---
46
+ def cosine_similarity(a, b):
47
+ a_norm = a / (np.linalg.norm(a, axis=-1, keepdims=True) + 1e-10)
48
+ b_norm = b / (np.linalg.norm(b, axis=-1, keepdims=True) + 1e-10)
49
+ return np.dot(a_norm, b_norm.T)
50
 
51
+ class SimpleVectorStore:
52
+ def __init__(self):
53
+ self.chunks = []
54
+ self.sources = []
55
+ self.embeddings = None
56
+
57
+ def add(self, chunks, sources, embeddings):
58
+ self.chunks = chunks
59
+ self.sources = sources
60
+ self.embeddings = embeddings
61
+
62
+ def search(self, query_embedding, top_k=3):
63
+ scores = cosine_similarity(query_embedding.reshape(1, -1), self.embeddings)[0]
64
+ top_indices = np.argsort(scores)[-top_k:][::-1]
65
+ results = [(self.chunks[i], self.sources[i], float(scores[i])) for i in top_indices]
66
+ return results
67
 
68
+ # --- Step 5: Build the knowledge store ---
69
+ def build_store(documents, filenames, client):
70
+ all_chunks = []
71
+ all_sources = []
72
+
73
+ for doc, fname in zip(documents, filenames):
74
+ chunks = chunk_text(doc)
75
+ for chunk in chunks:
76
+ all_chunks.append(chunk)
77
+ all_sources.append(fname)
78
+
79
+ print(f"Embedding {len(all_chunks)} chunks via API...")
80
+ embeddings = get_embeddings(all_chunks, client)
81
+ print("Embeddings complete.")
82
+
83
+ store = SimpleVectorStore()
84
+ store.add(all_chunks, all_sources, embeddings)
85
+ return store
86
 
87
+ # --- Step 6: RAG query ---
88
+ def query_rag(question, store, client):
89
+ q_emb = get_embeddings([question], client)
90
+ results = store.search(q_emb[0], top_k=3)
91
+
92
+ context = "\n\n".join([chunk for chunk, src, score in results])
93
+
94
+ system_prompt = f"""You are an AI Twin that represents a person. Use ONLY the following context to answer the question.
95
+ If you don't know the answer from the context, say "I don't have that information in my profile."
96
 
97
+ Context:
98
+ {context}"""
99
+
100
+ try:
101
+ response = client.chat_completion(
102
+ messages=[
103
+ {"role": "system", "content": system_prompt},
104
+ {"role": "user", "content": question}
105
+ ],
106
+ model="meta-llama/Meta-Llama-3-8B-Instruct",
107
+ max_tokens=512,
108
+ temperature=0.3,
109
+ )
110
+ return response.choices[0].message.content.strip()
111
+ except Exception as e:
112
+ return f"Error: {str(e)}"
113
 
114
+ # --- Initialization ---
115
+ print("Starting AI Twin...")
116
+ hf_token = os.environ.get("HUGGINGFACEHUB_API_TOKEN", None)
117
+ hf_client = InferenceClient(token=hf_token)
118
+
119
+ docs, fnames = load_documents()
120
+ print(f"Loaded {len(docs)} documents: {fnames}")
121
+
122
+ vector_store = build_store(docs, fnames, hf_client)
123
+ print("Ready!")
124
+
125
+ # --- Helpers ---
126
+ def load_profile():
127
+ try:
128
+ with open(os.path.join(KNOWLEDGE_BASE_DIR, "profile.txt"), "r", encoding="utf-8") as f:
129
+ return f.read()
130
+ except:
131
+ return "Profile not found."
132
+
133
+ def respond(message, chat_history):
134
+ if not message:
135
+ return "", chat_history
136
+ if chat_history is None:
137
+ chat_history = []
138
+ chat_history.append({"role": "user", "content": message})
139
+ try:
140
+ answer = query_rag(message, vector_store, hf_client)
141
+ chat_history.append({"role": "assistant", "content": answer})
142
+ except Exception as e:
143
+ chat_history.append({"role": "assistant", "content": f"Error: {str(e)}"})
144
+ return "", chat_history
145
+
146
+ # When a suggestion chip is clicked, fill the textbox with that prompt
147
+ def use_suggestion(prompt_text):
148
+ return prompt_text
149
+
150
+ # --- Default prompt suggestions ---
151
+ SUGGESTIONS = [
152
+ "πŸ’Ό What are my skills?",
153
+ "πŸš€ What projects have I done?",
154
+ "🎯 What roles am I eligible for?",
155
+ "πŸŽ“ What is my educational background?",
156
+ "🌍 What languages do I speak?",
157
+ "πŸ“ž How can someone contact me?",
158
+ ]
159
+
160
+ # --- Custom CSS for suggestion chips ---
161
+ custom_css = """
162
+ #suggestion-row {
163
+ display: flex;
164
+ flex-wrap: wrap;
165
+ gap: 8px;
166
+ margin-bottom: 10px;
167
+ }
168
+
169
+ .suggestion-chip {
170
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
171
+ color: white !important;
172
+ border: none !important;
173
+ border-radius: 20px !important;
174
+ padding: 6px 14px !important;
175
+ font-size: 13px !important;
176
+ cursor: pointer !important;
177
+ transition: transform 0.15s ease, box-shadow 0.15s ease !important;
178
+ white-space: nowrap !important;
179
+ }
180
+
181
+ .suggestion-chip:hover {
182
+ transform: translateY(-2px) !important;
183
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.45) !important;
184
+ }
185
+
186
+ .suggestion-chip:active {
187
+ transform: translateY(0px) !important;
188
+ }
189
+
190
+ #chatbot-col {
191
+ display: flex;
192
+ flex-direction: column;
193
+ }
194
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
+ # --- UI ---
197
+ with gr.Blocks(title="My AI Twin", theme=gr.themes.Soft(), css=custom_css) as demo:
198
+ gr.Markdown("# πŸ€– My AI Twin")
199
+ gr.Markdown("Ask me anything about my professional background, skills, and projects β€” or pick a suggestion below!")
200
+
201
+ with gr.Row():
202
+ # Left: Profile summary
203
+ with gr.Column(scale=1):
204
+ gr.Markdown("### πŸ“‹ Profile Summary")
205
+ gr.Textbox(
206
+ value=load_profile(),
207
+ label="About Me",
208
+ interactive=False,
209
+ lines=15,
210
+ )
211
+
212
+ # Right: Chat + suggestions
213
+ with gr.Column(scale=2, elem_id="chatbot-col"):
214
+ chatbot = gr.Chatbot(label="Conversation", height=380, type="messages")
215
+
216
+ # --- Suggestion chips ---
217
+ gr.Markdown("**πŸ’‘ Suggested questions β€” click to use:**")
218
+ with gr.Row(elem_id="suggestion-row"):
219
+ chip_btns = [
220
+ gr.Button(s, elem_classes=["suggestion-chip"], size="sm")
221
+ for s in SUGGESTIONS
222
+ ]
223
+
224
+ # --- Input area ---
225
+ msg = gr.Textbox(
226
+ label="Ask a question",
227
+ placeholder="Type your own question, or click a suggestion above…",
228
+ lines=1,
229
+ )
230
+ with gr.Row():
231
+ submit_btn = gr.Button("Submit", variant="primary")
232
+ clear_btn = gr.Button("Clear")
233
+
234
+ # Wire up suggestion chips β†’ fill textbox
235
+ for chip, suggestion in zip(chip_btns, SUGGESTIONS):
236
+ chip.click(
237
+ fn=use_suggestion,
238
+ inputs=[gr.State(suggestion)],
239
+ outputs=[msg],
240
+ )
241
+
242
+ # Wire up submit / enter
243
+ msg.submit(respond, [msg, chatbot], [msg, chatbot])
244
+ submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
245
+ clear_btn.click(lambda: (None, ""), None, [chatbot, msg], queue=False)
246
 
247
  if __name__ == "__main__":
248
+ demo.launch()