wu981526092 commited on
Commit
2b184aa
·
1 Parent(s): a16dbcd

Complete workflow redesign with ChatGPT-inspired UX

Browse files

Major Changes:
- Create dedicated Assistant Builder page (ChatGPT GPT Builder style)
- Add Simple Chat page for quick conversations
- Redesign Home page with clear user workflow paths
- Update sidebar navigation to reflect new structure
- Add Playground redirect page to guide users
- Improve system prompt visibility in Assistant Builder
- Create 3 clear user paths: Quick Chat, Build Assistant, Use Template

Features:
- Assistant Builder with large instruction area and real-time testing
- Simple Chat with conversation starters and assistant creation
- Clear workflow guidance for different user needs
- Professional ChatGPT-inspired design language

frontend/src/App.tsx CHANGED
@@ -1,7 +1,9 @@
1
  import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
2
  import { Layout } from './components/Layout'
3
  import { Home } from './pages/Home'
4
- import { Playground } from './pages/Playground'
 
 
5
  import { Models } from './pages/Models'
6
  import { Assistants } from './pages/Assistants'
7
  import { Community } from './pages/Community'
@@ -14,6 +16,8 @@ function App() {
14
  <Route path="/" element={<Layout />}>
15
  <Route index element={<Home />} />
16
  <Route path="playground" element={<Playground />} />
 
 
17
  <Route path="models" element={<Models />} />
18
  <Route path="assistants" element={<Assistants />} />
19
  <Route path="community" element={<Community />} />
 
1
  import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
2
  import { Layout } from './components/Layout'
3
  import { Home } from './pages/Home'
4
+ import { Playground } from './pages/PlaygroundRedirect'
5
+ import { SimpleChat } from './pages/SimpleChat'
6
+ import { AssistantBuilder } from './pages/AssistantBuilder'
7
  import { Models } from './pages/Models'
8
  import { Assistants } from './pages/Assistants'
9
  import { Community } from './pages/Community'
 
16
  <Route path="/" element={<Layout />}>
17
  <Route index element={<Home />} />
18
  <Route path="playground" element={<Playground />} />
19
+ <Route path="chat" element={<SimpleChat />} />
20
+ <Route path="assistant-builder" element={<AssistantBuilder />} />
21
  <Route path="models" element={<Models />} />
22
  <Route path="assistants" element={<Assistants />} />
23
  <Route path="community" element={<Community />} />
frontend/src/components/Sidebar.tsx CHANGED
@@ -18,28 +18,34 @@ const navigation = [
18
  description: 'Overview and getting started'
19
  },
20
  {
21
- name: 'Chat Playground',
22
- href: '/playground',
23
  icon: MessageSquare,
24
- description: 'AI chatbot with conversation history'
25
  },
26
  {
27
- name: 'Model Catalog',
28
- href: '/models',
29
- icon: BookOpen,
30
- description: 'Browse and manage AI models'
31
  },
32
  {
33
  name: 'My Assistants',
34
  href: '/assistants',
35
- icon: Bot,
36
- description: 'Manage your custom AI assistants'
37
  },
38
  {
39
  name: 'Community',
40
  href: '/community',
41
- icon: Users,
42
- description: 'Discover and share assistant templates'
 
 
 
 
 
 
43
  },
44
  {
45
  name: 'Settings',
@@ -71,7 +77,7 @@ export function Sidebar() {
71
  <div className="flex-1 px-3 py-4 space-y-8">
72
  <div>
73
  <h2 className="mb-2 px-3 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
74
- Get started
75
  </h2>
76
  <nav className="space-y-1">
77
  {navigation.map((item) => {
 
18
  description: 'Overview and getting started'
19
  },
20
  {
21
+ name: 'Quick Chat',
22
+ href: '/chat',
23
  icon: MessageSquare,
24
+ description: 'Simple AI conversations'
25
  },
26
  {
27
+ name: 'Assistant Builder',
28
+ href: '/assistant-builder',
29
+ icon: Bot,
30
+ description: 'Create custom AI assistants'
31
  },
32
  {
33
  name: 'My Assistants',
34
  href: '/assistants',
35
+ icon: Users,
36
+ description: 'Manage your saved assistants'
37
  },
38
  {
39
  name: 'Community',
40
  href: '/community',
41
+ icon: BookOpen,
42
+ description: 'Discover assistant templates'
43
+ },
44
+ {
45
+ name: 'Models',
46
+ href: '/models',
47
+ icon: Brain,
48
+ description: 'Manage AI models'
49
  },
50
  {
51
  name: 'Settings',
 
77
  <div className="flex-1 px-3 py-4 space-y-8">
78
  <div>
79
  <h2 className="mb-2 px-3 text-xs font-semibold text-muted-foreground uppercase tracking-wide">
80
+ Features
81
  </h2>
82
  <nav className="space-y-1">
83
  {navigation.map((item) => {
frontend/src/pages/AssistantBuilder.tsx ADDED
@@ -0,0 +1,347 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from 'react'
2
+ import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
3
+ import { Button } from '@/components/ui/button'
4
+ import { Input } from '@/components/ui/input'
5
+ import { Textarea } from '@/components/ui/textarea'
6
+ import { Badge } from '@/components/ui/badge'
7
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
8
+ import {
9
+ Bot,
10
+ MessageSquare,
11
+ Settings,
12
+ Save,
13
+ Play,
14
+ Sparkles,
15
+ ArrowLeft,
16
+ Send,
17
+ User,
18
+ Brain
19
+ } from 'lucide-react'
20
+
21
+ export function AssistantBuilder() {
22
+ const [assistantConfig, setAssistantConfig] = useState({
23
+ name: '',
24
+ description: '',
25
+ instructions: '',
26
+ model: 'Qwen/Qwen3-30B-A3B',
27
+ temperature: 0.7,
28
+ maxTokens: 2000,
29
+ conversation_starters: ['', '', '', '']
30
+ })
31
+
32
+ const [testMessages, setTestMessages] = useState<Array<{role: 'user' | 'assistant', content: string}>>([])
33
+ const [testInput, setTestInput] = useState('')
34
+ const [isTesting, setIsTesting] = useState(false)
35
+
36
+ const updateConfig = (field: string, value: any) => {
37
+ setAssistantConfig(prev => ({ ...prev, [field]: value }))
38
+ }
39
+
40
+ const updateConversationStarter = (index: number, value: string) => {
41
+ const starters = [...assistantConfig.conversation_starters]
42
+ starters[index] = value
43
+ updateConfig('conversation_starters', starters)
44
+ }
45
+
46
+ const testAssistant = async () => {
47
+ if (!testInput.trim()) return
48
+
49
+ const userMessage = { role: 'user' as const, content: testInput }
50
+ setTestMessages(prev => [...prev, userMessage])
51
+ setTestInput('')
52
+ setIsTesting(true)
53
+
54
+ try {
55
+ const baseUrl = `${window.location.protocol}//${window.location.host}`
56
+ const messages = [...testMessages, userMessage]
57
+
58
+ const response = await fetch(`${baseUrl}/chat/completions`, {
59
+ method: 'POST',
60
+ headers: { 'Content-Type': 'application/json' },
61
+ body: JSON.stringify({
62
+ messages: [
63
+ { role: 'system', content: assistantConfig.instructions },
64
+ ...messages
65
+ ],
66
+ temperature: assistantConfig.temperature,
67
+ max_tokens: assistantConfig.maxTokens
68
+ })
69
+ })
70
+
71
+ if (response.ok) {
72
+ const data = await response.json()
73
+ const assistantMessage = {
74
+ role: 'assistant' as const,
75
+ content: data.choices[0]?.message?.content || 'No response'
76
+ }
77
+ setTestMessages(prev => [...prev, assistantMessage])
78
+ }
79
+ } catch (error) {
80
+ console.error('Test failed:', error)
81
+ } finally {
82
+ setIsTesting(false)
83
+ }
84
+ }
85
+
86
+ const saveAssistant = () => {
87
+ const assistant = {
88
+ id: `assistant_${Date.now()}`,
89
+ ...assistantConfig,
90
+ createdAt: new Date().toISOString(),
91
+ systemPrompt: assistantConfig.instructions
92
+ }
93
+
94
+ try {
95
+ const saved = JSON.parse(localStorage.getItem('savedAssistants') || '[]')
96
+ saved.push(assistant)
97
+ localStorage.setItem('savedAssistants', JSON.stringify(saved))
98
+
99
+ alert('Assistant saved successfully!')
100
+ window.location.href = '/assistants'
101
+ } catch (error) {
102
+ console.error('Failed to save assistant:', error)
103
+ }
104
+ }
105
+
106
+ return (
107
+ <div className="min-h-screen bg-gray-50">
108
+ {/* Header */}
109
+ <div className="bg-white border-b border-gray-200">
110
+ <div className="max-w-7xl mx-auto px-6 py-4">
111
+ <div className="flex items-center justify-between">
112
+ <div className="flex items-center gap-4">
113
+ <Button
114
+ variant="ghost"
115
+ size="sm"
116
+ onClick={() => window.location.href = '/assistants'}
117
+ className="flex items-center gap-2"
118
+ >
119
+ <ArrowLeft className="h-4 w-4" />
120
+ Back
121
+ </Button>
122
+ <div className="flex items-center gap-3">
123
+ <div className="w-8 h-8 bg-purple-600 rounded-lg flex items-center justify-center">
124
+ <Bot className="h-5 w-5 text-white" />
125
+ </div>
126
+ <div>
127
+ <h1 className="text-xl font-semibold">Assistant Builder</h1>
128
+ <p className="text-sm text-gray-500">Create your custom AI assistant</p>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ <div className="flex items-center gap-3">
133
+ <Button variant="outline" onClick={() => setTestMessages([])}>
134
+ Clear Test
135
+ </Button>
136
+ <Button onClick={saveAssistant} className="flex items-center gap-2">
137
+ <Save className="h-4 w-4" />
138
+ Save Assistant
139
+ </Button>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <div className="max-w-7xl mx-auto p-6">
146
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 h-[calc(100vh-160px)]">
147
+
148
+ {/* Configuration Panel */}
149
+ <div className="space-y-6">
150
+ <Card>
151
+ <CardHeader>
152
+ <CardTitle className="flex items-center gap-2">
153
+ <Settings className="h-5 w-5" />
154
+ Configure Assistant
155
+ </CardTitle>
156
+ </CardHeader>
157
+ <CardContent className="space-y-6">
158
+ {/* Basic Info */}
159
+ <div className="space-y-4">
160
+ <div>
161
+ <label className="text-sm font-medium mb-2 block">Name</label>
162
+ <Input
163
+ placeholder="e.g., Marketing Expert"
164
+ value={assistantConfig.name}
165
+ onChange={(e) => updateConfig('name', e.target.value)}
166
+ />
167
+ </div>
168
+
169
+ <div>
170
+ <label className="text-sm font-medium mb-2 block">Description</label>
171
+ <Input
172
+ placeholder="What does this assistant do?"
173
+ value={assistantConfig.description}
174
+ onChange={(e) => updateConfig('description', e.target.value)}
175
+ />
176
+ </div>
177
+ </div>
178
+
179
+ {/* Instructions - Much Larger */}
180
+ <div>
181
+ <label className="text-sm font-medium mb-2 block">Instructions</label>
182
+ <p className="text-xs text-gray-500 mb-2">
183
+ Define your assistant's behavior, expertise, and communication style
184
+ </p>
185
+ <Textarea
186
+ className="min-h-[200px] font-mono text-sm"
187
+ placeholder={`You are a helpful marketing expert assistant. You should:
188
+
189
+ 1. Provide actionable marketing advice
190
+ 2. Use data-driven insights when possible
191
+ 3. Explain complex concepts simply
192
+ 4. Ask clarifying questions to give better recommendations
193
+ 5. Stay up-to-date with current marketing trends
194
+
195
+ Your responses should be professional yet approachable, and always include specific examples when helpful.`}
196
+ value={assistantConfig.instructions}
197
+ onChange={(e) => updateConfig('instructions', e.target.value)}
198
+ />
199
+ </div>
200
+
201
+ {/* Model Settings */}
202
+ <div className="grid grid-cols-2 gap-4">
203
+ <div>
204
+ <label className="text-sm font-medium mb-2 block">Temperature</label>
205
+ <Input
206
+ type="number"
207
+ min="0"
208
+ max="2"
209
+ step="0.1"
210
+ value={assistantConfig.temperature}
211
+ onChange={(e) => updateConfig('temperature', parseFloat(e.target.value))}
212
+ />
213
+ </div>
214
+ <div>
215
+ <label className="text-sm font-medium mb-2 block">Max Tokens</label>
216
+ <Input
217
+ type="number"
218
+ min="100"
219
+ max="4000"
220
+ step="100"
221
+ value={assistantConfig.maxTokens}
222
+ onChange={(e) => updateConfig('maxTokens', parseInt(e.target.value))}
223
+ />
224
+ </div>
225
+ </div>
226
+
227
+ {/* Conversation Starters */}
228
+ <div>
229
+ <label className="text-sm font-medium mb-2 block">Conversation Starters</label>
230
+ <p className="text-xs text-gray-500 mb-2">
231
+ Suggested prompts to help users get started
232
+ </p>
233
+ <div className="space-y-2">
234
+ {assistantConfig.conversation_starters.map((starter, index) => (
235
+ <Input
236
+ key={index}
237
+ placeholder={`Conversation starter ${index + 1}`}
238
+ value={starter}
239
+ onChange={(e) => updateConversationStarter(index, e.target.value)}
240
+ />
241
+ ))}
242
+ </div>
243
+ </div>
244
+ </CardContent>
245
+ </Card>
246
+ </div>
247
+
248
+ {/* Test Panel */}
249
+ <div className="flex flex-col">
250
+ <Card className="flex-1 flex flex-col">
251
+ <CardHeader className="pb-3">
252
+ <CardTitle className="flex items-center gap-2">
253
+ <MessageSquare className="h-5 w-5" />
254
+ Test Your Assistant
255
+ {assistantConfig.name && (
256
+ <Badge variant="outline" className="ml-auto">
257
+ {assistantConfig.name}
258
+ </Badge>
259
+ )}
260
+ </CardTitle>
261
+ </CardHeader>
262
+
263
+ {/* Messages Area */}
264
+ <CardContent className="flex-1 flex flex-col">
265
+ <div className="flex-1 bg-gray-50 rounded-lg p-4 mb-4 overflow-y-auto space-y-4 min-h-[300px]">
266
+ {testMessages.length === 0 ? (
267
+ <div className="flex flex-col items-center justify-center h-full text-center text-gray-500">
268
+ <Bot className="h-12 w-12 mb-4 text-gray-300" />
269
+ <p className="text-sm">Start testing your assistant</p>
270
+ <p className="text-xs text-gray-400 mt-1">Messages will appear here</p>
271
+ </div>
272
+ ) : (
273
+ testMessages.map((msg, index) => (
274
+ <div key={index} className={`flex gap-3 ${msg.role === 'assistant' ? 'justify-start' : 'justify-end'}`}>
275
+ {msg.role === 'assistant' && (
276
+ <div className="w-8 h-8 bg-purple-600 rounded-full flex items-center justify-center">
277
+ <Bot className="h-4 w-4 text-white" />
278
+ </div>
279
+ )}
280
+ <div className={`max-w-[80%] p-3 rounded-lg ${
281
+ msg.role === 'assistant'
282
+ ? 'bg-white border border-gray-200'
283
+ : 'bg-blue-600 text-white'
284
+ }`}>
285
+ <p className="text-sm whitespace-pre-wrap">{msg.content}</p>
286
+ </div>
287
+ {msg.role === 'user' && (
288
+ <div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
289
+ <User className="h-4 w-4 text-white" />
290
+ </div>
291
+ )}
292
+ </div>
293
+ ))
294
+ )}
295
+ </div>
296
+
297
+ {/* Input Area */}
298
+ <div className="flex gap-2">
299
+ <Input
300
+ placeholder="Test your assistant..."
301
+ value={testInput}
302
+ onChange={(e) => setTestInput(e.target.value)}
303
+ onKeyPress={(e) => e.key === 'Enter' && !e.shiftKey && testAssistant()}
304
+ disabled={isTesting}
305
+ />
306
+ <Button
307
+ onClick={testAssistant}
308
+ disabled={isTesting || !testInput.trim()}
309
+ size="sm"
310
+ >
311
+ {isTesting ? (
312
+ <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
313
+ ) : (
314
+ <Send className="h-4 w-4" />
315
+ )}
316
+ </Button>
317
+ </div>
318
+
319
+ {/* Conversation Starters in Test Area */}
320
+ {assistantConfig.conversation_starters.some(s => s.trim()) && (
321
+ <div className="mt-3 pt-3 border-t border-gray-200">
322
+ <p className="text-xs text-gray-500 mb-2">Try these:</p>
323
+ <div className="flex flex-wrap gap-2">
324
+ {assistantConfig.conversation_starters
325
+ .filter(s => s.trim())
326
+ .map((starter, index) => (
327
+ <Button
328
+ key={index}
329
+ variant="outline"
330
+ size="sm"
331
+ className="text-xs h-auto py-1 px-2"
332
+ onClick={() => setTestInput(starter)}
333
+ >
334
+ {starter}
335
+ </Button>
336
+ ))}
337
+ </div>
338
+ </div>
339
+ )}
340
+ </CardContent>
341
+ </Card>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </div>
346
+ )
347
+ }
frontend/src/pages/Home.tsx CHANGED
@@ -55,36 +55,40 @@ export function Home() {
55
 
56
  const quickActions = [
57
  {
58
- title: 'Start Chatting',
59
- description: 'Begin conversations with AI models',
60
  icon: MessageSquare,
61
- href: '/playground',
62
  color: 'bg-blue-600',
63
- primary: true
 
64
  },
65
  {
66
- title: 'Browse Models',
67
- description: 'Manage and load AI models',
68
- icon: BookOpen,
69
- href: '/models',
70
- color: 'bg-green-600',
71
- primary: false
72
- },
73
- {
74
- title: 'My Assistants',
75
- description: 'Custom AI assistant configurations',
76
  icon: Bot,
77
- href: '/assistants',
78
  color: 'bg-purple-600',
79
- primary: false
 
80
  },
81
  {
82
- title: 'Community',
83
- description: 'Discover and share templates',
84
  icon: Users,
85
  href: '/community',
86
- color: 'bg-orange-600',
87
- primary: false
 
 
 
 
 
 
 
 
 
 
88
  }
89
  ]
90
 
@@ -154,24 +158,62 @@ export function Home() {
154
 
155
  {/* Quick Actions */}
156
  <section>
157
- <h2 className="text-2xl font-semibold mb-6 text-center">Quick Actions</h2>
158
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
159
- {quickActions.map((action) => (
 
 
 
 
 
 
 
160
  <Card
161
  key={action.title}
162
- className={`hover:shadow-lg transition-all duration-200 cursor-pointer hover:-translate-y-1 ${
163
- action.primary ? 'ring-2 ring-blue-200' : ''
164
- }`}
165
  onClick={() => window.location.href = action.href}
166
  >
167
- <CardContent className="p-6 text-center space-y-4">
168
- <div className={`w-12 h-12 ${action.color} rounded-lg flex items-center justify-center mx-auto`}>
169
- <action.icon className="h-6 w-6 text-white" />
 
 
 
 
 
 
 
170
  </div>
171
  <div>
172
- <h3 className="font-semibold text-lg">{action.title}</h3>
173
- <p className="text-sm text-gray-600 mt-1">{action.description}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  </div>
 
 
 
 
 
175
  </CardContent>
176
  </Card>
177
  ))}
@@ -222,21 +264,27 @@ export function Home() {
222
  </div>
223
  </section>
224
 
225
- {/* Get Started */}
226
- <section className="text-center bg-gray-50 rounded-2xl p-8">
227
- <h2 className="text-2xl font-semibold mb-4">Ready to Get Started?</h2>
228
- <p className="text-gray-600 mb-6 max-w-2xl mx-auto">
229
- Choose your preferred way to begin. Load models for local processing or start chatting with cloud APIs immediately.
230
- </p>
231
- <div className="flex items-center justify-center gap-4">
232
- <Button onClick={() => window.location.href = '/playground'}>
233
- <MessageSquare className="h-4 w-4 mr-2" />
234
- Open Playground
235
- </Button>
236
- <Button variant="outline" onClick={() => window.location.href = '/assistants'}>
237
- <Bot className="h-4 w-4 mr-2" />
238
- Create Assistant
239
- </Button>
 
 
 
 
 
 
240
  </div>
241
  </section>
242
  </div>
 
55
 
56
  const quickActions = [
57
  {
58
+ title: 'Quick Chat',
59
+ description: 'Start chatting immediately - no setup required',
60
  icon: MessageSquare,
61
+ href: '/chat',
62
  color: 'bg-blue-600',
63
+ primary: true,
64
+ badge: 'Instant'
65
  },
66
  {
67
+ title: 'Build Assistant',
68
+ description: 'Create your custom AI assistant with specialized skills',
 
 
 
 
 
 
 
 
69
  icon: Bot,
70
+ href: '/assistant-builder',
71
  color: 'bg-purple-600',
72
+ primary: true,
73
+ badge: 'Advanced'
74
  },
75
  {
76
+ title: 'Use Template',
77
+ description: 'Start with pre-built assistant templates from the community',
78
  icon: Users,
79
  href: '/community',
80
+ color: 'bg-green-600',
81
+ primary: true,
82
+ badge: 'Popular'
83
+ },
84
+ {
85
+ title: 'Manage Models',
86
+ description: 'Configure and load AI models',
87
+ icon: BookOpen,
88
+ href: '/models',
89
+ color: 'bg-gray-600',
90
+ primary: false,
91
+ badge: null
92
  }
93
  ]
94
 
 
158
 
159
  {/* Quick Actions */}
160
  <section>
161
+ <div className="text-center mb-8">
162
+ <h2 className="text-2xl font-semibold mb-3">Choose Your Path</h2>
163
+ <p className="text-gray-600 max-w-2xl mx-auto">
164
+ Select the best way to get started based on your needs and experience level
165
+ </p>
166
+ </div>
167
+
168
+ {/* Primary Actions */}
169
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
170
+ {quickActions.filter(action => action.primary).map((action) => (
171
  <Card
172
  key={action.title}
173
+ className="hover:shadow-xl transition-all duration-300 cursor-pointer hover:-translate-y-2 border-2 border-transparent hover:border-blue-200"
 
 
174
  onClick={() => window.location.href = action.href}
175
  >
176
+ <CardContent className="p-8 text-center space-y-6">
177
+ <div className="relative">
178
+ <div className={`w-16 h-16 ${action.color} rounded-2xl flex items-center justify-center mx-auto`}>
179
+ <action.icon className="h-8 w-8 text-white" />
180
+ </div>
181
+ {action.badge && (
182
+ <Badge className="absolute -top-2 -right-2 bg-white text-gray-700 border border-gray-200">
183
+ {action.badge}
184
+ </Badge>
185
+ )}
186
  </div>
187
  <div>
188
+ <h3 className="font-bold text-xl mb-2">{action.title}</h3>
189
+ <p className="text-gray-600 text-sm leading-relaxed">{action.description}</p>
190
+ </div>
191
+ <Button className="w-full" size="lg">
192
+ Get Started
193
+ <ArrowRight className="h-4 w-4 ml-2" />
194
+ </Button>
195
+ </CardContent>
196
+ </Card>
197
+ ))}
198
+ </div>
199
+
200
+ {/* Secondary Actions */}
201
+ <div className="grid grid-cols-1 md:grid-cols-1 gap-4">
202
+ {quickActions.filter(action => !action.primary).map((action) => (
203
+ <Card
204
+ key={action.title}
205
+ className="hover:shadow-md transition-all duration-200 cursor-pointer hover:-translate-y-1"
206
+ onClick={() => window.location.href = action.href}
207
+ >
208
+ <CardContent className="p-4 flex items-center gap-4">
209
+ <div className={`w-12 h-12 ${action.color} rounded-lg flex items-center justify-center`}>
210
+ <action.icon className="h-6 w-6 text-white" />
211
  </div>
212
+ <div className="flex-1">
213
+ <h3 className="font-semibold text-base">{action.title}</h3>
214
+ <p className="text-sm text-gray-600">{action.description}</p>
215
+ </div>
216
+ <ArrowRight className="h-5 w-5 text-gray-400" />
217
  </CardContent>
218
  </Card>
219
  ))}
 
264
  </div>
265
  </section>
266
 
267
+ {/* Recommended Path */}
268
+ <section className="bg-gradient-to-r from-blue-50 to-purple-50 rounded-2xl p-8 border border-blue-200">
269
+ <div className="text-center">
270
+ <div className="w-12 h-12 bg-blue-600 rounded-xl flex items-center justify-center mx-auto mb-4">
271
+ <Zap className="h-6 w-6 text-white" />
272
+ </div>
273
+ <h2 className="text-2xl font-semibold mb-4">New to AI Assistants?</h2>
274
+ <p className="text-gray-600 mb-6 max-w-2xl mx-auto">
275
+ We recommend starting with <strong>Quick Chat</strong> to get familiar with AI conversations,
276
+ then try our <strong>Community Templates</strong> to see what's possible.
277
+ </p>
278
+ <div className="flex items-center justify-center gap-4">
279
+ <Button size="lg" onClick={() => window.location.href = '/chat'}>
280
+ <MessageSquare className="h-4 w-4 mr-2" />
281
+ Try Quick Chat
282
+ </Button>
283
+ <Button size="lg" variant="outline" onClick={() => window.location.href = '/community'}>
284
+ <Users className="h-4 w-4 mr-2" />
285
+ Browse Templates
286
+ </Button>
287
+ </div>
288
  </div>
289
  </section>
290
  </div>
frontend/src/pages/PlaygroundRedirect.tsx ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect } from 'react'
2
+ import { Card, CardContent } from '@/components/ui/card'
3
+ import { Button } from '@/components/ui/button'
4
+ import {
5
+ MessageSquare,
6
+ Bot,
7
+ ArrowRight,
8
+ Zap
9
+ } from 'lucide-react'
10
+
11
+ export function Playground() {
12
+ useEffect(() => {
13
+ // Auto-redirect to Quick Chat after 3 seconds if user doesn't choose
14
+ const timer = setTimeout(() => {
15
+ window.location.href = '/chat'
16
+ }, 5000)
17
+
18
+ return () => clearTimeout(timer)
19
+ }, [])
20
+
21
+ const options = [
22
+ {
23
+ title: 'Quick Chat',
24
+ description: 'Simple AI conversations with no configuration needed',
25
+ href: '/chat',
26
+ icon: MessageSquare,
27
+ color: 'bg-blue-600',
28
+ recommended: true
29
+ },
30
+ {
31
+ title: 'Build Assistant',
32
+ description: 'Create a custom AI assistant with specialized skills',
33
+ href: '/assistant-builder',
34
+ icon: Bot,
35
+ color: 'bg-purple-600',
36
+ recommended: false
37
+ }
38
+ ]
39
+
40
+ return (
41
+ <div className="min-h-screen bg-background flex items-center justify-center p-6">
42
+ <div className="max-w-4xl w-full">
43
+ <div className="text-center mb-12">
44
+ <div className="w-16 h-16 bg-blue-600 rounded-2xl flex items-center justify-center mx-auto mb-6">
45
+ <Zap className="h-8 w-8 text-white" />
46
+ </div>
47
+ <h1 className="text-3xl font-bold mb-4">Choose Your Experience</h1>
48
+ <p className="text-xl text-gray-600 max-w-2xl mx-auto">
49
+ The Playground has been redesigned! Choose the experience that best fits your needs.
50
+ </p>
51
+ </div>
52
+
53
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
54
+ {options.map((option) => (
55
+ <Card
56
+ key={option.title}
57
+ className={`cursor-pointer hover:shadow-xl transition-all duration-300 hover:-translate-y-2 ${
58
+ option.recommended ? 'ring-2 ring-blue-200 bg-blue-50' : ''
59
+ }`}
60
+ onClick={() => window.location.href = option.href}
61
+ >
62
+ <CardContent className="p-8 text-center space-y-6">
63
+ {option.recommended && (
64
+ <div className="bg-blue-600 text-white text-xs px-3 py-1 rounded-full inline-block mb-4">
65
+ Recommended for most users
66
+ </div>
67
+ )}
68
+ <div className={`w-16 h-16 ${option.color} rounded-2xl flex items-center justify-center mx-auto`}>
69
+ <option.icon className="h-8 w-8 text-white" />
70
+ </div>
71
+ <div>
72
+ <h3 className="font-bold text-2xl mb-3">{option.title}</h3>
73
+ <p className="text-gray-600 leading-relaxed">{option.description}</p>
74
+ </div>
75
+ <Button size="lg" className="w-full">
76
+ Continue
77
+ <ArrowRight className="h-4 w-4 ml-2" />
78
+ </Button>
79
+ </CardContent>
80
+ </Card>
81
+ ))}
82
+ </div>
83
+
84
+ <div className="text-center">
85
+ <p className="text-sm text-gray-500 mb-4">
86
+ This page will automatically redirect to Quick Chat in a few seconds
87
+ </p>
88
+ <div className="flex items-center justify-center gap-4">
89
+ <Button variant="outline" onClick={() => window.location.href = '/'}>
90
+ Back to Home
91
+ </Button>
92
+ <Button onClick={() => window.location.href = '/chat'}>
93
+ Go to Quick Chat
94
+ </Button>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ )
100
+ }
frontend/src/pages/SimpleChat.tsx ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect, useRef } from 'react'
2
+ import { Card, CardContent } from '@/components/ui/card'
3
+ import { Button } from '@/components/ui/button'
4
+ import { Badge } from '@/components/ui/badge'
5
+ import { Textarea } from '@/components/ui/textarea'
6
+ import {
7
+ Send,
8
+ Bot,
9
+ User,
10
+ MessageSquare,
11
+ Zap,
12
+ Plus,
13
+ Settings,
14
+ Sparkles
15
+ } from 'lucide-react'
16
+ import { Chat } from '@/components/ui/chat'
17
+
18
+ interface Message {
19
+ id: string
20
+ role: 'user' | 'assistant' | 'system'
21
+ content: string
22
+ timestamp: Date
23
+ }
24
+
25
+ export function SimpleChat() {
26
+ const [messages, setMessages] = useState<Message[]>([])
27
+ const [input, setInput] = useState('')
28
+ const [isLoading, setIsLoading] = useState(false)
29
+ const [selectedModel, setSelectedModel] = useState('Qwen/Qwen3-30B-A3B')
30
+ const [models, setModels] = useState<any[]>([])
31
+ const textareaRef = useRef<HTMLTextAreaElement>(null)
32
+
33
+ useEffect(() => {
34
+ fetchModels()
35
+ loadChatHistory()
36
+ }, [])
37
+
38
+ const fetchModels = async () => {
39
+ try {
40
+ const baseUrl = `${window.location.protocol}//${window.location.host}`
41
+ const res = await fetch(`${baseUrl}/models`)
42
+ if (res.ok) {
43
+ const data = await res.json()
44
+ setModels(data.models || [])
45
+ }
46
+ } catch (error) {
47
+ console.error('Failed to fetch models:', error)
48
+ }
49
+ }
50
+
51
+ const loadChatHistory = () => {
52
+ try {
53
+ const saved = localStorage.getItem('simpleChatHistory')
54
+ if (saved) {
55
+ const history = JSON.parse(saved)
56
+ setMessages(history.map((msg: any) => ({
57
+ ...msg,
58
+ timestamp: new Date(msg.timestamp)
59
+ })))
60
+ }
61
+ } catch (error) {
62
+ console.error('Failed to load chat history:', error)
63
+ }
64
+ }
65
+
66
+ const saveChatHistory = (msgs: Message[]) => {
67
+ try {
68
+ localStorage.setItem('simpleChatHistory', JSON.stringify(msgs))
69
+ } catch (error) {
70
+ console.error('Failed to save chat history:', error)
71
+ }
72
+ }
73
+
74
+ const sendMessage = async () => {
75
+ if (!input.trim()) return
76
+
77
+ const userMessage: Message = {
78
+ id: `user_${Date.now()}`,
79
+ role: 'user',
80
+ content: input.trim(),
81
+ timestamp: new Date()
82
+ }
83
+
84
+ const newMessages = [...messages, userMessage]
85
+ setMessages(newMessages)
86
+ setInput('')
87
+ setIsLoading(true)
88
+
89
+ try {
90
+ const baseUrl = `${window.location.protocol}//${window.location.host}`
91
+ const response = await fetch(`${baseUrl}/chat/completions`, {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify({
95
+ messages: newMessages.map(msg => ({
96
+ role: msg.role,
97
+ content: msg.content
98
+ })),
99
+ temperature: 0.7,
100
+ max_tokens: 2000
101
+ })
102
+ })
103
+
104
+ if (response.ok) {
105
+ const data = await response.json()
106
+ const assistantMessage: Message = {
107
+ id: `assistant_${Date.now()}`,
108
+ role: 'assistant',
109
+ content: data.choices[0]?.message?.content || 'No response received',
110
+ timestamp: new Date()
111
+ }
112
+
113
+ const finalMessages = [...newMessages, assistantMessage]
114
+ setMessages(finalMessages)
115
+ saveChatHistory(finalMessages)
116
+ } else {
117
+ throw new Error(`HTTP ${response.status}`)
118
+ }
119
+ } catch (error) {
120
+ console.error('Chat error:', error)
121
+ const errorMessage: Message = {
122
+ id: `error_${Date.now()}`,
123
+ role: 'assistant',
124
+ content: 'Sorry, I encountered an error. Please try again.',
125
+ timestamp: new Date()
126
+ }
127
+ const finalMessages = [...newMessages, errorMessage]
128
+ setMessages(finalMessages)
129
+ saveChatHistory(finalMessages)
130
+ } finally {
131
+ setIsLoading(false)
132
+ }
133
+ }
134
+
135
+ const handleKeyPress = (e: React.KeyboardEvent) => {
136
+ if (e.key === 'Enter' && !e.shiftKey) {
137
+ e.preventDefault()
138
+ sendMessage()
139
+ }
140
+ }
141
+
142
+ const clearChat = () => {
143
+ setMessages([])
144
+ localStorage.removeItem('simpleChatHistory')
145
+ }
146
+
147
+ const createAssistantFromChat = () => {
148
+ // Save current conversation as starting point for assistant
149
+ const conversation = messages.slice(-10) // Last 10 messages
150
+ localStorage.setItem('assistantBuilderContext', JSON.stringify({
151
+ messages: conversation,
152
+ model: selectedModel
153
+ }))
154
+ window.location.href = '/assistant-builder'
155
+ }
156
+
157
+ const quickPrompts = [
158
+ "Help me write a professional email",
159
+ "Explain quantum computing simply",
160
+ "Create a workout plan",
161
+ "Review my code for bugs"
162
+ ]
163
+
164
+ return (
165
+ <div className="min-h-screen bg-background">
166
+ {/* Header */}
167
+ <div className="border-b bg-background/95 backdrop-blur">
168
+ <div className="max-w-4xl mx-auto px-6 py-4">
169
+ <div className="flex items-center justify-between">
170
+ <div className="flex items-center gap-3">
171
+ <div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
172
+ <MessageSquare className="h-5 w-5 text-white" />
173
+ </div>
174
+ <div>
175
+ <h1 className="text-xl font-semibold">Quick Chat</h1>
176
+ <p className="text-sm text-muted-foreground">
177
+ Simple AI conversations - no setup required
178
+ </p>
179
+ </div>
180
+ </div>
181
+ <div className="flex items-center gap-2">
182
+ <Badge variant="outline" className="text-xs">
183
+ {selectedModel.split('/').pop()}
184
+ </Badge>
185
+ <Button variant="outline" size="sm" onClick={clearChat}>
186
+ <Plus className="h-4 w-4 mr-1" />
187
+ New Chat
188
+ </Button>
189
+ {messages.length > 0 && (
190
+ <Button size="sm" onClick={createAssistantFromChat}>
191
+ <Sparkles className="h-4 w-4 mr-1" />
192
+ Create Assistant
193
+ </Button>
194
+ )}
195
+ </div>
196
+ </div>
197
+ </div>
198
+ </div>
199
+
200
+ <div className="max-w-4xl mx-auto px-6 py-6">
201
+ <div className="flex flex-col h-[calc(100vh-200px)]">
202
+
203
+ {/* Messages Area */}
204
+ <div className="flex-1 mb-6 overflow-hidden">
205
+ <Card className="h-full">
206
+ <CardContent className="h-full p-0">
207
+ <div className="h-full p-6 overflow-y-auto">
208
+ {messages.length === 0 ? (
209
+ <div className="flex flex-col items-center justify-center h-full text-center">
210
+ <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mb-4">
211
+ <Bot className="h-8 w-8 text-blue-600" />
212
+ </div>
213
+ <h3 className="text-lg font-medium mb-2">Start a conversation</h3>
214
+ <p className="text-muted-foreground mb-6 max-w-md">
215
+ Ask me anything or try one of these suggestions to get started
216
+ </p>
217
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3 max-w-2xl">
218
+ {quickPrompts.map((prompt, index) => (
219
+ <Button
220
+ key={index}
221
+ variant="outline"
222
+ className="text-left h-auto py-3 px-4"
223
+ onClick={() => setInput(prompt)}
224
+ >
225
+ <div className="text-sm">{prompt}</div>
226
+ </Button>
227
+ ))}
228
+ </div>
229
+ </div>
230
+ ) : (
231
+ <div className="space-y-6">
232
+ {messages.map((message) => (
233
+ <div key={message.id} className={`flex gap-4 ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}>
234
+ {message.role === 'assistant' && (
235
+ <div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
236
+ <Bot className="h-4 w-4 text-white" />
237
+ </div>
238
+ )}
239
+ <div className={`max-w-[80%] p-4 rounded-lg ${
240
+ message.role === 'user'
241
+ ? 'bg-blue-600 text-white'
242
+ : 'bg-gray-100 text-gray-900'
243
+ }`}>
244
+ <div className="prose prose-sm max-w-none">
245
+ <Chat messages={[message]} />
246
+ </div>
247
+ </div>
248
+ {message.role === 'user' && (
249
+ <div className="w-8 h-8 bg-gray-600 rounded-full flex items-center justify-center">
250
+ <User className="h-4 w-4 text-white" />
251
+ </div>
252
+ )}
253
+ </div>
254
+ ))}
255
+ {isLoading && (
256
+ <div className="flex gap-4 justify-start">
257
+ <div className="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center">
258
+ <Bot className="h-4 w-4 text-white" />
259
+ </div>
260
+ <div className="bg-gray-100 rounded-lg p-4">
261
+ <div className="flex space-x-1">
262
+ <div className="w-2 h-2 bg-gray-400 rounded-full animate-pulse"></div>
263
+ <div className="w-2 h-2 bg-gray-400 rounded-full animate-pulse" style={{ animationDelay: '0.2s' }}></div>
264
+ <div className="w-2 h-2 bg-gray-400 rounded-full animate-pulse" style={{ animationDelay: '0.4s' }}></div>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ )}
269
+ </div>
270
+ )}
271
+ </div>
272
+ </CardContent>
273
+ </Card>
274
+ </div>
275
+
276
+ {/* Input Area */}
277
+ <div className="space-y-4">
278
+ <Card>
279
+ <CardContent className="p-4">
280
+ <div className="flex gap-3">
281
+ <Textarea
282
+ ref={textareaRef}
283
+ placeholder="Type your message..."
284
+ value={input}
285
+ onChange={(e) => setInput(e.target.value)}
286
+ onKeyPress={handleKeyPress}
287
+ className="flex-1 min-h-[60px] resize-none"
288
+ disabled={isLoading}
289
+ />
290
+ <Button
291
+ onClick={sendMessage}
292
+ disabled={isLoading || !input.trim()}
293
+ size="lg"
294
+ className="shrink-0"
295
+ >
296
+ {isLoading ? (
297
+ <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
298
+ ) : (
299
+ <Send className="h-4 w-4" />
300
+ )}
301
+ </Button>
302
+ </div>
303
+ </CardContent>
304
+ </Card>
305
+
306
+ <div className="text-center">
307
+ <p className="text-xs text-muted-foreground">
308
+ Press Enter to send, Shift+Enter for new line
309
+ </p>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ )
316
+ }