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,97 @@
|
|
|
1
|
+
{% extends "django_agent_studio/base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}Agent Studio - Home{% endblock %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
<div class="h-full p-6 overflow-auto">
|
|
7
|
+
<div class="max-w-6xl mx-auto">
|
|
8
|
+
<!-- Welcome Section -->
|
|
9
|
+
<div class="mb-8">
|
|
10
|
+
<h1 class="text-3xl font-bold text-gray-900 mb-2">Welcome to Agent Studio</h1>
|
|
11
|
+
<p class="text-gray-600">Build, customize, and manage your AI agents in one place.</p>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<!-- Quick Actions -->
|
|
15
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
|
16
|
+
<a href="{% url 'agent_studio:agent_create' %}"
|
|
17
|
+
class="bg-primary-600 hover:bg-primary-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">Create New Agent</span>
|
|
21
|
+
</div>
|
|
22
|
+
<p class="text-primary-100 text-sm">Start building a custom AI agent from scratch</p>
|
|
23
|
+
</a>
|
|
24
|
+
|
|
25
|
+
<a href="{% url 'agent_studio:agent_list' %}"
|
|
26
|
+
class="bg-white hover:bg-gray-50 border border-gray-200 rounded-lg p-6 transition-colors">
|
|
27
|
+
<div class="flex items-center space-x-3 mb-2">
|
|
28
|
+
<span class="text-2xl">📋</span>
|
|
29
|
+
<span class="text-lg font-semibold text-gray-800">My Agents</span>
|
|
30
|
+
</div>
|
|
31
|
+
<p class="text-gray-600 text-sm">View and manage your existing agents</p>
|
|
32
|
+
</a>
|
|
33
|
+
|
|
34
|
+
<div class="bg-white hover:bg-gray-50 border border-gray-200 rounded-lg p-6 transition-colors cursor-pointer">
|
|
35
|
+
<div class="flex items-center space-x-3 mb-2">
|
|
36
|
+
<span class="text-2xl">📚</span>
|
|
37
|
+
<span class="text-lg font-semibold text-gray-800">Templates</span>
|
|
38
|
+
</div>
|
|
39
|
+
<p class="text-gray-600 text-sm">Start from a pre-built template</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<!-- Recent Agents -->
|
|
44
|
+
{% if recent_agents %}
|
|
45
|
+
<div class="mb-8">
|
|
46
|
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Recent Agents</h2>
|
|
47
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
48
|
+
{% for agent in recent_agents %}
|
|
49
|
+
<a href="{% url 'agent_studio:agent_edit' agent.id %}"
|
|
50
|
+
class="bg-white border border-gray-200 rounded-lg p-4 hover:border-primary-300 hover:shadow-sm transition-all">
|
|
51
|
+
<div class="flex items-center space-x-3 mb-2">
|
|
52
|
+
<span class="text-xl">{{ agent.icon|default:"🤖" }}</span>
|
|
53
|
+
<span class="font-medium text-gray-800">{{ agent.name }}</span>
|
|
54
|
+
</div>
|
|
55
|
+
<p class="text-sm text-gray-500 line-clamp-2">{{ agent.description|default:"No description" }}</p>
|
|
56
|
+
<div class="mt-3 text-xs text-gray-400">
|
|
57
|
+
Updated {{ agent.updated_at|timesince }} ago
|
|
58
|
+
</div>
|
|
59
|
+
</a>
|
|
60
|
+
{% endfor %}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
{% endif %}
|
|
64
|
+
|
|
65
|
+
<!-- Template Agents -->
|
|
66
|
+
{% if template_agents %}
|
|
67
|
+
<div>
|
|
68
|
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Popular Templates</h2>
|
|
69
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
70
|
+
{% for agent in template_agents %}
|
|
71
|
+
<div class="bg-white border border-gray-200 rounded-lg p-4 hover:border-primary-300 hover:shadow-sm transition-all cursor-pointer">
|
|
72
|
+
<div class="flex items-center space-x-3 mb-2">
|
|
73
|
+
<span class="text-xl">{{ agent.icon|default:"📄" }}</span>
|
|
74
|
+
<span class="font-medium text-gray-800">{{ agent.name }}</span>
|
|
75
|
+
</div>
|
|
76
|
+
<p class="text-sm text-gray-500 line-clamp-2">{{ agent.description|default:"No description" }}</p>
|
|
77
|
+
</div>
|
|
78
|
+
{% endfor %}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
{% endif %}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
{% endblock %}
|
|
85
|
+
|
|
86
|
+
{% block extra_js %}
|
|
87
|
+
<script>
|
|
88
|
+
const { createApp } = Vue;
|
|
89
|
+
|
|
90
|
+
createApp({
|
|
91
|
+
data() {
|
|
92
|
+
return {}
|
|
93
|
+
}
|
|
94
|
+
}).use(primevue.config.default).mount('#app');
|
|
95
|
+
</script>
|
|
96
|
+
{% endblock %}
|
|
97
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{% extends "django_agent_studio/base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}Test {{ agent.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:agent_list' %}" class="text-gray-500 hover:text-gray-700">My Agents</a>
|
|
9
|
+
<span class="text-gray-400">/</span>
|
|
10
|
+
<a href="{% url 'agent_studio:agent_edit' agent.id %}" class="text-gray-500 hover:text-gray-700">{{ agent.name }}</a>
|
|
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 .cw-container {
|
|
19
|
+
position: absolute !important;
|
|
20
|
+
top: 0;
|
|
21
|
+
left: 0;
|
|
22
|
+
right: 0;
|
|
23
|
+
bottom: 0;
|
|
24
|
+
width: 100% !important;
|
|
25
|
+
height: 100% !important;
|
|
26
|
+
max-height: none !important;
|
|
27
|
+
border-radius: 0 !important;
|
|
28
|
+
box-shadow: none !important;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#fullscreen-chat-container .cw-toggle-btn {
|
|
32
|
+
display: none !important;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#fullscreen-chat-container .cw-widget {
|
|
36
|
+
position: absolute !important;
|
|
37
|
+
top: 0;
|
|
38
|
+
left: 0;
|
|
39
|
+
right: 0;
|
|
40
|
+
bottom: 0;
|
|
41
|
+
width: 100% !important;
|
|
42
|
+
height: 100% !important;
|
|
43
|
+
max-height: none !important;
|
|
44
|
+
border-radius: 0 !important;
|
|
45
|
+
display: flex !important;
|
|
46
|
+
}
|
|
47
|
+
</style>
|
|
48
|
+
{% endblock %}
|
|
49
|
+
|
|
50
|
+
{% block content %}
|
|
51
|
+
<div class="h-full flex flex-col">
|
|
52
|
+
<!-- Agent Info Bar -->
|
|
53
|
+
<div class="px-4 py-3 bg-white border-b border-gray-200 flex items-center justify-between">
|
|
54
|
+
<div class="flex items-center space-x-4">
|
|
55
|
+
<span class="text-2xl">{{ agent.icon|default:"🤖" }}</span>
|
|
56
|
+
<div>
|
|
57
|
+
<h1 class="font-semibold text-gray-800">{{ agent.name }}</h1>
|
|
58
|
+
<p class="text-sm text-gray-500">{{ agent.description|default:"No description"|truncatechars:100 }}</p>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="flex items-center space-x-3">
|
|
62
|
+
<a href="{% url 'agent_studio:agent_edit' agent.id %}"
|
|
63
|
+
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
64
|
+
← Back to Editor
|
|
65
|
+
</a>
|
|
66
|
+
<button @click="clearChat"
|
|
67
|
+
class="text-gray-600 hover:text-gray-800 px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
|
|
68
|
+
Clear Chat
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<!-- Full Screen Chat -->
|
|
74
|
+
<div class="flex-1 relative bg-gray-100">
|
|
75
|
+
<div id="fullscreen-chat-container" class="absolute inset-0"></div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
{% endblock %}
|
|
79
|
+
|
|
80
|
+
{% block extra_js %}
|
|
81
|
+
<script>
|
|
82
|
+
const { createApp } = Vue;
|
|
83
|
+
|
|
84
|
+
const agentSlug = "{{ agent.slug }}";
|
|
85
|
+
const backendUrl = window.location.origin;
|
|
86
|
+
|
|
87
|
+
function initChat() {
|
|
88
|
+
if (window.testChatWidget) {
|
|
89
|
+
window.testChatWidget.destroy();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
window.testChatWidget = ChatWidget.createInstance({
|
|
93
|
+
containerId: 'fullscreen-chat-container',
|
|
94
|
+
backendUrl: backendUrl,
|
|
95
|
+
agentKey: agentSlug,
|
|
96
|
+
title: '{{ agent.name }}',
|
|
97
|
+
primaryColor: '#3b82f6',
|
|
98
|
+
showClearButton: true,
|
|
99
|
+
showDebugButton: true,
|
|
100
|
+
showExpandButton: false,
|
|
101
|
+
embedded: true,
|
|
102
|
+
apiPaths: {
|
|
103
|
+
runs: '/api/agent-runtime/runs/',
|
|
104
|
+
runEvents: '/api/agent-runtime/runs/{runId}/events/',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
createApp({
|
|
110
|
+
data() {
|
|
111
|
+
return {}
|
|
112
|
+
},
|
|
113
|
+
methods: {
|
|
114
|
+
clearChat() {
|
|
115
|
+
if (window.testChatWidget) {
|
|
116
|
+
window.testChatWidget.clearMessages();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
mounted() {
|
|
121
|
+
initChat();
|
|
122
|
+
}
|
|
123
|
+
}).use(primevue.config.default).mount('#app');
|
|
124
|
+
</script>
|
|
125
|
+
{% endblock %}
|
|
126
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
URL configuration for django_agent_studio.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from django.urls import path, include
|
|
6
|
+
|
|
7
|
+
from django_agent_studio import views
|
|
8
|
+
from django_agent_studio.api import urls as api_urls
|
|
9
|
+
|
|
10
|
+
app_name = "agent_studio"
|
|
11
|
+
|
|
12
|
+
urlpatterns = [
|
|
13
|
+
# Main studio interface
|
|
14
|
+
path("", views.StudioHomeView.as_view(), name="home"),
|
|
15
|
+
|
|
16
|
+
# Agent builder/editor
|
|
17
|
+
path("agents/", views.AgentListView.as_view(), name="agent_list"),
|
|
18
|
+
path("agents/new/", views.AgentBuilderView.as_view(), name="agent_create"),
|
|
19
|
+
path("agents/<uuid:agent_id>/", views.AgentBuilderView.as_view(), name="agent_edit"),
|
|
20
|
+
path("agents/<uuid:agent_id>/test/", views.AgentTestView.as_view(), name="agent_test"),
|
|
21
|
+
|
|
22
|
+
# API endpoints
|
|
23
|
+
path("api/", include(api_urls)),
|
|
24
|
+
]
|
|
25
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Views for django_agent_studio.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from django.db.models import Q
|
|
6
|
+
from django.views.generic import TemplateView, ListView
|
|
7
|
+
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
8
|
+
|
|
9
|
+
from django_agent_runtime.models import AgentDefinition
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentAccessMixin:
|
|
13
|
+
"""
|
|
14
|
+
Mixin that provides consistent agent access logic.
|
|
15
|
+
|
|
16
|
+
- Superusers can access all agents
|
|
17
|
+
- Regular users can access their own agents
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def get_user_agents_queryset(self):
|
|
21
|
+
"""Get queryset of agents accessible to the current user."""
|
|
22
|
+
if self.request.user.is_superuser:
|
|
23
|
+
return AgentDefinition.objects.all()
|
|
24
|
+
return AgentDefinition.objects.filter(owner=self.request.user)
|
|
25
|
+
|
|
26
|
+
def get_agent_for_user(self, agent_id):
|
|
27
|
+
"""
|
|
28
|
+
Get a specific agent if the user has access.
|
|
29
|
+
|
|
30
|
+
- Superusers can access any agent
|
|
31
|
+
- Regular users can access their own agents
|
|
32
|
+
"""
|
|
33
|
+
if self.request.user.is_superuser:
|
|
34
|
+
return AgentDefinition.objects.get(id=agent_id)
|
|
35
|
+
return AgentDefinition.objects.get(id=agent_id, owner=self.request.user)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class StudioHomeView(LoginRequiredMixin, AgentAccessMixin, TemplateView):
|
|
39
|
+
"""Home page for the agent studio."""
|
|
40
|
+
|
|
41
|
+
template_name = "django_agent_studio/home.html"
|
|
42
|
+
|
|
43
|
+
def get_context_data(self, **kwargs):
|
|
44
|
+
context = super().get_context_data(**kwargs)
|
|
45
|
+
context["recent_agents"] = self.get_user_agents_queryset().order_by("-updated_at")[:5]
|
|
46
|
+
context["template_agents"] = AgentDefinition.objects.filter(
|
|
47
|
+
is_template=True,
|
|
48
|
+
is_public=True,
|
|
49
|
+
).order_by("-updated_at")[:10]
|
|
50
|
+
return context
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class AgentListView(LoginRequiredMixin, AgentAccessMixin, ListView):
|
|
54
|
+
"""List all agents for the current user."""
|
|
55
|
+
|
|
56
|
+
template_name = "django_agent_studio/agent_list.html"
|
|
57
|
+
context_object_name = "agents"
|
|
58
|
+
|
|
59
|
+
def get_queryset(self):
|
|
60
|
+
return self.get_user_agents_queryset().order_by("-updated_at")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class AgentBuilderView(LoginRequiredMixin, AgentAccessMixin, TemplateView):
|
|
64
|
+
"""
|
|
65
|
+
Two-pane agent builder interface.
|
|
66
|
+
|
|
67
|
+
Left pane: Test the agent (agent-frontend instance)
|
|
68
|
+
Right pane: Builder agent conversation (agent-frontend instance)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
template_name = "django_agent_studio/builder.html"
|
|
72
|
+
|
|
73
|
+
def get_context_data(self, **kwargs):
|
|
74
|
+
context = super().get_context_data(**kwargs)
|
|
75
|
+
agent_id = kwargs.get("agent_id")
|
|
76
|
+
|
|
77
|
+
if agent_id:
|
|
78
|
+
context["agent"] = self.get_agent_for_user(agent_id)
|
|
79
|
+
context["is_new"] = False
|
|
80
|
+
else:
|
|
81
|
+
context["agent"] = None
|
|
82
|
+
context["is_new"] = True
|
|
83
|
+
|
|
84
|
+
# Configuration for the builder agent
|
|
85
|
+
context["builder_agent_key"] = "agent-builder"
|
|
86
|
+
|
|
87
|
+
return context
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AgentTestView(LoginRequiredMixin, AgentAccessMixin, TemplateView):
|
|
91
|
+
"""Full-screen agent testing interface."""
|
|
92
|
+
|
|
93
|
+
template_name = "django_agent_studio/test.html"
|
|
94
|
+
|
|
95
|
+
def get_context_data(self, **kwargs):
|
|
96
|
+
context = super().get_context_data(**kwargs)
|
|
97
|
+
agent_id = kwargs.get("agent_id")
|
|
98
|
+
context["agent"] = self.get_agent_for_user(agent_id)
|
|
99
|
+
return context
|
|
100
|
+
|