Skip to content

Commit

Permalink
Merge branch 'danny-avila:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
paychex-ssmithrand authored Oct 31, 2023
2 parents 281465c + b031dea commit 3d4d159
Show file tree
Hide file tree
Showing 202 changed files with 4,430 additions and 1,095 deletions.
33 changes: 32 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

APP_TITLE=LibreChat

# Uncomment to add a custom footer.
# Uncomment and make empty "" to remove the footer.
# CUSTOM_FOOTER="My custom footer"

# The server will listen to localhost:3080 by default. You can change the target IP as you want.
# If you want to make this server available externally, for example to share the server with others
# or expose this from a Docker container, set host to 0.0.0.0 or your external IP interface.
Expand All @@ -13,6 +17,21 @@ APP_TITLE=LibreChat
HOST=localhost
PORT=3080

# Note: the following enables user balances, which you can add manually
# or you will need to build out a balance accruing system for users.
# For more info, see https://docs.librechat.ai/features/token_usage.html

# To manually add balances, run the following command:
# `npm run add-balance`

# You can also specify the email and token credit amount to add, e.g.:
# `npm run add-balance example@example.com 1000`

# This works well to track your own usage for personal use; 1000 credits = $0.001 (1 mill USD)

# Set to true to enable token credit balances for the OpenAI/Plugins endpoints
CHECK_BALANCE=false

# Automated Moderation System
# 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
Expand Down Expand Up @@ -52,6 +71,11 @@ LIMIT_MESSAGE_USER=false # Whether to limit the amount of messages an IP can sen
MESSAGE_USER_MAX=40 # The max amount of messages an IP can send per MESSAGE_USER_WINDOW
MESSAGE_USER_WINDOW=1 # in minutes, determines the window of time for MESSAGE_USER_MAX messages

# If you have permission problems, set here the UID and GID of the user running
# the docker compose command. The applications in the container will run with these uid/gid.
UID=1000
GID=1000

# Change this to proxy any API request.
# It's useful if your machine has difficulty calling the original API server.
# PROXY=
Expand Down Expand Up @@ -102,6 +126,12 @@ DEBUG_OPENAI=false # Set to true to enable debug mode for the OpenAI endpoint
# https://github.com/waylaidwanderer/node-chatgpt-api#using-a-reverse-proxy
# OPENAI_REVERSE_PROXY=

# (Advanced) Sometimes when using Local LLM APIs, you may need to force the API
# to be called with a `prompt` payload instead of a `messages` payload; to mimic the
# a `/v1/completions` request instead of `/v1/chat/completions`
# This may be the case for LocalAI with some models. To do so, uncomment the following:
# OPENAI_FORCE_PROMPT=true

##########################
# OpenRouter (overrides OpenAI and Plugins Endpoints):
##########################
Expand Down Expand Up @@ -372,7 +402,8 @@ DOMAIN_SERVER=http://localhost:3080
###########################

# Email is used for password reset. Note that all 4 values must be set for email to work.
# Failing to set the 4 values will result in LibreChat using the unsecured password reset!
EMAIL_SERVICE= # eg. gmail
EMAIL_USERNAME= # eg. your email address if using gmail
EMAIL_PASSWORD= # eg. this is the "app password" if using gmail
EMAIL_FROM= # eg. email address for from field like noreply@librechat.ai
EMAIL_FROM=noreply@librechat.ai # email address for from field, it is required to set a value here even in the cases where it's not porperly working.
2 changes: 1 addition & 1 deletion .github/workflows/backend-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
BAN_VIOLATIONS: ${{ secrets.BAN_VIOLATIONS }}
BAN_DURATION: ${{ secrets.BAN_DURATION }}
BAN_INTERVAL: ${{ secrets.BAN_INTERVAL }}
NODE_ENV: ci
NODE_ENV: CI
steps:
- uses: actions/checkout@v2
- name: Use Node.js 20.x
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/frontend-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ jobs:
run: npm run frontend:ci

- name: Run unit tests
run: cd client && npm run test:ci
run: npm run test:ci --verbose
working-directory: client
40 changes: 40 additions & 0 deletions .github/workflows/latest-images-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Docker Compose Build on Main Branch

on:
workflow_dispatch: # This line allows manual triggering

jobs:
build:
runs-on: ubuntu-latest

steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v2

# Set up Docker
- name: Set up Docker
uses: docker/setup-buildx-action@v1

# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Run docker-compose build
- name: Build Docker images
run: |
cp .env.example .env
docker-compose build
docker build -f Dockerfile.multi --target api-build -t librechat-api .
# Tag and push the images with the 'latest' tag
- name: Tag image and push
run: |
docker tag librechat:latest ghcr.io/${{ github.repository_owner }}/librechat:latest
docker push ghcr.io/${{ github.repository_owner }}/librechat:latest
docker tag librechat-api:latest ghcr.io/${{ github.repository_owner }}/librechat-api:latest
docker push ghcr.io/${{ github.repository_owner }}/librechat-api:latest
3 changes: 2 additions & 1 deletion .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
timeout-minutes: 60
runs-on: ubuntu-latest
env:
NODE_ENV: ci
NODE_ENV: CI
CI: true
SEARCH: false
BINGAI_TOKEN: user_provided
Expand All @@ -34,6 +34,7 @@ jobs:
DOMAIN_SERVER: ${{ secrets.DOMAIN_SERVER }}
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 # Skip downloading during npm install
PLAYWRIGHT_BROWSERS_PATH: 0 # Places binaries to node_modules/@playwright/test
TITLE_CONVO: false
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Keep up with the latest updates by visiting the releases page - [Releases](https
* [Third-Party Tools](docs/features/third_party.md)
* [Proxy](docs/features/proxy.md)
* [Bing Jailbreak](docs/features/bing_jailbreak.md)
* [Token Usage](docs/features/token_usage.md)
</details>

<details>
Expand All @@ -112,6 +113,7 @@ Keep up with the latest updates by visiting the releases page - [Releases](https
* [Ngrok](docs/deployment/ngrok.md)
* [HuggingFace](docs/deployment/huggingface.md)
* [Render](docs/deployment/render.md)
* [Meilisearch in Render](docs/deployment/meilisearch_in_render.md)
* [Hetzner](docs/deployment/hetzner_ubuntu.md)
* [Heroku](docs/deployment/heroku.md)
</details>
Expand All @@ -133,7 +135,9 @@ Keep up with the latest updates by visiting the releases page - [Releases](https

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=danny-avila/LibreChat&type=Date)](https://star-history.com/#danny-avila/LibreChat&Date)
<a href="https://star-history.com/#danny-avila/LibreChat&Date">
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=danny-avila/LibreChat&type=Date&theme=dark" onerror="this.src='https://api.star-history.com/svg?repos=danny-avila/LibreChat&type=Date'" />
</a>

---

Expand Down
4 changes: 2 additions & 2 deletions api/app/bingai.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const askBing = async ({
key = await getUserKey({ userId, name: 'bingAI' });
}

const { BingAIClient } = await import('@waylaidwanderer/chatgpt-api');
const { BingAIClient } = await import('nodejs-gpt');
const store = {
store: new KeyvFile({ filename: './data/cache.json' }),
};
Expand Down Expand Up @@ -94,7 +94,7 @@ const askBing = async ({
// don't give those parameters for new conversation
// for new conversation, conversationSignature always is null
if (conversationSignature) {
options.conversationSignature = conversationSignature;
options.encryptedConversationSignature = conversationSignature;
options.clientId = clientId;
options.invocationId = invocationId;
}
Expand Down
2 changes: 1 addition & 1 deletion api/app/chatgpt-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const browserClient = async ({
key = await getUserKey({ userId, name: 'chatGPTBrowser' });
}

const { ChatGPTBrowserClient } = await import('@waylaidwanderer/chatgpt-api');
const { ChatGPTBrowserClient } = await import('nodejs-gpt');
const store = {
store: new KeyvFile({ filename: './data/cache.json' }),
};
Expand Down
47 changes: 41 additions & 6 deletions api/app/clients/BaseClient.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const crypto = require('crypto');
const TextStream = require('./TextStream');
const { getConvo, getMessages, saveMessage, updateMessage, saveConvo } = require('../../models');
const { addSpaceIfNeeded } = require('../../server/utils');
const { addSpaceIfNeeded, isEnabled } = require('../../server/utils');
const checkBalance = require('../../models/checkBalance');

class BaseClient {
constructor(apiKey, options = {}) {
Expand Down Expand Up @@ -39,6 +40,18 @@ class BaseClient {
throw new Error('Subclasses attempted to call summarizeMessages without implementing it');
}

async getTokenCountForResponse(response) {
if (this.options.debug) {
console.debug('`recordTokenUsage` not implemented.', response);
}
}

async recordTokenUsage({ promptTokens, completionTokens }) {
if (this.options.debug) {
console.debug('`recordTokenUsage` not implemented.', { promptTokens, completionTokens });
}
}

getBuildMessagesOptions() {
throw new Error('Subclasses must implement getBuildMessagesOptions');
}
Expand All @@ -64,6 +77,7 @@ class BaseClient {
let responseMessageId = opts.responseMessageId ?? crypto.randomUUID();
let head = isEdited ? responseMessageId : parentMessageId;
this.currentMessages = (await this.loadHistory(conversationId, head)) ?? [];
this.conversationId = conversationId;

if (isEdited && !isContinued) {
responseMessageId = crypto.randomUUID();
Expand Down Expand Up @@ -114,8 +128,8 @@ class BaseClient {
text: message,
});

if (typeof opts?.getIds === 'function') {
opts.getIds({
if (typeof opts?.getReqData === 'function') {
opts.getReqData({
userMessage,
conversationId,
responseMessageId,
Expand Down Expand Up @@ -420,6 +434,21 @@ class BaseClient {
await this.saveMessageToDatabase(userMessage, saveOptions, user);
}

if (isEnabled(process.env.CHECK_BALANCE)) {
await checkBalance({
req: this.options.req,
res: this.options.res,
txData: {
user: this.user,
tokenType: 'prompt',
amount: promptTokens,
debug: this.options.debug,
model: this.modelOptions.model,
},
});
}

const completion = await this.sendCompletion(payload, opts);
const responseMessage = {
messageId: responseMessageId,
conversationId,
Expand All @@ -428,13 +457,19 @@ class BaseClient {
isEdited,
model: this.modelOptions.model,
sender: this.sender,
text: addSpaceIfNeeded(generation) + (await this.sendCompletion(payload, opts)),
text: addSpaceIfNeeded(generation) + completion,
promptTokens,
};

if (tokenCountMap && this.getTokenCountForResponse) {
if (
tokenCountMap &&
this.recordTokenUsage &&
this.getTokenCountForResponse &&
this.getTokenCount
) {
responseMessage.tokenCount = this.getTokenCountForResponse(responseMessage);
responseMessage.completionTokens = responseMessage.tokenCount;
const completionTokens = this.getTokenCount(completion);
await this.recordTokenUsage({ promptTokens, completionTokens });
}
await this.saveMessageToDatabase(responseMessage, saveOptions, user);
delete responseMessage.tokenCount;
Expand Down
Loading

0 comments on commit 3d4d159

Please sign in to comment.