Skip to content

Commit

Permalink
feat: add voyage embeddings (#171)
Browse files Browse the repository at this point in the history
Closes #169
  • Loading branch information
fynnfluegge authored Nov 29, 2024
1 parent d135ec9 commit a1f32ce
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 15 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@

</div>

Rocketnotes is a web-based Markdown note taking app with LLM-powered text completion, chat and semantic search.
Rocketnotes is a web-based Markdown note taking app with LLM-powered text completion, chat and semantic search.
It utilizes a [100% Serverless RAG pipeline](https://medium.com/@fynnfluegge/serverless-rag-on-aws-bf8029f8bffd) built with
[langchain](https://github.com/langchain-ai/langchain),
[sentence-transformers](https://github.com/UKPLab/sentence-transformers),
[faiss](https://github.com/facebookresearch/faiss),
[Ollama](https://github.com/jmorganca/ollama) and OpenAI or Anthropic.
[Ollama](https://github.com/jmorganca/ollama), OpenAI, Anthropic and Voyage.

## How to use

Expand All @@ -39,7 +39,7 @@ It utilizes a [100% Serverless RAG pipeline](https://medium.com/@fynnfluegge/ser
- 🔦 Semantic search
- ✍️ Copilot-like text completion
- 🤖 Chat with your documents
- Serverless RAG with faiss, OpenAI and/or Anthropic
- Serverless RAG with faiss, OpenAI, Anthropic, Voyage
- 📦 Local mode with Docker
- use Ollama and/or Sentence Transformers for 100% local RAG
- 📥 Zettelkasten with semantic archiving
Expand Down
14 changes: 13 additions & 1 deletion lambda-handler/chat-handler/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationSummaryMemory
from langchain_anthropic import ChatAnthropic
from langchain_community.embeddings import HuggingFaceEmbeddings, OllamaEmbeddings
from langchain_community.embeddings import (
HuggingFaceEmbeddings,
OllamaEmbeddings,
VoyageEmbeddings,
)
from langchain_community.llms import Ollama
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
Expand Down Expand Up @@ -72,6 +76,12 @@ def handler(event, context):
else:
return {"statusCode": 400, "body": "OpenAI API key is missing"}

if embeddings_model == "voyage-2":
if "voyageApiKey" in userConfig:
os.environ["VOYAGE_API_KEY"] = userConfig.get("voyageApiKey").get("S")
else:
return {"statusCode": 400, "body": "OpenAI API key is missing"}

if "llm" in userConfig:
llm_model = userConfig.get("llm").get("S")
else:
Expand Down Expand Up @@ -106,6 +116,8 @@ def handler(event, context):

if embeddings_model == "text-embedding-ada-002":
embeddings = OpenAIEmbeddings(client=None, model="text-embedding-ada-002")
elif embeddings_model == "voyage-2":
embeddings = VoyageEmbeddings(model=embeddings_model)
elif embeddings_model == "Sentence-Transformers":
embeddings = HuggingFaceEmbeddings(model_kwargs={"device": "cpu"})
elif embeddings_model == "Ollama-nomic-embed-text":
Expand Down
1 change: 1 addition & 0 deletions lambda-handler/get-user-config-handler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type UserConfig struct {
Llm string `json:"llm"`
OpenAiApiKey string `json:"openAiApiKey"`
AnthropicApiKey string `json:"anthropicApiKey"`
VoyageApiKey string `json:"voyageApiKey"`
}

func init() {
Expand Down
7 changes: 5 additions & 2 deletions lambda-handler/save-user-config-handler/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type RequestBody struct {
Llm string `json:"llm"`
OpenAiApiKey string `json:"openAiApiKey"`
AnthropicApiKey string `json:"anthropicApiKey"`
VoyageApiKey string `json:"voyageApiKey`
RecreateIndex bool `json:"recreateIndex"`
}

Expand All @@ -30,12 +31,14 @@ type UserConfig struct {
Llm string `json:"llm"`
OpenAiApiKey string `json:"openAiApiKey"`
AnthropicApiKey string `json:"anthropicApiKey"`
VoyageApiKey string `json:"voyageApiKey"`
}

type SqsMessage struct {
UserId string `json:"userId"`
OpenAiApiKey string `json:"openAiApiKey"`
AnthropicApiKey string `json:"anthropicApiKey"`
VoyageApiKey string `json:"voyageApiKey`
RecreateIndex bool `json:"recreateIndex"`
}

Expand Down Expand Up @@ -63,7 +66,7 @@ func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (

tableName := "tnn-UserConfig"

av, err := dynamodbattribute.MarshalMap(UserConfig{item.Id, item.EmbeddingModel, item.Llm, item.OpenAiApiKey, item.AnthropicApiKey})
av, err := dynamodbattribute.MarshalMap(UserConfig{item.Id, item.EmbeddingModel, item.Llm, item.OpenAiApiKey, item.AnthropicApiKey, item.VoyageApiKey})

if err != nil {
log.Fatalf("Got error marshalling new document item: %s", err)
Expand All @@ -83,7 +86,7 @@ func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (
if os.Getenv("USE_LOCAL_DYNAMODB") != "1" && item.RecreateIndex {
qsvc := sqs.New(sess)

m := SqsMessage{item.Id, item.OpenAiApiKey, item.AnthropicApiKey, item.RecreateIndex}
m := SqsMessage{item.Id, item.OpenAiApiKey, item.AnthropicApiKey, item.VoyageApiKey, item.RecreateIndex}
b, err := json.Marshal(m)

_, err = qsvc.SendMessage(&sqs.SendMessageInput{
Expand Down
25 changes: 19 additions & 6 deletions lambda-handler/save-vector-embeddings-handler/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
MarkdownHeaderTextSplitter,
RecursiveCharacterTextSplitter,
)
from langchain_community.embeddings import HuggingFaceEmbeddings, OllamaEmbeddings
from langchain_community.embeddings import (
HuggingFaceEmbeddings,
OllamaEmbeddings,
VoyageEmbeddings,
)
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

Expand Down Expand Up @@ -53,11 +57,11 @@ def handler(event, context):
userConfig = userConfig["Item"]
embeddingsModel = userConfig.get("embeddingModel", {}).get("S", None)
openAiApiKey = userConfig.get("openAiApiKey", {}).get("S", None)
anthropicApiKey = userConfig.get("anthropicApiKey", {}).get("S", None)
voyageApiKey = userConfig.get("voyageApiKey", {}).get("S", None)
if openAiApiKey is not None:
os.environ["OPENAI_API_KEY"] = openAiApiKey
if anthropicApiKey is not None:
os.environ["ANTHROPIC_API_KEY"] = anthropicApiKey
if voyageApiKey is not None:
os.environ["VOYAGE_API_KEY"] = voyageApiKey

if embeddingsModel == "text-embedding-ada-002":
if openAiApiKey is None:
Expand All @@ -66,6 +70,13 @@ def handler(event, context):
"body": json.dumps("OpenAI API key not found"),
}
embeddings = OpenAIEmbeddings(client=None, model=embeddingsModel)
elif embeddingsModel == "voyage-2":
if voyageApiKey is None:
return {
"statusCode": 400,
"body": json.dumps("Voyage API key not found"),
}
embeddings = VoyageEmbeddings(model=embeddingsModel)
elif embeddingsModel == "Sentence-Transformers":
embeddings = HuggingFaceEmbeddings(model_kwargs={"device": "cpu"})
elif embeddingsModel == "Ollama-nomic-embed-text":
Expand Down Expand Up @@ -140,10 +151,10 @@ def handler(event, context):
else:
# Faiss index does not exist, create the index from scratch
# ---------------------------------------------------------
filter_expression = "userId = :user_value AND deleted = :deleted_value"
filter_expression = "userId = :user_value"
expression_attribute_values = {
":user_value": {"S": userId},
":deleted_value": {"BOOL": False},
# ":deleted_value": {"BOOL": False},
}

# Execute the query
Expand All @@ -159,6 +170,8 @@ def handler(event, context):
if documents:
split_documents = []
for document in documents:
if document["deleted"]["BOOL"]:
continue
try:
content = document["content"]["S"]
documentId = document["id"]["S"]
Expand Down
14 changes: 12 additions & 2 deletions lambda-handler/semantic-search-handler/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
from pathlib import Path

import boto3
from langchain_community.embeddings import HuggingFaceEmbeddings, OllamaEmbeddings
from langchain_community.embeddings import (
HuggingFaceEmbeddings,
OllamaEmbeddings,
VoyageEmbeddings,
)
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

Expand Down Expand Up @@ -64,7 +68,13 @@ def handler(event, context):
os.environ["OPENAI_API_KEY"] = userConfig.get("openAiApiKey").get("S")
else:
return {"statusCode": 400, "body": "OpenAI API key is missing"}
embeddings = OpenAIEmbeddings(client=None, model="text-embedding-ada-002")
embeddings = OpenAIEmbeddings(client=None, model=embeddings_model)
elif embeddings_model == "voyage-2":
if "voyageApiKey" in userConfig:
os.environ["VOYAGE_API_KEY"] = userConfig.get("voyageApiKey").get("S")
else:
return {"statusCode": 400, "body": "OpenAI API key is missing"}
embeddings = VoyageEmbeddings(model=embeddings_model)
elif embeddings_model == "Sentence-Transformers":
embeddings = HuggingFaceEmbeddings(model_kwargs={"device": "cpu"})
elif embeddings_model == "Ollama-nomic-embed-text":
Expand Down
2 changes: 1 addition & 1 deletion landing-page/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ <h3 class="h3 mb-3">
>Ollama</a
>
<br />
and OpenAI or Anthropic.
and OpenAI/Anthropic/Voyage
</p>
<p>
Read details below to learn more about all feature of the powerful
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ <h5>Embeddings Model</h5>
value="text-embedding-ada-002"
/>Text-Embedding-Ada-002
</label>
<label class="radio-inline">
<input
type="radio"
name="embeddingModel"
[(ngModel)]="selectedEmbeddingModel"
value="voyage-2"
/>Voyage-2
</label>
<label class="radio-inline" *ngIf="isLocal">
<input
type="radio"
Expand Down Expand Up @@ -158,6 +166,25 @@ <h5>Anthropic API Key</h5>
key
</span>
</div>
<h5>Voyage API Key</h5>
<textarea
type="text"
id="voyageApiKey"
rows="2"
[placeholder]="'Enter your Voyage API key...'"
style="width: 94%"
[(ngModel)]="voyageApiKey"
#message="ngModel"
name="voyageApiKey"
></textarea>
<div>
<span
id="voyageApiKeyRequired"
style="text-align: center; color: var(--warning-color); display: none"
>
For the selected configuration, you need to provide a Voyage API key
</span>
</div>
<br />
<div>
<span style="text-align: right"
Expand Down
18 changes: 18 additions & 0 deletions webapp/src/app/component/dialog-config/config-dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class ConfigDialogComponent implements OnDestroy, OnInit {
selectedLlm: string = 'gpt-3.5-turbo';
openAiApiKey: string;
anthropicApiKey: string;
voyageApiKey: string;
isLocal: boolean = !environment.production;

constructor(
Expand All @@ -42,6 +43,7 @@ export class ConfigDialogComponent implements OnDestroy, OnInit {
}
this.openAiApiKey = config['openAiApiKey'] ?? '';
this.anthropicApiKey = config['anthropicApiKey'] ?? '';
this.voyageApiKey = config['voyageApiKey'] ?? '';
});
const overlay = document.getElementById('configDialog');
if (overlay.getAttribute('outsideClickListener') !== 'true') {
Expand Down Expand Up @@ -84,13 +86,28 @@ export class ConfigDialogComponent implements OnDestroy, OnInit {
'anthropicApiKeyRequired',
);
anthropicApiKeyRequiredWarning.style.display = 'none';

if (this.selectedEmbeddingModel === 'voyage-2' && !this.voyageApiKey) {
const voyageApiKeyRequiredWarning = document.getElementById(
'voyageApiKeyRequired',
);
voyageApiKeyRequiredWarning.style.display = 'block';
return;
} else {
const voyageApiKeyRequiredWarning = document.getElementById(
'voyageApiKeyRequired',
);
voyageApiKeyRequiredWarning.style.display = 'none';
}

this.restService
.post('userConfig', {
id: localStorage.getItem('currentUserId'),
embeddingModel: this.selectedEmbeddingModel,
llm: this.selectedLlm,
openAiApiKey: this.openAiApiKey,
anthropicApiKey: this.anthropicApiKey,
voyageApiKey: this.voyageApiKey,
recreateIndex:
this.currentEmbeddingModel !== this.selectedEmbeddingModel,
})
Expand All @@ -109,6 +126,7 @@ export class ConfigDialogComponent implements OnDestroy, OnInit {
llm: this.selectedLlm,
openAiApiKey: this.openAiApiKey,
anthropicApiKey: this.anthropicApiKey,
voyageApiKey: this.voyageApiKey,
}),
);
if (
Expand Down

0 comments on commit a1f32ce

Please sign in to comment.