mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-11 21:18:51 +01:00
* 🌊 feat: Implement multi-conversation feature with added conversation context and payload adjustments
* refactor: Replace isSubmittingFamily with isSubmitting across message components for consistency
* feat: Add loadAddedAgent and processAddedConvo for multi-conversation agent execution
* refactor: Update ContentRender usage to conditionally render PlaceholderRow based on isLast and isSubmitting
* WIP: first pass, sibling index
* feat: Enhance multi-conversation support with agent tracking and display improvements
* refactor: Introduce isEphemeralAgentId utility and update related logic for agent handling
* refactor: Implement createDualMessageContent utility for sibling message display and enhance useStepHandler for added conversations
* refactor: duplicate tools for added agent if ephemeral and primary agent is also ephemeral
* chore: remove deprecated multimessage rendering
* refactor: enhance dual message content creation and agent handling for parallel rendering
* refactor: streamline message rendering and submission handling by removing unused state and optimizing conditional logic
* refactor: adjust content handling in parallel mode to utilize existing content for improved agent display
* refactor: update @librechat/agents dependency to version 3.0.53
* refactor: update @langchain/core and @librechat/agents dependencies to latest versions
* refactor: remove deprecated @langchain/core dependency from package.json
* chore: remove unused SearchToolConfig and GetSourcesParams types from web.ts
* refactor: remove unused message properties from Message component
* refactor: enhance parallel content handling with groupId support in ContentParts and useStepHandler
* refactor: implement parallel content styling in Message, MessageRender, and ContentRender components. use explicit model name
* refactor: improve agent ID handling in createDualMessageContent for dual message display
* refactor: simplify title generation in AddedConvo by removing unused sender and preset logic
* refactor: replace string interpolation with cn utility for className in HoverButtons component
* refactor: enhance agent ID handling by adding suffix management for parallel agents and updating related components
* refactor: enhance column ordering in ContentParts by sorting agents with suffix management
* refactor: update @librechat/agents dependency to version 3.0.55
* feat: implement parallel content rendering with metadata support
- Added `ParallelContentRenderer` and `ParallelColumns` components for rendering messages in parallel based on groupId and agentId.
- Introduced `contentMetadataMap` to store metadata for each content part, allowing efficient parallel content detection.
- Updated `Message` and `ContentRender` components to utilize the new metadata structure for rendering.
- Modified `useStepHandler` to manage content indices and metadata during message processing.
- Enhanced `IJobStore` interface and its implementations to support storing and retrieving content metadata.
- Updated data schemas to include `contentMetadataMap` for messages, enabling multi-agent and parallel execution scenarios.
* refactor: update @librechat/agents dependency to version 3.0.56
* refactor: remove unused EPHEMERAL_AGENT_ID constant and simplify agent ID check
* refactor: enhance multi-agent message processing and primary agent determination
* refactor: implement branch message functionality for parallel responses
* refactor: integrate added conversation retrieval into message editing and regeneration processes
* refactor: remove unused isCard and isMultiMessage props from MessageRender and ContentRender components
* refactor: update @librechat/agents dependency to version 3.0.60
* refactor: replace usage of EPHEMERAL_AGENT_ID constant with isEphemeralAgentId function for improved clarity and consistency
* refactor: standardize agent ID format in tests for consistency
* chore: move addedConvo property to the correct position in payload construction
* refactor: rename agent_id values in loadAgent tests for clarity
* chore: reorder props in ContentParts component for improved readability
* refactor: rename variable 'content' to 'result' for clarity in RedisJobStore tests
* refactor: streamline useMessageActions by removing duplicate handleFeedback assignment
* chore: revert placeholder rendering logic MessageRender and ContentRender components to original
* refactor: implement useContentMetadata hook for optimized content metadata handling
* refactor: remove contentMetadataMap and related logic from the codebase and revert back to agentId/groupId in content parts
- Eliminated contentMetadataMap from various components and services, simplifying the handling of message content.
- Updated functions to directly access agentId and groupId from content parts instead of relying on a separate metadata map.
- Adjusted related hooks and components to reflect the removal of contentMetadataMap, ensuring consistent handling of message content.
- Updated tests and documentation to align with the new structure of message content handling.
* refactor: remove logging from groupParallelContent function to clean up output
* refactor: remove model parameter from TBranchMessageRequest type for simplification
* refactor: enhance branch message creation by stripping metadata for standalone content
* chore: streamline branch message creation by simplifying content filtering and removing unnecessary metadata checks
* refactor: include attachments in branch message creation for improved content handling
* refactor: streamline agent content processing by consolidating primary agent identification and filtering logic
* refactor: simplify multi-agent message processing by creating a dedicated mapping method and enhancing content filtering
* refactor: remove unused parameter from loadEphemeralAgent function for cleaner code
* refactor: update groupId handling in metadata to only set when provided by the server
212 lines
6.1 KiB
TypeScript
212 lines
6.1 KiB
TypeScript
import { useRef, useEffect, useCallback } from 'react';
|
|
import { useForm } from 'react-hook-form';
|
|
import { useRecoilValue } from 'recoil';
|
|
import { TextareaAutosize, TooltipAnchor } from '@librechat/client';
|
|
import { useUpdateMessageMutation } from 'librechat-data-provider/react-query';
|
|
import type { TEditProps } from '~/common';
|
|
import { useMessagesOperations, useMessagesConversation } from '~/Providers';
|
|
import { useGetAddedConvo } from '~/hooks/Chat';
|
|
import { cn, removeFocusRings } from '~/utils';
|
|
import { useLocalize } from '~/hooks';
|
|
import Container from './Container';
|
|
import store from '~/store';
|
|
|
|
const EditMessage = ({
|
|
text,
|
|
message,
|
|
isSubmitting,
|
|
ask,
|
|
enterEdit,
|
|
siblingIdx,
|
|
setSiblingIdx,
|
|
}: TEditProps) => {
|
|
const saveButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
const submitButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
const { conversation } = useMessagesConversation();
|
|
const { getMessages, setMessages } = useMessagesOperations();
|
|
|
|
const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
|
|
|
|
const { conversationId, parentMessageId, messageId } = message;
|
|
const updateMessageMutation = useUpdateMessageMutation(conversationId ?? '');
|
|
const localize = useLocalize();
|
|
|
|
const chatDirection = useRecoilValue(store.chatDirection).toLowerCase();
|
|
const isRTL = chatDirection === 'rtl';
|
|
|
|
const getAddedConvo = useGetAddedConvo();
|
|
|
|
const { register, handleSubmit, setValue } = useForm({
|
|
defaultValues: {
|
|
text: text ?? '',
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
const textArea = textAreaRef.current;
|
|
if (textArea) {
|
|
const length = textArea.value.length;
|
|
textArea.focus();
|
|
textArea.setSelectionRange(length, length);
|
|
}
|
|
}, []);
|
|
|
|
const resubmitMessage = (data: { text: string }) => {
|
|
if (message.isCreatedByUser) {
|
|
ask(
|
|
{
|
|
text: data.text,
|
|
parentMessageId,
|
|
conversationId,
|
|
},
|
|
{
|
|
overrideFiles: message.files,
|
|
addedConvo: getAddedConvo() || undefined,
|
|
},
|
|
);
|
|
|
|
setSiblingIdx((siblingIdx ?? 0) - 1);
|
|
} else {
|
|
const messages = getMessages();
|
|
const parentMessage = messages?.find((msg) => msg.messageId === parentMessageId);
|
|
|
|
if (!parentMessage) {
|
|
return;
|
|
}
|
|
ask(
|
|
{ ...parentMessage },
|
|
{
|
|
editedText: data.text,
|
|
editedMessageId: messageId,
|
|
isRegenerate: true,
|
|
isEdited: true,
|
|
addedConvo: getAddedConvo() || undefined,
|
|
},
|
|
);
|
|
|
|
setSiblingIdx((siblingIdx ?? 0) - 1);
|
|
}
|
|
|
|
enterEdit(true);
|
|
};
|
|
|
|
const updateMessage = (data: { text: string }) => {
|
|
const messages = getMessages();
|
|
if (!messages) {
|
|
return;
|
|
}
|
|
updateMessageMutation.mutate({
|
|
conversationId: conversationId ?? '',
|
|
model: conversation?.model ?? 'gpt-3.5-turbo',
|
|
text: data.text,
|
|
messageId,
|
|
});
|
|
|
|
const isInMessages = messages.some((message) => message.messageId === messageId);
|
|
if (!isInMessages) {
|
|
message.text = data.text;
|
|
} else {
|
|
setMessages(
|
|
messages.map((msg) =>
|
|
msg.messageId === messageId
|
|
? {
|
|
...msg,
|
|
text: data.text,
|
|
}
|
|
: msg,
|
|
),
|
|
);
|
|
}
|
|
|
|
enterEdit(true);
|
|
};
|
|
|
|
const handleKeyDown = useCallback(
|
|
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
|
e.preventDefault();
|
|
submitButtonRef.current?.click();
|
|
}
|
|
if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
|
|
e.preventDefault();
|
|
saveButtonRef.current?.click();
|
|
}
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
enterEdit(true);
|
|
}
|
|
},
|
|
[enterEdit],
|
|
);
|
|
|
|
const { ref, ...registerProps } = register('text', {
|
|
required: true,
|
|
onChange: (e) => {
|
|
setValue('text', e.target.value, { shouldValidate: true });
|
|
},
|
|
});
|
|
|
|
return (
|
|
<Container message={message}>
|
|
<div className="bg-token-main-surface-primary relative mt-2 flex w-full flex-grow flex-col overflow-hidden rounded-2xl border border-border-medium text-text-primary [&:has(textarea:focus)]:border-border-heavy [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]">
|
|
<TextareaAutosize
|
|
{...registerProps}
|
|
ref={(e) => {
|
|
ref(e);
|
|
textAreaRef.current = e;
|
|
}}
|
|
onKeyDown={handleKeyDown}
|
|
data-testid="message-text-editor"
|
|
className={cn(
|
|
'markdown prose dark:prose-invert light whitespace-pre-wrap break-words pl-3 md:pl-4',
|
|
'm-0 w-full resize-none border-0 bg-transparent py-[10px]',
|
|
'placeholder-text-secondary focus:ring-0 focus-visible:ring-0 md:py-3.5',
|
|
isRTL ? 'text-right' : 'text-left',
|
|
'max-h-[65vh] pr-3 md:max-h-[75vh] md:pr-4',
|
|
removeFocusRings,
|
|
)}
|
|
aria-label={localize('com_ui_message_input')}
|
|
dir={isRTL ? 'rtl' : 'ltr'}
|
|
/>
|
|
</div>
|
|
<div className="mt-2 flex w-full justify-center text-center">
|
|
<TooltipAnchor
|
|
description="Ctrl + Enter / ⌘ + Enter"
|
|
render={
|
|
<button
|
|
ref={submitButtonRef}
|
|
className="btn btn-primary relative mr-2"
|
|
disabled={isSubmitting}
|
|
onClick={handleSubmit(resubmitMessage)}
|
|
>
|
|
{localize('com_ui_save_submit')}
|
|
</button>
|
|
}
|
|
/>
|
|
<TooltipAnchor
|
|
description="Shift + Enter"
|
|
render={
|
|
<button
|
|
ref={saveButtonRef}
|
|
className="btn btn-secondary relative mr-2"
|
|
disabled={isSubmitting}
|
|
onClick={handleSubmit(updateMessage)}
|
|
>
|
|
{localize('com_ui_save')}
|
|
</button>
|
|
}
|
|
/>
|
|
<TooltipAnchor
|
|
description="Esc"
|
|
render={
|
|
<button className="btn btn-neutral relative" onClick={() => enterEdit(true)}>
|
|
{localize('com_ui_cancel')}
|
|
</button>
|
|
}
|
|
/>
|
|
</div>
|
|
</Container>
|
|
);
|
|
};
|
|
|
|
export default EditMessage;
|