wolfgangspoony commited on
Commit
9febb03
Β·
1 Parent(s): bf1369a

Fix: Clean DeepSeek integration with persistent index and proper architecture

Browse files
Files changed (4) hide show
  1. app.py +53 -9
  2. error.png +0 -0
  3. knowledge.py +245 -150
  4. requirements.txt +1 -1
app.py CHANGED
@@ -1,8 +1,23 @@
1
  import gradio as gr
2
- from knowledge import agent, agent_ctx, memory, run_with_trace, MEMORY_FILE, load_memory
 
 
 
 
 
 
 
 
3
 
4
  PIN = "1969"
5
 
 
 
 
 
 
 
 
6
 
7
  def check_pin(pin_input):
8
  if pin_input == PIN:
@@ -16,10 +31,12 @@ def load_history():
16
  if not sessions:
17
  return "No conversation history yet."
18
  output = []
19
- for i, ex in enumerate(sessions):
20
- output.append(f"[{i+1}] {ex.get('timestamp', '?')}")
21
  output.append(f"You: {ex.get('user', '')}")
22
  output.append(f"Brain: {ex.get('agent', '')}")
 
 
23
  output.append("-" * 40)
24
  return "\n".join(output)
25
 
@@ -30,20 +47,46 @@ with gr.Blocks(title="Brain") as demo:
30
  chatbot = gr.Chatbot(label="Brain", height=500)
31
  msg = gr.Textbox(placeholder="Type here...", show_label=False)
32
  send_btn = gr.Button("Send")
 
 
 
 
 
 
 
 
 
33
 
34
  async def respond(message, history):
35
  if not message.strip():
36
  return "", history
37
- if agent is None or agent_ctx is None:
38
- history.append([message, "Error: System failed to initialize."])
 
 
 
 
 
39
  return "", history
 
40
  try:
41
- reasoning, response, searches = await run_with_trace(
42
- agent, agent_ctx, message, memory
43
  )
44
- history.append([message, response])
 
 
 
 
 
 
 
45
  return "", history
 
46
  except Exception as e:
 
 
 
47
  history.append([message, f"Error: {str(e)}"])
48
  return "", history
49
 
@@ -74,4 +117,5 @@ with gr.Blocks(title="Brain") as demo:
74
  refresh_btn.click(fn=load_history, outputs=[history_display])
75
  pin_btn.click(fn=load_history, outputs=[history_display])
76
 
77
- demo.launch(theme=gr.themes.Soft())
 
 
1
  import gradio as gr
2
+ from knowledge import (
3
+ run_agent,
4
+ load_memory,
5
+ get_or_build_index,
6
+ SearchTool,
7
+ MEMORY_FILE,
8
+ DEEPSEEK_API_KEY,
9
+ init,
10
+ )
11
 
12
  PIN = "1969"
13
 
14
+ # Initialize on startup
15
+ print("Initializing Brain...")
16
+ index, memory = init()
17
+ search_tool = SearchTool(index, top_k=10) if index else None
18
+ print(f"API Key configured: {bool(DEEPSEEK_API_KEY)}")
19
+ print(f"Index loaded: {bool(index)}")
20
+
21
 
22
  def check_pin(pin_input):
23
  if pin_input == PIN:
 
31
  if not sessions:
32
  return "No conversation history yet."
33
  output = []
34
+ for i, ex in enumerate(reversed(sessions)): # Most recent first
35
+ output.append(f"[{len(sessions)-i}] {ex.get('timestamp', '?')}")
36
  output.append(f"You: {ex.get('user', '')}")
37
  output.append(f"Brain: {ex.get('agent', '')}")
38
+ if ex.get('searches'):
39
+ output.append(f"Searches: {', '.join(ex['searches'])}")
40
  output.append("-" * 40)
41
  return "\n".join(output)
42
 
 
47
  chatbot = gr.Chatbot(label="Brain", height=500)
48
  msg = gr.Textbox(placeholder="Type here...", show_label=False)
49
  send_btn = gr.Button("Send")
