Skip to content

Commit

Permalink
refactor: configs
Browse files Browse the repository at this point in the history
  • Loading branch information
ramchaik committed Sep 2, 2024
1 parent c183534 commit 206aff0
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 49 deletions.
120 changes: 119 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Nous: Your Personal Knowledge Assistant 🧠💬

Nous is an open-source personal knowledge assistant that allows you to interact with your personal documents and text files using natural language. It's designed with a focus on privacy, security, and local processing.
Nous is an open-source personal knowledge assistant that allows you to interact with your personal documents and text files using natural language. It's designed with a focus on privacy, security, and local processing.

## Demo 🎥

Expand Down Expand Up @@ -179,6 +179,124 @@ URLS = [

After adding your links, restart the Flask app to ingest the new documents into the vector database.

## 🔧 Configuration

Nous uses two main configuration files: one for the Golang HTTP server and another for the Python Flask LLM service.

### Golang HTTP Server Configuration

Located at: `app/internal/config/config.go`

The Golang server configuration manages various aspects of the web server, including paths, addresses, and connections.

#### Configuration Structure

```go
type Config struct {
StaticPath string
TemplatesPath string
ServerAddr string
DatabasePath string
LLMBaseURL string
RedisAddr string
}
```

#### Configuration Options

- `StaticPath`: Path to static files (default: `"../static"` relative to the executable)
- `TemplatesPath`: Path to HTML templates (default: `"../templates/*"` relative to the executable)
- `ServerAddr`: Address and port for the HTTP server (default: `:8080`)
- `DatabasePath`: Path to the SQLite database file (default: `"./nous.db"`)
- `LLMBaseURL`: URL of the LLM service (default: `"http://localhost:5000"`)
- `RedisAddr`: Address of the Redis server (default: `"localhost:6379"`)

#### Customizing the Configuration

You can customize these settings using environment variables:

1. `STATIC_PATH`: Set the path to static files
2. `TEMPLATES_PATH`: Set the path to HTML templates
3. `SERVER_ADDR`: Set the server address and port
4. `DATABASE_PATH`: Set the path to the SQLite database file
5. `LLM_BASE_URL`: Set the URL of the LLM service
6. `REDIS_ADDR`: Set the address of the Redis server

Example:
```bash
export SERVER_ADDR=:8081
```

### LLM Service Configuration

Located at: `llm_api/config.py`

The LLM service configuration controls the behavior of the Flask app that manages LLM interactions.

#### Configuration Options

1. **API Keys**
```python
TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")
```
- Used for web search capabilities. Set this in your `.env` file.

2. **Vectorstore Paths**
```python
DATA_INDEX_PATH = os.getenv('DATA_INDEX_PATH', "data/faiss_index.bin")
DATA_VECTOR_PATH = os.getenv('DATA_VECTOR_PATH', "data/vectors.npy")
DATA_INDEX_TO_ID_PATH = os.getenv('DATA_INDEX_TO_ID_PATH', "data/index_to_id.npy")
```
- Determine where vectorstore data is saved and loaded from.

3. **Ollama Configuration**
```python
class OllamaConfig:
HOST = os.getenv('OLLAMA_HOST', 'http://localhost:11434')
EMBEDDINGS_MODEL = os.getenv('OLLAMA_EMBEDDINGS_MODEL', "nomic-embed-text")
MODEL_RAG = os.getenv('OLLAMA_MODEL_RAG', "phi3:mini")
MODEL_RETRIEVER_GRADER = os.getenv('OLLAMA_MODEL_RETRIEVER_GRADER', "thinyllama:mini")
```
- `HOST`: URL where Ollama is running
- `EMBEDDINGS_MODEL`: Model for generating embeddings
- `MODEL_RAG`: Model for Retrieval-Augmented Generation
- `MODEL_RETRIEVER_GRADER`: Model for grading retrieval results

4. **Personal Knowledge Base Resources**
```python
KNOWLEDGE_BASE_URLS = [
"https://tip.golang.org/tour/concurrency.article",
"https://tip.golang.org/doc/effective_go",
"https://gosafir.com/mag/wp-content/uploads/2019/12/Tolkien-J.-The-lord-of-the-rings-HarperCollins-ebooks-2010.pdf",
"https://gist.github.com/silver-xu/1dcceaa14c4f0253d9637d4811948437",
]
```
- List of URLs for documents to be ingested into the knowledge base.

#### Customizing the Configuration

To modify LLM service settings:

1. Open `llm_api/config.py` in a text editor.
2. Adjust values as needed.
3. For environment variables, either:
- Set them in your system environment, or
- Create a `.env` file in the `llm_api` directory with the required key-value pairs.
4. Save the file and restart the LLM service.

### Applying Configuration Changes

After modifying any configuration:

1. For the Golang server, rebuild and restart the server.
2. For the LLM service, restart the Flask app.
3. If using Docker, rebuild the containers:
```bash
docker-compose up --build
```

This ensures that all configuration changes are properly incorporated into the running services.

## Docker Compose Configuration

The `docker-compose.yml` file in the root directory contains the following services:
Expand Down
58 changes: 35 additions & 23 deletions app/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,47 @@ import (
"path/filepath"
)

// Config holds all configuration for the application
type Config struct {
StaticPath string
TemplatesPath string
ServerAddr string
DatabasePath string
LLMBaseURL string
RedisAddr string
StaticPath string
TemplatesPath string
ServerAddr string
DatabasePath string
LLMBaseURL string
RedisAddr string
}

// Load returns a new Config struct populated with values from environment variables or defaults
func Load() (*Config, error) {
ex, err := os.Executable()
if err != nil {
return nil, err
}
exPath := filepath.Dir(ex)
exPath, err := getExecutablePath()
if err != nil {
return nil, err
}

return &Config{
StaticPath: getEnv("STATIC_PATH", filepath.Join(exPath, "..", "static")),
TemplatesPath: getEnv("TEMPLATES_PATH", filepath.Join(exPath, "..", "templates", "*")),
ServerAddr: getEnv("SERVER_ADDR", ":8080"),
DatabasePath: getEnv("DATABASE_PATH", "./nous.db"),
LLMBaseURL: getEnv("LLM_BASE_URL", "http://localhost:5000"),
RedisAddr: getEnv("REDIS_ADDR", "localhost:6379"),
}, nil
return &Config{
StaticPath: getEnv("STATIC_PATH", filepath.Join(exPath, "..", "static")),
TemplatesPath: getEnv("TEMPLATES_PATH", filepath.Join(exPath, "..", "templates", "*")),
ServerAddr: getEnv("SERVER_ADDR", ":8080"),
DatabasePath: getEnv("DATABASE_PATH", "./nous.db"),
LLMBaseURL: getEnv("LLM_BASE_URL", "http://localhost:5000"),
RedisAddr: getEnv("REDIS_ADDR", "localhost:6379"),
}, nil
}

// getExecutablePath returns the directory of the current executable
func getExecutablePath() (string, error) {
ex, err := os.Executable()
if err != nil {
return "", err
}
return filepath.Dir(ex), nil
}

// getEnv retrieves the value of the environment variable named by the key
// If the variable is not present, it returns the fallback value
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return fallback
if value, exists := os.LookupEnv(key); exists {
return value
}
return fallback
}
29 changes: 26 additions & 3 deletions llm_api/config.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
import os
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# API Keys
TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")

