mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🗓️ feat: Add Special Variables for Prompts & Agents, Prompt UI Improvements (#7123)
* wip: Add Instructions component for agent configuration
* ✨ feat: Implement DropdownPopup for variable insertion in instructions
* refactor: Enhance variable handling by exporting specialVariables and updating Markdown components
* feat: Add special variable support for current date and user in Instructions component
* refactor: Update handleAddVariable to include localized label
* feat: replace special variables in instructions presets
* chore: update parameter type for user in getListAgents function
* refactor: integrate dayjs for date handling and move replaceSpecialVars function to data-provider
* feat: enhance replaceSpecialVars to include day number in current date format
* feat: integrate replaceSpecialVars for processing agent instructions
* feat: add support for current date & time in replaceSpecialVars function
* feat: add iso_datetime support in replaceSpecialVars function
* fix: enforce text parameter to be a required field in replaceSpecialVars function
* feat: add ISO datetime support in translation file
* fix: disable eslint warning for autoFocus in TextareaAutosize component
* feat: add VariablesDropdown component and integrate it into CreatePromptForm and PromptEditor; update translation for special variables
* fix: CategorySelector and related localizations
* fix: add z-index class to LanguageSTTDropdown for proper stacking context
* fix: add max-height and overflow styles to OGDialogContent in VariableDialog and PreviewPrompt components
* fix: update variable detection logic to exclude special variables and improve regex matching
* fix: improve accessibility text for actions menu in ChatGroupItem component
* fix: adjust max-width and height styles for dialog components and improve markdown rendering for light vs. dark, height/widths, etc.
* fix: remove commented-out code for better readability in PromptVariableGfm component
* fix: handle undefined input parameter in setParams function call
* fix: update variable label types to use TSpecialVarLabel for consistency
* fix: remove outdated information from special variables description in translation file
* fix: enhance unused i18next keys detection for special variable keys
* fix: update color classes for consistency/a11y in category and prompt variable components
* fix: update PromptVariableGfm component and special variable styles for consistency
* fix: improve variable highlighting logic in VariableForm component
* fix: update background color classes for consistency in VariableForm component
* fix: add missing ref parameter to Dialog component in OriginalDialog
* refactor: move navigate call for new conversation to after setConversation update
* refactor: move message query hook to client workspace; fix: handle edge case for navigation from finalHandler creating race condition for response message DB save
* chore: bump librechat-data-provider to 0.7.793
* ci: add unit tests for replaceSpecialVars function
* fix: implement getToolkitKey function for image_gen_oai toolkit filtering/including
* ci: enhance dayjs mock for consistent date/time values in tests
* fix: MCP stdio server fail to start when passing env property
* fix: use optional chaining for clientRef dereferencing in AskController and EditController
feat: add context to saveMessage call in streamResponse utility
* fix: only save error messages if the userMessageId was initialized
* refactor: add isNotAppendable check to disable inputs in ChatForm and useTextarea
* feat: enhance error handling in useEventHandlers and update conversation state in useNewConvo
* refactor: prepend underscore to conversationId in newConversation template
* feat: log aborted conversations with minimal messages and use consistent conversationId generation
---------
Co-authored-by: Olivier Schiavo <olivier.schiavo@wengo.com>
Co-authored-by: aka012 <aka012@neowiz.com>
Co-authored-by: jiasheng <jiashengguo@outlook.com>
This commit is contained in:
parent
0e8041bcac
commit
55f5f2d11a
47 changed files with 707 additions and 195 deletions
|
|
@ -128,7 +128,7 @@ const AskController = async (req, res, next, initializeClient, addTitle) => {
|
|||
clientRef = new WeakRef(client);
|
||||
|
||||
getAbortData = () => {
|
||||
const currentClient = clientRef.deref();
|
||||
const currentClient = clientRef?.deref();
|
||||
const currentText =
|
||||
currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText();
|
||||
|
||||
|
|
@ -255,7 +255,7 @@ const AskController = async (req, res, next, initializeClient, addTitle) => {
|
|||
logger.error('[AskController] Error handling request', error);
|
||||
let partialText = '';
|
||||
try {
|
||||
const currentClient = clientRef.deref();
|
||||
const currentClient = clientRef?.deref();
|
||||
partialText =
|
||||
currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText();
|
||||
} catch (getTextError) {
|
||||
|
|
@ -268,6 +268,7 @@ const AskController = async (req, res, next, initializeClient, addTitle) => {
|
|||
conversationId: reqDataContext.conversationId,
|
||||
messageId: reqDataContext.responseMessageId,
|
||||
parentMessageId: overrideParentMessageId ?? reqDataContext.userMessageId ?? parentMessageId,
|
||||
userMessageId: reqDataContext.userMessageId,
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error('[AskController] Error in `handleAbortError` during catch block', err);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ const EditController = async (req, res, next, initializeClient) => {
|
|||
clientRef = new WeakRef(client);
|
||||
|
||||
getAbortData = () => {
|
||||
const currentClient = clientRef.deref();
|
||||
const currentClient = clientRef?.deref();
|
||||
const currentText =
|
||||
currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText();
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ const EditController = async (req, res, next, initializeClient) => {
|
|||
logger.error('[EditController] Error handling request', error);
|
||||
let partialText = '';
|
||||
try {
|
||||
const currentClient = clientRef.deref();
|
||||
const currentClient = clientRef?.deref();
|
||||
partialText =
|
||||
currentClient?.getStreamText != null ? currentClient.getStreamText() : getPartialText();
|
||||
} catch (getTextError) {
|
||||
|
|
@ -232,6 +232,7 @@ const EditController = async (req, res, next, initializeClient) => {
|
|||
conversationId,
|
||||
messageId: reqDataContext.responseMessageId,
|
||||
parentMessageId: overrideParentMessageId ?? userMessageId ?? parentMessageId,
|
||||
userMessageId,
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error('[EditController] Error in `handleAbortError` during catch block', err);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const { CacheKeys, AuthType } = require('librechat-data-provider');
|
||||
const { addOpenAPISpecs } = require('~/app/clients/tools/util/addOpenAPISpecs');
|
||||
const { getToolkitKey } = require('~/server/services/ToolService');
|
||||
const { getCustomConfig } = require('~/server/services/Config');
|
||||
const { availableTools } = require('~/app/clients/tools');
|
||||
const { getMCPManager } = require('~/config');
|
||||
|
|
@ -128,7 +129,7 @@ const getAvailableTools = async (req, res) => {
|
|||
(plugin) =>
|
||||
toolDefinitions[plugin.pluginKey] !== undefined ||
|
||||
(plugin.toolkit === true &&
|
||||
Object.keys(toolDefinitions).some((key) => key.startsWith(`${plugin.pluginKey}_`))),
|
||||
Object.keys(toolDefinitions).some((key) => getToolkitKey(key) === plugin.pluginKey)),
|
||||
);
|
||||
|
||||
await cache.set(CacheKeys.TOOLS, tools);
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
|||
sender,
|
||||
messageId: responseMessageId,
|
||||
parentMessageId: overrideParentMessageId ?? userMessageId ?? parentMessageId,
|
||||
userMessageId,
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.error('[api/server/controllers/agents/request] Error in `handleAbortError`', err);
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ const handleAbortError = async (res, req, error, data) => {
|
|||
} else {
|
||||
logger.error('[handleAbortError] AI response error; aborting request:', error);
|
||||
}
|
||||
const { sender, conversationId, messageId, parentMessageId, partialText } = data;
|
||||
const { sender, conversationId, messageId, parentMessageId, userMessageId, partialText } = data;
|
||||
|
||||
if (error.stack && error.stack.includes('google')) {
|
||||
logger.warn(
|
||||
|
|
@ -344,10 +344,10 @@ const handleAbortError = async (res, req, error, data) => {
|
|||
parentMessageId,
|
||||
text: errorText,
|
||||
user: req.user.id,
|
||||
shouldSaveMessage: true,
|
||||
spec: endpointOption?.spec,
|
||||
iconURL: endpointOption?.iconURL,
|
||||
modelLabel: endpointOption?.modelLabel,
|
||||
shouldSaveMessage: userMessageId != null,
|
||||
model: endpointOption?.modelOptions?.model || req.body?.model,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ async function createActionTool({
|
|||
/** @type {import('librechat-data-provider').ActionMetadataRuntime} */
|
||||
const metadata = action.metadata;
|
||||
const executor = requestBuilder.createExecutor();
|
||||
const preparedExecutor = executor.setParams(toolInput);
|
||||
const preparedExecutor = executor.setParams(toolInput ?? {});
|
||||
|
||||
if (metadata.auth && metadata.auth.type !== AuthTypeEnum.None) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const {
|
|||
EToolResources,
|
||||
getResponseSender,
|
||||
AgentCapabilities,
|
||||
replaceSpecialVars,
|
||||
providerEndpointMap,
|
||||
} = require('librechat-data-provider');
|
||||
const {
|
||||
|
|
@ -246,6 +247,13 @@ const initializeAgentOptions = async ({
|
|||
agent.model_parameters.model = agent.model;
|
||||
}
|
||||
|
||||
if (agent.instructions && agent.instructions !== '') {
|
||||
agent.instructions = replaceSpecialVars({
|
||||
text: agent.instructions,
|
||||
user: req.user,
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof agent.artifacts === 'string' && agent.artifacts !== '') {
|
||||
agent.additional_instructions = generateArtifactsPrompt({
|
||||
endpoint: agent.provider,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const {
|
|||
ErrorTypes,
|
||||
ContentTypes,
|
||||
imageGenTools,
|
||||
EToolResources,
|
||||
EModelEndpoint,
|
||||
actionDelimiter,
|
||||
ImageVisionTool,
|
||||
|
|
@ -36,6 +37,30 @@ const { redactMessage } = require('~/config/parsers');
|
|||
const { sleep } = require('~/server/utils');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* @param {string} toolName
|
||||
* @returns {string | undefined} toolKey
|
||||
*/
|
||||
function getToolkitKey(toolName) {
|
||||
/** @type {string|undefined} */
|
||||
let toolkitKey;
|
||||
for (const toolkit of toolkits) {
|
||||
if (toolName.startsWith(EToolResources.image_edit)) {
|
||||
const splitMatches = toolkit.pluginKey.split('_');
|
||||
const suffix = splitMatches[splitMatches.length - 1];
|
||||
if (toolName.endsWith(suffix)) {
|
||||
toolkitKey = toolkit.pluginKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toolName.startsWith(toolkit.pluginKey)) {
|
||||
toolkitKey = toolkit.pluginKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return toolkitKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and formats tools from the specified tool directory.
|
||||
*
|
||||
|
|
@ -108,7 +133,7 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] })
|
|||
tools.push(formattedTool);
|
||||
}
|
||||
|
||||
/** Basic Tools; schema: { input: string } */
|
||||
/** Basic Tools & Toolkits; schema: { input: string } */
|
||||
const basicToolInstances = [
|
||||
new Calculator(),
|
||||
...createOpenAIImageTools({ override: true }),
|
||||
|
|
@ -117,9 +142,7 @@ function loadAndFormatTools({ directory, adminFilter = [], adminIncluded = [] })
|
|||
for (const toolInstance of basicToolInstances) {
|
||||
const formattedTool = formatToOpenAIAssistantTool(toolInstance);
|
||||
let toolName = formattedTool[Tools.function].name;
|
||||
toolName = toolkits.some((toolkit) => toolName.startsWith(toolkit.pluginKey))
|
||||
? toolName.split('_')[0]
|
||||
: toolName;
|
||||
toolName = getToolkitKey(toolName) ?? toolName;
|
||||
if (filter.has(toolName) && included.size === 0) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -682,6 +705,7 @@ async function loadAgentTools({ req, res, agent, tool_resources, openAIApiKey })
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
getToolkitKey,
|
||||
loadAgentTools,
|
||||
loadAndFormatTools,
|
||||
processRequiredActions,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,13 @@ const sendError = async (req, res, options, callback) => {
|
|||
}
|
||||
|
||||
if (shouldSaveMessage) {
|
||||
await saveMessage(req, { ...errorMessage, user });
|
||||
await saveMessage(
|
||||
req,
|
||||
{ ...errorMessage, user },
|
||||
{
|
||||
context: 'api/server/utils/streamResponse.js - sendError',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!errorMessage.error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue