This commit is contained in:
Kashan Samad 2026-04-05 02:32:22 +00:00 committed by GitHub
commit dd7224b44c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 110 additions and 1 deletions

View file

@ -21,6 +21,7 @@ import FileSearch from './FileSearch';
import Artifacts from './Artifacts';
import MCPSelect from './MCPSelect';
import WebSearch from './WebSearch';
import NativeWebSearch from './NativeWebSearch';
import store from '~/store';
interface BadgeRowProps {
@ -371,6 +372,7 @@ function BadgeRow({
{showEphemeralBadges === true && (
<>
<WebSearch />
<NativeWebSearch />
<CodeInterpreter />
<FileSearch />
<Artifacts />

View file

@ -0,0 +1,57 @@
import React, { memo } from 'react';
import { Globe } from 'lucide-react';
import { CheckboxButton } from '@librechat/client';
import { LocalStorageKeys, Permissions, PermissionTypes } from 'librechat-data-provider';
import { useLocalize, useSetIndexOptions, useHasAccess } from '~/hooks';
import useLocalStorage from '~/hooks/useLocalStorageAlt';
import { useChatContext } from '~/Providers/ChatContext';
function NativeWebSearch() {
const localize = useLocalize();
const { setOption } = useSetIndexOptions();
const { conversation } = useChatContext();
const [isPinned] = useLocalStorage<boolean>(
`${LocalStorageKeys.LAST_NATIVE_WEB_SEARCH_TOGGLE_}pinned`,
false,
);
// Only show native web search if user doesn't have permission for authenticated web search
const canUseWebSearch = useHasAccess({
permissionType: PermissionTypes.WEB_SEARCH,
permission: Permissions.USE,
});
// Don't render if user has access to authenticated web search
if (canUseWebSearch) {
return null;
}
// Use conversation.web_search as the single source of truth
const webSearchEnabled = conversation?.web_search ?? false;
// Don't render if not enabled and not pinned
if (!webSearchEnabled && !isPinned) {
return null;
}
const handleChange = (values: {
e?: React.ChangeEvent<HTMLInputElement>;
value: string | boolean;
}) => {
const checked = typeof values.value === 'boolean' ? values.value : values.value === 'true';
setOption('web_search')(checked);
};
return (
<CheckboxButton
className="max-w-fit"
checked={webSearchEnabled}
setValue={handleChange}
label={localize('com_ui_web_search')}
isCheckedClassName="border-purple-600/40 bg-purple-500/10 hover:bg-purple-700/10"
icon={<Globe className="icon-md" />}
/>
);
}
export default memo(NativeWebSearch);

View file

@ -8,13 +8,16 @@ import {
Permissions,
ArtifactModes,
PermissionTypes,
LocalStorageKeys,
defaultAgentCapabilities,
} from 'librechat-data-provider';
import { useLocalize, useHasAccess, useAgentCapabilities } from '~/hooks';
import { useLocalize, useHasAccess, useAgentCapabilities, useSetIndexOptions } from '~/hooks';
import ArtifactsSubMenu from '~/components/Chat/Input/ArtifactsSubMenu';
import MCPSubMenu from '~/components/Chat/Input/MCPSubMenu';
import { useGetStartupConfig } from '~/data-provider';
import { useBadgeRowContext } from '~/Providers';
import { useChatContext } from '~/Providers/ChatContext';
import useLocalStorage from '~/hooks/useLocalStorageAlt';
import { cn } from '~/utils';
interface ToolsDropdownProps {
@ -36,6 +39,8 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
searchApiKeyForm,
} = useBadgeRowContext();
const { data: startupConfig } = useGetStartupConfig();
const { conversation } = useChatContext();
const { setOption } = useSetIndexOptions();
const { codeEnabled, webSearchEnabled, artifactsEnabled, fileSearchEnabled } =
useAgentCapabilities(agentsConfig?.capabilities ?? defaultAgentCapabilities);
@ -49,6 +54,10 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
setIsPinned: setIsSearchPinned,
authData: webSearchAuthData,
} = webSearch;
const [isNativeWebSearchPinned, setIsNativeWebSearchPinned] = useLocalStorage<boolean>(
`${LocalStorageKeys.LAST_NATIVE_WEB_SEARCH_TOGGLE_}pinned`,
false,
);
const {
isPinned: isCodePinned,
setIsPinned: setIsCodePinned,
@ -77,6 +86,8 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
permission: Permissions.USE,
});
const shouldShowNativeWebSearch = !canUseWebSearch;
const showWebSearchSettings = useMemo(() => {
const authTypes = webSearchAuthData?.authTypes ?? [];
if (authTypes.length === 0) return true;
@ -93,6 +104,11 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
webSearch.debouncedChange({ value: newValue });
}, [webSearch]);
const handleNativeWebSearchToggle = useCallback(() => {
const currentValue = conversation?.web_search ?? false;
setOption('web_search')(!currentValue);
}, [conversation?.web_search, setOption]);
const handleCodeInterpreterToggle = useCallback(() => {
const newValue = !codeInterpreter.toggleState;
codeInterpreter.debouncedChange({ value: newValue });
@ -220,6 +236,38 @@ const ToolsDropdown = ({ disabled }: ToolsDropdownProps) => {
});
}
if (shouldShowNativeWebSearch) {
dropdownItems.push({
onClick: handleNativeWebSearchToggle,
hideOnClick: false,
render: (props) => (
<div {...props}>
<div className="flex items-center gap-2">
<Globe className="icon-md" />
<span>{localize('com_ui_web_search')}</span>
</div>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
setIsNativeWebSearchPinned(!isNativeWebSearchPinned);
}}
className={cn(
'rounded p-1 transition-all duration-200',
'hover:bg-surface-secondary hover:shadow-sm',
!isNativeWebSearchPinned && 'text-text-secondary hover:text-text-primary',
)}
aria-label={isNativeWebSearchPinned ? localize('com_ui_unpin') : localize('com_ui_pin')}
>
<div className="h-4 w-4">
<PinIcon unpin={isNativeWebSearchPinned} />
</div>
</button>
</div>
),
});
}
if (canRunCode && codeEnabled) {
dropdownItems.push({
onClick: handleCodeInterpreterToggle,

View file

@ -1931,6 +1931,8 @@ export enum LocalStorageKeys {
LAST_CODE_TOGGLE_ = 'LAST_CODE_TOGGLE_',
/** Last checked toggle for Web Search per conversation ID */
LAST_WEB_SEARCH_TOGGLE_ = 'LAST_WEB_SEARCH_TOGGLE_',
/** Last checked toggle for Native Web Search per conversation ID */
LAST_NATIVE_WEB_SEARCH_TOGGLE_ = 'LAST_NATIVE_WEB_SEARCH_TOGGLE_',
/** Last checked toggle for File Search per conversation ID */
LAST_FILE_SEARCH_TOGGLE_ = 'LAST_FILE_SEARCH_TOGGLE_',
/** Last checked toggle for Artifacts per conversation ID */