URLS = [
# Vectorstore paths
DATA_INDEX_PATH = os.getenv('DATA_INDEX_PATH', "data/faiss_index.bin")
DATA_VECTOR_PATH = os.getenv('DATA_VECTOR_PATH', "data/vectors.npy")
DATA_INDEX_TO_ID_PATH = os.getenv('DATA_INDEX_TO_ID_PATH', "data/index_to_id.npy")

# Ollama configuration
class OllamaConfig:
HOST = os.getenv('OLLAMA_HOST', 'http://localhost:11434')
EMBEDDINGS_MODEL = os.getenv('OLLAMA_EMBEDDINGS_MODEL', "nomic-embed-text")
MODEL_RAG = os.getenv('OLLAMA_MODEL_RAG', "phi3:mini")
MODEL_RETRIEVER_GRADER = os.getenv('OLLAMA_MODEL_RETRIEVER_GRADER', "thinyllama:mini")

# Personal Knowledge base resources
KNOWLEDGE_BASE_URLS = [
"https://tip.golang.org/tour/concurrency.article",
"https://tip.golang.org/doc/effective_go",
# "https://socrates.acadiau.ca/courses/engl/rcunningham/resources/Shpe/Hamlet.pdf",
"https://gosafir.com/mag/wp-content/uploads/2019/12/Tolkien-J.-The-lord-of-the-rings-HarperCollins-ebooks-2010.pdf"
"https://gosafir.com/mag/wp-content/uploads/2019/12/Tolkien-J.-The-lord-of-the-rings-HarperCollins-ebooks-2010.pdf",
"https://gist.github.com/silver-xu/1dcceaa14c4f0253d9637d4811948437",
]

# Ensure all required environment variables are set
def validate_env_vars():
required_vars = ["TAVILY_API_KEY"]
for var in required_vars:
if not os.getenv(var):
raise EnvironmentError(f"Missing required environment variable: {var}")

validate_env_vars()
25 changes: 12 additions & 13 deletions llm_api/embeddings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@
from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.docstore.in_memory import InMemoryDocstore
import faiss
from llm_api.config import DATA_INDEX_PATH, DATA_INDEX_TO_ID_PATH, DATA_VECTOR_PATH, OllamaConfig
import numpy as np
import os
from typing import TypedDict, List

def create_vectorstore(documents: List[str], force_rebuild: bool = False) -> FAISS:
index_path = "data/faiss_index.bin"
vectors_path = "data/vectors.npy"
index_to_id_path = "data/index_to_id.npy"
index_path = DATA_INDEX_PATH
vectors_path = DATA_VECTOR_PATH
index_to_id_path = DATA_INDEX_TO_ID_PATH

if not force_rebuild and os.path.exists(index_path) and os.path.exists(vectors_path) and os.path.exists(index_to_id_path):
# Load existing index and vectors
index = faiss.read_index(index_path)
docstore_dict = np.load(vectors_path, allow_pickle=True).item()
index_to_id = np.load(index_to_id_path, allow_pickle=True).item()

# Create InMemoryDocstore from the loaded dictionary
docstore = InMemoryDocstore(docstore_dict)

ollama_host = os.getenv('OLLAMA_HOST', 'http://localhost:11434')
ollama_embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url=ollama_host)

