1
1
"use client"
2
2
3
- import { ArrowsDownUp , CaretCircleDown , CircleNotch , Sparkle } from "@phosphor-icons/react" ;
3
+ import { ArrowsDownUp , CaretCircleDown , CheckCircle , Circle , CircleNotch , PersonSimpleTaiChi , Sparkle } from "@phosphor-icons/react" ;
4
4
5
5
import { Button } from "@/components/ui/button" ;
6
6
@@ -14,13 +14,20 @@ import { mutate } from "swr";
14
14
import { Sheet , SheetContent } from "@/components/ui/sheet" ;
15
15
import { AgentData } from "../agentCard/agentCard" ;
16
16
import { useEffect , useState } from "react" ;
17
- import { getIconForSlashCommand , getIconFromIconName } from "@/app/common/iconUtils" ;
17
+ import { getAvailableIcons , getIconForSlashCommand , getIconFromIconName } from "@/app/common/iconUtils" ;
18
18
import { Label } from "@/components/ui/label" ;
19
19
import { Checkbox } from "@/components/ui/checkbox" ;
20
20
import { Tooltip , TooltipTrigger } from "@/components/ui/tooltip" ;
21
21
import { TooltipContent } from "@radix-ui/react-tooltip" ;
22
22
import { useAuthenticatedData } from "@/app/common/auth" ;
23
23
import { Popover , PopoverContent , PopoverTrigger } from "@/components/ui/popover" ;
24
+ import { Dialog , DialogClose , DialogContent , DialogFooter , DialogHeader , DialogTitle , DialogTrigger } from "@/components/ui/dialog" ;
25
+ import { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from "@/components/ui/select" ;
26
+ import { convertColorToTextClass , tailwindColors } from "@/app/common/colorUtils" ;
27
+ import { Input } from "@/components/ui/input" ;
28
+ import Link from "next/link" ;
29
+ import { motion } from "framer-motion" ;
30
+
24
31
25
32
interface ChatSideBarProps {
26
33
conversationId : string ;
@@ -54,11 +61,245 @@ export function ChatSidebar({ ...props }: ChatSideBarProps) {
54
61
) ;
55
62
}
56
63
64
+ interface IAgentCreationProps {
65
+ customPrompt : string ;
66
+ selectedModel : string ;
67
+ inputTools : string [ ] ;
68
+ outputModes : string [ ] ;
69
+ }
70
+
71
+ interface AgentError {
72
+ detail : string ;
73
+ }
74
+
75
+ function AgentCreationForm ( props : IAgentCreationProps ) {
76
+ const iconOptions = getAvailableIcons ( ) ;
77
+ const colorOptions = tailwindColors ;
78
+
79
+ const [ isCreating , setIsCreating ] = useState < boolean > ( false ) ;
80
+ const [ customAgentName , setCustomAgentName ] = useState < string | undefined > ( ) ;
81
+ const [ customAgentIcon , setCustomAgentIcon ] = useState < string | undefined > ( ) ;
82
+ const [ customAgentColor , setCustomAgentColor ] = useState < string | undefined > ( ) ;
83
+
84
+ const [ doneCreating , setDoneCreating ] = useState < boolean > ( false ) ;
85
+ const [ createdSlug , setCreatedSlug ] = useState < string | undefined > ( ) ;
86
+ const [ isValid , setIsValid ] = useState < boolean > ( false ) ;
87
+ const [ error , setError ] = useState < string | undefined > ( ) ;
88
+
89
+ function createAgent ( ) {
90
+ if ( isCreating ) {
91
+ return ;
92
+ }
93
+
94
+ setIsCreating ( true ) ;
95
+
96
+ const data = {
97
+ name : customAgentName ,
98
+ icon : customAgentIcon ,
99
+ color : customAgentColor ,
100
+ persona : props . customPrompt ,
101
+ chat_model : props . selectedModel ,
102
+ input_tools : props . inputTools ,
103
+ output_modes : props . outputModes ,
104
+ privacy_level : "private" ,
105
+ } ;
106
+
107
+ const createAgentUrl = `/api/agents` ;
108
+
109
+ fetch ( createAgentUrl , {
110
+ method : "POST" ,
111
+ headers : {
112
+ "Content-Type" : "application/json"
113
+ } ,
114
+ body : JSON . stringify ( data )
115
+ } )
116
+ . then ( ( res ) => res . json ( ) )
117
+ . then ( ( data : AgentData | AgentError ) => {
118
+ console . log ( "Success:" , data ) ;
119
+ if ( 'detail' in data ) {
120
+ setError ( `Error creating agent: ${ data . detail } ` ) ;
121
+ setIsCreating ( false ) ;
122
+ return ;
123
+ }
124
+ setDoneCreating ( true ) ;
125
+ setCreatedSlug ( data . slug ) ;
126
+ setIsCreating ( false ) ;
127
+ } )
128
+ . catch ( ( error ) => {
129
+ console . error ( "Error:" , error ) ;
130
+ setError ( `Error creating agent: ${ error } ` ) ;
131
+ setIsCreating ( false ) ;
132
+ } ) ;
133
+ }
134
+
135
+ useEffect ( ( ) => {
136
+ if ( customAgentName && customAgentIcon && customAgentColor ) {
137
+ setIsValid ( true ) ;
138
+ } else {
139
+ setIsValid ( false ) ;
140
+ }
141
+ } , [ customAgentName , customAgentIcon , customAgentColor ] ) ;
142
+
143
+ return (
144
+
145
+ < Dialog >
146
+ < DialogTrigger asChild >
147
+ < Button
148
+ className = "p-1"
149
+ variant = "ghost"
150
+ >
151
+ Create Agent
152
+ </ Button >
153
+ </ DialogTrigger >
154
+ < DialogContent >
155
+ < DialogHeader >
156
+ {
157
+ doneCreating && createdSlug ? (
158
+ < DialogTitle >
159
+ Created { customAgentName }
160
+ </ DialogTitle >
161
+ ) : (
162
+ < DialogTitle >
163
+ Create a New Agent
164
+ </ DialogTitle >
165
+ )
166
+ }
167
+ < DialogClose />
168
+ </ DialogHeader >
169
+ < div className = "py-4" >
170
+ {
171
+ doneCreating && createdSlug ? (
172
+ < div className = "flex flex-col items-center justify-center gap-4 py-8" >
173
+ < motion . div
174
+ initial = { { scale : 0 } }
175
+ animate = { { scale : 1 } }
176
+ transition = { {
177
+ type : "spring" ,
178
+ stiffness : 260 ,
179
+ damping : 20
180
+ } }
181
+ >
182
+ < CheckCircle
183
+ className = "w-16 h-16 text-green-500"
184
+ weight = "fill"
185
+ />
186
+ </ motion . div >
187
+ < motion . p
188
+ initial = { { opacity : 0 , y : 10 } }
189
+ animate = { { opacity : 1 , y : 0 } }
190
+ transition = { { delay : 0.2 } }
191
+ className = "text-center text-lg font-medium text-accent-foreground"
192
+ >
193
+ Created successfully!
194
+ </ motion . p >
195
+ < motion . div
196
+ initial = { { opacity : 0 , y : 10 } }
197
+ animate = { { opacity : 1 , y : 0 } }
198
+ transition = { { delay : 0.4 } }
199
+ >
200
+ < Link href = { `/agents?agent=${ createdSlug } ` } >
201
+ < Button variant = "secondary" className = "mt-2" >
202
+ Manage Agent
203
+ </ Button >
204
+ </ Link >
205
+ </ motion . div >
206
+ </ div >
207
+ ) :
208
+ < div className = "flex flex-col gap-4" >
209
+ < div >
210
+ < Label htmlFor = "agent_name" > Name</ Label >
211
+ < Input
212
+ id = "agent_name"
213
+ className = "w-full p-2 border mt-4 border-slate-500 rounded-lg"
214
+ disabled = { isCreating }
215
+ value = { customAgentName }
216
+ onChange = { ( e ) => setCustomAgentName ( e . target . value ) }
217
+ />
218
+ </ div >
219
+ < div className = "flex gap-4" >
220
+ < div className = "flex-1" >
221
+ < Select onValueChange = { setCustomAgentColor } defaultValue = { customAgentColor } >
222
+ < SelectTrigger className = "w-full dark:bg-muted" disabled = { isCreating } >
223
+ < SelectValue placeholder = "Color" />
224
+ </ SelectTrigger >
225
+ < SelectContent className = "items-center space-y-1 inline-flex flex-col" >
226
+ { colorOptions . map ( ( colorOption ) => (
227
+ < SelectItem key = { colorOption } value = { colorOption } >
228
+ < div className = "flex items-center space-x-2" >
229
+ < Circle
230
+ className = { `w-6 h-6 mr-2 ${ convertColorToTextClass ( colorOption ) } ` }
231
+ weight = "fill"
232
+ />
233
+ { colorOption }
234
+ </ div >
235
+ </ SelectItem >
236
+ ) ) }
237
+ </ SelectContent >
238
+ </ Select >
239
+ </ div >
240
+ < div className = "flex-1" >
241
+ < Select onValueChange = { setCustomAgentIcon } defaultValue = { customAgentIcon } >
242
+ < SelectTrigger className = "w-full dark:bg-muted" disabled = { isCreating } >
243
+ < SelectValue placeholder = "Icon" />
244
+ </ SelectTrigger >
245
+ < SelectContent className = "items-center space-y-1 inline-flex flex-col" >
246
+ { iconOptions . map ( ( iconOption ) => (
247
+ < SelectItem key = { iconOption } value = { iconOption } >
248
+ < div className = "flex items-center space-x-2" >
249
+ { getIconFromIconName (
250
+ iconOption ,
251
+ customAgentColor ?? "gray" ,
252
+ "w-6" ,
253
+ "h-6" ,
254
+ ) }
255
+ { iconOption }
256
+ </ div >
257
+ </ SelectItem >
258
+ ) ) }
259
+ </ SelectContent >
260
+ </ Select >
261
+ </ div >
262
+ </ div >
263
+ </ div >
264
+ }
265
+ </ div >
266
+ < DialogFooter >
267
+ {
268
+ error && (
269
+ < div className = "text-red-500 text-sm" >
270
+ { error }
271
+ </ div >
272
+ )
273
+ }
274
+ {
275
+ ! doneCreating && (
276
+ < Button
277
+ type = "submit"
278
+ onClick = { ( ) => createAgent ( ) }
279
+ disabled = { isCreating || ! isValid }
280
+ >
281
+ {
282
+ isCreating ?
283
+ < CircleNotch className = "animate-spin" />
284
+ :
285
+ < PersonSimpleTaiChi />
286
+ }
287
+ Create
288
+ </ Button >
289
+ )
290
+ }
291
+ < DialogClose />
292
+ </ DialogFooter >
293
+ </ DialogContent >
294
+ </ Dialog >
295
+
296
+ )
297
+ }
57
298
58
299
function ChatSidebarInternal ( { ...props } : ChatSideBarProps ) {
59
300
const [ isEditable , setIsEditable ] = useState < boolean > ( false ) ;
60
301
const { data : agentConfigurationOptions , error : agentConfigurationOptionsError } =
61
- useSWR < AgentConfigurationOptions > ( "/api/agents/options" , fetcher ) ;
302
+ useSWR < AgentConfigurationOptions > ( "/api/agents/options" , fetcher ) ;
62
303
63
304
const { data : agentData , isLoading : agentDataLoading , error : agentDataError } = useSWR < AgentData > ( `/api/agents/conversation?conversation_id=${ props . conversationId } ` , fetcher ) ;
64
305
const {
@@ -211,9 +452,20 @@ function ChatSidebarInternal({ ...props }: ChatSideBarProps) {
211
452
</ a >
212
453
</ div >
213
454
) : (
214
- < div className = "flex items-center relative text-sm" >
215
- { getIconFromIconName ( "lightbulb" , "orange" ) }
216
- Chat Options
455
+ < div className = "flex items-center relative text-sm justify-between" >
456
+ < p >
457
+ Chat Options
458
+ </ p >
459
+ {
460
+ isEditable && customPrompt && ! isDefaultAgent && selectedModel && (
461
+ < AgentCreationForm
462
+ customPrompt = { customPrompt }
463
+ selectedModel = { selectedModel }
464
+ inputTools = { inputTools ?? [ ] }
465
+ outputModes = { outputModes ?? [ ] }
466
+ />
467
+ )
468
+ }
217
469
</ div >
218
470
)
219
471
}
0 commit comments