mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 11:50:14 +01:00
🪄 feat: Artifacts Badge & Optimize Ephemeral Agent State (#8252)
* 🔧 fix: Update type annotations in useEventHandlers for better type safety * 🔧 refactor: `useToolToggle` for improved localStorage synchronization and allow string/falsy values for setting to storage * ✨ feat: Implement Artifacts badge to BadgeRow with toggle options and UI components - Added Artifacts component to manage artifacts state and options. - Introduced ArtifactsSubMenu for additional settings related to artifacts. - Integrated artifacts functionality into BadgeRow and ToolsDropdown components. - Updated localStorage handling for artifacts state persistence. - Enhanced localization for artifacts-related strings in translation files. - Refactored Agent model to include artifacts in the ephemeral agent response. * fix: set ephemeral agent state for conversation on finalization * chore: remove beta settings dialog tab * refactor: improve Ephemeral Agent statefulness * fix: update setValue parameter to use 'value' instead of 'isChecked' in CheckboxButton * refactor: update color classes for Artifact toggle and order of dropdown components * chore: remove unused i18n localization
This commit is contained in:
parent
458580ec87
commit
a288ad1d9c
23 changed files with 547 additions and 232 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { useRef, useEffect, useCallback, useMemo } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useCallback, useMemo, useEffect } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { Constants, LocalStorageKeys } from 'librechat-data-provider';
|
||||
import type { VerifyToolAuthResponse } from 'librechat-data-provider';
|
||||
import type { UseQueryOptions } from '@tanstack/react-query';
|
||||
|
|
@ -19,9 +19,11 @@ const storageCondition = (value: unknown, rawCurrentValue?: string | null) => {
|
|||
console.error(e);
|
||||
}
|
||||
}
|
||||
return value !== undefined && value !== null && value !== '' && value !== false;
|
||||
return value !== undefined && value !== null;
|
||||
};
|
||||
|
||||
type ToolValue = boolean | string;
|
||||
|
||||
interface UseToolToggleOptions {
|
||||
conversationId?: string | null;
|
||||
toolKey: string;
|
||||
|
|
@ -60,36 +62,52 @@ export function useToolToggle({
|
|||
[externalIsAuthenticated, authConfig, authQuery.data?.authenticated],
|
||||
);
|
||||
|
||||
const isToolEnabled = useMemo(() => {
|
||||
return ephemeralAgent?.[toolKey] ?? false;
|
||||
}, [ephemeralAgent, toolKey]);
|
||||
|
||||
/** Track previous value to prevent infinite loops */
|
||||
const prevIsToolEnabled = useRef(isToolEnabled);
|
||||
|
||||
const [toggleState, setToggleState] = useLocalStorage<boolean>(
|
||||
// Keep localStorage in sync
|
||||
const [, setLocalStorageValue] = useLocalStorage<ToolValue>(
|
||||
`${localStorageKey}${key}`,
|
||||
isToolEnabled,
|
||||
false,
|
||||
undefined,
|
||||
storageCondition,
|
||||
);
|
||||
|
||||
// The actual current value comes from ephemeralAgent
|
||||
const toolValue = useMemo(() => {
|
||||
return ephemeralAgent?.[toolKey] ?? false;
|
||||
}, [ephemeralAgent, toolKey]);
|
||||
|
||||
const isToolEnabled = useMemo(() => {
|
||||
// For backward compatibility, treat truthy string values as enabled
|
||||
if (typeof toolValue === 'string') {
|
||||
return toolValue.length > 0;
|
||||
}
|
||||
return toolValue === true;
|
||||
}, [toolValue]);
|
||||
|
||||
// Sync to localStorage when ephemeralAgent changes
|
||||
useEffect(() => {
|
||||
const value = ephemeralAgent?.[toolKey];
|
||||
if (value !== undefined) {
|
||||
setLocalStorageValue(value);
|
||||
}
|
||||
}, [ephemeralAgent, toolKey, setLocalStorageValue]);
|
||||
|
||||
const [isPinned, setIsPinned] = useLocalStorage<boolean>(`${localStorageKey}pinned`, false);
|
||||
|
||||
const handleChange = useCallback(
|
||||
({ e, isChecked }: { e?: React.ChangeEvent<HTMLInputElement>; isChecked: boolean }) => {
|
||||
({ e, value }: { e?: React.ChangeEvent<HTMLInputElement>; value: ToolValue }) => {
|
||||
if (isAuthenticated !== undefined && !isAuthenticated && setIsDialogOpen) {
|
||||
setIsDialogOpen(true);
|
||||
e?.preventDefault?.();
|
||||
return;
|
||||
}
|
||||
setToggleState(isChecked);
|
||||
|
||||
// Update ephemeralAgent (localStorage will sync automatically via effect)
|
||||
setEphemeralAgent((prev) => ({
|
||||
...prev,
|
||||
[toolKey]: isChecked,
|
||||
...(prev || {}),
|
||||
[toolKey]: value,
|
||||
}));
|
||||
},
|
||||
[setToggleState, setIsDialogOpen, isAuthenticated, setEphemeralAgent, toolKey],
|
||||
[setIsDialogOpen, isAuthenticated, setEphemeralAgent, toolKey],
|
||||
);
|
||||
|
||||
const debouncedChange = useMemo(
|
||||
|
|
@ -97,18 +115,12 @@ export function useToolToggle({
|
|||
[handleChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (prevIsToolEnabled.current !== isToolEnabled) {
|
||||
setToggleState(isToolEnabled);
|
||||
}
|
||||
prevIsToolEnabled.current = isToolEnabled;
|
||||
}, [isToolEnabled, setToggleState]);
|
||||
|
||||
return {
|
||||
toggleState,
|
||||
toggleState: toolValue, // Return the actual value from ephemeralAgent
|
||||
handleChange,
|
||||
isToolEnabled,
|
||||
setToggleState,
|
||||
toolValue,
|
||||
setToggleState: (value: ToolValue) => handleChange({ value }), // Adapter for direct setting
|
||||
ephemeralAgent,
|
||||
debouncedChange,
|
||||
setEphemeralAgent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue