-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9ee8fbd
commit 1bb31e9
Showing
9 changed files
with
569 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Autonomous RAG with SingleStore and Phidata | ||
|
||
This cookbook shows how to build an Autonomous RAG application with SingleStore and Phidata. | ||
|
||
We'll use the following LLMs: | ||
- GPT-4 by OpenAI (needs an API key) | ||
- Hermes2 running locally using Ollama (no API key needed) | ||
- Claude3 by Anthropic (needs an API key) | ||
|
||
> Note: Fork and clone this repository if needed | ||
### 1. Create a virtual environment | ||
|
||
```shell | ||
python3 -m venv ~/.venvs/aienv | ||
source ~/.venvs/aienv/bin/activate | ||
``` | ||
|
||
### 2. Install libraries | ||
|
||
```shell | ||
pip install -r cookbook/integrations/singlestore/auto_rag/requirements.txt | ||
``` | ||
|
||
### 3. To use a local model, install and run Ollama | ||
|
||
- [Install](https://github.com/ollama/ollama?tab=readme-ov-file#macos) ollama | ||
|
||
- Run you embedding model | ||
|
||
```shell | ||
ollama run nomic-embed-text | ||
``` | ||
|
||
- Run your chat model | ||
|
||
```shell | ||
ollama run adrienbrault/nous-hermes2pro:Q8_0 'Hey!' | ||
``` | ||
|
||
### 4. Provide credentials | ||
|
||
- For SingleStore | ||
|
||
> Note: Please make sure you provide a certificate file for SSL connection [Read more](https://docs.singlestore.com/cloud/connect-to-your-workspace/connect-with-mysql/connect-with-mysql-client/connect-to-singlestore-helios-using-tls-ssl/) | ||
```shell | ||
export SINGLESTORE_HOST="host" | ||
export SINGLESTORE_PORT="3333" | ||
export SINGLESTORE_USERNAME="user" | ||
export SINGLESTORE_PASSWORD="password" | ||
export SINGLESTORE_DATABASE="db" | ||
export SINGLESTORE_SSL_CA=".certs/singlestore_bundle.pem" | ||
``` | ||
|
||
- To use OpenAI GPT-4, provide your OPENAI_API_KEY | ||
|
||
```shell | ||
export OPENAI_API_KEY="sk-..." | ||
``` | ||
|
||
- To use Anthropic Claude3, provide your ANTHROPIC_API_KEY | ||
|
||
```shell | ||
export ANTHROPIC_API_KEY="sk-..." | ||
``` | ||
|
||
### 6. Run Streamlit application | ||
|
||
```shell | ||
streamlit run cookbook/integrations/singlestore/auto_rag/app.py | ||
``` | ||
|
||
### 7. Message us on [discord](https://discord.gg/4MtYHHrgA8) if you have any questions | ||
|
||
### 8. Star ⭐️ the project if you like it. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
from typing import List | ||
|
||
import streamlit as st | ||
from phi.assistant import Assistant | ||
from phi.document import Document | ||
from phi.document.reader.website import WebsiteReader | ||
from phi.tools.streamlit.components import ( | ||
check_password, | ||
reload_button_sidebar, | ||
get_username_sidebar, | ||
) | ||
|
||
from assistant import get_local_rag_assistant # Adjust the import statement as needed | ||
from logging import getLogger | ||
|
||
logger = getLogger(__name__) | ||
|
||
st.set_page_config( | ||
page_title="Local RAG with Web Scraping \n Using SingleStore as a backend databse", | ||
page_icon=":orange_heart:", | ||
) | ||
st.title("Local RAG with Web Scraping") | ||
st.markdown("##### :orange_heart: Built using [phidata](https://github.com/phidatahq/phidata)") | ||
def restart_assistant(): | ||
st.session_state["web_assistant"] = None | ||
st.session_state["web_assistant_run_id"] = None | ||
st.session_state["url_scrape_key"] += 1 | ||
st.rerun() | ||
|
||
|
||
def main() -> None: | ||
# Get username | ||
username = get_username_sidebar() | ||
if username: | ||
st.sidebar.info(f":technologist: User: {username}") | ||
else: | ||
st.write(":technologist: Please enter a username") | ||
return | ||
|
||
# Get model | ||
local_rag_model = st.sidebar.selectbox("Select Model", options=["openhermes", "codellama"]) | ||
# Set assistant_type in session state | ||
if "local_rag_model" not in st.session_state: | ||
st.session_state["local_rag_model"] = local_rag_model | ||
# Restart the assistant if assistant_type has changed | ||
elif st.session_state["local_rag_model"] != local_rag_model: | ||
st.session_state["local_rag_model"] = local_rag_model | ||
restart_assistant() | ||
|
||
# Get the assistant | ||
web_assistant: Assistant | ||
if "web_assistant" not in st.session_state or st.session_state["web_assistant"] is None: | ||
logger.info("---*--- Creating Web Assistant ---*---") | ||
web_assistant = get_local_rag_assistant( | ||
model=local_rag_model, | ||
user_id=username, | ||
debug_mode=True, | ||
) | ||
st.session_state["web_assistant"] = web_assistant | ||
else: | ||
web_assistant = st.session_state["web_assistant"] | ||
|
||
# Create assistant run (i.e. log to database) and save run_id in session state | ||
try: | ||
st.session_state["web_assistant_run_id"] = web_assistant.create_run() | ||
except Exception: | ||
st.warning("Could not create assistant, is the database running?") | ||
return | ||
|
||
# Load existing messages | ||
assistant_chat_history = web_assistant.memory.get_chat_history() | ||
if len(assistant_chat_history) > 0: | ||
logger.debug("Loading chat history") | ||
st.session_state["messages"] = assistant_chat_history | ||
else: | ||
logger.debug("No chat history found") | ||
st.session_state["messages"] = [{"role": "web_assistant", "content": "Ask me anything..."}] | ||
|
||
# Prompt for user input | ||
if prompt := st.chat_input(): | ||
st.session_state["messages"].append({"role": "user", "content": prompt}) | ||
|
||
# Display existing chat messages | ||
for message in st.session_state["messages"]: | ||
if message["role"] == "system": | ||
continue | ||
with st.chat_message(message["role"]): | ||
st.write(message["content"]) | ||
|
||
# If last message is from a user, generate a new response | ||
last_message = st.session_state["messages"][-1] | ||
if last_message.get("role") == "user": | ||
question = last_message["content"] | ||
with st.chat_message("web_assistant"): | ||
response = "" | ||
resp_container = st.empty() | ||
for delta in web_assistant.run(question): | ||
response += delta # type: ignore | ||
resp_container.markdown(response) | ||
|
||
st.session_state["messages"].append({"role": "web_assistant", "content": response}) | ||
|
||
if st.sidebar.button("New Run"): | ||
restart_assistant() | ||
|
||
if st.sidebar.button("Auto Rename"): | ||
web_assistant.auto_rename_run() | ||
|
||
# Upload Web Content | ||
if web_assistant.knowledge_base: | ||
if "url_scrape_key" not in st.session_state: | ||
st.session_state["url_scrape_key"] = 0 | ||
|
||
scraped_url = st.sidebar.text_input( | ||
"Input URL", | ||
type="default", | ||
key=st.session_state["url_scrape_key"] | ||
) | ||
append_button = st.sidebar.button("Search URL") | ||
if append_button: | ||
if scraped_url is not None: | ||
alert = st.sidebar.info("Processing URLs...", icon="🧠") | ||
if f"{scraped_url}_scraped" not in st.session_state: | ||
scraper = WebsiteReader() | ||
web_documents: List[Document] = scraper.read(scraped_url) | ||
if web_documents: | ||
web_assistant.knowledge_base.load_documents(web_documents, upsert=True, skip_existing=True) | ||
else: | ||
st.sidebar.error("Could not read Website") | ||
st.session_state[f"{scraped_url}_uploaded"] = True | ||
alert.empty() | ||
|
||
if web_assistant.storage: | ||
web_assistant_run_ids: List[str] = web_assistant.storage.get_all_run_ids(user_id=username) | ||
new_web_assistant_run_id = st.sidebar.selectbox("Run ID", options=web_assistant_run_ids) | ||
if st.session_state["web_assistant_run_id"] != new_web_assistant_run_id: | ||
logger.info(f"---*--- Loading run: {new_web_assistant_run_id} ---*---") | ||
st.session_state["web_assistant"] = get_local_rag_assistant( | ||
model=local_rag_model, | ||
user_id=username, | ||
run_id=new_web_assistant_run_id, | ||
debug_mode=True, | ||
) | ||
st.rerun() | ||
|
||
web_assistant_run_name = web_assistant.run_name | ||
if web_assistant_run_name: | ||
st.sidebar.write(f":thread: {web_assistant_run_name}") | ||
|
||
# Show reload button | ||
reload_button_sidebar() | ||
|
||
|
||
if check_password(): | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
from typing import Optional | ||
|
||
from phi.assistant import Assistant | ||
from phi.knowledge import AssistantKnowledge | ||
from phi.llm.ollama import Ollama | ||
from phi.embedder.ollama import OllamaEmbedder | ||
from phi.storage.assistant.singlestore import S2AssistantStorage | ||
from phi.vectordb.singlestore import S2VectorDb | ||
|
||
from resources import config # type: ignore | ||
|
||
# Setup SingleStore connection | ||
db_url = ( | ||
f"mysql+pymysql://{config['username']}:{config['password']}@{config['host']}:{config['port']}/{config['database']}?charset=utf8mb4" | ||
) | ||
|
||
local_assistant_storage = S2AssistantStorage( | ||
table_name="local_rag_assistant", | ||
schema=config["database"], | ||
db_url=db_url, | ||
) | ||
|
||
local_assistant_knowledge = AssistantKnowledge( | ||
vector_db=S2VectorDb( | ||
collection="web_documents_singlestore", | ||
schema=config["database"], | ||
db_url=db_url, | ||
# Assuming OllamaEmbedder or a compatible embedder is used for SingleStore | ||
embedder=OllamaEmbedder(model="nomic-embed-text", dimensions=768), | ||
), | ||
num_documents=5, | ||
) | ||
|
||
|
||
def get_local_rag_assistant( | ||
model: str = "openhermes", | ||
user_id: Optional[str] = None, | ||
run_id: Optional[str] = None, | ||
debug_mode: bool = False, | ||
) -> Assistant: | ||
"""Get a Local URL RAG Assistant with SingleStore backend.""" | ||
return Assistant( | ||
name="local_rag_assistant", | ||
run_id=run_id, | ||
user_id=user_id, | ||
llm=Ollama(model=model), | ||
storage=local_assistant_storage, | ||
knowledge_base=local_assistant_knowledge, | ||
add_chat_history_to_messages=False, | ||
add_references_to_prompt=True, | ||
num_history_messages=4, | ||
markdown=True, | ||
debug_mode=debug_mode, | ||
description="You are an AI called 'Phi' designed to help users answer questions from a knowledge base.", | ||
assistant_data={"assistant_type": "rag"}, | ||
use_tools=True, | ||
show_tool_calls=True, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import typer | ||
from phi.assistant import Assistant | ||
from phi.llm.ollama import Ollama | ||
from phi.vectordb.singlestore import S2VectorDb | ||
from phi.embedder.ollama import OllamaEmbedder | ||
from phi.knowledge.pdf import PDFUrlKnowledgeBase | ||
from phi.storage.assistant.singlestore import S2AssistantStorage | ||
from resources import config # type: ignore | ||
|
||
db_url = ( | ||
f"mysql+pymysql://{config['username']}:{config['password']}@{config['host']}:{config['port']}/{config['database']}" | ||
) | ||
storage = S2AssistantStorage(table_name="pdf_assistant", schema=config["database"], db_url=db_url) | ||
knowledge_base = PDFUrlKnowledgeBase( | ||
urls=["https://phi-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf"], | ||
vector_db=S2VectorDb( | ||
collection="recipes", | ||
schema=config["database"], | ||
db_url=db_url, | ||
embedder=OllamaEmbedder(model="nomic-embed-text", dimensions=768), | ||
), | ||
) | ||
# Comment after first run to avoid reloading the knowledge base | ||
# knowledge_base.load(recreate=False) | ||
|
||
|
||
def local_assistant(model: str = "openhermes", debug: bool = False): | ||
print(f"============= Running: {model} =============") | ||
Assistant( | ||
llm=Ollama(model=model), | ||
storage=storage, | ||
knowledge_base=knowledge_base, | ||
add_references_to_prompt=True, | ||
debug_mode=debug, | ||
).cli_app(markdown=True) | ||
|
||
|
||
if __name__ == "__main__": | ||
typer.run(local_assistant) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
openai | ||
streamlit | ||
pypdf | ||
pymysql | ||
sqlalchemy | ||
phidata | ||
ollama | ||
anthropic |
Oops, something went wrong.