wu981526092 commited on
Commit
af6ace3
·
1 Parent(s): 9592d69

Add minimal custom assistant system - save/load assistants in Playground and Models pages

Browse files
IMPLEMENTATION_GUIDE.md ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 自定义AI助手平台 - 实施指南
2
+
3
+ ## 🎯 核心概念总结
4
+
5
+ 你的想法非常有价值!这个平台结合了:
6
+ - **Prompt Engineering** - 让用户通过调试参数和提示词创造专属AI
7
+ - **社区协作** - 分享和发现优质用例模板
8
+ - **个性化定制** - 针对特定场景优化AI行为
9
+ - **降低门槛** - 无需编程技能即可创建AI助手
10
+
11
+ ## 🏗️ 技术架构
12
+
13
+ ### 前端架构
14
+ ```
15
+ src/
16
+ ├── pages/
17
+ │ ├── AssistantBuilder.tsx # 助手创建器
18
+ │ ├── Community.tsx # 社区市场
19
+ │ ├── MyAssistants.tsx # 个人工作台
20
+ │ └── AssistantDetail.tsx # 助手详情页
21
+ ├── components/
22
+ │ ├── ParameterPanel.tsx # 参数调试面板
23
+ │ ├── PromptEditor.tsx # Prompt编辑器
24
+ │ ├── TestChat.tsx # 测试对话
25
+ │ └── AssistantCard.tsx # 助手卡片
26
+ └── hooks/
27
+ ├── useAssistant.ts # 助手管理
28
+ └── useCommunity.ts # 社区数据
29
+ ```
30
+
31
+ ### 后端架构
32
+ ```
33
+ backend/
34
+ ├── models_community.py # 数据模型
35
+ ├── api/
36
+ │ ├── community_routes.py # 社区API
37
+ │ └── assistant_routes.py # 助手管理API
38
+ ├── services/
39
+ │ ├── assistant_service.py # 助手业务逻辑
40
+ │ └── community_service.py # 社区功能
41
+ └── storage/
42
+ ├── assistants.json # 助手数据
43
+ └── community.json # 社区数据
44
+ ```
45
+
46
+ ## 🚀 MVP功能优先级
47
+
48
+ ### Phase 1: 核心创建器 (优先级: 高)
49
+ - [ ] **参数调试面板**
50
+ - Temperature, Max Tokens, Top-P, Frequency Penalty
51
+ - 实时参数效果预览
52
+ - 参数说明和建议范围
53
+
54
+ - [ ] **Prompt工程器**
55
+ - 系统提示词编辑器
56
+ - 示例对话管理
57
+ - 预设模板库
58
+
59
+ - [ ] **实时测试环境**
60
+ - 即时对话测试
61
+ - 参数对比功能
62
+ - 测试历史记录
63
+
64
+ ### Phase 2: 本地存储 (优先级: 高)
65
+ - [ ] **助手管理**
66
+ - 本地保存/加载
67
+ - 版本管理
68
+ - 导入/导出功能
69
+
70
+ - [ ] **用户界面优化**
71
+ - 拖拽式参数调节
72
+ - 快捷模板选择
73
+ - 一键Fork功能
74
+
75
+ ### Phase 3: 社区功能 (优先级: 中)
76
+ - [ ] **基础社区**
77
+ - 助手分享发布
78
+ - 分类浏览
79
+ - 搜索和筛选
80
+
81
+ - [ ] **互动功能**
82
+ - 点赞和收藏
83
+ - 下载统计
84
+ - 简单评价系统
85
+
86
+ ### Phase 4: 高级功能 (优先级: 低)
87
+ - [ ] **用户系统**
88
+ - 账户注册/登录
89
+ - 个人主页
90
+ - 关注功能
91
+
92
+ - [ ] **社区生态**
93
+ - 详细评论系统
94
+ - 付费助手市场
95
+ - API接口开放
96
+
97
+ ## 💡 关键设计决策
98
+
99
+ ### 1. 参数调试体验
100
+ ```typescript
101
+ // 实时参数预览
102
+ const ParameterPanel = () => {
103
+ const [temperature, setTemperature] = useState(0.7)
104
+ const [previewText, setPreviewText] = useState('')
105
+
106
+ // 参数变化时实时更新预览
107
+ useEffect(() => {
108
+ generatePreview(temperature)
109
+ }, [temperature])
110
+
111
+ return (
112
+ <div>
113
+ <Slider
114
+ value={temperature}
115
+ onChange={setTemperature}
116
+ hint={getTemperatureHint(temperature)}
117
+ />
118
+ <PreviewBox text={previewText} />
119
+ </div>
120
+ )
121
+ }
122
+ ```
123
+
124
+ ### 2. Prompt工程辅助
125
+ ```typescript
126
+ // 智能提示和模板
127
+ const PromptEditor = () => {
128
+ const [prompt, setPrompt] = useState('')
129
+ const suggestions = usePromptSuggestions(prompt)
130
+
131
+ return (
132
+ <div>
133
+ <Textarea
134
+ value={prompt}
135
+ onChange={setPrompt}
136
+ placeholder="定义AI的角色和行为..."
137
+ />
138
+ <SuggestionPanel suggestions={suggestions} />
139
+ <TemplateGallery onSelect={setPrompt} />
140
+ </div>
141
+ )
142
+ }
143
+ ```
144
+
145
+ ### 3. 社区发现算法
146
+ ```typescript
147
+ // 个性化推荐
148
+ const getCommunityRecommendations = (user: UserProfile) => {
149
+ const userInterests = analyzeUserInterests(user)
150
+ const trendingAssistants = getTrendingAssistants()
151
+ const personalizedRecs = getPersonalizedRecommendations(userInterests)
152
+
153
+ return {
154
+ trending: trendingAssistants,
155
+ forYou: personalizedRecs,
156
+ newReleases: getNewestAssistants()
157
+ }
158
+ }
159
+ ```
160
+
161
+ ## 🎨 用户体验设计
162
+
163
+ ### 创建流程优化
164
+ 1. **零门槛入门**: 提供向导式创建流程
165
+ 2. **即时反馈**: 参数调整立即显示效果
166
+ 3. **模板起步**: 从预设模板开始,降低创建难度
167
+ 4. **渐进增强**: 基础→高级参数,逐步解锁功能
168
+
169
+ ### 社区互动设计
170
+ 1. **发现机制**: 个性化推荐 + 分类浏览 + 搜索
171
+ 2. **质量保证**: 官方认证 + 社区评价 + 使用统计
172
+ 3. **激励体系**: 创作者积分 + 下载排行 + 精选推荐
173
+
174
+ ## 💼 商业模式建议
175
+
176
+ ### 免费版功能
177
+ - 创建最多3个私人助手
178
+ - 使用社区免费助手
179
+ - 基础参数调试
180
+ - 本地存储
181
+
182
+ ### 专业版功能 ($9.99/月)
183
+ - 无限制创建助手
184
+ - 高级参数调试
185
+ - 私有助手分享
186
+ - 云端同步
187
+ - 优先技术支持
188
+
189
+ ### 企业版功能 ($49.99/月)
190
+ - 团队协作功能
191
+ - 私有部署选项
192
+ - API集成���口
193
+ - 企业级安全
194
+ - 定制开发服务
195
+
196
+ ### 社区分成模式
197
+ - 付费助手创作者分成 70%
198
+ - 平台服务费 30%
199
+ - 月度创作者激励计划
200
+
201
+ ## 🎯 成功指标
202
+
203
+ ### 用户参与度
204
+ - 月活跃用户数
205
+ - 助手创建数量
206
+ - 平均使用时长
207
+ - 用户留存率
208
+
209
+ ### 社区健康度
210
+ - 助手分享率
211
+ - 社区互动率
212
+ - 高质量内容比例
213
+ - 用户满意度评分
214
+
215
+ ### 商业指标
216
+ - 付费转化率
217
+ - 月经常性收入 (MRR)
218
+ - 客户生命周期价值 (LTV)
219
+ - 获客成本 (CAC)
220
+
221
+ ## 🚀 下一步行动
222
+
223
+ 1. **立即开始**: 实现基础的参数调试界面
224
+ 2. **用户测试**: 找5-10个潜在用户测试MVP
225
+ 3. **社区预热**: 准备20-30个高质量预设助手
226
+ 4. **技术优化**: 完善实时预览和保存功能
227
+ 5. **市场推广**: 制定社区增长策略
228
+
229
+ 这个产品方向非常有潜力,结合了技术创新和用户需求!建议先从MVP开始,快速验证核心价值主张。
PRODUCT_ROADMAP.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Edge LLM 产品路线图 - 自定义AI助手平台
2
+
3
+ ## 🎯 产品愿景
4
+ 打造一个让用户能够轻松创建、调试和分享AI助手的平台,通过参数调优和prompt工程实现个性化AI体验。
5
+
6
+ ## 📋 功能模块
7
+
8
+ ### 1. 助手创建器 (Assistant Builder)
9
+ - **参数调试面板**: Temperature, Max Tokens, Top-P, Frequency Penalty
10
+ - **Prompt工程器**: System Prompt编辑器,示例对话管理
11
+ - **实时测试**: 即时对话测试,参数效果预览
12
+ - **版本管理**: 保存多个版本,回滚功能
13
+
14
+ ### 2. 社区市场 (Community Hub)
15
+ - **用例库**: 分类浏览(编程、写作、分析、创意等)
16
+ - **搜索发现**: 按场景、标签、评分搜索
17
+ - **评价系统**: 点赞、评论、收藏、下载统计
18
+ - **精选推荐**: 官方精选、热门趋势
19
+
20
+ ### 3. 个人工作台 (Workspace)
21
+ - **我的助手**: 创建的助手管理
22
+ - **收藏夹**: 喜欢的社区助手
23
+ - **使用历史**: 对话记录、使用统计
24
+ - **分享管理**: 发布状态、反馈管理
25
+
26
+ ## 🚀 分阶段实施计划
27
+
28
+ ### Phase 1: 基础创建器 (4-6周)
29
+ - [ ] 助手创建界面
30
+ - [ ] 基础参数调试
31
+ - [ ] 本地保存功能
32
+ - [ ] 简单测试环境
33
+
34
+ ### Phase 2: 高级功能 (6-8周)
35
+ - [ ] 高级prompt工程
36
+ - [ ] 版本管理系统
37
+ - [ ] 导入/导出功能
38
+ - [ ] 批量测试工具
39
+
40
+ ### Phase 3: 社区功能 (8-10周)
41
+ - [ ] 用户系统
42
+ - [ ] 助手分享发布
43
+ - [ ] 社区浏览界面
44
+ - [ ] 评价和搜索
45
+
46
+ ### Phase 4: 生态完善 (ongoing)
47
+ - [ ] 高级分析工具
48
+ - [ ] API接口开放
49
+ - [ ] 企业版功能
50
+ - [ ] 移动端适配
51
+
52
+ ## 💼 商业模式
53
+ 1. **免费版**: 基础创建、有限保存
54
+ 2. **专业版**: 无限助手、高级分析、优先支持
55
+ 3. **企业版**: 私有部署、团队协作、API集成
56
+ 4. **社区分成**: 优质付费助手分成模式
57
+
58
+ ## 🎨 用户体验重点
59
+ - **零门槛创建**: 拖拽式界面,可视化参数调节
60
+ - **即时反馈**: 实时预览参数效果
61
+ - **社区发现**: 个性化推荐算法
62
+ - **协作友好**: 一键fork,版本对比
frontend/src/pages/Models.tsx CHANGED
@@ -12,7 +12,9 @@ import {
12
  Info,
13
  CheckCircle,
14
  Cloud,
15
- HardDrive
 
 
16
  } from 'lucide-react'
