Skip to content

Commit

Permalink
Merge pull request #1 from danny-avila/main
Browse files Browse the repository at this point in the history
Updates from their main branch
  • Loading branch information
tmendenhall authored Feb 8, 2024
2 parents 81ff598 + ff05715 commit 63e8490
Show file tree
Hide file tree
Showing 141 changed files with 4,398 additions and 1,585 deletions.
5 changes: 5 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM node:18-bullseye

RUN useradd -m -s /bin/bash vscode
RUN mkdir -p /workspaces && chown -R vscode:vscode /workspaces
WORKDIR /workspaces
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
}
},
"postCreateCommand": "",
"features": { "ghcr.io/devcontainers/features/git:1": {} }
"features": { "ghcr.io/devcontainers/features/git:1": {} },
"remoteUser": "vscode"
}
8 changes: 5 additions & 3 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ version: "3.8"

services:
app:
image: node:19-bullseye
build:
context: ..
dockerfile: .devcontainer/Dockerfile
# restart: always
links:
- mongodb
Expand Down Expand Up @@ -30,8 +32,8 @@ services:
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)

# Uncomment the next line to use a non-root user for all processes - See https://aka.ms/vscode-remote/containers/non-root for details.
# user: vscode
# Use a non-root user for all processes - See https://aka.ms/vscode-remote/containers/non-root for details.
user: vscode

# Overrides default command so things don't shut down after the process ends.
command: /bin/sh -c "while sleep 1000; do :; done"
Expand Down
48 changes: 24 additions & 24 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#=============================================================#
# LibreChat Configuration #
#=============================================================#
# Please refer to the reference documentation for assistance #
# with configuring your LibreChat environment. The guide is #
# available both online and within your local LibreChat #
# directory: #
# Online: https://docs.librechat.ai/install/dotenv.html #
# Locally: ./docs/install/dotenv.md #
#=============================================================#
#=====================================================================#
# LibreChat Configuration #
#=====================================================================#
# Please refer to the reference documentation for assistance #
# with configuring your LibreChat environment. The guide is #
# available both online and within your local LibreChat #
# directory: #
# Online: https://docs.librechat.ai/install/configuration/dotenv.html #
# Locally: ./docs/install/configuration/dotenv.md #
#=====================================================================#

#==================================================#
# Server Configuration #
Expand Down Expand Up @@ -86,7 +86,7 @@ BINGAI_TOKEN=user_provided

CHATGPT_TOKEN=
CHATGPT_MODELS=text-davinci-002-render-sha
# CHATGPT_REVERSE_PROXY=<YOUR REVERSE PROXY>
# CHATGPT_REVERSE_PROXY=

#============#
# Google #
Expand All @@ -101,7 +101,7 @@ GOOGLE_KEY=user_provided
#============#

OPENAI_API_KEY=user_provided
# OPENAI_MODELS=gpt-3.5-turbo-1106,gpt-4-1106-preview,gpt-3.5-turbo,gpt-3.5-turbo-16k,gpt-3.5-turbo-0301,gpt-4,gpt-4-0314,gpt-4-0613
# OPENAI_MODELS=gpt-3.5-turbo-0125,gpt-3.5-turbo-0301,gpt-3.5-turbo,gpt-4,gpt-4-0613,gpt-4-vision-preview,gpt-3.5-turbo-0613,gpt-3.5-turbo-16k-0613,gpt-4-0125-preview,gpt-4-turbo-preview,gpt-4-1106-preview,gpt-3.5-turbo-1106,gpt-3.5-turbo-instruct,gpt-3.5-turbo-instruct-0914,gpt-3.5-turbo-16k

DEBUG_OPENAI=false

Expand All @@ -127,7 +127,7 @@ DEBUG_OPENAI=false
# Plugins #
#============#

# PLUGIN_MODELS=gpt-3.5-turbo,gpt-3.5-turbo-16k,gpt-3.5-turbo-0301,gpt-4,gpt-4-0314,gpt-4-0613
# PLUGIN_MODELS=gpt-4,gpt-4-turbo-preview,gpt-4-0125-preview,gpt-4-1106-preview,gpt-4-0613,gpt-3.5-turbo,gpt-3.5-turbo-0125,gpt-3.5-turbo-1106,gpt-3.5-turbo-0613

DEBUG_PLUGINS=true

Expand All @@ -147,20 +147,20 @@ AZURE_AI_SEARCH_SEARCH_OPTION_SELECT=

