mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
* WIP: initial logging changes add several transports in ~/config/winston omit messages in logs, truncate long strings add short blurb in dotenv for debug logging GoogleClient: using logger OpenAIClient: using logger, handleOpenAIErrors Adding typedef for payload message bumped winston and using winston-daily-rotate-file moved config for server paths to ~/config dir Added `DEBUG_LOGGING=true` to .env.example * WIP: Refactor logging statements in code * WIP: Refactor logging statements and import configurations * WIP: Refactor logging statements and import configurations * refactor: broadcast Redis initialization message with `info` not `debug` * refactor: complete Refactor logging statements and import configurations * chore: delete unused tools * fix: circular dependencies due to accessing logger * refactor(handleText): handle booleans and write tests * refactor: redact sensitive values, better formatting * chore: improve log formatting, avoid passing strings to 2nd arg * fix(ci): fix jest tests due to logger changes * refactor(getAvailablePluginsController): cache plugins as they are static and avoids async addOpenAPISpecs call every time * chore: update docs * chore: update docs * chore: create separate meiliSync logger, clean up logs to avoid being unnecessarily verbose * chore: spread objects where they are commonly logged to allow string truncation * chore: improve error log formatting
220 lines
6.6 KiB
JavaScript
220 lines
6.6 KiB
JavaScript
const { ZeroShotAgentOutputParser } = require('langchain/agents');
|
|
const { logger } = require('~/config');
|
|
|
|
class CustomOutputParser extends ZeroShotAgentOutputParser {
|
|
constructor(fields) {
|
|
super(fields);
|
|
this.tools = fields.tools;
|
|
this.longestToolName = '';
|
|
for (const tool of this.tools) {
|
|
if (tool.name.length > this.longestToolName.length) {
|
|
this.longestToolName = tool.name;
|
|
}
|
|
}
|
|
this.finishToolNameRegex = /(?:the\s+)?final\s+answer:\s*/i;
|
|
this.actionValues =
|
|
/(?:Action(?: [1-9])?:) ([\s\S]*?)(?:\n(?:Action Input(?: [1-9])?:) ([\s\S]*?))?$/i;
|
|
this.actionInputRegex = /(?:Action Input(?: *\d*):) ?([\s\S]*?)$/i;
|
|
this.thoughtRegex = /(?:Thought(?: *\d*):) ?([\s\S]*?)$/i;
|
|
}
|
|
|
|
getValidTool(text) {
|
|
let result = false;
|
|
for (const tool of this.tools) {
|
|
const { name } = tool;
|
|
const toolIndex = text.indexOf(name);
|
|
if (toolIndex !== -1) {
|
|
result = name;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
checkIfValidTool(text) {
|
|
let isValidTool = false;
|
|
for (const tool of this.tools) {
|
|
const { name } = tool;
|
|
if (text === name) {
|
|
isValidTool = true;
|
|
break;
|
|
}
|
|
}
|
|
return isValidTool;
|
|
}
|
|
|
|
async parse(text) {
|
|
const finalMatch = text.match(this.finishToolNameRegex);
|
|
// if (text.includes(this.finishToolName)) {
|
|
// const parts = text.split(this.finishToolName);
|
|
// const output = parts[parts.length - 1].trim();
|
|
// return {
|
|
// returnValues: { output },
|
|
// log: text
|
|
// };
|
|
// }
|
|
|
|
if (finalMatch) {
|
|
const output = text.substring(finalMatch.index + finalMatch[0].length).trim();
|
|
return {
|
|
returnValues: { output },
|
|
log: text,
|
|
};
|
|
}
|
|
|
|
const match = this.actionValues.exec(text); // old v2
|
|
|
|
if (!match) {
|
|
logger.debug(
|
|
'\n\n<----------------------[CustomOutputParser] HIT NO MATCH PARSING ERROR---------------------->\n\n' +
|
|
match,
|
|
);
|
|
const thoughts = text.replace(/[tT]hought:/, '').split('\n');
|
|
// return {
|
|
// tool: 'self-reflection',
|
|
// toolInput: thoughts[0],
|
|
// log: thoughts.slice(1).join('\n')
|
|
// };
|
|
|
|
return {
|
|
returnValues: { output: thoughts[0] },
|
|
log: thoughts.slice(1).join('\n'),
|
|
};
|
|
}
|
|
|
|
let selectedTool = match?.[1].trim().toLowerCase();
|
|
|
|
if (match && selectedTool === 'n/a') {
|
|
logger.debug(
|
|
'\n\n<----------------------[CustomOutputParser] HIT N/A PARSING ERROR---------------------->\n\n' +
|
|
match,
|
|
);
|
|
return {
|
|
tool: 'self-reflection',
|
|
toolInput: match[2]?.trim().replace(/^"+|"+$/g, '') ?? '',
|
|
log: text,
|
|
};
|
|
}
|
|
|
|
let toolIsValid = this.checkIfValidTool(selectedTool);
|
|
if (match && !toolIsValid) {
|
|
logger.debug(
|
|
'\n\n<----------------[CustomOutputParser] Tool invalid: Re-assigning Selected Tool---------------->\n\n' +
|
|
match,
|
|
);
|
|
selectedTool = this.getValidTool(selectedTool);
|
|
}
|
|
|
|
if (match && !selectedTool) {
|
|
logger.debug(
|
|
'\n\n<----------------------[CustomOutputParser] HIT INVALID TOOL PARSING ERROR---------------------->\n\n' +
|
|
match,
|
|
);
|
|
selectedTool = 'self-reflection';
|
|
}
|
|
|
|
if (match && !match[2]) {
|
|
logger.debug(
|
|
'\n\n<----------------------[CustomOutputParser] HIT NO ACTION INPUT PARSING ERROR---------------------->\n\n' +
|
|
match,
|
|
);
|
|
|
|
// In case there is no action input, let's double-check if there is an action input in 'text' variable
|
|
const actionInputMatch = this.actionInputRegex.exec(text);
|
|
const thoughtMatch = this.thoughtRegex.exec(text);
|
|
if (actionInputMatch) {
|
|
return {
|
|
tool: selectedTool,
|
|
toolInput: actionInputMatch[1].trim(),
|
|
log: text,
|
|
};
|
|
}
|
|
|
|
if (thoughtMatch && !actionInputMatch) {
|
|
return {
|
|
tool: selectedTool,
|
|
toolInput: thoughtMatch[1].trim(),
|
|
log: text,
|
|
};
|
|
}
|
|
}
|
|
|
|
if (match && selectedTool.length > this.longestToolName.length) {
|
|
logger.debug(
|
|
'\n\n<----------------------[CustomOutputParser] HIT LONG PARSING ERROR---------------------->\n\n',
|
|
);
|
|
|
|
let action, input, thought;
|
|
let firstIndex = Infinity;
|
|
|
|
for (const tool of this.tools) {
|
|
const { name } = tool;
|
|
const toolIndex = text.indexOf(name);
|
|
if (toolIndex !== -1 && toolIndex < firstIndex) {
|
|
firstIndex = toolIndex;
|
|
action = name;
|
|
}
|
|
}
|
|
|
|
// In case there is no action input, let's double-check if there is an action input in 'text' variable
|
|
const actionInputMatch = this.actionInputRegex.exec(text);
|
|
if (action && actionInputMatch) {
|
|
logger.debug(
|
|
'\n\n<------[CustomOutputParser] Matched Action Input in Long Parsing Error------>\n\n' +
|
|
actionInputMatch,
|
|
);
|
|
return {
|
|
tool: action,
|
|
toolInput: actionInputMatch[1].trim().replaceAll('"', ''),
|
|
log: text,
|
|
};
|
|
}
|
|
|
|
if (action) {
|
|
const actionEndIndex = text.indexOf('Action:', firstIndex + action.length);
|
|
const inputText = text
|
|
.slice(firstIndex + action.length, actionEndIndex !== -1 ? actionEndIndex : undefined)
|
|
.trim();
|
|
const inputLines = inputText.split('\n');
|
|
input = inputLines[0];
|
|
if (inputLines.length > 1) {
|
|
thought = inputLines.slice(1).join('\n');
|
|
}
|
|
const returnValues = {
|
|
tool: action,
|
|
toolInput: input,
|
|
log: thought || inputText,
|
|
};
|
|
|
|
const inputMatch = this.actionValues.exec(returnValues.log); //new
|
|
if (inputMatch) {
|
|
logger.debug('[CustomOutputParser] inputMatch', inputMatch);
|
|
returnValues.toolInput = inputMatch[1].replaceAll('"', '').trim();
|
|
returnValues.log = returnValues.log.replace(this.actionValues, '');
|
|
}
|
|
|
|
return returnValues;
|
|
} else {
|
|
logger.debug('[CustomOutputParser] No valid tool mentioned.', this.tools, text);
|
|
return {
|
|
tool: 'self-reflection',
|
|
toolInput: 'Hypothetical actions: \n"' + text + '"\n',
|
|
log: 'Thought: I need to look at my hypothetical actions and try one',
|
|
};
|
|
}
|
|
|
|
// if (action && input) {
|
|
// logger.debug('Action:', action);
|
|
// logger.debug('Input:', input);
|
|
// }
|
|
}
|
|
|
|
return {
|
|
tool: selectedTool,
|
|
toolInput: match[2]?.trim()?.replace(/^"+|"+$/g, '') ?? '',
|
|
log: text,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = { CustomOutputParser };
|