mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
🏄♂️ refactor: Optimize Reasoning UI & Token Streaming (#5546)
* ✨ feat: Implement Show Thinking feature; refactor: testing thinking render optimizations * ✨ feat: Refactor Thinking component styles and enhance Markdown rendering * chore: add back removed code, revert type changes * chore: Add back resetCounter effect to Markdown component for improved code block indexing * chore: bump @librechat/agents and google langchain packages * WIP: reasoning type updates * WIP: first pass, reasoning content blocks * chore: revert code * chore: bump @librechat/agents * refactor: optimize reasoning tag handling * style: ul indent padding * feat: add Reasoning component to handle reasoning display * feat: first pass, content reasoning part styling * refactor: add content placeholder for endpoints using new stream handler * refactor: only cache messages when requesting stream audio * fix: circular dep. * fix: add default param * refactor: tts, only request after message stream, fix chrome autoplay * style: update label for submitting state and add localization for 'Thinking...' * fix: improve global audio pause logic and reset active run ID * fix: handle artifact edge cases * fix: remove unnecessary console log from artifact update test * feat: add support for continued message handling with new streaming method --------- Co-authored-by: Marco Beretta <81851188+berry-13@users.noreply.github.com>
This commit is contained in:
parent
d60a149ad9
commit
591a019766
48 changed files with 1791 additions and 726 deletions
|
|
@ -1,20 +1,62 @@
|
|||
import { useState } from 'react';
|
||||
import { useState, useMemo, memo, useCallback } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Atom, ChevronDown } from 'lucide-react';
|
||||
import type { MouseEvent } from 'react';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import type { MouseEvent, FC } from 'react';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
interface ThinkingProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
const BUTTON_STYLES = {
|
||||
base: 'group mt-3 flex w-fit items-center justify-center rounded-xl bg-surface-tertiary px-3 py-2 text-xs leading-[18px] animate-thinking-appear',
|
||||
icon: 'icon-sm ml-1.5 transform-gpu text-text-primary transition-transform duration-200',
|
||||
} as const;
|
||||
|
||||
const Thinking = ({ children }: ThinkingProps) => {
|
||||
const CONTENT_STYLES = {
|
||||
wrapper: 'relative pl-3 text-text-secondary',
|
||||
border:
|
||||
'absolute left-0 h-[calc(100%-10px)] border-l-2 border-border-medium dark:border-border-heavy',
|
||||
partBorder:
|
||||
'absolute left-0 h-[calc(100%)] border-l-2 border-border-medium dark:border-border-heavy',
|
||||
text: 'whitespace-pre-wrap leading-[26px]',
|
||||
} as const;
|
||||
|
||||
export const ThinkingContent: FC<{ children: React.ReactNode; isPart?: boolean }> = memo(
|
||||
({ isPart, children }) => (
|
||||
<div className={CONTENT_STYLES.wrapper}>
|
||||
<div className={isPart === true ? CONTENT_STYLES.partBorder : CONTENT_STYLES.border} />
|
||||
<p className={CONTENT_STYLES.text}>{children}</p>
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
export const ThinkingButton = memo(
|
||||
({
|
||||
isExpanded,
|
||||
onClick,
|
||||
label,
|
||||
}: {
|
||||
isExpanded: boolean;
|
||||
onClick: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||
label: string;
|
||||
}) => (
|
||||
<button type="button" onClick={onClick} className={BUTTON_STYLES.base}>
|
||||
<Atom size={14} className="mr-1.5 text-text-secondary" />
|
||||
{label}
|
||||
<ChevronDown className={`${BUTTON_STYLES.icon} ${isExpanded ? 'rotate-180' : ''}`} />
|
||||
</button>
|
||||
),
|
||||
);
|
||||
|
||||
const Thinking: React.ElementType = memo(({ children }: { children: React.ReactNode }) => {
|
||||
const localize = useLocalize();
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
const showThinking = useRecoilValue<boolean>(store.showThinking);
|
||||
const [isExpanded, setIsExpanded] = useState(showThinking);
|
||||
|
||||
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
const handleClick = useCallback((e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
setIsExpanded((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const label = useMemo(() => localize('com_ui_thoughts'), [localize]);
|
||||
|
||||
if (children == null) {
|
||||
return null;
|
||||
|
|
@ -22,28 +64,23 @@ const Thinking = ({ children }: ThinkingProps) => {
|
|||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClick}
|
||||
className="group mb-3 flex w-fit items-center justify-center rounded-xl bg-surface-tertiary px-3.5 py-2 text-xs leading-[18px] text-text-primary transition-colors hover:bg-surface-secondary"
|
||||
<ThinkingButton isExpanded={isExpanded} onClick={handleClick} label={label} />
|
||||
<div
|
||||
className="grid transition-all duration-300 ease-out"
|
||||
style={{
|
||||
gridTemplateRows: isExpanded ? '1fr' : '0fr',
|
||||
}}
|
||||
>
|
||||
<Atom size={14} className="mr-1.5 text-text-secondary" />
|
||||
{localize('com_ui_thoughts')}
|
||||
<ChevronDown
|
||||
className="icon-sm ml-1.5 text-text-primary transition-transform duration-200"
|
||||
style={{
|
||||
transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
{isExpanded && (
|
||||
<div className="relative pl-3 text-text-secondary">
|
||||
<div className="absolute left-0 top-[5px] h-[calc(100%-10px)] border-l-2 border-border-medium dark:border-border-heavy" />
|
||||
<p className="my-4 whitespace-pre-wrap leading-[26px]">{children}</p>
|
||||
<div className="overflow-hidden">
|
||||
<ThinkingContent>{children}</ThinkingContent>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default Thinking;
|
||||
ThinkingButton.displayName = 'ThinkingButton';
|
||||
ThinkingContent.displayName = 'ThinkingContent';
|
||||
Thinking.displayName = 'Thinking';
|
||||
|
||||
export default memo(Thinking);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue