chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
MONGODB_URI="<mongodb-connection-string>"
|
||||
VOYAGE_API_KEY="<voyage-api-key>"
|
||||
OPENAI_API_KEY= "<openai-api-key>"
|
||||
@@ -0,0 +1,110 @@
|
||||
# Database Memory Agent
|
||||
|
||||
We're building a Database Memory Agent with RAG (Retrieval Augmented Generation) capabilities that integrates MongoDB Atlas Vector Search for semantic document retrieval, Voyage AI for embeddings, and OpenAI for intelligent responses. The agent uses tools (vector search and calculator) to answer questions from uploaded documents and perform calculations, with context-aware memory across conversations.
|
||||
|
||||
We use:
|
||||
|
||||
- [MongoDB Atlas Vector Search](https://www.mongodb.com/products/platform/atlas-vector-search) for semantic search and document storage
|
||||
- [Voyage AI](https://www.voyageai.com/) for generating embeddings (voyage-3-large model)
|
||||
- [OpenAI](https://openai.com/) for LLM responses (gpt-4o)
|
||||
- [Streamlit](https://streamlit.io/) to wrap the logic in an interactive UI
|
||||
|
||||
## Set Up
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You must have the following:
|
||||
|
||||
- One of the following MongoDB cluster types:
|
||||
|
||||
- An [Atlas cluster](https://www.mongodb.com/docs/atlas/tutorial/create-new-cluster/) running MongoDB version 6.0.11, 7.0.2, or later. Ensure that your IP address (Internet Protocol address) is included in your Atlas project's [access list](https://www.mongodb.com/docs/atlas/security/ip-access-list/).
|
||||
|
||||
- A local Atlas deployment created using the Atlas CLI. To learn more, see [Create a Local Atlas Deployment](https://www.mongodb.com/docs/atlas/cli/current/atlas-cli-deploy-local/).
|
||||
|
||||
- A MongoDB Community or Enterprise cluster with [Search and Vector Search](https://www.mongodb.com/docs/manual/administration/install-community/#std-label-community-search-deploy) installed.
|
||||
|
||||
- A Voyage AI API key.
|
||||
|
||||
- An OpenAI API key.
|
||||
|
||||
### Configure Environment Variables
|
||||
|
||||
Copy `.env.example` to `.env` and configure the following environment variables:
|
||||
|
||||
```env
|
||||
MONGODB_URI="<mongodb-connection-string>"
|
||||
VOYAGE_API_KEY="<your-voyage-api-key>"
|
||||
OPENAI_API_KEY="<your-openai-api-key>"
|
||||
```
|
||||
|
||||
Replace `<mongodb-connection-string>` with the connection string for your Atlas cluster or local Atlas deployment.
|
||||
|
||||
**Atlas Cluster:**
|
||||
|
||||
Your connection string should use the following format:
|
||||
|
||||
```
|
||||
mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
|
||||
```
|
||||
|
||||
To learn more, see [Connect to a Cluster via Drivers](https://www.mongodb.com/docs/atlas/driver-connection/).
|
||||
|
||||
**Local or Self-Managed:**
|
||||
|
||||
Your connection string should use the following format:
|
||||
|
||||
```
|
||||
mongodb://localhost:<port-number>/?directConnection=true
|
||||
```
|
||||
|
||||
To learn more, see [Connection Strings](https://www.mongodb.com/docs/manual/reference/connection-string/).
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
### Run the Application
|
||||
|
||||
Run the application with:
|
||||
|
||||
```bash
|
||||
streamlit run app.py
|
||||
```
|
||||
|
||||
Or use the CLI version:
|
||||
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
[Get your Voyage AI API keys here](https://dashboard.voyageai.com/)
|
||||
|
||||
[Get your OpenAI API keys here](https://platform.openai.com/api-keys)
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
database-memory-agent/
|
||||
├── .env # Environment variables (create from .env.example)
|
||||
├── config.py # MongoDB and API configuration
|
||||
├── ingest_data.py # PDF ingestion and vector index creation
|
||||
├── tools.py # Agent tools (vector search, calculator)
|
||||
├── memory.py # Chat history storage
|
||||
├── planning.py # Agent planning and response generation
|
||||
├── app.py # Streamlit web application
|
||||
├── main.py # CLI application
|
||||
├── pyproject.toml # Project dependencies
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## 📬 Stay Updated with Our Newsletter!
|
||||
|
||||
**Get a FREE Data Science eBook** 📖 with 150+ essential lessons in Data Science when you subscribe to our newsletter! Stay in the loop with the latest tutorials, insights, and exclusive resources. [Subscribe now!](https://join.dailydoseofds.com)
|
||||
|
||||
[](https://join.dailydoseofds.com)
|
||||
|
||||
## Contribution
|
||||
|
||||
Contributions are welcome! Feel free to fork this repository and submit pull requests with your improvements.
|
||||
@@ -0,0 +1,260 @@
|
||||
import streamlit as st
|
||||
import base64
|
||||
import uuid
|
||||
import sys
|
||||
from io import StringIO
|
||||
|
||||
from config import vector_collection
|
||||
from ingest_data import ingest_data
|
||||
from planning import generate_response, tool_selector
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Set up page configuration
|
||||
st.set_page_config(page_title="Database Memory Agent", layout="wide")
|
||||
|
||||
# Initialize session state variables
|
||||
if "session_id" not in st.session_state:
|
||||
st.session_state.session_id = str(uuid.uuid4())[:8]
|
||||
|
||||
if "messages" not in st.session_state:
|
||||
st.session_state.messages = []
|
||||
|
||||
if "vector_index_ready" not in st.session_state:
|
||||
st.session_state.vector_index_ready = False
|
||||
|
||||
if "data_ingested" not in st.session_state:
|
||||
st.session_state.data_ingested = False
|
||||
|
||||
session_id = st.session_state.session_id
|
||||
|
||||
def reset_chat():
|
||||
"""Reset chat history."""
|
||||
st.session_state.messages = []
|
||||
st.session_state.session_id = str(uuid.uuid4())[:8]
|
||||
|
||||
def display_pdf(file):
|
||||
"""Display PDF preview in sidebar."""
|
||||
st.markdown("### PDF Preview")
|
||||
base64_pdf = base64.b64encode(file.read()).decode("utf-8")
|
||||
|
||||
pdf_display = f"""<iframe src="data:application/pdf;base64,{base64_pdf}" width="400" height="100%" type="application/pdf"
|
||||
style="height:100vh; width:100%"
|
||||
>
|
||||
</iframe>"""
|
||||
|
||||
st.markdown(pdf_display, unsafe_allow_html=True)
|
||||
|
||||
def check_vector_index():
|
||||
"""Check if vector index exists and is ready."""
|
||||
if st.session_state.vector_index_ready:
|
||||
return True
|
||||
|
||||
try:
|
||||
existing_indexes = list(vector_collection.list_search_indexes("vector_index"))
|
||||
if existing_indexes and existing_indexes[0].get("queryable"):
|
||||
st.session_state.vector_index_ready = True
|
||||
return True
|
||||
except Exception as e:
|
||||
st.error(f"Error checking vector index: {e}")
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def process_pdf_upload(uploaded_file):
|
||||
"""Process uploaded PDF and ingest into MongoDB."""
|
||||
with st.spinner("🔄 Processing..."):
|
||||
try:
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
|
||||
try:
|
||||
ingest_data()
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
|
||||
if check_vector_index():
|
||||
st.session_state.data_ingested = True
|
||||
st.success("✅ Document processed and ready for queries!")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
st.error(f"Error processing PDF: {str(e)}")
|
||||
return False
|
||||
|
||||
def ingest_sample_data():
|
||||
"""Ingest sample MongoDB earnings report."""
|
||||
try:
|
||||
with st.spinner("🔄 Processing..."):
|
||||
# Suppress print output from ingest_data()
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
|
||||
try:
|
||||
ingest_data()
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
|
||||
# ingest_data() already creates the index, just check if it's ready
|
||||
if check_vector_index():
|
||||
st.session_state.data_ingested = True
|
||||
st.success("✅ Sample data ingested and ready for queries!")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
st.error(f"Error ingesting sample data: {str(e)}")
|
||||
return False
|
||||
|
||||
# Sidebar for configuration and document upload
|
||||
with st.sidebar:
|
||||
st.header("🔧 Configuration")
|
||||
|
||||
st.markdown("**Session ID:**")
|
||||
st.code(session_id)
|
||||
|
||||
if st.button("🔄 New Session"):
|
||||
reset_chat()
|
||||
st.rerun()
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# Document upload section
|
||||
st.header("📄 Upload Document")
|
||||
st.markdown("Upload a PDF document or use sample data")
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
if st.button("📊 Use Sample Data", use_container_width=True):
|
||||
ingest_sample_data()
|
||||
|
||||
with col2:
|
||||
if st.button("🗑️ Clear Data", use_container_width=True):
|
||||
st.session_state.data_ingested = False
|
||||
st.session_state.vector_index_ready = False
|
||||
st.info("Data cleared. Upload a new document to continue.")
|
||||
|
||||
uploaded_file = st.file_uploader("Or upload your PDF file", type="pdf")
|
||||
|
||||
if uploaded_file:
|
||||
if process_pdf_upload(uploaded_file):
|
||||
display_pdf(uploaded_file)
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# System status
|
||||
st.header("📊 System Status")
|
||||
if st.session_state.data_ingested:
|
||||
st.success("🟢 Data Ready")
|
||||
else:
|
||||
st.info("🔵 No Data Loaded")
|
||||
|
||||
if st.session_state.vector_index_ready:
|
||||
st.success("🟢 Vector Index Ready")
|
||||
else:
|
||||
st.warning("🟡 Vector Index Not Ready")
|
||||
|
||||
# Main chat interface
|
||||
col1, col2 = st.columns([6, 1])
|
||||
|
||||
with col1:
|
||||
st.markdown('''
|
||||
<h1 style='color: #2E86AB; margin-bottom: 10px; font-size: 2.5em;'>
|
||||
Database Memory Agent
|
||||
</h1>
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 20px;">
|
||||
<span style='color: #A23B72; font-size: 16px;'>Powered by</span>
|
||||
<div style="display: flex; align-items: center; gap: 20px;">
|
||||
<a href="https://www.mongodb.com/" style="display: inline-block; vertical-align: middle;">
|
||||
<img src="https://webimages.mongodb.com/_com_assets/cms/mongodb_logo1-76twgcu2dm.png"
|
||||
alt="MongoDB" style="height: 40px;">
|
||||
</a>
|
||||
<a href="https://www.voyageai.com/" style="display: inline-block; vertical-align: middle;">
|
||||
<img src="https://www.voyageai.com/favicon.ico"
|
||||
alt="Voyage AI" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
''', unsafe_allow_html=True)
|
||||
|
||||
with col2:
|
||||
if st.button("Clear Chat ↺", on_click=reset_chat):
|
||||
st.rerun()
|
||||
|
||||
# System info
|
||||
if st.session_state.data_ingested and st.session_state.vector_index_ready:
|
||||
st.success("🟢 System Ready - You can ask questions about your document!")
|
||||
elif st.session_state.data_ingested:
|
||||
st.warning("🟡 Data loaded but vector index is not ready. Please wait...")
|
||||
else:
|
||||
st.info("🔵 Upload a PDF document or use sample data to get started")
|
||||
|
||||
# Display chat messages from history
|
||||
for message in st.session_state.messages:
|
||||
with st.chat_message(message["role"]):
|
||||
st.markdown(message["content"])
|
||||
|
||||
|
||||
# Accept user input
|
||||
if prompt := st.chat_input("Ask a question about your document..."):
|
||||
if not st.session_state.data_ingested or not st.session_state.vector_index_ready:
|
||||
st.error("⚠️ Please upload a document or use sample data first.")
|
||||
st.stop()
|
||||
|
||||
# Add user message to chat history
|
||||
st.session_state.messages.append({
|
||||
"role": "user",
|
||||
"content": prompt
|
||||
})
|
||||
|
||||
# Display user message
|
||||
with st.chat_message("user"):
|
||||
st.markdown(prompt)
|
||||
|
||||
# Generate response
|
||||
with st.chat_message("assistant"):
|
||||
message_placeholder = st.empty()
|
||||
|
||||
try:
|
||||
with st.spinner("🔄 Processing..."):
|
||||
# Get tool info for display (simple check)
|
||||
session_history = [{"role": msg["role"], "content": msg["content"]} for msg in st.session_state.messages[-5:]]
|
||||
tool, _ = tool_selector(prompt, session_history if session_history else None)
|
||||
|
||||
# Generate response
|
||||
response = generate_response(session_id, prompt)
|
||||
|
||||
message_placeholder.markdown(response)
|
||||
|
||||
# Show simple tool indicator
|
||||
if tool == "vector_search_tool":
|
||||
st.info("📚 Using document search")
|
||||
elif tool == "calculator_tool":
|
||||
st.info("🔢 Using calculator")
|
||||
|
||||
metadata = {"tool": tool}
|
||||
|
||||
except Exception as e:
|
||||
st.error(f"❌ Error processing your question: {str(e)}")
|
||||
response = "I apologize, but I encountered an error while processing your question. Please try again."
|
||||
message_placeholder.markdown(response)
|
||||
metadata = {}
|
||||
|
||||
# Add assistant response to chat history
|
||||
st.session_state.messages.append({
|
||||
"role": "assistant",
|
||||
"content": response,
|
||||
"metadata": metadata
|
||||
})
|
||||
|
||||
# Footer
|
||||
st.markdown("---")
|
||||
st.markdown(
|
||||
"<p style='text-align: center; color: #666; font-size: 12px;'>"
|
||||
"Database Memory Agent • Built with Streamlit, MongoDB Atlas Vector Search, and Voyage AI"
|
||||
"</p>",
|
||||
unsafe_allow_html=True
|
||||
)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
from pymongo import MongoClient
|
||||
from openai import OpenAI
|
||||
import voyageai
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
# Environment variables (private)
|
||||
MONGODB_URI = os.getenv("MONGODB_URI")
|
||||
VOYAGE_API_KEY = os.getenv("VOYAGE_API_KEY")
|
||||
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
||||
|
||||
# MongoDB cluster configuration
|
||||
mongo_client = MongoClient(MONGODB_URI)
|
||||
agent_db = mongo_client["ai_agent_db"]
|
||||
vector_collection = agent_db["embeddings"]
|
||||
memory_collection = agent_db["chat_history"]
|
||||
|
||||
# Model configuration
|
||||
voyage_client = voyageai.Client(api_key=VOYAGE_API_KEY)
|
||||
openai_client = OpenAI(api_key=OPENAI_API_KEY)
|
||||
VOYAGE_MODEL = "voyage-3-large"
|
||||
OPENAI_MODEL = "gpt-4o"
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
from config import vector_collection, voyage_client, VOYAGE_MODEL
|
||||
from pymongo.operations import SearchIndexModel
|
||||
from langchain_community.document_loaders import PyPDFLoader
|
||||
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
||||
import time
|
||||
|
||||
def get_embedding(data, input_type = "document"):
|
||||
embeddings = voyage_client.embed(
|
||||
data, model = VOYAGE_MODEL, input_type = input_type
|
||||
).embeddings
|
||||
return embeddings[0]
|
||||
|
||||
def ingest_data():
|
||||
loader = PyPDFLoader("https://investors.mongodb.com/node/13176/pdf")
|
||||
data = loader.load()
|
||||
text_splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=20)
|
||||
documents = text_splitter.split_documents(data)
|
||||
print(f"Successfully split PDF into {len(documents)} chunks.")
|
||||
|
||||
print("Generating embeddings and ingesting documents...")
|
||||
docs_to_insert = []
|
||||
for i, doc in enumerate(documents):
|
||||
embedding = get_embedding(doc.page_content)
|
||||
if embedding:
|
||||
docs_to_insert.append({
|
||||
"text": doc.page_content,
|
||||
"embedding": embedding
|
||||
})
|
||||
|
||||
if docs_to_insert:
|
||||
result = vector_collection.insert_many(docs_to_insert)
|
||||
print(f"Inserted {len(result.inserted_ids)} documents into the collection.")
|
||||
else:
|
||||
print("No documents were inserted. Check embedding generation process.")
|
||||
|
||||
index_name = "vector_index"
|
||||
|
||||
search_index_model = SearchIndexModel(
|
||||
definition = {
|
||||
"fields": [
|
||||
{
|
||||
"type": "vector",
|
||||
"numDimensions": 1024,
|
||||
"path": "embedding",
|
||||
"similarity": "cosine"
|
||||
}
|
||||
]
|
||||
},
|
||||
name=index_name,
|
||||
type="vectorSearch"
|
||||
)
|
||||
try:
|
||||
vector_collection.create_search_index(model=search_index_model)
|
||||
print(f"Search index '{index_name}' creation initiated.")
|
||||
except Exception as e:
|
||||
print(f"Error creating search index: {e}")
|
||||
return
|
||||
|
||||
print("Polling to check if the index is ready. This may take up to a minute.")
|
||||
predicate=None
|
||||
if predicate is None:
|
||||
predicate = lambda index: index.get("queryable") is True
|
||||
|
||||
while True:
|
||||
indices = list(vector_collection.list_search_indexes(index_name))
|
||||
if len(indices) and predicate(indices[0]):
|
||||
break
|
||||
time.sleep(5)
|
||||
print(index_name + " is ready for querying.")
|
||||
@@ -0,0 +1,28 @@
|
||||
from config import mongo_client
|
||||
from ingest_data import ingest_data
|
||||
from planning import generate_response
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
run_ingest = input("Ingest sample data? (y/n): ")
|
||||
if run_ingest.lower() == 'y':
|
||||
ingest_data()
|
||||
|
||||
session_id = input("Enter a session ID: ")
|
||||
|
||||
while True:
|
||||
user_query = input("\nEnter your query (or type 'quit' to exit): ")
|
||||
if user_query.lower() == 'quit':
|
||||
break
|
||||
|
||||
if not user_query.strip():
|
||||
print("Query cannot be empty. Please try again.")
|
||||
continue
|
||||
|
||||
answer = generate_response(session_id, user_query)
|
||||
print("\nAnswer:")
|
||||
print(answer)
|
||||
finally:
|
||||
mongo_client.close()
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from config import memory_collection
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
def store_chat_message(session_id: str, role: str, content: str) -> None:
|
||||
message = {
|
||||
"session_id": session_id, # Unique identifier for the chat session
|
||||
"role": role, # Role of the sender (user or system)
|
||||
"content": content, # Content of the message
|
||||
"timestamp": datetime.now(), # Timestamp of when the message was sent
|
||||
}
|
||||
memory_collection.insert_one(message)
|
||||
|
||||
def retrieve_session_history(session_id: str) -> List:
|
||||
# Query the collection for messages with a specific "session_id" in ascending order
|
||||
cursor = memory_collection.find({"session_id": session_id}).sort("timestamp", 1)
|
||||
|
||||
# Iterate through the cursor and return a JSON object with the message role and content
|
||||
if cursor:
|
||||
messages = [{"role": msg["role"], "content": msg["content"]} for msg in cursor]
|
||||
else:
|
||||
messages = []
|
||||
return messages
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
from config import openai_client, OPENAI_MODEL
|
||||
from tools import vector_search_tool, calculator_tool
|
||||
from memory import store_chat_message, retrieve_session_history
|
||||
|
||||
def tool_selector(user_input, session_history=None):
|
||||
messages = [
|
||||
{
|
||||
"role": "system",
|
||||
"content": (
|
||||
"Select the appropriate tool from the options below. Consider the full context of the conversation before deciding.\n\n"
|
||||
"Tools available:\n"
|
||||
"- vector_search_tool: Retrieve specific context from the MongoDB earnings report document. Use this for questions about MongoDB, its products, programs, acquisitions, financials, or any topics that might be covered in the document\n"
|
||||
"- calculator_tool: For mathematical operations\n"
|
||||
"- none: Only for general questions that are clearly unrelated to the document content\n\n"
|
||||
|
||||
"Process for making your decision:\n"
|
||||
"1. When in doubt, prefer vector_search_tool - it can answer questions about MongoDB, its programs (like MAAP), products, acquisitions, financials, and announcements\n"
|
||||
"2. Analyze if the current question relates to or follows up on a previous vector search query\n"
|
||||
"3. For follow-up questions, incorporate context from previous exchanges to create a comprehensive search query\n"
|
||||
"4. Only use calculator_tool for explicit mathematical operations\n"
|
||||
"5. Default to none only when certain the question is completely unrelated to the document\n\n"
|
||||
|
||||
"When continuing a conversation:\n"
|
||||
"- Identify the specific topic being discussed\n"
|
||||
"- Include relevant details from previous exchanges\n"
|
||||
"- Formulate a query that stands alone but preserves conversation context\n\n"
|
||||
|
||||
"Return a JSON object only: {\"tool\": \"selected_tool\", \"input\": \"your_query\"}"
|
||||
)
|
||||
}
|
||||
]
|
||||
if session_history:
|
||||
messages.extend(session_history)
|
||||
messages.append({"role": "user", "content": user_input})
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model=OPENAI_MODEL,
|
||||
messages=messages
|
||||
).choices[0].message.content
|
||||
try:
|
||||
tool_call = eval(response)
|
||||
return tool_call.get("tool"), tool_call.get("input")
|
||||
except:
|
||||
return "none", user_input
|
||||
|
||||
def generate_response(session_id: str, user_input: str) -> str:
|
||||
|
||||
store_chat_message(session_id, "user", user_input)
|
||||
|
||||
llm_input = []
|
||||
|
||||
session_history = retrieve_session_history(session_id)
|
||||
llm_input.extend(session_history)
|
||||
|
||||
user_message = {
|
||||
"role": "user",
|
||||
"content": user_input
|
||||
}
|
||||
llm_input.append(user_message)
|
||||
|
||||
tool, tool_input = tool_selector(user_input, session_history)
|
||||
print("Tool selected: ", tool)
|
||||
|
||||
if tool == "vector_search_tool":
|
||||
context = vector_search_tool(tool_input)
|
||||
system_message_content = (
|
||||
f"Answer the user's question based on the retrieved context and conversation history.\n"
|
||||
f"1. First, understand what specific information the user is requesting\n"
|
||||
f"2. Then, locate the most relevant details in the context provided\n"
|
||||
f"3. Finally, provide a clear, accurate response that directly addresses the question\n\n"
|
||||
f"If the current question builds on previous exchanges, maintain continuity in your answer.\n"
|
||||
f"Only state facts clearly supported by the provided context. If information is not available, say 'I DON'T KNOW'.\n\n"
|
||||
f"Context:\n{context}"
|
||||
)
|
||||
response = get_llm_response(llm_input, system_message_content)
|
||||
elif tool == "calculator_tool":
|
||||
response = calculator_tool(tool_input)
|
||||
else:
|
||||
system_message_content = "You are a helpful assistant. Respond to the user's prompt as best as you can based on the conversation history."
|
||||
response = get_llm_response(llm_input, system_message_content)
|
||||
|
||||
store_chat_message(session_id, "system", response)
|
||||
return response
|
||||
|
||||
def get_llm_response(messages, system_message_content):
|
||||
system_message = {
|
||||
"role": "system",
|
||||
"content": system_message_content,
|
||||
}
|
||||
|
||||
if any(msg.get("role") == "system" for msg in messages):
|
||||
messages.append(system_message)
|
||||
else:
|
||||
messages = [system_message] + messages
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model=OPENAI_MODEL,
|
||||
messages=messages
|
||||
).choices[0].message.content
|
||||
|
||||
return response
|
||||
@@ -0,0 +1,17 @@
|
||||
[project]
|
||||
name = "database-memory-agent"
|
||||
version = "0.1.0"
|
||||
description = "Database Memory Agent with RAG capabilities using MongoDB Atlas Vector Search, Voyage AI, and OpenAI"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"pymongo",
|
||||
"voyageai",
|
||||
"openai",
|
||||
"langchain",
|
||||
"langchain-mongodb",
|
||||
"langchain-community",
|
||||
"python-dotenv",
|
||||
"streamlit",
|
||||
"pypdf",
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
from config import vector_collection
|
||||
from ingest_data import get_embedding
|
||||
|
||||
def vector_search_tool(user_input: str) -> str:
|
||||
query_embedding = get_embedding(user_input)
|
||||
|
||||
pipeline = [
|
||||
{
|
||||
"$vectorSearch": {
|
||||
"index": "vector_index",
|
||||
"queryVector": query_embedding,
|
||||
"path": "embedding",
|
||||
"exact": True,
|
||||
"limit": 5,
|
||||
}
|
||||
},
|
||||
{
|
||||
"$project": {
|
||||
"_id": 0,
|
||||
"text": 1,
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
results = vector_collection.aggregate(pipeline)
|
||||
array_of_results = []
|
||||
for doc in results:
|
||||
array_of_results.append(doc)
|
||||
return array_of_results
|
||||
|
||||
|
||||
def calculator_tool(user_input: str) -> str:
|
||||
try:
|
||||
result = eval(user_input)
|
||||
return str(result)
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
|
||||
Generated
+3203
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user