Skip to content

Commit

Permalink
S2 demo
Browse files Browse the repository at this point in the history
  • Loading branch information
ashpreetbedi committed Apr 11, 2024
1 parent 9ee8fbd commit 1bb31e9
Show file tree
Hide file tree
Showing 9 changed files with 569 additions and 0 deletions.
Empty file.
Empty file.
76 changes: 76 additions & 0 deletions cookbook/integrations/singlestore/auto_rag/README.md
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.
155 changes: 155 additions & 0 deletions cookbook/integrations/singlestore/auto_rag/app.py
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()
58 changes: 58 additions & 0 deletions cookbook/integrations/singlestore/auto_rag/assistant.py
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,
)
39 changes: 39 additions & 0 deletions cookbook/integrations/singlestore/auto_rag/cli.py
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)
8 changes: 8 additions & 0 deletions cookbook/integrations/singlestore/auto_rag/requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
openai
streamlit
pypdf
pymysql
sqlalchemy
phidata
ollama
anthropic
Loading

0 comments on commit 1bb31e9

Please sign in to comment.