# DALL·E
#----------------
# DALLE_API_KEY= # Key for both DALL-E-2 and DALL-E-3
# DALLE3_API_KEY= # Key for DALL-E-3 only
# DALLE2_API_KEY= # Key for DALL-E-2 only
# DALLE3_SYSTEM_PROMPT="Your DALL-E-3 System Prompt here"
# DALLE2_SYSTEM_PROMPT="Your DALL-E-2 System Prompt here"
# DALLE_REVERSE_PROXY= # Reverse proxy for DALL-E-2 and DALL-E-3
# DALLE3_BASEURL= # Base URL for DALL-E-3
# DALLE2_BASEURL= # Base URL for DALL-E-2
# DALLE_API_KEY=
# DALLE3_API_KEY=
# DALLE2_API_KEY=
# DALLE3_SYSTEM_PROMPT=
# DALLE2_SYSTEM_PROMPT=
# DALLE_REVERSE_PROXY=
# DALLE3_BASEURL=
# DALLE2_BASEURL=

# DALL·E (via Azure OpenAI)
# Note: requires some of the variables above to be set
#----------------
# DALLE3_AZURE_API_VERSION= # Azure OpenAI API version for DALL-E-3
# DALLE2_AZURE_API_VERSION= # Azure OpenAI API versiion for DALL-E-2
# DALLE3_AZURE_API_VERSION=
# DALLE2_AZURE_API_VERSION=

# Google
#-----------------
Expand Down Expand Up @@ -202,7 +202,7 @@ MEILI_MASTER_KEY=DrhYf7zENyR6AlUCKmnz0eYASOQdl6zxH7s7MKFSfFCt

OPENAI_MODERATION=false
OPENAI_MODERATION_API_KEY=
# OPENAI_MODERATION_REVERSE_PROXY=not working with some reverse proxys
# OPENAI_MODERATION_REVERSE_PROXY=

BAN_VIOLATIONS=true
BAN_DURATION=1000 * 60 * 60 * 2
Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@
</p>

<p align="center">
<a href="https://railway.app/template/b5k2mn?referralCode=HI9hWz">
<img src="https://railway.app/button.svg" alt="Deploy on Railway">
</a>
</p>

<p align="center">
<a href="https://template.cloud.sealos.io/deploy?templateName=librechat">
<img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos">
</a>
<a href="https://railway.app/template/b5k2mn?referralCode=HI9hWz">
<img src="https://railway.app/button.svg" alt="Deploy on Railway" height="30">
</a>
<a href="https://zeabur.com/templates/0X2ZY8">
<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30"/>
</a>
<a href="https://template.cloud.sealos.io/deploy?templateName=librechat">
<img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos" height="30">
</a>
</p>

# 📃 Features
Expand Down
6 changes: 5 additions & 1 deletion api/app/clients/BaseClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,10 @@ class BaseClient {
await this.saveMessageToDatabase(userMessage, saveOptions, user);
}

if (isEnabled(process.env.CHECK_BALANCE) && supportsBalanceCheck[this.options.endpoint]) {
if (
isEnabled(process.env.CHECK_BALANCE) &&
supportsBalanceCheck[this.options.endpointType ?? this.options.endpoint]
) {
await checkBalance({
req: this.options.req,
res: this.options.res,
Expand All @@ -438,6 +441,7 @@ class BaseClient {
amount: promptTokens,
model: this.modelOptions.model,
endpoint: this.options.endpoint,
endpointTokenConfig: this.options.endpointTokenConfig,
},
});
}
Expand Down
31 changes: 28 additions & 3 deletions api/app/clients/OpenAIClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ class OpenAIClient extends BaseClient {
const { isChatGptModel } = this;
this.isUnofficialChatGptModel =
model.startsWith('text-chat') || model.startsWith('text-davinci-002-render');
this.maxContextTokens = getModelMaxTokens(model) ?? 4095; // 1 less than maximum

this.maxContextTokens =
getModelMaxTokens(
model,
this.options.endpointType ?? this.options.endpoint,
this.options.endpointTokenConfig,
) ?? 4095; // 1 less than maximum

if (this.shouldSummarize) {
this.maxContextTokens = Math.floor(this.maxContextTokens / 2);
Expand Down Expand Up @@ -779,7 +785,12 @@ ${convo}
// TODO: remove the gpt fallback and make it specific to endpoint
const { OPENAI_SUMMARY_MODEL = 'gpt-3.5-turbo' } = process.env ?? {};
const model = this.options.summaryModel ?? OPENAI_SUMMARY_MODEL;
const maxContextTokens = getModelMaxTokens(model) ?? 4095;
const maxContextTokens =
getModelMaxTokens(
model,
this.options.endpointType ?? this.options.endpoint,
this.options.endpointTokenConfig,
) ?? 4095; // 1 less than maximum

// 3 tokens for the assistant label, and 98 for the summarizer prompt (101)
let promptBuffer = 101;
Expand Down Expand Up @@ -885,6 +896,7 @@ ${convo}
model: this.modelOptions.model,
context: 'message',
conversationId: this.conversationId,
endpointTokenConfig: this.options.endpointTokenConfig,
},
{ promptTokens, completionTokens },
);
Expand Down Expand Up @@ -975,9 +987,22 @@ ${convo}
...opts,
});

