mirror of
https://github.com/StanGirard/quivr.git
synced 2024-12-24 11:52:45 +03:00
feat: save last chat config and make it default one (#1266)
* fix(RBAC): skip validation for unplug * feat(chatSettings): set last config as default --------- Co-authored-by: Zineb El Bachiri <100568984+gozineb@users.noreply.github.com>
This commit is contained in:
parent
da6d5b698d
commit
c8f045dfad
@ -195,6 +195,13 @@ async def create_question_handler(
|
||||
)
|
||||
|
||||
# Retrieve user's OpenAI API key
|
||||
if brain_id:
|
||||
validate_brain_authorization(
|
||||
brain_id=brain_id,
|
||||
user_id=current_user.id,
|
||||
required_roles=[RoleEnum.Viewer, RoleEnum.Editor, RoleEnum.Owner],
|
||||
)
|
||||
|
||||
current_user.openai_api_key = request.headers.get("Openai-Api-Key")
|
||||
brain = Brain(id=brain_id)
|
||||
brain_details: BrainEntity | None = None
|
||||
|
@ -42,6 +42,22 @@ vi.mock("@/lib/api/chat/useChatApi", () => ({
|
||||
getHistory: () => [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/hooks", async () => {
|
||||
const actual = await vi.importActual<typeof import("@/lib/hooks")>(
|
||||
"@/lib/hooks"
|
||||
);
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useAxios: () => ({
|
||||
axiosInstance: {
|
||||
get: vi.fn(() => ({ data: [] })),
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@tanstack/react-query", async () => {
|
||||
const actual = await vi.importActual<typeof import("@tanstack/react-query")>(
|
||||
"@tanstack/react-query"
|
||||
|
@ -7,7 +7,7 @@ import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";
|
||||
|
||||
import { useConfigModal } from "./hooks/useConfigModal";
|
||||
|
||||
export const ConfigModal = ({ chatId }: { chatId?: string }): JSX.Element => {
|
||||
export const ConfigModal = (): JSX.Element => {
|
||||
const {
|
||||
handleSubmit,
|
||||
isConfigModalOpen,
|
||||
@ -17,11 +17,7 @@ export const ConfigModal = ({ chatId }: { chatId?: string }): JSX.Element => {
|
||||
maxTokens,
|
||||
model,
|
||||
accessibleModels,
|
||||
} = useConfigModal(chatId);
|
||||
|
||||
if (chatId === undefined) {
|
||||
return <div />;
|
||||
}
|
||||
} = useConfigModal();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -79,7 +75,7 @@ export const ConfigModal = ({ chatId }: { chatId?: string }): JSX.Element => {
|
||||
<input
|
||||
type="range"
|
||||
min="10"
|
||||
max={defineMaxTokens(model ?? "gpt-3.5-turbo")}
|
||||
max={defineMaxTokens(model)}
|
||||
value={maxTokens}
|
||||
{...register("maxTokens")}
|
||||
/>
|
||||
|
@ -1,28 +1,27 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { useBrainApi } from "@/lib/api/brain/useBrainApi";
|
||||
import {
|
||||
getChatConfigFromLocalStorage,
|
||||
saveChatConfigInLocalStorage,
|
||||
getChatsConfigFromLocalStorage,
|
||||
saveChatsConfigInLocalStorage,
|
||||
} from "@/lib/api/chat/chat.local";
|
||||
import { USER_DATA_KEY, USER_IDENTITY_DATA_KEY } from "@/lib/api/user/config";
|
||||
import { useUserApi } from "@/lib/api/user/useUserApi";
|
||||
import { defaultBrainConfig } from "@/lib/config/defaultBrainConfig";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { ChatConfig } from "@/lib/context/ChatProvider/types";
|
||||
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";
|
||||
import { getAccessibleModels } from "@/lib/helpers/getAccessibleModels";
|
||||
import { useToast } from "@/lib/hooks";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useConfigModal = (chatId?: string) => {
|
||||
export const useConfigModal = () => {
|
||||
const { publish } = useToast();
|
||||
const [isConfigModalOpen, setIsConfigModalOpen] = useState(false);
|
||||
const { getBrain } = useBrainApi();
|
||||
const { currentBrain } = useBrainContext();
|
||||
const { currentBrainId } = useBrainContext();
|
||||
const { getUser, getUserIdentity } = useUserApi();
|
||||
|
||||
const { data: userData } = useQuery({
|
||||
@ -34,10 +33,12 @@ export const useConfigModal = (chatId?: string) => {
|
||||
queryFn: getUserIdentity,
|
||||
});
|
||||
|
||||
const defaultValues: ChatConfig = {};
|
||||
|
||||
const { register, watch, setValue } = useForm({
|
||||
defaultValues,
|
||||
const { register, watch, setValue } = useForm<ChatConfig>({
|
||||
defaultValues: {
|
||||
model: defaultBrainConfig.model,
|
||||
temperature: defaultBrainConfig.temperature,
|
||||
maxTokens: defaultBrainConfig.maxTokens,
|
||||
},
|
||||
});
|
||||
|
||||
const model = watch("model");
|
||||
@ -49,54 +50,40 @@ export const useConfigModal = (chatId?: string) => {
|
||||
userData,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchChatConfig = async () => {
|
||||
if (chatId === undefined) {
|
||||
const fetchChatConfig = useCallback(async () => {
|
||||
const chatConfig = getChatsConfigFromLocalStorage();
|
||||
if (chatConfig !== undefined) {
|
||||
setValue("model", chatConfig.model);
|
||||
setValue("temperature", chatConfig.temperature);
|
||||
setValue("maxTokens", chatConfig.maxTokens);
|
||||
} else {
|
||||
if (currentBrainId === null) {
|
||||
return;
|
||||
}
|
||||
const relatedBrainConfig = await getBrain(currentBrainId);
|
||||
|
||||
const chatConfig = getChatConfigFromLocalStorage(chatId);
|
||||
if (chatConfig !== undefined) {
|
||||
setValue("model", chatConfig.model);
|
||||
setValue("temperature", chatConfig.temperature);
|
||||
setValue("maxTokens", chatConfig.maxTokens);
|
||||
} else {
|
||||
if (currentBrain === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const relatedBrainConfig = await getBrain(currentBrain.id);
|
||||
if (relatedBrainConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
setValue("model", relatedBrainConfig.model ?? defaultBrainConfig.model);
|
||||
setValue(
|
||||
"temperature",
|
||||
relatedBrainConfig.temperature ?? defaultBrainConfig.temperature
|
||||
);
|
||||
setValue(
|
||||
"maxTokens",
|
||||
relatedBrainConfig.max_tokens ?? defaultBrainConfig.maxTokens
|
||||
);
|
||||
if (relatedBrainConfig === undefined) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
void fetchChatConfig();
|
||||
setValue("model", relatedBrainConfig.model ?? defaultBrainConfig.model);
|
||||
setValue(
|
||||
"temperature",
|
||||
relatedBrainConfig.temperature ?? defaultBrainConfig.temperature
|
||||
);
|
||||
setValue(
|
||||
"maxTokens",
|
||||
relatedBrainConfig.max_tokens ?? defaultBrainConfig.maxTokens
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (maxTokens === undefined || model === undefined) {
|
||||
return;
|
||||
}
|
||||
void fetchChatConfig();
|
||||
}, [fetchChatConfig]);
|
||||
|
||||
setValue("maxTokens", Math.min(maxTokens, defineMaxTokens(model)));
|
||||
}, [maxTokens, model, setValue]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (chatId === undefined) {
|
||||
return;
|
||||
}
|
||||
const handleSubmit = useCallback(() => {
|
||||
try {
|
||||
saveChatConfigInLocalStorage(chatId, {
|
||||
saveChatsConfigInLocalStorage({
|
||||
maxTokens,
|
||||
model,
|
||||
temperature,
|
||||
@ -112,7 +99,7 @@ export const useConfigModal = (chatId?: string) => {
|
||||
text: "An error occurred while updating chat config",
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [maxTokens, model, publish, temperature]);
|
||||
|
||||
return {
|
||||
isConfigModalOpen,
|
||||
|
@ -25,7 +25,7 @@ export const ChatInput = ({
|
||||
setShouldDisplayUploadCard,
|
||||
hasContentToFeedBrain,
|
||||
}: ChatInputProps): JSX.Element => {
|
||||
const { setMessage, submitQuestion, chatId, generatingAnswer, message } =
|
||||
const { setMessage, submitQuestion, generatingAnswer, message } =
|
||||
useChatInput();
|
||||
const { t } = useTranslation(["chat"]);
|
||||
const { currentBrainId } = useBrainContext();
|
||||
@ -80,7 +80,7 @@ export const ChatInput = ({
|
||||
<>
|
||||
{isEmptyMessage ? (
|
||||
<div className="md:hidden flex items-center">
|
||||
<ConfigModal chatId={chatId} />
|
||||
<ConfigModal />
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
@ -95,7 +95,7 @@ export const ChatInput = ({
|
||||
</Button>
|
||||
)}
|
||||
<div className="hidden md:flex items-center">
|
||||
<ConfigModal chatId={chatId} />
|
||||
<ConfigModal />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
@ -4,7 +4,7 @@ import { useParams, useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getChatConfigFromLocalStorage } from "@/lib/api/chat/chat.local";
|
||||
import { getChatsConfigFromLocalStorage } from "@/lib/api/chat/chat.local";
|
||||
import { useChatApi } from "@/lib/api/chat/useChatApi";
|
||||
import { useChatContext } from "@/lib/context";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
@ -64,7 +64,7 @@ export const useChat = () => {
|
||||
promptId: currentPromptId,
|
||||
});
|
||||
|
||||
const chatConfig = getChatConfigFromLocalStorage(currentChatId);
|
||||
const chatConfig = getChatsConfigFromLocalStorage();
|
||||
|
||||
const chatQuestion: ChatQuestion = {
|
||||
model: chatConfig?.model,
|
||||
|
@ -6,17 +6,20 @@ import { ChatHeader } from "./components/ChatHeader";
|
||||
|
||||
const SelectedChatPage = (): JSX.Element => {
|
||||
return (
|
||||
<main className="flex flex-col w-full h-[calc(100vh-61px)] overflow-hidden" data-testid="chat-page">
|
||||
<section className="flex flex-col flex-1 items-center w-full h-full overflow-y-auto">
|
||||
<ChatHeader /> {/* Added margin-bottom */}
|
||||
<main
|
||||
className="flex flex-col w-full h-[calc(100vh-61px)] overflow-hidden"
|
||||
data-testid="chat-page"
|
||||
>
|
||||
<section className="flex flex-col flex-1 items-center w-full h-full overflow-y-auto">
|
||||
<ChatHeader />
|
||||
<div className="flex-1 flex flex-col mt-4 md:mt-8 w-full shadow-md dark:shadow-primary/25 hover:shadow-xl transition-shadow rounded-xl overflow-hidden bg-white dark:bg-black border border-black/10 dark:border-white/25 p-2 md:p-12 pt-4 md:pt-10">
|
||||
<div className="flex flex-1 flex-col overflow-y-auto">
|
||||
<ChatDialogueArea />
|
||||
</div>
|
||||
<ActionsBar/> {/* Added margin-top */}
|
||||
<div className="flex flex-1 flex-col overflow-y-auto">
|
||||
<ChatDialogueArea />
|
||||
</div>
|
||||
<ActionsBar />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { ChatConfig } from "@/lib/context/ChatProvider/types";
|
||||
|
||||
export const saveChatConfigInLocalStorage = (
|
||||
chatId: string,
|
||||
chatConfig: ChatConfig
|
||||
): void => {
|
||||
localStorage.setItem(`chat-config-${chatId}`, JSON.stringify(chatConfig));
|
||||
const chatConfigLocalStorageKey = "chat-config";
|
||||
export const saveChatsConfigInLocalStorage = (chatConfig: ChatConfig): void => {
|
||||
localStorage.setItem(chatConfigLocalStorageKey, JSON.stringify(chatConfig));
|
||||
};
|
||||
|
||||
export const getChatConfigFromLocalStorage = (
|
||||
chatId: string
|
||||
): ChatConfig | undefined => {
|
||||
const config = localStorage.getItem(`chat-config-${chatId}`);
|
||||
export const getChatsConfigFromLocalStorage = (): ChatConfig | undefined => {
|
||||
const config = localStorage.getItem(chatConfigLocalStorageKey);
|
||||
|
||||
if (config === null) {
|
||||
return undefined;
|
||||
|
@ -3,9 +3,9 @@ import { ChatMessage, Notification } from "@/app/chat/[chatId]/types";
|
||||
import { Model } from "../../types/brainConfig";
|
||||
|
||||
export type ChatConfig = {
|
||||
model?: Model;
|
||||
temperature?: number;
|
||||
maxTokens?: number;
|
||||
model: Model;
|
||||
temperature: number;
|
||||
maxTokens: number;
|
||||
};
|
||||
|
||||
export type ChatContextProps = {
|
||||
|
Loading…
Reference in New Issue
Block a user