mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 11:50:14 +01:00
🎉 feat: Optimizations and Anthropic Title Generation (#2184)
* feat: add claude-3-haiku-20240307 to default anthropic list * refactor: optimize `saveMessage` calls mid-stream via throttling * chore: remove addMetadata operations and consolidate in BaseClient * fix(listAssistantsForAzure): attempt to specify correct model mapping as accurately as possible (#2177) * refactor(client): update last conversation setup with current assistant model, call newConvo again when assistants load to allow fast initial load and ensure assistant model is always the default, not the last selected model * refactor(cache): explicitly add TTL of 2 minutes when setting titleCache and add default TTL of 10 minutes to abortKeys cache * feat(AnthropicClient): conversation titling using Anthropic Function Calling * chore: remove extraneous token usage logging * fix(convos): unhandled edge case for conversation grouping (undefined conversation) * style: Improved style of Search Bar after recent UI update * chore: remove unused code, content part helpers * feat: always show code option
This commit is contained in:
parent
8e7816468d
commit
1f0fb497f8
31 changed files with 426 additions and 188 deletions
|
|
@ -1,9 +1,11 @@
|
|||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import ProgressCircle from './ProgressCircle';
|
||||
import ProgressText from './ProgressText';
|
||||
import FinishedIcon from './FinishedIcon';
|
||||
import MarkdownLite from './MarkdownLite';
|
||||
import { useProgress } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
export default function CodeAnalyze({
|
||||
initialProgress = 0.1,
|
||||
|
|
@ -14,7 +16,8 @@ export default function CodeAnalyze({
|
|||
code: string;
|
||||
outputs: Record<string, unknown>[];
|
||||
}) {
|
||||
const [showCode, setShowCode] = useState(false);
|
||||
const showCodeDefault = useRecoilValue(store.showCode);
|
||||
const [showCode, setShowCode] = useState(showCodeDefault);
|
||||
const progress = useProgress(initialProgress);
|
||||
const radius = 56.08695652173913;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export default function Settings({
|
|||
setOption,
|
||||
readonly,
|
||||
}: Omit<TModelSelectProps, 'models'>) {
|
||||
/* This is an unfinished component for future update */
|
||||
const localize = useLocalize();
|
||||
const {
|
||||
endpoint,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const SearchBar = forwardRef((props: SearchBarProps, ref: Ref<HTMLDivElement>) =
|
|||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className="relative mt-1 flex flex h-10 cursor-pointer items-center gap-3 rounded-lg border-white bg-gray-50 px-2 px-3 py-2 text-black transition-colors duration-200 hover:bg-gray-200 focus:bg-gray-800 dark:bg-gray-900 dark:text-white dark:hover:bg-gray-800"
|
||||
className="relative mt-1 flex flex h-10 cursor-pointer items-center gap-3 rounded-lg border-white bg-gray-50 px-2 px-3 py-2 text-black transition-colors duration-200 focus-within:bg-gray-200 hover:bg-gray-200 dark:bg-gray-750 dark:text-white dark:focus-within:bg-gray-800 dark:hover:bg-gray-800"
|
||||
>
|
||||
{<Search className="absolute left-3 h-4 w-4" />}
|
||||
<input
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
import type { TDangerButtonProps } from '~/common';
|
||||
import HideSidePanelSwitch from './HideSidePanelSwitch';
|
||||
import AutoScrollSwitch from './AutoScrollSwitch';
|
||||
import ShowCodeSwitch from './ShowCodeSwitch';
|
||||
import { Dropdown } from '~/components/ui';
|
||||
import DangerButton from '../DangerButton';
|
||||
import store from '~/store';
|
||||
|
|
@ -181,6 +182,17 @@ function General() {
|
|||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<LangSelector langcode={selectedLang} onChange={changeLang} />
|
||||
</div>
|
||||
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutoScrollSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<ShowCodeSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<HideSidePanelSwitch />
|
||||
</div>
|
||||
{/* Clear Chats should be last */}
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<ClearChatsButton
|
||||
confirmClear={confirmClear}
|
||||
|
|
@ -189,12 +201,6 @@ function General() {
|
|||
mutation={clearConvosMutation}
|
||||
/>
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<AutoScrollSwitch />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
|
||||
<HideSidePanelSwitch />
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import { Switch } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
export default function ShowCodeSwitch({
|
||||
onCheckedChange,
|
||||
}: {
|
||||
onCheckedChange?: (value: boolean) => void;
|
||||
}) {
|
||||
const [showCode, setShowCode] = useRecoilState<boolean>(store.showCode);
|
||||
const localize = useLocalize();
|
||||
|
||||
const handleCheckedChange = (value: boolean) => {
|
||||
setShowCode(value);
|
||||
if (onCheckedChange) {
|
||||
onCheckedChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_show_code')} </div>
|
||||
<Switch
|
||||
id="showCode"
|
||||
checked={showCode}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
className="ml-4 mt-2"
|
||||
data-testid="showCode"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -396,6 +396,7 @@ export default {
|
|||
com_nav_theme_dark: 'Dark',
|
||||
com_nav_theme_light: 'Light',
|
||||
com_nav_user_name_display: 'Display username in messages',
|
||||
com_nav_show_code: 'Always show code when using code interpreter',
|
||||
com_nav_clear_all_chats: 'Clear all chats',
|
||||
com_nav_confirm_clear: 'Confirm Clear',
|
||||
com_nav_close_sidebar: 'Close sidebar',
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import {
|
|||
useGetStartupConfig,
|
||||
useGetEndpointsQuery,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import { defaultOrderQuery } from 'librechat-data-provider';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import { useGetConvoIdQuery, useListAssistantsQuery } from '~/data-provider';
|
||||
import { useNewConvo, useConfigOverride } from '~/hooks';
|
||||
import { useGetConvoIdQuery } from '~/data-provider';
|
||||
import ChatView from '~/components/Chat/ChatView';
|
||||
import useAuthRedirect from './useAuthRedirect';
|
||||
import { Spinner } from '~/components/svg';
|
||||
|
|
@ -32,6 +33,10 @@ export default function ChatRoute() {
|
|||
enabled: isAuthenticated && conversationId !== 'new',
|
||||
});
|
||||
const endpointsQuery = useGetEndpointsQuery({ enabled: isAuthenticated && modelsQueryEnabled });
|
||||
const { data: assistants = null } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) =>
|
||||
res.data.map(({ id, name, metadata, model }) => ({ id, name, metadata, model })),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (startupConfig?.appTitle) {
|
||||
|
|
@ -48,7 +53,7 @@ export default function ChatRoute() {
|
|||
!hasSetConversation.current
|
||||
) {
|
||||
newConversation({ modelsData: modelsQuery.data });
|
||||
hasSetConversation.current = true;
|
||||
hasSetConversation.current = !!assistants;
|
||||
} else if (
|
||||
initialConvoQuery.data &&
|
||||
endpointsQuery.data &&
|
||||
|
|
@ -61,10 +66,20 @@ export default function ChatRoute() {
|
|||
preset: initialConvoQuery.data as TPreset,
|
||||
modelsData: modelsQuery.data,
|
||||
});
|
||||
hasSetConversation.current = !!assistants;
|
||||
} else if (!hasSetConversation.current && conversationId === 'new' && assistants) {
|
||||
newConversation({ modelsData: modelsQuery.data });
|
||||
hasSetConversation.current = true;
|
||||
} else if (!hasSetConversation.current && assistants) {
|
||||
newConversation({
|
||||
template: initialConvoQuery.data,
|
||||
preset: initialConvoQuery.data as TPreset,
|
||||
modelsData: modelsQuery.data,
|
||||
});
|
||||
hasSetConversation.current = true;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [initialConvoQuery.data, modelsQuery.data, endpointsQuery.data]);
|
||||
}, [initialConvoQuery.data, modelsQuery.data, endpointsQuery.data, assistants]);
|
||||
|
||||
if (endpointsQuery.isLoading || modelsQuery.isLoading) {
|
||||
return <Spinner className="m-auto text-black dark:text-white" />;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,25 @@ const autoScroll = atom<boolean>({
|
|||
] as const,
|
||||
});
|
||||
|
||||
const showCode = atom<boolean>({
|
||||
key: 'showCode',
|
||||
default: localStorage.getItem('showCode') === 'true',
|
||||
effects: [
|
||||
({ setSelf, onSet }) => {
|
||||
const savedValue = localStorage.getItem('showCode');
|
||||
if (savedValue != null) {
|
||||
setSelf(savedValue === 'true');
|
||||
}
|
||||
|
||||
onSet((newValue: unknown) => {
|
||||
if (typeof newValue === 'boolean') {
|
||||
localStorage.setItem('showCode', newValue.toString());
|
||||
}
|
||||
});
|
||||
},
|
||||
] as const,
|
||||
});
|
||||
|
||||
const hideSidePanel = atom<boolean>({
|
||||
key: 'hideSidePanel',
|
||||
default: localStorage.getItem('hideSidePanel') === 'true',
|
||||
|
|
@ -147,6 +166,7 @@ export default {
|
|||
showBingToneSetting,
|
||||
showPopover,
|
||||
autoScroll,
|
||||
showCode,
|
||||
hideSidePanel,
|
||||
modularChat,
|
||||
LaTeXParsing,
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
import { ToolCallTypes } from 'librechat-data-provider';
|
||||
import type {
|
||||
ContentPart,
|
||||
CodeToolCall,
|
||||
ImageFile,
|
||||
Text,
|
||||
PartMetadata,
|
||||
} from 'librechat-data-provider';
|
||||
|
||||
export function isText(part: ContentPart): part is Text & PartMetadata {
|
||||
return (part as Text).value !== undefined;
|
||||
}
|
||||
|
||||
export function isCodeToolCall(part: ContentPart): part is CodeToolCall & PartMetadata {
|
||||
return (part as CodeToolCall).type === ToolCallTypes.CODE_INTERPRETER;
|
||||
}
|
||||
|
||||
export function isImageFile(part: ContentPart): part is ImageFile & PartMetadata {
|
||||
return (part as ImageFile).file_id !== undefined;
|
||||
}
|
||||
|
|
@ -42,6 +42,10 @@ export const groupConversationsByDate = (conversations: TConversation[]): Groupe
|
|||
|
||||
const seenConversationIds = new Set();
|
||||
const groups = conversations.reduce((acc, conversation) => {
|
||||
if (!conversation) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (seenConversationIds.has(conversation.conversationId)) {
|
||||
return acc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,12 @@ export function updateLastSelectedModel({
|
|||
if (!model) {
|
||||
return;
|
||||
}
|
||||
const lastConversationSetup = JSON.parse(localStorage.getItem('lastConversationSetup') || '{}');
|
||||
const lastSelectedModels = JSON.parse(localStorage.getItem('lastSelectedModel') || '{}');
|
||||
if (lastConversationSetup.endpoint === endpoint) {
|
||||
lastConversationSetup.model = model;
|
||||
localStorage.setItem('lastConversationSetup', JSON.stringify(lastConversationSetup));
|
||||
}
|
||||
lastSelectedModels[endpoint] = model;
|
||||
localStorage.setItem('lastSelectedModel', JSON.stringify(lastSelectedModels));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ export * from './files';
|
|||
export * from './latex';
|
||||
export * from './convos';
|
||||
export * from './presets';
|
||||
export * from './content';
|
||||
export * from './languages';
|
||||
export * from './endpoints';
|
||||
export { default as cn } from './cn';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue