django-agent-studio 0.3.1__py3-none-any.whl → 0.3.3__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/api/serializers.py +139 -0
- django_agent_studio/api/urls.py +35 -0
- django_agent_studio/api/views.py +470 -8
- django_agent_studio/static/agent-frontend/chat-widget.css +6 -1
- django_agent_studio/templates/django_agent_studio/agent_list.html +12 -5
- django_agent_studio/templates/django_agent_studio/base.html +12 -0
- django_agent_studio/templates/django_agent_studio/collaborators.html +418 -0
- django_agent_studio/templates/django_agent_studio/home.html +21 -12
- django_agent_studio/templates/django_agent_studio/system_create.html +148 -0
- django_agent_studio/templates/django_agent_studio/system_list.html +15 -3
- django_agent_studio/templates/django_agent_studio/system_test.html +19 -6
- django_agent_studio/templates/django_agent_studio/test.html +18 -5
- django_agent_studio/urls.py +4 -0
- django_agent_studio/views.py +317 -15
- {django_agent_studio-0.3.1.dist-info → django_agent_studio-0.3.3.dist-info}/METADATA +3 -1
- {django_agent_studio-0.3.1.dist-info → django_agent_studio-0.3.3.dist-info}/RECORD +19 -17
- {django_agent_studio-0.3.1.dist-info → django_agent_studio-0.3.3.dist-info}/WHEEL +0 -0
- {django_agent_studio-0.3.1.dist-info → django_agent_studio-0.3.3.dist-info}/licenses/LICENSE +0 -0
- {django_agent_studio-0.3.1.dist-info → django_agent_studio-0.3.3.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
display: flex;
|
|
20
20
|
justify-content: center;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
#fullscreen-chat-container .cw-container {
|
|
24
24
|
position: relative !important;
|
|
25
25
|
width: 100% !important;
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
border-radius: 0 !important;
|
|
29
29
|
box-shadow: 0 0 20px rgba(0,0,0,0.1) !important;
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
#fullscreen-chat-container .cw-toggle-btn {
|
|
33
33
|
display: none !important;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
#fullscreen-chat-container .cw-widget {
|
|
37
37
|
position: relative !important;
|
|
38
38
|
width: 100% !important;
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
max-height: none !important;
|
|
41
41
|
border-radius: 0 !important;
|
|
42
42
|
display: flex !important;
|
|
43
|
+
flex-direction: column !important;
|
|
44
|
+
overflow: hidden !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#fullscreen-chat-container .cw-messages {
|
|
48
|
+
flex: 1 !important;
|
|
49
|
+
min-height: 0 !important;
|
|
50
|
+
overflow-y: auto !important;
|
|
43
51
|
}
|
|
44
52
|
</style>
|
|
45
53
|
{% endblock %}
|
|
@@ -60,11 +68,16 @@
|
|
|
60
68
|
</div>
|
|
61
69
|
</div>
|
|
62
70
|
<div class="flex items-center space-x-3">
|
|
63
|
-
<a href="{% url 'agent_studio:
|
|
71
|
+
<a href="{% url 'agent_studio:system_collaborators' system.id %}"
|
|
72
|
+
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm"
|
|
73
|
+
title="Manage collaborators">
|
|
74
|
+
<i class="pi pi-users"></i>
|
|
75
|
+
</a>
|
|
76
|
+
<a href="{% url 'agent_studio:system_list' %}"
|
|
64
77
|
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
65
78
|
← Back to Systems
|
|
66
79
|
</a>
|
|
67
|
-
<button @click="clearChat"
|
|
80
|
+
<button @click="clearChat"
|
|
68
81
|
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
69
82
|
Clear Chat
|
|
70
83
|
</button>
|
|
@@ -72,7 +85,7 @@
|
|
|
72
85
|
</div>
|
|
73
86
|
|
|
74
87
|
<!-- Full Screen Chat -->
|
|
75
|
-
<div class="flex-1 relative bg-gray-100 flex justify-center">
|
|
88
|
+
<div class="flex-1 min-h-0 relative bg-gray-100 flex justify-center">
|
|
76
89
|
<div id="fullscreen-chat-container" class="relative w-full max-w-[800px] h-full"></div>
|
|
77
90
|
</div>
|
|
78
91
|
</div>
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
border-radius: 0 !important;
|
|
28
28
|
box-shadow: none !important;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
#fullscreen-chat-container .cw-toggle-btn {
|
|
32
32
|
display: none !important;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
#fullscreen-chat-container .cw-widget {
|
|
36
36
|
position: absolute !important;
|
|
37
37
|
top: 0;
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
max-height: none !important;
|
|
44
44
|
border-radius: 0 !important;
|
|
45
45
|
display: flex !important;
|
|
46
|
+
flex-direction: column !important;
|
|
47
|
+
overflow: hidden !important;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#fullscreen-chat-container .cw-messages {
|
|
51
|
+
flex: 1 !important;
|
|
52
|
+
min-height: 0 !important;
|
|
53
|
+
overflow-y: auto !important;
|
|
46
54
|
}
|
|
47
55
|
</style>
|
|
48
56
|
{% endblock %}
|
|
@@ -59,11 +67,16 @@
|
|
|
59
67
|
</div>
|
|
60
68
|
</div>
|
|
61
69
|
<div class="flex items-center space-x-3">
|
|
62
|
-
<a href="{% url 'agent_studio:
|
|
70
|
+
<a href="{% url 'agent_studio:agent_collaborators' agent.id %}"
|
|
71
|
+
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm"
|
|
72
|
+
title="Manage collaborators">
|
|
73
|
+
<i class="pi pi-users"></i>
|
|
74
|
+
</a>
|
|
75
|
+
<a href="{% url 'agent_studio:agent_edit' agent.id %}"
|
|
63
76
|
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
64
77
|
← Back to Editor
|
|
65
78
|
</a>
|
|
66
|
-
<button @click="clearChat"
|
|
79
|
+
<button @click="clearChat"
|
|
67
80
|
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
68
81
|
Clear Chat
|
|
69
82
|
</button>
|
|
@@ -71,7 +84,7 @@
|
|
|
71
84
|
</div>
|
|
72
85
|
|
|
73
86
|
<!-- Full Screen Chat -->
|
|
74
|
-
<div class="flex-1 relative bg-gray-100">
|
|
87
|
+
<div class="flex-1 min-h-0 relative bg-gray-100">
|
|
75
88
|
<div id="fullscreen-chat-container" class="absolute inset-0"></div>
|
|
76
89
|
</div>
|
|
77
90
|
</div>
|
django_agent_studio/urls.py
CHANGED
|
@@ -12,16 +12,20 @@ app_name = "agent_studio"
|
|
|
12
12
|
urlpatterns = [
|
|
13
13
|
# Main studio interface
|
|
14
14
|
path("", views.StudioHomeView.as_view(), name="home"),
|
|
15
|
+
path("logout/", views.LogoutView.as_view(), name="logout"),
|
|
15
16
|
|
|
16
17
|
# Systems (multi-agent systems)
|
|
17
18
|
path("systems/", views.SystemListView.as_view(), name="system_list"),
|
|
19
|
+
path("systems/create/", views.SystemCreateView.as_view(), name="system_create"),
|
|
18
20
|
path("systems/<uuid:system_id>/test/", views.SystemTestView.as_view(), name="system_test"),
|
|
21
|
+
path("systems/<uuid:system_id>/collaborators/", views.SystemCollaboratorsView.as_view(), name="system_collaborators"),
|
|
19
22
|
|
|
20
23
|
# Agent builder/editor
|
|
21
24
|
path("agents/", views.AgentListView.as_view(), name="agent_list"),
|
|
22
25
|
path("agents/new/", views.AgentBuilderView.as_view(), name="agent_create"),
|
|
23
26
|
path("agents/<uuid:agent_id>/", views.AgentBuilderView.as_view(), name="agent_edit"),
|
|
24
27
|
path("agents/<uuid:agent_id>/test/", views.AgentTestView.as_view(), name="agent_test"),
|
|
28
|
+
path("agents/<uuid:agent_id>/collaborators/", views.AgentCollaboratorsView.as_view(), name="agent_collaborators"),
|
|
25
29
|
|
|
26
30
|
# API endpoints
|
|
27
31
|
path("api/", include(api_urls)),
|
django_agent_studio/views.py
CHANGED
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
Views for django_agent_studio.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from django.contrib.auth import logout
|
|
5
6
|
from django.db.models import Q
|
|
7
|
+
from django.shortcuts import redirect
|
|
8
|
+
from django.views import View
|
|
6
9
|
from django.views.generic import TemplateView, ListView
|
|
7
10
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
8
11
|
|
|
9
|
-
from django_agent_runtime.models import
|
|
12
|
+
from django_agent_runtime.models import (
|
|
13
|
+
AgentDefinition,
|
|
14
|
+
AgentSystem,
|
|
15
|
+
AgentSystemMember,
|
|
16
|
+
AgentCollaborator,
|
|
17
|
+
SystemCollaborator,
|
|
18
|
+
CollaboratorRole,
|
|
19
|
+
)
|
|
10
20
|
|
|
11
21
|
|
|
12
22
|
class AgentAccessMixin:
|
|
@@ -14,25 +24,170 @@ class AgentAccessMixin:
|
|
|
14
24
|
Mixin that provides consistent agent access logic.
|
|
15
25
|
|
|
16
26
|
- Superusers can access all agents
|
|
17
|
-
-
|
|
27
|
+
- Owners can access their own agents
|
|
28
|
+
- Collaborators can access agents they've been granted access to
|
|
29
|
+
- Users with system access can access agents in that system
|
|
18
30
|
"""
|
|
19
31
|
|
|
20
32
|
def get_user_agents_queryset(self):
|
|
21
33
|
"""Get queryset of agents accessible to the current user."""
|
|
22
34
|
if self.request.user.is_superuser:
|
|
23
35
|
return AgentDefinition.objects.all()
|
|
24
|
-
return AgentDefinition.objects.filter(owner=self.request.user)
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
user = self.request.user
|
|
38
|
+
|
|
39
|
+
# Get system IDs the user has access to (owner or collaborator)
|
|
40
|
+
accessible_system_ids = AgentSystem.objects.filter(
|
|
41
|
+
Q(owner=user) | Q(collaborators__user=user)
|
|
42
|
+
).values_list('id', flat=True)
|
|
43
|
+
|
|
44
|
+
# Get agent IDs that are members of accessible systems
|
|
45
|
+
agents_via_systems = AgentSystemMember.objects.filter(
|
|
46
|
+
system_id__in=accessible_system_ids
|
|
47
|
+
).values_list('agent_id', flat=True)
|
|
48
|
+
|
|
49
|
+
return AgentDefinition.objects.filter(
|
|
50
|
+
Q(owner=user) |
|
|
51
|
+
Q(collaborators__user=user) |
|
|
52
|
+
Q(id__in=agents_via_systems)
|
|
53
|
+
).distinct()
|
|
54
|
+
|
|
55
|
+
def get_agent_for_user(self, agent_id, require_edit=False):
|
|
27
56
|
"""
|
|
28
57
|
Get a specific agent if the user has access.
|
|
29
58
|
|
|
30
|
-
|
|
31
|
-
|
|
59
|
+
Args:
|
|
60
|
+
agent_id: The agent's ID
|
|
61
|
+
require_edit: If True, requires edit permission (editor or admin role)
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
AgentDefinition if user has access
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
AgentDefinition.DoesNotExist if no access
|
|
32
68
|
"""
|
|
33
69
|
if self.request.user.is_superuser:
|
|
34
70
|
return AgentDefinition.objects.get(id=agent_id)
|
|
35
|
-
|
|
71
|
+
|
|
72
|
+
agent = AgentDefinition.objects.get(id=agent_id)
|
|
73
|
+
user = self.request.user
|
|
74
|
+
|
|
75
|
+
# Owner always has full access
|
|
76
|
+
if agent.owner == user:
|
|
77
|
+
return agent
|
|
78
|
+
|
|
79
|
+
# Check direct collaborator access
|
|
80
|
+
try:
|
|
81
|
+
collab = AgentCollaborator.objects.get(agent=agent, user=user)
|
|
82
|
+
if require_edit and not collab.can_edit:
|
|
83
|
+
raise AgentDefinition.DoesNotExist("Edit permission required")
|
|
84
|
+
return agent
|
|
85
|
+
except AgentCollaborator.DoesNotExist:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
# Check if user has access via a system that contains this agent
|
|
89
|
+
agent_system_ids = AgentSystemMember.objects.filter(
|
|
90
|
+
agent=agent
|
|
91
|
+
).values_list('system_id', flat=True)
|
|
92
|
+
|
|
93
|
+
if agent_system_ids:
|
|
94
|
+
# Check for system owner or collaborator access
|
|
95
|
+
system_collab = SystemCollaborator.objects.filter(
|
|
96
|
+
system_id__in=agent_system_ids, user=user
|
|
97
|
+
).first()
|
|
98
|
+
if system_collab:
|
|
99
|
+
if require_edit and not system_collab.can_edit:
|
|
100
|
+
raise AgentDefinition.DoesNotExist("Edit permission required")
|
|
101
|
+
return agent
|
|
102
|
+
|
|
103
|
+
# Check if user owns any of these systems
|
|
104
|
+
if AgentSystem.objects.filter(id__in=agent_system_ids, owner=user).exists():
|
|
105
|
+
return agent
|
|
106
|
+
|
|
107
|
+
raise AgentDefinition.DoesNotExist("No access to this agent")
|
|
108
|
+
|
|
109
|
+
def get_agent_role(self, agent):
|
|
110
|
+
"""
|
|
111
|
+
Get the user's role for an agent.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
'owner', 'admin', 'editor', 'viewer', or None
|
|
115
|
+
Also returns tuple (role, source) where source is 'direct' or 'system:<name>'
|
|
116
|
+
"""
|
|
117
|
+
user = self.request.user
|
|
118
|
+
if user.is_superuser or agent.owner == user:
|
|
119
|
+
return 'owner'
|
|
120
|
+
|
|
121
|
+
# Check direct collaborator
|
|
122
|
+
try:
|
|
123
|
+
collab = AgentCollaborator.objects.get(agent=agent, user=user)
|
|
124
|
+
return collab.role
|
|
125
|
+
except AgentCollaborator.DoesNotExist:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
# Check system-level access
|
|
129
|
+
agent_systems = AgentSystemMember.objects.filter(
|
|
130
|
+
agent=agent
|
|
131
|
+
).select_related('system')
|
|
132
|
+
|
|
133
|
+
for member in agent_systems:
|
|
134
|
+
system = member.system
|
|
135
|
+
if system.owner == user:
|
|
136
|
+
return 'owner'
|
|
137
|
+
try:
|
|
138
|
+
sys_collab = SystemCollaborator.objects.get(system=system, user=user)
|
|
139
|
+
return sys_collab.role
|
|
140
|
+
except SystemCollaborator.DoesNotExist:
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
def get_agent_access_info(self, agent):
|
|
146
|
+
"""
|
|
147
|
+
Get detailed access info for an agent.
|
|
148
|
+
|
|
149
|
+
Returns dict with:
|
|
150
|
+
- role: 'owner', 'admin', 'editor', 'viewer', or None
|
|
151
|
+
- source: 'direct', 'system', or None
|
|
152
|
+
- system_name: Name of system granting access (if source is 'system')
|
|
153
|
+
"""
|
|
154
|
+
user = self.request.user
|
|
155
|
+
if user.is_superuser or agent.owner == user:
|
|
156
|
+
return {'role': 'owner', 'source': 'direct', 'system_name': None}
|
|
157
|
+
|
|
158
|
+
# Check direct collaborator
|
|
159
|
+
try:
|
|
160
|
+
collab = AgentCollaborator.objects.get(agent=agent, user=user)
|
|
161
|
+
return {'role': collab.role, 'source': 'direct', 'system_name': None}
|
|
162
|
+
except AgentCollaborator.DoesNotExist:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
# Check system-level access
|
|
166
|
+
agent_systems = AgentSystemMember.objects.filter(
|
|
167
|
+
agent=agent
|
|
168
|
+
).select_related('system')
|
|
169
|
+
|
|
170
|
+
for member in agent_systems:
|
|
171
|
+
system = member.system
|
|
172
|
+
if system.owner == user:
|
|
173
|
+
return {'role': 'owner', 'source': 'system', 'system_name': system.name}
|
|
174
|
+
try:
|
|
175
|
+
sys_collab = SystemCollaborator.objects.get(system=system, user=user)
|
|
176
|
+
return {'role': sys_collab.role, 'source': 'system', 'system_name': system.name}
|
|
177
|
+
except SystemCollaborator.DoesNotExist:
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
return {'role': None, 'source': None, 'system_name': None}
|
|
181
|
+
|
|
182
|
+
def can_edit_agent(self, agent):
|
|
183
|
+
"""Check if user can edit the agent."""
|
|
184
|
+
role = self.get_agent_role(agent)
|
|
185
|
+
return role in ['owner', 'admin', 'editor']
|
|
186
|
+
|
|
187
|
+
def can_admin_agent(self, agent):
|
|
188
|
+
"""Check if user can manage collaborators on the agent."""
|
|
189
|
+
role = self.get_agent_role(agent)
|
|
190
|
+
return role in ['owner', 'admin']
|
|
36
191
|
|
|
37
192
|
|
|
38
193
|
class SystemAccessMixin:
|
|
@@ -40,27 +195,76 @@ class SystemAccessMixin:
|
|
|
40
195
|
Mixin that provides consistent system access logic.
|
|
41
196
|
|
|
42
197
|
- Superusers can access all systems
|
|
43
|
-
-
|
|
198
|
+
- Owners can access their own systems
|
|
199
|
+
- Collaborators can access systems they've been granted access to
|
|
44
200
|
"""
|
|
45
201
|
|
|
46
202
|
def get_user_systems_queryset(self):
|
|
47
203
|
"""Get queryset of systems accessible to the current user."""
|
|
48
204
|
if self.request.user.is_superuser:
|
|
49
205
|
return AgentSystem.objects.all()
|
|
50
|
-
|
|
206
|
+
# Include owned systems and systems where user is a collaborator
|
|
207
|
+
return AgentSystem.objects.filter(
|
|
208
|
+
Q(owner=self.request.user) |
|
|
209
|
+
Q(collaborators__user=self.request.user)
|
|
210
|
+
).distinct()
|
|
51
211
|
|
|
52
|
-
def get_system_for_user(self, system_id):
|
|
212
|
+
def get_system_for_user(self, system_id, require_edit=False):
|
|
53
213
|
"""
|
|
54
214
|
Get a specific system if the user has access.
|
|
55
215
|
|
|
56
|
-
|
|
57
|
-
|
|
216
|
+
Args:
|
|
217
|
+
system_id: The system's ID
|
|
218
|
+
require_edit: If True, requires edit permission (editor or admin role)
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
AgentSystem if user has access
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
AgentSystem.DoesNotExist if no access
|
|
58
225
|
"""
|
|
59
226
|
if self.request.user.is_superuser:
|
|
60
227
|
return AgentSystem.objects.select_related('entry_agent').get(id=system_id)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
228
|
+
|
|
229
|
+
system = AgentSystem.objects.select_related('entry_agent').get(id=system_id)
|
|
230
|
+
|
|
231
|
+
# Owner always has full access
|
|
232
|
+
if system.owner == self.request.user:
|
|
233
|
+
return system
|
|
234
|
+
|
|
235
|
+
# Check collaborator access
|
|
236
|
+
try:
|
|
237
|
+
collab = SystemCollaborator.objects.get(system=system, user=self.request.user)
|
|
238
|
+
if require_edit and not collab.can_edit:
|
|
239
|
+
raise AgentSystem.DoesNotExist("Edit permission required")
|
|
240
|
+
return system
|
|
241
|
+
except SystemCollaborator.DoesNotExist:
|
|
242
|
+
raise AgentSystem.DoesNotExist("No access to this system")
|
|
243
|
+
|
|
244
|
+
def get_system_role(self, system):
|
|
245
|
+
"""
|
|
246
|
+
Get the user's role for a system.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
'owner', 'admin', 'editor', 'viewer', or None
|
|
250
|
+
"""
|
|
251
|
+
if self.request.user.is_superuser or system.owner == self.request.user:
|
|
252
|
+
return 'owner'
|
|
253
|
+
try:
|
|
254
|
+
collab = SystemCollaborator.objects.get(system=system, user=self.request.user)
|
|
255
|
+
return collab.role
|
|
256
|
+
except SystemCollaborator.DoesNotExist:
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
def can_edit_system(self, system):
|
|
260
|
+
"""Check if user can edit the system."""
|
|
261
|
+
role = self.get_system_role(system)
|
|
262
|
+
return role in ['owner', 'admin', 'editor']
|
|
263
|
+
|
|
264
|
+
def can_admin_system(self, system):
|
|
265
|
+
"""Check if user can manage collaborators on the system."""
|
|
266
|
+
role = self.get_system_role(system)
|
|
267
|
+
return role in ['owner', 'admin']
|
|
64
268
|
|
|
65
269
|
|
|
66
270
|
class StudioHomeView(LoginRequiredMixin, AgentAccessMixin, SystemAccessMixin, TemplateView):
|
|
@@ -144,6 +348,18 @@ class SystemListView(LoginRequiredMixin, SystemAccessMixin, ListView):
|
|
|
144
348
|
).prefetch_related('members__agent').order_by("-updated_at")
|
|
145
349
|
|
|
146
350
|
|
|
351
|
+
class SystemCreateView(LoginRequiredMixin, AgentAccessMixin, TemplateView):
|
|
352
|
+
"""Create a new multi-agent system."""
|
|
353
|
+
|
|
354
|
+
template_name = "django_agent_studio/system_create.html"
|
|
355
|
+
|
|
356
|
+
def get_context_data(self, **kwargs):
|
|
357
|
+
context = super().get_context_data(**kwargs)
|
|
358
|
+
# Get user's agents for the entry agent dropdown
|
|
359
|
+
context["agents"] = self.get_user_agents_queryset().order_by("name")
|
|
360
|
+
return context
|
|
361
|
+
|
|
362
|
+
|
|
147
363
|
class SystemTestView(LoginRequiredMixin, SystemAccessMixin, TemplateView):
|
|
148
364
|
"""
|
|
149
365
|
Full-screen system testing interface.
|
|
@@ -161,3 +377,89 @@ class SystemTestView(LoginRequiredMixin, SystemAccessMixin, TemplateView):
|
|
|
161
377
|
context["agent"] = system.entry_agent # The entry point agent
|
|
162
378
|
return context
|
|
163
379
|
|
|
380
|
+
|
|
381
|
+
class AgentCollaboratorsView(LoginRequiredMixin, AgentAccessMixin, TemplateView):
|
|
382
|
+
"""Manage collaborators for an agent."""
|
|
383
|
+
|
|
384
|
+
template_name = "django_agent_studio/collaborators.html"
|
|
385
|
+
|
|
386
|
+
def get_context_data(self, **kwargs):
|
|
387
|
+
context = super().get_context_data(**kwargs)
|
|
388
|
+
agent_id = kwargs.get("agent_id")
|
|
389
|
+
agent = self.get_agent_for_user(agent_id)
|
|
390
|
+
|
|
391
|
+
context["object"] = agent
|
|
392
|
+
context["object_name"] = agent.name
|
|
393
|
+
context["object_type"] = "agent"
|
|
394
|
+
context["can_admin"] = self.can_admin_agent(agent)
|
|
395
|
+
|
|
396
|
+
# Owner info
|
|
397
|
+
if agent.owner:
|
|
398
|
+
context["owner_email"] = agent.owner.email
|
|
399
|
+
context["owner_initial"] = agent.owner.email[0].upper() if agent.owner.email else "?"
|
|
400
|
+
else:
|
|
401
|
+
context["owner_email"] = "No owner"
|
|
402
|
+
context["owner_initial"] = "?"
|
|
403
|
+
|
|
404
|
+
# Get systems this agent belongs to (for showing inherited access info)
|
|
405
|
+
agent_systems = AgentSystemMember.objects.filter(
|
|
406
|
+
agent=agent
|
|
407
|
+
).select_related('system', 'system__owner')
|
|
408
|
+
context["parent_systems"] = [
|
|
409
|
+
{
|
|
410
|
+
'id': str(member.system.id),
|
|
411
|
+
'name': member.system.name,
|
|
412
|
+
'role': member.role,
|
|
413
|
+
}
|
|
414
|
+
for member in agent_systems
|
|
415
|
+
]
|
|
416
|
+
|
|
417
|
+
return context
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class SystemCollaboratorsView(LoginRequiredMixin, SystemAccessMixin, TemplateView):
|
|
421
|
+
"""Manage collaborators for a system."""
|
|
422
|
+
|
|
423
|
+
template_name = "django_agent_studio/collaborators.html"
|
|
424
|
+
|
|
425
|
+
def get_context_data(self, **kwargs):
|
|
426
|
+
context = super().get_context_data(**kwargs)
|
|
427
|
+
system_id = kwargs.get("system_id")
|
|
428
|
+
system = self.get_system_for_user(system_id)
|
|
429
|
+
|
|
430
|
+
context["object"] = system
|
|
431
|
+
context["object_name"] = system.name
|
|
432
|
+
context["object_type"] = "system"
|
|
433
|
+
context["can_admin"] = self.can_admin_system(system)
|
|
434
|
+
|
|
435
|
+
# Owner info
|
|
436
|
+
if system.owner:
|
|
437
|
+
context["owner_email"] = system.owner.email
|
|
438
|
+
context["owner_initial"] = system.owner.email[0].upper() if system.owner.email else "?"
|
|
439
|
+
else:
|
|
440
|
+
context["owner_email"] = "No owner"
|
|
441
|
+
context["owner_initial"] = "?"
|
|
442
|
+
|
|
443
|
+
# Get agents in this system (for showing what agents are included)
|
|
444
|
+
system_members = AgentSystemMember.objects.filter(
|
|
445
|
+
system=system
|
|
446
|
+
).select_related('agent')
|
|
447
|
+
context["member_agents"] = [
|
|
448
|
+
{
|
|
449
|
+
'id': str(member.agent.id),
|
|
450
|
+
'name': member.agent.name,
|
|
451
|
+
'role': member.role,
|
|
452
|
+
}
|
|
453
|
+
for member in system_members
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
return context
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class LogoutView(View):
|
|
460
|
+
"""Simple logout view that works with any auth backend."""
|
|
461
|
+
|
|
462
|
+
def post(self, request):
|
|
463
|
+
logout(request)
|
|
464
|
+
return redirect('/')
|
|
465
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-agent-studio
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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.3** | 2026-01-30 | **Logout & Scroll Fixes** - Added logout button to header, fixed embedded chat widget scrolling with proper flex layout |
|
|
130
|
+
| **0.3.2** | 2026-01-30 | **Multi-User Access Control** - Collaborator management UI, user search autocomplete, supports both email and username-based User models, system creation from homepage, permission inheritance display |
|
|
129
131
|
| **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
132
|
| **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 |
|
|
131
133
|
| **0.2.0** | 2026-01-28 | **Multi-Agent Systems** - System management UI, shared memory configuration, memory privacy controls, builder tools for managing agent systems |
|
|
@@ -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=qoqmIaYx2qB4Ail9AhOzH3XKrMK9T8wxk8Kms2sHbkk,1333
|
|
4
|
+
django_agent_studio/views.py,sha256=iAO_0oY3vlJdjaVACVXJN2Z784-Tc82PILXQqA2vHDk,15844
|
|
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
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=qIQ74JzOdJRp_Sh2xtETuRwtRThcpn3EYS14rvAKdHA,23593
|
|
11
|
+
django_agent_studio/api/urls.py,sha256=xfL3AbTP0GKRYarDExvUoXuz0txhgNCumgjVehSfmTY,9893
|
|
12
|
+
django_agent_studio/api/views.py,sha256=fECFWpu08ibQpOR-sL7AVCoTh4Ox6cYUEm4lh5GAPsE,90869
|
|
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
|
|
@@ -19,20 +19,22 @@ django_agent_studio/models/permissions.py,sha256=NepltvQzeehv5EwcOTqULj8n3N5XGVT
|
|
|
19
19
|
django_agent_studio/services/__init__.py,sha256=06cc1eLnAbROTKFxBgvkeCpsYV5cQsyJVGXaE_rcp3M,244
|
|
20
20
|
django_agent_studio/services/permissions.py,sha256=-cwe0YZCFBFYh_ScXKncsPwEkCfEyIrtOzp8Wt9GSco,7785
|
|
21
21
|
django_agent_studio/static/agent-frontend/chat-widget-markdown.js,sha256=_TQCux4HPRynCiLKydITO-docIRoi454jLpX0fegAFM,3722
|
|
22
|
-
django_agent_studio/static/agent-frontend/chat-widget.css,sha256=
|
|
22
|
+
django_agent_studio/static/agent-frontend/chat-widget.css,sha256=qz495hBIVvDj-TtDRAhejfs6LkCQR9mKp8niYDPr2nw,38381
|
|
23
23
|
django_agent_studio/static/agent-frontend/chat-widget.js,sha256=Yla8YlsmMtdXZRjN45Fup-4-NTCoM3NgR3yPsA5_Ot4,60548
|
|
24
24
|
django_agent_studio/static/django_agent_studio/js/builder.js,sha256=eEUw2z-pNdoU3xv3NhLQ-uDZV7tsf8Bawseg7B7dpCo,413948
|
|
25
25
|
django_agent_studio/static/django_agent_studio/js/builder.js.map,sha256=sVkeGyZsBWEr4MKhqAfd3B4JL5n7D6eKc_4ZYUsbKO8,1633363
|
|
26
26
|
django_agent_studio/static/django_agent_studio/js/style.css,sha256=rigQJy_oHIhgb1_7DwAQ_8G002gQnT0AXUtwxImWj1o,1626515
|
|
27
|
-
django_agent_studio/templates/django_agent_studio/agent_list.html,sha256=
|
|
28
|
-
django_agent_studio/templates/django_agent_studio/base.html,sha256=
|
|
27
|
+
django_agent_studio/templates/django_agent_studio/agent_list.html,sha256=pIW6cRQ-i_hKWB_qDUuSBbzZw2qL_qJumhrW8C4Rs14,4931
|
|
28
|
+
django_agent_studio/templates/django_agent_studio/base.html,sha256=whRDhBrk-uRkHSdbRNFpp6GBEnV-_lEDFHtaGLqJ3kA,6566
|
|
29
29
|
django_agent_studio/templates/django_agent_studio/builder.html,sha256=yPzb0Pdp0d7Sp7gVkJd7OouDq-toKCA8i7OJLU9x6Z8,1542
|
|
30
|
-
django_agent_studio/templates/django_agent_studio/
|
|
31
|
-
django_agent_studio/templates/django_agent_studio/
|
|
32
|
-
django_agent_studio/templates/django_agent_studio/
|
|
33
|
-
django_agent_studio/templates/django_agent_studio/
|
|
34
|
-
django_agent_studio
|
|
35
|
-
django_agent_studio
|
|
36
|
-
django_agent_studio-0.3.
|
|
37
|
-
django_agent_studio-0.3.
|
|
38
|
-
django_agent_studio-0.3.
|
|
30
|
+
django_agent_studio/templates/django_agent_studio/collaborators.html,sha256=qPV0YG8V8eFCdK3e--kgxh3iT4jPHAXCxkSzOBkx7bg,19482
|
|
31
|
+
django_agent_studio/templates/django_agent_studio/home.html,sha256=ebA-U1HseIzY5pPeXKluzI8vsKOh1owkKZxhys27Yns,6914
|
|
32
|
+
django_agent_studio/templates/django_agent_studio/system_create.html,sha256=MOhKMuiR2Iley0J7gvFk9yxLT51BqPrOhk0PbJ-QXjo,6392
|
|
33
|
+
django_agent_studio/templates/django_agent_studio/system_list.html,sha256=CN2o3nFpsZHwv6Wva1DkzYaAZnK204Q6FAqVH08L6_0,4666
|
|
34
|
+
django_agent_studio/templates/django_agent_studio/system_test.html,sha256=Jo_pbIneXkTuT10lULuWa3i2BhJZv2uE4AClroaSNoE,4466
|
|
35
|
+
django_agent_studio/templates/django_agent_studio/test.html,sha256=pACtNyD64KJhPnkpEnixLjdxkbeHbNCBBE1NEruKZ-Q,4320
|
|
36
|
+
django_agent_studio-0.3.3.dist-info/licenses/LICENSE,sha256=WIh21lpD7d7xCUtLysKK-kbfW4SG7GNPf_k7_Xm_sZg,3851
|
|
37
|
+
django_agent_studio-0.3.3.dist-info/METADATA,sha256=Z7h7mKgUj3VCgjsbwWmgQGH4kjXTR7IFtyEWYHBJiBY,17048
|
|
38
|
+
django_agent_studio-0.3.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
39
|
+
django_agent_studio-0.3.3.dist-info/top_level.txt,sha256=O1kqZzXPOsJlqnPSAcB2fH5WpJNY8ZNfHEJzX9_SZ0A,20
|
|
40
|
+
django_agent_studio-0.3.3.dist-info/RECORD,,
|
|
File without changes
|
{django_agent_studio-0.3.1.dist-info → django_agent_studio-0.3.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|