Skip to content

Commit

Permalink
Merge pull request #1 from danny-avila/main
Browse files Browse the repository at this point in the history
Catchup with upstream
  • Loading branch information
ineiti authored Jan 11, 2024
2 parents 51050cc + 79c1783 commit a2636bf
Show file tree
Hide file tree
Showing 271 changed files with 8,105 additions and 3,588 deletions.
1 change: 0 additions & 1 deletion .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ services:
# ports:
# - 7700:7700 # if exposing these ports, make sure your master key is not the default value
environment:
- MEILI_HTTP_ADDR=meilisearch:7700
- MEILI_NO_ANALYTICS=true
- MEILI_MASTER_KEY=5c71cf56d672d009e36070b5bc5e47b743535ae55c818ae3b735bb6ebfb4ba63
volumes:
Expand Down
18 changes: 15 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
**/.circleci
**/.editorconfig
**/.dockerignore
**/.git
**/.DS_Store
**/.vscode
**/node_modules
client/dist/images

# Specific patterns to ignore
data-node
.env
**/.env
meili_data*
librechat*
Dockerfile*
docs

# Ignore all hidden files
.*
19 changes: 18 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ MONGO_URI=mongodb://127.0.0.1:27017/LibreChat
DOMAIN_CLIENT=http://localhost:3080
DOMAIN_SERVER=http://localhost:3080

NO_INDEX=true

#===============#
# Debug Logging #
#===============#

DEBUG_LOGGING=true
DEBUG_CONSOLE=false

Expand Down Expand Up @@ -174,7 +177,6 @@ ZAPIER_NLA_API_KEY=
SEARCH=true
MEILI_NO_ANALYTICS=true
MEILI_HOST=http://0.0.0.0:7700
MEILI_HTTP_ADDR=0.0.0.0:7700
MEILI_MASTER_KEY=DrhYf7zENyR6AlUCKmnz0eYASOQdl6zxH7s7MKFSfFCt

#===================================================#
Expand All @@ -185,6 +187,10 @@ MEILI_MASTER_KEY=DrhYf7zENyR6AlUCKmnz0eYASOQdl6zxH7s7MKFSfFCt
# Moderation #
#========================#

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

BAN_VIOLATIONS=true
BAN_DURATION=1000 * 60 * 60 * 2
BAN_INTERVAL=20
Expand Down Expand Up @@ -278,6 +284,17 @@ EMAIL_PASSWORD=
EMAIL_FROM_NAME=
EMAIL_FROM=[email protected]

#========================#
# Firebase CDN #
#========================#

FIREBASE_API_KEY=
FIREBASE_AUTH_DOMAIN=
FIREBASE_PROJECT_ID=
FIREBASE_STORAGE_BUCKET=
FIREBASE_MESSAGING_SENDER_ID=
FIREBASE_APP_ID=

#==================================================#
# Others #
#==================================================#
Expand Down
3 changes: 2 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Please delete any irrelevant options.
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Documentation update
- [ ] Documentation update
- [ ] Translation update

## Testing

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ jobs:
mkdocs-material-
- run: pip install mkdocs-material
- run: pip install mkdocs-nav-weight
- run: pip install mkdocs-publisher
- run: pip install mkdocs-exclude
- run: mkdocs gh-deploy --force
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ bower_components/
.floo
.flooignore

#config file
librechat.yaml

