🔍 feat: Show Messages from Search Result (#2699)

* refactor(Nav): delegate Search-specific variables/hooks to SearchContext

* fix: safely determine firstTodayConvoId if convo is undefined

* chore: remove empty line

* feat: initial render of search messages

* feat: SearchButtons

* update Ko.ts

* update localizations with new key phrases

* chore: localization comparisons

* fix: clear conversation state on searchQuery navigation

* style: search messages view styling

* refactor(Convo): consolidate logic to navigateWithLastTools from useNavigateToConvo

* fix(SearchButtons): styling and correct navigation logic

* fix(SearchBar): invalidate all message queries and invoke `clearText` if onChange value is empty

* refactor(NewChat): consolidate new chat button logic to NewChatButtonIcon

* chore: localizations for Nav date groups

* chore: update comparisons

* fix: early return from sendRequest to avoid quick searchQuery reset

* style: Link Icon

* chore: bump tiktoken, use o200k_base for gpt-4o
This commit is contained in:
Danny Avila 2024-05-14 11:00:01 -04:00 committed by GitHub
parent 638ac5bba6
commit e42709bd1f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 2742 additions and 234 deletions

View file

@ -308,7 +308,7 @@ class OpenAIClient extends BaseClient {
let tokenizer;
this.encoding = 'text-davinci-003';
if (this.isChatCompletion) {
this.encoding = 'cl100k_base';
this.encoding = this.modelOptions.model.includes('gpt-4o') ? 'o200k_base' : 'cl100k_base';
tokenizer = this.constructor.getTokenizer(this.encoding);
} else if (this.isUnofficialChatGptModel) {
const extendSpecialTokens = {

View file

@ -89,7 +89,7 @@
"passport-local": "^1.0.0",
"pino": "^8.12.1",
"sharp": "^0.32.6",
"tiktoken": "^1.0.10",
"tiktoken": "^1.0.15",
"traverse": "^0.6.7",
"ua-parser-js": "^1.0.36",
"winston": "^3.11.0",

View file

@ -0,0 +1,6 @@
import { createContext, useContext } from 'react';
import useSearch from '~/hooks/Conversations/useSearch';
type SearchContextType = ReturnType<typeof useSearch>;
export const SearchContext = createContext<SearchContextType>({} as SearchContextType);
export const useSearchContext = () => useContext(SearchContext);

View file

@ -2,6 +2,7 @@ export { default as ToastProvider } from './ToastContext';
export { default as AssistantsProvider } from './AssistantsContext';
export * from './ChatContext';
export * from './ToastContext';
export * from './SearchContext';
export * from './FileMapContext';
export * from './AssistantsContext';
export * from './AssistantsMapContext';

View file

@ -0,0 +1,53 @@
import { Suspense } from 'react';
import type { TMessage, TMessageContentParts } from 'librechat-data-provider';
import { UnfinishedMessage } from './MessageContent';
import { DelayedRender } from '~/components/ui';
import MarkdownLite from './MarkdownLite';
import { cn } from '~/utils';
import Part from './Part';
const SearchContent = ({ message }: { message: TMessage }) => {
const { messageId } = message;
if (Array.isArray(message.content) && message.content.length > 0) {
return (
<>
{message.content
.filter((part: TMessageContentParts | undefined) => part)
.map((part: TMessageContentParts | undefined, idx: number) => {
if (!part) {
return null;
}
return (
<Part
key={`display-${messageId}-${idx}`}
showCursor={false}
isSubmitting={false}
part={part}
message={message}
/>
);
})}
{message.unfinished && (
<Suspense>
<DelayedRender delay={250}>
<UnfinishedMessage message={message} key={`unfinished-${messageId}`} />
</DelayedRender>
</Suspense>
)}
</>
);
}
return (
<div
className={cn(
'markdown prose dark:prose-invert light w-full break-words',
message.isCreatedByUser ? 'whitespace-pre-wrap dark:text-gray-20' : 'dark:text-gray-70',
)}
>
<MarkdownLite content={message.text ?? ''} />
</div>
);
};
export default SearchContent;

View file

@ -0,0 +1,42 @@
import React from 'react';
import { cn } from '~/utils';
const MinimalMessages = React.forwardRef(
(
props: { children: React.ReactNode; className?: string },
ref: React.ForwardedRef<HTMLDivElement>,
) => {
return (
<div
className={cn(
'relative flex w-full grow overflow-hidden bg-white dark:bg-gray-800',
props.className,
)}
>
<div className="transition-width relative h-full w-full flex-1 overflow-auto bg-white dark:bg-gray-800">
<div className="flex h-full flex-col" role="presentation" tabIndex={0}>
<div className="flex-1 overflow-hidden overflow-y-auto">
<div className="dark:gpt-dark-gray relative h-full">
<div
ref={ref}
style={{
height: '100%',
overflowY: 'auto',
width: '100%',
}}
>
<div className="flex flex-col pb-9 text-sm dark:bg-transparent">
{props.children}
<div className="dark:gpt-dark-gray group h-0 w-full flex-shrink-0 dark:border-gray-800/50" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
},
);
export default MinimalMessages;

View file

@ -0,0 +1,40 @@
import { Link } from 'lucide-react';
import type { TMessage } from 'librechat-data-provider';
import { useLocalize, useNavigateToConvo } from '~/hooks';
import { useSearchContext } from '~/Providers';
import { getConversationById } from '~/utils';
export default function SearchButtons({ message }: { message: TMessage }) {
const localize = useLocalize();
const { searchQueryRes } = useSearchContext();
const { navigateWithLastTools } = useNavigateToConvo();
if (!message.conversationId) {
return null;
}
const clickHandler = (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
const conversation = getConversationById(searchQueryRes?.data, message.conversationId);
if (!conversation) {
return;
}
document.title = message.title ?? '';
navigateWithLastTools(conversation);
};
return (
<div className="visible mt-0 flex items-center justify-center gap-1 self-end text-gray-400 lg:justify-start">
<a
className="ml-0 flex cursor-pointer items-center gap-1.5 rounded-md p-1 text-xs hover:text-gray-900 hover:underline dark:text-gray-400/70 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"
onClick={clickHandler}
title={localize('com_ui_go_to_conversation')}
>
<Link className="icon-sm" />
{message.title}
</a>
</div>
);
}

View file

@ -0,0 +1,61 @@
import { useRecoilValue } from 'recoil';
import { useAuthContext, useLocalize } from '~/hooks';
import type { TMessageProps } from '~/common';
import Icon from '~/components/Chat/Messages/MessageIcon';
import SearchContent from './Content/SearchContent';
import SearchButtons from './SearchButtons';
import SubRow from './SubRow';
import { cn } from '~/utils';
import store from '~/store';
export default function Message({ message }: Pick<TMessageProps, 'message'>) {
const UsernameDisplay = useRecoilValue<boolean>(store.UsernameDisplay);
const { user } = useAuthContext();
const localize = useLocalize();
if (!message) {
return null;
}
const { isCreatedByUser } = message ?? {};
let messageLabel = '';
if (isCreatedByUser) {
messageLabel = UsernameDisplay ? user?.name || user?.username : localize('com_user_message');
} else {
messageLabel = message.sender;
}
return (
<>
<div className="text-token-text-primary w-full border-0 bg-transparent dark:border-0 dark:bg-transparent">
<div className="m-auto justify-center p-4 py-2 text-base md:gap-6 ">
<div className="final-completion group mx-auto flex flex-1 gap-3 text-base md:max-w-3xl md:px-5 lg:max-w-[40rem] lg:px-1 xl:max-w-[48rem] xl:px-5">
<div className="relative flex flex-shrink-0 flex-col items-end">
<div>
<div className="pt-0.5">
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
<Icon message={message} />
</div>
</div>
</div>
</div>
<div
className={cn('relative flex w-11/12 flex-col', isCreatedByUser ? '' : 'agent-turn')}
>
<div className="select-none font-semibold">{messageLabel}</div>
<div className="flex-col gap-1 md:gap-3">
<div className="flex max-w-full flex-grow flex-col gap-0">
<SearchContent message={message} />
</div>
</div>
<SubRow classes="text-xs">
<SearchButtons message={message} />
</SubRow>
</div>
</div>
</div>
</div>
</>
);
}

View file

@ -1,22 +0,0 @@
import { memo } from 'react';
import { useRecoilValue } from 'recoil';
import MessagesView from './Messages/MessagesView';
import store from '~/store';
import Header from './Header';
function SearchView() {
const searchResultMessagesTree = useRecoilValue(store.searchResultMessagesTree);
return (
<div className="relative flex w-full grow overflow-hidden bg-white dark:bg-gray-800">
<div className="transition-width relative flex h-full w-full flex-1 flex-col items-stretch overflow-hidden bg-white pt-0 dark:bg-gray-800">
<div className="flex h-full flex-col" role="presentation" tabIndex={0}>
<MessagesView messagesTree={searchResultMessagesTree} Header={<Header />} />
</div>
</div>
</div>
);
}
export default memo(SearchView);

View file

@ -2,6 +2,7 @@ import { useMemo, memo } from 'react';
import { parseISO, isToday } from 'date-fns';
import { TConversation } from 'librechat-data-provider';
import { groupConversationsByDate } from '~/utils';
import { useLocalize } from '~/hooks';
import Convo from './Convo';
const Conversations = ({
@ -13,12 +14,14 @@ const Conversations = ({
moveToTop: () => void;
toggleNav: () => void;
}) => {
const localize = useLocalize();
const groupedConversations = useMemo(
() => groupConversationsByDate(conversations),
[conversations],
);
const firstTodayConvoId = useMemo(
() => conversations.find((convo) => isToday(parseISO(convo.updatedAt)))?.conversationId,
() =>
conversations.find((convo) => convo && isToday(parseISO(convo.updatedAt)))?.conversationId,
[conversations],
);
@ -37,7 +40,7 @@ const Conversations = ({
paddingLeft: '10px',
}}
>
{groupName}
{localize(groupName) || groupName}
</div>
{convos.map((convo, i) => (
<Convo

View file

@ -1,7 +1,6 @@
import { useRecoilValue } from 'recoil';
import { useParams } from 'react-router-dom';
import { useState, useRef, useMemo } from 'react';
import { EModelEndpoint, LocalStorageKeys } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
import { useUpdateConversationMutation } from '~/data-provider';
@ -26,8 +25,8 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
const updateConvoMutation = useUpdateConversationMutation(currentConvoId ?? '');
const activeConvos = useRecoilValue(store.allConversationsSelector);
const { data: endpointsConfig } = useGetEndpointsQuery();
const { navigateWithLastTools } = useNavigateToConvo();
const { refreshConversations } = useConversations();
const { navigateToConvo } = useNavigateToConvo();
const { showToast } = useToastContext();
const { conversationId, title } = conversation;
@ -51,23 +50,7 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
// set document title
document.title = title;
// set conversation to the new conversation
if (conversation?.endpoint === EModelEndpoint.gptPlugins) {
let lastSelectedTools = [];
try {
lastSelectedTools =
JSON.parse(localStorage.getItem(LocalStorageKeys.LAST_TOOLS) ?? '') ?? [];
} catch (e) {
// console.error(e);
}
navigateToConvo({
...conversation,
tools: conversation?.tools?.length ? conversation?.tools : lastSelectedTools,
});
} else {
navigateToConvo(conversation);
}
navigateWithLastTools(conversation);
};
const renameHandler = (e: MouseEvent<HTMLButtonElement>) => {

View file

@ -1,7 +1,6 @@
import { useParams } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { useCallback, useEffect, useState, useMemo, memo } from 'react';
import type { ConversationListResponse } from 'librechat-data-provider';
import {
useMediaQuery,
useAuthContext,
@ -10,9 +9,10 @@ import {
useNavScrolling,
useConversations,
} from '~/hooks';
import { useSearchInfiniteQuery, useConversationsInfiniteQuery } from '~/data-provider';
import { useConversationsInfiniteQuery } from '~/data-provider';
import { TooltipProvider, Tooltip } from '~/components/ui';
import { Conversations } from '~/components/Conversations';
import { useSearchContext } from '~/Providers';
import { Spinner } from '~/components/svg';
import SearchBar from './SearchBar';
import NavToggle from './NavToggle';
@ -47,26 +47,18 @@ const Nav = ({ navVisible, setNavVisible }) => {
}
}, [isSmallScreen]);
const [pageNumber, setPageNumber] = useState(1);
const { newConversation } = useConversation();
const [showLoading, setShowLoading] = useState(false);
const searchQuery = useRecoilValue(store.searchQuery);
const isSearchEnabled = useRecoilValue(store.isSearchEnabled);
const { newConversation, searchPlaceholderConversation } = useConversation();
const { refreshConversations } = useConversations();
const setSearchResultMessages = useSetRecoilState(store.searchResultMessages);
const { pageNumber, searchQuery, setPageNumber, searchQueryRes } = useSearchContext();
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useConversationsInfiniteQuery(
{ pageNumber: pageNumber.toString(), isArchived: false },
{ enabled: isAuthenticated },
);
const searchQueryRes = useSearchInfiniteQuery(
{ pageNumber: pageNumber.toString(), searchQuery: searchQuery, isArchived: false },
{ enabled: isAuthenticated && !!searchQuery.length },
);
const { containerRef, moveToTop } = useNavScrolling({
setShowLoading,
hasNextPage: searchQuery ? searchQueryRes.hasNextPage : hasNextPage,
@ -81,21 +73,6 @@ const Nav = ({ navVisible, setNavVisible }) => {
[data, searchQuery, searchQueryRes?.data],
);
const onSearchSuccess = useCallback(({ data }: { data: ConversationListResponse }) => {
const res = data;
searchPlaceholderConversation();
setSearchResultMessages(res.messages);
/* disabled due recoil methods not recognized as state setters */
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // Empty dependency array
useEffect(() => {
//we use isInitialLoading here instead of isLoading because query is disabled by default
if (searchQueryRes.data) {
onSearchSuccess({ data: searchQueryRes.data.pages[0] });
}
}, [searchQueryRes.data, searchQueryRes.isInitialLoading, onSearchSuccess]);
const clearSearch = () => {
setPageNumber(1);
refreshConversations();

View file

@ -1,3 +1,5 @@
import { Search } from 'lucide-react';
import { useRecoilValue } from 'recoil';
import { useNavigate } from 'react-router-dom';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui';
@ -7,6 +9,50 @@ import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
import { useLocalize, useNewConvo } from '~/hooks';
import { NewChatIcon } from '~/components/svg';
import store from '~/store';
import type { TConversation } from 'librechat-data-provider';
const NewChatButtonIcon = ({ conversation }: { conversation: TConversation | null }) => {
const searchQuery = useRecoilValue(store.searchQuery);
const { data: endpointsConfig } = useGetEndpointsQuery();
if (searchQuery) {
return (
<div className="shadow-stroke relative flex h-7 w-7 items-center justify-center rounded-full bg-white text-black dark:bg-white">
<Search className="h-5 w-5" />
</div>
);
}
let { endpoint = '' } = conversation ?? {};
const iconURL = conversation?.iconURL ?? '';
endpoint = getIconEndpoint({ endpointsConfig, iconURL, endpoint });
const endpointType = getEndpointField(endpointsConfig, endpoint, 'type');
const endpointIconURL = getEndpointField(endpointsConfig, endpoint, 'iconURL');
const iconKey = getIconKey({ endpoint, endpointsConfig, endpointType, endpointIconURL });
const Icon = icons[iconKey];
return (
<div className="h-7 w-7 flex-shrink-0">
{iconURL && iconURL.includes('http') ? (
<ConvoIconURL preset={conversation} endpointIconURL={iconURL} context="nav" />
) : (
<div className="shadow-stroke relative flex h-full items-center justify-center rounded-full bg-white text-black dark:bg-white">
{endpoint &&
Icon &&
Icon({
size: 41,
context: 'nav',
className: 'h-2/3 w-2/3',
endpoint,
endpointType,
iconURL: endpointIconURL,
})}
</div>
)}
</div>
);
};
export default function NewChat({
index = 0,
@ -22,16 +68,7 @@ export default function NewChat({
const navigate = useNavigate();
const localize = useLocalize();
const { data: endpointsConfig } = useGetEndpointsQuery();
const { conversation } = store.useCreateConversationAtom(index);
let { endpoint = '' } = conversation ?? {};
const iconURL = conversation?.iconURL ?? '';
endpoint = getIconEndpoint({ endpointsConfig, iconURL, endpoint });
const endpointType = getEndpointField(endpointsConfig, endpoint, 'type');
const endpointIconURL = getEndpointField(endpointsConfig, endpoint, 'iconURL');
const iconKey = getIconKey({ endpoint, endpointsConfig, endpointType, endpointIconURL });
const Icon = icons[iconKey];
const clickHandler = (event: React.MouseEvent<HTMLAnchorElement>) => {
if (event.button === 0 && !event.ctrlKey) {
@ -53,24 +90,7 @@ export default function NewChat({
onClick={clickHandler}
className="group flex h-10 items-center gap-2 rounded-lg px-2 font-medium hover:bg-gray-200 dark:hover:bg-gray-700"
>
<div className="h-7 w-7 flex-shrink-0">
{iconURL && iconURL.includes('http') ? (
<ConvoIconURL preset={conversation} endpointIconURL={iconURL} context="nav" />
) : (
<div className="shadow-stroke relative flex h-full items-center justify-center rounded-full bg-white text-black dark:bg-white">
{endpoint &&
Icon &&
Icon({
size: 41,
context: 'nav',
className: 'h-2/3 w-2/3',
endpoint,
endpointType,
iconURL: endpointIconURL,
})}
</div>
)}
</div>
<NewChatButtonIcon conversation={conversation} />
<div className="text-token-text-primary grow overflow-hidden text-ellipsis whitespace-nowrap text-sm">
{localize('com_ui_new_chat')}
</div>

View file

@ -1,7 +1,9 @@
import { forwardRef, useState, useCallback, useMemo, Ref } from 'react';
import debounce from 'lodash/debounce';
import { Search, X } from 'lucide-react';
import { useSetRecoilState } from 'recoil';
import debounce from 'lodash/debounce';
import { QueryKeys } from 'librechat-data-provider';
import { useQueryClient } from '@tanstack/react-query';
import { forwardRef, useState, useCallback, useMemo, Ref } from 'react';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
import store from '~/store';
@ -12,6 +14,8 @@ type SearchBarProps = {
const SearchBar = forwardRef((props: SearchBarProps, ref: Ref<HTMLDivElement>) => {
const { clearSearch } = props;
const queryClient = useQueryClient();
const clearConvoState = store.useClearConvoState();
const setSearchQuery = useSetRecoilState(store.searchQuery);
const [showClearIcon, setShowClearIcon] = useState(false);
const [text, setText] = useState('');
@ -31,7 +35,17 @@ const SearchBar = forwardRef((props: SearchBarProps, ref: Ref<HTMLDivElement>) =
}
};
const sendRequest = useCallback((value: string) => setSearchQuery(value), [setSearchQuery]);
const sendRequest = useCallback(
(value: string) => {
setSearchQuery(value);
if (!value) {
return;
}
queryClient.invalidateQueries([QueryKeys.messages]);
clearConvoState();
},
[queryClient, clearConvoState, setSearchQuery],
);
const debouncedSendRequest = useMemo(() => debounce(sendRequest, 350), [sendRequest]);
const onChange = (e: React.FormEvent<HTMLInputElement>) => {

View file

@ -1,3 +1,4 @@
export { default as useSearch } from './useSearch';
export { default as usePresets } from './usePresets';
export { default as useGetSender } from './useGetSender';
export { default as useDefaultConvo } from './useDefaultConvo';

View file

@ -1,6 +1,6 @@
import { useQueryClient } from '@tanstack/react-query';
import { useSetRecoilState, useResetRecoilState } from 'recoil';
import { QueryKeys } from 'librechat-data-provider';
import { QueryKeys, EModelEndpoint, LocalStorageKeys } from 'librechat-data-provider';
import type { TConversation, TEndpointsConfig, TModelsConfig } from 'librechat-data-provider';
import { buildDefaultConvo, getDefaultEndpoint, getEndpointField } from '~/utils';
import useOriginNavigate from '../useOriginNavigate';
@ -51,8 +51,28 @@ const useNavigateToConvo = (index = 0) => {
navigate(convo?.conversationId);
};
const navigateWithLastTools = (conversation: TConversation) => {
// set conversation to the new conversation
if (conversation?.endpoint === EModelEndpoint.gptPlugins) {
let lastSelectedTools = [];
try {
lastSelectedTools =
JSON.parse(localStorage.getItem(LocalStorageKeys.LAST_TOOLS) ?? '') ?? [];
} catch (e) {
// console.error(e);
}
navigateToConvo({
...conversation,
tools: conversation?.tools?.length ? conversation?.tools : lastSelectedTools,
});
} else {
navigateToConvo(conversation);
}
};
return {
navigateToConvo,
navigateWithLastTools,
};
};

View file

@ -0,0 +1,69 @@
import { useEffect, useState, useCallback } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useNavigate, useLocation } from 'react-router-dom';
import { useGetSearchEnabledQuery } from 'librechat-data-provider/react-query';
import { useSearchInfiniteQuery } from '~/data-provider';
import useConversation from './useConversation';
import store from '~/store';
export default function useSearchMessages({ isAuthenticated }: { isAuthenticated: boolean }) {
const navigate = useNavigate();
const location = useLocation();
const [pageNumber, setPageNumber] = useState(1);
const { searchPlaceholderConversation } = useConversation();
const searchQuery = useRecoilValue(store.searchQuery);
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
const searchEnabledQuery = useGetSearchEnabledQuery({ enabled: isAuthenticated });
const searchQueryRes = useSearchInfiniteQuery(
{ pageNumber: pageNumber.toString(), searchQuery: searchQuery, isArchived: false },
{ enabled: isAuthenticated && !!searchQuery.length },
);
useEffect(() => {
if (searchQuery && searchQuery.length > 0) {
navigate('/search', { replace: true });
return;
}
if (location.pathname && location.pathname.includes('/c/')) {
return;
}
navigate('/c/new', { replace: true });
/* Disabled eslint rule because we don't want to run this effect when location changes */
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigate, searchQuery]);
useEffect(() => {
if (searchEnabledQuery.data) {
setIsSearchEnabled(searchEnabledQuery.data);
} else if (searchEnabledQuery.isError) {
console.error('Failed to get search enabled', searchEnabledQuery.error);
}
}, [
searchEnabledQuery.data,
searchEnabledQuery.error,
searchEnabledQuery.isError,
setIsSearchEnabled,
]);
const onSearchSuccess = useCallback(
() => searchPlaceholderConversation(),
[searchPlaceholderConversation],
);
useEffect(() => {
//we use isInitialLoading here instead of isLoading because query is disabled by default
if (searchQueryRes.data) {
onSearchSuccess();
}
}, [searchQueryRes.data, searchQueryRes.isInitialLoading, onSearchSuccess]);
return {
pageNumber,
searchQuery,
setPageNumber,
searchQueryRes,
};
}

View file

@ -280,6 +280,24 @@ export default {
com_nav_setting_general: 'عام',
com_nav_setting_data: 'تحكم في البيانات',
/* The following are AI translated */
com_ui_date_today: 'اليوم',
com_ui_date_yesterday: 'أمس',
com_ui_date_previous_7_days: 'الأيام السبعة السابقة',
com_ui_date_previous_30_days: 'الـ 30 يومًا السابقة',
com_ui_date_january: 'يناير',
com_ui_date_february: 'فبراير',
com_ui_date_march: 'مارس',
com_ui_date_april: 'أبريل',
com_ui_date_may: 'مايو',
com_ui_date_june: 'يونيو',
com_ui_date_july: 'يوليو',
com_ui_date_august: 'أغسطس',
com_ui_date_september: 'سبتمبر',
com_ui_date_october: 'أكتوبر',
com_ui_date_november: 'نوفمبر',
com_ui_date_december: 'ديسمبر',
com_ui_nothing_found: 'لم يتم العثور على أي شيء',
com_ui_go_to_conversation: 'انتقل إلى المحادثة',
com_error_moderation:
'يبدو أن المحتوى المقدم قد تم وضع علامة عليه من قبل نظام الرقابة لدينا لعدم توافقه مع إرشادات مجتمعنا. لا نستطيع المضي قدمًا في هذا الموضوع المحدد. إذا كانت لديك أسئلة أخرى أو مواضيع ترغب في استكشافها، يرجى تحرير رسالتك، أو إنشاء محادثة جديدة.',
com_error_no_user_key: 'لم يتم العثور على مفتاح. يرجى تقديم مفتاح والمحاولة مرة أخرى.',
@ -1534,6 +1552,78 @@ export const comparisons = {
english: 'Data controls',
translated: 'تحكم في البيانات',
},
com_ui_date_today: {
english: 'Today',
translated: 'اليوم',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: 'أمس',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: 'الأيام السبعة السابقة',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: 'الـ 30 يومًا السابقة',
},
com_ui_date_january: {
english: 'January',
translated: 'يناير',
},
com_ui_date_february: {
english: 'February',
translated: 'فبراير',
},
com_ui_date_march: {
english: 'March',
translated: 'مارس',
},
com_ui_date_april: {
english: 'April',
translated: 'أبريل',
},
com_ui_date_may: {
english: 'May',
translated: 'مايو',
},
com_ui_date_june: {
english: 'June',
translated: 'يونيو',
},
com_ui_date_july: {
english: 'July',
translated: 'يوليو',
},
com_ui_date_august: {
english: 'August',
translated: 'أغسطس',
},
com_ui_date_september: {
english: 'September',
translated: 'سبتمبر',
},
com_ui_date_october: {
english: 'October',
translated: 'أكتوبر',
},
com_ui_date_november: {
english: 'November',
translated: 'نوفمبر',
},
com_ui_date_december: {
english: 'December',
translated: 'ديسمبر',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: 'لم يتم العثور على أي شيء',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: 'انتقل إلى المحادثة',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -464,6 +464,24 @@ export default {
com_nav_setting_account: 'Konto',
com_nav_language: 'Sprache',
/* The following are AI Translated */
com_ui_date_today: 'Heute',
com_ui_date_yesterday: 'Gestern',
com_ui_date_previous_7_days: 'Letzte 7 Tage',
com_ui_date_previous_30_days: 'Letzte 30 Tage',
com_ui_date_january: 'Januar',
com_ui_date_february: 'Februar',
com_ui_date_march: 'März',
com_ui_date_april: 'April',
com_ui_date_may: 'Mai',
com_ui_date_june: 'Juni',
com_ui_date_july: 'Juli',
com_ui_date_august: 'August',
com_ui_date_september: 'September',
com_ui_date_october: 'Oktober',
com_ui_date_november: 'November',
com_ui_date_december: 'Dezember',
com_ui_nothing_found: 'Keine Ergebnisse gefunden',
com_ui_go_to_conversation: 'Zum Chat wechseln',
com_error_moderation:
'Es sieht so aus, als ob der übermittelte Inhalt von unserem Moderationssystem als nicht konform mit unseren Gemeinschaftsrichtlinien markiert wurde. Wir können mit diesem spezifischen Thema leider nicht fortfahren. Wenn du andere Fragen oder Themen hast, die du gerne erörtern möchtest, bearbeite bitte deine Nachricht oder starte eine neue Konversation.',
com_error_no_user_key:
@ -2203,6 +2221,78 @@ export const comparisons = {
english: 'Language',
translated: 'Sprache',
},
com_ui_date_today: {
english: 'Today',
translated: 'Heute',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: 'Gestern',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: 'Letzte 7 Tage',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: 'Letzte 30 Tage',
},
com_ui_date_january: {
english: 'January',
translated: 'Januar',
},
com_ui_date_february: {
english: 'February',
translated: 'Februar',
},
com_ui_date_march: {
english: 'March',
translated: 'März',
},
com_ui_date_april: {
english: 'April',
translated: 'April',
},
com_ui_date_may: {
english: 'May',
translated: 'Mai',
},
com_ui_date_june: {
english: 'June',
translated: 'Juni',
},
com_ui_date_july: {
english: 'July',
translated: 'Juli',
},
com_ui_date_august: {
english: 'August',
translated: 'August',
},
com_ui_date_september: {
english: 'September',
translated: 'September',
},
com_ui_date_october: {
english: 'October',
translated: 'Oktober',
},
com_ui_date_november: {
english: 'November',
translated: 'November',
},
com_ui_date_december: {
english: 'December',
translated: 'Dezember',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: 'Keine Ergebnisse gefunden',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: 'Zum Chat wechseln',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -54,6 +54,22 @@ export default {
com_assistants_update_error: 'There was an error updating your assistant.',
com_assistants_create_success: 'Successfully created',
com_assistants_create_error: 'There was an error creating your assistant.',
com_ui_date_today: 'Today',
com_ui_date_yesterday: 'Yesterday',
com_ui_date_previous_7_days: 'Previous 7 days',
com_ui_date_previous_30_days: 'Previous 30 days',
com_ui_date_january: 'January',
com_ui_date_february: 'February',
com_ui_date_march: 'March',
com_ui_date_april: 'April',
com_ui_date_may: 'May',
com_ui_date_june: 'June',
com_ui_date_july: 'July',
com_ui_date_august: 'August',
com_ui_date_september: 'September',
com_ui_date_october: 'October',
com_ui_date_november: 'November',
com_ui_date_december: 'December',
com_ui_field_required: 'This field is required',
com_ui_download_error: 'Error downloading file. The file may have been deleted.',
com_ui_attach_error_type: 'Unsupported file type for endpoint:',
@ -165,6 +181,8 @@ export default {
com_ui_revoke: 'Revoke',
com_ui_revoke_info: 'Revoke all user provided credentials',
com_ui_import_conversation: 'Import',
com_ui_nothing_found: 'Nothing found',
com_ui_go_to_conversation: 'Go to conversation',
com_ui_import_conversation_info: 'Import conversations from a JSON file',
com_ui_import_conversation_success: 'Conversations imported successfully',
com_ui_import_conversation_error: 'There was an error importing your conversations',

View file

@ -458,6 +458,24 @@ export default {
com_nav_lang_auto: 'Detección automática',
com_nav_lang_spanish: 'Español',
/* The following are AI Translated */
com_ui_date_today: 'Hoy',
com_ui_date_yesterday: 'Ayer',
com_ui_date_previous_7_days: 'Últimos 7 días',
com_ui_date_previous_30_days: 'Últimos 30 días',
com_ui_date_january: 'Enero',
com_ui_date_february: 'Febrero',
com_ui_date_march: 'Marzo',
com_ui_date_april: 'Abril',
com_ui_date_may: 'Mayo',
com_ui_date_june: 'Junio',
com_ui_date_july: 'Julio',
com_ui_date_august: 'Agosto',
com_ui_date_september: 'Septiembre',
com_ui_date_october: 'Octubre',
com_ui_date_november: 'Noviembre',
com_ui_date_december: 'Diciembre',
com_ui_nothing_found: 'No se encontró nada',
com_ui_go_to_conversation: 'Ir a la conversación',
com_error_moderation:
'Parece que el contenido enviado ha sido marcado por nuestro sistema de moderación por no estar alineado con nuestras pautas comunitarias. No podemos proceder con este tema específico. Si tiene alguna otra pregunta o tema que le gustaría explorar, por favor edite su mensaje o cree una nueva conversación.',
com_error_no_user_key:
@ -2184,6 +2202,78 @@ export const comparisons = {
english: 'Español',
translated: 'Español',
},
com_ui_date_today: {
english: 'Today',
translated: 'Hoy',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: 'Ayer',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: 'Últimos 7 días',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: 'Últimos 30 días',
},
com_ui_date_january: {
english: 'January',
translated: 'Enero',
},
com_ui_date_february: {
english: 'February',
translated: 'Febrero',
},
com_ui_date_march: {
english: 'March',
translated: 'Marzo',
},
com_ui_date_april: {
english: 'April',
translated: 'Abril',
},
com_ui_date_may: {
english: 'May',
translated: 'Mayo',
},
com_ui_date_june: {
english: 'June',
translated: 'Junio',
},
com_ui_date_july: {
english: 'July',
translated: 'Julio',
},
com_ui_date_august: {
english: 'August',
translated: 'Agosto',
},
com_ui_date_september: {
english: 'September',
translated: 'Septiembre',
},
com_ui_date_october: {
english: 'October',
translated: 'Octubre',
},
com_ui_date_november: {
english: 'November',
translated: 'Noviembre',
},
com_ui_date_december: {
english: 'December',
translated: 'Diciembre',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: 'No se encontró nada',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: 'Ir a la conversación',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -347,6 +347,55 @@ export default {
com_nav_setting_data: 'Contrôles des données',
com_nav_setting_account: 'Compte',
/* The following are AI Translated */
com_ui_date_today: 'Aujourd\'hui',
com_ui_date_yesterday: 'Hier',
com_ui_date_previous_7_days: '7 derniers jours',
com_ui_date_previous_30_days: '30 derniers jours',
com_ui_date_january: 'Janvier',
com_ui_date_february: 'Février',
com_ui_date_march: 'Mars',
com_ui_date_april: 'Avril',
com_ui_date_may: 'Mai',
com_ui_date_june: 'Juin',
com_ui_date_july: 'Juillet',
com_ui_date_august: 'Août',
com_ui_date_september: 'Septembre',
com_ui_date_october: 'Octobre',
com_ui_date_november: 'Novembre',
com_ui_date_december: 'Décembre',
com_ui_nothing_found: 'Aucun résultat trouvé',
com_ui_go_to_conversation: 'Aller à la conversation',
com_nav_tool_add: 'Ajouter',
com_nav_tool_remove: 'Supprimer',
com_nav_tool_dialog: 'Outils de l\'assistant',
com_nav_tool_dialog_description:
'L\'assistant doit être sauvegardé pour conserver les sélections d\'outils.',
com_nav_tool_search: 'Outils de recherche',
com_nav_my_files: 'Mes fichiers',
com_nav_enter_to_send: 'Appuyez sur Entrée pour envoyer des messages',
com_nav_show_code: 'Toujours afficher le code lors de l\'utilisation de l\'interpréteur de code',
com_nav_archived_chats_empty: 'Vous n\'avez aucune conversation archivée.',
com_nav_language: 'Langue',
com_nav_lang_auto: 'Détection automatique',
com_nav_lang_english: 'Anglais',
com_nav_lang_chinese: 'Chinois',
com_nav_lang_german: 'Allemand',
com_nav_lang_spanish: 'Espagnol',
com_nav_lang_french: 'Français',
com_nav_lang_italian: 'Italien',
com_nav_lang_polish: 'Polonais',
com_nav_lang_brazilian_portuguese: 'Portugais brésilien',
com_nav_lang_russian: 'Russe',
com_nav_lang_japanese: 'Japonais',
com_nav_lang_swedish: 'Suédois',
com_nav_lang_korean: 'Coréen',
com_nav_lang_vietnamese: 'Vietnamien',
com_nav_lang_traditionalchinese: 'Chinois traditionnel',
com_nav_lang_arabic: 'Arabe',
com_nav_lang_turkish: 'Turc',
com_nav_lang_dutch: 'Néerlandais',
com_nav_lang_indonesia: 'Indonésie',
com_nav_lang_hebrew: 'Hébreu',
com_error_moderation:
'Il semble que le contenu soumis ait été signalé par notre système de modération pour ne pas être conforme à nos lignes directrices communautaires. Nous ne pouvons pas procéder avec ce sujet spécifique. Si vous avez d\'autres questions ou sujets que vous souhaitez explorer, veuillez modifier votre message ou créer une nouvelle conversation.',
com_error_no_user_key: 'Aucune clé trouvée. Veuillez fournir une clé et réessayer.',
@ -1730,6 +1779,198 @@ export const comparisons = {
english: 'Account',
translated: 'Compte',
},
com_ui_date_today: {
english: 'Today',
translated: 'Aujourd\'hui',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: 'Hier',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: '7 derniers jours',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: '30 derniers jours',
},
com_ui_date_january: {
english: 'January',
translated: 'Janvier',
},
com_ui_date_february: {
english: 'February',
translated: 'Février',
},
com_ui_date_march: {
english: 'March',
translated: 'Mars',
},
com_ui_date_april: {
english: 'April',
translated: 'Avril',
},
com_ui_date_may: {
english: 'May',
translated: 'Mai',
},
com_ui_date_june: {
english: 'June',
translated: 'Juin',
},
com_ui_date_july: {
english: 'July',
translated: 'Juillet',
},
com_ui_date_august: {
english: 'August',
translated: 'Août',
},
com_ui_date_september: {
english: 'September',
translated: 'Septembre',
},
com_ui_date_october: {
english: 'October',
translated: 'Octobre',
},
com_ui_date_november: {
english: 'November',
translated: 'Novembre',
},
com_ui_date_december: {
english: 'December',
translated: 'Décembre',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: 'Aucun résultat trouvé',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: 'Aller à la conversation',
},
com_nav_tool_add: {
english: 'Add',
translated: 'Ajouter',
},
com_nav_tool_remove: {
english: 'Remove',
translated: 'Supprimer',
},
com_nav_tool_dialog: {
english: 'Assistant Tools',
translated: 'Outils de l\'assistant',
},
com_nav_tool_dialog_description: {
english: 'Assistant must be saved to persist tool selections.',
translated: 'L\'assistant doit être sauvegardé pour conserver les sélections d\'outils.',
},
com_nav_tool_search: {
english: 'Search tools',
translated: 'Outils de recherche',
},
com_nav_my_files: {
english: 'My Files',
translated: 'Mes fichiers',
},
com_nav_enter_to_send: {
english: 'Press Enter to send messages',
translated: 'Appuyez sur Entrée pour envoyer des messages',
},
com_nav_show_code: {
english: 'Always show code when using code interpreter',
translated: 'Toujours afficher le code lors de l\'utilisation de l\'interpréteur de code',
},
com_nav_archived_chats_empty: {
english: 'You have no archived conversations.',
translated: 'Vous n\'avez aucune conversation archivée.',
},
com_nav_language: {
english: 'Language',
translated: 'Langue',
},
com_nav_lang_auto: {
english: 'Auto detect',
translated: 'Détection automatique',
},
com_nav_lang_english: {
english: 'English',
translated: 'Anglais',
},
com_nav_lang_chinese: {
english: '中文',
translated: 'Chinois',
},
com_nav_lang_german: {
english: 'Deutsch',
translated: 'Allemand',
},
com_nav_lang_spanish: {
english: 'Español',
translated: 'Espagnol',
},
com_nav_lang_french: {
english: 'Français ',
translated: 'Français',
},
com_nav_lang_italian: {
english: 'Italiano',
translated: 'Italien',
},
com_nav_lang_polish: {
english: 'Polski',
translated: 'Polonais',
},
com_nav_lang_brazilian_portuguese: {
english: 'Português Brasileiro',
translated: 'Portugais brésilien',
},
com_nav_lang_russian: {
english: 'Русский',
translated: 'Russe',
},
com_nav_lang_japanese: {
english: '日本語',
translated: 'Japonais',
},
com_nav_lang_swedish: {
english: 'Svenska',
translated: 'Suédois',
},
com_nav_lang_korean: {
english: '한국어',
translated: 'Coréen',
},
com_nav_lang_vietnamese: {
english: 'Tiếng Việt',
translated: 'Vietnamien',
},
com_nav_lang_traditionalchinese: {
english: '繁體中文',
translated: 'Chinois traditionnel',
},
com_nav_lang_arabic: {
english: 'العربية',
translated: 'Arabe',
},
com_nav_lang_turkish: {
english: 'Türkçe',
translated: 'Turc',
},
com_nav_lang_dutch: {
english: 'Nederlands',
translated: 'Néerlandais',
},
com_nav_lang_indonesia: {
english: 'Indonesia',
translated: 'Indonésie',
},
com_nav_lang_hebrew: {
english: 'עברית',
translated: 'Hébreu',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -508,6 +508,24 @@ export default {
com_nav_setting_data: 'Controlli dati',
com_nav_setting_account: 'Account',
/* The following are AI Translated */
com_ui_date_today: 'Oggi',
com_ui_date_yesterday: 'Ieri',
com_ui_date_previous_7_days: 'Ultimi 7 giorni',
com_ui_date_previous_30_days: 'Ultimi 30 giorni',
com_ui_date_january: 'Gennaio',
com_ui_date_february: 'Febbraio',
com_ui_date_march: 'Marzo',
com_ui_date_april: 'Aprile',
com_ui_date_may: 'Maggio',
com_ui_date_june: 'Giugno',
com_ui_date_july: 'Luglio',
com_ui_date_august: 'Agosto',
com_ui_date_september: 'Settembre',
com_ui_date_october: 'Ottobre',
com_ui_date_november: 'Novembre',
com_ui_date_december: 'Dicembre',
com_ui_nothing_found: 'Non è stato trovato nulla',
com_ui_go_to_conversation: 'Vai alla conversazione',
com_user_message: 'Mostra nome utente nei messaggi',
com_ui_fork: 'Duplica',
com_ui_mention: 'Menziona un endpoint, assistente o preset per passare rapidamente ad esso',
@ -2341,6 +2359,78 @@ export const comparisons = {
english: 'Account',
translated: 'Account',
},
com_ui_date_today: {
english: 'Today',
translated: 'Oggi',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: 'Ieri',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: 'Ultimi 7 giorni',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: 'Ultimi 30 giorni',
},
com_ui_date_january: {
english: 'January',
translated: 'Gennaio',
},
com_ui_date_february: {
english: 'February',
translated: 'Febbraio',
},
com_ui_date_march: {
english: 'March',
translated: 'Marzo',
},
com_ui_date_april: {
english: 'April',
translated: 'Aprile',
},
com_ui_date_may: {
english: 'May',
translated: 'Maggio',
},
com_ui_date_june: {
english: 'June',
translated: 'Giugno',
},
com_ui_date_july: {
english: 'July',
translated: 'Luglio',
},
com_ui_date_august: {
english: 'August',
translated: 'Agosto',
},
com_ui_date_september: {
english: 'September',
translated: 'Settembre',
},
com_ui_date_october: {
english: 'October',
translated: 'Ottobre',
},
com_ui_date_november: {
english: 'November',
translated: 'Novembre',
},
com_ui_date_december: {
english: 'December',
translated: 'Dicembre',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: 'Non è stato trovato nulla',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: 'Vai alla conversazione',
},
com_user_message: {
english: 'You',
translated: 'Mostra nome utente nei messaggi',

View file

@ -455,6 +455,24 @@ export default {
com_nav_setting_data: 'データ管理',
com_nav_setting_account: 'アカウント',
/* The following are AI translated */
com_ui_date_today: '今日',
com_ui_date_yesterday: '昨日',
com_ui_date_previous_7_days: '過去7日間',
com_ui_date_previous_30_days: '過去30日間',
com_ui_date_january: '1月',
com_ui_date_february: '2月',
com_ui_date_march: '3月',
com_ui_date_april: '4月',
com_ui_date_may: '5月',
com_ui_date_june: '6月',
com_ui_date_july: '7月',
com_ui_date_august: '8月',
com_ui_date_september: '9月',
com_ui_date_october: '10月',
com_ui_date_november: '11月',
com_ui_date_december: '12月',
com_ui_nothing_found: '該当するものが見つかりませんでした',
com_ui_go_to_conversation: '会話に移動する',
com_error_invalid_user_key: '無効なキーが提供されました。キーを入力して再試行してください。',
com_ui_none_selected: '選択されていません',
com_ui_fork: '分岐',
@ -2193,6 +2211,78 @@ export const comparisons = {
english: 'Account',
translated: 'アカウント',
},
com_ui_date_today: {
english: 'Today',
translated: '今日',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: '昨日',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: '過去7日間',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: '過去30日間',
},
com_ui_date_january: {
english: 'January',
translated: '1月',
},
com_ui_date_february: {
english: 'February',
translated: '2月',
},
com_ui_date_march: {
english: 'March',
translated: '3月',
},
com_ui_date_april: {
english: 'April',
translated: '4月',
},
com_ui_date_may: {
english: 'May',
translated: '5月',
},
com_ui_date_june: {
english: 'June',
translated: '6月',
},
com_ui_date_july: {
english: 'July',
translated: '7月',
},
com_ui_date_august: {
english: 'August',
translated: '8月',
},
com_ui_date_september: {
english: 'September',
translated: '9月',
},
com_ui_date_october: {
english: 'October',
translated: '10月',
},
com_ui_date_november: {
english: 'November',
translated: '11月',
},
com_ui_date_december: {
english: 'December',
translated: '12月',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: '該当するものが見つかりませんでした',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: '会話に移動する',
},
com_error_invalid_user_key: {
english: 'Invalid key provided. Please provide a key and try again.',
translated: '無効なキーが提供されました。キーを入力して再試行してください。',

File diff suppressed because it is too large Load diff

View file

@ -364,6 +364,24 @@ export default {
com_ui_upload_error: 'Произошла ошибка при загрузке вашего файла',
com_user_message: 'Вы',
/* The following are AI Translated */
com_ui_date_today: 'Сегодня',
com_ui_date_yesterday: 'Вчера',
com_ui_date_previous_7_days: 'Предыдущие 7 дней',
com_ui_date_previous_30_days: 'За последние 30 дней',
com_ui_date_january: 'Январь',
com_ui_date_february: 'Февраль',
com_ui_date_march: 'Март',
com_ui_date_april: 'Апрель',
com_ui_date_may: 'Май',
com_ui_date_june: 'Июнь',
com_ui_date_july: 'Июль',
com_ui_date_august: 'Август',
com_ui_date_september: 'Сентябрь',
com_ui_date_october: 'Октябрь',
com_ui_date_november: 'Ноябрь',
com_ui_date_december: 'Декабрь',
com_ui_nothing_found: 'Ничего не найдено',
com_ui_go_to_conversation: 'Перейти к беседе',
com_error_moderation:
'К сожалению, отправленный вами контент был помечен нашей системой модерации как не соответствующий правилам сообщества. Мы не можем продолжить обсуждение этой конкретной темы. Если у вас есть другие вопросы или темы, которые вы хотели бы обсудить, пожалуйста, отредактируйте сообщение или начните новый диалог.',
com_error_no_user_key: 'Ключ не найден. Пожалуйста, укажите ключ и повторите попытку.',
@ -1846,6 +1864,78 @@ export const comparisons = {
english: 'You',
translated: 'Вы',
},
com_ui_date_today: {
english: 'Today',
translated: 'Сегодня',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: 'Вчера',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: 'Предыдущие 7 дней',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: 'За последние 30 дней',
},
com_ui_date_january: {
english: 'January',
translated: 'Январь',
},
com_ui_date_february: {
english: 'February',
translated: 'Февраль',
},
com_ui_date_march: {
english: 'March',
translated: 'Март',
},
com_ui_date_april: {
english: 'April',
translated: 'Апрель',
},
com_ui_date_may: {
english: 'May',
translated: 'Май',
},
com_ui_date_june: {
english: 'June',
translated: 'Июнь',
},
com_ui_date_july: {
english: 'July',
translated: 'Июль',
},
com_ui_date_august: {
english: 'August',
translated: 'Август',
},
com_ui_date_september: {
english: 'September',
translated: 'Сентябрь',
},
com_ui_date_october: {
english: 'October',
translated: 'Октябрь',
},
com_ui_date_november: {
english: 'November',
translated: 'Ноябрь',
},
com_ui_date_december: {
english: 'December',
translated: 'Декабрь',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: 'Ничего не найдено',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: 'Перейти к беседе',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -417,6 +417,21 @@ export default {
com_nav_setting_data: '数据管理',
com_nav_setting_account: '账户',
/* The following are AI Translated */
com_ui_date_today: '今天',
com_ui_date_yesterday: '昨天',
com_ui_date_previous_7_days: '过去7天',
com_ui_date_previous_30_days: '过去30天',
com_ui_date_january: '一月',
com_ui_date_february: '二月',
com_ui_date_march: '三月',
com_ui_date_april: '四月',
com_ui_date_may: '五月',
com_ui_date_june: '六月',
com_ui_date_july: '七月',
com_ui_date_august: '八月',
com_ui_date_september: '九月',
com_ui_nothing_found: '未找到任何内容',
com_ui_go_to_conversation: '转到对话',
com_error_moderation:
'很抱歉,您提交的内容被我们的审核系统标记为不符合社区指引。我们无法就此特定主题继续交流。如果您有任何其他问题或想探讨的话题,请编辑您的消息或开启新的对话。',
com_error_no_user_key: '没有找到密钥。请提供密钥后重试。',
@ -2096,6 +2111,66 @@ export const comparisons = {
english: 'Account',
translated: '账户',
},
com_ui_date_today: {
english: 'Today',
translated: '今天',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: '昨天',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: '过去7天',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: '过去30天',
},
com_ui_date_january: {
english: 'January',
translated: '一月',
},
com_ui_date_february: {
english: 'February',
translated: '二月',
},
com_ui_date_march: {
english: 'March',
translated: '三月',
},
com_ui_date_april: {
english: 'April',
translated: '四月',
},
com_ui_date_may: {
english: 'May',
translated: '五月',
},
com_ui_date_june: {
english: 'June',
translated: '六月',
},
com_ui_date_july: {
english: 'July',
translated: '七月',
},
com_ui_date_august: {
english: 'August',
translated: '八月',
},
com_ui_date_september: {
english: 'September',
translated: '九月',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: '未找到任何内容',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: '转到对话',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -266,6 +266,24 @@ export default {
com_nav_setting_general: '一般',
com_nav_setting_data: '資料控制',
/* The following are AI translated */
com_ui_date_today: '今天',
com_ui_date_yesterday: '昨天',
com_ui_date_previous_7_days: '前 7 天',
com_ui_date_previous_30_days: '過去 30 天',
com_ui_date_january: '一月',
com_ui_date_february: '二月',
com_ui_date_march: '三月',
com_ui_date_april: '四月',
com_ui_date_may: '五月',
com_ui_date_june: '六月',
com_ui_date_july: '七月',
com_ui_date_august: '八月',
com_ui_date_september: '九月',
com_ui_date_october: '十月',
com_ui_date_november: '十一月',
com_ui_date_december: '十二月',
com_ui_nothing_found: '找不到任何內容',
com_ui_go_to_conversation: '前往對話',
com_error_moderation:
'似乎您所提交的內容被我們的內容審查系統標記為不符合社群準則。我們無法就此特定主題繼續進行。如果您有任何其他問題或想要探討的主題,請編輯您的訊息或開啟新的對話。',
com_error_no_user_key: '找不到金鑰,請提供金鑰後再試一次。',
@ -1509,6 +1527,78 @@ export const comparisons = {
english: 'Data controls',
translated: '資料控制',
},
com_ui_date_today: {
english: 'Today',
translated: '今天',
},
com_ui_date_yesterday: {
english: 'Yesterday',
translated: '昨天',
},
com_ui_date_previous_7_days: {
english: 'Previous 7 days',
translated: '前 7 天',
},
com_ui_date_previous_30_days: {
english: 'Previous 30 days',
translated: '過去 30 天',
},
com_ui_date_january: {
english: 'January',
translated: '一月',
},
com_ui_date_february: {
english: 'February',
translated: '二月',
},
com_ui_date_march: {
english: 'March',
translated: '三月',
},
com_ui_date_april: {
english: 'April',
translated: '四月',
},
com_ui_date_may: {
english: 'May',
translated: '五月',
},
com_ui_date_june: {
english: 'June',
translated: '六月',
},
com_ui_date_july: {
english: 'July',
translated: '七月',
},
com_ui_date_august: {
english: 'August',
translated: '八月',
},
com_ui_date_september: {
english: 'September',
translated: '九月',
},
com_ui_date_october: {
english: 'October',
translated: '十月',
},
com_ui_date_november: {
english: 'November',
translated: '十一月',
},
com_ui_date_december: {
english: 'December',
translated: '十二月',
},
com_ui_nothing_found: {
english: 'Nothing found',
translated: '找不到任何內容',
},
com_ui_go_to_conversation: {
english: 'Go to conversation',
translated: '前往對話',
},
com_error_moderation: {
english:
'It appears that the content submitted has been flagged by our moderation system for not aligning with our community guidelines. We\'re unable to proceed with this specific topic. If you have any other questions or topics you\'d like to explore, please edit your message, or create a new conversation.',

View file

@ -1,12 +1,10 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { Outlet } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { useGetSearchEnabledQuery } from 'librechat-data-provider/react-query';
import type { ContextType } from '~/common';
import { useAuthContext, useAssistantsMap, useFileMap } from '~/hooks';
import { AssistantsMapContext, FileMapContext } from '~/Providers';
import { useAuthContext, useAssistantsMap, useFileMap, useSearch } from '~/hooks';
import { AssistantsMapContext, FileMapContext, SearchContext } from '~/Providers';
import { Nav, MobileNav } from '~/components/Nav';
import store from '~/store';
export default function Root() {
const { isAuthenticated } = useAuthContext();
@ -15,42 +13,29 @@ export default function Root() {
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : true;
});
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
const search = useSearch({ isAuthenticated });
const fileMap = useFileMap({ isAuthenticated });
const assistantsMap = useAssistantsMap({ isAuthenticated });
const searchEnabledQuery = useGetSearchEnabledQuery({ enabled: isAuthenticated });
useEffect(() => {
if (searchEnabledQuery.data) {
setIsSearchEnabled(searchEnabledQuery.data);
} else if (searchEnabledQuery.isError) {
console.error('Failed to get search enabled', searchEnabledQuery.error);
}
}, [
searchEnabledQuery.data,
searchEnabledQuery.error,
searchEnabledQuery.isError,
setIsSearchEnabled,
]);
if (!isAuthenticated) {
return null;
}
return (
<FileMapContext.Provider value={fileMap}>
<AssistantsMapContext.Provider value={assistantsMap}>
<div className="flex h-dvh">
<div className="relative z-0 flex h-full w-full overflow-hidden">
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
<div className="relative flex h-full max-w-full flex-1 flex-col overflow-hidden">
<MobileNav setNavVisible={setNavVisible} />
<Outlet context={{ navVisible, setNavVisible } satisfies ContextType} />
<SearchContext.Provider value={search}>
<FileMapContext.Provider value={fileMap}>
<AssistantsMapContext.Provider value={assistantsMap}>
<div className="flex h-dvh">
<div className="relative z-0 flex h-full w-full overflow-hidden">
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
<div className="relative flex h-full max-w-full flex-1 flex-col overflow-hidden">
<MobileNav setNavVisible={setNavVisible} />
<Outlet context={{ navVisible, setNavVisible } satisfies ContextType} />
</div>
</div>
</div>
</div>
</AssistantsMapContext.Provider>
</FileMapContext.Provider>
</AssistantsMapContext.Provider>
</FileMapContext.Provider>
</SearchContext.Provider>
);
}

View file

@ -1,58 +1,42 @@
import React, { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
// import TextChat from '~/components/Input/TextChat';
import { useConversation } from '~/hooks';
import store from '~/store';
import { useMemo } from 'react';
import MinimalMessagesWrapper from '~/components/Chat/Messages/MinimalMessages';
import SearchMessage from '~/components/Chat/Messages/SearchMessage';
import { useSearchContext, useFileMapContext } from '~/Providers';
import { useNavScrolling, useLocalize } from '~/hooks';
import { buildTree } from '~/utils';
export default function Search() {
const [searchQuery, setSearchQuery] = useRecoilState(store.searchQuery);
const conversation = useRecoilValue(store.conversation);
const { searchPlaceholderConversation } = useConversation();
const { query } = useParams();
const navigate = useNavigate();
const localize = useLocalize();
const fileMap = useFileMapContext();
const { searchQuery, searchQueryRes } = useSearchContext();
// when conversation changed or conversationId (in url) changed
useEffect(() => {
if (conversation === null) {
// no current conversation, we need to do something
if (query) {
// create new
searchPlaceholderConversation();
setSearchQuery(query);
} else {
navigate('/c/new');
}
} else if (conversation?.conversationId === 'search') {
// jump to search page
if (searchQuery !== query) {
navigate(`/search/${searchQuery}`);
}
} else {
// conversationId (in url) should always follow conversation?.conversationId, unless conversation is null
navigate(`/chat/${conversation?.conversationId}`);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [conversation, query, searchQuery]);
const { containerRef } = useNavScrolling({
setShowLoading: () => ({}),
hasNextPage: searchQueryRes?.hasNextPage,
fetchNextPage: searchQueryRes?.fetchNextPage,
isFetchingNextPage: searchQueryRes?.isFetchingNextPage,
});
// if not a search
if (conversation?.conversationId !== 'search') {
return null;
}
// if query not match
if (searchQuery !== query) {
return null;
}
// if query is null
if (!query) {
const messages = useMemo(() => {
const msgs = searchQueryRes?.data?.pages.flatMap((page) => page.messages) || [];
const dataTree = buildTree({ messages: msgs, fileMap });
return dataTree?.length === 0 ? null : dataTree ?? null;
}, [fileMap, searchQueryRes?.data?.pages]);
if (!searchQuery || !searchQueryRes?.data) {
return null;
}
return (
<>
{/* <Messages isSearchView={true} /> */}
{/* <TextChat isSearchView={true} /> */}
</>
<MinimalMessagesWrapper ref={containerRef} className="pt-4">
{(messages && messages?.length == 0) || messages === null ? (
<div className="my-auto flex h-full w-full items-center justify-center gap-1 bg-gray-50 p-3 text-lg text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300">
{localize('com_ui_nothing_found')}
</div>
) : (
messages?.map((message) => <SearchMessage key={message.messageId} message={message} />)
)}
<div className="absolute bottom-0 left-0 right-0 h-[5%] bg-gradient-to-t from-gray-800 to-transparent" />
</MinimalMessagesWrapper>
);
}

View file

@ -1,7 +1,4 @@
import { createBrowserRouter, Navigate, Outlet } from 'react-router-dom';
import Root from './Root';
import ChatRoute from './ChatRoute';
// import Search from './Search';
import {
Login,
Registration,
@ -10,6 +7,9 @@ import {
ApiErrorWatcher,
} from '~/components/Auth';
import { AuthContextProvider } from '~/hooks/AuthContext';
import ChatRoute from './ChatRoute';
import Search from './Search';
import Root from './Root';
const AuthLayout = () => (
<AuthContextProvider>
@ -50,10 +50,10 @@ export const router = createBrowserRouter([
path: 'c/:conversationId?',
element: <ChatRoute />,
},
// {
// path: 'search/:query?',
// element: <Search />,
// },
{
path: 'search',
element: <Search />,
},
],
},
],

View file

@ -5,6 +5,7 @@ import {
useRecoilState,
useRecoilValue,
useSetRecoilState,
useRecoilCallback,
} from 'recoil';
import { LocalStorageKeys } from 'librechat-data-provider';
import type { TMessage, TPreset, TConversation, TSubmission } from 'librechat-data-provider';
@ -32,6 +33,10 @@ const conversationByIndex = atomFamily<TConversation | null, string | number>({
);
}
if (!newValue) {
return;
}
storeEndpointSettings(newValue);
localStorage.setItem(LocalStorageKeys.LAST_CONVO_SETUP, JSON.stringify(newValue));
});
@ -131,6 +136,29 @@ function useCreateConversationAtom(key: string | number) {
return { conversation, setConversation };
}
function useClearConvoState() {
const clearAllConversations = useRecoilCallback(
({ reset, snapshot }) =>
async () => {
const conversationKeys = await snapshot.getPromise(conversationKeysAtom);
for (const conversationKey of conversationKeys) {
reset(conversationByIndex(conversationKey));
const conversation = await snapshot.getPromise(conversationByIndex(conversationKey));
if (conversation) {
reset(latestMessageFamily(conversationKey));
}
}
reset(conversationKeysAtom);
},
[],
);
return clearAllConversations;
}
export default {
conversationByIndex,
filesByIndex,
@ -146,6 +174,7 @@ export default {
showPopoverFamily,
latestMessageFamily,
allConversationsSelector,
useClearConvoState,
useCreateConversationAtom,
showMentionPopoverFamily,
};

View file

@ -1,6 +1,4 @@
import { TMessage } from 'librechat-data-provider';
import { atom, selector } from 'recoil';
import { buildTree } from '~/utils';
import { atom } from 'recoil';
const isSearchEnabled = atom<boolean | null>({
key: 'isSearchEnabled',
@ -12,30 +10,7 @@ const searchQuery = atom({
default: '',
});
const searchResultMessages = atom<TMessage[] | null>({
key: 'searchResultMessages',
default: null,
});
const searchResultMessagesTree = selector({
key: 'searchResultMessagesTree',
get: ({ get }) => {
return buildTree({ messages: get(searchResultMessages), groupAll: true });
},
});
const isSearching = selector({
key: 'isSearching',
get: ({ get }) => {
const data = get(searchQuery);
return !!data;
},
});
export default {
isSearchEnabled,
isSearching,
searchResultMessages,
searchResultMessagesTree,
searchQuery,
};

View file

@ -1,11 +1,12 @@
import { convoData } from './convos.fakeData';
import {
groupConversationsByDate,
dateKeys,
addConversation,
updateConversation,
updateConvoFields,
updateConversation,
deleteConversation,
findPageForConversation,
groupConversationsByDate,
} from './convos';
import type { TConversation, ConversationData } from 'librechat-data-provider';
@ -20,13 +21,13 @@ describe('Conversation Utilities', () => {
{ conversationId: '5', updatedAt: new Date(Date.now() - 86400000 * 8).toISOString() }, // 8 days ago (previous 30 days)
];
const grouped = groupConversationsByDate(conversations as TConversation[]);
expect(grouped[0][0]).toBe('Today');
expect(grouped[0][0]).toBe(dateKeys.today);
expect(grouped[0][1]).toHaveLength(1);
expect(grouped[1][0]).toBe('Yesterday');
expect(grouped[1][0]).toBe(dateKeys.yesterday);
expect(grouped[1][1]).toHaveLength(1);
expect(grouped[2][0]).toBe('Previous 7 days');
expect(grouped[2][0]).toBe(dateKeys.previous7Days);
expect(grouped[2][1]).toHaveLength(1);
expect(grouped[3][0]).toBe('Previous 30 days');
expect(grouped[3][0]).toBe(dateKeys.previous30Days);
expect(grouped[3][1]).toHaveLength(1);
expect(grouped[4][0]).toBe(' 2023');
expect(grouped[4][1]).toHaveLength(1);

View file

@ -16,22 +16,42 @@ import type {
GroupedConversations,
} from 'librechat-data-provider';
export const dateKeys = {
today: 'com_ui_date_today',
yesterday: 'com_ui_date_yesterday',
previous7Days: 'com_ui_date_previous_7_days',
previous30Days: 'com_ui_date_previous_30_days',
january: 'com_ui_date_january',
february: 'com_ui_date_february',
march: 'com_ui_date_march',
april: 'com_ui_date_april',
may: 'com_ui_date_may',
june: 'com_ui_date_june',
july: 'com_ui_date_july',
august: 'com_ui_date_august',
september: 'com_ui_date_september',
october: 'com_ui_date_october',
november: 'com_ui_date_november',
december: 'com_ui_date_december',
};
const getGroupName = (date: Date) => {
const now = new Date();
if (isToday(date)) {
return 'Today';
return dateKeys.today;
}
if (isWithinInterval(date, { start: startOfDay(subDays(now, 1)), end: now })) {
return 'Yesterday';
return dateKeys.yesterday;
}
if (isWithinInterval(date, { start: subDays(now, 7), end: now })) {
return 'Previous 7 days';
return dateKeys.previous7Days;
}
if (isWithinInterval(date, { start: subDays(now, 30), end: now })) {
return 'Previous 30 days';
return dateKeys.previous30Days;
}
if (isWithinInterval(date, { start: startOfYear(now), end: now })) {
return ' ' + format(date, 'MMMM');
const month = format(date, 'MMMM').toLowerCase();
return dateKeys[month];
}
return ' ' + getYear(date).toString();
};
@ -62,7 +82,12 @@ export const groupConversationsByDate = (conversations: TConversation[]): Groupe
}, {});
const sortedGroups = {};
const dateGroups = ['Today', 'Last 7 days', 'Last 30 days'];
const dateGroups = [
dateKeys.today,
dateKeys.yesterday,
dateKeys.previous7Days,
dateKeys.previous30Days,
];
dateGroups.forEach((group) => {
if (groups[group]) {
sortedGroups[group] = groups[group];

8
package-lock.json generated
View file

@ -97,7 +97,7 @@
"passport-local": "^1.0.0",
"pino": "^8.12.1",
"sharp": "^0.32.6",
"tiktoken": "^1.0.10",
"tiktoken": "^1.0.15",
"traverse": "^0.6.7",
"ua-parser-js": "^1.0.36",
"winston": "^3.11.0",
@ -26736,9 +26736,9 @@
}
},
"node_modules/tiktoken": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.13.tgz",
"integrity": "sha512-JaL9ZnvTbGFMDIBeGdVkLt4qWTeCPw+n7Ock+wceAGRenuHA6nOOvMJFliNDyXsjg2osGKJWsXtO2xc74VxyDw=="
"version": "1.0.15",
"resolved": "https://registry.npmjs.org/tiktoken/-/tiktoken-1.0.15.tgz",
"integrity": "sha512-sCsrq/vMWUSEW29CJLNmPvWxlVp7yh2tlkAjpJltIKqp5CKf98ZNpdeHRmAlPVFlGEbswDc6SmI8vz64W/qErw=="
},
"node_modules/timers-browserify": {
"version": "2.0.12",