Skip to content

Commit

Permalink
feat: show key configuration alert dialog when not set the apikey in …
Browse files Browse the repository at this point in the history
…client side
  • Loading branch information
guangzhengli committed Jun 17, 2023
1 parent cef609c commit 322d9c7
Show file tree
Hide file tree
Showing 10 changed files with 564 additions and 270 deletions.
4 changes: 2 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NEXT_PUBLIC_CHAT_FILES_MAX_SIZE=0
NEXT_PUBLIC_CHAT_FILES_UPLOAD_PATH=public/uploads # if you want to deploy on vercel, please use /tmp folder
NEXT_PUBLIC_CHAT_FILES_UPLOAD_PATH=public/uploads# if you want to deploy on vercel, please use /tmp folder
SUPABASE_SERVICE_ROLE_KEY=xxxx
SUPABASE_URL=https://xxxx.supabase.co
OPENAI_TYPE=OPENAI # OPENAI_TYPE=AZURE_OPENAI
Expand All @@ -8,4 +8,4 @@ AZURE_OPENAI_API_KEY=
AZURE_OPENAI_API_DEPLOYMENT_NAME=
AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME=
AZURE_OPENAI_API_INSTANCE_NAME=
AZURE_OPENAI_API_VERSION=2023-05-15 #default
AZURE_OPENAI_API_VERSION=2023-05-15#default
491 changes: 260 additions & 231 deletions components/Chat/Chat.tsx

Large diffs are not rendered by default.

55 changes: 30 additions & 25 deletions components/Chat/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Message, OpenAIModel, OpenAIModelID } from '@/types';
import { IconPlayerStop, IconRepeat, IconSend } from '@tabler/icons-react';
import {Message, OpenAIModel, OpenAIModelID} from '@/types';
import {IconPlayerStop, IconRepeat, IconSend} from '@tabler/icons-react';
import {
FC,
KeyboardEvent,
MutableRefObject,
useEffect,
useState,
} from 'react';
import { useTranslation } from 'next-i18next';
import {useTranslation} from 'next-i18next';

