mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 09:50:15 +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
|
|
@ -6,6 +6,7 @@ import {
|
|||
ContentTypes,
|
||||
EModelEndpoint,
|
||||
parseCompactConvo,
|
||||
replaceSpecialVars,
|
||||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import { useSetRecoilState, useResetRecoilState, useRecoilValue } from 'recoil';
|
||||
|
|
@ -26,6 +27,7 @@ import { getArtifactsMode } from '~/utils/artifacts';
|
|||
import { getEndpointField, logger } from '~/utils';
|
||||
import useUserKey from '~/hooks/Input/useUserKey';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuthContext } from '~/hooks';
|
||||
|
||||
const logChatRequest = (request: Record<string, unknown>) => {
|
||||
logger.log('=====================================\nAsk function called with:');
|
||||
|
|
@ -66,19 +68,19 @@ export default function useChatFunctions({
|
|||
setSubmission: SetterOrUpdater<TSubmission | null>;
|
||||
setLatestMessage?: SetterOrUpdater<TMessage | null>;
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const getSender = useGetSender();
|
||||
const { user } = useAuthContext();
|
||||
const queryClient = useQueryClient();
|
||||
const setFilesToDelete = useSetFilesToDelete();
|
||||
const getEphemeralAgent = useGetEphemeralAgent();
|
||||
const isTemporary = useRecoilValue(store.isTemporary);
|
||||
const codeArtifacts = useRecoilValue(store.codeArtifacts);
|
||||
const includeShadcnui = useRecoilValue(store.includeShadcnui);
|
||||
const customPromptMode = useRecoilValue(store.customPromptMode);
|
||||
const navigate = useNavigate();
|
||||
const resetLatestMultiMessage = useResetRecoilState(store.latestMessageFamily(index + 1));
|
||||
const setShowStopButton = useSetRecoilState(store.showStopButtonByIndex(index));
|
||||
const setFilesToDelete = useSetFilesToDelete();
|
||||
const getSender = useGetSender();
|
||||
const isTemporary = useRecoilValue(store.isTemporary);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const { getExpiry } = useUserKey(conversation?.endpoint ?? '');
|
||||
const customPromptMode = useRecoilValue(store.customPromptMode);
|
||||
const setShowStopButton = useSetRecoilState(store.showStopButtonByIndex(index));
|
||||
const resetLatestMultiMessage = useResetRecoilState(store.latestMessageFamily(index + 1));
|
||||
|
||||
const ask: TAskFunction = (
|
||||
{
|
||||
|
|
@ -128,6 +130,13 @@ export default function useChatFunctions({
|
|||
|
||||
let currentMessages: TMessage[] | null = overrideMessages ?? getMessages() ?? [];
|
||||
|
||||
if (conversation?.promptPrefix) {
|
||||
conversation.promptPrefix = replaceSpecialVars({
|
||||
text: conversation.promptPrefix,
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
// construct the query message
|
||||
// this is not a real messageId, it is used as placeholder before real messageId returned
|
||||
text = text.trim();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryKeys } from 'librechat-data-provider';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { useGetMessagesByConvoId } from 'librechat-data-provider/react-query';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import useChatFunctions from '~/hooks/Chat/useChatFunctions';
|
||||
import { useGetMessagesByConvoId } from '~/data-provider';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import useNewConvo from '~/hooks/useNewConvo';
|
||||
import store from '~/store';
|
||||
|
|
|
|||
|
|
@ -96,13 +96,14 @@ export default function useTextarea({
|
|||
return localize('com_endpoint_message_not_appendable');
|
||||
}
|
||||
|
||||
const sender = isAssistant || isAgent
|
||||
? getEntityName({ name: entityName, isAgent, localize })
|
||||
: getSender(conversation as TEndpointOption);
|
||||
const sender =
|
||||
isAssistant || isAgent
|
||||
? getEntityName({ name: entityName, isAgent, localize })
|
||||
: getSender(conversation as TEndpointOption);
|
||||
|
||||
return `${localize(
|
||||
'com_endpoint_message_new', { 0: sender ? sender : localize('com_endpoint_ai') },
|
||||
)}`;
|
||||
return `${localize('com_endpoint_message_new', {
|
||||
0: sender ? sender : localize('com_endpoint_ai'),
|
||||
})}`;
|
||||
};
|
||||
|
||||
const placeholder = getPlaceholderText();
|
||||
|
|
@ -237,7 +238,8 @@ export default function useTextarea({
|
|||
textAreaRef,
|
||||
handlePaste,
|
||||
handleKeyDown,
|
||||
handleCompositionStart,
|
||||
isNotAppendable,
|
||||
handleCompositionEnd,
|
||||
handleCompositionStart,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { v4 } from 'uuid';
|
||||
import { useCallback } from 'react';
|
||||
import { Constants } from 'librechat-data-provider';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Constants, replaceSpecialVars } from 'librechat-data-provider';
|
||||
import { useChatContext, useChatFormContext, useAddedChatContext } from '~/Providers';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import { replaceSpecialVars } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
const appendIndex = (index: number, value?: string) => {
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ import type { TGenTitleMutation } from '~/data-provider';
|
|||
import type { SetterOrUpdater, Resetter } from 'recoil';
|
||||
import type { ConversationCursorData } from '~/utils';
|
||||
import {
|
||||
logger,
|
||||
scrollToEnd,
|
||||
getAllContentText,
|
||||
addConvoToAllQueries,
|
||||
updateConvoInAllQueries,
|
||||
removeConvoFromAllQueries,
|
||||
findConversationInInfinite,
|
||||
getAllContentText,
|
||||
} from '~/utils';
|
||||
import useAttachmentHandler from '~/hooks/SSE/useAttachmentHandler';
|
||||
import useContentHandler from '~/hooks/SSE/useContentHandler';
|
||||
|
|
@ -487,10 +488,6 @@ export default function useEventHandlers({
|
|||
}
|
||||
|
||||
if (setConversation && isAddedRequest !== true) {
|
||||
if (location.pathname === '/c/new') {
|
||||
navigate(`/c/${conversation.conversationId}`, { replace: true });
|
||||
}
|
||||
|
||||
setConversation((prevState) => {
|
||||
const update = {
|
||||
...prevState,
|
||||
|
|
@ -508,6 +505,9 @@ export default function useEventHandlers({
|
|||
}
|
||||
return update;
|
||||
});
|
||||
if (location.pathname === '/c/new') {
|
||||
navigate(`/c/${conversation.conversationId}`, { replace: true });
|
||||
}
|
||||
}
|
||||
|
||||
setIsSubmitting(false);
|
||||
|
|
@ -536,6 +536,12 @@ export default function useEventHandlers({
|
|||
const conversationId =
|
||||
userMessage.conversationId ?? submission.conversation?.conversationId ?? '';
|
||||
|
||||
const setErrorMessages = (convoId: string, errorMessage: TMessage) => {
|
||||
const finalMessages: TMessage[] = [...messages, userMessage, errorMessage];
|
||||
setMessages(finalMessages);
|
||||
queryClient.setQueryData<TMessage[]>([QueryKeys.messages, convoId], finalMessages);
|
||||
};
|
||||
|
||||
const parseErrorResponse = (data: TResData | Partial<TMessage>) => {
|
||||
const metadata = data['responseMessage'] ?? data;
|
||||
const errorMessage: Partial<TMessage> = {
|
||||
|
|
@ -553,7 +559,7 @@ export default function useEventHandlers({
|
|||
};
|
||||
|
||||
if (!data) {
|
||||
const convoId = conversationId || v4();
|
||||
const convoId = conversationId || `_${v4()}`;
|
||||
const errorMetadata = parseErrorResponse({
|
||||
text: 'Error connecting to server, try refreshing the page.',
|
||||
...submission,
|
||||
|
|
@ -564,7 +570,7 @@ export default function useEventHandlers({
|
|||
getMessages,
|
||||
submission,
|
||||
});
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
setErrorMessages(convoId, errorResponse);
|
||||
if (newConversation) {
|
||||
newConversation({
|
||||
template: { conversationId: convoId },
|
||||
|
|
@ -577,9 +583,9 @@ export default function useEventHandlers({
|
|||
|
||||
const receivedConvoId = data.conversationId ?? '';
|
||||
if (!conversationId && !receivedConvoId) {
|
||||
const convoId = v4();
|
||||
const convoId = `_${v4()}`;
|
||||
const errorResponse = parseErrorResponse(data);
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
setErrorMessages(convoId, errorResponse);
|
||||
if (newConversation) {
|
||||
newConversation({
|
||||
template: { conversationId: convoId },
|
||||
|
|
@ -590,7 +596,7 @@ export default function useEventHandlers({
|
|||
return;
|
||||
} else if (!receivedConvoId) {
|
||||
const errorResponse = parseErrorResponse(data);
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
setErrorMessages(conversationId, errorResponse);
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -601,7 +607,7 @@ export default function useEventHandlers({
|
|||
parentMessageId: userMessage.messageId,
|
||||
});
|
||||
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
setErrorMessages(receivedConvoId, errorResponse);
|
||||
if (receivedConvoId && paramId === Constants.NEW_CONVO && newConversation) {
|
||||
newConversation({
|
||||
template: { conversationId: receivedConvoId },
|
||||
|
|
@ -612,7 +618,15 @@ export default function useEventHandlers({
|
|||
setIsSubmitting(false);
|
||||
return;
|
||||
},
|
||||
[setCompleted, setMessages, paramId, newConversation, setIsSubmitting, getMessages],
|
||||
[
|
||||
setCompleted,
|
||||
setMessages,
|
||||
paramId,
|
||||
newConversation,
|
||||
setIsSubmitting,
|
||||
getMessages,
|
||||
queryClient,
|
||||
],
|
||||
);
|
||||
|
||||
const abortConversation = useCallback(
|
||||
|
|
@ -649,9 +663,11 @@ export default function useEventHandlers({
|
|||
);
|
||||
return;
|
||||
} else if (!isAssistantsEndpoint(endpoint)) {
|
||||
const convoId = conversationId || `_${v4()}`;
|
||||
logger.log('conversation', 'Aborted conversation with minimal messages, ID: ' + convoId);
|
||||
if (newConversation) {
|
||||
newConversation({
|
||||
template: { conversationId: conversationId || v4() },
|
||||
template: { conversationId: convoId },
|
||||
preset: tPresetSchema.parse(submission.conversation),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,12 +151,28 @@ const useNewConvo = (index = 0) => {
|
|||
if (!(keepAddedConvos ?? false)) {
|
||||
clearAllConversations(true);
|
||||
}
|
||||
logger.log('conversation', 'Setting conversation from `useNewConvo`', conversation);
|
||||
setConversation(conversation);
|
||||
const isCancelled = conversation.conversationId?.startsWith('_');
|
||||
if (isCancelled) {
|
||||
logger.log(
|
||||
'conversation',
|
||||
'Cancelled conversation, setting to `new` in `useNewConvo`',
|
||||
conversation,
|
||||
);
|
||||
setConversation({
|
||||
...conversation,
|
||||
conversationId: 'new',
|
||||
});
|
||||
} else {
|
||||
logger.log('conversation', 'Setting conversation from `useNewConvo`', conversation);
|
||||
setConversation(conversation);
|
||||
}
|
||||
setSubmission({} as TSubmission);
|
||||
if (!(keepLatestMessage ?? false)) {
|
||||
clearAllLatestMessages();
|
||||
}
|
||||
if (isCancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const searchParamsString = searchParams?.toString();
|
||||
const getParams = () => (searchParamsString ? `?${searchParamsString}` : '');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue