Spaces:
Sleeping
Sleeping
Commit
•
d0a1b70
1
Parent(s):
f80b091
Fix deployment + Chat id pages (#5)
Browse files* Fix deployment
* use Response
* done
* done
* pretty
- .prettierrc +34 -0
- app/api/upload/route.ts +22 -7
- app/chat/[id]/page.tsx +8 -0
- app/chat/layout.tsx +2 -2
- app/chat/page.tsx +2 -4
- components/chat-sidebar/ChatCard.tsx +17 -6
- components/chat-sidebar/ChatListSidebar.tsx +1 -2
- components/chat/ImageSelector.tsx +31 -13
- components/chat/index.tsx +3 -3
- lib/hooks/useImageUpload.ts +21 -22
- lib/kv/chat.ts +4 -9
- lib/types.ts +18 -9
- lib/utils.ts +0 -1
- prettier.config.cjs +0 -36
.prettierrc
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"endOfLine": "lf",
|
3 |
+
"semi": true,
|
4 |
+
"singleQuote": true,
|
5 |
+
"arrowParens": "avoid",
|
6 |
+
"tabWidth": 2,
|
7 |
+
"useTabs": false,
|
8 |
+
"trailingComma": "all",
|
9 |
+
"bracketSpacing": true,
|
10 |
+
"importOrder": [
|
11 |
+
"^(react/(.*)$)|^(react$)",
|
12 |
+
"^(next/(.*)$)|^(next$)",
|
13 |
+
"<THIRD_PARTY_MODULES>",
|
14 |
+
"",
|
15 |
+
"^types$",
|
16 |
+
"^@/types/(.*)$",
|
17 |
+
"^@/config/(.*)$",
|
18 |
+
"^@/lib/(.*)$",
|
19 |
+
"^@/hooks/(.*)$",
|
20 |
+
"^@/components/ui/(.*)$",
|
21 |
+
"^@/components/(.*)$",
|
22 |
+
"^@/registry/(.*)$",
|
23 |
+
"^@/styles/(.*)$",
|
24 |
+
"^@/app/(.*)$",
|
25 |
+
"",
|
26 |
+
"^[./]"
|
27 |
+
],
|
28 |
+
"importOrderSeparation": false,
|
29 |
+
"importOrderSortSpecifiers": true,
|
30 |
+
"importOrderBuiltinModulesToTop": true,
|
31 |
+
"importOrderParserPlugins": ["typescript", "jsx", "decorators-legacy"],
|
32 |
+
"importOrderMergeDuplicateImports": true,
|
33 |
+
"importOrderCombineTypeAndValueImports": true
|
34 |
+
}
|
app/api/upload/route.ts
CHANGED
@@ -1,11 +1,15 @@
|
|
1 |
import { auth } from '@/auth';
|
|
|
2 |
import { nanoid } from '@/lib/utils';
|
3 |
import { kv } from '@vercel/kv';
|
4 |
-
import { format } from 'date-fns';
|
5 |
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
7 |
const session = await auth();
|
8 |
-
console.log('[Ming] ~ POST ~ session:', session);
|
9 |
const email = session?.user?.email;
|
10 |
if (!email) {
|
11 |
return new Response('Unauthorized', {
|
@@ -13,12 +17,23 @@ export async function POST(req: Request) {
|
|
13 |
});
|
14 |
}
|
15 |
|
16 |
-
const
|
17 |
-
|
|
|
|
|
|
|
|
|
18 |
|
19 |
const id = nanoid();
|
20 |
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
await kv.zadd(`user:chat:${email}`, {
|
23 |
score: Date.now(),
|
24 |
member: `chat:${id}`,
|
@@ -28,5 +43,5 @@ export async function POST(req: Request) {
|
|
28 |
member: `chat:${id}`,
|
29 |
});
|
30 |
|
31 |
-
return
|
32 |
}
|
|
|
1 |
import { auth } from '@/auth';
|
2 |
+
import { ChatEntity, MessageBase } from '@/lib/types';
|
3 |
import { nanoid } from '@/lib/utils';
|
4 |
import { kv } from '@vercel/kv';
|
|
|
5 |
|
6 |
+
/**
|
7 |
+
* TODO: this should be replaced with actual upload to S3
|
8 |
+
* @param req
|
9 |
+
* @returns
|
10 |
+
*/
|
11 |
+
export async function POST(req: Request): Promise<Response> {
|
12 |
const session = await auth();
|
|
|
13 |
const email = session?.user?.email;
|
14 |
if (!email) {
|
15 |
return new Response('Unauthorized', {
|
|
|
17 |
});
|
18 |
}
|
19 |
|
20 |
+
const { url, initMessages } = (await req.json()) as {
|
21 |
+
url?: string;
|
22 |
+
file?: File;
|
23 |
+
base64?: string;
|
24 |
+
initMessages?: MessageBase[];
|
25 |
+
};
|
26 |
|
27 |
const id = nanoid();
|
28 |
|
29 |
+
const payload: ChatEntity = {
|
30 |
+
url: url!, // TODO can be uploaded as well
|
31 |
+
id,
|
32 |
+
user: email,
|
33 |
+
messages: initMessages ?? [],
|
34 |
+
};
|
35 |
+
|
36 |
+
await kv.hmset(`chat:${id}`, payload);
|
37 |
await kv.zadd(`user:chat:${email}`, {
|
38 |
score: Date.now(),
|
39 |
member: `chat:${id}`,
|
|
|
43 |
member: `chat:${id}`,
|
44 |
});
|
45 |
|
46 |
+
return Response.json(payload);
|
47 |
}
|
app/chat/[id]/page.tsx
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { nanoid } from '@/lib/utils';
|
2 |
+
import { Chat } from '@/components/chat';
|
3 |
+
|
4 |
+
export default async function Page() {
|
5 |
+
const id = nanoid();
|
6 |
+
|
7 |
+
return <Chat id={id} />;
|
8 |
+
}
|
app/chat/layout.tsx
CHANGED
@@ -11,14 +11,14 @@ export default async function Layout({ children }: ChatLayoutProps) {
|
|
11 |
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
|
12 |
<div
|
13 |
data-state="open"
|
14 |
-
className="peer absolute inset-y-0 z-30 hidden border-r bg-muted duration-300 ease-in-out translate-x-0 lg:flex lg:w-[250px]
|
15 |
>
|
16 |
<Suspense fallback={<Loading />}>
|
17 |
<ChatSidebarList />
|
18 |
</Suspense>
|
19 |
</div>
|
20 |
<Suspense fallback={<Loading />}>
|
21 |
-
<div className="group w-full overflow-auto pl-0 animate-in duration-300 ease-in-out peer-[[data-state=open]]:lg:pl-[250px]
|
22 |
{children}
|
23 |
</div>
|
24 |
</Suspense>
|
|
|
11 |
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
|
12 |
<div
|
13 |
data-state="open"
|
14 |
+
className="peer absolute inset-y-0 z-30 hidden border-r bg-muted duration-300 ease-in-out translate-x-0 lg:flex lg:w-[250px] h-full flex-col dark:bg-zinc-950 overflow-auto py-2"
|
15 |
>
|
16 |
<Suspense fallback={<Loading />}>
|
17 |
<ChatSidebarList />
|
18 |
</Suspense>
|
19 |
</div>
|
20 |
<Suspense fallback={<Loading />}>
|
21 |
+
<div className="group w-full overflow-auto pl-0 animate-in duration-300 ease-in-out peer-[[data-state=open]]:lg:pl-[250px]">
|
22 |
{children}
|
23 |
</div>
|
24 |
</Suspense>
|
app/chat/page.tsx
CHANGED
@@ -1,7 +1,5 @@
|
|
1 |
-
import
|
2 |
-
import { Chat } from '@/components/chat';
|
3 |
|
4 |
export default function Page() {
|
5 |
-
|
6 |
-
return <Chat id={id} />;
|
7 |
}
|
|
|
1 |
+
import ImageSelector from '@/components/chat/ImageSelector';
|
|
|
2 |
|
3 |
export default function Page() {
|
4 |
+
return <ImageSelector />;
|
|
|
5 |
}
|
components/chat-sidebar/ChatCard.tsx
CHANGED
@@ -3,24 +3,35 @@
|
|
3 |
import Link from 'next/link';
|
4 |
import { useParams } from 'next/navigation';
|
5 |
import { cn } from '@/lib/utils';
|
|
|
|
|
6 |
|
7 |
export interface ChatCardProps {
|
8 |
-
|
9 |
-
title: string;
|
10 |
}
|
11 |
|
12 |
-
const ChatCard: React.FC<ChatCardProps> = ({
|
13 |
const { chatId: chatIdFromParam } = useParams();
|
|
|
14 |
return (
|
15 |
<Link
|
16 |
className={cn(
|
17 |
-
'p-
|
18 |
chatIdFromParam === id && 'border-gray-500',
|
19 |
)}
|
20 |
href={`/chat/${id}`}
|
21 |
>
|
22 |
-
<div className="overflow-hidden">
|
23 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
</div>
|
25 |
</Link>
|
26 |
);
|
|
|
3 |
import Link from 'next/link';
|
4 |
import { useParams } from 'next/navigation';
|
5 |
import { cn } from '@/lib/utils';
|
6 |
+
import { ChatEntity } from '@/lib/types';
|
7 |
+
import Image from 'next/image';
|
8 |
|
9 |
export interface ChatCardProps {
|
10 |
+
chat: ChatEntity;
|
|
|
11 |
}
|
12 |
|
13 |
+
const ChatCard: React.FC<ChatCardProps> = ({ chat }) => {
|
14 |
const { chatId: chatIdFromParam } = useParams();
|
15 |
+
const { id, url, messages, user } = chat;
|
16 |
return (
|
17 |
<Link
|
18 |
className={cn(
|
19 |
+
'p-2 m-2 bg-white l:h-[250px] rounded-xl shadow-md flex items-center border border-transparent hover:border-gray-500 transition-all cursor-pointer',
|
20 |
chatIdFromParam === id && 'border-gray-500',
|
21 |
)}
|
22 |
href={`/chat/${id}`}
|
23 |
>
|
24 |
+
<div className="overflow-hidden flex items-center">
|
25 |
+
<Image
|
26 |
+
src={url}
|
27 |
+
alt={url}
|
28 |
+
width={50}
|
29 |
+
height={50}
|
30 |
+
className="rounded w-1/4 "
|
31 |
+
/>
|
32 |
+
<p className="text-xs text-gray-500 w-3/4 ml-2">
|
33 |
+
{messages?.[0].content.slice(0, 50) + ' ...' ?? 'new chat'}
|
34 |
+
</p>
|
35 |
</div>
|
36 |
</Link>
|
37 |
);
|
components/chat-sidebar/ChatListSidebar.tsx
CHANGED
@@ -5,11 +5,10 @@ export interface ChatSidebarListProps {}
|
|
5 |
|
6 |
export default async function ChatSidebarList({}: ChatSidebarListProps) {
|
7 |
const chats = await getKVChats();
|
8 |
-
console.log('[Ming] ~ ChatSidebarList ~ chats:', chats);
|
9 |
return (
|
10 |
<>
|
11 |
{chats.map(chat => (
|
12 |
-
<ChatCard key={chat.id}
|
13 |
))}
|
14 |
</>
|
15 |
);
|
|
|
5 |
|
6 |
export default async function ChatSidebarList({}: ChatSidebarListProps) {
|
7 |
const chats = await getKVChats();
|
|
|
8 |
return (
|
9 |
<>
|
10 |
{chats.map(chat => (
|
11 |
+
<ChatCard key={chat.id} chat={chat} />
|
12 |
))}
|
13 |
</>
|
14 |
);
|
components/chat/ImageSelector.tsx
CHANGED
@@ -1,21 +1,36 @@
|
|
|
|
|
|
1 |
import React from 'react';
|
2 |
import Image from 'next/image';
|
3 |
import useImageUpload from '../../lib/hooks/useImageUpload';
|
4 |
import { fetcher } from '@/lib/utils';
|
|
|
|
|
5 |
|
6 |
export interface ImageSelectorProps {}
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
];
|
14 |
|
15 |
const ImageSelector: React.FC<ImageSelectorProps> = () => {
|
|
|
16 |
const { getRootProps, getInputProps } = useImageUpload();
|
17 |
return (
|
18 |
-
<div className="mx-auto max-w-2xl px-4">
|
19 |
<div className="rounded-lg border bg-background p-8">
|
20 |
<h1 className="mb-2 text-lg font-semibold">Welcome to Vision Agent</h1>
|
21 |
<p>Lets start by choosing an image</p>
|
@@ -32,20 +47,23 @@ const ImageSelector: React.FC<ImageSelectorProps> = () => {
|
|
32 |
You can also choose from below examples we provided
|
33 |
</p>
|
34 |
<div className="flex">
|
35 |
-
{examples.map((
|
36 |
<Image
|
37 |
-
src={
|
38 |
key={index}
|
39 |
width={120}
|
40 |
height={120}
|
41 |
alt="example images"
|
42 |
className="object-cover rounded mr-3 shadow-md hover:scale-105 cursor-pointer transition-transform"
|
43 |
-
onClick={() =>
|
44 |
-
fetcher('/api/upload', {
|
45 |
method: 'POST',
|
46 |
-
body: JSON.stringify({ url
|
47 |
-
})
|
48 |
-
|
|
|
|
|
|
|
49 |
/>
|
50 |
))}
|
51 |
</div>
|
|
|
1 |
+
'use client';
|
2 |
+
|
3 |
import React from 'react';
|
4 |
import Image from 'next/image';
|
5 |
import useImageUpload from '../../lib/hooks/useImageUpload';
|
6 |
import { fetcher } from '@/lib/utils';
|
7 |
+
import { ChatEntity, MessageBase } from '@/lib/types';
|
8 |
+
import { useRouter } from 'next/navigation';
|
9 |
|
10 |
export interface ImageSelectorProps {}
|
11 |
|
12 |
+
type Example = {
|
13 |
+
url: string;
|
14 |
+
initMessages: MessageBase[];
|
15 |
+
};
|
16 |
+
|
17 |
+
const examples: Example[] = [
|
18 |
+
{
|
19 |
+
url: 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/cereal-example.jpg',
|
20 |
+
initMessages: [
|
21 |
+
{ role: 'user', content: 'how many cereals are there in the image?' },
|
22 |
+
],
|
23 |
+
},
|
24 |
+
// 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/people-example.jpeg',
|
25 |
+
// 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/house-exmaple.jpg',
|
26 |
+
// 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/safari-example.png',
|
27 |
];
|
28 |
|
29 |
const ImageSelector: React.FC<ImageSelectorProps> = () => {
|
30 |
+
const router = useRouter();
|
31 |
const { getRootProps, getInputProps } = useImageUpload();
|
32 |
return (
|
33 |
+
<div className="mx-auto max-w-2xl px-4 mt-8">
|
34 |
<div className="rounded-lg border bg-background p-8">
|
35 |
<h1 className="mb-2 text-lg font-semibold">Welcome to Vision Agent</h1>
|
36 |
<p>Lets start by choosing an image</p>
|
|
|
47 |
You can also choose from below examples we provided
|
48 |
</p>
|
49 |
<div className="flex">
|
50 |
+
{examples.map(({ url, initMessages }, index) => (
|
51 |
<Image
|
52 |
+
src={url}
|
53 |
key={index}
|
54 |
width={120}
|
55 |
height={120}
|
56 |
alt="example images"
|
57 |
className="object-cover rounded mr-3 shadow-md hover:scale-105 cursor-pointer transition-transform"
|
58 |
+
onClick={async () => {
|
59 |
+
const resp = await fetcher<ChatEntity>('/api/upload', {
|
60 |
method: 'POST',
|
61 |
+
body: JSON.stringify({ url, initMessages }),
|
62 |
+
});
|
63 |
+
if (resp) {
|
64 |
+
router.push(`/chat/${resp.id}`);
|
65 |
+
}
|
66 |
+
}}
|
67 |
/>
|
68 |
))}
|
69 |
</div>
|
components/chat/index.tsx
CHANGED
@@ -26,12 +26,12 @@ export function Chat({ id, className }: ChatProps) {
|
|
26 |
<ImageSelector />
|
27 |
</div>
|
28 |
<div className="w-1/2 relative overflow-auto">
|
29 |
-
<ChatList messages={messages} />
|
30 |
<ChatScrollAnchor trackVisibility={isLoading} />
|
31 |
</div>
|
32 |
</div>
|
33 |
</div>
|
34 |
-
<ChatPanel
|
35 |
id={id}
|
36 |
isLoading={isLoading}
|
37 |
stop={stop}
|
@@ -40,7 +40,7 @@ export function Chat({ id, className }: ChatProps) {
|
|
40 |
messages={messages}
|
41 |
input={input}
|
42 |
setInput={setInput}
|
43 |
-
/>
|
44 |
</>
|
45 |
);
|
46 |
}
|
|
|
26 |
<ImageSelector />
|
27 |
</div>
|
28 |
<div className="w-1/2 relative overflow-auto">
|
29 |
+
{/* <ChatList messages={messages} /> */}
|
30 |
<ChatScrollAnchor trackVisibility={isLoading} />
|
31 |
</div>
|
32 |
</div>
|
33 |
</div>
|
34 |
+
{/* <ChatPanel
|
35 |
id={id}
|
36 |
isLoading={isLoading}
|
37 |
stop={stop}
|
|
|
40 |
messages={messages}
|
41 |
input={input}
|
42 |
setInput={setInput}
|
43 |
+
/> */}
|
44 |
</>
|
45 |
);
|
46 |
}
|
lib/hooks/useImageUpload.ts
CHANGED
@@ -5,7 +5,6 @@ import { toast } from 'react-hot-toast';
|
|
5 |
import { DatasetImageEntity } from '../types';
|
6 |
|
7 |
const useImageUpload = (options?: Partial<DropzoneOptions>) => {
|
8 |
-
const [, setTarget] = useAtom(datasetAtom);
|
9 |
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
10 |
accept: {
|
11 |
'image/*': ['.jpeg', '.png'],
|
@@ -21,27 +20,27 @@ const useImageUpload = (options?: Partial<DropzoneOptions>) => {
|
|
21 |
try {
|
22 |
const reader = new FileReader();
|
23 |
reader.onloadend = () => {
|
24 |
-
const newImage = reader.result as string;
|
25 |
-
setTarget(prev => {
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
});
|
45 |
};
|
46 |
reader.readAsDataURL(file);
|
47 |
} catch (err) {
|
|
|
5 |
import { DatasetImageEntity } from '../types';
|
6 |
|
7 |
const useImageUpload = (options?: Partial<DropzoneOptions>) => {
|
|
|
8 |
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
9 |
accept: {
|
10 |
'image/*': ['.jpeg', '.png'],
|
|
|
20 |
try {
|
21 |
const reader = new FileReader();
|
22 |
reader.onloadend = () => {
|
23 |
+
// const newImage = reader.result as string;
|
24 |
+
// setTarget(prev => {
|
25 |
+
// // Check if the image already exists in the state
|
26 |
+
// if (
|
27 |
+
// // prev.length >= 10 ||
|
28 |
+
// prev.find(entity => entity.url === newImage)
|
29 |
+
// ) {
|
30 |
+
// // If it does, return the state unchanged
|
31 |
+
// return prev;
|
32 |
+
// } else {
|
33 |
+
// // If it doesn't, add the new image to the state
|
34 |
+
// return [
|
35 |
+
// ...prev,
|
36 |
+
// {
|
37 |
+
// url: newImage,
|
38 |
+
// selected: false,
|
39 |
+
// name: `i-${prev.length + 1}`,
|
40 |
+
// } satisfies DatasetImageEntity,
|
41 |
+
// ];
|
42 |
+
// }
|
43 |
+
// });
|
44 |
};
|
45 |
reader.readAsDataURL(file);
|
46 |
} catch (err) {
|
lib/kv/chat.ts
CHANGED
@@ -1,11 +1,10 @@
|
|
1 |
'use server';
|
2 |
|
3 |
import { revalidatePath } from 'next/cache';
|
4 |
-
import { redirect } from 'next/navigation';
|
5 |
import { kv } from '@vercel/kv';
|
6 |
|
7 |
import { auth } from '@/auth';
|
8 |
-
import {
|
9 |
|
10 |
export async function getKVChats() {
|
11 |
const session = await auth();
|
@@ -27,18 +26,14 @@ export async function getKVChats() {
|
|
27 |
|
28 |
const results = await pipeline.exec();
|
29 |
|
30 |
-
return results as
|
31 |
} catch (error) {
|
32 |
return [];
|
33 |
}
|
34 |
}
|
35 |
|
36 |
-
export async function getKVChat(id: string
|
37 |
-
const chat = await kv.hgetall<
|
38 |
-
|
39 |
-
if (!chat || (userId && chat.userId !== userId)) {
|
40 |
-
return null;
|
41 |
-
}
|
42 |
|
43 |
return chat;
|
44 |
}
|
|
|
1 |
'use server';
|
2 |
|
3 |
import { revalidatePath } from 'next/cache';
|
|
|
4 |
import { kv } from '@vercel/kv';
|
5 |
|
6 |
import { auth } from '@/auth';
|
7 |
+
import { ChatEntity } from '@/lib/types';
|
8 |
|
9 |
export async function getKVChats() {
|
10 |
const session = await auth();
|
|
|
26 |
|
27 |
const results = await pipeline.exec();
|
28 |
|
29 |
+
return results as ChatEntity[];
|
30 |
} catch (error) {
|
31 |
return [];
|
32 |
}
|
33 |
}
|
34 |
|
35 |
+
export async function getKVChat(id: string) {
|
36 |
+
const chat = await kv.hgetall<ChatEntity>(`chat:${id}`);
|
|
|
|
|
|
|
|
|
37 |
|
38 |
return chat;
|
39 |
}
|
lib/types.ts
CHANGED
@@ -1,14 +1,5 @@
|
|
1 |
import { type Message } from 'ai';
|
2 |
|
3 |
-
export interface Chat extends Record<string, any> {
|
4 |
-
id: string;
|
5 |
-
title: string;
|
6 |
-
createdAt: Date;
|
7 |
-
userId: string;
|
8 |
-
path: string;
|
9 |
-
messages: Message[];
|
10 |
-
}
|
11 |
-
|
12 |
export type ServerActionResult<Result> = Promise<
|
13 |
| Result
|
14 |
| {
|
@@ -16,12 +7,30 @@ export type ServerActionResult<Result> = Promise<
|
|
16 |
}
|
17 |
>;
|
18 |
|
|
|
|
|
|
|
19 |
export type DatasetImageEntity = {
|
20 |
url: string;
|
21 |
selected?: boolean;
|
22 |
name: string;
|
23 |
};
|
24 |
|
|
|
|
|
|
|
25 |
export type MessageWithSelectedDataset = Message & {
|
26 |
dataset: DatasetImageEntity[];
|
27 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import { type Message } from 'ai';
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
export type ServerActionResult<Result> = Promise<
|
4 |
| Result
|
5 |
| {
|
|
|
7 |
}
|
8 |
>;
|
9 |
|
10 |
+
/**
|
11 |
+
* @deprecated
|
12 |
+
*/
|
13 |
export type DatasetImageEntity = {
|
14 |
url: string;
|
15 |
selected?: boolean;
|
16 |
name: string;
|
17 |
};
|
18 |
|
19 |
+
/**
|
20 |
+
* @deprecated
|
21 |
+
*/
|
22 |
export type MessageWithSelectedDataset = Message & {
|
23 |
dataset: DatasetImageEntity[];
|
24 |
};
|
25 |
+
|
26 |
+
export type MessageBase = {
|
27 |
+
role: 'user' | 'system' | 'assistant';
|
28 |
+
content: string;
|
29 |
+
};
|
30 |
+
|
31 |
+
export type ChatEntity = {
|
32 |
+
url: string;
|
33 |
+
id: string;
|
34 |
+
user: string; // email
|
35 |
+
messages: MessageBase[];
|
36 |
+
};
|
lib/utils.ts
CHANGED
@@ -16,7 +16,6 @@ export async function fetcher<JSON = any>(
|
|
16 |
init?: RequestInit,
|
17 |
): Promise<JSON> {
|
18 |
const res = await fetch(input, init);
|
19 |
-
console.log('[Ming] ~ res:', res);
|
20 |
|
21 |
if (!res.ok) {
|
22 |
const json = await res.json();
|
|
|
16 |
init?: RequestInit,
|
17 |
): Promise<JSON> {
|
18 |
const res = await fetch(input, init);
|
|
|
19 |
|
20 |
if (!res.ok) {
|
21 |
const json = await res.json();
|
prettier.config.cjs
DELETED
@@ -1,36 +0,0 @@
|
|
1 |
-
/** @type {import('prettier').Config} */
|
2 |
-
module.exports = {
|
3 |
-
endOfLine: 'lf',
|
4 |
-
semi: true,
|
5 |
-
useTabs: true,
|
6 |
-
singleQuote: true,
|
7 |
-
arrowParens: 'avoid',
|
8 |
-
tabWidth: 2,
|
9 |
-
useTabs: false,
|
10 |
-
trailingComma: 'all',
|
11 |
-
bracketSpacing: true,
|
12 |
-
importOrder: [
|
13 |
-
'^(react/(.*)$)|^(react$)',
|
14 |
-
'^(next/(.*)$)|^(next$)',
|
15 |
-
'<THIRD_PARTY_MODULES>',
|
16 |
-
'',
|
17 |
-
'^types$',
|
18 |
-
'^@/types/(.*)$',
|
19 |
-
'^@/config/(.*)$',
|
20 |
-
'^@/lib/(.*)$',
|
21 |
-
'^@/hooks/(.*)$',
|
22 |
-
'^@/components/ui/(.*)$',
|
23 |
-
'^@/components/(.*)$',
|
24 |
-
'^@/registry/(.*)$',
|
25 |
-
'^@/styles/(.*)$',
|
26 |
-
'^@/app/(.*)$',
|
27 |
-
'',
|
28 |
-
'^[./]',
|
29 |
-
],
|
30 |
-
importOrderSeparation: false,
|
31 |
-
importOrderSortSpecifiers: true,
|
32 |
-
importOrderBuiltinModulesToTop: true,
|
33 |
-
importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
|
34 |
-
importOrderMergeDuplicateImports: true,
|
35 |
-
importOrderCombineTypeAndValueImports: true,
|
36 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|