mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 08:12:00 +02:00
🐛 fix: Prevent Node Server Crash Due to Unhandled ChatCompletionMessage Error (#1278)
* refactor(addTitle): avoid generating title when a request was aborted * chore: bump openai to latest * fix: catch OpenAIError Uncaught error as last resort * fix: handle final messages excludes role=assistant * Update OpenAIClient.js * chore: fix linting errors
This commit is contained in:
parent
076a9b9b9c
commit
f1bc711cd7
7 changed files with 95 additions and 30 deletions
|
@ -771,6 +771,7 @@ ${convo}
|
||||||
...opts,
|
...opts,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let UnexpectedRoleError = false;
|
||||||
if (modelOptions.stream) {
|
if (modelOptions.stream) {
|
||||||
const stream = await openai.beta.chat.completions
|
const stream = await openai.beta.chat.completions
|
||||||
.stream({
|
.stream({
|
||||||
|
@ -782,6 +783,12 @@ ${convo}
|
||||||
})
|
})
|
||||||
.on('error', (err) => {
|
.on('error', (err) => {
|
||||||
handleOpenAIErrors(err, errorCallback, 'stream');
|
handleOpenAIErrors(err, errorCallback, 'stream');
|
||||||
|
})
|
||||||
|
.on('finalMessage', (message) => {
|
||||||
|
if (message?.role !== 'assistant') {
|
||||||
|
stream.messages.push({ role: 'assistant', content: intermediateReply });
|
||||||
|
UnexpectedRoleError = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const chunk of stream) {
|
for await (const chunk of stream) {
|
||||||
|
@ -794,9 +801,11 @@ ${convo}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chatCompletion = await stream.finalChatCompletion().catch((err) => {
|
if (!UnexpectedRoleError) {
|
||||||
handleOpenAIErrors(err, errorCallback, 'finalChatCompletion');
|
chatCompletion = await stream.finalChatCompletion().catch((err) => {
|
||||||
});
|
handleOpenAIErrors(err, errorCallback, 'finalChatCompletion');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// regular completion
|
// regular completion
|
||||||
else {
|
else {
|
||||||
|
@ -809,7 +818,11 @@ ${convo}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chatCompletion && error) {
|
if (!chatCompletion && UnexpectedRoleError) {
|
||||||
|
throw new Error(
|
||||||
|
'OpenAIError: Invalid final message: OpenAI expects final message to include role=assistant',
|
||||||
|
);
|
||||||
|
} else if (!chatCompletion && error) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
} else if (!chatCompletion) {
|
} else if (!chatCompletion) {
|
||||||
throw new Error('Chat completion failed');
|
throw new Error('Chat completion failed');
|
||||||
|
@ -829,7 +842,9 @@ ${convo}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
err?.message?.includes('stream ended') ||
|
err?.message?.includes(
|
||||||
|
'OpenAIError: Invalid final message: OpenAI expects final message to include role=assistant',
|
||||||
|
) ||
|
||||||
err?.message?.includes('The server had an error processing your request') ||
|
err?.message?.includes('The server had an error processing your request') ||
|
||||||
err?.message?.includes('missing finish_reason') ||
|
err?.message?.includes('missing finish_reason') ||
|
||||||
(err instanceof OpenAI.OpenAIError && err?.message?.includes('missing finish_reason'))
|
(err instanceof OpenAI.OpenAIError && err?.message?.includes('missing finish_reason'))
|
||||||
|
|
|
@ -17,17 +17,40 @@ class AzureAISearch extends StructuredTool {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// Initialize properties using helper function
|
// Initialize properties using helper function
|
||||||
this.serviceEndpoint = this._initializeField(fields.AZURE_AI_SEARCH_SERVICE_ENDPOINT, 'AZURE_AI_SEARCH_SERVICE_ENDPOINT');
|
this.serviceEndpoint = this._initializeField(
|
||||||
this.indexName = this._initializeField(fields.AZURE_AI_SEARCH_INDEX_NAME, 'AZURE_AI_SEARCH_INDEX_NAME');
|
fields.AZURE_AI_SEARCH_SERVICE_ENDPOINT,
|
||||||
|
'AZURE_AI_SEARCH_SERVICE_ENDPOINT',
|
||||||
|
);
|
||||||
|
this.indexName = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_INDEX_NAME,
|
||||||
|
'AZURE_AI_SEARCH_INDEX_NAME',
|
||||||
|
);
|
||||||
this.apiKey = this._initializeField(fields.AZURE_AI_SEARCH_API_KEY, 'AZURE_AI_SEARCH_API_KEY');
|
this.apiKey = this._initializeField(fields.AZURE_AI_SEARCH_API_KEY, 'AZURE_AI_SEARCH_API_KEY');
|
||||||
this.apiVersion = this._initializeField(fields.AZURE_AI_SEARCH_API_VERSION, 'AZURE_AI_SEARCH_API_VERSION', AzureAISearch.DEFAULT_API_VERSION);
|
this.apiVersion = this._initializeField(
|
||||||
this.queryType = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE, 'AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE', AzureAISearch.DEFAULT_QUERY_TYPE);
|
fields.AZURE_AI_SEARCH_API_VERSION,
|
||||||
this.top = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_TOP, 'AZURE_AI_SEARCH_SEARCH_OPTION_TOP', AzureAISearch.DEFAULT_TOP);
|
'AZURE_AI_SEARCH_API_VERSION',
|
||||||
this.select = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_SELECT, 'AZURE_AI_SEARCH_SEARCH_OPTION_SELECT');
|
AzureAISearch.DEFAULT_API_VERSION,
|
||||||
|
);
|
||||||
|
this.queryType = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE,
|
||||||
|
'AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE',
|
||||||
|
AzureAISearch.DEFAULT_QUERY_TYPE,
|
||||||
|
);
|
||||||
|
this.top = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_SEARCH_OPTION_TOP,
|
||||||
|
'AZURE_AI_SEARCH_SEARCH_OPTION_TOP',
|
||||||
|
AzureAISearch.DEFAULT_TOP,
|
||||||
|
);
|
||||||
|
this.select = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_SEARCH_OPTION_SELECT,
|
||||||
|
'AZURE_AI_SEARCH_SEARCH_OPTION_SELECT',
|
||||||
|
);
|
||||||
|
|
||||||
// Check for required fields
|
// Check for required fields
|
||||||
if (!this.serviceEndpoint || !this.indexName || !this.apiKey) {
|
if (!this.serviceEndpoint || !this.indexName || !this.apiKey) {
|
||||||
throw new Error('Missing AZURE_AI_SEARCH_SERVICE_ENDPOINT, AZURE_AI_SEARCH_INDEX_NAME, or AZURE_AI_SEARCH_API_KEY environment variable.');
|
throw new Error(
|
||||||
|
'Missing AZURE_AI_SEARCH_SERVICE_ENDPOINT, AZURE_AI_SEARCH_INDEX_NAME, or AZURE_AI_SEARCH_API_KEY environment variable.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create SearchClient
|
// Create SearchClient
|
||||||
|
@ -35,7 +58,7 @@ class AzureAISearch extends StructuredTool {
|
||||||
this.serviceEndpoint,
|
this.serviceEndpoint,
|
||||||
this.indexName,
|
this.indexName,
|
||||||
new AzureKeyCredential(this.apiKey),
|
new AzureKeyCredential(this.apiKey),
|
||||||
{ apiVersion: this.apiVersion }
|
{ apiVersion: this.apiVersion },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Define schema
|
// Define schema
|
||||||
|
|
|
@ -17,17 +17,40 @@ class AzureAISearch extends StructuredTool {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// Initialize properties using helper function
|
// Initialize properties using helper function
|
||||||
this.serviceEndpoint = this._initializeField(fields.AZURE_AI_SEARCH_SERVICE_ENDPOINT, 'AZURE_AI_SEARCH_SERVICE_ENDPOINT');
|
this.serviceEndpoint = this._initializeField(
|
||||||
this.indexName = this._initializeField(fields.AZURE_AI_SEARCH_INDEX_NAME, 'AZURE_AI_SEARCH_INDEX_NAME');
|
fields.AZURE_AI_SEARCH_SERVICE_ENDPOINT,
|
||||||
|
'AZURE_AI_SEARCH_SERVICE_ENDPOINT',
|
||||||
|
);
|
||||||
|
this.indexName = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_INDEX_NAME,
|
||||||
|
'AZURE_AI_SEARCH_INDEX_NAME',
|
||||||
|
);
|
||||||
this.apiKey = this._initializeField(fields.AZURE_AI_SEARCH_API_KEY, 'AZURE_AI_SEARCH_API_KEY');
|
this.apiKey = this._initializeField(fields.AZURE_AI_SEARCH_API_KEY, 'AZURE_AI_SEARCH_API_KEY');
|
||||||
this.apiVersion = this._initializeField(fields.AZURE_AI_SEARCH_API_VERSION, 'AZURE_AI_SEARCH_API_VERSION', AzureAISearch.DEFAULT_API_VERSION);
|
this.apiVersion = this._initializeField(
|
||||||
this.queryType = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE, 'AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE', AzureAISearch.DEFAULT_QUERY_TYPE);
|
fields.AZURE_AI_SEARCH_API_VERSION,
|
||||||
this.top = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_TOP, 'AZURE_AI_SEARCH_SEARCH_OPTION_TOP', AzureAISearch.DEFAULT_TOP);
|
'AZURE_AI_SEARCH_API_VERSION',
|
||||||
this.select = this._initializeField(fields.AZURE_AI_SEARCH_SEARCH_OPTION_SELECT, 'AZURE_AI_SEARCH_SEARCH_OPTION_SELECT');
|
AzureAISearch.DEFAULT_API_VERSION,
|
||||||
|
);
|
||||||
|
this.queryType = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE,
|
||||||
|
'AZURE_AI_SEARCH_SEARCH_OPTION_QUERY_TYPE',
|
||||||
|
AzureAISearch.DEFAULT_QUERY_TYPE,
|
||||||
|
);
|
||||||
|
this.top = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_SEARCH_OPTION_TOP,
|
||||||
|
'AZURE_AI_SEARCH_SEARCH_OPTION_TOP',
|
||||||
|
AzureAISearch.DEFAULT_TOP,
|
||||||
|
);
|
||||||
|
this.select = this._initializeField(
|
||||||
|
fields.AZURE_AI_SEARCH_SEARCH_OPTION_SELECT,
|
||||||
|
'AZURE_AI_SEARCH_SEARCH_OPTION_SELECT',
|
||||||
|
);
|
||||||
|
|
||||||
// Check for required fields
|
// Check for required fields
|
||||||
if (!this.serviceEndpoint || !this.indexName || !this.apiKey) {
|
if (!this.serviceEndpoint || !this.indexName || !this.apiKey) {
|
||||||
throw new Error('Missing AZURE_AI_SEARCH_SERVICE_ENDPOINT, AZURE_AI_SEARCH_INDEX_NAME, or AZURE_AI_SEARCH_API_KEY environment variable.');
|
throw new Error(
|
||||||
|
'Missing AZURE_AI_SEARCH_SERVICE_ENDPOINT, AZURE_AI_SEARCH_INDEX_NAME, or AZURE_AI_SEARCH_API_KEY environment variable.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create SearchClient
|
// Create SearchClient
|
||||||
|
@ -35,7 +58,7 @@ class AzureAISearch extends StructuredTool {
|
||||||
this.serviceEndpoint,
|
this.serviceEndpoint,
|
||||||
this.indexName,
|
this.indexName,
|
||||||
new AzureKeyCredential(this.apiKey),
|
new AzureKeyCredential(this.apiKey),
|
||||||
{ apiVersion: this.apiVersion }
|
{ apiVersion: this.apiVersion },
|
||||||
);
|
);
|
||||||
|
|
||||||
// Define schema
|
// Define schema
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"nodejs-gpt": "^1.37.4",
|
"nodejs-gpt": "^1.37.4",
|
||||||
"nodemailer": "^6.9.4",
|
"nodemailer": "^6.9.4",
|
||||||
"openai": "^4.16.1",
|
"openai": "^4.20.1",
|
||||||
"openai-chat-tokens": "^0.2.8",
|
"openai-chat-tokens": "^0.2.8",
|
||||||
"openid-client": "^5.4.2",
|
"openid-client": "^5.4.2",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
|
|
|
@ -105,7 +105,7 @@ process.on('uncaughtException', (err) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err.message.includes('OpenAIError')) {
|
if (err.message.includes('OpenAIError') || err.message.includes('ChatCompletionMessage')) {
|
||||||
console.error(
|
console.error(
|
||||||
'\n\nAn Uncaught `OpenAIError` error may be due to your reverse-proxy setup or stream configuration, or a bug in the `openai` node package.',
|
'\n\nAn Uncaught `OpenAIError` error may be due to your reverse-proxy setup or stream configuration, or a bug in the `openai` node package.',
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const { isEnabled } = require('../../../utils');
|
const { saveConvo } = require('~/models');
|
||||||
const { saveConvo } = require('../../../../models');
|
const { isEnabled } = require('~/server/utils');
|
||||||
|
|
||||||
const addTitle = async (req, { text, response, client }) => {
|
const addTitle = async (req, { text, response, client }) => {
|
||||||
const { TITLE_CONVO = 'true' } = process.env ?? {};
|
const { TITLE_CONVO = 'true' } = process.env ?? {};
|
||||||
|
@ -7,6 +7,11 @@ const addTitle = async (req, { text, response, client }) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the request was aborted, don't generate the title.
|
||||||
|
if (client.abortController.signal.aborted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const title = await client.titleConvo({ text, responseText: response?.text });
|
const title = await client.titleConvo({ text, responseText: response?.text });
|
||||||
await saveConvo(req.user.id, {
|
await saveConvo(req.user.id, {
|
||||||
conversationId: response.conversationId,
|
conversationId: response.conversationId,
|
||||||
|
|
9
package-lock.json
generated
9
package-lock.json
generated
|
@ -7,7 +7,6 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "LibreChat",
|
"name": "LibreChat",
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"api",
|
"api",
|
||||||
|
@ -74,7 +73,7 @@
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"nodejs-gpt": "^1.37.4",
|
"nodejs-gpt": "^1.37.4",
|
||||||
"nodemailer": "^6.9.4",
|
"nodemailer": "^6.9.4",
|
||||||
"openai": "^4.16.1",
|
"openai": "^4.20.1",
|
||||||
"openai-chat-tokens": "^0.2.8",
|
"openai-chat-tokens": "^0.2.8",
|
||||||
"openid-client": "^5.4.2",
|
"openid-client": "^5.4.2",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
|
@ -18170,9 +18169,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/openai": {
|
"node_modules/openai": {
|
||||||
"version": "4.17.4",
|
"version": "4.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/openai/-/openai-4.17.4.tgz",
|
"resolved": "https://registry.npmjs.org/openai/-/openai-4.20.1.tgz",
|
||||||
"integrity": "sha512-ThRFkl6snLbcAKS58St7N3CaKuI5WdYUvIjPvf4s+8SdymgNtOfzmZcZXVcCefx04oKFnvZJvIcTh3eAFUUhAQ==",
|
"integrity": "sha512-Dd3q8EvINfganZFtg6V36HjrMaihqRgIcKiHua4Nq9aw/PxOP48dhbsk8x5klrxajt5Lpnc1KTOG5i1S6BKAJA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^18.11.18",
|
"@types/node": "^18.11.18",
|
||||||
"@types/node-fetch": "^2.6.4",
|
"@types/node-fetch": "^2.6.4",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue