File size: 3,797 Bytes
f0743f4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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';
import { useVerifyAgentToolAuth } from '~/data-provider';
import { setTimestamp } from '~/utils/timestamps';
import useLocalStorage from '~/hooks/useLocalStorageAlt';
import { ephemeralAgentByConvoId } from '~/store';
type ToolValue = boolean | string;
interface UseToolToggleOptions {
conversationId?: string | null;
toolKey: string;
localStorageKey: LocalStorageKeys;
isAuthenticated?: boolean;
setIsDialogOpen?: (open: boolean) => void;
/** Options for auth verification */
authConfig?: {
toolId: string;
queryOptions?: UseQueryOptions<VerifyToolAuthResponse>;
};
}
export function useToolToggle({
conversationId,
toolKey: _toolKey,
localStorageKey,
isAuthenticated: externalIsAuthenticated,
setIsDialogOpen,
authConfig,
}: UseToolToggleOptions) {
const key = conversationId ?? Constants.NEW_CONVO;
const [ephemeralAgent, setEphemeralAgent] = useRecoilState(ephemeralAgentByConvoId(key));
const authQuery = useVerifyAgentToolAuth(
{ toolId: authConfig?.toolId || '' },
{
enabled: !!authConfig?.toolId,
...authConfig?.queryOptions,
},
);
const isAuthenticated = useMemo(
() =>
externalIsAuthenticated ?? (authConfig ? (authQuery?.data?.authenticated ?? false) : false),
[externalIsAuthenticated, authConfig, authQuery.data?.authenticated],
);
const toolKey = useMemo(() => _toolKey, [_toolKey]);
const storageKey = useMemo(() => `${localStorageKey}${key}`, [localStorageKey, key]);
// 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 with timestamps when ephemeralAgent changes
useEffect(() => {
const value = ephemeralAgent?.[toolKey];
if (value !== undefined) {
localStorage.setItem(storageKey, JSON.stringify(value));
setTimestamp(storageKey);
}
}, [ephemeralAgent, toolKey, storageKey]);
const [isPinned, setIsPinned] = useLocalStorage<boolean>(`${localStorageKey}pinned`, false);
const handleChange = useCallback(
({ e, value }: { e?: React.ChangeEvent<HTMLInputElement>; value: ToolValue }) => {
if (isAuthenticated !== undefined && !isAuthenticated && setIsDialogOpen) {
setIsDialogOpen(true);
e?.preventDefault?.();
setEphemeralAgent((prev) => ({
...(prev || {}),
[toolKey]: false,
}));
return;
}
// Update ephemeralAgent (localStorage will sync automatically via effect)
setEphemeralAgent((prev) => ({
...(prev || {}),
[toolKey]: value,
}));
},
[setIsDialogOpen, isAuthenticated, setEphemeralAgent, toolKey],
);
const debouncedChange = useMemo(
() => debounce(handleChange, 50, { leading: true }),
[handleChange],
);
return {
toggleState: toolValue, // Return the actual value from ephemeralAgent
handleChange,
isToolEnabled,
toolValue,
setToggleState: (value: ToolValue) => handleChange({ value }), // Adapter for direct setting
ephemeralAgent,
debouncedChange,
setEphemeralAgent,
authData: authQuery?.data,
isPinned,
setIsPinned,
};
}
|