17
 
18
  interface ModelInfo {
@@ -34,11 +36,36 @@ export function Models() {
34
  const [models, setModels] = useState<ModelInfo[]>([])
35
  const [loading, setLoading] = useState(true)
36
  const [modelLoading, setModelLoading] = useState<string | null>(null)
 
37
 
38
  useEffect(() => {
39
  fetchModels()
 
40
  }, [])
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  const fetchModels = async () => {
43
  try {
44
  const baseUrl = `${window.location.protocol}//${window.location.host}`
@@ -145,6 +172,54 @@ export function Models() {
145
  </CardContent>
146
  </Card>
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  {/* API Models Section */}
149
  <div>
150
  <h2 className="text-xl font-semibold mb-4 flex items-center gap-2">
 
12
  Info,
13
  CheckCircle,
14
  Cloud,
15
+ HardDrive,
16
+ Bot,
17
+ MessageSquare
18
  } from 'lucide-react'
19
 
20
  interface ModelInfo {
 
36
  const [models, setModels] = useState<ModelInfo[]>([])
37
  const [loading, setLoading] = useState(true)
38
  const [modelLoading, setModelLoading] = useState<string | null>(null)
39
+ const [savedAssistants, setSavedAssistants] = useState<any[]>([])
40
 
41
  useEffect(() => {
42
  fetchModels()
43
+ loadSavedAssistants()
44
  }, [])
45
 
46
+ const loadSavedAssistants = () => {
47
+ try {
48
+ const assistants = JSON.parse(localStorage.getItem('savedAssistants') || '[]')
49
+ setSavedAssistants(assistants)
50
+ } catch (error) {
51
+ console.error('Failed to load saved assistants:', error)
52
+ setSavedAssistants([])
53
+ }
54
+ }
55
+
56
+ const loadAssistant = (assistant: any) => {
57
+ // Store the assistant config in localStorage for the playground to use
58
+ localStorage.setItem('loadAssistantConfig', JSON.stringify(assistant))
59
+ // Navigate to playground
60
+ window.location.href = '/playground'
61
+ }
62
+
63
+ const deleteAssistant = (assistantId: string) => {
64
+ const updatedAssistants = savedAssistants.filter(a => a.id !== assistantId)
65
+ setSavedAssistants(updatedAssistants)
66
+ localStorage.setItem('savedAssistants', JSON.stringify(updatedAssistants))
67
+ }
68
+
69
  const fetchModels = async () => {
70
  try {
71
  const baseUrl = `${window.location.protocol}//${window.location.host}`
 
172
  </CardContent>
173
  </Card>
174
 
175
+ {/* Custom Assistants */}
176
+ {savedAssistants.length > 0 && (
177
+ <div>
178
+ <h2 className="text-xl font-semibold mb-4 flex items-center gap-2">
179
+ <Bot className="h-5 w-5" />
180
+ My Custom Assistants
181
+ </h2>
182
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
183
+ {savedAssistants.map((assistant) => (
184
+ <Card key={assistant.id} className="hover:shadow-md transition-shadow">
185
+ <CardHeader className="pb-3">
186
+ <div className="flex items-start justify-between">
187
+ <div className="flex-1">
188
+ <CardTitle className="text-base">{assistant.name}</CardTitle>
189
+ <p className="text-sm text-muted-foreground mt-1">
190
+ {assistant.description || 'No description'}
191
+ </p>
192
+ </div>
193
+ <Button
194
+ variant="ghost"
195
+ size="sm"
196
+ onClick={() => deleteAssistant(assistant.id)}
197
+ className="text-destructive hover:text-destructive"
198
+ >
199
+ <Trash2 className="h-4 w-4" />
200
+ </Button>
201
+ </div>
202
+ </CardHeader>
203
+ <CardContent className="space-y-3">
204
+ <div className="text-xs space-y-1 text-muted-foreground">
205
+ <div><span className="font-medium">Model:</span> {assistant.model}</div>
206
+ <div><span className="font-medium">Temperature:</span> {assistant.temperature}</div>
207
+ <div><span className="font-medium">Max Tokens:</span> {assistant.maxTokens}</div>
208
+ <div><span className="font-medium">Created:</span> {new Date(assistant.createdAt).toLocaleDateString()}</div>
209
+ </div>
210
+ <div className="flex gap-2">
211
+ <Button size="sm" onClick={() => loadAssistant(assistant)} className="flex-1">
212
+ <MessageSquare className="h-4 w-4 mr-1" />
213
+ Use
214
+ </Button>
215
+ </div>
216
+ </CardContent>
217
+ </Card>
218
+ ))}
219
+ </div>
220
+ </div>
221
+ )}
222
+
223
  {/* API Models Section */}
224
  <div>
225
  <h2 className="text-xl font-semibold mb-4 flex items-center gap-2">
frontend/src/pages/Playground.tsx CHANGED
@@ -4,6 +4,8 @@ import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
4
  import { Slider } from '@/components/ui/slider'
5
  import { Label } from '@/components/ui/label'
6
  import { Badge } from '@/components/ui/badge'
 
 
7
  import {
8
  Select,
9
  SelectContent,
@@ -49,7 +51,9 @@ import {
49
  Download,
50
  AlertTriangle,
51
  Plus,
52
- Trash2
 
 
53
  } from 'lucide-react'
54
 
55
  interface ModelInfo {
@@ -100,9 +104,42 @@ export function Playground() {
100
  const [showLoadConfirm, setShowLoadConfirm] = useState(false)
101
  const [pendingModelToLoad, setPendingModelToLoad] = useState<ModelInfo | null>(null)
102
 
 
 
 
 
 
103
  // Model management state
104
  const [models, setModels] = useState<ModelInfo[]>([])
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  // Preset system prompts
107
  const systemPromptPresets = [
108
  {
@@ -158,8 +195,34 @@ export function Playground() {
158
  // Load available models on startup
159
  useEffect(() => {
160
  fetchModels()
 
 
161
  }, [])
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  // Update selected model when models change
164
  useEffect(() => {
165
  // Only reset if the selected model no longer exists in the models list
@@ -425,6 +488,15 @@ export function Playground() {
425
  <Upload className="h-4 w-4 mr-2" />
426
  <span className="hidden sm:inline">Import</span>
427
  </Button>
 
 
 
 
 
 
 
 
 
428
  <Button variant="outline" size="sm" className="flex-shrink-0">
429
  <Share className="h-4 w-4 mr-2" />
430
  <span className="hidden sm:inline">Export</span>
@@ -765,6 +837,61 @@ export function Playground() {
765
  </AlertDialogFooter>
766
  </AlertDialogContent>
767
  </AlertDialog>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
768
  </div>
769
  )
770
  }
 
4
  import { Slider } from '@/components/ui/slider'
5
  import { Label } from '@/components/ui/label'
6
  import { Badge } from '@/components/ui/badge'
7
+ import { Input } from '@/components/ui/input'
8
+ import { Textarea } from '@/components/ui/textarea'
9
  import {
10
  Select,
11
  SelectContent,
 
51
  Download,
52
  AlertTriangle,
53
  Plus,
54
+ Trash2,
55
+ Save,
56
+ Bot
57
  } from 'lucide-react'
58
 
59
  interface ModelInfo {
 
104
  const [showLoadConfirm, setShowLoadConfirm] = useState(false)
105
  const [pendingModelToLoad, setPendingModelToLoad] = useState<ModelInfo | null>(null)
106
 
107
+ // Assistant saving
108
+ const [showSaveAssistant, setShowSaveAssistant] = useState(false)
109
+ const [assistantName, setAssistantName] = useState('')
110
+ const [assistantDescription, setAssistantDescription] = useState('')
111
+
112
  // Model management state
113
  const [models, setModels] = useState<ModelInfo[]>([])
114
 
115
+ // Assistant management
116
+ const saveAssistant = () => {
117
+ if (!assistantName.trim()) return
118
+
119
+ const assistant = {
120
+ id: Date.now().toString(),
121
+ name: assistantName,
122
+ description: assistantDescription,
123
+ model: selectedModel,
124
+ systemPrompt: systemPrompt,
125
+ temperature: temperature,
126
+ maxTokens: maxTokens,
127
+ createdAt: new Date().toISOString()
128
+ }
129
+
130
+ // Save to localStorage
131
+ const savedAssistants = JSON.parse(localStorage.getItem('savedAssistants') || '[]')
132
+ savedAssistants.push(assistant)
133
+ localStorage.setItem('savedAssistants', JSON.stringify(savedAssistants))
134
+
135
+ // Reset form
136
+ setAssistantName('')
137
+ setAssistantDescription('')
138
+ setShowSaveAssistant(false)
139
+
140
+ alert('助手保存成功!可在模型目录中查看。')
141
+ }
142
+
143
  // Preset system prompts
144
  const systemPromptPresets = [
145
  {
 
195
  // Load available models on startup
196
  useEffect(() => {
197
  fetchModels()
198
+ // Check if we need to load an assistant configuration
199
+ loadAssistantIfNeeded()
200
  }, [])
201
 
202
+ const loadAssistantIfNeeded = () => {
203
+ try {
204
+ const assistantConfig = localStorage.getItem('loadAssistantConfig')
205
+ if (assistantConfig) {
206
+ const assistant = JSON.parse(assistantConfig)
207
+ // Load the assistant configuration
208
+ setSelectedModel(assistant.model)
209
+ setSystemPrompt(assistant.systemPrompt || '')
210
+ setTemperature(assistant.temperature || 0.7)
211
+ setMaxTokens(assistant.maxTokens || 1024)
212
+
213
+ // Clear the load flag
214
+ localStorage.removeItem('loadAssistantConfig')
215
+
216
+ // Show a success message
217
+ setTimeout(() => {
218
+ alert(`Assistant "${assistant.name}" loaded successfully!`)
219
+ }, 500)
220
+ }
221
+ } catch (error) {
222
+ console.error('Failed to load assistant configuration:', error)
223
+ }
224
+ }
225
+
226
  // Update selected model when models change
227
  useEffect(() => {
228
  // Only reset if the selected model no longer exists in the models list
 
488
  <Upload className="h-4 w-4 mr-2" />
489
  <span className="hidden sm:inline">Import</span>
490
  </Button>
491
+ <Button
492
+ variant="outline"
493
+ size="sm"
494
+ className="flex-shrink-0"
495
+ onClick={() => setShowSaveAssistant(true)}
496
+ >
497
+ <Save className="h-4 w-4 mr-2" />
498
+ <span className="hidden sm:inline">Save Assistant</span>
499
+ </Button>
500
  <Button variant="outline" size="sm" className="flex-shrink-0">
501
  <Share className="h-4 w-4 mr-2" />
502
  <span className="hidden sm:inline">Export</span>
 
837
  </AlertDialogFooter>
838
  </AlertDialogContent>
839
  </AlertDialog>
840
+
841
+ {/* Save Assistant Dialog */}
842
+ <AlertDialog open={showSaveAssistant} onOpenChange={setShowSaveAssistant}>
843
+ <AlertDialogContent>
844
+ <AlertDialogHeader>
845
+ <AlertDialogTitle className="flex items-center gap-2">
846
+ <Bot className="h-5 w-5 text-blue-500" />
847
+ Save Current Configuration as Assistant
848
+ </AlertDialogTitle>
849
+ <AlertDialogDescription>
850
+ Save your current model settings and system prompt as a reusable assistant.
851
+ </AlertDialogDescription>
852
+ </AlertDialogHeader>
853
+ <div className="space-y-4 py-4">
854
+ <div>
855
+ <Label htmlFor="assistant-name">Assistant Name</Label>
856
+ <Input
857
+ id="assistant-name"
858
+ value={assistantName}
859
+ onChange={(e) => setAssistantName(e.target.value)}
860
+ placeholder="e.g., Code Review Expert"
861
+ className="mt-1"
862
+ />
863
+ </div>
864
+ <div>
865
+ <Label htmlFor="assistant-description">Description</Label>
866
+ <Textarea
867
+ id="assistant-description"
868
+ value={assistantDescription}
869
+ onChange={(e) => setAssistantDescription(e.target.value)}
870
+ placeholder="Describe what this assistant is good at..."
871
+ rows={3}
872
+ className="mt-1"
873
+ />
874
+ </div>
875
+ <div className="bg-muted p-3 rounded-md text-sm">
876
+ <div className="font-medium mb-2">Current Configuration:</div>
877
+ <div className="space-y-1 text-muted-foreground">
878
+ <div>Model: {selectedModel}</div>
879
+ <div>Temperature: {temperature}</div>
880
+ <div>Max Tokens: {maxTokens}</div>
881
+ <div>System Prompt: {systemPrompt ? systemPrompt.slice(0, 50) + '...' : 'None'}</div>
882
+ </div>
883
+ </div>
884
+ </div>
885
+ <AlertDialogFooter>
886
+ <AlertDialogCancel onClick={() => setShowSaveAssistant(false)}>
887
+ Cancel
888
+ </AlertDialogCancel>
889
+ <AlertDialogAction onClick={saveAssistant} disabled={!assistantName.trim()}>
890
+ Save Assistant
891
+ </AlertDialogAction>
892
+ </AlertDialogFooter>
893
+ </AlertDialogContent>
894
+ </AlertDialog>
895
  </div>
896
  )
897
  }