/* hacky fix for Mistral AI API not allowing a singular system message in payload */
/* hacky fixes for Mistral AI API:
- Re-orders system message to the top of the messages payload, as not allowed anywhere else
- If there is only one message and it's a system message, change the role to user
*/
if (opts.baseURL.includes('https://api.mistral.ai/v1') && modelOptions.messages) {
const { messages } = modelOptions;

const systemMessageIndex = messages.findIndex((msg) => msg.role === 'system');

if (systemMessageIndex > 0) {
const [systemMessage] = messages.splice(systemMessageIndex, 1);
messages.unshift(systemMessage);
}

modelOptions.messages = messages;

if (messages.length === 1 && messages[0].role === 'system') {
modelOptions.messages[0].role = 'user';
}
Expand Down
1 change: 1 addition & 0 deletions api/app/clients/tools/util/handleTools.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe('Tool Handlers', () => {
username: 'fakeuser',
email: '[email protected]',
emailVerified: false,
// file deepcode ignore NoHardcodedPasswords/test: fake value
password: 'fakepassword123',
avatar: '',
provider: 'local',
Expand Down
15 changes: 15 additions & 0 deletions api/cache/getLogStores.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ const config = isEnabled(USE_REDIS)
? new Keyv({ store: keyvRedis })
: new Keyv({ namespace: CacheKeys.CONFIG_STORE });

const tokenConfig = isEnabled(USE_REDIS) // ttl: 30 minutes
? new Keyv({ store: keyvRedis, ttl: 1800000 })
: new Keyv({ namespace: CacheKeys.TOKEN_CONFIG, ttl: 1800000 });

const genTitle = isEnabled(USE_REDIS) // ttl: 2 minutes
? new Keyv({ store: keyvRedis, ttl: 120000 })
: new Keyv({ namespace: CacheKeys.GEN_TITLE, ttl: 120000 });

const modelQueries = isEnabled(process.env.USE_REDIS)
? new Keyv({ store: keyvRedis })
: new Keyv({ namespace: 'models' });

const namespaces = {
[CacheKeys.CONFIG_STORE]: config,
pending_req,
Expand All @@ -34,6 +46,9 @@ const namespaces = {
token_balance: createViolationInstance('token_balance'),
registrations: createViolationInstance('registrations'),
logins: createViolationInstance('logins'),
[CacheKeys.TOKEN_CONFIG]: tokenConfig,
[CacheKeys.GEN_TITLE]: genTitle,
[CacheKeys.MODEL_QUERIES]: modelQueries,
};

/**
Expand Down
4 changes: 3 additions & 1 deletion api/models/Balance.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ balanceSchema.statics.check = async function ({
valueKey,
tokenType,
amount,
endpointTokenConfig,
}) {
const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint });
const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint, endpointTokenConfig });
const tokenCost = amount * multiplier;
const { tokenCredits: balance } = (await this.findOne({ user }, 'tokenCredits').lean()) ?? {};

Expand All @@ -24,6 +25,7 @@ balanceSchema.statics.check = async function ({
amount,
balance,
multiplier,
endpointTokenConfig: !!endpointTokenConfig,
});

if (!balance) {
Expand Down
6 changes: 3 additions & 3 deletions api/models/Conversation.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ module.exports = {
return { message: 'Error saving conversation' };
}
},
getConvosByPage: async (user, pageNumber = 1, pageSize = 14) => {
getConvosByPage: async (user, pageNumber = 1, pageSize = 25) => {
try {
const totalConvos = (await Conversation.countDocuments({ user })) || 1;
const totalPages = Math.ceil(totalConvos / pageSize);
const convos = await Conversation.find({ user })
.sort({ createdAt: -1 })
.sort({ updatedAt: -1 })
.skip((pageNumber - 1) * pageSize)
.limit(pageSize)
.lean();
Expand All @@ -45,7 +45,7 @@ module.exports = {
return { message: 'Error getting conversations' };
}
},
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 14) => {
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 25) => {
try {
if (!convoIds || convoIds.length === 0) {
return { conversations: [], pages: 1, pageNumber, pageSize };
Expand Down
5 changes: 3 additions & 2 deletions api/models/Transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ transactionSchema.methods.calculateTokenValue = function () {
if (!this.valueKey || !this.tokenType) {
this.tokenValue = this.rawAmount;
}
const { valueKey, tokenType, model } = this;
const multiplier = getMultiplier({ valueKey, tokenType, model });
const { valueKey, tokenType, model, endpointTokenConfig } = this;
const multiplier = getMultiplier({ valueKey, tokenType, model, endpointTokenConfig });
this.rate = multiplier;
this.tokenValue = this.rawAmount * multiplier;
if (this.context && this.tokenType === 'completion' && this.context === 'incomplete') {
Expand All @@ -25,6 +25,7 @@ transactionSchema.statics.create = async function (transactionData) {
const Transaction = this;

const transaction = new Transaction(transactionData);
transaction.endpointTokenConfig = transactionData.endpointTokenConfig;
transaction.calculateTokenValue();

// Save the transaction
Expand Down
1 change: 1 addition & 0 deletions api/models/checkBalance.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const { logViolation } = require('../cache');
* @param {('prompt' | 'completion')} params.txData.tokenType - The type of token.
* @param {number} params.txData.amount - The amount of tokens.
* @param {string} params.txData.model - The model name or identifier.
* @param {string} [params.txData.endpointTokenConfig] - The token configuration for the endpoint.
* @returns {Promise<boolean>} Returns true if the user can spend the amount, otherwise denies the request.
* @throws {Error} Throws an error if there's an issue with the balance check.
*/
Expand Down
2 changes: 1 addition & 1 deletion api/models/schema/convoSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
});
}

convoSchema.index({ createdAt: 1 });
convoSchema.index({ createdAt: 1, updatedAt: 1 });

const Conversation = mongoose.models.Conversation || mongoose.model('Conversation', convoSchema);

Expand Down
1 change: 1 addition & 0 deletions api/models/spendTokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { logger } = require('~/config');
* @param {String} txData.conversationId - The ID of the conversation.
* @param {String} txData.model - The model name.
* @param {String} txData.context - The context in which the transaction is made.
* @param {String} [txData.endpointTokenConfig] - The current endpoint token config.
* @param {String} [txData.valueKey] - The value key (optional).
* @param {Object} tokenUsage - The number of tokens used.
* @param {Number} tokenUsage.promptTokens - The number of prompt tokens used.
Expand Down
14 changes: 13 additions & 1 deletion api/models/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const tokenValues = {
'16k': { prompt: 3, completion: 4 },
'gpt-3.5-turbo-1106': { prompt: 1, completion: 2 },
'gpt-4-1106': { prompt: 10, completion: 30 },
'gpt-3.5-turbo-0125': { prompt: 0.5, completion: 1.5 },
};

/**
Expand All @@ -29,12 +30,18 @@ const getValueKey = (model, endpoint) => {

if (modelName.includes('gpt-3.5-turbo-16k')) {
return '16k';
} else if (modelName.includes('gpt-3.5-turbo-0125')) {
return 'gpt-3.5-turbo-0125';
} else if (modelName.includes('gpt-3.5-turbo-1106')) {
return 'gpt-3.5-turbo-1106';
} else if (modelName.includes('gpt-3.5')) {
return '4k';
} else if (modelName.includes('gpt-4-1106')) {
return 'gpt-4-1106';
} else if (modelName.includes('gpt-4-0125')) {
return 'gpt-4-1106';
} else if (modelName.includes('gpt-4-turbo')) {
return 'gpt-4-1106';
} else if (modelName.includes('gpt-4-32k')) {
return '32k';
} else if (modelName.includes('gpt-4')) {
Expand All @@ -53,9 +60,14 @@ const getValueKey = (model, endpoint) => {
* @param {string} [params.tokenType] - The type of token (e.g., 'prompt' or 'completion').
* @param {string} [params.model] - The model name to derive the value key from if not provided.
* @param {string} [params.endpoint] - The endpoint name to derive the value key from if not provided.
* @param {EndpointTokenConfig} [params.endpointTokenConfig] - The token configuration for the endpoint.
* @returns {number} The multiplier for the given parameters, or a default value if not found.
*/
const getMultiplier = ({ valueKey, tokenType, model, endpoint }) => {
const getMultiplier = ({ valueKey, tokenType, model, endpoint, endpointTokenConfig }) => {
if (endpointTokenConfig) {
return endpointTokenConfig?.[model]?.[tokenType] ?? defaultRate;
}

if (valueKey && tokenType) {
return tokenValues[valueKey][tokenType] ?? defaultRate;
}
Expand Down
Loading

0 comments on commit 63e8490

Please sign in to comment.