Skip to content

Commit

Permalink
🐳 feat: RAG for Default Docker Compose Files + Docs Update (danny-avi…
Browse files Browse the repository at this point in the history
…la#2246)

* refactor(deploy-compose.yml): use long-syntax to avoid implicit folder creation of librechat.yaml

* refactor(docker-compose.override.yml.example): use long-syntax to avoid implicit folder creation of librechat.yaml

* chore: add simple health check for RAG_API_URL

* chore: improve axios error handling, adding `logAxiosError`

* chore: more informative message detailing RAG_API_URL path

* feat: add rag_api and vectordb to default compose file

* chore(rag.yml): update standalone rag compose file to use RAG_PORT

* chore: documentation updates

* docs: Update rag_api.md with images

* Update rag_api.md

* Update rag_api.md, assistants clarification

* add RAG API note to breaking changes
  • Loading branch information
danny-avila authored Mar 30, 2024
1 parent 6a6b2e7 commit 56ea0f9
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 74 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@

- 🖥️ UI matching ChatGPT, including Dark mode, Streaming, and latest updates
- 💬 Multimodal Chat:
- Upload and analyze images with GPT-4 and Gemini Vision 📸
- General file support now available through the Assistants API integration. 🗃️
- Local RAG in Active Development 🚧
- Upload and analyze images with Claude 3, GPT-4, and Gemini Vision 📸
- Chat with Files using Custom Endpoints, OpenAI, Azure, Anthropic, & Google. 🗃️
- Advanced Agents with Files, Code Interpreter, Tools, and API Actions 🔦
- Available through the [OpenAI Assistants API](https://platform.openai.com/docs/assistants/overview) 🌤️
- Non-OpenAI Agents in Active Development 🚧
- 🌎 Multilingual UI:
- English, 中文, Deutsch, Español, Français, Italiano, Polski, Português Brasileiro,
- Русский, 日本語, Svenska, 한국어, Tiếng Việt, 繁體中文, العربية, Türkçe, Nederlands, עברית
Expand All @@ -55,7 +57,9 @@
- 🔍 Search all messages/conversations
- 🔌 Plugins, including web access, image generation with DALL-E-3 and more
- 👥 Multi-User, Secure Authentication with Moderation and Token spend tools
- ⚙️ Configure Proxy, Reverse Proxy, Docker, many Deployment options, and completely Open-Source
- ⚙️ Configure Proxy, Reverse Proxy, Docker, & many Deployment options
- 📖 Completely Open-Source & Built in Public
- 🧑‍🤝‍🧑 Community-driven development, support, and feedback

[For a thorough review of our features, see our docs here](https://docs.librechat.ai/features/plugins/introduction.html) 📚

Expand Down
11 changes: 11 additions & 0 deletions api/server/services/AppService.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,17 @@ const AppService = async (app) => {
};
}

try {
const response = await fetch(`${process.env.RAG_API_URL}/health`);
if (response?.ok && response?.status === 200) {
logger.info(`RAG API is running and reachable at ${process.env.RAG_API_URL}.`);
}
} catch (error) {
logger.warn(
`RAG API is either not running or not reachable at ${process.env.RAG_API_URL}, you may experience errors with file uploads.`,
);
}

app.locals = {
socialLogins,
availableTools,
Expand Down
29 changes: 2 additions & 27 deletions api/server/services/ModelService.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { EModelEndpoint, defaultModels, CacheKeys } = require('librechat-data-provider');
const { extractBaseURL, inputSchema, processModelData } = require('~/utils');
const { extractBaseURL, inputSchema, processModelData, logAxiosError } = require('~/utils');
const getLogStores = require('~/cache/getLogStores');
const { logger } = require('~/config');

// const { getAzureCredentials, genAzureChatCompletion } = require('~/utils/');

const { openAIApiKey, userProvidedOpenAI } = require('./Config/EndpointService').config;

Expand Down Expand Up @@ -77,29 +74,7 @@ const fetchModels = async ({
models = input.data.map((item) => item.id);
} catch (error) {
const logMessage = `Failed to fetch models from ${azure ? 'Azure ' : ''}${name} API`;
if (error.response) {
logger.error(
`${logMessage} The request was made and the server responded with a status code that falls out of the range of 2xx: ${
error.message ? error.message : ''
}`,
{
headers: error.response.headers,
status: error.response.status,
data: error.response.data,
},
);
} else if (error.request) {
logger.error(
`${logMessage} The request was made but no response was received: ${
error.message ? error.message : ''
}`,
{
request: error.request,
},
);
} else {
logger.error(`${logMessage} Something happened in setting up the request`, error);
}
logAxiosError({ message: logMessage, error });
}

return models;
Expand Down
31 changes: 3 additions & 28 deletions api/server/services/Runs/methods.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const axios = require('axios');
const { EModelEndpoint } = require('librechat-data-provider');
const { logger } = require('~/config');
const { logAxiosError } = require('~/utils');

/**
* @typedef {Object} RetrieveOptions
Expand Down Expand Up @@ -54,33 +54,8 @@ async function retrieveRun({ thread_id, run_id, timeout, openai }) {
const response = await axios.get(url, axiosConfig);
return response.data;
} catch (error) {
const logMessage = '[retrieveRun] Failed to retrieve run data:';
const timedOutMessage = 'Cannot read properties of undefined (reading \'status\')';
if (error?.response && error?.response?.status) {
logger.error(
`${logMessage} The request was made and the server responded with a status code that falls out of the range of 2xx: ${
error.message ? error.message : ''
}`,
{
headers: error.response.headers,
status: error.response.status,
data: error.response.data,
},
);
} else if (error.request) {
logger.error(
`${logMessage} The request was made but no response was received: ${
error.message ? error.message : ''
}`,
{
request: error.request,
},
);
} else if (error?.message && !error?.message?.includes(timedOutMessage)) {
logger.error(`${logMessage} Something happened in setting up the request`, {
message: error.message,
});
}
const message = '[retrieveRun] Failed to retrieve run data:';
logAxiosError({ message, error });
throw error;
}
}
Expand Down
6 changes: 4 additions & 2 deletions api/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
const loadYaml = require('./loadYaml');
const tokenHelpers = require('./tokens');
const azureUtils = require('./azureUtils');
const logAxiosError = require('./logAxiosError');
const extractBaseURL = require('./extractBaseURL');
const findMessageContent = require('./findMessageContent');

module.exports = {
...azureUtils,
loadYaml,
...tokenHelpers,
...azureUtils,
logAxiosError,
extractBaseURL,
findMessageContent,
loadYaml,
};
45 changes: 45 additions & 0 deletions api/utils/logAxiosError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { logger } = require('~/config');

/**
* Logs Axios errors based on the error object and a custom message.
*
* @param {Object} options - The options object.
* @param {string} options.message - The custom message to be logged.
* @param {Error} options.error - The Axios error object.
*/
const logAxiosError = ({ message, error }) => {
const timedOutMessage = 'Cannot read properties of undefined (reading \'status\')';
if (error.response) {
logger.error(
`${message} The request was made and the server responded with a status code that falls out of the range of 2xx: ${
error.message ? error.message : ''
}. Error response data:\n`,
{
headers: error.response?.headers,
status: error.response?.status,
data: error.response?.data,
},
);
} else if (error.request) {
logger.error(
`${message} The request was made but no response was received: ${
error.message ? error.message : ''
}. Error Request:\n`,
{
request: error.request,
},
);
} else if (error?.message?.includes(timedOutMessage)) {
logger.error(
`${message}\nThe request either timed out or was unsuccessful. Error message:\n`,
error,
);
} else {
logger.error(
`${message}\nSomething happened in setting up the request. Error message:\n`,
error,
);
}
};

module.exports = logAxiosError;
30 changes: 28 additions & 2 deletions deploy-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ services:
- 3080:3080
depends_on:
- mongodb
- rag_api
restart: always
extra_hosts:
- "host.docker.internal:host-gateway"
Expand All @@ -21,11 +22,14 @@ services:
- NODE_ENV=production
- MONGO_URI=mongodb://mongodb:27017/LibreChat
- MEILI_HOST=http://meilisearch:7700
- RAG_PORT=${RAG_PORT:-8000}
- RAG_API_URL=http://rag_api:${RAG_PORT:-8000}
volumes:
- type: bind
source: ./librechat.yaml
target: /app/librechat.yaml
- ./images:/app/client/public/images
- ./librechat.yaml:/app/librechat.yaml
- ./logs:/app/api/logs
- ./uploads:/app/uploads
client:
build:
context: .
Expand Down Expand Up @@ -61,3 +65,25 @@ services:
- MEILI_NO_ANALYTICS=true
volumes:
- ./meili_data_v1.7:/meili_data
vectordb:
image: ankane/pgvector:latest
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
restart: always
volumes:
- pgdata2:/var/lib/postgresql/data
rag_api:
image: ghcr.io/danny-avila/librechat-rag-api-dev-lite:latest
environment:
- DB_HOST=vectordb
- RAG_PORT=${RAG_PORT:-8000}
restart: always
depends_on:
- vectordb
env_file:
- .env

volumes:
pgdata2:
12 changes: 10 additions & 2 deletions docker-compose.override.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ version: '3.4'
# services:
# api:
# volumes:
# - ./librechat.yaml:/app/librechat.yaml
# - type: bind
# source: ./librechat.yaml
# target: /app/librechat.yaml
# image: ghcr.io/danny-avila/librechat:latest

# ---------------------------------------------------
Expand All @@ -26,7 +28,9 @@ version: '3.4'
# # USE LIBRECHAT CONFIG FILE
# api:
# volumes:
# - ./librechat.yaml:/app/librechat.yaml
# - type: bind
# source: ./librechat.yaml
# target: /app/librechat.yaml

# # LOCAL BUILD
# api:
Expand Down Expand Up @@ -93,6 +97,10 @@ version: '3.4'
# ports:
# - 7700:7700

# # USE RAG API IMAGE WITH LOCAL EMBEDDINGS SUPPORT
# rag_api:
# image: ghcr.io/danny-avila/librechat-rag-api-dev:latest

# # ADD OLLAMA
# ollama:
# image: ollama/ollama:latest
Expand Down
30 changes: 28 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ services:
- "${PORT}:${PORT}"
depends_on:
- mongodb
- rag_api
image: ghcr.io/danny-avila/librechat-dev:latest
restart: always
user: "${UID}:${GID}"
Expand All @@ -19,10 +20,13 @@ services:
- HOST=0.0.0.0
- MONGO_URI=mongodb://mongodb:27017/LibreChat
- MEILI_HOST=http://meilisearch:7700
- RAG_PORT=${RAG_PORT:-8000}
- RAG_API_URL=http://rag_api:${RAG_PORT:-8000}
volumes:
- ./.env:/app/.env
- type: bind
source: ./.env
target: /app/.env
- ./images:/app/client/public/images
- ./uploads:/app/uploads
- ./logs:/app/api/logs
mongodb:
container_name: chat-mongodb
Expand All @@ -42,3 +46,25 @@ services:
- MEILI_NO_ANALYTICS=true
volumes:
- ./meili_data_v1.7:/meili_data
vectordb:
image: ankane/pgvector:latest
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
restart: always
volumes:
- pgdata2:/var/lib/postgresql/data
rag_api:
image: ghcr.io/danny-avila/librechat-rag-api-dev-lite:latest
environment:
- DB_HOST=vectordb
- RAG_PORT=${RAG_PORT:-8000}
restart: always
depends_on:
- vectordb
env_file:
- .env

volumes:
pgdata2:
3 changes: 2 additions & 1 deletion docs/features/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ weight: 2
---

* 🤖[Custom Endpoints](../install/configuration/custom_config.md)
* 🗃️ [RAG API (Chat with Files)](./rag_api.md)
* 🔖 [Presets](./presets.md)
* 🔌[Plugins](./plugins/index.md)
* 🔌 [Introduction](./plugins/introduction.md)
* 🛠️ [Make Your Own](./plugins/make_your_own.md)
Expand All @@ -17,7 +19,6 @@ weight: 2
* 🖌️ [Stable Diffusion](./plugins/stable_diffusion.md)
* 🧠 [Wolfram|Alpha](./plugins/wolfram.md)
*[Azure AI Search](./plugins/azure_ai_search.md)
* 🔖 [Presets](./presets.md)

---

Expand Down
2 changes: 1 addition & 1 deletion docs/features/mod_system.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: 🔨 Automated Moderation
description: The Automated Moderation System uses a scoring mechanism to track user violations. As users commit actions like excessive logins, registrations, or messaging, they accumulate violation scores. Upon reaching a set threshold, the user and their IP are temporarily banned. This system ensures platform security by monitoring and penalizing rapid or suspicious activities.
weight: -8
weight: -7
---
## Automated Moderation System (optional)
The Automated Moderation System uses a scoring mechanism to track user violations. As users commit actions like excessive logins, registrations, or messaging, they accumulate violation scores. Upon reaching a set threshold, the user and their IP are temporarily banned. This system ensures platform security by monitoring and penalizing rapid or suspicious activities.
Expand Down
2 changes: 1 addition & 1 deletion docs/features/plugins/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Plugins
description: 🔌 All about plugins, how to make them, how use the official ChatGPT plugins, and how to configure custom plugins.
weight: -10
weight: -9
---

# Plugins
Expand Down
Loading

0 comments on commit 56ea0f9

Please sign in to comment.