Spaces:
Sleeping
Sleeping
File size: 7,692 Bytes
d1d1019 | 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 |
import json
import networkx as nx
import numpy as np
import os
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
from duckduckgo_search import DDGS
try:
import chromadb
from chromadb.config import Settings
CHROMA_AVAILABLE = True
except ImportError:
CHROMA_AVAILABLE = False
print("⚠ ChromaDB not found. Falling back to in-memory vector search.")
# Config
DATA_PATH = "data/mental_health_knowledge.json"
MODEL_NAME = 'all-MiniLM-L6-v2'
CHROMA_HOST = os.getenv("CHROMA_HOST", "localhost")
CHROMA_PORT = int(os.getenv("CHROMA_PORT", 8000))
class RAGEngine:
def __init__(self):
print("Initializing RAG Engine (ChromaDB + Graph + Web)...")
self.encoder = SentenceTransformer(MODEL_NAME)
# Initialize ChromaDB Client
try:
if CHROMA_AVAILABLE:
if os.getenv("CHROMA_HOST"):
self.chroma_client = chromadb.HttpClient(host=CHROMA_HOST, port=CHROMA_PORT)
else:
self.chroma_client = chromadb.PersistentClient(path="./chroma_db") # Persistent local storage
self.collection = self.chroma_client.get_or_create_collection(name="mental_health_docs")
self.use_chroma = True
print(f"✓ Connected to ChromaDB (Persistent)")
else:
self.use_chroma = False
except Exception as e:
print(f"⚠ ChromaDB connection failed ({e}), falling back to in-memory vector search.")
self.use_chroma = False
self.load_knowledge_base()
def load_knowledge_base(self):
with open(DATA_PATH, 'r') as f:
data = json.load(f)
self.documents = data['documents']
self.nodes = data['nodes']
self.edges = data['edges']
# Populate Vector DB if using Chroma
if self.use_chroma:
try:
if self.collection.count() == 0:
print("Populating ChromaDB...")
ids = [d['id'] for d in self.documents]
docs = [d['content'] for d in self.documents]
metadatas = [{'title': d['title']} for d in self.documents]
embeddings = self.encoder.encode(docs).tolist()
self.collection.add(
embeddings=embeddings,
documents=docs,
metadatas=metadatas,
ids=ids
)
except Exception as e:
print(f"Error populating Chroma: {e}")
else:
# Fallback
self.doc_texts = [d['content'] for d in self.documents]
self.doc_embeddings = self.encoder.encode(self.doc_texts)
# Build Knowledge Graph
self.graph = nx.DiGraph()
for node in self.nodes:
self.graph.add_node(node['id'], **node)
for edge in self.edges:
self.graph.add_edge(edge['source'], edge['target'], relation=edge['relation'])
def vector_search(self, query, top_k=2):
if self.use_chroma:
try:
query_vec = self.encoder.encode([query]).tolist()
results = self.collection.query(
query_embeddings=query_vec,
n_results=top_k
)
output = []
# Chroma returns list of lists
if results['documents']:
for i, doc in enumerate(results['documents'][0]):
meta = results['metadatas'][0][i]
output.append({
'source': 'Local Knowledge (Vector)',
'title': meta['title'],
'content': doc,
'score': 0.9 # Chroma doesn't always return normalized score easily
})
return output
except Exception as e:
print(f"Chroma search error: {e}")
return []
else:
# Fallback implementation
query_vec = self.encoder.encode([query])
sims = cosine_similarity(query_vec, self.doc_embeddings)[0]
top_indices = np.argsort(sims)[-top_k:][::-1]
results = []
for idx in top_indices:
if sims[idx] > 0.3:
results.append({
'source': 'Local Knowledge',
'title': self.documents[idx]['title'],
'content': self.documents[idx]['content'],
'score': float(sims[idx])
})
return results
def graph_search(self, query):
"""Find related concepts in graph"""
related_nodes = []
query_lower = query.lower()
start_nodes = [n for n in self.graph.nodes if n in query_lower or query_lower in n]
for start_node in start_nodes:
neighbors = list(self.graph.neighbors(start_node))
related_nodes.extend(neighbors)
related_nodes.append(start_node)
results = []
unique_nodes = set(related_nodes)
for node_id in unique_nodes:
node_data = self.graph.nodes[node_id]
desc = node_data.get('description', '')
results.append({
'source': 'Knowledge Graph',
'title': f"Concept: {node_id.title()}",
'content': f"{node_id.title()} ({node_data.get('type')}): {desc}",
'score': 1.0
})
return results
def web_search(self, query, max_results=2):
print(f"Searching web for: {query}")
results = []
try:
with DDGS() as ddgs:
context_query = f"{query} mental health research"
search_results = list(ddgs.text(context_query, max_results=max_results))
for r in search_results:
results.append({
'source': 'Web Search (DuckDuckGo)',
'title': r['title'],
'content': r['body'],
'url': r['href'],
'score': 0.8
})
except Exception as e:
print(f"Web search failed: {e}")
return results
def save_memory(self, text, metadata=None):
if self.use_chroma:
try:
import uuid
doc_id = str(uuid.uuid4())
embedding = self.encoder.encode([text]).tolist()
self.collection.add(
embeddings=embedding,
documents=[text],
metadatas=[metadata or {"source": "user_memory"}],
ids=[doc_id]
)
print(f"saved memory {doc_id}")
except Exception as e:
print(f"Error saving memory: {e}")
def query(self, user_query):
vector_results = self.vector_search(user_query)
graph_results = self.graph_search(user_query)
web_results = self.web_search(user_query)
all_results = vector_results + graph_results + web_results
seen = set()
final_results = []
for r in all_results:
if r['content'] not in seen:
seen.add(r['content'])
final_results.append(r)
return final_results
|