Spaces:
Running
Running
Commit
·
cc43e3c
1
Parent(s):
a665cd2
lift 10 image restriction
Browse files- app/api/chat/route.ts +1 -0
- components/chat/ImageList.tsx +2 -2
- components/prompt-form.tsx +70 -70
- lib/hooks/useChatWithDataset.ts +8 -8
- lib/hooks/useImageUpload.ts +7 -7
app/api/chat/route.ts
CHANGED
|
@@ -33,6 +33,7 @@ export async function POST(req: Request) {
|
|
| 33 |
const formattedMessage: ChatCompletionMessageParam[] = messages.map(
|
| 34 |
message => {
|
| 35 |
const { dataset, ...rest } = message;
|
|
|
|
| 36 |
|
| 37 |
const contentWithImage: ChatCompletionContentPart[] = [
|
| 38 |
{
|
|
|
|
| 33 |
const formattedMessage: ChatCompletionMessageParam[] = messages.map(
|
| 34 |
message => {
|
| 35 |
const { dataset, ...rest } = message;
|
| 36 |
+
console.log('[Ming] ~ POST ~ dataset:', dataset?.length);
|
| 37 |
|
| 38 |
const contentWithImage: ChatCompletionContentPart[] = [
|
| 39 |
{
|
components/chat/ImageList.tsx
CHANGED
|
@@ -15,7 +15,7 @@ const ImageList: React.FC<ImageListProps> = () => {
|
|
| 15 |
const [dataset, setDataset] = useAtom(datasetAtom);
|
| 16 |
return (
|
| 17 |
<div className="relative size-full px-12 max-w-3xl mx-auto">
|
| 18 |
-
{dataset.length < 10 ? (
|
| 19 |
<div className="col-span-full px-8 py-4 rounded-xl bg-blue-100 text-blue-400 mb-8">
|
| 20 |
You can upload up to 10 images max by dragging image.
|
| 21 |
</div>
|
|
@@ -23,7 +23,7 @@ const ImageList: React.FC<ImageListProps> = () => {
|
|
| 23 |
<div className="col-span-full px-8 py-4 rounded-xl bg-red-100 text-red-400 mb-8">
|
| 24 |
You have reached the maximum limit of 10 images.
|
| 25 |
</div>
|
| 26 |
-
)}
|
| 27 |
<div
|
| 28 |
{...getRootProps()}
|
| 29 |
className="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4"
|
|
|
|
| 15 |
const [dataset, setDataset] = useAtom(datasetAtom);
|
| 16 |
return (
|
| 17 |
<div className="relative size-full px-12 max-w-3xl mx-auto">
|
| 18 |
+
{/* {dataset.length < 10 ? (
|
| 19 |
<div className="col-span-full px-8 py-4 rounded-xl bg-blue-100 text-blue-400 mb-8">
|
| 20 |
You can upload up to 10 images max by dragging image.
|
| 21 |
</div>
|
|
|
|
| 23 |
<div className="col-span-full px-8 py-4 rounded-xl bg-red-100 text-red-400 mb-8">
|
| 24 |
You have reached the maximum limit of 10 images.
|
| 25 |
</div>
|
| 26 |
+
)} */}
|
| 27 |
<div
|
| 28 |
{...getRootProps()}
|
| 29 |
className="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4"
|
components/prompt-form.tsx
CHANGED
|
@@ -1,52 +1,52 @@
|
|
| 1 |
-
import * as React from 'react'
|
| 2 |
-
import Textarea from 'react-textarea-autosize'
|
| 3 |
-
import { UseChatHelpers } from 'ai/react'
|
| 4 |
-
import { useEnterSubmit } from '@/lib/hooks/use-enter-submit'
|
| 5 |
-
import { cn } from '@/lib/utils'
|
| 6 |
-
import { Button, buttonVariants } from '@/components/ui/button'
|
| 7 |
import {
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
} from '@/components/ui/tooltip'
|
| 12 |
-
import { IconArrowElbow, IconPlus } from '@/components/ui/icons'
|
| 13 |
-
import { useRouter } from 'next/navigation'
|
| 14 |
|
| 15 |
export interface PromptProps
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
}
|
| 20 |
|
| 21 |
export function PromptForm({
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
}: PromptProps) {
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
<TooltipTrigger asChild>
|
| 51 |
<button
|
| 52 |
onClick={e => {
|
|
@@ -65,33 +65,33 @@ export function PromptForm({
|
|
| 65 |
</TooltipTrigger>
|
| 66 |
<TooltipContent>New Chat</TooltipContent>
|
| 67 |
</Tooltip> */}
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
}
|
|
|
|
| 1 |
+
import * as React from 'react';
|
| 2 |
+
import Textarea from 'react-textarea-autosize';
|
| 3 |
+
import { UseChatHelpers } from 'ai/react';
|
| 4 |
+
import { useEnterSubmit } from '@/lib/hooks/use-enter-submit';
|
| 5 |
+
import { cn } from '@/lib/utils';
|
| 6 |
+
import { Button, buttonVariants } from '@/components/ui/button';
|
| 7 |
import {
|
| 8 |
+
Tooltip,
|
| 9 |
+
TooltipContent,
|
| 10 |
+
TooltipTrigger,
|
| 11 |
+
} from '@/components/ui/tooltip';
|
| 12 |
+
import { IconArrowElbow, IconPlus } from '@/components/ui/icons';
|
| 13 |
+
import { useRouter } from 'next/navigation';
|
| 14 |
|
| 15 |
export interface PromptProps
|
| 16 |
+
extends Pick<UseChatHelpers, 'input' | 'setInput'> {
|
| 17 |
+
onSubmit: (value: string) => void;
|
| 18 |
+
isLoading: boolean;
|
| 19 |
}
|
| 20 |
|
| 21 |
export function PromptForm({
|
| 22 |
+
onSubmit,
|
| 23 |
+
input,
|
| 24 |
+
setInput,
|
| 25 |
+
isLoading,
|
| 26 |
}: PromptProps) {
|
| 27 |
+
const { formRef, onKeyDown } = useEnterSubmit();
|
| 28 |
+
const inputRef = React.useRef<HTMLTextAreaElement>(null);
|
| 29 |
+
const router = useRouter();
|
| 30 |
+
React.useEffect(() => {
|
| 31 |
+
if (inputRef.current) {
|
| 32 |
+
inputRef.current.focus();
|
| 33 |
+
}
|
| 34 |
+
}, []);
|
| 35 |
|
| 36 |
+
return (
|
| 37 |
+
<form
|
| 38 |
+
onSubmit={async e => {
|
| 39 |
+
e.preventDefault();
|
| 40 |
+
if (!input?.trim()) {
|
| 41 |
+
return;
|
| 42 |
+
}
|
| 43 |
+
setInput('');
|
| 44 |
+
await onSubmit(input);
|
| 45 |
+
}}
|
| 46 |
+
ref={formRef}
|
| 47 |
+
>
|
| 48 |
+
<div className="relative flex flex-col w-full px-8 pl-2 overflow-hidden max-h-60 grow bg-background sm:rounded-md sm:border sm:px-12 sm:pl-2">
|
| 49 |
+
{/* <Tooltip>
|
| 50 |
<TooltipTrigger asChild>
|
| 51 |
<button
|
| 52 |
onClick={e => {
|
|
|
|
| 65 |
</TooltipTrigger>
|
| 66 |
<TooltipContent>New Chat</TooltipContent>
|
| 67 |
</Tooltip> */}
|
| 68 |
+
<Textarea
|
| 69 |
+
ref={inputRef}
|
| 70 |
+
tabIndex={0}
|
| 71 |
+
onKeyDown={onKeyDown}
|
| 72 |
+
rows={1}
|
| 73 |
+
value={input}
|
| 74 |
+
onChange={e => setInput(e.target.value)}
|
| 75 |
+
placeholder="Ask questions about all or selected the images."
|
| 76 |
+
spellCheck={false}
|
| 77 |
+
className="min-h-[60px] w-full resize-none bg-transparent px-4 py-[1.3rem] focus-within:outline-none sm:text-sm"
|
| 78 |
+
/>
|
| 79 |
+
<div className="absolute right-0 top-4 sm:right-4">
|
| 80 |
+
<Tooltip>
|
| 81 |
+
<TooltipTrigger asChild>
|
| 82 |
+
<Button
|
| 83 |
+
type="submit"
|
| 84 |
+
size="icon"
|
| 85 |
+
disabled={isLoading || input === ''}
|
| 86 |
+
>
|
| 87 |
+
<IconArrowElbow />
|
| 88 |
+
<span className="sr-only">Send message</span>
|
| 89 |
+
</Button>
|
| 90 |
+
</TooltipTrigger>
|
| 91 |
+
<TooltipContent>Send message</TooltipContent>
|
| 92 |
+
</Tooltip>
|
| 93 |
+
</div>
|
| 94 |
+
</div>
|
| 95 |
+
</form>
|
| 96 |
+
);
|
| 97 |
}
|
lib/hooks/useChatWithDataset.ts
CHANGED
|
@@ -73,14 +73,14 @@ const useChatWithDataset = () => {
|
|
| 73 |
dataset;
|
| 74 |
|
| 75 |
const appendWithDataset: typeof append = message => {
|
| 76 |
-
const newSystemMessage: Message = {
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
};
|
| 83 |
-
setMessages([...messages, newSystemMessage]);
|
| 84 |
return append({
|
| 85 |
...message,
|
| 86 |
// @ts-ignore this is extra fields
|
|
|
|
| 73 |
dataset;
|
| 74 |
|
| 75 |
const appendWithDataset: typeof append = message => {
|
| 76 |
+
// const newSystemMessage: Message = {
|
| 77 |
+
// id: 'fake-id',
|
| 78 |
+
// content:
|
| 79 |
+
// 'For the next prompt, here are names of images provided by user, please use these name if you need reference: ' +
|
| 80 |
+
// selectedDataset.map(entity => entity.name).join(', '),
|
| 81 |
+
// role: 'system',
|
| 82 |
+
// };
|
| 83 |
+
// setMessages([...messages, newSystemMessage]);
|
| 84 |
return append({
|
| 85 |
...message,
|
| 86 |
// @ts-ignore this is extra fields
|
lib/hooks/useImageUpload.ts
CHANGED
|
@@ -12,12 +12,12 @@ const useImageUpload = (options?: Partial<DropzoneOptions>) => {
|
|
| 12 |
},
|
| 13 |
multiple: true,
|
| 14 |
onDrop: acceptedFiles => {
|
| 15 |
-
if (acceptedFiles.length > 10) {
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
}
|
| 20 |
-
acceptedFiles.
|
| 21 |
try {
|
| 22 |
const reader = new FileReader();
|
| 23 |
reader.onloadend = () => {
|
|
@@ -25,7 +25,7 @@ const useImageUpload = (options?: Partial<DropzoneOptions>) => {
|
|
| 25 |
setTarget(prev => {
|
| 26 |
// Check if the image already exists in the state
|
| 27 |
if (
|
| 28 |
-
prev.length >= 10 ||
|
| 29 |
prev.find(entity => entity.url === newImage)
|
| 30 |
) {
|
| 31 |
// If it does, return the state unchanged
|
|
|
|
| 12 |
},
|
| 13 |
multiple: true,
|
| 14 |
onDrop: acceptedFiles => {
|
| 15 |
+
// if (acceptedFiles.length > 10) {
|
| 16 |
+
// toast('You can only upload 10 images max.', {
|
| 17 |
+
// icon: '⚠️',
|
| 18 |
+
// });
|
| 19 |
+
// }
|
| 20 |
+
acceptedFiles.forEach(file => {
|
| 21 |
try {
|
| 22 |
const reader = new FileReader();
|
| 23 |
reader.onloadend = () => {
|
|
|
|
| 25 |
setTarget(prev => {
|
| 26 |
// Check if the image already exists in the state
|
| 27 |
if (
|
| 28 |
+
// prev.length >= 10 ||
|
| 29 |
prev.find(entity => entity.url === newImage)
|
| 30 |
) {
|
| 31 |
// If it does, return the state unchanged
|