ollama_embeddings = OllamaEmbeddings(model=OllamaConfig.EMBEDDINGS_MODEL, base_url=OllamaConfig.HOST)
vectorstore = FAISS(
embedding_function=ollama_embeddings,
index=index,
Expand All @@ -31,14 +31,13 @@ def create_vectorstore(documents: List[str], force_rebuild: bool = False) -> FAI
print("Loaded existing vectorstore from disk.")
else:
# Create new vectorstore
ollama_host = os.getenv('OLLAMA_HOST', 'http://localhost:11434')
ollama_embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url=ollama_host)
ollama_embeddings = OllamaEmbeddings(model=OllamaConfig.EMBEDDINGS_MODEL, base_url=OllamaConfig.HOST)
vectorstore = FAISS.from_documents(documents, ollama_embeddings)

# Save the index, vectors, and index_to_docstore_id separately
faiss.write_index(vectorstore.index, index_path)
np.save(vectors_path, vectorstore.docstore._dict)
np.save(index_to_id_path, vectorstore.index_to_docstore_id)
print("Created new vectorstore and saved to disk.")
return vectorstore

return vectorstore
8 changes: 4 additions & 4 deletions llm_api/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import logging
import concurrent.futures

from config import TAVILY_API_KEY, URLS
from config import OllamaConfig, TAVILY_API_KEY, KNOWLEDGE_BASE_URLS
from document_processor import load_and_split_documents
from embeddings import create_vectorstore
from retriever import create_retriever
Expand All @@ -21,13 +21,13 @@
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

doc_splits = load_and_split_documents(URLS)
doc_splits = load_and_split_documents(KNOWLEDGE_BASE_URLS)
vectorstore = create_vectorstore(doc_splits, force_rebuild=False)
retriever = create_retriever(vectorstore)

llm = create_llm(model="phi3:mini")
llm = create_llm(model=OllamaConfig.MODEL_RAG)
rag_chain = rag_prompt | llm | StrOutputParser()
retrieval_grader = grading_prompt | create_llm(model="tinyllama", format="json") | JsonOutputParser()
retrieval_grader = grading_prompt | create_llm(model=OllamaConfig.MODEL_RETRIEVER_GRADER, format="json") | JsonOutputParser()

web_search_tool = TavilySearchResults(api_key=TAVILY_API_KEY)

Expand Down
11 changes: 6 additions & 5 deletions llm_api/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from langchain_ollama import ChatOllama
from functools import lru_cache

from llm_api.config import OLLAMA_HOST

@lru_cache(maxsize=2)
def create_llm(model="llama3.1", temperature=0, format=''):
ollama_host = os.getenv('OLLAMA_HOST', 'http://localhost:11434')
return ChatOllama(
model=model,
temperature=temperature,
model=model,
temperature=temperature,
format=format,
base_url=ollama_host
)
base_url=OLLAMA_HOST
)

0 comments on commit 206aff0

Please sign in to comment.