50
+
51
+ # Status indicator
52
+ status_msg = "βœ… System ready"
53
+ if not DEEPSEEK_API_KEY:
54
+ status_msg = "❌ Error: DEEPSEEK_API_KEY not configured"
55
+ elif not index:
56
+ status_msg = "⚠️ Warning: Knowledge index not loaded"
57
+
58
+ status_text = gr.Markdown(status_msg)
59
 
60
  async def respond(message, history):
61
  if not message.strip():
62
  return "", history
63
+
64
+ if not DEEPSEEK_API_KEY:
65
+ history.append([message, "Error: DEEPSEEK_API_KEY not configured."])
66
+ return "", history
67
+
68
+ if not search_tool:
69
+ history.append([message, "Error: Knowledge index not available."])
70
  return "", history
71
+
72
  try:
73
+ response, searches = await run_agent(
74
+ search_tool, message, memory, DEEPSEEK_API_KEY
75
  )
76
+
77
+ # Format response with search indicator
78
+ display_response = response
79
+ if searches:
80
+ search_info = f"πŸ” Searches: {', '.join(searches)}\n\n"
81
+ display_response = search_info + response
82
+
83
+ history.append([message, display_response])
84
  return "", history
85
+
86
  except Exception as e:
87
+ import traceback
88
+ error_msg = f"Error: {str(e)}\n{traceback.format_exc()}"
89
+ print(error_msg)
90
  history.append([message, f"Error: {str(e)}"])
91
  return "", history
92
 
 
117
  refresh_btn.click(fn=load_history, outputs=[history_display])
118
  pin_btn.click(fn=load_history, outputs=[history_display])
119
 
120
+ if __name__ == "__main__":
121
+ demo.launch(theme=gr.themes.Soft())
error.png ADDED
knowledge.py CHANGED
@@ -1,47 +1,161 @@
1
  """
2
- Brain.
3
  """
4
  import os
5
  import json
 
6
  from pathlib import Path
7
  from datetime import datetime
 
 
8
 
9
  from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
10
- from llama_index.core.tools import QueryEngineTool, ToolMetadata
11
- from llama_index.core.agent.workflow import ReActAgent, AgentStream, ToolCallResult
12
- from llama_index.core.workflow import Context
13
- from llama_index.core.storage import StorageContext
14
- from llama_index.llms.openai_like import OpenAILike
15
  from llama_index.embeddings.huggingface import HuggingFaceEmbedding
16
  from llama_index.vector_stores.chroma import ChromaVectorStore
 
17
  import chromadb
18
 
19
- NOTES_DIR = Path(__file__).parent / "KNOWLEDGE"
 
20
  MEMORY_FILE = Path(__file__).parent / "memory.json"
 
21
 
22
- # If on HF Spaces with persistent storage, use /data
23
  if os.environ.get("SPACE_ID"):
24
  persistent = Path("/data")
25
  if persistent.exists():
26
  MEMORY_FILE = persistent / "memory.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
 
28
 
29
- TOOL_DESCRIPTION = (
30
- "Search the notes. Contains political analysis, power structures research, "
31
- "cultural commentary, personal conversations, diary entries."
32
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
 
35
- # === PERSISTENT MEMORY ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  def load_memory():
38
- """Load persistent memory from disk."""
 
 
 
 
 
39
  if MEMORY_FILE.exists():
40
  try:
41
- return json.loads(MEMORY_FILE.read_text(encoding="utf-8"))
 
42
  except Exception:
43
  pass
44
- return {"sessions": [], "insights": []}
 
 
45
 
46
 
47
  def save_memory(memory):
@@ -53,7 +167,7 @@ def save_memory(memory):
53
  print(f"Warning: Could not save memory: {e}")
54
 
55
 
56
- def record_exchange(memory, user_msg, agent_response, searches):
57
  """Record a conversation exchange to persistent memory."""
58
  exchange = {
59
  "timestamp": datetime.now().isoformat(),
@@ -61,160 +175,141 @@ def record_exchange(memory, user_msg, agent_response, searches):
61
  "agent": agent_response,
62
  "searches": searches,
63
  }
64
-
65
- if not memory.get("sessions"):
66
  memory["sessions"] = []
67
-
68
  memory["sessions"].append(exchange)
69
- if len(memory["sessions"]) > 50:
70
- memory["sessions"] = memory["sessions"][-50:]
71
-
 
 
72
  save_memory(memory)
73
 
74
 
75
- def get_memory_context(memory):
76
- """Build a memory context string from recent sessions."""
77
  if not memory.get("sessions"):
78
- return ""
79
-
80
- recent = memory["sessions"][-5:]
81
- lines = ["Earlier in this conversation:"]
82
- for ex in recent:
83
- lines.append(f"User: {ex['user']}")
84
- lines.append(f"Response: {ex['agent']}")
85
- lines.append("")
86
- return "\n".join(lines)
87
 
88
 
89
- # === BUILD AGENT ===
90
 
91
- def get_api_key():
92
- """Get DeepSeek API key from environment or file."""
93
- api_key = os.environ.get("DEEPSEEK_API_KEY", "")
94
- if api_key:
95
- return api_key
 
 
96
 
97
- # Try to read from file
98
- key_file = Path(__file__).parent / "DEEPSEEK_API_KEY.txt"
99
- if key_file.exists():
100
- try:
101
- api_key = key_file.read_text(encoding="utf-8").strip()
102
- if api_key:
103
- return api_key
104
- except Exception:
105
- pass
106
- return ""
107
-
108
-
109
- def build_agent():
110
- """Build the full agentic pipeline."""
111
 
112
- api_key = get_api_key()
113
- if not api_key:
114
- print("ERROR: DEEPSEEK_API_KEY not found in environment or DEEPSEEK_API_KEY.txt")
115
- return None
116
-
117
- llm = OpenAILike(
118
- model="deepseek-chat",
119
- api_base="https://api.deepseek.com/v1",
120
- api_key=api_key,
121
- is_chat_model=True,
122
- context_window=64000,
123
- max_tokens=2048,
124
- temperature=0.7,
125
- )
126
-
127
- embed_model = HuggingFaceEmbedding(model_name="all-MiniLM-L6-v2")
128
-
129
- Settings.llm = llm
130
- Settings.embed_model = embed_model
131
-
132
- if not NOTES_DIR.exists():
133
- print(f"WARNING: KNOWLEDGE directory not found at {NOTES_DIR}")
134
- return None
135
-
136
- print("Loading documents from KNOWLEDGE...")
137
- documents = SimpleDirectoryReader(
138
- str(NOTES_DIR),
139
- recursive=True,
140
- required_exts=[".md", ".txt"],
141
- ).load_data()
142
- print(f"Loaded {len(documents)} documents.")
143
 
144
- chroma_client = chromadb.Client()
145
- chroma_collection = chroma_client.get_or_create_collection("notes")
146
- vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
147
- storage_context = StorageContext.from_defaults(vector_store=vector_store)
148
 
149
- print("Building vector index...")
150
- index = VectorStoreIndex.from_documents(
151
- documents,
152
- storage_context=storage_context,
153
- show_progress=True,
154
- )
155
- print("Index ready.")
156
-
157
- query_engine = index.as_query_engine(similarity_top_k=15)
158
- notes_tool = QueryEngineTool(
159
- query_engine=query_engine,
160
- metadata=ToolMetadata(
161
- name="search_notes",
162
- description=TOOL_DESCRIPTION,
163
- ),
164
- )
165
-
166
- agent = ReActAgent(
167
- name="brain",
168
- tools=[notes_tool],
169
- llm=llm,
170
- )
171
 
172
- return agent
173
 
 
 
174
 
175
- async def run_with_trace(agent, ctx, message, memory):
176
- """Run the agent and capture response."""
177
- mem_context = get_memory_context(memory)
178
- augmented = message
179
- if mem_context:
180
- augmented = f"{mem_context}\n\n{message}"
181
 
182
- handler = agent.run(augmented, ctx=ctx)
183
 
184
- reasoning_parts = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  searches = []
186
- final_text = ""
187
-
188
- async for ev in handler.stream_events():
189
- if isinstance(ev, AgentStream):
190
- final_text += ev.delta
191
- elif isinstance(ev, ToolCallResult):
192
- searches.append(ev.tool_name)
193
- reasoning_parts.append({
194
- "type": "tool_call",
195
- "tool": ev.tool_name,
196
- "result_preview": str(ev.tool_output.content)[:300] if ev.tool_output else "N/A",
197
- })
198
-
199
- response = await handler
200
- if not final_text:
201
- final_text = str(response)
202
-
203
- record_exchange(memory, message, final_text, searches)
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
- return reasoning_parts, final_text, searches
206
 
 
207
 
208
- # === STARTUP ===
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
- print("Starting up...")
211
- agent = build_agent()
212
- memory = load_memory()
213
 
214
- if agent:
215
- agent_ctx = Context(agent)
216
- session_count = len(memory.get("sessions", []))
217
- print(f"Ready. {session_count} prior exchanges loaded.")
218
- else:
219
- agent_ctx = None
220
- print("Failed to start.")
 
1
  """
2
+ Brain - Clean DeepSeek Integration
3
  """
4
  import os
5
  import json
6
+ import asyncio
7
  from pathlib import Path
8
  from datetime import datetime
9
+ from typing import List, Dict, Tuple
10
+ import httpx
11
 
12
  from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
 
 
 
 
 
13
  from llama_index.embeddings.huggingface import HuggingFaceEmbedding
14
  from llama_index.vector_stores.chroma import ChromaVectorStore
15
+ from llama_index.core.storage import StorageContext
16
  import chromadb
17
 
18
+ # Paths
19
+ KNOWLEDGE_DIR = Path(__file__).parent / "KNOWLEDGE"
20
  MEMORY_FILE = Path(__file__).parent / "memory.json"
21
+ CHROMA_DIR = Path(__file__).parent / ".chroma"
22
 
23
+ # HF Spaces persistent storage
24
  if os.environ.get("SPACE_ID"):
25
  persistent = Path("/data")
26
  if persistent.exists():
27
  MEMORY_FILE = persistent / "memory.json"
28
+ CHROMA_DIR = persistent / ".chroma"
29
+
30
+ DEEPSEEK_API_KEY = ""
31
+ _search_index = None
32
+ _memory_cache = None
33
+
34
+
35
+ def get_api_key() -> str:
36
+ """Get DeepSeek API key from environment or file."""
37
+ global DEEPSEEK_API_KEY
38
+
39
+ api_key = os.environ.get("DEEPSEEK_API_KEY", "")
40
+ if api_key:
41
+ DEEPSEEK_API_KEY = api_key
42
+ return api_key
43
+
44
+ key_file = Path(__file__).parent / "DEEPSEEK_API_KEY.txt"
45
+ if key_file.exists():
46
+ try:
47
+ api_key = key_file.read_text(encoding="utf-8").strip()
48
+ if api_key:
49
+ DEEPSEEK_API_KEY = api_key
50
+ return api_key
51
+ except Exception:
52
+ pass
53
+ return ""
54
+
55
 
56
+ # === VECTOR SEARCH ===
57
 
58
+ def get_or_build_index():
59
+ """Get existing index or build new one. Persists to disk."""
60
+ global _search_index
61
+
62
+ if _search_index is not None:
63
+ return _search_index
64
+
65
+ embed_model = HuggingFaceEmbedding(model_name="all-MiniLM-L6-v2")
66
+ Settings.embed_model = embed_model
67
+
68
+ if not KNOWLEDGE_DIR.exists():
69
+ print(f"WARNING: KNOWLEDGE directory not found at {KNOWLEDGE_DIR}")
70
+ return None
71
+
72
+ # Try to load existing persistent index
73
+ if CHROMA_DIR.exists():
74
+ try:
75
+ print("Loading existing vector index...")
76
+ chroma_client = chromadb.PersistentClient(path=str(CHROMA_DIR))
77
+ chroma_collection = chroma_client.get_or_create_collection("knowledge")
78
+ vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
79
+ storage_context = StorageContext.from_defaults(vector_store=vector_store)
80
+ _search_index = VectorStoreIndex.from_vector_store(
81
+ vector_store, storage_context=storage_context
82
+ )
83
+ print("Index loaded from disk.")
84
+ return _search_index
85
+ except Exception as e:
86
+ print(f"Could not load existing index: {e}")
87
+ print("Rebuilding index...")
88
+
89
+ # Build new index
90
+ print("Loading documents from KNOWLEDGE...")
91
+ documents = SimpleDirectoryReader(
92
+ str(KNOWLEDGE_DIR),
93
+ recursive=True,
94
+ required_exts=[".md", ".txt"],
95
+ ).load_data()
96
+ print(f"Loaded {len(documents)} documents.")
97
+
98
+ CHROMA_DIR.mkdir(parents=True, exist_ok=True)
99
+ chroma_client = chromadb.PersistentClient(path=str(CHROMA_DIR))
100
+ chroma_collection = chroma_client.get_or_create_collection("knowledge")
101
+ vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
102
+ storage_context = StorageContext.from_defaults(vector_store=vector_store)
103
+
104
+ print("Building vector index...")
105
+ _search_index = VectorStoreIndex.from_documents(
106
+ documents,
107
+ storage_context=storage_context,
108
+ show_progress=True,
109
+ )
110
+ print("Index ready and saved to disk.")
111
+
112
+ return _search_index
113
 
114
 
115
+ class SearchTool:
116
+ """Simple search tool that persists its query engine."""
117
+
118
+ def __init__(self, index, top_k: int = 10):
119
+ self.index = index
120
+ self.query_engine = None
121
+ self.top_k = top_k
122
+
123
+ def search(self, query: str) -> List[str]:
124
+ """Search notes and return relevant text chunks."""
125
+ if self.index is None:
126
+ return []
127
+
128
+ if self.query_engine is None:
129
+ self.query_engine = self.index.as_query_engine(similarity_top_k=self.top_k)
130
+
131
+ response = self.query_engine.query(query)
132
+
133
+ results = []
134
+ if hasattr(response, 'source_nodes'):
135
+ for node in response.source_nodes:
136
+ results.append(node.node.text)
137
+
138
+ return results
139
+
140
+
141
+ # === MEMORY ===
142
 
143
  def load_memory():
144
+ """Load persistent memory from disk with caching."""
145
+ global _memory_cache
146
+
147
+ if _memory_cache is not None:
148
+ return _memory_cache
149
+
150
  if MEMORY_FILE.exists():
151
  try:
152
+ _memory_cache = json.loads(MEMORY_FILE.read_text(encoding="utf-8"))
153
+ return _memory_cache
154
  except Exception:
155
  pass
156
+
157
+ _memory_cache = {"sessions": []}
158
+ return _memory_cache
159
 
160
 
161
  def save_memory(memory):
 
167
  print(f"Warning: Could not save memory: {e}")
168
 
169
 
170
+ def record_exchange(memory, user_msg: str, agent_response: str, searches: List[str]):
171
  """Record a conversation exchange to persistent memory."""
172
  exchange = {
173
  "timestamp": datetime.now().isoformat(),
 
175
  "agent": agent_response,
176
  "searches": searches,
177
  }
178
+
179
+ if "sessions" not in memory:
180
  memory["sessions"] = []
181
+
182
  memory["sessions"].append(exchange)
183
+
184
+ # Keep last 100 sessions
185
+ if len(memory["sessions"]) > 100:
186
+ memory["sessions"] = memory["sessions"][-100:]
187
+
188
  save_memory(memory)
189
 
190
 
191
+ def get_recent_history(memory, n: int = 3) -> List[Dict]:
192
+ """Get recent conversation history as structured list."""
193
  if not memory.get("sessions"):
194
+ return []
195
+ return memory["sessions"][-n:]
 
 
 
 
 
 
 
196
 
197
 
198
+ # === DEEPSEEK API ===
199
 
200
+ async def call_deepseek_chat(
201
+ messages: List[Dict[str, str]],
202
+ api_key: str,
203
+ temperature: float = 0.7,
204
+ max_tokens: int = 2048,
205
+ ) -> str:
206
+ """Call DeepSeek API directly."""
207
 
208
+ url = "https://api.deepseek.com/v1/chat/completions"
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
+ headers = {
211
+ "Authorization": f"Bearer {api_key}",
212
+ "Content-Type": "application/json",
213
+ }
214
+
215
+ payload = {
216
+ "model": "deepseek-chat",
217
+ "messages": messages,
218
+ "temperature": temperature,
219
+ "max_tokens": max_tokens,
220
+ }
221
+
222
+ async with httpx.AsyncClient() as client:
223
+ response = await client.post(url, headers=headers, json=payload, timeout=60.0)
224
+ response.raise_for_status()
225
+ data = response.json()
226
+ return data["choices"][0]["message"]["content"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
 
 
 
 
228
 
229
+ # === AGENT LOGIC ===
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
 
231
+ SYSTEM_PROMPT = """You are a direct, analytical conversational partner. You have access to a search tool for querying your knowledge base of notes, transcripts, and conversations.
232
 
233
+ To search, output exactly: SEARCH: <query>
234
+ Example: SEARCH: CIA occult programs
235
 
236
+ Guidelines:
237
+ - Search when you need specific facts from your notes
238
+ - Don't mirror the user's language patterns
239
+ - Have your own ideas and disagree when appropriate
240
+ - Be concise and direct"""
 
241
 
 
242
 
243
+ async def run_agent(
244
+ search_tool: SearchTool,
245
+ message: str,
246
+ memory: Dict,
247
+ api_key: str,
248
+ ) -> Tuple[str, List[str]]:
249
+ """Run the agent with search capability."""
250
+
251
+ # Build message history (last 3 exchanges for context)
252
+ history = get_recent_history(memory, n=3)
253
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
254
+
255
+ # Add history as separate messages
256
+ for ex in history:
257
+ messages.append({"role": "user", "content": ex["user"]})
258
+ messages.append({"role": "assistant", "content": ex["agent"]})
259
+
260
+ # Add current message
261
+ messages.append({"role": "user", "content": message})
262
+
263
+ # First call - see if agent wants to search
264
  searches = []
265
+ response_text = await call_deepseek_chat(messages, api_key)
266
+
267
+ # Check if search is needed
268
+ if response_text.strip().upper().startswith("SEARCH:"):
269
+ # Extract search query
270
+ search_query = response_text.strip()[7:].strip()
271
+ searches.append(search_query)
272
+
273
+ # Perform search
274
+ search_results = search_tool.search(search_query)
275
+
276
+ # Build search context
277
+ if search_results:
278
+ search_context = "=== SEARCH RESULTS ===\n\n"
279
+ for i, result in enumerate(search_results[:5], 1): # Top 5 results
280
+ search_context += f"[{i}] {result[:800]}...\n\n"
281
+ else:
282
+ search_context = "=== SEARCH RESULTS ===\nNo relevant results found.\n"
283
+
284
+ # Add the search flow to conversation
285
+ messages.append({"role": "assistant", "content": response_text})
286
+ messages.append({"role": "user", "content": search_context})
287
+
288
+ # Second call with search results
289
+ response_text = await call_deepseek_chat(messages, api_key)
290
+
291
+ # Record to memory
292
+ record_exchange(memory, message, response_text, searches)
293
+
294
+ return response_text, searches
295
 
 
296
 
297
+ # === LAZY INITIALIZATION ===
298
 
299
+ def init():
300
+ """Initialize everything. Call this explicitly instead of at import."""
301
+ global _search_index, _memory_cache
302
+
303
+ get_api_key()
304
+
305
+ if _memory_cache is None:
306
+ _memory_cache = load_memory()
307
+
308
+ if _search_index is None:
309
+ _search_index = get_or_build_index()
310
+
311
+ return _search_index, _memory_cache
312
 
 
 
 
313
 
314
+ # Don't auto-init on import - let app.py call init()
315
+ print("Brain module loaded. Call init() to start.")
 
 
 
 
 
requirements.txt CHANGED
@@ -1,7 +1,7 @@
1
  gradio==6.6.0
2
  huggingface_hub>=0.20.0
3
  llama-index-core>=0.11.0
4
- llama-index-llms-openai-like>=0.6.0
5
  llama-index-embeddings-huggingface>=0.3.0
6
  llama-index-vector-stores-chroma>=0.4.0
7
  chromadb>=0.4.0
 
 
1
  gradio==6.6.0
2
  huggingface_hub>=0.20.0
3
  llama-index-core>=0.11.0
 
4
  llama-index-embeddings-huggingface>=0.3.0
5
  llama-index-vector-stores-chroma>=0.4.0
6
  chromadb>=0.4.0
7
+ httpx>=0.25.0