# Environment
.npmrc
.env*
Expand Down Expand Up @@ -82,4 +85,6 @@ data.ms/*
auth.json

/packages/ux-shared/
/images
/images

!client/src/components/Nav/SettingsTabs/Data/
10 changes: 4 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# Base node image
FROM node:19-alpine AS node
FROM node:18-alpine AS node

COPY . /app
WORKDIR /app

# Allow mounting of these files, which have no default
# values.
RUN touch .env
# Install call deps - Install curl for health check
RUN apk --no-cache add curl && \
# We want to inherit env from the container, not the file
# This will preserve any existing env file if it's already in source
# otherwise it will create a new one
touch .env && \
# Build deps in seperate
npm ci

# React client build
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 LibreChat
Copyright (c) 2024 LibreChat

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
3 changes: 2 additions & 1 deletion api/app/clients/BaseClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -516,10 +516,11 @@ class BaseClient {
}

async saveMessageToDatabase(message, endpointOptions, user = null) {
await saveMessage({ ...message, user, unfinished: false, cancelled: false });
await saveMessage({ ...message, endpoint: this.options.endpoint, user, unfinished: false });
await saveConvo(user, {
conversationId: message.conversationId,
endpoint: this.options.endpoint,
endpointType: this.options.endpointType,
...endpointOptions,
});
}
Expand Down
124 changes: 99 additions & 25 deletions api/app/clients/OpenAIClient.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const OpenAI = require('openai');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { getResponseSender, EModelEndpoint } = require('librechat-data-provider');
const { getResponseSender } = require('librechat-data-provider');
const { encoding_for_model: encodingForModel, get_encoding: getEncoding } = require('tiktoken');
const { encodeAndFormat, validateVisionModel } = require('~/server/services/Files/images');
const { getModelMaxTokens, genAzureChatCompletion, extractBaseURL } = require('~/utils');
Expand Down Expand Up @@ -94,10 +94,23 @@ class OpenAIClient extends BaseClient {
}

const { reverseProxyUrl: reverseProxy } = this.options;

if (
!this.useOpenRouter &&
reverseProxy &&
reverseProxy.includes('https://openrouter.ai/api/v1')
) {
this.useOpenRouter = true;
}

this.FORCE_PROMPT =
isEnabled(OPENAI_FORCE_PROMPT) ||
(reverseProxy && reverseProxy.includes('completions') && !reverseProxy.includes('chat'));

if (typeof this.options.forcePrompt === 'boolean') {
this.FORCE_PROMPT = this.options.forcePrompt;
}

if (this.azure && process.env.AZURE_OPENAI_DEFAULT_MODEL) {
this.azureEndpoint = genAzureChatCompletion(this.azure, this.modelOptions.model);
this.modelOptions.model = process.env.AZURE_OPENAI_DEFAULT_MODEL;
Expand Down Expand Up @@ -146,8 +159,10 @@ class OpenAIClient extends BaseClient {
this.options.sender ??
getResponseSender({
model: this.modelOptions.model,
endpoint: EModelEndpoint.openAI,
endpoint: this.options.endpoint,
endpointType: this.options.endpointType,
chatGptLabel: this.options.chatGptLabel,
modelDisplayLabel: this.options.modelDisplayLabel,
});

this.userLabel = this.options.userLabel || 'User';
Expand Down Expand Up @@ -434,7 +449,7 @@ class OpenAIClient extends BaseClient {
},
opts.abortController || new AbortController(),
);
} else if (typeof opts.onProgress === 'function') {
} else if (typeof opts.onProgress === 'function' || this.options.useChatCompletion) {
reply = await this.chatCompletion({
payload,
clientOptions: opts,
Expand Down Expand Up @@ -530,6 +545,19 @@ class OpenAIClient extends BaseClient {
return llm;
}

/**
* Generates a concise title for a conversation based on the user's input text and response.
* Uses either specified method or starts with the OpenAI `functions` method (using LangChain).
* If the `functions` method fails, it falls back to the `completion` method,
* which involves sending a chat completion request with specific instructions for title generation.
*
* @param {Object} params - The parameters for the conversation title generation.
* @param {string} params.text - The user's input.
* @param {string} [params.responseText=''] - The AI's immediate response to the user.
*
* @returns {Promise<string | 'New Chat'>} A promise that resolves to the generated conversation title.
* In case of failure, it will return the default title, "New Chat".
*/
async titleConvo({ text, responseText = '' }) {
let title = 'New Chat';
const convo = `||>User:
Expand All @@ -539,32 +567,25 @@ class OpenAIClient extends BaseClient {

const { OPENAI_TITLE_MODEL } = process.env ?? {};

const model = this.options.titleModel ?? OPENAI_TITLE_MODEL ?? 'gpt-3.5-turbo';

const modelOptions = {
model: OPENAI_TITLE_MODEL ?? 'gpt-3.5-turbo',
// TODO: remove the gpt fallback and make it specific to endpoint
model,
temperature: 0.2,
presence_penalty: 0,
frequency_penalty: 0,
max_tokens: 16,
};

try {
this.abortController = new AbortController();
const llm = this.initializeLLM({ ...modelOptions, context: 'title', tokenBuffer: 150 });
title = await runTitleChain({ llm, text, convo, signal: this.abortController.signal });
} catch (e) {
if (e?.message?.toLowerCase()?.includes('abort')) {
logger.debug('[OpenAIClient] Aborted title generation');
return;
}
logger.error(
'[OpenAIClient] There was an issue generating title with LangChain, trying the old method...',
e,
);
modelOptions.model = OPENAI_TITLE_MODEL ?? 'gpt-3.5-turbo';
const titleChatCompletion = async () => {
modelOptions.model = model;

if (this.azure) {
modelOptions.model = process.env.AZURE_OPENAI_DEFAULT_MODEL ?? modelOptions.model;
this.azureEndpoint = genAzureChatCompletion(this.azure, modelOptions.model);
}

const instructionsPayload = [
{
role: 'system',
Expand All @@ -578,10 +599,38 @@ ${convo}
];

try {
title = (await this.sendPayload(instructionsPayload, { modelOptions })).replaceAll('"', '');
title = (
await this.sendPayload(instructionsPayload, { modelOptions, useChatCompletion: true })
).replaceAll('"', '');
} catch (e) {
logger.error('[OpenAIClient] There was another issue generating the title', e);
logger.error(
'[OpenAIClient] There was an issue generating the title with the completion method',
e,
);
}
};

if (this.options.titleMethod === 'completion') {
await titleChatCompletion();
logger.debug('[OpenAIClient] Convo Title: ' + title);
return title;
}

try {
this.abortController = new AbortController();
const llm = this.initializeLLM({ ...modelOptions, context: 'title', tokenBuffer: 150 });
title = await runTitleChain({ llm, text, convo, signal: this.abortController.signal });
} catch (e) {
if (e?.message?.toLowerCase()?.includes('abort')) {
logger.debug('[OpenAIClient] Aborted title generation');
return;
}
logger.error(
'[OpenAIClient] There was an issue generating title with LangChain, trying completion method...',
e,
);

await titleChatCompletion();
}

logger.debug('[OpenAIClient] Convo Title: ' + title);
Expand All @@ -593,8 +642,11 @@ ${convo}
let context = messagesToRefine;
let prompt;

// TODO: remove the gpt fallback and make it specific to endpoint
const { OPENAI_SUMMARY_MODEL = 'gpt-3.5-turbo' } = process.env ?? {};
const maxContextTokens = getModelMaxTokens(OPENAI_SUMMARY_MODEL) ?? 4095;
const model = this.options.summaryModel ?? OPENAI_SUMMARY_MODEL;
const maxContextTokens = getModelMaxTokens(model) ?? 4095;

// 3 tokens for the assistant label, and 98 for the summarizer prompt (101)
let promptBuffer = 101;

Expand Down Expand Up @@ -644,7 +696,7 @@ ${convo}
logger.debug('[OpenAIClient] initialPromptTokens', initialPromptTokens);

const llm = this.initializeLLM({
model: OPENAI_SUMMARY_MODEL,
model,
temperature: 0.2,
context: 'summary',
tokenBuffer: initialPromptTokens,
Expand Down Expand Up @@ -719,7 +771,9 @@ ${convo}
if (!abortController) {
abortController = new AbortController();
}
const modelOptions = { ...this.modelOptions };

let modelOptions = { ...this.modelOptions };

if (typeof onProgress === 'function') {
modelOptions.stream = true;
}
Expand Down Expand Up @@ -779,6 +833,27 @@ ${convo}
...opts,
});

/* hacky fix for Mistral AI API not allowing a singular system message in payload */
if (opts.baseURL.includes('https://api.mistral.ai/v1') && modelOptions.messages) {
const { messages } = modelOptions;
if (messages.length === 1 && messages[0].role === 'system') {
modelOptions.messages[0].role = 'user';
}
}

if (this.options.addParams && typeof this.options.addParams === 'object') {
modelOptions = {
...modelOptions,
...this.options.addParams,
};
}

if (this.options.dropParams && Array.isArray(this.options.dropParams)) {
this.options.dropParams.forEach((param) => {
delete modelOptions[param];
});
}

let UnexpectedRoleError = false;
if (modelOptions.stream) {
const stream = await openai.beta.chat.completions
Expand Down Expand Up @@ -847,7 +922,7 @@ ${convo}
err?.message?.includes('abort') ||
(err instanceof OpenAI.APIError && err?.message?.includes('abort'))
) {
return '';
return intermediateReply;
}
if (
err?.message?.includes(
Expand All @@ -859,7 +934,6 @@ ${convo}
(err instanceof OpenAI.OpenAIError && err?.message?.includes('missing finish_reason'))
) {
logger.error('[OpenAIClient] Known OpenAI error:', err);
await abortController.abortCompletion();
return intermediateReply;
} else if (err instanceof OpenAI.APIError) {
if (intermediateReply) {
Expand Down
Loading

0 comments on commit a2636bf

Please sign in to comment.