django-cfg 1.1.82__py3-none-any.whl → 1.2.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.
Files changed (244) hide show
  1. django_cfg/__init__.py +20 -448
  2. django_cfg/apps/accounts/README.md +3 -3
  3. django_cfg/apps/accounts/admin/__init__.py +0 -2
  4. django_cfg/apps/accounts/admin/activity.py +2 -9
  5. django_cfg/apps/accounts/admin/filters.py +0 -42
  6. django_cfg/apps/accounts/admin/inlines.py +8 -8
  7. django_cfg/apps/accounts/admin/otp.py +5 -5
  8. django_cfg/apps/accounts/admin/registration_source.py +1 -8
  9. django_cfg/apps/accounts/admin/user.py +12 -20
  10. django_cfg/apps/accounts/managers/user_manager.py +2 -129
  11. django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
  12. django_cfg/apps/accounts/models.py +3 -123
  13. django_cfg/apps/accounts/serializers/otp.py +40 -44
  14. django_cfg/apps/accounts/serializers/profile.py +0 -2
  15. django_cfg/apps/accounts/services/otp_service.py +98 -186
  16. django_cfg/apps/accounts/signals.py +25 -15
  17. django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
  18. django_cfg/apps/accounts/views/otp.py +35 -36
  19. django_cfg/apps/agents/README.md +129 -0
  20. django_cfg/apps/agents/__init__.py +68 -0
  21. django_cfg/apps/agents/admin/__init__.py +17 -0
  22. django_cfg/apps/agents/admin/execution_admin.py +460 -0
  23. django_cfg/apps/agents/admin/registry_admin.py +360 -0
  24. django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
  25. django_cfg/apps/agents/apps.py +29 -0
  26. django_cfg/apps/agents/core/__init__.py +20 -0
  27. django_cfg/apps/agents/core/agent.py +281 -0
  28. django_cfg/apps/agents/core/dependencies.py +154 -0
  29. django_cfg/apps/agents/core/exceptions.py +66 -0
  30. django_cfg/apps/agents/core/models.py +106 -0
  31. django_cfg/apps/agents/core/orchestrator.py +391 -0
  32. django_cfg/apps/agents/examples/__init__.py +3 -0
  33. django_cfg/apps/agents/examples/simple_example.py +161 -0
  34. django_cfg/apps/agents/integration/__init__.py +14 -0
  35. django_cfg/apps/agents/integration/middleware.py +80 -0
  36. django_cfg/apps/agents/integration/registry.py +345 -0
  37. django_cfg/apps/agents/integration/signals.py +50 -0
  38. django_cfg/apps/agents/management/__init__.py +3 -0
  39. django_cfg/apps/agents/management/commands/__init__.py +3 -0
  40. django_cfg/apps/agents/management/commands/create_agent.py +365 -0
  41. django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
  42. django_cfg/apps/agents/managers/__init__.py +23 -0
  43. django_cfg/apps/agents/managers/execution.py +236 -0
  44. django_cfg/apps/agents/managers/registry.py +254 -0
  45. django_cfg/apps/agents/managers/toolsets.py +496 -0
  46. django_cfg/apps/agents/migrations/0001_initial.py +286 -0
  47. django_cfg/apps/agents/migrations/__init__.py +5 -0
  48. django_cfg/apps/agents/models/__init__.py +15 -0
  49. django_cfg/apps/agents/models/execution.py +215 -0
  50. django_cfg/apps/agents/models/registry.py +220 -0
  51. django_cfg/apps/agents/models/toolsets.py +305 -0
  52. django_cfg/apps/agents/patterns/__init__.py +24 -0
  53. django_cfg/apps/agents/patterns/content_agents.py +234 -0
  54. django_cfg/apps/agents/toolsets/__init__.py +15 -0
  55. django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
  56. django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
  57. django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
  58. django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
  59. django_cfg/apps/agents/urls.py +46 -0
  60. django_cfg/apps/knowbase/README.md +150 -0
  61. django_cfg/apps/knowbase/__init__.py +27 -0
  62. django_cfg/apps/knowbase/admin/__init__.py +23 -0
  63. django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
  64. django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
  65. django_cfg/apps/knowbase/admin/document_admin.py +650 -0
  66. django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
  67. django_cfg/apps/knowbase/apps.py +81 -0
  68. django_cfg/apps/knowbase/config/README.md +176 -0
  69. django_cfg/apps/knowbase/config/__init__.py +51 -0
  70. django_cfg/apps/knowbase/config/constance_fields.py +186 -0
  71. django_cfg/apps/knowbase/config/constance_settings.py +200 -0
  72. django_cfg/apps/knowbase/config/settings.py +444 -0
  73. django_cfg/apps/knowbase/examples/__init__.py +3 -0
  74. django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
  75. django_cfg/apps/knowbase/management/__init__.py +0 -0
  76. django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
  77. django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
  78. django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
  79. django_cfg/apps/knowbase/managers/__init__.py +22 -0
  80. django_cfg/apps/knowbase/managers/archive.py +426 -0
  81. django_cfg/apps/knowbase/managers/base.py +32 -0
  82. django_cfg/apps/knowbase/managers/chat.py +141 -0
  83. django_cfg/apps/knowbase/managers/document.py +203 -0
  84. django_cfg/apps/knowbase/managers/external_data.py +471 -0
  85. django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
  86. django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
  87. django_cfg/apps/knowbase/migrations/__init__.py +5 -0
  88. django_cfg/apps/knowbase/mixins/__init__.py +15 -0
  89. django_cfg/apps/knowbase/mixins/config.py +108 -0
  90. django_cfg/apps/knowbase/mixins/creator.py +81 -0
  91. django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
  92. django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
  93. django_cfg/apps/knowbase/mixins/service.py +362 -0
  94. django_cfg/apps/knowbase/models/__init__.py +41 -0
  95. django_cfg/apps/knowbase/models/archive.py +599 -0
  96. django_cfg/apps/knowbase/models/base.py +58 -0
  97. django_cfg/apps/knowbase/models/chat.py +157 -0
  98. django_cfg/apps/knowbase/models/document.py +267 -0
  99. django_cfg/apps/knowbase/models/external_data.py +376 -0
  100. django_cfg/apps/knowbase/serializers/__init__.py +68 -0
  101. django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
  102. django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
  103. django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
  104. django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
  105. django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
  106. django_cfg/apps/knowbase/services/__init__.py +40 -0
  107. django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
  108. django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
  109. django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
  110. django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
  111. django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
  112. django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
  113. django_cfg/apps/knowbase/services/base.py +53 -0
  114. django_cfg/apps/knowbase/services/chat_service.py +239 -0
  115. django_cfg/apps/knowbase/services/document_service.py +144 -0
  116. django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
  117. django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
  118. django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
  119. django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
  120. django_cfg/apps/knowbase/services/embedding/models.py +229 -0
  121. django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
  122. django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
  123. django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
  124. django_cfg/apps/knowbase/services/search_service.py +293 -0
  125. django_cfg/apps/knowbase/signals/__init__.py +21 -0
  126. django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
  127. django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
  128. django_cfg/apps/knowbase/signals/document_signals.py +143 -0
  129. django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
  130. django_cfg/apps/knowbase/tasks/__init__.py +39 -0
  131. django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
  132. django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
  133. django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
  134. django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
  135. django_cfg/apps/knowbase/urls.py +43 -0
  136. django_cfg/apps/knowbase/utils/__init__.py +12 -0
  137. django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
  138. django_cfg/apps/knowbase/utils/text_processing.py +375 -0
  139. django_cfg/apps/knowbase/utils/validation.py +99 -0
  140. django_cfg/apps/knowbase/views/__init__.py +28 -0
  141. django_cfg/apps/knowbase/views/archive_views.py +469 -0
  142. django_cfg/apps/knowbase/views/base.py +49 -0
  143. django_cfg/apps/knowbase/views/chat_views.py +181 -0
  144. django_cfg/apps/knowbase/views/document_views.py +183 -0
  145. django_cfg/apps/knowbase/views/public_views.py +129 -0
  146. django_cfg/apps/leads/admin.py +70 -0
  147. django_cfg/apps/newsletter/admin.py +234 -0
  148. django_cfg/apps/newsletter/admin_filters.py +124 -0
  149. django_cfg/apps/support/admin.py +196 -0
  150. django_cfg/apps/support/admin_filters.py +71 -0
  151. django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
  152. django_cfg/apps/urls.py +5 -4
  153. django_cfg/cli/README.md +1 -1
  154. django_cfg/cli/commands/create_project.py +2 -2
  155. django_cfg/cli/commands/info.py +1 -1
  156. django_cfg/config.py +44 -0
  157. django_cfg/core/config.py +29 -82
  158. django_cfg/core/environment.py +1 -1
  159. django_cfg/core/generation.py +19 -107
  160. django_cfg/{integration.py → core/integration.py} +18 -16
  161. django_cfg/core/validation.py +1 -1
  162. django_cfg/management/__init__.py +1 -1
  163. django_cfg/management/commands/__init__.py +1 -1
  164. django_cfg/management/commands/auto_generate.py +482 -0
  165. django_cfg/management/commands/migrator.py +19 -101
  166. django_cfg/management/commands/test_email.py +1 -1
  167. django_cfg/middleware/README.md +0 -158
  168. django_cfg/middleware/__init__.py +0 -2
  169. django_cfg/middleware/user_activity.py +3 -3
  170. django_cfg/models/api.py +145 -0
  171. django_cfg/models/base.py +287 -0
  172. django_cfg/models/cache.py +4 -4
  173. django_cfg/models/constance.py +25 -88
  174. django_cfg/models/database.py +9 -9
  175. django_cfg/models/drf.py +3 -36
  176. django_cfg/models/email.py +163 -0
  177. django_cfg/models/environment.py +276 -0
  178. django_cfg/models/limits.py +1 -1
  179. django_cfg/models/logging.py +366 -0
  180. django_cfg/models/revolution.py +41 -2
  181. django_cfg/models/security.py +125 -0
  182. django_cfg/models/services.py +1 -1
  183. django_cfg/modules/__init__.py +2 -56
  184. django_cfg/modules/base.py +78 -52
  185. django_cfg/modules/django_currency/service.py +2 -2
  186. django_cfg/modules/django_email.py +2 -2
  187. django_cfg/modules/django_health.py +267 -0
  188. django_cfg/modules/django_llm/llm/client.py +79 -17
  189. django_cfg/modules/django_llm/translator/translator.py +2 -2
  190. django_cfg/modules/django_logger.py +2 -2
  191. django_cfg/modules/django_ngrok.py +2 -2
  192. django_cfg/modules/django_tasks.py +68 -3
  193. django_cfg/modules/django_telegram.py +3 -3
  194. django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
  195. django_cfg/modules/django_twilio/service.py +2 -2
  196. django_cfg/modules/django_twilio/simple_service.py +2 -2
  197. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  198. django_cfg/modules/django_unfold/__init__.py +69 -0
  199. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  200. django_cfg/modules/django_unfold/dashboard.py +278 -0
  201. django_cfg/modules/django_unfold/icons/README.md +145 -0
  202. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  203. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  204. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  205. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  206. django_cfg/modules/django_unfold/models/config.py +601 -0
  207. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  208. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  209. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  210. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  211. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  212. django_cfg/modules/django_unfold/utils.py +140 -0
  213. django_cfg/registry/__init__.py +23 -0
  214. django_cfg/registry/core.py +61 -0
  215. django_cfg/registry/exceptions.py +11 -0
  216. django_cfg/registry/modules.py +12 -0
  217. django_cfg/registry/services.py +26 -0
  218. django_cfg/registry/third_party.py +52 -0
  219. django_cfg/routing/__init__.py +19 -0
  220. django_cfg/routing/callbacks.py +198 -0
  221. django_cfg/routing/routers.py +48 -0
  222. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  223. django_cfg/templatetags/__init__.py +0 -0
  224. django_cfg/templatetags/django_cfg.py +33 -0
  225. django_cfg/urls.py +33 -0
  226. django_cfg/utils/path_resolution.py +1 -1
  227. django_cfg/utils/smart_defaults.py +7 -61
  228. django_cfg/utils/toolkit.py +663 -0
  229. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  230. django_cfg-1.2.0.dist-info/RECORD +441 -0
  231. django_cfg/archive/django_sample.zip +0 -0
  232. django_cfg/models/unfold.py +0 -271
  233. django_cfg/modules/unfold/__init__.py +0 -29
  234. django_cfg/modules/unfold/dashboard.py +0 -318
  235. django_cfg/pyproject.toml +0 -370
  236. django_cfg/routers.py +0 -83
  237. django_cfg-1.1.82.dist-info/RECORD +0 -278
  238. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  239. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  240. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  241. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  242. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  243. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  244. {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,360 @@
1
+ """
2
+ Registry admin interfaces with Unfold optimization.
3
+ """
4
+
5
+ from django.contrib import admin, messages
6
+ from django.utils.html import format_html
7
+ from django.urls import reverse
8
+ from django.utils.safestring import mark_safe
9
+ from django.db import models
10
+ from django.db.models import Count, Avg, Sum, Q
11
+ from django.utils import timezone
12
+ from django.db.models.fields.json import JSONField
13
+ from datetime import timedelta
14
+ from django_json_widget.widgets import JSONEditorWidget
15
+ from unfold.admin import ModelAdmin, TabularInline
16
+ from unfold.decorators import display, action
17
+ from unfold.enums import ActionVariant
18
+ from unfold.contrib.filters.admin import AutocompleteSelectFilter, AutocompleteSelectMultipleFilter
19
+ from unfold.contrib.forms.widgets import WysiwygWidget
20
+
21
+ from ..models.registry import AgentDefinition, AgentTemplate
22
+ from ..models.execution import AgentExecution
23
+
24
+
25
+ class AgentExecutionInline(TabularInline):
26
+ """Inline for agent executions with Unfold styling."""
27
+
28
+ model = AgentExecution
29
+ verbose_name = "Agent Execution"
30
+ verbose_name_plural = "🚀 Recent Executions (Read-only)"
31
+ extra = 0
32
+ max_num = 5 # Show only last 5 executions
33
+ can_delete = False
34
+ show_change_link = True
35
+
36
+ def has_add_permission(self, request, obj=None):
37
+ return False
38
+
39
+ def has_change_permission(self, request, obj=None):
40
+ return False
41
+
42
+ def has_delete_permission(self, request, obj=None):
43
+ return False
44
+
45
+ fields = [
46
+ 'user', 'status_badge_inline', 'execution_time_display',
47
+ 'tokens_used', 'cost_display_inline', 'created_at'
48
+ ]
49
+ readonly_fields = [
50
+ 'user', 'status_badge_inline', 'execution_time_display',
51
+ 'tokens_used', 'cost_display_inline', 'created_at'
52
+ ]
53
+
54
+ # Unfold specific options
55
+ hide_title = False
56
+ classes = ['collapse']
57
+
58
+ @display(description="Status")
59
+ def status_badge_inline(self, obj):
60
+ """Status badge for inline display."""
61
+ colors = {
62
+ 'pending': 'bg-yellow-100 text-yellow-800',
63
+ 'running': 'bg-blue-100 text-blue-800',
64
+ 'completed': 'bg-green-100 text-green-800',
65
+ 'failed': 'bg-red-100 text-red-800',
66
+ 'cancelled': 'bg-gray-100 text-gray-800'
67
+ }
68
+ color_class = colors.get(obj.status, 'bg-gray-100 text-gray-800')
69
+ return format_html(
70
+ '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {}">{}</span>',
71
+ color_class, obj.get_status_display()
72
+ )
73
+
74
+ @display(description="Execution Time")
75
+ def execution_time_display(self, obj):
76
+ """Execution time display for inline."""
77
+ if obj.execution_time:
78
+ return f"{obj.execution_time:.2f}s"
79
+ return "-"
80
+
81
+ @display(description="Cost")
82
+ def cost_display_inline(self, obj):
83
+ """Cost display for inline."""
84
+ if obj.cost:
85
+ return f"${obj.cost:.4f}"
86
+ return "-"
87
+
88
+ def get_queryset(self, request):
89
+ """Optimize queryset for inline display."""
90
+ return super().get_queryset(request).select_related('user').order_by('-created_at')
91
+
92
+
93
+ @admin.register(AgentDefinition)
94
+ class AgentDefinitionAdmin(ModelAdmin):
95
+ """Admin interface for AgentDefinition with Unfold styling."""
96
+
97
+ list_display = [
98
+ 'name_display', 'display_name', 'category_badge', 'status_badges',
99
+ 'usage_stats', 'performance_indicator', 'created_by', 'created_at'
100
+ ]
101
+ ordering = ['-created_at']
102
+ inlines = [AgentExecutionInline]
103
+ list_filter = [
104
+ 'is_active', 'is_public', 'category', 'enable_caching', 'created_at',
105
+ ('created_by', AutocompleteSelectFilter)
106
+ ]
107
+ search_fields = ['name', 'display_name', 'description', 'instructions']
108
+ autocomplete_fields = ['created_by', 'allowed_users', 'allowed_groups']
109
+ readonly_fields = [
110
+ 'usage_count', 'last_used_at', 'created_at', 'updated_at',
111
+ 'performance_metrics', 'recent_executions_summary'
112
+ ]
113
+
114
+ # Unfold form field overrides
115
+ formfield_overrides = {
116
+ models.TextField: {"widget": WysiwygWidget},
117
+ JSONField: {"widget": JSONEditorWidget},
118
+ }
119
+
120
+ fieldsets = (
121
+ ("🤖 Basic Information", {
122
+ 'fields': ('name', 'display_name', 'description', 'category', 'tags'),
123
+ 'classes': ('tab',)
124
+ }),
125
+ ("⚙️ Configuration", {
126
+ 'fields': ('instructions', 'deps_type', 'output_type', 'model'),
127
+ 'classes': ('tab',)
128
+ }),
129
+ ("🔧 Execution Settings", {
130
+ 'fields': ('timeout', 'max_retries', 'enable_caching'),
131
+ 'classes': ('tab',)
132
+ }),
133
+ ("🛠️ Tools & Advanced", {
134
+ 'fields': ('tools_config',),
135
+ 'classes': ('tab', 'collapse')
136
+ }),
137
+ ("🔐 Access Control", {
138
+ 'fields': ('is_active', 'is_public', 'allowed_users', 'allowed_groups'),
139
+ 'classes': ('tab',)
140
+ }),
141
+ ("📊 Statistics", {
142
+ 'fields': ('usage_count', 'last_used_at', 'performance_metrics', 'recent_executions_summary'),
143
+ 'classes': ('tab', 'collapse')
144
+ }),
145
+ ("📝 Metadata", {
146
+ 'fields': ('version', 'created_by', 'created_at', 'updated_at'),
147
+ 'classes': ('tab', 'collapse')
148
+ }),
149
+ )
150
+
151
+ # Unfold actions
152
+ actions = ['activate_agents', 'deactivate_agents', 'make_public', 'make_private']
153
+
154
+ @display(description="Agent Name")
155
+ def name_display(self, obj):
156
+ """Enhanced name display with icon."""
157
+ return format_html(
158
+ '<div class="flex items-center space-x-2">'
159
+ '<span class="text-blue-600 font-medium">{}</span>'
160
+ '</div>',
161
+ obj.name
162
+ )
163
+
164
+ @display(description="Category")
165
+ def category_badge(self, obj):
166
+ """Category with badge styling."""
167
+ if not obj.category:
168
+ return "-"
169
+ return format_html(
170
+ '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-800">{}</span>',
171
+ obj.category
172
+ )
173
+
174
+ @display(description="Status")
175
+ def status_badges(self, obj):
176
+ """Combined status badges."""
177
+ badges = []
178
+
179
+ if obj.is_active:
180
+ badges.append('<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">Active</span>')
181
+ else:
182
+ badges.append('<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-red-100 text-red-800">Inactive</span>')
183
+
184
+ if obj.is_public:
185
+ badges.append('<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">Public</span>')
186
+ else:
187
+ badges.append('<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-800">Private</span>')
188
+
189
+ if obj.enable_caching:
190
+ badges.append('<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">Cached</span>')
191
+
192
+ return format_html('<div class="space-y-1">{}</div>', ''.join(badges))
193
+
194
+ @display(description="Usage Stats")
195
+ def usage_stats(self, obj):
196
+ """Usage statistics display."""
197
+ return format_html(
198
+ '<div class="text-sm">'
199
+ '<div class="font-medium text-gray-900">{} executions</div>'
200
+ '<div class="text-gray-500">{}</div>'
201
+ '</div>',
202
+ obj.usage_count,
203
+ f"Last used: {obj.last_used_at.strftime('%m/%d %H:%M')}" if obj.last_used_at else "Never used"
204
+ )
205
+
206
+ @display(description="Performance")
207
+ def performance_indicator(self, obj):
208
+ """Performance indicator based on recent executions."""
209
+ # This would need to be calculated from related executions
210
+ return format_html(
211
+ '<div class="flex items-center space-x-1">'
212
+ '<div class="w-2 h-2 bg-green-400 rounded-full"></div>'
213
+ '<span class="text-xs text-gray-600">Good</span>'
214
+ '</div>'
215
+ )
216
+
217
+ @display(description="Performance Metrics")
218
+ def performance_metrics(self, obj):
219
+ """Detailed performance metrics."""
220
+ # This would calculate from related AgentExecution objects
221
+ return format_html(
222
+ '<div class="space-y-2">'
223
+ '<div>Avg Execution Time: <span class="font-mono">-</span></div>'
224
+ '<div>Success Rate: <span class="font-mono">-</span></div>'
225
+ '<div>Avg Cost: <span class="font-mono">-</span></div>'
226
+ '</div>'
227
+ )
228
+
229
+ @display(description="Recent Executions")
230
+ def recent_executions_summary(self, obj):
231
+ """Summary of recent executions."""
232
+ return format_html(
233
+ '<div class="text-sm text-gray-600">'
234
+ 'See inline executions below for recent activity'
235
+ '</div>'
236
+ )
237
+
238
+ @action(description="Activate selected agents", icon="play_arrow", variant=ActionVariant.SUCCESS)
239
+ def activate_agents(self, request, queryset):
240
+ """Activate selected agents."""
241
+ updated = queryset.update(is_active=True)
242
+ messages.success(request, f"Activated {updated} agents.")
243
+
244
+ @action(description="Deactivate selected agents", icon="pause", variant=ActionVariant.WARNING)
245
+ def deactivate_agents(self, request, queryset):
246
+ """Deactivate selected agents."""
247
+ updated = queryset.update(is_active=False)
248
+ messages.warning(request, f"Deactivated {updated} agents.")
249
+
250
+ @action(description="Make public", icon="public", variant=ActionVariant.INFO)
251
+ def make_public(self, request, queryset):
252
+ """Make selected agents public."""
253
+ updated = queryset.update(is_public=True)
254
+ messages.info(request, f"Made {updated} agents public.")
255
+
256
+ @action(description="Make private", icon="lock", variant=ActionVariant.DEFAULT)
257
+ def make_private(self, request, queryset):
258
+ """Make selected agents private."""
259
+ updated = queryset.update(is_public=False)
260
+ messages.info(request, f"Made {updated} agents private.")
261
+
262
+ def get_queryset(self, request):
263
+ """Optimize queryset."""
264
+ return super().get_queryset(request).select_related('created_by').prefetch_related('allowed_users', 'allowed_groups')
265
+
266
+ def save_model(self, request, obj, form, change):
267
+ """Set created_by on new objects."""
268
+ if not change:
269
+ obj.created_by = request.user
270
+ super().save_model(request, obj, form, change)
271
+
272
+
273
+ @admin.register(AgentTemplate)
274
+ class AgentTemplateAdmin(ModelAdmin):
275
+ """Admin interface for AgentTemplate with Unfold styling."""
276
+
277
+ list_display = ['name_display', 'category_badge', 'status_badge', 'use_cases_preview', 'created_by', 'created_at']
278
+ ordering = ['-created_at']
279
+ list_filter = [
280
+ 'category', 'is_active', 'created_at',
281
+ ('created_by', AutocompleteSelectFilter)
282
+ ]
283
+ search_fields = ['name', 'description', 'use_cases']
284
+ autocomplete_fields = ['created_by']
285
+ readonly_fields = ['created_at', 'updated_at']
286
+
287
+ # Unfold form field overrides
288
+ formfield_overrides = {
289
+ models.TextField: {"widget": WysiwygWidget},
290
+ JSONField: {"widget": JSONEditorWidget},
291
+ }
292
+
293
+ fieldsets = (
294
+ ("📋 Template Information", {
295
+ 'fields': ('name', 'description', 'category', 'use_cases'),
296
+ 'classes': ('tab',)
297
+ }),
298
+ ("⚙️ Template Configuration", {
299
+ 'fields': ('template_config', 'default_instructions', 'recommended_model'),
300
+ 'classes': ('tab',)
301
+ }),
302
+ ("🔧 Settings", {
303
+ 'fields': ('is_active', 'created_by', 'created_at', 'updated_at'),
304
+ 'classes': ('tab',)
305
+ }),
306
+ )
307
+
308
+ actions = ['activate_templates', 'deactivate_templates']
309
+
310
+ @display(description="Template Name")
311
+ def name_display(self, obj):
312
+ """Enhanced name display."""
313
+ return format_html(
314
+ '<div class="flex items-center space-x-2">'
315
+ '<span class="text-purple-600 font-medium">{}</span>'
316
+ '</div>',
317
+ obj.name
318
+ )
319
+
320
+ @display(description="Category")
321
+ def category_badge(self, obj):
322
+ """Category badge."""
323
+ if not obj.category:
324
+ return "-"
325
+ return format_html(
326
+ '<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800">{}</span>',
327
+ obj.category
328
+ )
329
+
330
+ @display(description="Status", boolean=True)
331
+ def status_badge(self, obj):
332
+ """Status badge."""
333
+ return obj.is_active
334
+
335
+ @display(description="Use Cases")
336
+ def use_cases_preview(self, obj):
337
+ """Preview of use cases."""
338
+ if not obj.use_cases:
339
+ return "-"
340
+ preview = obj.use_cases[:100] + "..." if len(obj.use_cases) > 100 else obj.use_cases
341
+ return format_html(
342
+ '<div class="text-sm text-gray-600 max-w-xs truncate">{}</div>',
343
+ preview
344
+ )
345
+
346
+ @action(description="Activate templates", icon="play_arrow", variant=ActionVariant.SUCCESS)
347
+ def activate_templates(self, request, queryset):
348
+ """Activate selected templates."""
349
+ updated = queryset.update(is_active=True)
350
+ messages.success(request, f"Activated {updated} templates.")
351
+
352
+ @action(description="Deactivate templates", icon="pause", variant=ActionVariant.WARNING)
353
+ def deactivate_templates(self, request, queryset):
354
+ """Deactivate selected templates."""
355
+ updated = queryset.update(is_active=False)
356
+ messages.warning(request, f"Deactivated {updated} templates.")
357
+
358
+ def get_queryset(self, request):
359
+ """Optimize queryset."""
360
+ return super().get_queryset(request).select_related('created_by')