django-agent-studio 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_agent_studio/__init__.py +12 -0
- django_agent_studio/api/__init__.py +4 -0
- django_agent_studio/api/permissions.py +160 -0
- django_agent_studio/api/serializers.py +606 -0
- django_agent_studio/api/urls.py +245 -0
- django_agent_studio/api/views.py +1548 -0
- django_agent_studio/apps.py +24 -0
- django_agent_studio/management/__init__.py +2 -0
- django_agent_studio/management/commands/__init__.py +2 -0
- django_agent_studio/static/agent-frontend/chat-widget-markdown.js +110 -0
- django_agent_studio/static/agent-frontend/chat-widget.css +1401 -0
- django_agent_studio/static/agent-frontend/chat-widget.js +319 -0
- django_agent_studio/templates/django_agent_studio/agent_list.html +101 -0
- django_agent_studio/templates/django_agent_studio/base.html +137 -0
- django_agent_studio/templates/django_agent_studio/builder.html +1443 -0
- django_agent_studio/templates/django_agent_studio/home.html +97 -0
- django_agent_studio/templates/django_agent_studio/test.html +126 -0
- django_agent_studio/urls.py +25 -0
- django_agent_studio/views.py +100 -0
- django_agent_studio-0.1.0.dist-info/METADATA +416 -0
- django_agent_studio-0.1.0.dist-info/RECORD +23 -0
- django_agent_studio-0.1.0.dist-info/WHEEL +5 -0
- django_agent_studio-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1443 @@
|
|
|
1
|
+
{% extends "django_agent_studio/base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}{% if is_new %}Create Agent{% else %}Edit {{ agent.name }}{% endif %} - Agent Studio{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block breadcrumbs %}
|
|
6
|
+
<nav class="flex items-center space-x-2 ml-4 text-sm">
|
|
7
|
+
<span class="text-gray-400">/</span>
|
|
8
|
+
<a href="{% url 'agent_studio:agent_list' %}" class="text-gray-500 hover:text-gray-700">My Agents</a>
|
|
9
|
+
<span class="text-gray-400">/</span>
|
|
10
|
+
<span class="text-gray-600">{% if is_new %}New Agent{% else %}{{ agent.name }}{% endif %}</span>
|
|
11
|
+
</nav>
|
|
12
|
+
{% endblock %}
|
|
13
|
+
|
|
14
|
+
{% block extra_head %}
|
|
15
|
+
<style>
|
|
16
|
+
/* Embedded chat widget styles */
|
|
17
|
+
.chat-pane {
|
|
18
|
+
height: 100%;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
}
|
|
22
|
+
.chat-pane .chat-container {
|
|
23
|
+
flex: 1;
|
|
24
|
+
position: relative;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Make chat widgets fill their containers */
|
|
29
|
+
#test-chat-container .cw-container,
|
|
30
|
+
#builder-chat-container .cw-container {
|
|
31
|
+
position: absolute !important;
|
|
32
|
+
top: 0;
|
|
33
|
+
left: 0;
|
|
34
|
+
right: 0;
|
|
35
|
+
bottom: 0;
|
|
36
|
+
width: 100% !important;
|
|
37
|
+
height: 100% !important;
|
|
38
|
+
max-height: none !important;
|
|
39
|
+
border-radius: 0 !important;
|
|
40
|
+
box-shadow: none !important;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#test-chat-container .cw-toggle-btn,
|
|
44
|
+
#builder-chat-container .cw-toggle-btn {
|
|
45
|
+
display: none !important;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#test-chat-container .cw-widget,
|
|
49
|
+
#builder-chat-container .cw-widget {
|
|
50
|
+
position: absolute !important;
|
|
51
|
+
top: 0;
|
|
52
|
+
left: 0;
|
|
53
|
+
right: 0;
|
|
54
|
+
bottom: 0;
|
|
55
|
+
width: 100% !important;
|
|
56
|
+
height: 100% !important;
|
|
57
|
+
max-height: none !important;
|
|
58
|
+
border-radius: 0 !important;
|
|
59
|
+
display: flex !important;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Schema Editor Modal Styles */
|
|
63
|
+
.schema-modal-overlay {
|
|
64
|
+
position: fixed;
|
|
65
|
+
inset: 0;
|
|
66
|
+
background: rgba(0, 0, 0, 0.5);
|
|
67
|
+
z-index: 1000;
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
padding: 1rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.schema-modal {
|
|
75
|
+
background: white;
|
|
76
|
+
border-radius: 0.75rem;
|
|
77
|
+
width: 100%;
|
|
78
|
+
max-width: 1200px;
|
|
79
|
+
height: 90vh;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.schema-modal-header {
|
|
86
|
+
padding: 1rem 1.5rem;
|
|
87
|
+
border-bottom: 1px solid #e5e7eb;
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: space-between;
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.schema-modal-body {
|
|
95
|
+
flex: 1;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
display: flex;
|
|
98
|
+
flex-direction: column;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.schema-tabs {
|
|
102
|
+
display: flex;
|
|
103
|
+
border-bottom: 1px solid #e5e7eb;
|
|
104
|
+
padding: 0 1rem;
|
|
105
|
+
flex-shrink: 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.schema-tab {
|
|
109
|
+
padding: 0.75rem 1rem;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
border-bottom: 2px solid transparent;
|
|
112
|
+
color: #6b7280;
|
|
113
|
+
font-size: 0.875rem;
|
|
114
|
+
font-weight: 500;
|
|
115
|
+
transition: all 0.15s;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.schema-tab:hover {
|
|
119
|
+
color: #374151;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.schema-tab.active {
|
|
123
|
+
color: #3b82f6;
|
|
124
|
+
border-bottom-color: #3b82f6;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.schema-content {
|
|
128
|
+
flex: 1;
|
|
129
|
+
overflow: auto;
|
|
130
|
+
padding: 1rem;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.schema-editor {
|
|
134
|
+
width: 100%;
|
|
135
|
+
height: 100%;
|
|
136
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
137
|
+
font-size: 13px;
|
|
138
|
+
line-height: 1.5;
|
|
139
|
+
padding: 1rem;
|
|
140
|
+
border: 1px solid #e5e7eb;
|
|
141
|
+
border-radius: 0.5rem;
|
|
142
|
+
resize: none;
|
|
143
|
+
background: #1e1e1e;
|
|
144
|
+
color: #d4d4d4;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.schema-editor:focus {
|
|
148
|
+
outline: none;
|
|
149
|
+
border-color: #3b82f6;
|
|
150
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.schema-modal-footer {
|
|
154
|
+
padding: 1rem 1.5rem;
|
|
155
|
+
border-top: 1px solid #e5e7eb;
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
justify-content: space-between;
|
|
159
|
+
flex-shrink: 0;
|
|
160
|
+
background: #f9fafb;
|
|
161
|
+
border-radius: 0 0 0.75rem 0.75rem;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.schema-status {
|
|
165
|
+
font-size: 0.875rem;
|
|
166
|
+
color: #6b7280;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.schema-status.error {
|
|
170
|
+
color: #dc2626;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.schema-status.success {
|
|
174
|
+
color: #16a34a;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.btn-group {
|
|
178
|
+
display: flex;
|
|
179
|
+
gap: 0.5rem;
|
|
180
|
+
}
|
|
181
|
+
</style>
|
|
182
|
+
{% endblock %}
|
|
183
|
+
|
|
184
|
+
{% block content %}
|
|
185
|
+
<div class="h-full flex flex-col">
|
|
186
|
+
<!-- Top Bar: Agent Selector -->
|
|
187
|
+
<div class="px-4 py-2 bg-white border-b border-gray-200 flex items-center justify-between">
|
|
188
|
+
<div class="flex items-center space-x-4">
|
|
189
|
+
<!-- Agent Selector -->
|
|
190
|
+
<div class="flex items-center space-x-2">
|
|
191
|
+
<label class="text-sm font-medium text-gray-600">Agent:</label>
|
|
192
|
+
<select v-model="selectedAgentId"
|
|
193
|
+
@change="onAgentChange"
|
|
194
|
+
class="px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 min-w-[200px]">
|
|
195
|
+
<option value="">-- Select an agent --</option>
|
|
196
|
+
<option v-for="agent in agents" :key="agent.id" :value="agent.id">
|
|
197
|
+
[[ agent.icon || '🤖' ]] [[ agent.name ]]
|
|
198
|
+
</option>
|
|
199
|
+
</select>
|
|
200
|
+
<button @click="createNewAgent"
|
|
201
|
+
class="px-3 py-1.5 text-sm text-green-600 border border-green-300 rounded hover:bg-green-50"
|
|
202
|
+
title="Create new agent">
|
|
203
|
+
<span>+ New</span>
|
|
204
|
+
</button>
|
|
205
|
+
<button v-if="selectedAgentId"
|
|
206
|
+
@click="deleteAgent"
|
|
207
|
+
class="px-2 py-1.5 text-sm text-red-600 border border-red-300 rounded hover:bg-red-50"
|
|
208
|
+
title="Delete this agent">
|
|
209
|
+
<span>🗑️</span>
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<!-- Version Selector (shown when agent selected) -->
|
|
214
|
+
<div v-if="selectedAgentId && versions.length > 0" class="flex items-center space-x-2 border-l border-gray-200 pl-4">
|
|
215
|
+
<label class="text-sm font-medium text-gray-600">Version:</label>
|
|
216
|
+
<select v-model="selectedVersionId"
|
|
217
|
+
@change="onVersionChange"
|
|
218
|
+
class="px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
219
|
+
<option v-for="version in versions" :key="version.id" :value="version.id">
|
|
220
|
+
[[ version.version ]] [[ version.is_active ? '(active)' : '' ]] [[ version.is_draft ? '(draft)' : '' ]]
|
|
221
|
+
</option>
|
|
222
|
+
</select>
|
|
223
|
+
<button v-if="selectedVersionId && !isActiveVersion"
|
|
224
|
+
@click="activateVersion"
|
|
225
|
+
class="px-2 py-1 text-xs text-blue-600 border border-blue-300 rounded hover:bg-blue-50"
|
|
226
|
+
title="Make this version active">
|
|
227
|
+
Activate
|
|
228
|
+
</button>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<!-- Systems Selector -->
|
|
232
|
+
<div class="flex items-center space-x-2 border-l border-gray-200 pl-4">
|
|
233
|
+
<label class="text-sm font-medium text-gray-600">System:</label>
|
|
234
|
+
<select v-model="selectedSystemId"
|
|
235
|
+
@change="onSystemChange"
|
|
236
|
+
class="px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 min-w-[180px]">
|
|
237
|
+
<option value="">-- No system --</option>
|
|
238
|
+
<option v-for="system in systems" :key="system.id" :value="system.id">
|
|
239
|
+
🔗 [[ system.name ]]
|
|
240
|
+
</option>
|
|
241
|
+
</select>
|
|
242
|
+
<button @click="openSystemsModal"
|
|
243
|
+
class="px-2 py-1.5 text-sm text-purple-600 border border-purple-300 rounded hover:bg-purple-50"
|
|
244
|
+
title="Manage multi-agent systems">
|
|
245
|
+
<span>⚙️</span>
|
|
246
|
+
</button>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<!-- Agent Info & Actions -->
|
|
251
|
+
<div class="flex items-center space-x-3">
|
|
252
|
+
<div v-if="selectedAgent" class="text-sm text-gray-500">
|
|
253
|
+
<span class="font-mono text-xs bg-gray-100 px-2 py-0.5 rounded">[[ selectedAgent.slug ]]</span>
|
|
254
|
+
</div>
|
|
255
|
+
<button @click="openSchemaEditor"
|
|
256
|
+
class="flex items-center space-x-1 px-2 py-1 text-sm rounded border"
|
|
257
|
+
:class="selectedAgentId ? 'text-blue-600 border-blue-300 hover:bg-blue-50 hover:border-blue-400' : 'text-gray-400 border-gray-200 cursor-not-allowed'"
|
|
258
|
+
title="Edit Agent Schema (JSON)"
|
|
259
|
+
:disabled="!selectedAgentId">
|
|
260
|
+
<span>📋</span>
|
|
261
|
+
<span>Schema</span>
|
|
262
|
+
</button>
|
|
263
|
+
<button @click="loadAgents"
|
|
264
|
+
class="text-gray-500 hover:text-gray-700 p-1.5 rounded hover:bg-gray-100"
|
|
265
|
+
title="Refresh agent list">
|
|
266
|
+
<i class="pi pi-refresh"></i>
|
|
267
|
+
</button>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<!-- Main Content -->
|
|
272
|
+
<div class="flex-1 flex overflow-hidden">
|
|
273
|
+
<!-- Left Pane: Test Your Agent -->
|
|
274
|
+
<div class="w-1/2 border-r border-gray-200 flex flex-col">
|
|
275
|
+
<div class="px-4 py-2 bg-gray-50 border-b border-gray-200 flex items-center justify-between">
|
|
276
|
+
<div class="flex items-center space-x-2">
|
|
277
|
+
<span class="text-lg">🧪</span>
|
|
278
|
+
<h2 class="font-medium text-gray-700">Test Your Agent</h2>
|
|
279
|
+
</div>
|
|
280
|
+
<button @click="refreshTestAgent"
|
|
281
|
+
class="text-gray-500 hover:text-gray-700 p-1 rounded hover:bg-gray-200"
|
|
282
|
+
title="Refresh agent config">
|
|
283
|
+
<i class="pi pi-refresh text-sm"></i>
|
|
284
|
+
</button>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="flex-1 relative bg-gray-100">
|
|
287
|
+
<div id="test-chat-container" class="absolute inset-0"></div>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<!-- Right Pane: Builder Agent -->
|
|
292
|
+
<div class="w-1/2 flex flex-col">
|
|
293
|
+
<div class="px-4 py-2 bg-gray-50 border-b border-gray-200 flex items-center justify-between">
|
|
294
|
+
<div class="flex items-center space-x-2">
|
|
295
|
+
<span class="text-lg">🛠️</span>
|
|
296
|
+
<h2 class="font-medium text-gray-700">Agent Builder</h2>
|
|
297
|
+
</div>
|
|
298
|
+
<span class="text-xs text-gray-500">Chat with the builder to customize your agent</span>
|
|
299
|
+
</div>
|
|
300
|
+
<div class="flex-1 relative bg-gray-50">
|
|
301
|
+
<div id="builder-chat-container" class="absolute inset-0"></div>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<!-- Schema Editor Modal -->
|
|
308
|
+
<div v-if="schemaEditorOpen" class="schema-modal-overlay" @click.self="closeSchemaEditor">
|
|
309
|
+
<div class="schema-modal">
|
|
310
|
+
<div class="schema-modal-header">
|
|
311
|
+
<div class="flex items-center space-x-3">
|
|
312
|
+
<span class="text-xl">📋</span>
|
|
313
|
+
<div>
|
|
314
|
+
<h3 class="text-lg font-semibold text-gray-800">Agent Schema Editor</h3>
|
|
315
|
+
<p class="text-sm text-gray-500">View and edit the complete agent configuration</p>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
<div class="flex items-center space-x-2">
|
|
319
|
+
<button @click="reloadSchema"
|
|
320
|
+
class="px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded"
|
|
321
|
+
:disabled="schemaLoading"
|
|
322
|
+
title="Reload schema from server">
|
|
323
|
+
<i class="pi pi-refresh" :class="{'pi-spin': schemaLoading}"></i>
|
|
324
|
+
<span class="ml-1">Reload</span>
|
|
325
|
+
</button>
|
|
326
|
+
<button @click="closeSchemaEditor"
|
|
327
|
+
class="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded">
|
|
328
|
+
<i class="pi pi-times text-lg"></i>
|
|
329
|
+
</button>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
<div class="schema-tabs">
|
|
334
|
+
<div class="schema-tab" :class="{active: schemaTab === 'full'}" @click="schemaTab = 'full'">
|
|
335
|
+
Full Schema
|
|
336
|
+
</div>
|
|
337
|
+
<div class="schema-tab" :class="{active: schemaTab === 'version'}" @click="schemaTab = 'version'">
|
|
338
|
+
Version Config
|
|
339
|
+
</div>
|
|
340
|
+
<div class="schema-tab" :class="{active: schemaTab === 'tools'}" @click="schemaTab = 'tools'">
|
|
341
|
+
Tools
|
|
342
|
+
</div>
|
|
343
|
+
<div class="schema-tab" :class="{active: schemaTab === 'dynamic_tools'}" @click="schemaTab = 'dynamic_tools'">
|
|
344
|
+
Dynamic Tools
|
|
345
|
+
</div>
|
|
346
|
+
<div class="schema-tab" :class="{active: schemaTab === 'sub_agents'}" @click="schemaTab = 'sub_agents'">
|
|
347
|
+
🔗 Sub-Agents
|
|
348
|
+
</div>
|
|
349
|
+
<div class="schema-tab" :class="{active: schemaTab === 'knowledge'}" @click="schemaTab = 'knowledge'">
|
|
350
|
+
Knowledge
|
|
351
|
+
</div>
|
|
352
|
+
<div class="schema-tab" :class="{active: schemaTab === 'rag'}" @click="schemaTab = 'rag'">
|
|
353
|
+
RAG Config
|
|
354
|
+
</div>
|
|
355
|
+
<div class="schema-tab" :class="{active: schemaTab === 'functions'}" @click="schemaTab = 'functions'">
|
|
356
|
+
Available Functions
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<div class="schema-modal-body">
|
|
361
|
+
<div class="schema-content">
|
|
362
|
+
<textarea
|
|
363
|
+
v-if="schemaTab === 'full'"
|
|
364
|
+
class="schema-editor"
|
|
365
|
+
v-model="schemaJson"
|
|
366
|
+
@input="validateSchema"
|
|
367
|
+
spellcheck="false"
|
|
368
|
+
></textarea>
|
|
369
|
+
<textarea
|
|
370
|
+
v-else-if="schemaTab === 'version'"
|
|
371
|
+
class="schema-editor"
|
|
372
|
+
v-model="versionJson"
|
|
373
|
+
@input="validateSchema"
|
|
374
|
+
spellcheck="false"
|
|
375
|
+
></textarea>
|
|
376
|
+
<textarea
|
|
377
|
+
v-else-if="schemaTab === 'tools'"
|
|
378
|
+
class="schema-editor"
|
|
379
|
+
v-model="toolsJson"
|
|
380
|
+
@input="validateSchema"
|
|
381
|
+
spellcheck="false"
|
|
382
|
+
></textarea>
|
|
383
|
+
<textarea
|
|
384
|
+
v-else-if="schemaTab === 'dynamic_tools'"
|
|
385
|
+
class="schema-editor"
|
|
386
|
+
v-model="dynamicToolsJson"
|
|
387
|
+
@input="validateSchema"
|
|
388
|
+
spellcheck="false"
|
|
389
|
+
></textarea>
|
|
390
|
+
<textarea
|
|
391
|
+
v-else-if="schemaTab === 'sub_agents'"
|
|
392
|
+
class="schema-editor"
|
|
393
|
+
v-model="subAgentToolsJson"
|
|
394
|
+
@input="validateSchema"
|
|
395
|
+
spellcheck="false"
|
|
396
|
+
></textarea>
|
|
397
|
+
<textarea
|
|
398
|
+
v-else-if="schemaTab === 'knowledge'"
|
|
399
|
+
class="schema-editor"
|
|
400
|
+
v-model="knowledgeJson"
|
|
401
|
+
@input="validateSchema"
|
|
402
|
+
spellcheck="false"
|
|
403
|
+
></textarea>
|
|
404
|
+
<textarea
|
|
405
|
+
v-else-if="schemaTab === 'rag'"
|
|
406
|
+
class="schema-editor"
|
|
407
|
+
v-model="ragConfigJson"
|
|
408
|
+
@input="validateSchema"
|
|
409
|
+
spellcheck="false"
|
|
410
|
+
></textarea>
|
|
411
|
+
<textarea
|
|
412
|
+
v-else-if="schemaTab === 'functions'"
|
|
413
|
+
class="schema-editor"
|
|
414
|
+
v-model="functionsJson"
|
|
415
|
+
readonly
|
|
416
|
+
spellcheck="false"
|
|
417
|
+
></textarea>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
|
|
421
|
+
<div class="schema-modal-footer">
|
|
422
|
+
<div class="schema-status" :class="{error: schemaError, success: schemaSaved}">
|
|
423
|
+
<span v-if="schemaLoading"><i class="pi pi-spin pi-spinner"></i> Loading...</span>
|
|
424
|
+
<span v-else-if="schemaError"><i class="pi pi-exclamation-triangle"></i> [[ schemaError ]]</span>
|
|
425
|
+
<span v-else-if="schemaSaved"><i class="pi pi-check"></i> Saved successfully</span>
|
|
426
|
+
<span v-else-if="schemaModified"><i class="pi pi-pencil"></i> Modified (unsaved)</span>
|
|
427
|
+
<span v-else>Last loaded: [[ schemaLoadedAt ]]</span>
|
|
428
|
+
</div>
|
|
429
|
+
<div class="btn-group">
|
|
430
|
+
<button @click="copySchema"
|
|
431
|
+
class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 border border-gray-300 rounded hover:bg-gray-50">
|
|
432
|
+
<i class="pi pi-copy mr-1"></i> Copy
|
|
433
|
+
</button>
|
|
434
|
+
<button @click="closeSchemaEditor"
|
|
435
|
+
class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 border border-gray-300 rounded hover:bg-gray-50">
|
|
436
|
+
Cancel
|
|
437
|
+
</button>
|
|
438
|
+
<button @click="saveSchema"
|
|
439
|
+
class="px-4 py-2 text-sm text-white bg-blue-600 hover:bg-blue-700 rounded"
|
|
440
|
+
:disabled="schemaLoading || !!schemaError || schemaTab === 'functions'">
|
|
441
|
+
<i class="pi pi-save mr-1"></i> Save Changes
|
|
442
|
+
</button>
|
|
443
|
+
</div>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
</div>
|
|
447
|
+
|
|
448
|
+
<!-- Systems Management Modal -->
|
|
449
|
+
<div v-if="systemsModalOpen" class="schema-modal-overlay" @click.self="closeSystemsModal">
|
|
450
|
+
<div class="schema-modal" style="max-width: 900px; height: 80vh;">
|
|
451
|
+
<div class="schema-modal-header">
|
|
452
|
+
<div class="flex items-center space-x-3">
|
|
453
|
+
<span class="text-xl">🔗</span>
|
|
454
|
+
<div>
|
|
455
|
+
<h3 class="text-lg font-semibold text-gray-800">Multi-Agent Systems</h3>
|
|
456
|
+
<p class="text-sm text-gray-500">Manage agent systems and their members</p>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
<div class="flex items-center space-x-2">
|
|
460
|
+
<button @click="loadSystems"
|
|
461
|
+
class="px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded"
|
|
462
|
+
:disabled="systemsLoading"
|
|
463
|
+
title="Refresh systems">
|
|
464
|
+
<i class="pi pi-refresh" :class="{'pi-spin': systemsLoading}"></i>
|
|
465
|
+
</button>
|
|
466
|
+
<button @click="closeSystemsModal"
|
|
467
|
+
class="p-1.5 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded">
|
|
468
|
+
<i class="pi pi-times text-lg"></i>
|
|
469
|
+
</button>
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
<div class="schema-modal-body" style="padding: 1rem;">
|
|
474
|
+
<!-- Create New System -->
|
|
475
|
+
<div class="mb-4 p-4 bg-gray-50 rounded-lg border border-gray-200">
|
|
476
|
+
<h4 class="text-sm font-medium text-gray-700 mb-3">Create New System</h4>
|
|
477
|
+
<div class="flex items-end space-x-3">
|
|
478
|
+
<div class="flex-1">
|
|
479
|
+
<label class="block text-xs text-gray-500 mb-1">Name</label>
|
|
480
|
+
<input v-model="newSystemName"
|
|
481
|
+
type="text"
|
|
482
|
+
placeholder="e.g., Customer Support"
|
|
483
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-purple-500">
|
|
484
|
+
</div>
|
|
485
|
+
<div class="flex-1">
|
|
486
|
+
<label class="block text-xs text-gray-500 mb-1">Slug</label>
|
|
487
|
+
<input v-model="newSystemSlug"
|
|
488
|
+
type="text"
|
|
489
|
+
placeholder="e.g., customer-support"
|
|
490
|
+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-purple-500">
|
|
491
|
+
</div>
|
|
492
|
+
<div>
|
|
493
|
+
<label class="block text-xs text-gray-500 mb-1">Entry Agent</label>
|
|
494
|
+
<select v-model="newSystemEntryAgent"
|
|
495
|
+
class="px-3 py-2 text-sm border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-purple-500">
|
|
496
|
+
<option value="">-- Select --</option>
|
|
497
|
+
<option v-for="agent in agents" :key="agent.id" :value="agent.id">
|
|
498
|
+
[[ agent.name ]]
|
|
499
|
+
</option>
|
|
500
|
+
</select>
|
|
501
|
+
</div>
|
|
502
|
+
<button @click="createSystem"
|
|
503
|
+
:disabled="!newSystemName || !newSystemSlug"
|
|
504
|
+
class="px-4 py-2 text-sm text-white bg-purple-600 hover:bg-purple-700 rounded disabled:opacity-50 disabled:cursor-not-allowed">
|
|
505
|
+
Create
|
|
506
|
+
</button>
|
|
507
|
+
</div>
|
|
508
|
+
</div>
|
|
509
|
+
|
|
510
|
+
<!-- Systems List -->
|
|
511
|
+
<div class="space-y-3 overflow-y-auto" style="max-height: calc(80vh - 280px);">
|
|
512
|
+
<div v-if="systemsLoading" class="text-center py-8 text-gray-500">
|
|
513
|
+
<i class="pi pi-spin pi-spinner text-2xl"></i>
|
|
514
|
+
<p class="mt-2">Loading systems...</p>
|
|
515
|
+
</div>
|
|
516
|
+
<div v-else-if="systems.length === 0" class="text-center py-8 text-gray-500">
|
|
517
|
+
<span class="text-4xl">🔗</span>
|
|
518
|
+
<p class="mt-2">No systems yet. Create one above!</p>
|
|
519
|
+
</div>
|
|
520
|
+
<div v-else v-for="system in systems" :key="system.id"
|
|
521
|
+
class="p-4 bg-white border border-gray-200 rounded-lg hover:border-purple-300 transition-colors">
|
|
522
|
+
<div class="flex items-start justify-between">
|
|
523
|
+
<div class="flex-1">
|
|
524
|
+
<div class="flex items-center space-x-2">
|
|
525
|
+
<h4 class="font-medium text-gray-800">[[ system.name ]]</h4>
|
|
526
|
+
<span class="text-xs font-mono bg-gray-100 px-2 py-0.5 rounded">[[ system.slug ]]</span>
|
|
527
|
+
</div>
|
|
528
|
+
<p class="text-sm text-gray-500 mt-1">[[ system.description || 'No description' ]]</p>
|
|
529
|
+
<div class="flex items-center space-x-4 mt-2 text-xs text-gray-500">
|
|
530
|
+
<span v-if="system.entry_agent_slug">
|
|
531
|
+
Entry: <span class="font-mono">[[ system.entry_agent_slug ]]</span>
|
|
532
|
+
</span>
|
|
533
|
+
<span>[[ system.member_count || 0 ]] members</span>
|
|
534
|
+
<span v-if="system.active_version">
|
|
535
|
+
Active: v[[ system.active_version ]]
|
|
536
|
+
</span>
|
|
537
|
+
</div>
|
|
538
|
+
</div>
|
|
539
|
+
<div class="flex items-center space-x-2">
|
|
540
|
+
<button @click="viewSystemDetails(system)"
|
|
541
|
+
class="px-3 py-1.5 text-xs text-purple-600 border border-purple-300 rounded hover:bg-purple-50">
|
|
542
|
+
Details
|
|
543
|
+
</button>
|
|
544
|
+
<button @click="publishSystem(system)"
|
|
545
|
+
class="px-3 py-1.5 text-xs text-green-600 border border-green-300 rounded hover:bg-green-50">
|
|
546
|
+
Publish
|
|
547
|
+
</button>
|
|
548
|
+
<button @click="deleteSystem(system)"
|
|
549
|
+
class="px-3 py-1.5 text-xs text-red-600 border border-red-300 rounded hover:bg-red-50">
|
|
550
|
+
Delete
|
|
551
|
+
</button>
|
|
552
|
+
</div>
|
|
553
|
+
</div>
|
|
554
|
+
|
|
555
|
+
<!-- System Members (expandable) -->
|
|
556
|
+
<div v-if="expandedSystemId === system.id" class="mt-4 pt-4 border-t border-gray-100">
|
|
557
|
+
<div class="flex items-center justify-between mb-2">
|
|
558
|
+
<h5 class="text-sm font-medium text-gray-700">Members</h5>
|
|
559
|
+
<button @click="addMemberToSystem(system)"
|
|
560
|
+
class="text-xs text-purple-600 hover:text-purple-800">
|
|
561
|
+
+ Add Member
|
|
562
|
+
</button>
|
|
563
|
+
</div>
|
|
564
|
+
<div v-if="system.members && system.members.length > 0" class="space-y-2">
|
|
565
|
+
<div v-for="member in system.members" :key="member.id"
|
|
566
|
+
class="flex items-center justify-between p-2 bg-gray-50 rounded text-sm">
|
|
567
|
+
<div class="flex items-center space-x-2">
|
|
568
|
+
<span>[[ member.agent_name ]]</span>
|
|
569
|
+
<span class="text-xs font-mono text-gray-500">([[ member.agent_slug ]])</span>
|
|
570
|
+
<span v-if="member.role" class="text-xs bg-purple-100 text-purple-700 px-2 py-0.5 rounded">
|
|
571
|
+
[[ member.role ]]
|
|
572
|
+
</span>
|
|
573
|
+
<span v-if="member.is_entry_point" class="text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded">
|
|
574
|
+
Entry Point
|
|
575
|
+
</span>
|
|
576
|
+
</div>
|
|
577
|
+
<button @click="removeMemberFromSystem(system, member)"
|
|
578
|
+
class="text-red-500 hover:text-red-700">
|
|
579
|
+
<i class="pi pi-times text-xs"></i>
|
|
580
|
+
</button>
|
|
581
|
+
</div>
|
|
582
|
+
</div>
|
|
583
|
+
<div v-else class="text-sm text-gray-500 italic">No members yet</div>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
|
|
589
|
+
<div class="schema-modal-footer">
|
|
590
|
+
<div class="text-sm text-gray-500">
|
|
591
|
+
[[ systems.length ]] system(s)
|
|
592
|
+
</div>
|
|
593
|
+
<button @click="closeSystemsModal"
|
|
594
|
+
class="px-4 py-2 text-sm text-gray-600 hover:text-gray-800 border border-gray-300 rounded hover:bg-gray-50">
|
|
595
|
+
Close
|
|
596
|
+
</button>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
<!-- Delete Confirmation Modal -->
|
|
602
|
+
<div v-if="deleteConfirmOpen" class="schema-modal-overlay" @click.self="cancelDelete">
|
|
603
|
+
<div class="bg-white rounded-lg shadow-xl p-6 max-w-md w-full">
|
|
604
|
+
<div class="flex items-center space-x-3 mb-4">
|
|
605
|
+
<span class="text-3xl">⚠️</span>
|
|
606
|
+
<div>
|
|
607
|
+
<h3 class="text-lg font-semibold text-gray-800">Confirm Delete</h3>
|
|
608
|
+
<p class="text-sm text-gray-500">[[ deleteConfirmMessage ]]</p>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
<div class="flex justify-end space-x-3">
|
|
612
|
+
<button @click="cancelDelete"
|
|
613
|
+
class="px-4 py-2 text-sm text-gray-600 border border-gray-300 rounded hover:bg-gray-50">
|
|
614
|
+
Cancel
|
|
615
|
+
</button>
|
|
616
|
+
<button @click="confirmDelete"
|
|
617
|
+
class="px-4 py-2 text-sm text-white bg-red-600 hover:bg-red-700 rounded">
|
|
618
|
+
Delete
|
|
619
|
+
</button>
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
</div>
|
|
623
|
+
{% endblock %}
|
|
624
|
+
|
|
625
|
+
{% block extra_js %}
|
|
626
|
+
<script>
|
|
627
|
+
const { createApp } = Vue;
|
|
628
|
+
|
|
629
|
+
// Agent configuration from Django
|
|
630
|
+
const agentConfig = {
|
|
631
|
+
isNew: {{ is_new|yesno:"true,false" }},
|
|
632
|
+
agentId: {% if agent %}"{{ agent.id }}"{% else %}null{% endif %},
|
|
633
|
+
agentSlug: {% if agent %}"{{ agent.slug }}"{% else %}null{% endif %},
|
|
634
|
+
builderAgentKey: "{{ builder_agent_key }}",
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
// Backend URL (same origin)
|
|
638
|
+
const backendUrl = window.location.origin;
|
|
639
|
+
|
|
640
|
+
// Initialize the test agent chat widget
|
|
641
|
+
function initTestChat(agentSlug = null) {
|
|
642
|
+
const container = document.getElementById('test-chat-container');
|
|
643
|
+
if (!container) return; // Container not ready yet
|
|
644
|
+
|
|
645
|
+
// Use passed slug or fall back to agentConfig
|
|
646
|
+
const slugToUse = agentSlug || agentConfig.agentSlug;
|
|
647
|
+
|
|
648
|
+
if (!slugToUse) {
|
|
649
|
+
// Show placeholder for new agents
|
|
650
|
+
container.innerHTML = `
|
|
651
|
+
<div class="flex items-center justify-center h-full text-gray-500">
|
|
652
|
+
<div class="text-center">
|
|
653
|
+
<div class="text-4xl mb-4">🤖</div>
|
|
654
|
+
<p>Select an agent above to test it</p>
|
|
655
|
+
<p class="text-sm mt-2">Or create a new one with the "+ New" button</p>
|
|
656
|
+
</div>
|
|
657
|
+
</div>
|
|
658
|
+
`;
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Create a unique instance for the test chat
|
|
663
|
+
if (window.testChatWidget) {
|
|
664
|
+
window.testChatWidget.destroy();
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
window.testChatWidget = ChatWidget.createInstance({
|
|
668
|
+
containerId: 'test-chat-container',
|
|
669
|
+
backendUrl: backendUrl,
|
|
670
|
+
agentKey: slugToUse,
|
|
671
|
+
title: 'Test Agent',
|
|
672
|
+
primaryColor: '#3b82f6',
|
|
673
|
+
showClearButton: true,
|
|
674
|
+
showDebugButton: true,
|
|
675
|
+
showExpandButton: false,
|
|
676
|
+
showModelSelector: true,
|
|
677
|
+
embedded: true,
|
|
678
|
+
authStrategy: 'session',
|
|
679
|
+
apiPaths: {
|
|
680
|
+
runs: '/api/agent-runtime/runs/',
|
|
681
|
+
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
682
|
+
models: '/api/agent-runtime/models/',
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Initialize the builder agent chat widget
|
|
688
|
+
function initBuilderChat(targetAgentId = null, vueApp = null) {
|
|
689
|
+
const container = document.getElementById('builder-chat-container');
|
|
690
|
+
if (!container) return; // Container not ready yet
|
|
691
|
+
|
|
692
|
+
if (window.builderChatWidget) {
|
|
693
|
+
window.builderChatWidget.destroy();
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Use passed agent ID or fall back to agentConfig
|
|
697
|
+
const agentIdToUse = targetAgentId || agentConfig.agentId;
|
|
698
|
+
|
|
699
|
+
window.builderChatWidget = ChatWidget.createInstance({
|
|
700
|
+
containerId: 'builder-chat-container',
|
|
701
|
+
backendUrl: backendUrl,
|
|
702
|
+
agentKey: agentConfig.builderAgentKey,
|
|
703
|
+
title: 'Agent Builder',
|
|
704
|
+
primaryColor: '#8b5cf6',
|
|
705
|
+
showClearButton: true,
|
|
706
|
+
showDebugButton: true,
|
|
707
|
+
showExpandButton: false,
|
|
708
|
+
showModelSelector: true,
|
|
709
|
+
embedded: true,
|
|
710
|
+
authStrategy: 'session',
|
|
711
|
+
apiPaths: {
|
|
712
|
+
runs: '/api/agent-runtime/runs/',
|
|
713
|
+
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
714
|
+
models: '/api/agent-runtime/models/',
|
|
715
|
+
},
|
|
716
|
+
// Pass the agent ID to the builder agent
|
|
717
|
+
metadata: {
|
|
718
|
+
agent_id: agentIdToUse,
|
|
719
|
+
},
|
|
720
|
+
// Handle UI control events from the builder agent
|
|
721
|
+
onUIControl: (payload) => {
|
|
722
|
+
console.log('[Builder] UI Control event:', payload);
|
|
723
|
+
if (vueApp && payload.action) {
|
|
724
|
+
switch (payload.action) {
|
|
725
|
+
case 'switch_agent':
|
|
726
|
+
// Switch to a different agent
|
|
727
|
+
if (payload.agent_id) {
|
|
728
|
+
vueApp.selectedAgentId = payload.agent_id;
|
|
729
|
+
vueApp.onAgentChange();
|
|
730
|
+
}
|
|
731
|
+
break;
|
|
732
|
+
case 'switch_system':
|
|
733
|
+
// Switch to a different system
|
|
734
|
+
if (payload.system_id) {
|
|
735
|
+
vueApp.selectedSystemId = payload.system_id;
|
|
736
|
+
vueApp.onSystemChange();
|
|
737
|
+
}
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
// Handle events for debugging/logging
|
|
743
|
+
onEvent: (eventType, payload) => {
|
|
744
|
+
// Log sub-agent events for visibility
|
|
745
|
+
if (eventType === 'sub_agent.start' || eventType === 'sub_agent.end') {
|
|
746
|
+
console.log(`[Builder] ${eventType}:`, payload);
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
createApp({
|
|
753
|
+
delimiters: ['[[', ']]'], // Avoid conflict with Django templates
|
|
754
|
+
data() {
|
|
755
|
+
return {
|
|
756
|
+
agentConfig,
|
|
757
|
+
// Agent/version selection
|
|
758
|
+
agents: [],
|
|
759
|
+
versions: [],
|
|
760
|
+
selectedAgentId: agentConfig.agentId || '',
|
|
761
|
+
selectedVersionId: '',
|
|
762
|
+
// Schema editor state
|
|
763
|
+
schemaEditorOpen: false,
|
|
764
|
+
schemaTab: 'full',
|
|
765
|
+
schemaLoading: false,
|
|
766
|
+
schemaError: null,
|
|
767
|
+
schemaSaved: false,
|
|
768
|
+
schemaModified: false,
|
|
769
|
+
schemaLoadedAt: null,
|
|
770
|
+
// Schema data
|
|
771
|
+
fullSchema: null,
|
|
772
|
+
schemaJson: '',
|
|
773
|
+
versionJson: '',
|
|
774
|
+
toolsJson: '',
|
|
775
|
+
dynamicToolsJson: '',
|
|
776
|
+
subAgentToolsJson: '',
|
|
777
|
+
knowledgeJson: '',
|
|
778
|
+
ragConfigJson: '',
|
|
779
|
+
functionsJson: '',
|
|
780
|
+
// Systems management
|
|
781
|
+
systems: [],
|
|
782
|
+
selectedSystemId: '',
|
|
783
|
+
systemsModalOpen: false,
|
|
784
|
+
systemsLoading: false,
|
|
785
|
+
expandedSystemId: null,
|
|
786
|
+
newSystemName: '',
|
|
787
|
+
newSystemSlug: '',
|
|
788
|
+
newSystemEntryAgent: '',
|
|
789
|
+
// Delete confirmation
|
|
790
|
+
deleteConfirmOpen: false,
|
|
791
|
+
deleteConfirmMessage: '',
|
|
792
|
+
deleteConfirmCallback: null,
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
computed: {
|
|
796
|
+
selectedAgent() {
|
|
797
|
+
return this.agents.find(a => a.id === this.selectedAgentId);
|
|
798
|
+
},
|
|
799
|
+
isActiveVersion() {
|
|
800
|
+
const version = this.versions.find(v => v.id === this.selectedVersionId);
|
|
801
|
+
return version && version.is_active;
|
|
802
|
+
},
|
|
803
|
+
},
|
|
804
|
+
methods: {
|
|
805
|
+
async loadAgents() {
|
|
806
|
+
try {
|
|
807
|
+
const response = await fetch('/studio/api/agents/', {
|
|
808
|
+
credentials: 'include',
|
|
809
|
+
headers: { 'Accept': 'application/json' },
|
|
810
|
+
});
|
|
811
|
+
if (response.ok) {
|
|
812
|
+
const data = await response.json();
|
|
813
|
+
// Handle paginated response (results array) or direct array
|
|
814
|
+
const agentList = Array.isArray(data) ? data : (data.results || []);
|
|
815
|
+
// Filter out any agents without valid IDs
|
|
816
|
+
this.agents = agentList.filter(a => a && a.id);
|
|
817
|
+
// If we have a pre-selected agent, load its versions
|
|
818
|
+
if (this.selectedAgentId) {
|
|
819
|
+
await this.loadVersions();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
} catch (error) {
|
|
823
|
+
console.error('Error loading agents:', error);
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
|
|
827
|
+
async loadVersions() {
|
|
828
|
+
if (!this.selectedAgentId) {
|
|
829
|
+
this.versions = [];
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
const response = await fetch(`/studio/api/agents/${this.selectedAgentId}/versions/`, {
|
|
834
|
+
credentials: 'include',
|
|
835
|
+
headers: { 'Accept': 'application/json' },
|
|
836
|
+
});
|
|
837
|
+
if (response.ok) {
|
|
838
|
+
const data = await response.json();
|
|
839
|
+
// Handle paginated response (results array) or direct array
|
|
840
|
+
this.versions = Array.isArray(data) ? data : (data.results || []);
|
|
841
|
+
// Select the active version by default
|
|
842
|
+
const activeVersion = this.versions.find(v => v.is_active);
|
|
843
|
+
if (activeVersion) {
|
|
844
|
+
this.selectedVersionId = activeVersion.id;
|
|
845
|
+
} else if (this.versions.length > 0) {
|
|
846
|
+
this.selectedVersionId = this.versions[0].id;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
} catch (error) {
|
|
850
|
+
console.error('Error loading versions:', error);
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
|
|
854
|
+
async onAgentChange() {
|
|
855
|
+
// Update the agentConfig for chat widgets
|
|
856
|
+
const agent = this.selectedAgent;
|
|
857
|
+
if (agent) {
|
|
858
|
+
this.agentConfig.agentId = agent.id;
|
|
859
|
+
this.agentConfig.agentSlug = agent.slug;
|
|
860
|
+
// Update URL without reload
|
|
861
|
+
window.history.replaceState({}, '', `/studio/agents/${agent.id}/`);
|
|
862
|
+
} else {
|
|
863
|
+
this.agentConfig.agentId = null;
|
|
864
|
+
this.agentConfig.agentSlug = null;
|
|
865
|
+
window.history.replaceState({}, '', '/studio/agents/new/');
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Load versions for the new agent
|
|
869
|
+
await this.loadVersions();
|
|
870
|
+
|
|
871
|
+
// Reinitialize chat widgets
|
|
872
|
+
this.refreshTestAgent();
|
|
873
|
+
this.refreshBuilderChat();
|
|
874
|
+
},
|
|
875
|
+
|
|
876
|
+
async onVersionChange() {
|
|
877
|
+
// Version change might affect the test agent behavior
|
|
878
|
+
// For now, just refresh the test chat
|
|
879
|
+
this.refreshTestAgent();
|
|
880
|
+
},
|
|
881
|
+
|
|
882
|
+
async activateVersion() {
|
|
883
|
+
if (!this.selectedAgentId || !this.selectedVersionId) return;
|
|
884
|
+
|
|
885
|
+
try {
|
|
886
|
+
const response = await fetch(
|
|
887
|
+
`/studio/api/agents/${this.selectedAgentId}/versions/${this.selectedVersionId}/activate/`,
|
|
888
|
+
{
|
|
889
|
+
method: 'POST',
|
|
890
|
+
credentials: 'include',
|
|
891
|
+
headers: {
|
|
892
|
+
'Content-Type': 'application/json',
|
|
893
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
894
|
+
},
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
if (response.ok) {
|
|
898
|
+
await this.loadVersions();
|
|
899
|
+
this.refreshTestAgent();
|
|
900
|
+
}
|
|
901
|
+
} catch (error) {
|
|
902
|
+
console.error('Error activating version:', error);
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
|
|
906
|
+
async createNewAgent() {
|
|
907
|
+
const name = prompt('Enter a name for the new agent:');
|
|
908
|
+
if (!name) return;
|
|
909
|
+
|
|
910
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
911
|
+
|
|
912
|
+
try {
|
|
913
|
+
const response = await fetch('/studio/api/agents/', {
|
|
914
|
+
method: 'POST',
|
|
915
|
+
credentials: 'include',
|
|
916
|
+
headers: {
|
|
917
|
+
'Content-Type': 'application/json',
|
|
918
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
919
|
+
},
|
|
920
|
+
body: JSON.stringify({ name, slug }),
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
if (response.ok) {
|
|
924
|
+
const newAgent = await response.json();
|
|
925
|
+
await this.loadAgents();
|
|
926
|
+
this.selectedAgentId = newAgent.id;
|
|
927
|
+
await this.onAgentChange();
|
|
928
|
+
} else {
|
|
929
|
+
const error = await response.json();
|
|
930
|
+
alert(`Failed to create agent: ${error.detail || error.slug || JSON.stringify(error)}`);
|
|
931
|
+
}
|
|
932
|
+
} catch (error) {
|
|
933
|
+
console.error('Error creating agent:', error);
|
|
934
|
+
alert('Failed to create agent');
|
|
935
|
+
}
|
|
936
|
+
},
|
|
937
|
+
|
|
938
|
+
refreshTestAgent() {
|
|
939
|
+
const agent = this.selectedAgent;
|
|
940
|
+
initTestChat(agent ? agent.slug : null);
|
|
941
|
+
},
|
|
942
|
+
|
|
943
|
+
refreshBuilderChat() {
|
|
944
|
+
initBuilderChat(this.selectedAgentId, this);
|
|
945
|
+
},
|
|
946
|
+
|
|
947
|
+
async openSchemaEditor() {
|
|
948
|
+
if (!this.selectedAgentId) return;
|
|
949
|
+
this.schemaEditorOpen = true;
|
|
950
|
+
await this.reloadSchema();
|
|
951
|
+
},
|
|
952
|
+
|
|
953
|
+
closeSchemaEditor() {
|
|
954
|
+
this.schemaEditorOpen = false;
|
|
955
|
+
this.schemaError = null;
|
|
956
|
+
this.schemaSaved = false;
|
|
957
|
+
this.schemaModified = false;
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
async reloadSchema() {
|
|
961
|
+
if (!this.selectedAgentId) return;
|
|
962
|
+
|
|
963
|
+
this.schemaLoading = true;
|
|
964
|
+
this.schemaError = null;
|
|
965
|
+
this.schemaSaved = false;
|
|
966
|
+
this.schemaModified = false;
|
|
967
|
+
|
|
968
|
+
try {
|
|
969
|
+
const response = await fetch(`/studio/api/agents/${this.selectedAgentId}/full-schema/`, {
|
|
970
|
+
credentials: 'include',
|
|
971
|
+
headers: {
|
|
972
|
+
'Accept': 'application/json',
|
|
973
|
+
},
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
if (!response.ok) {
|
|
977
|
+
throw new Error(`Failed to load schema: ${response.status}`);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
this.fullSchema = await response.json();
|
|
981
|
+
this.schemaLoadedAt = new Date().toLocaleTimeString();
|
|
982
|
+
|
|
983
|
+
// Update individual JSON views
|
|
984
|
+
this.updateJsonViews();
|
|
985
|
+
|
|
986
|
+
} catch (error) {
|
|
987
|
+
console.error('Error loading schema:', error);
|
|
988
|
+
this.schemaError = error.message;
|
|
989
|
+
} finally {
|
|
990
|
+
this.schemaLoading = false;
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
|
|
994
|
+
updateJsonViews() {
|
|
995
|
+
if (!this.fullSchema) return;
|
|
996
|
+
|
|
997
|
+
this.schemaJson = JSON.stringify(this.fullSchema, null, 2);
|
|
998
|
+
this.versionJson = JSON.stringify(this.fullSchema.version || {}, null, 2);
|
|
999
|
+
this.toolsJson = JSON.stringify(this.fullSchema.tools || [], null, 2);
|
|
1000
|
+
this.dynamicToolsJson = JSON.stringify(this.fullSchema.dynamic_tools || [], null, 2);
|
|
1001
|
+
this.subAgentToolsJson = JSON.stringify(this.fullSchema.sub_agent_tools || [], null, 2);
|
|
1002
|
+
this.knowledgeJson = JSON.stringify(this.fullSchema.knowledge || [], null, 2);
|
|
1003
|
+
this.ragConfigJson = JSON.stringify(this.fullSchema.rag_config || {}, null, 2);
|
|
1004
|
+
this.functionsJson = JSON.stringify(this.fullSchema.available_functions || [], null, 2);
|
|
1005
|
+
},
|
|
1006
|
+
|
|
1007
|
+
validateSchema() {
|
|
1008
|
+
this.schemaModified = true;
|
|
1009
|
+
this.schemaSaved = false;
|
|
1010
|
+
this.schemaError = null;
|
|
1011
|
+
|
|
1012
|
+
try {
|
|
1013
|
+
// Validate current tab's JSON
|
|
1014
|
+
switch (this.schemaTab) {
|
|
1015
|
+
case 'full':
|
|
1016
|
+
JSON.parse(this.schemaJson);
|
|
1017
|
+
break;
|
|
1018
|
+
case 'version':
|
|
1019
|
+
JSON.parse(this.versionJson);
|
|
1020
|
+
break;
|
|
1021
|
+
case 'tools':
|
|
1022
|
+
JSON.parse(this.toolsJson);
|
|
1023
|
+
break;
|
|
1024
|
+
case 'dynamic_tools':
|
|
1025
|
+
JSON.parse(this.dynamicToolsJson);
|
|
1026
|
+
break;
|
|
1027
|
+
case 'sub_agents':
|
|
1028
|
+
JSON.parse(this.subAgentToolsJson);
|
|
1029
|
+
break;
|
|
1030
|
+
case 'knowledge':
|
|
1031
|
+
JSON.parse(this.knowledgeJson);
|
|
1032
|
+
break;
|
|
1033
|
+
case 'rag':
|
|
1034
|
+
JSON.parse(this.ragConfigJson);
|
|
1035
|
+
break;
|
|
1036
|
+
}
|
|
1037
|
+
} catch (e) {
|
|
1038
|
+
this.schemaError = `Invalid JSON: ${e.message}`;
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
|
|
1042
|
+
async saveSchema() {
|
|
1043
|
+
if (this.schemaError || this.schemaTab === 'functions') return;
|
|
1044
|
+
|
|
1045
|
+
this.schemaLoading = true;
|
|
1046
|
+
this.schemaError = null;
|
|
1047
|
+
|
|
1048
|
+
try {
|
|
1049
|
+
// Build the update payload based on current tab
|
|
1050
|
+
let payload = {};
|
|
1051
|
+
|
|
1052
|
+
switch (this.schemaTab) {
|
|
1053
|
+
case 'full':
|
|
1054
|
+
payload = JSON.parse(this.schemaJson);
|
|
1055
|
+
break;
|
|
1056
|
+
case 'version':
|
|
1057
|
+
payload = { version: JSON.parse(this.versionJson) };
|
|
1058
|
+
break;
|
|
1059
|
+
case 'tools':
|
|
1060
|
+
payload = { tools: JSON.parse(this.toolsJson) };
|
|
1061
|
+
break;
|
|
1062
|
+
case 'dynamic_tools':
|
|
1063
|
+
payload = { dynamic_tools: JSON.parse(this.dynamicToolsJson) };
|
|
1064
|
+
break;
|
|
1065
|
+
case 'sub_agents':
|
|
1066
|
+
payload = { sub_agent_tools: JSON.parse(this.subAgentToolsJson) };
|
|
1067
|
+
break;
|
|
1068
|
+
case 'knowledge':
|
|
1069
|
+
payload = { knowledge: JSON.parse(this.knowledgeJson) };
|
|
1070
|
+
break;
|
|
1071
|
+
case 'rag':
|
|
1072
|
+
payload = { rag_config: JSON.parse(this.ragConfigJson) };
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
const response = await fetch(`/studio/api/agents/${this.selectedAgentId}/full-schema/`, {
|
|
1077
|
+
method: 'PUT',
|
|
1078
|
+
credentials: 'include',
|
|
1079
|
+
headers: {
|
|
1080
|
+
'Content-Type': 'application/json',
|
|
1081
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1082
|
+
},
|
|
1083
|
+
body: JSON.stringify(payload),
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
if (!response.ok) {
|
|
1087
|
+
const error = await response.json();
|
|
1088
|
+
throw new Error(error.detail || error.message || `Failed to save: ${response.status}`);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
this.schemaSaved = true;
|
|
1092
|
+
this.schemaModified = false;
|
|
1093
|
+
|
|
1094
|
+
// Reload to get the updated schema
|
|
1095
|
+
setTimeout(() => {
|
|
1096
|
+
this.reloadSchema();
|
|
1097
|
+
}, 1000);
|
|
1098
|
+
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
console.error('Error saving schema:', error);
|
|
1101
|
+
this.schemaError = error.message;
|
|
1102
|
+
} finally {
|
|
1103
|
+
this.schemaLoading = false;
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
|
|
1107
|
+
async copySchema() {
|
|
1108
|
+
let textToCopy = '';
|
|
1109
|
+
switch (this.schemaTab) {
|
|
1110
|
+
case 'full':
|
|
1111
|
+
textToCopy = this.schemaJson;
|
|
1112
|
+
break;
|
|
1113
|
+
case 'version':
|
|
1114
|
+
textToCopy = this.versionJson;
|
|
1115
|
+
break;
|
|
1116
|
+
case 'tools':
|
|
1117
|
+
textToCopy = this.toolsJson;
|
|
1118
|
+
break;
|
|
1119
|
+
case 'dynamic_tools':
|
|
1120
|
+
textToCopy = this.dynamicToolsJson;
|
|
1121
|
+
break;
|
|
1122
|
+
case 'sub_agents':
|
|
1123
|
+
textToCopy = this.subAgentToolsJson;
|
|
1124
|
+
break;
|
|
1125
|
+
case 'knowledge':
|
|
1126
|
+
textToCopy = this.knowledgeJson;
|
|
1127
|
+
break;
|
|
1128
|
+
case 'rag':
|
|
1129
|
+
textToCopy = this.ragConfigJson;
|
|
1130
|
+
break;
|
|
1131
|
+
case 'functions':
|
|
1132
|
+
textToCopy = this.functionsJson;
|
|
1133
|
+
break;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
try {
|
|
1137
|
+
await navigator.clipboard.writeText(textToCopy);
|
|
1138
|
+
// Could show a toast here
|
|
1139
|
+
} catch (e) {
|
|
1140
|
+
console.error('Failed to copy:', e);
|
|
1141
|
+
}
|
|
1142
|
+
},
|
|
1143
|
+
|
|
1144
|
+
// ==========================================================================
|
|
1145
|
+
// Delete Agent Methods
|
|
1146
|
+
// ==========================================================================
|
|
1147
|
+
|
|
1148
|
+
async deleteAgent() {
|
|
1149
|
+
if (!this.selectedAgentId) return;
|
|
1150
|
+
const agent = this.selectedAgent;
|
|
1151
|
+
if (!agent) return;
|
|
1152
|
+
|
|
1153
|
+
this.deleteConfirmMessage = `Are you sure you want to delete "${agent.name}"? This action cannot be undone.`;
|
|
1154
|
+
this.deleteConfirmCallback = async () => {
|
|
1155
|
+
try {
|
|
1156
|
+
const response = await fetch(`/studio/api/agents/${this.selectedAgentId}/`, {
|
|
1157
|
+
method: 'DELETE',
|
|
1158
|
+
credentials: 'include',
|
|
1159
|
+
headers: {
|
|
1160
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1161
|
+
},
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
if (response.ok || response.status === 204) {
|
|
1165
|
+
this.selectedAgentId = '';
|
|
1166
|
+
this.agentConfig.agentId = null;
|
|
1167
|
+
this.agentConfig.agentSlug = null;
|
|
1168
|
+
window.history.replaceState({}, '', '/studio/agents/new/');
|
|
1169
|
+
await this.loadAgents();
|
|
1170
|
+
this.refreshTestAgent();
|
|
1171
|
+
this.refreshBuilderChat();
|
|
1172
|
+
} else {
|
|
1173
|
+
const error = await response.json();
|
|
1174
|
+
alert(`Failed to delete agent: ${error.detail || JSON.stringify(error)}`);
|
|
1175
|
+
}
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
console.error('Error deleting agent:', error);
|
|
1178
|
+
alert('Failed to delete agent');
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
this.deleteConfirmOpen = true;
|
|
1182
|
+
},
|
|
1183
|
+
|
|
1184
|
+
// ==========================================================================
|
|
1185
|
+
// Systems Management Methods
|
|
1186
|
+
// ==========================================================================
|
|
1187
|
+
|
|
1188
|
+
async loadSystems() {
|
|
1189
|
+
this.systemsLoading = true;
|
|
1190
|
+
try {
|
|
1191
|
+
const response = await fetch('/studio/api/systems/', {
|
|
1192
|
+
credentials: 'include',
|
|
1193
|
+
headers: { 'Accept': 'application/json' },
|
|
1194
|
+
});
|
|
1195
|
+
if (response.ok) {
|
|
1196
|
+
const data = await response.json();
|
|
1197
|
+
this.systems = Array.isArray(data) ? data : (data.results || []);
|
|
1198
|
+
}
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
console.error('Error loading systems:', error);
|
|
1201
|
+
} finally {
|
|
1202
|
+
this.systemsLoading = false;
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
|
|
1206
|
+
onSystemChange() {
|
|
1207
|
+
// Could filter agents by system or show system-specific info
|
|
1208
|
+
console.log('Selected system:', this.selectedSystemId);
|
|
1209
|
+
},
|
|
1210
|
+
|
|
1211
|
+
openSystemsModal() {
|
|
1212
|
+
this.systemsModalOpen = true;
|
|
1213
|
+
this.loadSystems();
|
|
1214
|
+
},
|
|
1215
|
+
|
|
1216
|
+
closeSystemsModal() {
|
|
1217
|
+
this.systemsModalOpen = false;
|
|
1218
|
+
this.expandedSystemId = null;
|
|
1219
|
+
},
|
|
1220
|
+
|
|
1221
|
+
async createSystem() {
|
|
1222
|
+
if (!this.newSystemName || !this.newSystemSlug) return;
|
|
1223
|
+
|
|
1224
|
+
try {
|
|
1225
|
+
const payload = {
|
|
1226
|
+
name: this.newSystemName,
|
|
1227
|
+
slug: this.newSystemSlug,
|
|
1228
|
+
};
|
|
1229
|
+
if (this.newSystemEntryAgent) {
|
|
1230
|
+
payload.entry_agent = this.newSystemEntryAgent;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
const response = await fetch('/studio/api/systems/', {
|
|
1234
|
+
method: 'POST',
|
|
1235
|
+
credentials: 'include',
|
|
1236
|
+
headers: {
|
|
1237
|
+
'Content-Type': 'application/json',
|
|
1238
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1239
|
+
},
|
|
1240
|
+
body: JSON.stringify(payload),
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
if (response.ok) {
|
|
1244
|
+
this.newSystemName = '';
|
|
1245
|
+
this.newSystemSlug = '';
|
|
1246
|
+
this.newSystemEntryAgent = '';
|
|
1247
|
+
await this.loadSystems();
|
|
1248
|
+
} else {
|
|
1249
|
+
const error = await response.json();
|
|
1250
|
+
alert(`Failed to create system: ${error.detail || error.slug || JSON.stringify(error)}`);
|
|
1251
|
+
}
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
console.error('Error creating system:', error);
|
|
1254
|
+
alert('Failed to create system');
|
|
1255
|
+
}
|
|
1256
|
+
},
|
|
1257
|
+
|
|
1258
|
+
viewSystemDetails(system) {
|
|
1259
|
+
if (this.expandedSystemId === system.id) {
|
|
1260
|
+
this.expandedSystemId = null;
|
|
1261
|
+
} else {
|
|
1262
|
+
this.expandedSystemId = system.id;
|
|
1263
|
+
this.loadSystemMembers(system);
|
|
1264
|
+
}
|
|
1265
|
+
},
|
|
1266
|
+
|
|
1267
|
+
async loadSystemMembers(system) {
|
|
1268
|
+
try {
|
|
1269
|
+
const response = await fetch(`/studio/api/systems/${system.id}/members/`, {
|
|
1270
|
+
credentials: 'include',
|
|
1271
|
+
headers: { 'Accept': 'application/json' },
|
|
1272
|
+
});
|
|
1273
|
+
if (response.ok) {
|
|
1274
|
+
const data = await response.json();
|
|
1275
|
+
const members = Array.isArray(data) ? data : (data.results || []);
|
|
1276
|
+
// Update the system in our list with members
|
|
1277
|
+
const idx = this.systems.findIndex(s => s.id === system.id);
|
|
1278
|
+
if (idx !== -1) {
|
|
1279
|
+
this.systems[idx] = { ...this.systems[idx], members };
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
console.error('Error loading system members:', error);
|
|
1284
|
+
}
|
|
1285
|
+
},
|
|
1286
|
+
|
|
1287
|
+
async publishSystem(system) {
|
|
1288
|
+
try {
|
|
1289
|
+
const response = await fetch(`/studio/api/systems/${system.id}/publish/`, {
|
|
1290
|
+
method: 'POST',
|
|
1291
|
+
credentials: 'include',
|
|
1292
|
+
headers: {
|
|
1293
|
+
'Content-Type': 'application/json',
|
|
1294
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1295
|
+
},
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
if (response.ok) {
|
|
1299
|
+
alert('System version published successfully!');
|
|
1300
|
+
await this.loadSystems();
|
|
1301
|
+
} else {
|
|
1302
|
+
const error = await response.json();
|
|
1303
|
+
alert(`Failed to publish: ${error.detail || JSON.stringify(error)}`);
|
|
1304
|
+
}
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
console.error('Error publishing system:', error);
|
|
1307
|
+
alert('Failed to publish system');
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
|
|
1311
|
+
async deleteSystem(system) {
|
|
1312
|
+
this.deleteConfirmMessage = `Are you sure you want to delete system "${system.name}"? This action cannot be undone.`;
|
|
1313
|
+
this.deleteConfirmCallback = async () => {
|
|
1314
|
+
try {
|
|
1315
|
+
const response = await fetch(`/studio/api/systems/${system.id}/`, {
|
|
1316
|
+
method: 'DELETE',
|
|
1317
|
+
credentials: 'include',
|
|
1318
|
+
headers: {
|
|
1319
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1320
|
+
},
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
if (response.ok || response.status === 204) {
|
|
1324
|
+
if (this.selectedSystemId === system.id) {
|
|
1325
|
+
this.selectedSystemId = '';
|
|
1326
|
+
}
|
|
1327
|
+
await this.loadSystems();
|
|
1328
|
+
} else {
|
|
1329
|
+
const error = await response.json();
|
|
1330
|
+
alert(`Failed to delete system: ${error.detail || JSON.stringify(error)}`);
|
|
1331
|
+
}
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
console.error('Error deleting system:', error);
|
|
1334
|
+
alert('Failed to delete system');
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
this.deleteConfirmOpen = true;
|
|
1338
|
+
},
|
|
1339
|
+
|
|
1340
|
+
async addMemberToSystem(system) {
|
|
1341
|
+
// Simple prompt for now - could be a modal
|
|
1342
|
+
const agentId = prompt('Enter the agent ID to add:');
|
|
1343
|
+
if (!agentId) return;
|
|
1344
|
+
|
|
1345
|
+
try {
|
|
1346
|
+
const response = await fetch(`/studio/api/systems/${system.id}/members/`, {
|
|
1347
|
+
method: 'POST',
|
|
1348
|
+
credentials: 'include',
|
|
1349
|
+
headers: {
|
|
1350
|
+
'Content-Type': 'application/json',
|
|
1351
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1352
|
+
},
|
|
1353
|
+
body: JSON.stringify({ agent: agentId }),
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
if (response.ok) {
|
|
1357
|
+
await this.loadSystemMembers(system);
|
|
1358
|
+
} else {
|
|
1359
|
+
const error = await response.json();
|
|
1360
|
+
alert(`Failed to add member: ${error.detail || JSON.stringify(error)}`);
|
|
1361
|
+
}
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
console.error('Error adding member:', error);
|
|
1364
|
+
alert('Failed to add member');
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1367
|
+
|
|
1368
|
+
async removeMemberFromSystem(system, member) {
|
|
1369
|
+
try {
|
|
1370
|
+
const response = await fetch(`/studio/api/systems/${system.id}/members/${member.id}/`, {
|
|
1371
|
+
method: 'DELETE',
|
|
1372
|
+
credentials: 'include',
|
|
1373
|
+
headers: {
|
|
1374
|
+
'X-CSRFToken': this.getCsrfToken(),
|
|
1375
|
+
},
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
if (response.ok || response.status === 204) {
|
|
1379
|
+
await this.loadSystemMembers(system);
|
|
1380
|
+
} else {
|
|
1381
|
+
const error = await response.json();
|
|
1382
|
+
alert(`Failed to remove member: ${error.detail || JSON.stringify(error)}`);
|
|
1383
|
+
}
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
console.error('Error removing member:', error);
|
|
1386
|
+
alert('Failed to remove member');
|
|
1387
|
+
}
|
|
1388
|
+
},
|
|
1389
|
+
|
|
1390
|
+
// ==========================================================================
|
|
1391
|
+
// Delete Confirmation Methods
|
|
1392
|
+
// ==========================================================================
|
|
1393
|
+
|
|
1394
|
+
cancelDelete() {
|
|
1395
|
+
this.deleteConfirmOpen = false;
|
|
1396
|
+
this.deleteConfirmMessage = '';
|
|
1397
|
+
this.deleteConfirmCallback = null;
|
|
1398
|
+
},
|
|
1399
|
+
|
|
1400
|
+
async confirmDelete() {
|
|
1401
|
+
if (this.deleteConfirmCallback) {
|
|
1402
|
+
await this.deleteConfirmCallback();
|
|
1403
|
+
}
|
|
1404
|
+
this.cancelDelete();
|
|
1405
|
+
},
|
|
1406
|
+
|
|
1407
|
+
getCsrfToken() {
|
|
1408
|
+
const name = 'csrftoken';
|
|
1409
|
+
let cookieValue = null;
|
|
1410
|
+
if (document.cookie && document.cookie !== '') {
|
|
1411
|
+
const cookies = document.cookie.split(';');
|
|
1412
|
+
for (let i = 0; i < cookies.length; i++) {
|
|
1413
|
+
const cookie = cookies[i].trim();
|
|
1414
|
+
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
1415
|
+
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
1416
|
+
break;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return cookieValue;
|
|
1421
|
+
},
|
|
1422
|
+
},
|
|
1423
|
+
async mounted() {
|
|
1424
|
+
// Load agents and systems lists
|
|
1425
|
+
await Promise.all([
|
|
1426
|
+
this.loadAgents(),
|
|
1427
|
+
this.loadSystems(),
|
|
1428
|
+
]);
|
|
1429
|
+
|
|
1430
|
+
// Auto-select first agent if none selected and agents exist
|
|
1431
|
+
if (!this.selectedAgentId && this.agents.length > 0) {
|
|
1432
|
+
this.selectedAgentId = this.agents[0].id;
|
|
1433
|
+
await this.onAgentChange();
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// Initialize chat widgets with current selection
|
|
1437
|
+
this.refreshTestAgent();
|
|
1438
|
+
this.refreshBuilderChat();
|
|
1439
|
+
}
|
|
1440
|
+
}).use(primevue.config.default).mount('#app');
|
|
1441
|
+
</script>
|
|
1442
|
+
{% endblock %}
|
|
1443
|
+
|