Spaces:
Running
Running
wuyiqunLu
commited on
feat: add switch to toggle self reflection (#44)
Browse files<img width="904" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/5b2fd8ee-1ade-41d7-a59f-57c323cd5036">
<img width="884" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/c3373cfc-f8c3-49d9-8415-e40492328303">
- app/api/vision-agent/route.ts +3 -2
- components/chat/Composer.tsx +32 -16
- components/chat/index.tsx +6 -1
- components/project/ProjectChat.tsx +14 -7
- components/ui/Switch.tsx +26 -0
- lib/hooks/useVisionAgent.ts +2 -1
app/api/vision-agent/route.ts
CHANGED
@@ -17,10 +17,11 @@ export const POST = withLogging(
|
|
17 |
messages: MessageBase[];
|
18 |
id: string;
|
19 |
url: string;
|
|
|
20 |
},
|
21 |
request,
|
22 |
) => {
|
23 |
-
const { messages, url } = json;
|
24 |
|
25 |
// const session = await auth();
|
26 |
// if (!session?.user?.email) {
|
@@ -55,7 +56,7 @@ export const POST = withLogging(
|
|
55 |
formData.append('image', url);
|
56 |
|
57 |
const fetchResponse = await fetch(
|
58 |
-
|
59 |
// 'http://localhost:5050/v1/agent/chat?agent_class=vision_agent',
|
60 |
{
|
61 |
method: 'POST',
|
|
|
17 |
messages: MessageBase[];
|
18 |
id: string;
|
19 |
url: string;
|
20 |
+
enableSelfReflection: boolean;
|
21 |
},
|
22 |
request,
|
23 |
) => {
|
24 |
+
const { messages, url, enableSelfReflection } = json;
|
25 |
|
26 |
// const session = await auth();
|
27 |
// if (!session?.user?.email) {
|
|
|
56 |
formData.append('image', url);
|
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',
|
components/chat/Composer.tsx
CHANGED
@@ -21,6 +21,7 @@ import {
|
|
21 |
} from '@/components/ui/Icons';
|
22 |
import { cn } from '@/lib/utils';
|
23 |
import { generateInputImageMarkdown } from '@/lib/messageUtils';
|
|
|
24 |
|
25 |
export interface ComposerProps
|
26 |
extends Pick<
|
@@ -33,6 +34,8 @@ export interface ComposerProps
|
|
33 |
url?: string;
|
34 |
isAtBottom: boolean;
|
35 |
scrollToBottom: () => void;
|
|
|
|
|
36 |
}
|
37 |
|
38 |
export function Composer({
|
@@ -47,6 +50,8 @@ export function Composer({
|
|
47 |
messages,
|
48 |
isAtBottom,
|
49 |
scrollToBottom,
|
|
|
|
|
50 |
url,
|
51 |
}: ComposerProps) {
|
52 |
const { formRef, onKeyDown } = useEnterSubmit();
|
@@ -102,22 +107,33 @@ export function Composer({
|
|
102 |
</Tooltip>
|
103 |
</div>
|
104 |
)}
|
105 |
-
<
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
{/* Scroll to bottom Icon */}
|
122 |
<div
|
123 |
className={cn(
|
|
|
21 |
} from '@/components/ui/Icons';
|
22 |
import { cn } from '@/lib/utils';
|
23 |
import { generateInputImageMarkdown } from '@/lib/messageUtils';
|
24 |
+
import { Switch } from '../ui/Switch';
|
25 |
|
26 |
export interface ComposerProps
|
27 |
extends Pick<
|
|
|
34 |
url?: string;
|
35 |
isAtBottom: boolean;
|
36 |
scrollToBottom: () => void;
|
37 |
+
enableSelfReflection: boolean;
|
38 |
+
setEnableSelfReflection: (value: boolean) => void;
|
39 |
}
|
40 |
|
41 |
export function Composer({
|
|
|
50 |
messages,
|
51 |
isAtBottom,
|
52 |
scrollToBottom,
|
53 |
+
enableSelfReflection,
|
54 |
+
setEnableSelfReflection,
|
55 |
url,
|
56 |
}: ComposerProps) {
|
57 |
const { formRef, onKeyDown } = useEnterSubmit();
|
|
|
107 |
</Tooltip>
|
108 |
</div>
|
109 |
)}
|
110 |
+
<div className="flex flex-col gap-2 w-4/5 px-4">
|
111 |
+
<Textarea
|
112 |
+
ref={inputRef}
|
113 |
+
tabIndex={0}
|
114 |
+
onKeyDown={onKeyDown}
|
115 |
+
rows={1}
|
116 |
+
value={input}
|
117 |
+
disabled={isLoading}
|
118 |
+
onChange={e => setInput(e.target.value)}
|
119 |
+
placeholder={
|
120 |
+
isLoading
|
121 |
+
? 'Vision Agent is thinking...'
|
122 |
+
: 'Ask question about the image.'
|
123 |
+
}
|
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
|
139 |
className={cn(
|
components/chat/index.tsx
CHANGED
@@ -6,6 +6,7 @@ import { ChatEntity } from '@/lib/types';
|
|
6 |
import useVisionAgent from '@/lib/hooks/useVisionAgent';
|
7 |
import { useScrollAnchor } from '@/lib/hooks/useScrollAnchor';
|
8 |
import { Session } from 'next-auth';
|
|
|
9 |
|
10 |
export interface ChatProps extends React.ComponentProps<'div'> {
|
11 |
chat: ChatEntity;
|
@@ -14,8 +15,10 @@ export interface ChatProps extends React.ComponentProps<'div'> {
|
|
14 |
|
15 |
export function Chat({ chat, session }: ChatProps) {
|
16 |
const { url, id } = chat;
|
|
|
|
|
17 |
const { messages, append, reload, stop, isLoading, input, setInput } =
|
18 |
-
useVisionAgent(chat);
|
19 |
|
20 |
const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
|
21 |
useScrollAnchor();
|
@@ -45,6 +48,8 @@ export function Chat({ chat, session }: ChatProps) {
|
|
45 |
setInput={setInput}
|
46 |
isAtBottom={isAtBottom}
|
47 |
scrollToBottom={scrollToBottom}
|
|
|
|
|
48 |
/>
|
49 |
</div>
|
50 |
</>
|
|
|
6 |
import useVisionAgent from '@/lib/hooks/useVisionAgent';
|
7 |
import { useScrollAnchor } from '@/lib/hooks/useScrollAnchor';
|
8 |
import { Session } from 'next-auth';
|
9 |
+
import { useState } from 'react';
|
10 |
|
11 |
export interface ChatProps extends React.ComponentProps<'div'> {
|
12 |
chat: ChatEntity;
|
|
|
15 |
|
16 |
export function Chat({ chat, session }: ChatProps) {
|
17 |
const { url, id } = chat;
|
18 |
+
const [enableSelfReflection, setEnableSelfReflection] =
|
19 |
+
useState<boolean>(true);
|
20 |
const { messages, append, reload, stop, isLoading, input, setInput } =
|
21 |
+
useVisionAgent(chat, enableSelfReflection);
|
22 |
|
23 |
const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
|
24 |
useScrollAnchor();
|
|
|
48 |
setInput={setInput}
|
49 |
isAtBottom={isAtBottom}
|
50 |
scrollToBottom={scrollToBottom}
|
51 |
+
enableSelfReflection={enableSelfReflection}
|
52 |
+
setEnableSelfReflection={setEnableSelfReflection}
|
53 |
/>
|
54 |
</div>
|
55 |
</>
|
components/project/ProjectChat.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
'use client';
|
2 |
|
3 |
import { MediaDetails } from '@/lib/fetch';
|
4 |
-
import React from 'react';
|
5 |
import { ChatList } from '../chat/ChatList';
|
6 |
import useVisionAgent from '@/lib/hooks/useVisionAgent';
|
7 |
import { nanoid } from '@/lib/utils';
|
@@ -19,13 +19,18 @@ 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 { messages, append, reload, stop, isLoading, input, setInput } =
|
23 |
-
useVisionAgent(
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
29 |
|
30 |
const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
|
31 |
useScrollAnchor();
|
@@ -50,6 +55,8 @@ const ProjectChat: React.FC<ChatProps> = ({ mediaList }) => {
|
|
50 |
setInput={setInput}
|
51 |
isAtBottom={isAtBottom}
|
52 |
scrollToBottom={scrollToBottom}
|
|
|
|
|
53 |
/>
|
54 |
</div>
|
55 |
</>
|
|
|
1 |
'use client';
|
2 |
|
3 |
import { MediaDetails } from '@/lib/fetch';
|
4 |
+
import React, { useState } from 'react';
|
5 |
import { ChatList } from '../chat/ChatList';
|
6 |
import useVisionAgent from '@/lib/hooks/useVisionAgent';
|
7 |
import { nanoid } from '@/lib/utils';
|
|
|
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 |
setInput={setInput}
|
56 |
isAtBottom={isAtBottom}
|
57 |
scrollToBottom={scrollToBottom}
|
58 |
+
enableSelfReflection={enableSelfReflection}
|
59 |
+
setEnableSelfReflection={setEnableSelfReflection}
|
60 |
/>
|
61 |
</div>
|
62 |
</>
|
components/ui/Switch.tsx
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
'use client';
|
2 |
+
|
3 |
+
import * as React from 'react';
|
4 |
+
import * as SwitchPrimitive from '@radix-ui/react-switch';
|
5 |
+
|
6 |
+
import { cn } from '@/lib/utils';
|
7 |
+
|
8 |
+
const Switch = React.forwardRef<
|
9 |
+
React.ElementRef<typeof SwitchPrimitive.Root>,
|
10 |
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>
|
11 |
+
>(({ className, ...props }, ref) => (
|
12 |
+
<SwitchPrimitive.Root
|
13 |
+
className={cn(
|
14 |
+
'h-[20px] w-[36px] cursor-pointer rounded-full bg-slate-500 data-[state=checked]:bg-sky-600 ',
|
15 |
+
className,
|
16 |
+
)}
|
17 |
+
{...props}
|
18 |
+
ref={ref}
|
19 |
+
>
|
20 |
+
<SwitchPrimitive.Thumb className="w-[18px] h-[18px] block rounded-full bg-white shadow-sm data-[state=checked]:translate-x-[18px] transition-transform" />
|
21 |
+
</SwitchPrimitive.Root>
|
22 |
+
));
|
23 |
+
|
24 |
+
Switch.displayName = SwitchPrimitive.Root.displayName;
|
25 |
+
|
26 |
+
export { Switch };
|
lib/hooks/useVisionAgent.ts
CHANGED
@@ -49,7 +49,7 @@ const uploadBase64 = async (
|
|
49 |
}
|
50 |
};
|
51 |
|
52 |
-
const useVisionAgent = (chat: ChatEntity) => {
|
53 |
const { messages: initialMessages, id, url } = chat;
|
54 |
const {
|
55 |
messages,
|
@@ -120,6 +120,7 @@ const useVisionAgent = (chat: ChatEntity) => {
|
|
120 |
body: {
|
121 |
url,
|
122 |
id,
|
|
|
123 |
},
|
124 |
});
|
125 |
|
|
|
49 |
}
|
50 |
};
|
51 |
|
52 |
+
const useVisionAgent = (chat: ChatEntity, enableSelfReflection = true) => {
|
53 |
const { messages: initialMessages, id, url } = chat;
|
54 |
const {
|
55 |
messages,
|
|
|
120 |
body: {
|
121 |
url,
|
122 |
id,
|
123 |
+
enableSelfReflection,
|
124 |
},
|
125 |
});
|
126 |
|