django-agent-studio 0.2.9__py3-none-any.whl → 0.3.1__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/agents/dynamic.py +2 -3
- django_agent_studio/api/serializers.py +18 -1
- django_agent_studio/api/urls.py +10 -10
- django_agent_studio/api/views.py +22 -12
- django_agent_studio/templates/django_agent_studio/builder.html +2 -2
- django_agent_studio/templates/django_agent_studio/home.html +44 -6
- django_agent_studio/templates/django_agent_studio/system_list.html +92 -0
- django_agent_studio/templates/django_agent_studio/system_test.html +128 -0
- django_agent_studio/templates/django_agent_studio/test.html +2 -2
- django_agent_studio/urls.py +6 -2
- django_agent_studio/views.py +65 -2
- {django_agent_studio-0.2.9.dist-info → django_agent_studio-0.3.1.dist-info}/METADATA +3 -1
- {django_agent_studio-0.2.9.dist-info → django_agent_studio-0.3.1.dist-info}/RECORD +16 -14
- {django_agent_studio-0.2.9.dist-info → django_agent_studio-0.3.1.dist-info}/WHEEL +0 -0
- {django_agent_studio-0.2.9.dist-info → django_agent_studio-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {django_agent_studio-0.2.9.dist-info → django_agent_studio-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -266,9 +266,8 @@ class DynamicAgentRuntime(AgentRuntime):
|
|
|
266
266
|
**model_settings,
|
|
267
267
|
)
|
|
268
268
|
|
|
269
|
-
#
|
|
270
|
-
|
|
271
|
-
await ctx.emit(EventType.ASSISTANT_MESSAGE, {"content": result.final_content})
|
|
269
|
+
# Note: run_agentic_loop already emits ASSISTANT_MESSAGE events,
|
|
270
|
+
# so we don't emit here to avoid duplicate messages in the UI.
|
|
272
271
|
|
|
273
272
|
return RunResult(
|
|
274
273
|
final_output={"response": result.final_content},
|
|
@@ -570,12 +570,29 @@ class AgentSystemCreateSerializer(serializers.Serializer):
|
|
|
570
570
|
slug = serializers.SlugField(max_length=100)
|
|
571
571
|
name = serializers.CharField(max_length=255)
|
|
572
572
|
description = serializers.CharField(required=False, default="")
|
|
573
|
-
|
|
573
|
+
# Accept both entry_agent_id and entry_agent for flexibility
|
|
574
|
+
entry_agent_id = serializers.UUIDField(
|
|
575
|
+
required=False,
|
|
576
|
+
help_text="ID of the entry point agent",
|
|
577
|
+
)
|
|
578
|
+
entry_agent = serializers.UUIDField(
|
|
579
|
+
required=False,
|
|
580
|
+
help_text="ID of the entry point agent (alias for entry_agent_id)",
|
|
581
|
+
)
|
|
574
582
|
auto_discover = serializers.BooleanField(
|
|
575
583
|
default=True,
|
|
576
584
|
help_text="Automatically discover and add all reachable sub-agents",
|
|
577
585
|
)
|
|
578
586
|
|
|
587
|
+
def validate(self, data):
|
|
588
|
+
"""Normalize entry_agent to entry_agent_id."""
|
|
589
|
+
# Accept either entry_agent or entry_agent_id
|
|
590
|
+
if 'entry_agent' in data and data['entry_agent']:
|
|
591
|
+
data['entry_agent_id'] = data.pop('entry_agent')
|
|
592
|
+
elif 'entry_agent' in data:
|
|
593
|
+
data.pop('entry_agent')
|
|
594
|
+
return data
|
|
595
|
+
|
|
579
596
|
|
|
580
597
|
class AddMemberSerializer(serializers.Serializer):
|
|
581
598
|
"""Serializer for adding a member to a system."""
|
django_agent_studio/api/urls.py
CHANGED
|
@@ -8,8 +8,8 @@ from django_agent_studio.api import views
|
|
|
8
8
|
|
|
9
9
|
urlpatterns = [
|
|
10
10
|
# Agent definition CRUD
|
|
11
|
-
path("agents/", views.AgentDefinitionListCreateView.as_view(), name="
|
|
12
|
-
path("agents/<uuid:pk>/", views.AgentDefinitionDetailView.as_view(), name="
|
|
11
|
+
path("agents/", views.AgentDefinitionListCreateView.as_view(), name="api_agent_list"),
|
|
12
|
+
path("agents/<uuid:pk>/", views.AgentDefinitionDetailView.as_view(), name="api_agent_detail"),
|
|
13
13
|
|
|
14
14
|
# Agent versions
|
|
15
15
|
path(
|
|
@@ -194,46 +194,46 @@ urlpatterns = [
|
|
|
194
194
|
path(
|
|
195
195
|
"systems/",
|
|
196
196
|
views.AgentSystemListCreateView.as_view(),
|
|
197
|
-
name="
|
|
197
|
+
name="api_system_list",
|
|
198
198
|
),
|
|
199
199
|
path(
|
|
200
200
|
"systems/<uuid:pk>/",
|
|
201
201
|
views.AgentSystemDetailView.as_view(),
|
|
202
|
-
name="
|
|
202
|
+
name="api_system_detail",
|
|
203
203
|
),
|
|
204
204
|
|
|
205
205
|
# System members
|
|
206
206
|
path(
|
|
207
207
|
"systems/<uuid:system_id>/members/",
|
|
208
208
|
views.AgentSystemMemberListCreateView.as_view(),
|
|
209
|
-
name="
|
|
209
|
+
name="api_system_member_list",
|
|
210
210
|
),
|
|
211
211
|
path(
|
|
212
212
|
"systems/<uuid:system_id>/members/<uuid:pk>/",
|
|
213
213
|
views.AgentSystemMemberDetailView.as_view(),
|
|
214
|
-
name="
|
|
214
|
+
name="api_system_member_detail",
|
|
215
215
|
),
|
|
216
216
|
|
|
217
217
|
# System versions
|
|
218
218
|
path(
|
|
219
219
|
"systems/<uuid:system_id>/versions/",
|
|
220
220
|
views.AgentSystemVersionListView.as_view(),
|
|
221
|
-
name="
|
|
221
|
+
name="api_system_version_list",
|
|
222
222
|
),
|
|
223
223
|
path(
|
|
224
224
|
"systems/<uuid:system_id>/publish/",
|
|
225
225
|
views.AgentSystemPublishView.as_view(),
|
|
226
|
-
name="
|
|
226
|
+
name="api_system_publish",
|
|
227
227
|
),
|
|
228
228
|
path(
|
|
229
229
|
"systems/<uuid:system_id>/versions/<uuid:version_id>/deploy/",
|
|
230
230
|
views.AgentSystemDeployView.as_view(),
|
|
231
|
-
name="
|
|
231
|
+
name="api_system_deploy",
|
|
232
232
|
),
|
|
233
233
|
path(
|
|
234
234
|
"systems/<uuid:system_id>/versions/<uuid:version_id>/export/",
|
|
235
235
|
views.AgentSystemExportView.as_view(),
|
|
236
|
-
name="
|
|
236
|
+
name="api_system_export",
|
|
237
237
|
),
|
|
238
238
|
|
|
239
239
|
# Discover agents from entry point
|
django_agent_studio/api/views.py
CHANGED
|
@@ -1434,20 +1434,30 @@ class AgentSystemListCreateView(generics.ListCreateAPIView):
|
|
|
1434
1434
|
serializer = self.get_serializer(data=request.data)
|
|
1435
1435
|
serializer.is_valid(raise_exception=True)
|
|
1436
1436
|
|
|
1437
|
-
|
|
1438
|
-
entry_agent = get_agent_for_user(request.user, serializer.validated_data['entry_agent_id'])
|
|
1437
|
+
entry_agent_id = serializer.validated_data.get('entry_agent_id')
|
|
1439
1438
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1439
|
+
if entry_agent_id:
|
|
1440
|
+
# Get the entry agent and use the service to create with auto-discovery
|
|
1441
|
+
entry_agent = get_agent_for_user(request.user, entry_agent_id)
|
|
1442
1442
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1443
|
+
from django_agent_runtime.services.multi_agent import create_system_from_entry_agent
|
|
1444
|
+
|
|
1445
|
+
system = create_system_from_entry_agent(
|
|
1446
|
+
slug=serializer.validated_data['slug'],
|
|
1447
|
+
name=serializer.validated_data['name'],
|
|
1448
|
+
entry_agent=entry_agent,
|
|
1449
|
+
description=serializer.validated_data.get('description', ''),
|
|
1450
|
+
owner=request.user,
|
|
1451
|
+
auto_discover=serializer.validated_data.get('auto_discover', True),
|
|
1452
|
+
)
|
|
1453
|
+
else:
|
|
1454
|
+
# Create system without entry agent - can be set later
|
|
1455
|
+
system = AgentSystem.objects.create(
|
|
1456
|
+
slug=serializer.validated_data['slug'],
|
|
1457
|
+
name=serializer.validated_data['name'],
|
|
1458
|
+
description=serializer.validated_data.get('description', ''),
|
|
1459
|
+
owner=request.user,
|
|
1460
|
+
)
|
|
1451
1461
|
|
|
1452
1462
|
return Response(
|
|
1453
1463
|
AgentSystemDetailSerializer(system).data,
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
<script>
|
|
26
26
|
// Configuration passed from Django
|
|
27
27
|
window.STUDIO_CONFIG = {
|
|
28
|
-
agentId: {% if agent %}"{{ agent.id }}"{% else %}null{% endif %},
|
|
28
|
+
agentId: {% if agent %}"{{ agent.id|escapejs }}"{% else %}null{% endif %},
|
|
29
29
|
csrfToken: "{{ csrf_token }}",
|
|
30
|
-
builderAgentKey: "{{ builder_agent_key }}",
|
|
30
|
+
builderAgentKey: "{{ builder_agent_key|escapejs }}",
|
|
31
31
|
};
|
|
32
32
|
</script>
|
|
33
33
|
|
|
@@ -12,8 +12,17 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
<!-- Quick Actions -->
|
|
15
|
-
<div class="grid grid-cols-1 md:grid-cols-
|
|
16
|
-
<a href="{% url 'agent_studio:
|
|
15
|
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
|
16
|
+
<a href="{% url 'agent_studio:system_list' %}"
|
|
17
|
+
class="bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg p-6 transition-colors">
|
|
18
|
+
<div class="flex items-center space-x-3 mb-2">
|
|
19
|
+
<span class="text-2xl">🔗</span>
|
|
20
|
+
<span class="text-lg font-semibold">My Systems</span>
|
|
21
|
+
</div>
|
|
22
|
+
<p class="text-indigo-100 text-sm">Multi-agent systems that work together</p>
|
|
23
|
+
</a>
|
|
24
|
+
|
|
25
|
+
<a href="{% url 'agent_studio:agent_create' %}"
|
|
17
26
|
class="bg-primary-600 hover:bg-primary-700 text-white rounded-lg p-6 transition-colors">
|
|
18
27
|
<div class="flex items-center space-x-3 mb-2">
|
|
19
28
|
<span class="text-2xl">✨</span>
|
|
@@ -21,8 +30,8 @@
|
|
|
21
30
|
</div>
|
|
22
31
|
<p class="text-primary-100 text-sm">Start building a custom AI agent from scratch</p>
|
|
23
32
|
</a>
|
|
24
|
-
|
|
25
|
-
<a href="{% url 'agent_studio:agent_list' %}"
|
|
33
|
+
|
|
34
|
+
<a href="{% url 'agent_studio:agent_list' %}"
|
|
26
35
|
class="bg-white hover:bg-gray-50 border border-gray-200 rounded-lg p-6 transition-colors">
|
|
27
36
|
<div class="flex items-center space-x-3 mb-2">
|
|
28
37
|
<span class="text-2xl">📋</span>
|
|
@@ -30,7 +39,7 @@
|
|
|
30
39
|
</div>
|
|
31
40
|
<p class="text-gray-600 text-sm">View and manage your existing agents</p>
|
|
32
41
|
</a>
|
|
33
|
-
|
|
42
|
+
|
|
34
43
|
<div class="bg-white hover:bg-gray-50 border border-gray-200 rounded-lg p-6 transition-colors cursor-pointer">
|
|
35
44
|
<div class="flex items-center space-x-3 mb-2">
|
|
36
45
|
<span class="text-2xl">📚</span>
|
|
@@ -40,13 +49,42 @@
|
|
|
40
49
|
</div>
|
|
41
50
|
</div>
|
|
42
51
|
|
|
52
|
+
<!-- Recent Systems -->
|
|
53
|
+
<div class="mb-8">
|
|
54
|
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Recent Systems</h2>
|
|
55
|
+
{% if recent_systems %}
|
|
56
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
57
|
+
{% for system in recent_systems %}
|
|
58
|
+
<a href="{% url 'agent_studio:system_test' system.id %}"
|
|
59
|
+
class="bg-white border border-gray-200 rounded-lg p-4 hover:border-indigo-300 hover:shadow-sm transition-all">
|
|
60
|
+
<div class="flex items-center space-x-3 mb-2">
|
|
61
|
+
<span class="text-xl">🔗</span>
|
|
62
|
+
<span class="font-medium text-gray-800">{{ system.name }}</span>
|
|
63
|
+
</div>
|
|
64
|
+
<p class="text-sm text-gray-500 line-clamp-2">{{ system.description|default:"No description" }}</p>
|
|
65
|
+
<div class="mt-2 flex items-center space-x-2 text-xs text-gray-400">
|
|
66
|
+
<span>Entry: {{ system.entry_agent.icon|default:"🤖" }} {{ system.entry_agent.name }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="mt-2 text-xs text-gray-400">
|
|
69
|
+
Updated {{ system.updated_at|timesince }} ago
|
|
70
|
+
</div>
|
|
71
|
+
</a>
|
|
72
|
+
{% endfor %}
|
|
73
|
+
</div>
|
|
74
|
+
{% else %}
|
|
75
|
+
<div class="bg-gray-50 border border-gray-200 rounded-lg p-6 text-center">
|
|
76
|
+
<p class="text-gray-500 text-sm">No systems yet. Systems combine multiple agents to work together.</p>
|
|
77
|
+
</div>
|
|
78
|
+
{% endif %}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
43
81
|
<!-- Recent Agents -->
|
|
44
82
|
{% if recent_agents %}
|
|
45
83
|
<div class="mb-8">
|
|
46
84
|
<h2 class="text-xl font-semibold text-gray-800 mb-4">Recent Agents</h2>
|
|
47
85
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
48
86
|
{% for agent in recent_agents %}
|
|
49
|
-
<a href="{% url 'agent_studio:agent_edit' agent.id %}"
|
|
87
|
+
<a href="{% url 'agent_studio:agent_edit' agent.id %}"
|
|
50
88
|
class="bg-white border border-gray-200 rounded-lg p-4 hover:border-primary-300 hover:shadow-sm transition-all">
|
|
51
89
|
<div class="flex items-center space-x-3 mb-2">
|
|
52
90
|
<span class="text-xl">{{ agent.icon|default:"🤖" }}</span>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{% extends "django_agent_studio/base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}My Systems - 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
|
+
<span class="text-gray-600">My Systems</span>
|
|
9
|
+
</nav>
|
|
10
|
+
{% endblock %}
|
|
11
|
+
|
|
12
|
+
{% block content %}
|
|
13
|
+
<div class="h-full p-6 overflow-auto">
|
|
14
|
+
<div class="max-w-6xl mx-auto">
|
|
15
|
+
<!-- Header -->
|
|
16
|
+
<div class="flex items-center justify-between mb-6">
|
|
17
|
+
<h1 class="text-2xl font-bold text-gray-900">My Systems</h1>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
{% if systems %}
|
|
21
|
+
<!-- System Grid -->
|
|
22
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
23
|
+
{% for system in systems %}
|
|
24
|
+
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden hover:border-primary-300 hover:shadow-md transition-all">
|
|
25
|
+
<div class="p-4">
|
|
26
|
+
<div class="flex items-center justify-between mb-3">
|
|
27
|
+
<div class="flex items-center space-x-3">
|
|
28
|
+
<span class="text-2xl">🔗</span>
|
|
29
|
+
<div>
|
|
30
|
+
<h3 class="font-semibold text-gray-800">{{ system.name }}</h3>
|
|
31
|
+
<span class="text-xs text-gray-400">{{ system.slug }}</span>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="flex items-center space-x-1">
|
|
35
|
+
{% if system.is_active %}
|
|
36
|
+
<span class="w-2 h-2 bg-green-500 rounded-full" title="Active"></span>
|
|
37
|
+
{% else %}
|
|
38
|
+
<span class="w-2 h-2 bg-gray-300 rounded-full" title="Inactive"></span>
|
|
39
|
+
{% endif %}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<p class="text-sm text-gray-500 line-clamp-2 mb-3">{{ system.description|default:"No description" }}</p>
|
|
43
|
+
|
|
44
|
+
<!-- Entry Agent Info -->
|
|
45
|
+
<div class="bg-gray-50 rounded-lg p-2 mb-3">
|
|
46
|
+
<div class="flex items-center space-x-2 text-sm">
|
|
47
|
+
<span class="text-gray-400">Entry:</span>
|
|
48
|
+
<span class="text-lg">{{ system.entry_agent.icon|default:"🤖" }}</span>
|
|
49
|
+
<span class="text-gray-700 font-medium">{{ system.entry_agent.name }}</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<!-- Member Count -->
|
|
54
|
+
<div class="flex items-center justify-between text-xs text-gray-400">
|
|
55
|
+
<span>{{ system.members.count }} agent{{ system.members.count|pluralize }}</span>
|
|
56
|
+
<span>Updated {{ system.updated_at|timesince }} ago</span>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="border-t border-gray-100 px-4 py-3 bg-gray-50 flex items-center justify-end">
|
|
60
|
+
<a href="{% url 'agent_studio:system_test' system.id %}"
|
|
61
|
+
class="text-primary-600 hover:text-primary-700 text-sm font-medium">
|
|
62
|
+
Test System →
|
|
63
|
+
</a>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
{% endfor %}
|
|
67
|
+
</div>
|
|
68
|
+
{% else %}
|
|
69
|
+
<!-- Empty State -->
|
|
70
|
+
<div class="text-center py-12">
|
|
71
|
+
<div class="text-6xl mb-4">🔗</div>
|
|
72
|
+
<h3 class="text-xl font-semibold text-gray-800 mb-2">No systems yet</h3>
|
|
73
|
+
<p class="text-gray-500 mb-6">Systems are multi-agent configurations that work together.</p>
|
|
74
|
+
<p class="text-gray-400 text-sm">Create agents first, then combine them into a system.</p>
|
|
75
|
+
</div>
|
|
76
|
+
{% endif %}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
{% endblock %}
|
|
80
|
+
|
|
81
|
+
{% block extra_js %}
|
|
82
|
+
<script>
|
|
83
|
+
const { createApp } = Vue;
|
|
84
|
+
|
|
85
|
+
createApp({
|
|
86
|
+
data() {
|
|
87
|
+
return {}
|
|
88
|
+
}
|
|
89
|
+
}).use(primevue.config.default).mount('#app');
|
|
90
|
+
</script>
|
|
91
|
+
{% endblock %}
|
|
92
|
+
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{% extends "django_agent_studio/base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}Test {{ system.name }} - 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:system_list' %}" class="text-gray-500 hover:text-gray-700">My Systems</a>
|
|
9
|
+
<span class="text-gray-400">/</span>
|
|
10
|
+
<span class="text-gray-600">{{ system.name }}</span>
|
|
11
|
+
<span class="text-gray-400">/</span>
|
|
12
|
+
<span class="text-gray-600">Test</span>
|
|
13
|
+
</nav>
|
|
14
|
+
{% endblock %}
|
|
15
|
+
|
|
16
|
+
{% block extra_head %}
|
|
17
|
+
<style>
|
|
18
|
+
#fullscreen-chat-container {
|
|
19
|
+
display: flex;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#fullscreen-chat-container .cw-container {
|
|
24
|
+
position: relative !important;
|
|
25
|
+
width: 100% !important;
|
|
26
|
+
height: 100% !important;
|
|
27
|
+
max-height: none !important;
|
|
28
|
+
border-radius: 0 !important;
|
|
29
|
+
box-shadow: 0 0 20px rgba(0,0,0,0.1) !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#fullscreen-chat-container .cw-toggle-btn {
|
|
33
|
+
display: none !important;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#fullscreen-chat-container .cw-widget {
|
|
37
|
+
position: relative !important;
|
|
38
|
+
width: 100% !important;
|
|
39
|
+
height: 100% !important;
|
|
40
|
+
max-height: none !important;
|
|
41
|
+
border-radius: 0 !important;
|
|
42
|
+
display: flex !important;
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
45
|
+
{% endblock %}
|
|
46
|
+
|
|
47
|
+
{% block content %}
|
|
48
|
+
<div class="h-full flex flex-col">
|
|
49
|
+
<!-- System Info Bar -->
|
|
50
|
+
<div class="px-4 py-3 bg-white border-b border-gray-200 flex items-center justify-between">
|
|
51
|
+
<div class="flex items-center space-x-4">
|
|
52
|
+
<span class="text-2xl">🔗</span>
|
|
53
|
+
<div>
|
|
54
|
+
<h1 class="font-semibold text-gray-800">{{ system.name }}</h1>
|
|
55
|
+
<p class="text-sm text-gray-500">
|
|
56
|
+
Entry: {{ agent.icon|default:"🤖" }} {{ agent.name }}
|
|
57
|
+
<span class="text-gray-400 mx-2">•</span>
|
|
58
|
+
{{ system.description|default:"No description"|truncatechars:80 }}
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="flex items-center space-x-3">
|
|
63
|
+
<a href="{% url 'agent_studio:system_list' %}"
|
|
64
|
+
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
65
|
+
← Back to Systems
|
|
66
|
+
</a>
|
|
67
|
+
<button @click="clearChat"
|
|
68
|
+
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
69
|
+
Clear Chat
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- Full Screen Chat -->
|
|
75
|
+
<div class="flex-1 relative bg-gray-100 flex justify-center">
|
|
76
|
+
<div id="fullscreen-chat-container" class="relative w-full max-w-[800px] h-full"></div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
{% endblock %}
|
|
80
|
+
|
|
81
|
+
{% block extra_js %}
|
|
82
|
+
<script>
|
|
83
|
+
const { createApp } = Vue;
|
|
84
|
+
|
|
85
|
+
// Use the entry agent's slug for the chat
|
|
86
|
+
const agentSlug = "{{ agent.slug|escapejs }}";
|
|
87
|
+
const backendUrl = window.location.origin;
|
|
88
|
+
|
|
89
|
+
function initChat() {
|
|
90
|
+
if (window.testChatWidget) {
|
|
91
|
+
window.testChatWidget.destroy();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
window.testChatWidget = ChatWidget.createInstance({
|
|
95
|
+
containerId: 'fullscreen-chat-container',
|
|
96
|
+
backendUrl: backendUrl,
|
|
97
|
+
agentKey: agentSlug,
|
|
98
|
+
title: '{{ system.name|escapejs }}',
|
|
99
|
+
primaryColor: '#3b82f6',
|
|
100
|
+
showClearButton: true,
|
|
101
|
+
showDebugButton: true,
|
|
102
|
+
showExpandButton: false,
|
|
103
|
+
embedded: true,
|
|
104
|
+
apiPaths: {
|
|
105
|
+
runs: '/api/agent-runtime/runs/',
|
|
106
|
+
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
createApp({
|
|
112
|
+
data() {
|
|
113
|
+
return {}
|
|
114
|
+
},
|
|
115
|
+
methods: {
|
|
116
|
+
clearChat() {
|
|
117
|
+
if (window.testChatWidget) {
|
|
118
|
+
window.testChatWidget.clearMessages();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
mounted() {
|
|
123
|
+
initChat();
|
|
124
|
+
}
|
|
125
|
+
}).use(primevue.config.default).mount('#app');
|
|
126
|
+
</script>
|
|
127
|
+
{% endblock %}
|
|
128
|
+
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
<script>
|
|
82
82
|
const { createApp } = Vue;
|
|
83
83
|
|
|
84
|
-
const agentSlug = "{{ agent.slug }}";
|
|
84
|
+
const agentSlug = "{{ agent.slug|escapejs }}";
|
|
85
85
|
const backendUrl = window.location.origin;
|
|
86
86
|
|
|
87
87
|
function initChat() {
|
|
@@ -93,7 +93,7 @@ function initChat() {
|
|
|
93
93
|
containerId: 'fullscreen-chat-container',
|
|
94
94
|
backendUrl: backendUrl,
|
|
95
95
|
agentKey: agentSlug,
|
|
96
|
-
title: '{{ agent.name }}',
|
|
96
|
+
title: '{{ agent.name|escapejs }}',
|
|
97
97
|
primaryColor: '#3b82f6',
|
|
98
98
|
showClearButton: true,
|
|
99
99
|
showDebugButton: true,
|
django_agent_studio/urls.py
CHANGED
|
@@ -12,13 +12,17 @@ app_name = "agent_studio"
|
|
|
12
12
|
urlpatterns = [
|
|
13
13
|
# Main studio interface
|
|
14
14
|
path("", views.StudioHomeView.as_view(), name="home"),
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
# Systems (multi-agent systems)
|
|
17
|
+
path("systems/", views.SystemListView.as_view(), name="system_list"),
|
|
18
|
+
path("systems/<uuid:system_id>/test/", views.SystemTestView.as_view(), name="system_test"),
|
|
19
|
+
|
|
16
20
|
# Agent builder/editor
|
|
17
21
|
path("agents/", views.AgentListView.as_view(), name="agent_list"),
|
|
18
22
|
path("agents/new/", views.AgentBuilderView.as_view(), name="agent_create"),
|
|
19
23
|
path("agents/<uuid:agent_id>/", views.AgentBuilderView.as_view(), name="agent_edit"),
|
|
20
24
|
path("agents/<uuid:agent_id>/test/", views.AgentTestView.as_view(), name="agent_test"),
|
|
21
|
-
|
|
25
|
+
|
|
22
26
|
# API endpoints
|
|
23
27
|
path("api/", include(api_urls)),
|
|
24
28
|
]
|
django_agent_studio/views.py
CHANGED
|
@@ -6,7 +6,7 @@ from django.db.models import Q
|
|
|
6
6
|
from django.views.generic import TemplateView, ListView
|
|
7
7
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
8
8
|
|
|
9
|
-
from django_agent_runtime.models import AgentDefinition
|
|
9
|
+
from django_agent_runtime.models import AgentDefinition, AgentSystem
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class AgentAccessMixin:
|
|
@@ -35,13 +35,46 @@ class AgentAccessMixin:
|
|
|
35
35
|
return AgentDefinition.objects.get(id=agent_id, owner=self.request.user)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class
|
|
38
|
+
class SystemAccessMixin:
|
|
39
|
+
"""
|
|
40
|
+
Mixin that provides consistent system access logic.
|
|
41
|
+
|
|
42
|
+
- Superusers can access all systems
|
|
43
|
+
- Regular users can access their own systems
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def get_user_systems_queryset(self):
|
|
47
|
+
"""Get queryset of systems accessible to the current user."""
|
|
48
|
+
if self.request.user.is_superuser:
|
|
49
|
+
return AgentSystem.objects.all()
|
|
50
|
+
return AgentSystem.objects.filter(owner=self.request.user)
|
|
51
|
+
|
|
52
|
+
def get_system_for_user(self, system_id):
|
|
53
|
+
"""
|
|
54
|
+
Get a specific system if the user has access.
|
|
55
|
+
|
|
56
|
+
- Superusers can access any system
|
|
57
|
+
- Regular users can access their own systems
|
|
58
|
+
"""
|
|
59
|
+
if self.request.user.is_superuser:
|
|
60
|
+
return AgentSystem.objects.select_related('entry_agent').get(id=system_id)
|
|
61
|
+
return AgentSystem.objects.select_related('entry_agent').get(
|
|
62
|
+
id=system_id, owner=self.request.user
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class StudioHomeView(LoginRequiredMixin, AgentAccessMixin, SystemAccessMixin, TemplateView):
|
|
39
67
|
"""Home page for the agent studio."""
|
|
40
68
|
|
|
41
69
|
template_name = "django_agent_studio/home.html"
|
|
42
70
|
|
|
43
71
|
def get_context_data(self, **kwargs):
|
|
44
72
|
context = super().get_context_data(**kwargs)
|
|
73
|
+
# Systems (shown above agents)
|
|
74
|
+
context["recent_systems"] = self.get_user_systems_queryset().select_related(
|
|
75
|
+
'entry_agent'
|
|
76
|
+
).order_by("-updated_at")[:5]
|
|
77
|
+
# Agents
|
|
45
78
|
context["recent_agents"] = self.get_user_agents_queryset().order_by("-updated_at")[:5]
|
|
46
79
|
context["template_agents"] = AgentDefinition.objects.filter(
|
|
47
80
|
is_template=True,
|
|
@@ -98,3 +131,33 @@ class AgentTestView(LoginRequiredMixin, AgentAccessMixin, TemplateView):
|
|
|
98
131
|
context["agent"] = self.get_agent_for_user(agent_id)
|
|
99
132
|
return context
|
|
100
133
|
|
|
134
|
+
|
|
135
|
+
class SystemListView(LoginRequiredMixin, SystemAccessMixin, ListView):
|
|
136
|
+
"""List all systems for the current user."""
|
|
137
|
+
|
|
138
|
+
template_name = "django_agent_studio/system_list.html"
|
|
139
|
+
context_object_name = "systems"
|
|
140
|
+
|
|
141
|
+
def get_queryset(self):
|
|
142
|
+
return self.get_user_systems_queryset().select_related(
|
|
143
|
+
'entry_agent'
|
|
144
|
+
).prefetch_related('members__agent').order_by("-updated_at")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SystemTestView(LoginRequiredMixin, SystemAccessMixin, TemplateView):
|
|
148
|
+
"""
|
|
149
|
+
Full-screen system testing interface.
|
|
150
|
+
|
|
151
|
+
Uses the system's entry_agent as the starting point for conversations.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
template_name = "django_agent_studio/system_test.html"
|
|
155
|
+
|
|
156
|
+
def get_context_data(self, **kwargs):
|
|
157
|
+
context = super().get_context_data(**kwargs)
|
|
158
|
+
system_id = kwargs.get("system_id")
|
|
159
|
+
system = self.get_system_for_user(system_id)
|
|
160
|
+
context["system"] = system
|
|
161
|
+
context["agent"] = system.entry_agent # The entry point agent
|
|
162
|
+
return context
|
|
163
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-agent-studio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Visual agent builder and management studio for Django - build custom GPTs with a two-pane interface
|
|
5
5
|
Author: Chris Barry
|
|
6
6
|
License: Business Source License 1.1
|
|
@@ -126,6 +126,8 @@ A visual agent builder and management interface for Django applications. Create,
|
|
|
126
126
|
|
|
127
127
|
| Version | Date | Changes |
|
|
128
128
|
|---------|------|---------|
|
|
129
|
+
| **0.3.1** | 2026-01-30 | **Bug Fixes** - Fixed duplicate message emit in dynamic agents, fixed escapejs encoding in templates, added 800px max-width constraint to system test view |
|
|
130
|
+
| **0.3.0** | 2026-01-29 | **System Listing & Navigation** - Browse and test multi-agent systems from homepage, fixed URL routing for My Systems/My Agents links, system creation now supports optional entry agent |
|
|
129
131
|
| **0.2.0** | 2026-01-28 | **Multi-Agent Systems** - System management UI, shared memory configuration, memory privacy controls, builder tools for managing agent systems |
|
|
130
132
|
| **0.1.9** | 2026-01-27 | Spec documents migration, improved builder agent |
|
|
131
133
|
| **0.1.0** | 2026-01-25 | Initial release with builder interface, dynamic tools, knowledge management |
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
django_agent_studio/__init__.py,sha256=E2vGoil7ZmwTebaB5ap9Lt0ptAzqYB5xJpyi4bFbqL8,378
|
|
2
2
|
django_agent_studio/apps.py,sha256=L89QWn4XOvPBs2z6qHAhaE4uZQpasJntYD75aSd2p_k,607
|
|
3
|
-
django_agent_studio/urls.py,sha256=
|
|
4
|
-
django_agent_studio/views.py,sha256=
|
|
3
|
+
django_agent_studio/urls.py,sha256=4Lge5RtvBNZlpt4UWvKvJgQdgX2_6jRyCrfy8GzIjnY,942
|
|
4
|
+
django_agent_studio/views.py,sha256=K1fBnGgplJneNBZxQm5zfVz0NLC_OkaJ4jqWT3NqSPI,5327
|
|
5
5
|
django_agent_studio/agents/__init__.py,sha256=VYL_ato0DtggIo4BGRkyiz9cm1ARPXhhTQFzoG__NVM,800
|
|
6
6
|
django_agent_studio/agents/builder.py,sha256=dRPQk5-AhUsTBOFxv8H4Uk2X2PfK631jjVJZ1w13GtM,146777
|
|
7
|
-
django_agent_studio/agents/dynamic.py,sha256=
|
|
7
|
+
django_agent_studio/agents/dynamic.py,sha256=b2bwvwF9DOjEds4c0748mjKzCWbymcSlR4B9nP9Bk4Q,25367
|
|
8
8
|
django_agent_studio/api/__init__.py,sha256=vtBwuvBENyFFhFqCWyFsI6cYu4N9ZGqSMmHIRhr9a_U,45
|
|
9
9
|
django_agent_studio/api/permissions.py,sha256=MutmA8TxZb4ZwGfeEoolK-QI04Gbcxs7DPNzkXe_Bss,5302
|
|
10
|
-
django_agent_studio/api/serializers.py,sha256=
|
|
11
|
-
django_agent_studio/api/urls.py,sha256=
|
|
12
|
-
django_agent_studio/api/views.py,sha256=
|
|
10
|
+
django_agent_studio/api/serializers.py,sha256=0JJ7J-Rx_iDiZRZF8ja_kiUyRDcpDhRp-O-i1ifw9Zk,18870
|
|
11
|
+
django_agent_studio/api/urls.py,sha256=KYdkpyYCvxRN0nfUs1188apGYMpJT5FcG8kxwYhRAIE,8800
|
|
12
|
+
django_agent_studio/api/views.py,sha256=BqqxgPjnZUwYU6IVcagohg1mN4npinxPy4aBUkSsL0E,75071
|
|
13
13
|
django_agent_studio/management/__init__.py,sha256=6O9RHMN_xMDrEzKWytpai6QqgTXZVqwRg4h9mIQWtA8,52
|
|
14
14
|
django_agent_studio/management/commands/__init__.py,sha256=6O9RHMN_xMDrEzKWytpai6QqgTXZVqwRg4h9mIQWtA8,52
|
|
15
15
|
django_agent_studio/migrations/0001_initial.py,sha256=ThIhf1X6ZaACQd-5GjJG-MDjIm6ETl0Nc2hcYK0L2Wg,4292
|
|
@@ -26,11 +26,13 @@ django_agent_studio/static/django_agent_studio/js/builder.js.map,sha256=sVkeGyZs
|
|
|
26
26
|
django_agent_studio/static/django_agent_studio/js/style.css,sha256=rigQJy_oHIhgb1_7DwAQ_8G002gQnT0AXUtwxImWj1o,1626515
|
|
27
27
|
django_agent_studio/templates/django_agent_studio/agent_list.html,sha256=Qw2Jz39qrde69mI4EjhfWyc5elyKcpFYhicrLqOJZzU,4521
|
|
28
28
|
django_agent_studio/templates/django_agent_studio/base.html,sha256=wSgwaqK1DAIIyYeZWsVvPlpAZyZHBEx3_NGtdRo0J3A,6019
|
|
29
|
-
django_agent_studio/templates/django_agent_studio/builder.html,sha256=
|
|
30
|
-
django_agent_studio/templates/django_agent_studio/home.html,sha256=
|
|
31
|
-
django_agent_studio/templates/django_agent_studio/
|
|
32
|
-
django_agent_studio
|
|
33
|
-
django_agent_studio
|
|
34
|
-
django_agent_studio-0.
|
|
35
|
-
django_agent_studio-0.
|
|
36
|
-
django_agent_studio-0.
|
|
29
|
+
django_agent_studio/templates/django_agent_studio/builder.html,sha256=yPzb0Pdp0d7Sp7gVkJd7OouDq-toKCA8i7OJLU9x6Z8,1542
|
|
30
|
+
django_agent_studio/templates/django_agent_studio/home.html,sha256=2YQXJww0JzSRakVt2uMKeSOcAXsF7QT3ef0U14pa-50,6470
|
|
31
|
+
django_agent_studio/templates/django_agent_studio/system_list.html,sha256=IRWVQHsqtolfSDDPpOSKQNd6a1u9U9iQvTzFJWbm8DE,4026
|
|
32
|
+
django_agent_studio/templates/django_agent_studio/system_test.html,sha256=kQAgUTMqAXjYFIcelxq34J2YXmFANWmd4Bz0UoU3fPs,3945
|
|
33
|
+
django_agent_studio/templates/django_agent_studio/test.html,sha256=C4qxjrG8USjO_NBpVnyaDibsxJ9A6kwUHEAKYXN7Jh0,3797
|
|
34
|
+
django_agent_studio-0.3.1.dist-info/licenses/LICENSE,sha256=WIh21lpD7d7xCUtLysKK-kbfW4SG7GNPf_k7_Xm_sZg,3851
|
|
35
|
+
django_agent_studio-0.3.1.dist-info/METADATA,sha256=p7UkoDYVrWfniMIG9O23Ds_GmzWlz_a5fXDkd6mjYF8,16670
|
|
36
|
+
django_agent_studio-0.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
37
|
+
django_agent_studio-0.3.1.dist-info/top_level.txt,sha256=O1kqZzXPOsJlqnPSAcB2fH5WpJNY8ZNfHEJzX9_SZ0A,20
|
|
38
|
+
django_agent_studio-0.3.1.dist-info/RECORD,,
|
|
File without changes
|
{django_agent_studio-0.2.9.dist-info → django_agent_studio-0.3.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|