interface Props {
messageIsStreaming: boolean;
Expand All @@ -17,18 +17,20 @@ interface Props {
onRegenerate: () => void;
stopConversationRef: MutableRefObject<boolean>;
textareaRef: MutableRefObject<HTMLTextAreaElement | null>;
handleKeyConfigurationValidation: () => boolean;
}

export const ChatInput: FC<Props> = ({
messageIsStreaming,
model,
conversationIsEmpty,
onSend,
onRegenerate,
stopConversationRef,
textareaRef,
}) => {
const { t } = useTranslation('chat');
messageIsStreaming,
model,
conversationIsEmpty,
onSend,
onRegenerate,
stopConversationRef,
textareaRef,
handleKeyConfigurationValidation,
}) => {
const {t} = useTranslation('chat');
const [content, setContent] = useState<string>();
const [isTyping, setIsTyping] = useState<boolean>(false);

Expand All @@ -40,7 +42,7 @@ export const ChatInput: FC<Props> = ({
alert(
t(
`Message limit is {{maxLength}} characters. You have entered {{valueLength}} characters.`,
{ maxLength, valueLength: value.length },
{maxLength, valueLength: value.length},
),
);
return;
Expand All @@ -59,7 +61,7 @@ export const ChatInput: FC<Props> = ({
return;
}

onSend({ role: 'user', content });
onSend({role: 'user', content});
setContent('');

if (window.innerWidth < 640 && textareaRef && textareaRef.current) {
Expand Down Expand Up @@ -102,14 +104,16 @@ export const ChatInput: FC<Props> = ({
}

return (
<div className="absolute bottom-0 left-0 w-full border-transparent bg-gradient-to-b from-transparent via-white to-white pt-6 dark:border-white/20 dark:via-[#343541] dark:to-[#343541] md:pt-2">
<div className="stretch mx-2 mt-4 flex flex-row gap-3 last:mb-2 md:mx-4 md:mt-[52px] md:last:mb-6 lg:mx-auto lg:max-w-3xl">
<div
className="absolute bottom-0 left-0 w-full border-transparent bg-gradient-to-b from-transparent via-white to-white pt-6 dark:border-white/20 dark:via-[#343541] dark:to-[#343541] md:pt-2">
<div
className="stretch mx-2 mt-4 flex flex-row gap-3 last:mb-2 md:mx-4 md:mt-[52px] md:last:mb-6 lg:mx-auto lg:max-w-3xl">
{messageIsStreaming && (
<button
className="absolute -top-2 left-0 right-0 mx-auto w-fit rounded border border-neutral-200 bg-white py-2 px-4 text-black dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:top-0"
onClick={handleStopConversation}
>
<IconPlayerStop size={16} className="mb-[2px] inline-block" />{' '}
<IconPlayerStop size={16} className="mb-[2px] inline-block"/>{' '}
{t('Stop Generating')}
</button>
)}
Expand All @@ -119,12 +123,13 @@ export const ChatInput: FC<Props> = ({
className="absolute -top-2 left-0 right-0 mx-auto w-fit rounded border border-neutral-200 bg-white py-2 px-4 text-black dark:border-neutral-600 dark:bg-[#343541] dark:text-white md:top-0"
onClick={onRegenerate}
>
<IconRepeat size={16} className="mb-[2px] inline-block" />{' '}
<IconRepeat size={16} className="mb-[2px] inline-block"/>{' '}
{t('Regenerate response')}
</button>
)}

<div className="relative flex w-full flex-grow flex-col rounded-md border border-black/10 bg-white py-2 shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 dark:bg-[#40414F] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] md:py-3 md:pl-4">
<div
className="relative flex w-full flex-grow flex-col rounded-md border border-black/10 bg-white py-2 shadow-[0_0_10px_rgba(0,0,0,0.10)] dark:border-gray-900/50 dark:bg-[#40414F] dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] md:py-3 md:pl-4">
<textarea
ref={textareaRef}
className="m-0 w-full resize-none border-0 bg-transparent p-0 pr-7 pl-2 text-black outline-none focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:text-white md:pl-0"
Expand All @@ -151,22 +156,22 @@ export const ChatInput: FC<Props> = ({
className="absolute right-3 rounded-sm p-1 text-neutral-800 hover:bg-neutral-200 hover:text-neutral-900 focus:outline-none dark:bg-opacity-50 dark:text-neutral-100 dark:hover:text-neutral-200"
onClick={handleSend}
>
<IconSend size={16} className="opacity-60" />
<IconSend size={16} className="opacity-60"/>
</button>
</div>
</div>
<div className="px-3 pt-2 pb-3 text-center text-[12px] text-black/50 dark:text-white/50 md:px-4 md:pt-3 md:pb-6">
<a
href="https://github.com/guangzhengli/ChatFiles"
target="_blank"
rel="noreferrer"
className="underline"
href="https://github.com/guangzhengli/ChatFiles"
target="_blank"
rel="noreferrer"
className="underline"
>
ChatFiles
</a>
{' '}
{t(
"aims to establish embeddings for ChatGPT and facilitate its ability to engage in document-based conversations.",
"aims to establish embeddings for ChatGPT and facilitate its ability to engage in document-based conversations.",
)}
</div>
</div>
Expand Down
13 changes: 9 additions & 4 deletions components/Chat/Upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,24 @@ interface Props {
handleIsUploading: (isUploading: boolean) => void;
handleIsUploadSuccess: (isUploadSuccess: boolean) => void;
handleUploadError: (error: string) => void;
handleKeyConfigurationValidation: () => boolean;
}

export const Upload = ({
onIndexChange,
onIndexChange,
keyConfiguration,
handleIsUploading,
handleIsUploadSuccess,
handleUploadError
handleIsUploading,
handleIsUploadSuccess,
handleUploadError,
handleKeyConfigurationValidation,
}: Props) => {

const { t } = useTranslation('sidebar');

const handleFile = async (file: File) => {
if (!handleKeyConfigurationValidation()) {
return;
}
if (!validateFile(file)) {
handleIsUploadSuccess(false);
return;
Expand Down
42 changes: 42 additions & 0 deletions components/Sidebar/KeySettingsAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {KeyConfiguration} from "@/types";
import {FC, useState} from "react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"

interface Props {
onCancellation: () => void;
onContinue: () => void;
}

export const KeySettingsAlertDialog: FC<Props> = ({
onCancellation,
onContinue,
}) => {
return (
<>
<AlertDialog open={true}>
<AlertDialogContent >
<AlertDialogHeader>
<AlertDialogTitle>You need to configure the OpenAI key for use.</AlertDialogTitle>
<AlertDialogDescription>
This is an open-source project and we need your own key. We will not store your key, or you can set up the ChatFiles service by yourself.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={onCancellation}>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={onContinue}>Setup</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
)
}
150 changes: 150 additions & 0 deletions components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
"use client"

import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"

import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"

const AlertDialog = AlertDialogPrimitive.Root

const AlertDialogTrigger = AlertDialogPrimitive.Trigger

const AlertDialogPortal = ({
className,
children,
...props
}: AlertDialogPrimitive.AlertDialogPortalProps) => (
<AlertDialogPrimitive.Portal className={cn(className)} {...props}>
<div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
{children}
</div>
</AlertDialogPrimitive.Portal>
)
AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName

const AlertDialogOverlay = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
>(({ className, children, ...props }, ref) => (
<AlertDialogPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-opacity animate-in fade-in",
className
)}
{...props}
ref={ref}
/>
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName

const AlertDialogContent = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
>(({ className, ...props }, ref) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
ref={ref}
className={cn(
"fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-background p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full",
className
)}
{...props}
/>
</AlertDialogPortal>
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName

const AlertDialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
AlertDialogHeader.displayName = "AlertDialogHeader"

const AlertDialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
AlertDialogFooter.displayName = "AlertDialogFooter"

const AlertDialogTitle = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold", className)}
{...props}
/>
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName

const AlertDialogDescription = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName

const AlertDialogAction = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Action>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Action
ref={ref}
className={cn(buttonVariants(), className)}
{...props}
/>
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName

const AlertDialogCancel = React.forwardRef<
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Cancel
ref={ref}
className={cn(
buttonVariants({ variant: "outline" }),
"mt-2 sm:mt-0",
className
)}
{...props}
/>
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName

export {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}
Loading

0 comments on commit 322d9c7

Please sign in to comment.