Julian Vanecek commited on
Commit
9967c6c
·
1 Parent(s): a35d3a6

Add document search functionality

Browse files

- Create minimal vector search implementation
- Add embedding-based document retrieval
- Integrate search results into chat context
- Use simple Blocks interface instead of ChatInterface

Files changed (3) hide show
  1. app.py +61 -7
  2. requirements.txt +3 -1
  3. simple_vector_search.py +65 -0
app.py CHANGED
@@ -1,9 +1,38 @@
1
  import gradio as gr
2
  import os
3
  from openai import OpenAI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  def chat_function(message, history):
6
- """Chat function with OpenAI integration"""
7
  # Check if API key is set
8
  api_key = os.environ.get("OPENAI_API_KEY", "")
9
 
@@ -14,8 +43,16 @@ def chat_function(message, history):
14
  # Initialize OpenAI client
15
  client = OpenAI(api_key=api_key)
16
 
 
 
 
 
 
 
 
 
17
  # Convert history to OpenAI format
18
- messages = [{"role": "system", "content": "You are a helpful AI assistant."}]
19
 
20
  for user_msg, assistant_msg in history:
21
  messages.append({"role": "user", "content": user_msg})
@@ -40,10 +77,27 @@ def chat_function(message, history):
40
  return history
41
 
42
  # Create chat interface
43
- demo = gr.ChatInterface(
44
- fn=chat_function,
45
- title="AI Assistant Multi-Agent System",
46
- description="Chat with OpenAI-powered AI assistant"
47
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  demo.launch()
 
1
  import gradio as gr
2
  import os
3
  from openai import OpenAI
4
+ from simple_vector_search import SimpleVectorSearch
5
+
6
+ # Initialize vector search
7
+ vector_search = SimpleVectorSearch()
8
+
9
+ def get_relevant_context(query: str, client: OpenAI) -> str:
10
+ """Get relevant context from documents"""
11
+ try:
12
+ # Get query embedding
13
+ response = client.embeddings.create(
14
+ model="text-embedding-ada-002",
15
+ input=query
16
+ )
17
+ query_embedding = response.data[0].embedding
18
+
19
+ # Search for relevant documents
20
+ results = vector_search.search(query_embedding, k=3)
21
+
22
+ # Format context
23
+ if results:
24
+ context = "Relevant information from documentation:\n\n"
25
+ for i, result in enumerate(results, 1):
26
+ context += f"{i}. {result['content'][:500]}...\n\n"
27
+ return context
28
+ else:
29
+ return ""
30
+ except Exception as e:
31
+ print(f"Error in vector search: {e}")
32
+ return ""
33
 
34
  def chat_function(message, history):
35
+ """Chat function with OpenAI integration and document search"""
36
  # Check if API key is set
37
  api_key = os.environ.get("OPENAI_API_KEY", "")
38
 
 
43
  # Initialize OpenAI client
44
  client = OpenAI(api_key=api_key)
45
 
46
+ # Get relevant context
47
+ context = get_relevant_context(message, client)
48
+
49
+ # Build system message
50
+ system_content = "You are a helpful AI assistant with access to technical documentation."
51
+ if context:
52
+ system_content += f"\n\n{context}"
53
+
54
  # Convert history to OpenAI format
55
+ messages = [{"role": "system", "content": system_content}]
56
 
57
  for user_msg, assistant_msg in history:
58
  messages.append({"role": "user", "content": user_msg})
 
77
  return history
78
 
79
  # Create chat interface
80
+ with gr.Blocks(title="AI Assistant") as demo:
81
+ gr.Markdown("# 🤖 AI Assistant Multi-Agent System")
82
+ gr.Markdown("Chat with an AI assistant that has access to technical documentation.")
83
+
84
+ chatbot = gr.Chatbot()
85
+ msg = gr.Textbox(
86
+ label="Your Message",
87
+ placeholder="Ask about Harmony or Chorus documentation...",
88
+ lines=2
89
+ )
90
+
91
+ with gr.Row():
92
+ submit = gr.Button("Send", variant="primary")
93
+ clear = gr.Button("Clear")
94
+
95
+ # Event handlers
96
+ def respond(message, history):
97
+ return chat_function(message, history), ""
98
+
99
+ msg.submit(respond, [msg, chatbot], [chatbot, msg])
100
+ submit.click(respond, [msg, chatbot], [chatbot, msg])
101
+ clear.click(lambda: ([], ""), outputs=[chatbot, msg])
102
 
103
  demo.launch()
requirements.txt CHANGED
@@ -1,2 +1,4 @@
1
  gradio>=4.0.0
2
- openai>=1.0.0
 
 
 
1
  gradio>=4.0.0
2
+ openai>=1.0.0
3
+ numpy>=1.24.0
4
+ tiktoken>=0.5.0
simple_vector_search.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Minimal vector search implementation for HuggingFace deployment
3
+ """
4
+ import json
5
+ import numpy as np
6
+ from pathlib import Path
7
+ from typing import List, Dict, Tuple
8
+
9
+ class SimpleVectorSearch:
10
+ """Simple in-memory vector search"""
11
+
12
+ def __init__(self, data_dir: str = "py/backend/data/embeddings"):
13
+ self.data_dir = Path(data_dir)
14
+ self.documents = []
15
+ self.embeddings = None
16
+ self._load_embeddings()
17
+
18
+ def _load_embeddings(self):
19
+ """Load all embedding files"""
20
+ all_docs = []
21
+ all_embeddings = []
22
+
23
+ # Load all JSON files
24
+ for json_file in self.data_dir.glob("*.json"):
25
+ try:
26
+ with open(json_file, 'r') as f:
27
+ data = json.load(f)
28
+ for item in data:
29
+ all_docs.append({
30
+ 'content': item['content'],
31
+ 'metadata': item.get('metadata', {})
32
+ })
33
+ all_embeddings.append(item['embedding'])
34
+ except Exception as e:
35
+ print(f"Error loading {json_file}: {e}")
36
+
37
+ self.documents = all_docs
38
+ self.embeddings = np.array(all_embeddings) if all_embeddings else None
39
+
40
+ def search(self, query_embedding: List[float], k: int = 3) -> List[Dict]:
41
+ """Search for similar documents"""
42
+ if self.embeddings is None or len(self.embeddings) == 0:
43
+ return []
44
+
45
+ # Convert query to numpy array
46
+ query_vec = np.array(query_embedding)
47
+
48
+ # Compute cosine similarity
49
+ query_norm = query_vec / (np.linalg.norm(query_vec) + 1e-10)
50
+ embeddings_norm = self.embeddings / (np.linalg.norm(self.embeddings, axis=1, keepdims=True) + 1e-10)
51
+ similarities = np.dot(embeddings_norm, query_norm)
52
+
53
+ # Get top k indices
54
+ top_indices = np.argsort(similarities)[-k:][::-1]
55
+
56
+ # Return documents with scores
57
+ results = []
58
+ for idx in top_indices:
59
+ results.append({
60
+ 'content': self.documents[idx]['content'],
61
+ 'metadata': self.documents[idx]['metadata'],
62
+ 'score': float(similarities[idx])
63
+ })
64
+
65
+ return results