mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🛡️ feat: Add Model Refusal Error Handling (Anthropic) (#10478)
* feat: Add error handling for model refusal and update translations * refactor: error handling in AgentClient to improve logging and cleanup process * refactor: Update error message for response refusal to improve clarity
This commit is contained in:
parent
3f62ce054f
commit
524fc5bae4
5 changed files with 54 additions and 29 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
const { nanoid } = require('nanoid');
|
const { nanoid } = require('nanoid');
|
||||||
const { sendEvent } = require('@librechat/api');
|
const { sendEvent } = require('@librechat/api');
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const { Tools, StepTypes, FileContext } = require('librechat-data-provider');
|
const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider');
|
||||||
const {
|
const {
|
||||||
EnvVar,
|
EnvVar,
|
||||||
Providers,
|
Providers,
|
||||||
|
|
@ -27,6 +27,13 @@ class ModelEndHandler {
|
||||||
this.collectedUsage = collectedUsage;
|
this.collectedUsage = collectedUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalize(errorMessage) {
|
||||||
|
if (!errorMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} event
|
* @param {string} event
|
||||||
* @param {ModelEndData | undefined} data
|
* @param {ModelEndData | undefined} data
|
||||||
|
|
@ -40,10 +47,25 @@ class ModelEndHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {string | undefined} */
|
||||||
|
let errorMessage;
|
||||||
try {
|
try {
|
||||||
const agentContext = graph.getAgentContext(metadata);
|
const agentContext = graph.getAgentContext(metadata);
|
||||||
const isGoogle = agentContext.provider === Providers.GOOGLE;
|
const isGoogle = agentContext.provider === Providers.GOOGLE;
|
||||||
const streamingDisabled = !!agentContext.clientOptions?.disableStreaming;
|
const streamingDisabled = !!agentContext.clientOptions?.disableStreaming;
|
||||||
|
if (data?.output?.additional_kwargs?.stop_reason === 'refusal') {
|
||||||
|
const info = { ...data.output.additional_kwargs };
|
||||||
|
errorMessage = JSON.stringify({
|
||||||
|
type: ErrorTypes.REFUSAL,
|
||||||
|
info,
|
||||||
|
});
|
||||||
|
logger.debug(`[ModelEndHandler] Model refused to respond`, {
|
||||||
|
...info,
|
||||||
|
userId: metadata.user_id,
|
||||||
|
messageId: metadata.run_id,
|
||||||
|
conversationId: metadata.thread_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const toolCalls = data?.output?.tool_calls;
|
const toolCalls = data?.output?.tool_calls;
|
||||||
let hasUnprocessedToolCalls = false;
|
let hasUnprocessedToolCalls = false;
|
||||||
|
|
@ -62,7 +84,7 @@ class ModelEndHandler {
|
||||||
|
|
||||||
const usage = data?.output?.usage_metadata;
|
const usage = data?.output?.usage_metadata;
|
||||||
if (!usage) {
|
if (!usage) {
|
||||||
return;
|
return this.finalize(errorMessage);
|
||||||
}
|
}
|
||||||
const modelName = metadata?.ls_model_name || agentContext.clientOptions?.model;
|
const modelName = metadata?.ls_model_name || agentContext.clientOptions?.model;
|
||||||
if (modelName) {
|
if (modelName) {
|
||||||
|
|
@ -71,10 +93,10 @@ class ModelEndHandler {
|
||||||
|
|
||||||
this.collectedUsage.push(usage);
|
this.collectedUsage.push(usage);
|
||||||
if (!streamingDisabled) {
|
if (!streamingDisabled) {
|
||||||
return;
|
return this.finalize(errorMessage);
|
||||||
}
|
}
|
||||||
if (!data.output.content) {
|
if (!data.output.content) {
|
||||||
return;
|
return this.finalize(errorMessage);
|
||||||
}
|
}
|
||||||
const stepKey = graph.getStepKey(metadata);
|
const stepKey = graph.getStepKey(metadata);
|
||||||
const message_id = getMessageId(stepKey, graph) ?? '';
|
const message_id = getMessageId(stepKey, graph) ?? '';
|
||||||
|
|
@ -104,6 +126,7 @@ class ModelEndHandler {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error handling model end event:', error);
|
logger.error('Error handling model end event:', error);
|
||||||
|
return this.finalize(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -764,12 +764,14 @@ class AgentClient extends BaseClient {
|
||||||
let run;
|
let run;
|
||||||
/** @type {Promise<(TAttachment | null)[] | undefined>} */
|
/** @type {Promise<(TAttachment | null)[] | undefined>} */
|
||||||
let memoryPromise;
|
let memoryPromise;
|
||||||
|
const appConfig = this.options.req.config;
|
||||||
|
const balanceConfig = getBalanceConfig(appConfig);
|
||||||
|
const transactionsConfig = getTransactionsConfig(appConfig);
|
||||||
try {
|
try {
|
||||||
if (!abortController) {
|
if (!abortController) {
|
||||||
abortController = new AbortController();
|
abortController = new AbortController();
|
||||||
}
|
}
|
||||||
|
|
||||||
const appConfig = this.options.req.config;
|
|
||||||
/** @type {AppConfig['endpoints']['agents']} */
|
/** @type {AppConfig['endpoints']['agents']} */
|
||||||
const agentsEConfig = appConfig.endpoints?.[EModelEndpoint.agents];
|
const agentsEConfig = appConfig.endpoints?.[EModelEndpoint.agents];
|
||||||
|
|
||||||
|
|
@ -899,31 +901,7 @@ class AgentClient extends BaseClient {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const attachments = await this.awaitMemoryWithTimeout(memoryPromise);
|
|
||||||
if (attachments && attachments.length > 0) {
|
|
||||||
this.artifactPromises.push(...attachments);
|
|
||||||
}
|
|
||||||
|
|
||||||
const balanceConfig = getBalanceConfig(appConfig);
|
|
||||||
const transactionsConfig = getTransactionsConfig(appConfig);
|
|
||||||
await this.recordCollectedUsage({
|
|
||||||
context: 'message',
|
|
||||||
balance: balanceConfig,
|
|
||||||
transactions: transactionsConfig,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(
|
|
||||||
'[api/server/controllers/agents/client.js #chatCompletion] Error recording collected usage',
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const attachments = await this.awaitMemoryWithTimeout(memoryPromise);
|
|
||||||
if (attachments && attachments.length > 0) {
|
|
||||||
this.artifactPromises.push(...attachments);
|
|
||||||
}
|
|
||||||
logger.error(
|
logger.error(
|
||||||
'[api/server/controllers/agents/client.js #sendCompletion] Operation aborted',
|
'[api/server/controllers/agents/client.js #sendCompletion] Operation aborted',
|
||||||
err,
|
err,
|
||||||
|
|
@ -938,6 +916,24 @@ class AgentClient extends BaseClient {
|
||||||
[ContentTypes.ERROR]: `An error occurred while processing the request${err?.message ? `: ${err.message}` : ''}`,
|
[ContentTypes.ERROR]: `An error occurred while processing the request${err?.message ? `: ${err.message}` : ''}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
const attachments = await this.awaitMemoryWithTimeout(memoryPromise);
|
||||||
|
if (attachments && attachments.length > 0) {
|
||||||
|
this.artifactPromises.push(...attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.recordCollectedUsage({
|
||||||
|
context: 'message',
|
||||||
|
balance: balanceConfig,
|
||||||
|
transactions: transactionsConfig,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(
|
||||||
|
'[api/server/controllers/agents/client.js #chatCompletion] Error in cleanup phase',
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ const errorMessages = {
|
||||||
[ErrorTypes.NO_BASE_URL]: 'com_error_no_base_url',
|
[ErrorTypes.NO_BASE_URL]: 'com_error_no_base_url',
|
||||||
[ErrorTypes.INVALID_ACTION]: `com_error_${ErrorTypes.INVALID_ACTION}`,
|
[ErrorTypes.INVALID_ACTION]: `com_error_${ErrorTypes.INVALID_ACTION}`,
|
||||||
[ErrorTypes.INVALID_REQUEST]: `com_error_${ErrorTypes.INVALID_REQUEST}`,
|
[ErrorTypes.INVALID_REQUEST]: `com_error_${ErrorTypes.INVALID_REQUEST}`,
|
||||||
|
[ErrorTypes.REFUSAL]: 'com_error_refusal',
|
||||||
[ErrorTypes.MISSING_MODEL]: (json: TGenericError, localize: LocalizeFunction) => {
|
[ErrorTypes.MISSING_MODEL]: (json: TGenericError, localize: LocalizeFunction) => {
|
||||||
const { info: endpoint } = json;
|
const { info: endpoint } = json;
|
||||||
const provider = (alternateName[endpoint ?? ''] as string | undefined) ?? endpoint ?? 'unknown';
|
const provider = (alternateName[endpoint ?? ''] as string | undefined) ?? endpoint ?? 'unknown';
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,7 @@
|
||||||
"com_error_moderation": "It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We're unable to proceed with this specific topic. If you have any other questions or topics you'd like to explore, please edit your message, or create a new conversation.",
|
"com_error_moderation": "It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We're unable to proceed with this specific topic. If you have any other questions or topics you'd like to explore, please edit your message, or create a new conversation.",
|
||||||
"com_error_no_base_url": "No base URL found. Please provide one and try again.",
|
"com_error_no_base_url": "No base URL found. Please provide one and try again.",
|
||||||
"com_error_no_user_key": "No key found. Please provide a key and try again.",
|
"com_error_no_user_key": "No key found. Please provide a key and try again.",
|
||||||
|
"com_error_refusal": "Response refused by safety filters. Rewrite your message and try again. If you encounter this frequently while using Claude Sonnet 4.5 or Opus 4.1, you can try Sonnet 4, which has different usage restrictions.",
|
||||||
"com_file_pages": "Pages: {{pages}}",
|
"com_file_pages": "Pages: {{pages}}",
|
||||||
"com_file_source": "File",
|
"com_file_source": "File",
|
||||||
"com_file_unknown": "Unknown File",
|
"com_file_unknown": "Unknown File",
|
||||||
|
|
|
||||||
|
|
@ -1455,6 +1455,10 @@ export enum ErrorTypes {
|
||||||
* Generic Authentication failure
|
* Generic Authentication failure
|
||||||
*/
|
*/
|
||||||
AUTH_FAILED = 'auth_failed',
|
AUTH_FAILED = 'auth_failed',
|
||||||
|
/**
|
||||||
|
* Model refused to respond (content policy violation)
|
||||||
|
*/
|
||||||
|
REFUSAL = 'refusal',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue