Isshi14 commited on
Commit
e48d327
·
verified ·
1 Parent(s): e84cd12

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +64 -79
  2. requirements.txt +0 -1
app.py CHANGED
@@ -1,15 +1,14 @@
1
  import os
 
 
2
  import gradio as gr
3
- import chromadb
4
  from huggingface_hub import InferenceClient
5
 
6
  # --- Configuration ---
7
  KNOWLEDGE_BASE_DIR = "knowledge_base"
8
- COLLECTION_NAME = "ai_twin_kb"
9
 
10
  # --- Step 1: Load documents ---
11
  def load_documents():
12
- """Loads all .txt files from the knowledge base directory."""
13
  documents = []
14
  filenames = []
15
  for filename in os.listdir(KNOWLEDGE_BASE_DIR):
@@ -24,7 +23,6 @@ def load_documents():
24
 
25
  # --- Step 2: Chunk documents ---
26
  def chunk_text(text, chunk_size=500, overlap=100):
27
- """Splits text into overlapping chunks."""
28
  chunks = []
29
  start = 0
30
  while start < len(text):
@@ -33,77 +31,67 @@ def chunk_text(text, chunk_size=500, overlap=100):
33
  start += chunk_size - overlap
34
  return chunks
35
 
36
- # --- Step 3: Get embeddings via HF API (no local model!) ---
37
  def get_embeddings(texts, client):
38
- """Gets embeddings from Hugging Face Inference API."""
39
- embeddings = client.feature_extraction(
40
- texts,
41
- model="sentence-transformers/all-MiniLM-L6-v2"
42
- )
43
- # The API returns nested lists, convert to list of lists
44
- result = []
45
- for emb in embeddings:
46
- if isinstance(emb[0], list):
47
- # Mean pooling if token-level embeddings returned
48
- import numpy as np
49
- arr = np.array(emb)
50
- pooled = arr.mean(axis=0).tolist()
51
- result.append(pooled)
52
- else:
53
- result.append(emb)
54
- return result
55
-
56
- # --- Step 4: Build vector store ---
57
- def build_vector_store(documents, filenames, client):
58
- """Creates embeddings via API and stores them in ChromaDB."""
59
- chroma_client = chromadb.Client()
60
- try:
61
- chroma_client.delete_collection(COLLECTION_NAME)
62
- except:
63
- pass
64
- collection = chroma_client.create_collection(name=COLLECTION_NAME)
65
 
 
 
 
 
 
 
 
 
66
  all_chunks = []
67
- all_ids = []
68
- all_metadata = []
69
- chunk_id = 0
70
 
71
  for doc, fname in zip(documents, filenames):
72
  chunks = chunk_text(doc)
73
  for chunk in chunks:
74
  all_chunks.append(chunk)
75
- all_ids.append(f"chunk_{chunk_id}")
76
- all_metadata.append({"source": fname})
77
- chunk_id += 1
78
 
79
- print(f"Generating embeddings for {len(all_chunks)} chunks via API...")
80
- # Process in batches to avoid API limits
81
- batch_size = 16
82
- all_embeddings = []
83
- for i in range(0, len(all_chunks), batch_size):
84
- batch = all_chunks[i:i+batch_size]
85
- batch_embeddings = get_embeddings(batch, client)
86
- all_embeddings.extend(batch_embeddings)
87
- print(f" Processed {min(i+batch_size, len(all_chunks))}/{len(all_chunks)} chunks")
88
 
89
- collection.add(
90
- documents=all_chunks,
91
- embeddings=all_embeddings,
92
- ids=all_ids,
93
- metadatas=all_metadata
94
- )
 
 
95
 
96
- return collection
97
-
98
- # --- Step 5: RAG query function ---
99
- def query_rag(question, collection, client):
100
- """Retrieves relevant chunks and generates an answer."""
101
- q_embedding = get_embeddings([question], client)
102
-
103
- results = collection.query(query_embeddings=q_embedding, n_results=3)
104
- context = "\n\n".join(results["documents"][0])
105
 
106
- prompt = f"""You are an AI Twin that represents a person. Use ONLY the following context to answer the question.
107
  If you don't know the answer from the context, say "I don't have that information in my profile."
108
 
109
  Context:
@@ -123,31 +111,29 @@ Answer:"""
123
  )
124
  return response.strip()
125
  except Exception as e:
126
- return f"Error generating response: {str(e)}"
127
 
128
- # --- Global Initialization ---
129
- print("Initializing HF client...")
130
  hf_token = os.environ.get("HUGGINGFACEHUB_API_TOKEN", None)
131
  hf_client = InferenceClient(token=hf_token)
132
 
133
- print("Loading documents...")
134
  docs, fnames = load_documents()
135
  print(f"Loaded {len(docs)} documents: {fnames}")
136
 
137
- print("Building vector store (using API for embeddings)...")
138
- kb_collection = build_vector_store(docs, fnames, hf_client)
139
- print("Vector store ready!")
140
 
141
- # --- Gradio UI ---
142
- def load_profile_summary():
143
  try:
144
  with open(os.path.join(KNOWLEDGE_BASE_DIR, "profile.txt"), "r", encoding="utf-8") as f:
145
  return f.read()
146
- except FileNotFoundError:
147
  return "Profile not found."
148
 
149
- def ask_ai_twin(message, chat_history):
150
- answer = query_rag(message, kb_collection, hf_client)
151
  chat_history.append((message, answer))
152
  return "", chat_history
153
 
@@ -158,8 +144,7 @@ with gr.Blocks(title="My AI Twin", theme=gr.themes.Soft()) as demo:
158
  with gr.Row():
159
  with gr.Column(scale=1):
160
  gr.Markdown("### 📋 Profile Summary")
161
- profile_content = load_profile_summary()
162
- gr.Textbox(value=profile_content, label="About Me", interactive=False, lines=15)
163
 
164
  with gr.Column(scale=2):
165
  chatbot = gr.Chatbot(label="Conversation", height=400)
@@ -168,8 +153,8 @@ with gr.Blocks(title="My AI Twin", theme=gr.themes.Soft()) as demo:
168
  submit_btn = gr.Button("Submit", variant="primary")
169
  clear = gr.Button("Clear")
170
 
171
- msg.submit(ask_ai_twin, [msg, chatbot], [msg, chatbot])
172
- submit_btn.click(ask_ai_twin, [msg, chatbot], [msg, chatbot])
173
  clear.click(lambda: None, None, chatbot, queue=False)
174
 
175
  if __name__ == "__main__":
 
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):
 
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):
 
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
+ 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:
 
111
  )
112
  return response.strip()
113
  except Exception as e:
114
+ return f"Error: {str(e)}"
115
 
116
+ # --- Initialization ---
117
+ print("Starting AI Twin...")
118
  hf_token = os.environ.get("HUGGINGFACEHUB_API_TOKEN", None)
119
  hf_client = InferenceClient(token=hf_token)
120
 
 
121
  docs, fnames = load_documents()
122
  print(f"Loaded {len(docs)} documents: {fnames}")
123
 
124
+ vector_store = build_store(docs, fnames, hf_client)
125
+ print("Ready!")
 
126
 
127
+ # --- UI ---
128
+ def load_profile():
129
  try:
130
  with open(os.path.join(KNOWLEDGE_BASE_DIR, "profile.txt"), "r", encoding="utf-8") as f:
131
  return f.read()
132
+ except:
133
  return "Profile not found."
134
 
135
+ def respond(message, chat_history):
136
+ answer = query_rag(message, vector_store, hf_client)
137
  chat_history.append((message, answer))
138
  return "", chat_history
139
 
 
144
  with gr.Row():
145
  with gr.Column(scale=1):
146
  gr.Markdown("### 📋 Profile Summary")
147
+ gr.Textbox(value=load_profile(), label="About Me", interactive=False, lines=15)
 
148
 
149
  with gr.Column(scale=2):
150
  chatbot = gr.Chatbot(label="Conversation", height=400)
 
153
  submit_btn = gr.Button("Submit", variant="primary")
154
  clear = gr.Button("Clear")
155
 
156
+ msg.submit(respond, [msg, chatbot], [msg, chatbot])
157
+ submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
158
  clear.click(lambda: None, None, chatbot, queue=False)
159
 
160
  if __name__ == "__main__":
requirements.txt CHANGED
@@ -1,4 +1,3 @@
1
- chromadb
2
  gradio
3
  huggingface-hub
4
  numpy
 
 
1
  gradio
2
  huggingface-hub
3
  numpy