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 { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { Tools, StepTypes, FileContext } = require('librechat-data-provider');
|
||||
const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider');
|
||||
const {
|
||||
EnvVar,
|
||||
Providers,
|
||||
|
|
@ -27,6 +27,13 @@ class ModelEndHandler {
|
|||
this.collectedUsage = collectedUsage;
|
||||
}
|
||||
|
||||
finalize(errorMessage) {
|
||||
if (!errorMessage) {
|
||||
return;
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} event
|
||||
* @param {ModelEndData | undefined} data
|
||||
|
|
@ -40,10 +47,25 @@ class ModelEndHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let errorMessage;
|
||||
try {
|
||||
const agentContext = graph.getAgentContext(metadata);
|
||||
const isGoogle = agentContext.provider === Providers.GOOGLE;
|
||||
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;
|
||||
let hasUnprocessedToolCalls = false;
|
||||
|
|
@ -62,7 +84,7 @@ class ModelEndHandler {
|
|||
|
||||
const usage = data?.output?.usage_metadata;
|
||||
if (!usage) {
|
||||
return;
|
||||
return this.finalize(errorMessage);
|
||||
}
|
||||
const modelName = metadata?.ls_model_name || agentContext.clientOptions?.model;
|
||||
if (modelName) {
|
||||
|
|
@ -71,10 +93,10 @@ class ModelEndHandler {
|
|||
|
||||
this.collectedUsage.push(usage);
|
||||
if (!streamingDisabled) {
|
||||
return;
|
||||
return this.finalize(errorMessage);
|
||||
}
|
||||
if (!data.output.content) {
|
||||
return;
|
||||
return this.finalize(errorMessage);
|
||||
}
|
||||
const stepKey = graph.getStepKey(metadata);
|
||||
const message_id = getMessageId(stepKey, graph) ?? '';
|
||||
|
|
@ -104,6 +126,7 @@ class ModelEndHandler {
|
|||
}
|
||||
} catch (error) {
|
||||
logger.error('Error handling model end event:', error);
|
||||
return this.finalize(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -764,12 +764,14 @@ class AgentClient extends BaseClient {
|
|||
let run;
|
||||
/** @type {Promise<(TAttachment | null)[] | undefined>} */
|
||||
let memoryPromise;
|
||||
const appConfig = this.options.req.config;
|
||||
const balanceConfig = getBalanceConfig(appConfig);
|
||||
const transactionsConfig = getTransactionsConfig(appConfig);
|
||||
try {
|
||||
if (!abortController) {
|
||||
abortController = new AbortController();
|
||||
}
|
||||
|
||||
const appConfig = this.options.req.config;
|
||||
/** @type {AppConfig['endpoints']['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) {
|
||||
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(
|
||||
'[api/server/controllers/agents/client.js #sendCompletion] Operation aborted',
|
||||
err,
|
||||
|
|
@ -938,6 +916,24 @@ class AgentClient extends BaseClient {
|
|||
[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.INVALID_ACTION]: `com_error_${ErrorTypes.INVALID_ACTION}`,
|
||||
[ErrorTypes.INVALID_REQUEST]: `com_error_${ErrorTypes.INVALID_REQUEST}`,
|
||||
[ErrorTypes.REFUSAL]: 'com_error_refusal',
|
||||
[ErrorTypes.MISSING_MODEL]: (json: TGenericError, localize: LocalizeFunction) => {
|
||||
const { info: endpoint } = json;
|
||||
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_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_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_source": "File",
|
||||
"com_file_unknown": "Unknown File",
|
||||
|
|
|
|||
|
|
@ -1455,6 +1455,10 @@ export enum ErrorTypes {
|
|||
* Generic Authentication failure
|
||||
*/
|
||||
AUTH_FAILED = 'auth_failed',
|
||||
/**
|
||||
* Model refused to respond (content policy violation)
|
||||
*/
|
||||
REFUSAL = 'refusal',
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue