MingruiZhang commited on
Commit
d553ae5
·
unverified ·
1 Parent(s): 7415500

Delete chat and clean up toggle into search params (#48)

Browse files

<img width="246" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/5669963/2b1b7cdc-7671-4211-a9be-32e8954c73e1">

app/api/vision-agent/route.ts CHANGED
@@ -57,7 +57,7 @@ export const POST = withLogging(
57
 
58
  const fetchResponse = await fetch(
59
  `https://api.dev.landing.ai/v1/agent/chat?agent_class=vision_agent&visualize_output=true&self_reflection=${enableSelfReflection}`,
60
- // 'http://localhost:5050/v1/agent/chat?agent_class=vision_agent',
61
  {
62
  method: 'POST',
63
  headers: {
 
57
 
58
  const fetchResponse = await fetch(
59
  `https://api.dev.landing.ai/v1/agent/chat?agent_class=vision_agent&visualize_output=true&self_reflection=${enableSelfReflection}`,
60
+ // `http://localhost:5001/v1/agent/chat?agent_class=vision_agent&visualize_output=true&self_reflection=${enableSelfReflection}`,
61
  {
62
  method: 'POST',
63
  headers: {
components/chat-sidebar/ChatCard.tsx CHANGED
@@ -10,6 +10,8 @@ import clsx from 'clsx';
10
  import Img from '../ui/Img';
11
  import { format } from 'date-fns';
12
  import { cleanInputMessage } from '@/lib/messageUtils';
 
 
13
  // import { format } from 'date-fns';
14
 
15
  type ChatCardProps = PropsWithChildren<{
@@ -36,6 +38,9 @@ export const ChatCardLayout: React.FC<
36
  const ChatCard: React.FC<ChatCardProps> = ({ chat, isAdminView }) => {
37
  const { id: chatIdFromParam } = useParams();
38
  const { id, url, messages, user, updatedAt } = chat;
 
 
 
39
  const firstMessage = cleanInputMessage(messages?.[0]?.content ?? '');
40
  const title = firstMessage
41
  ? firstMessage.length > 50
@@ -47,7 +52,7 @@ const ChatCard: React.FC<ChatCardProps> = ({ chat, isAdminView }) => {
47
  link={isAdminView ? `/all/chat/${id}` : `/chat/${id}`}
48
  classNames={chatIdFromParam === id && 'border-gray-500'}
49
  >
50
- <div className="overflow-hidden flex items-center size-full">
51
  <Img src={url} alt={`chat-${id}-card-image`} className="w-1/4" />
52
  <div className="flex items-start flex-col h-full ml-3 w-3/4">
53
  <p className="text-sm mb-1">{title}</p>
@@ -55,6 +60,13 @@ const ChatCard: React.FC<ChatCardProps> = ({ chat, isAdminView }) => {
55
  {updatedAt ? format(Number(updatedAt), 'yyyy-MM-dd') : '-'}
56
  </p>
57
  {isAdminView && <p className="text-xs text-gray-500">{user}</p>}
 
 
 
 
 
 
 
58
  </div>
59
  </div>
60
  </ChatCardLayout>
 
10
  import Img from '../ui/Img';
11
  import { format } from 'date-fns';
12
  import { cleanInputMessage } from '@/lib/messageUtils';
13
+ import { IconClose } from '../ui/Icons';
14
+ import { removeKVChat } from '@/lib/kv/chat';
15
  // import { format } from 'date-fns';
16
 
17
  type ChatCardProps = PropsWithChildren<{
 
38
  const ChatCard: React.FC<ChatCardProps> = ({ chat, isAdminView }) => {
39
  const { id: chatIdFromParam } = useParams();
40
  const { id, url, messages, user, updatedAt } = chat;
41
+ if (!id) {
42
+ return null;
43
+ }
44
  const firstMessage = cleanInputMessage(messages?.[0]?.content ?? '');
45
  const title = firstMessage
46
  ? firstMessage.length > 50
 
52
  link={isAdminView ? `/all/chat/${id}` : `/chat/${id}`}
53
  classNames={chatIdFromParam === id && 'border-gray-500'}
54
  >
55
+ <div className="overflow-hidden flex items-center size-full group">
56
  <Img src={url} alt={`chat-${id}-card-image`} className="w-1/4" />
57
  <div className="flex items-start flex-col h-full ml-3 w-3/4">
58
  <p className="text-sm mb-1">{title}</p>
 
60
  {updatedAt ? format(Number(updatedAt), 'yyyy-MM-dd') : '-'}
61
  </p>
62
  {isAdminView && <p className="text-xs text-gray-500">{user}</p>}
63
+ <IconClose
64
+ onClick={async e => {
65
+ e.stopPropagation();
66
+ await removeKVChat(id);
67
+ }}
68
+ className="absolute right-4 opacity-0 group-hover:opacity-100 top-1/2 -translate-y-1/2"
69
+ />
70
  </div>
71
  </div>
72
  </ChatCardLayout>
components/chat-sidebar/ChatListSidebar.tsx CHANGED
@@ -19,7 +19,7 @@ const getItemSize = (message: string, isAdminView?: boolean) => {
19
  else return 88;
20
  };
21
 
22
- export default async function ChatSidebarList({
23
  chats,
24
  isAdminView,
25
  }: ChatSidebarListProps) {
 
19
  else return 88;
20
  };
21
 
22
+ export default function ChatSidebarList({
23
  chats,
24
  isAdminView,
25
  }: ChatSidebarListProps) {
components/chat/Composer.tsx CHANGED
@@ -34,8 +34,6 @@ export interface ComposerProps
34
  url?: string;
35
  isAtBottom: boolean;
36
  scrollToBottom: () => void;
37
- enableSelfReflection: boolean;
38
- setEnableSelfReflection: (value: boolean) => void;
39
  }
40
 
41
  export function Composer({
@@ -50,8 +48,6 @@ export function Composer({
50
  messages,
51
  isAtBottom,
52
  scrollToBottom,
53
- enableSelfReflection,
54
- setEnableSelfReflection,
55
  url,
56
  }: ComposerProps) {
57
  const { formRef, onKeyDown } = useEnterSubmit();
@@ -124,15 +120,6 @@ export function Composer({
124
  spellCheck={false}
125
  className="min-h-[60px] resize-none bg-transparent py-[1.3em] focus-within:outline-none sm:text-sm"
126
  />
127
- <div className="flex items-center gap-2 mt-4">
128
- <Switch
129
- checked={enableSelfReflection}
130
- onCheckedChange={checked => {
131
- setEnableSelfReflection(checked);
132
- }}
133
- />
134
- <p className="text-sm">Self Reflection</p>
135
- </div>
136
  </div>
137
  {/* Scroll to bottom Icon */}
138
  <div
 
34
  url?: string;
35
  isAtBottom: boolean;
36
  scrollToBottom: () => void;
 
 
37
  }
38
 
39
  export function Composer({
 
48
  messages,
49
  isAtBottom,
50
  scrollToBottom,
 
 
51
  url,
52
  }: ComposerProps) {
53
  const { formRef, onKeyDown } = useEnterSubmit();
 
120
  spellCheck={false}
121
  className="min-h-[60px] resize-none bg-transparent py-[1.3em] focus-within:outline-none sm:text-sm"
122
  />
 
 
 
 
 
 
 
 
 
123
  </div>
124
  {/* Scroll to bottom Icon */}
125
  <div
components/chat/index.tsx CHANGED
@@ -16,10 +16,8 @@ export interface ChatProps extends React.ComponentProps<'div'> {
16
 
17
  export function Chat({ chat, session, isAdminView }: ChatProps) {
18
  const { url, id } = chat;
19
- const [enableSelfReflection, setEnableSelfReflection] =
20
- useState<boolean>(true);
21
  const { messages, append, reload, stop, isLoading, input, setInput } =
22
- useVisionAgent(chat, enableSelfReflection);
23
 
24
  const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
25
  useScrollAnchor();
@@ -50,8 +48,6 @@ export function Chat({ chat, session, isAdminView }: ChatProps) {
50
  setInput={setInput}
51
  isAtBottom={isAtBottom}
52
  scrollToBottom={scrollToBottom}
53
- enableSelfReflection={enableSelfReflection}
54
- setEnableSelfReflection={setEnableSelfReflection}
55
  />
56
  </div>
57
  )}
 
16
 
17
  export function Chat({ chat, session, isAdminView }: ChatProps) {
18
  const { url, id } = chat;
 
 
19
  const { messages, append, reload, stop, isLoading, input, setInput } =
20
+ useVisionAgent(chat);
21
 
22
  const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
23
  useScrollAnchor();
 
48
  setInput={setInput}
49
  isAtBottom={isAtBottom}
50
  scrollToBottom={scrollToBottom}
 
 
51
  />
52
  </div>
53
  )}
components/project/ProjectChat.tsx CHANGED
@@ -19,18 +19,13 @@ const ProjectChat: React.FC<ChatProps> = ({ mediaList }) => {
19
  // fallback to the first media
20
  const selectedMedia =
21
  mediaList.find(media => media.id === selectedMediaId) ?? mediaList[0];
22
- const [enableSelfReflection, setEnableSelfReflection] =
23
- useState<boolean>(true);
24
  const { messages, append, reload, stop, isLoading, input, setInput } =
25
- useVisionAgent(
26
- {
27
- url: selectedMedia.url,
28
- messages: [],
29
- user: 'does-not-matter@landing.ai',
30
- updatedAt: Date.now(),
31
- },
32
- enableSelfReflection,
33
- );
34
 
35
  const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
36
  useScrollAnchor();
@@ -55,8 +50,6 @@ const ProjectChat: React.FC<ChatProps> = ({ mediaList }) => {
55
  setInput={setInput}
56
  isAtBottom={isAtBottom}
57
  scrollToBottom={scrollToBottom}
58
- enableSelfReflection={enableSelfReflection}
59
- setEnableSelfReflection={setEnableSelfReflection}
60
  />
61
  </div>
62
  </>
 
19
  // fallback to the first media
20
  const selectedMedia =
21
  mediaList.find(media => media.id === selectedMediaId) ?? mediaList[0];
 
 
22
  const { messages, append, reload, stop, isLoading, input, setInput } =
23
+ useVisionAgent({
24
+ url: selectedMedia.url,
25
+ messages: [],
26
+ user: '[email protected]',
27
+ updatedAt: Date.now(),
28
+ });
 
 
 
29
 
30
  const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
31
  useScrollAnchor();
 
50
  setInput={setInput}
51
  isAtBottom={isAtBottom}
52
  scrollToBottom={scrollToBottom}
 
 
53
  />
54
  </div>
55
  </>
lib/hooks/useVisionAgent.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
  generateInputImageMarkdown,
11
  } from '../messageUtils';
12
  import { CLEANED_SEPARATOR } from '../constants';
 
13
 
14
  const uploadBase64 = async (
15
  base64: string,
@@ -49,8 +50,11 @@ const uploadBase64 = async (
49
  }
50
  };
51
 
52
- const useVisionAgent = (chat: ChatEntity, enableSelfReflection = true) => {
53
  const { messages: initialMessages, id, url } = chat;
 
 
 
54
  const {
55
  messages,
56
  append: appendRaw,
@@ -120,7 +124,7 @@ const useVisionAgent = (chat: ChatEntity, enableSelfReflection = true) => {
120
  body: {
121
  url,
122
  id,
123
- enableSelfReflection,
124
  },
125
  });
126
 
 
10
  generateInputImageMarkdown,
11
  } from '../messageUtils';
12
  import { CLEANED_SEPARATOR } from '../constants';
13
+ import { useSearchParams } from 'next/navigation';
14
 
15
  const uploadBase64 = async (
16
  base64: string,
 
50
  }
51
  };
52
 
53
+ const useVisionAgent = (chat: ChatEntity) => {
54
  const { messages: initialMessages, id, url } = chat;
55
+ const searchParams = useSearchParams();
56
+ const reflectionValue = searchParams.get('reflection');
57
+
58
  const {
59
  messages,
60
  append: appendRaw,
 
124
  body: {
125
  url,
126
  id,
127
+ enableSelfReflection: reflectionValue === 'true',
128
  },
129
  });
130
 
lib/kv/chat.ts CHANGED
@@ -23,8 +23,11 @@ export async function getKVChats() {
23
 
24
  const results = (await pipeline.exec()) as ChatEntity[];
25
 
26
- return results.sort((r1, r2) => r2.updatedAt - r1.updatedAt);
 
 
27
  } catch (error) {
 
28
  return [];
29
  }
30
  }
@@ -94,30 +97,22 @@ export async function saveKVChatMessage(id: string, message: MessageBase) {
94
  messages: [...messages, message],
95
  updatedAt: Date.now(),
96
  });
97
- revalidatePath('/chat', 'layout');
98
  }
99
 
100
- export async function removeKVChat({ id, path }: { id: string; path: string }) {
101
- const session = await auth();
102
-
103
- if (!session) {
104
- return {
105
- error: 'Unauthorized',
106
- };
107
- }
108
-
109
- //Convert uid to string for consistent comparison with session.user.id
110
- const uid = String(await kv.hget(`chat:${id}`, 'userId'));
111
 
112
- if (uid !== session?.user?.id) {
113
  return {
114
  error: 'Unauthorized',
115
  };
116
  }
117
 
118
- await kv.del(`chat:${id}`);
119
- await kv.zrem(`user:chat:${session.user.id}`, `chat:${id}`);
 
 
120
 
121
- revalidatePath('/chat/layout', 'layout');
122
- return revalidatePath(path);
123
  }
 
23
 
24
  const results = (await pipeline.exec()) as ChatEntity[];
25
 
26
+ return results
27
+ .filter(r => !!r)
28
+ .sort((r1, r2) => r2.updatedAt - r1.updatedAt);
29
  } catch (error) {
30
+ console.error('getKVChats error:', error);
31
  return [];
32
  }
33
  }
 
97
  messages: [...messages, message],
98
  updatedAt: Date.now(),
99
  });
100
+ return revalidatePath('/chat', 'layout');
101
  }
102
 
103
+ export async function removeKVChat(id: string) {
104
+ const { email } = await authEmail();
 
 
 
 
 
 
 
 
 
105
 
106
+ if (!email) {
107
  return {
108
  error: 'Unauthorized',
109
  };
110
  }
111
 
112
+ await Promise.all([
113
+ kv.zrem(`user:chat:${email}`, `chat:${id}`),
114
+ kv.del(`chat:${id}`),
115
+ ]);
116
 
117
+ return revalidatePath('/chat', 'layout');
 
118
  }
package.json CHANGED
@@ -78,4 +78,4 @@
78
  "typescript": "^5.3.3"
79
  },
80
  "packageManager": "[email protected]"
81
- }
 
78
  "typescript": "^5.3.3"
79
  },
80
  "packageManager": "[email protected]"
81
+ }