django-cfg 1.1.81__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.
- django_cfg/__init__.py +20 -448
- django_cfg/apps/accounts/README.md +3 -3
- django_cfg/apps/accounts/admin/__init__.py +0 -2
- django_cfg/apps/accounts/admin/activity.py +2 -9
- django_cfg/apps/accounts/admin/filters.py +0 -42
- django_cfg/apps/accounts/admin/inlines.py +8 -8
- django_cfg/apps/accounts/admin/otp.py +5 -5
- django_cfg/apps/accounts/admin/registration_source.py +1 -8
- django_cfg/apps/accounts/admin/user.py +12 -20
- django_cfg/apps/accounts/managers/user_manager.py +2 -129
- django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
- django_cfg/apps/accounts/models.py +3 -123
- django_cfg/apps/accounts/serializers/otp.py +40 -44
- django_cfg/apps/accounts/serializers/profile.py +0 -2
- django_cfg/apps/accounts/services/otp_service.py +98 -186
- django_cfg/apps/accounts/signals.py +25 -15
- django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
- django_cfg/apps/accounts/views/otp.py +35 -36
- django_cfg/apps/agents/README.md +129 -0
- django_cfg/apps/agents/__init__.py +68 -0
- django_cfg/apps/agents/admin/__init__.py +17 -0
- django_cfg/apps/agents/admin/execution_admin.py +460 -0
- django_cfg/apps/agents/admin/registry_admin.py +360 -0
- django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
- django_cfg/apps/agents/apps.py +29 -0
- django_cfg/apps/agents/core/__init__.py +20 -0
- django_cfg/apps/agents/core/agent.py +281 -0
- django_cfg/apps/agents/core/dependencies.py +154 -0
- django_cfg/apps/agents/core/exceptions.py +66 -0
- django_cfg/apps/agents/core/models.py +106 -0
- django_cfg/apps/agents/core/orchestrator.py +391 -0
- django_cfg/apps/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- django_cfg/apps/agents/integration/__init__.py +14 -0
- django_cfg/apps/agents/integration/middleware.py +80 -0
- django_cfg/apps/agents/integration/registry.py +345 -0
- django_cfg/apps/agents/integration/signals.py +50 -0
- django_cfg/apps/agents/management/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/create_agent.py +365 -0
- django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
- django_cfg/apps/agents/managers/__init__.py +23 -0
- django_cfg/apps/agents/managers/execution.py +236 -0
- django_cfg/apps/agents/managers/registry.py +254 -0
- django_cfg/apps/agents/managers/toolsets.py +496 -0
- django_cfg/apps/agents/migrations/0001_initial.py +286 -0
- django_cfg/apps/agents/migrations/__init__.py +5 -0
- django_cfg/apps/agents/models/__init__.py +15 -0
- django_cfg/apps/agents/models/execution.py +215 -0
- django_cfg/apps/agents/models/registry.py +220 -0
- django_cfg/apps/agents/models/toolsets.py +305 -0
- django_cfg/apps/agents/patterns/__init__.py +24 -0
- django_cfg/apps/agents/patterns/content_agents.py +234 -0
- django_cfg/apps/agents/toolsets/__init__.py +15 -0
- django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
- django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
- django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
- django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
- django_cfg/apps/agents/urls.py +46 -0
- django_cfg/apps/knowbase/README.md +150 -0
- django_cfg/apps/knowbase/__init__.py +27 -0
- django_cfg/apps/knowbase/admin/__init__.py +23 -0
- django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
- django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
- django_cfg/apps/knowbase/admin/document_admin.py +650 -0
- django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
- django_cfg/apps/knowbase/apps.py +81 -0
- django_cfg/apps/knowbase/config/README.md +176 -0
- django_cfg/apps/knowbase/config/__init__.py +51 -0
- django_cfg/apps/knowbase/config/constance_fields.py +186 -0
- django_cfg/apps/knowbase/config/constance_settings.py +200 -0
- django_cfg/apps/knowbase/config/settings.py +444 -0
- django_cfg/apps/knowbase/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- django_cfg/apps/knowbase/management/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
- django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
- django_cfg/apps/knowbase/managers/__init__.py +22 -0
- django_cfg/apps/knowbase/managers/archive.py +426 -0
- django_cfg/apps/knowbase/managers/base.py +32 -0
- django_cfg/apps/knowbase/managers/chat.py +141 -0
- django_cfg/apps/knowbase/managers/document.py +203 -0
- django_cfg/apps/knowbase/managers/external_data.py +471 -0
- django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
- django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
- django_cfg/apps/knowbase/migrations/__init__.py +5 -0
- django_cfg/apps/knowbase/mixins/__init__.py +15 -0
- django_cfg/apps/knowbase/mixins/config.py +108 -0
- django_cfg/apps/knowbase/mixins/creator.py +81 -0
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
- django_cfg/apps/knowbase/mixins/service.py +362 -0
- django_cfg/apps/knowbase/models/__init__.py +41 -0
- django_cfg/apps/knowbase/models/archive.py +599 -0
- django_cfg/apps/knowbase/models/base.py +58 -0
- django_cfg/apps/knowbase/models/chat.py +157 -0
- django_cfg/apps/knowbase/models/document.py +267 -0
- django_cfg/apps/knowbase/models/external_data.py +376 -0
- django_cfg/apps/knowbase/serializers/__init__.py +68 -0
- django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
- django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
- django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
- django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
- django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
- django_cfg/apps/knowbase/services/__init__.py +40 -0
- django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
- django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
- django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
- django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
- django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
- django_cfg/apps/knowbase/services/base.py +53 -0
- django_cfg/apps/knowbase/services/chat_service.py +239 -0
- django_cfg/apps/knowbase/services/document_service.py +144 -0
- django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
- django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
- django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
- django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
- django_cfg/apps/knowbase/services/embedding/models.py +229 -0
- django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
- django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
- django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
- django_cfg/apps/knowbase/services/search_service.py +293 -0
- django_cfg/apps/knowbase/signals/__init__.py +21 -0
- django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
- django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
- django_cfg/apps/knowbase/signals/document_signals.py +143 -0
- django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
- django_cfg/apps/knowbase/tasks/__init__.py +39 -0
- django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
- django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
- django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
- django_cfg/apps/knowbase/urls.py +43 -0
- django_cfg/apps/knowbase/utils/__init__.py +12 -0
- django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
- django_cfg/apps/knowbase/utils/text_processing.py +375 -0
- django_cfg/apps/knowbase/utils/validation.py +99 -0
- django_cfg/apps/knowbase/views/__init__.py +28 -0
- django_cfg/apps/knowbase/views/archive_views.py +469 -0
- django_cfg/apps/knowbase/views/base.py +49 -0
- django_cfg/apps/knowbase/views/chat_views.py +181 -0
- django_cfg/apps/knowbase/views/document_views.py +183 -0
- django_cfg/apps/knowbase/views/public_views.py +129 -0
- django_cfg/apps/leads/admin.py +70 -0
- django_cfg/apps/newsletter/admin.py +234 -0
- django_cfg/apps/newsletter/admin_filters.py +124 -0
- django_cfg/apps/support/admin.py +196 -0
- django_cfg/apps/support/admin_filters.py +71 -0
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/apps/urls.py +5 -4
- django_cfg/cli/README.md +1 -1
- django_cfg/cli/commands/create_project.py +2 -2
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/config.py +44 -0
- django_cfg/core/config.py +29 -82
- django_cfg/core/environment.py +1 -1
- django_cfg/core/generation.py +19 -107
- django_cfg/{integration.py → core/integration.py} +18 -16
- django_cfg/core/validation.py +1 -1
- django_cfg/management/__init__.py +1 -1
- django_cfg/management/commands/__init__.py +1 -1
- django_cfg/management/commands/auto_generate.py +482 -0
- django_cfg/management/commands/migrator.py +19 -101
- django_cfg/management/commands/test_email.py +1 -1
- django_cfg/middleware/README.md +0 -158
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/api.py +145 -0
- django_cfg/models/base.py +287 -0
- django_cfg/models/cache.py +4 -4
- django_cfg/models/constance.py +25 -88
- django_cfg/models/database.py +9 -9
- django_cfg/models/drf.py +3 -36
- django_cfg/models/email.py +163 -0
- django_cfg/models/environment.py +276 -0
- django_cfg/models/limits.py +1 -1
- django_cfg/models/logging.py +366 -0
- django_cfg/models/revolution.py +41 -2
- django_cfg/models/security.py +125 -0
- django_cfg/models/services.py +1 -1
- django_cfg/modules/__init__.py +2 -56
- django_cfg/modules/base.py +78 -52
- django_cfg/modules/django_currency/service.py +2 -2
- django_cfg/modules/django_email.py +2 -2
- django_cfg/modules/django_health.py +267 -0
- django_cfg/modules/django_llm/llm/client.py +79 -17
- django_cfg/modules/django_llm/translator/translator.py +2 -2
- django_cfg/modules/django_logger.py +2 -2
- django_cfg/modules/django_ngrok.py +2 -2
- django_cfg/modules/django_tasks.py +68 -3
- django_cfg/modules/django_telegram.py +3 -3
- django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
- django_cfg/modules/django_twilio/service.py +2 -2
- django_cfg/modules/django_twilio/simple_service.py +2 -2
- django_cfg/modules/django_twilio/templates/guide.md +266 -0
- django_cfg/modules/django_twilio/twilio_service.py +2 -2
- django_cfg/modules/django_unfold/__init__.py +69 -0
- django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
- django_cfg/modules/django_unfold/dashboard.py +278 -0
- django_cfg/modules/django_unfold/icons/README.md +145 -0
- django_cfg/modules/django_unfold/icons/__init__.py +12 -0
- django_cfg/modules/django_unfold/icons/constants.py +2851 -0
- django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
- django_cfg/modules/django_unfold/models/__init__.py +42 -0
- django_cfg/modules/django_unfold/models/config.py +601 -0
- django_cfg/modules/django_unfold/models/dashboard.py +206 -0
- django_cfg/modules/django_unfold/models/dropdown.py +40 -0
- django_cfg/modules/django_unfold/models/navigation.py +73 -0
- django_cfg/modules/django_unfold/models/tabs.py +25 -0
- django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
- django_cfg/modules/django_unfold/utils.py +140 -0
- django_cfg/registry/__init__.py +23 -0
- django_cfg/registry/core.py +61 -0
- django_cfg/registry/exceptions.py +11 -0
- django_cfg/registry/modules.py +12 -0
- django_cfg/registry/services.py +26 -0
- django_cfg/registry/third_party.py +52 -0
- django_cfg/routing/__init__.py +19 -0
- django_cfg/routing/callbacks.py +198 -0
- django_cfg/routing/routers.py +48 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
- django_cfg/templatetags/__init__.py +0 -0
- django_cfg/templatetags/django_cfg.py +33 -0
- django_cfg/urls.py +33 -0
- django_cfg/utils/path_resolution.py +1 -1
- django_cfg/utils/smart_defaults.py +7 -61
- django_cfg/utils/toolkit.py +663 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
- django_cfg-1.2.0.dist-info/RECORD +441 -0
- django_cfg/apps/tasks/@docs/README.md +0 -195
- django_cfg/archive/django_sample.zip +0 -0
- django_cfg/models/unfold.py +0 -271
- django_cfg/modules/unfold/__init__.py +0 -29
- django_cfg/modules/unfold/dashboard.py +0 -318
- django_cfg/pyproject.toml +0 -370
- django_cfg/routers.py +0 -83
- django_cfg-1.1.81.dist-info/RECORD +0 -278
- /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
- /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
- /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
- /django_cfg/{version_check.py → utils/version_check.py} +0 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,482 @@
|
|
1
|
+
"""
|
2
|
+
Toolsets 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.toolsets import ToolExecution, ApprovalLog, ToolsetConfiguration
|
22
|
+
|
23
|
+
|
24
|
+
@admin.register(ToolExecution)
|
25
|
+
class ToolExecutionAdmin(ModelAdmin):
|
26
|
+
"""Admin interface for ToolExecution with Unfold styling."""
|
27
|
+
|
28
|
+
list_display = [
|
29
|
+
'id_display', 'tool_name_display', 'toolset_badge', 'status_badge', 'user',
|
30
|
+
'execution_metrics', 'retry_badge', 'created_at'
|
31
|
+
]
|
32
|
+
ordering = ['-created_at']
|
33
|
+
list_filter = [
|
34
|
+
'status', 'tool_name', 'toolset_name', 'created_at',
|
35
|
+
('user', AutocompleteSelectFilter),
|
36
|
+
('agent_execution', AutocompleteSelectFilter)
|
37
|
+
]
|
38
|
+
search_fields = ['tool_name', 'toolset_name', 'user__username', 'arguments', 'result']
|
39
|
+
autocomplete_fields = ['user', 'agent_execution']
|
40
|
+
readonly_fields = [
|
41
|
+
'id', 'execution_time', 'created_at', 'started_at', 'completed_at',
|
42
|
+
'duration_display', 'arguments_preview', 'result_preview', 'error_preview'
|
43
|
+
]
|
44
|
+
|
45
|
+
# Unfold form field overrides
|
46
|
+
formfield_overrides = {
|
47
|
+
models.TextField: {"widget": WysiwygWidget},
|
48
|
+
JSONField: {"widget": JSONEditorWidget},
|
49
|
+
}
|
50
|
+
|
51
|
+
fieldsets = (
|
52
|
+
("🛠️ Tool Info", {
|
53
|
+
'fields': ('id', 'tool_name', 'toolset_name', 'user', 'status'),
|
54
|
+
'classes': ('tab',)
|
55
|
+
}),
|
56
|
+
("📝 Execution Data", {
|
57
|
+
'fields': ('arguments_preview', 'arguments', 'result_preview', 'result', 'error_preview', 'error_message'),
|
58
|
+
'classes': ('tab',)
|
59
|
+
}),
|
60
|
+
("📊 Metrics", {
|
61
|
+
'fields': ('execution_time', 'retry_count'),
|
62
|
+
'classes': ('tab',)
|
63
|
+
}),
|
64
|
+
("🔗 Context", {
|
65
|
+
'fields': ('agent_execution',),
|
66
|
+
'classes': ('tab', 'collapse')
|
67
|
+
}),
|
68
|
+
("⏰ Timestamps", {
|
69
|
+
'fields': ('created_at', 'started_at', 'completed_at', 'duration_display'),
|
70
|
+
'classes': ('tab', 'collapse')
|
71
|
+
}),
|
72
|
+
)
|
73
|
+
|
74
|
+
actions = ['retry_failed_tools', 'clear_errors']
|
75
|
+
|
76
|
+
@display(description="ID")
|
77
|
+
def id_display(self, obj):
|
78
|
+
"""Enhanced ID display."""
|
79
|
+
return format_html(
|
80
|
+
'<span class="font-mono text-sm text-gray-600">#{}</span>',
|
81
|
+
str(obj.id)[:8]
|
82
|
+
)
|
83
|
+
|
84
|
+
@display(description="Tool")
|
85
|
+
def tool_name_display(self, obj):
|
86
|
+
"""Enhanced tool name display."""
|
87
|
+
return format_html(
|
88
|
+
'<div class="flex items-center space-x-2">'
|
89
|
+
'<span class="text-orange-600 font-medium">{}</span>'
|
90
|
+
'</div>',
|
91
|
+
obj.tool_name
|
92
|
+
)
|
93
|
+
|
94
|
+
@display(description="Toolset")
|
95
|
+
def toolset_badge(self, obj):
|
96
|
+
"""Toolset badge."""
|
97
|
+
if not obj.toolset_name:
|
98
|
+
return "-"
|
99
|
+
return format_html(
|
100
|
+
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-amber-100 text-amber-800">{}</span>',
|
101
|
+
obj.toolset_name
|
102
|
+
)
|
103
|
+
|
104
|
+
@display(description="Status")
|
105
|
+
def status_badge(self, obj):
|
106
|
+
"""Status badge with color coding."""
|
107
|
+
colors = {
|
108
|
+
'pending': 'bg-yellow-100 text-yellow-800',
|
109
|
+
'running': 'bg-blue-100 text-blue-800',
|
110
|
+
'completed': 'bg-green-100 text-green-800',
|
111
|
+
'failed': 'bg-red-100 text-red-800',
|
112
|
+
'cancelled': 'bg-gray-100 text-gray-800'
|
113
|
+
}
|
114
|
+
color_class = colors.get(obj.status, 'bg-gray-100 text-gray-800')
|
115
|
+
return format_html(
|
116
|
+
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {}">{}</span>',
|
117
|
+
color_class, obj.get_status_display()
|
118
|
+
)
|
119
|
+
|
120
|
+
@display(description="Metrics")
|
121
|
+
def execution_metrics(self, obj):
|
122
|
+
"""Combined execution metrics."""
|
123
|
+
return format_html(
|
124
|
+
'<div class="text-sm space-y-1">'
|
125
|
+
'<div><span class="font-medium">Time:</span> {}</div>'
|
126
|
+
'</div>',
|
127
|
+
f"{obj.execution_time:.3f}s" if obj.execution_time else "-"
|
128
|
+
)
|
129
|
+
|
130
|
+
@display(description="Retries")
|
131
|
+
def retry_badge(self, obj):
|
132
|
+
"""Retry count badge."""
|
133
|
+
if obj.retry_count > 0:
|
134
|
+
return format_html(
|
135
|
+
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-orange-100 text-orange-800">{}</span>',
|
136
|
+
obj.retry_count
|
137
|
+
)
|
138
|
+
return format_html(
|
139
|
+
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">0</span>'
|
140
|
+
)
|
141
|
+
|
142
|
+
@display(description="Duration")
|
143
|
+
def duration_display(self, obj):
|
144
|
+
"""Display execution duration."""
|
145
|
+
if obj.duration:
|
146
|
+
return f"{obj.duration:.3f}s"
|
147
|
+
return "-"
|
148
|
+
|
149
|
+
@display(description="Arguments Preview")
|
150
|
+
def arguments_preview(self, obj):
|
151
|
+
"""Preview of arguments."""
|
152
|
+
if not obj.arguments:
|
153
|
+
return "-"
|
154
|
+
preview = str(obj.arguments)[:200] + "..." if len(str(obj.arguments)) > 200 else str(obj.arguments)
|
155
|
+
return format_html(
|
156
|
+
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
157
|
+
preview
|
158
|
+
)
|
159
|
+
|
160
|
+
@display(description="Result Preview")
|
161
|
+
def result_preview(self, obj):
|
162
|
+
"""Preview of result."""
|
163
|
+
if not obj.result:
|
164
|
+
return "-"
|
165
|
+
preview = str(obj.result)[:200] + "..." if len(str(obj.result)) > 200 else str(obj.result)
|
166
|
+
return format_html(
|
167
|
+
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
168
|
+
preview
|
169
|
+
)
|
170
|
+
|
171
|
+
@display(description="Error Preview")
|
172
|
+
def error_preview(self, obj):
|
173
|
+
"""Preview of error message."""
|
174
|
+
if not obj.error_message:
|
175
|
+
return "-"
|
176
|
+
preview = obj.error_message[:200] + "..." if len(obj.error_message) > 200 else obj.error_message
|
177
|
+
return format_html(
|
178
|
+
'<div class="text-sm text-red-600 max-w-md">{}</div>',
|
179
|
+
preview
|
180
|
+
)
|
181
|
+
|
182
|
+
@action(description="Retry failed tools", icon="refresh", variant=ActionVariant.WARNING)
|
183
|
+
def retry_failed_tools(self, request, queryset):
|
184
|
+
"""Retry failed tool executions."""
|
185
|
+
failed_count = queryset.filter(status='failed').count()
|
186
|
+
messages.warning(request, f"Retry functionality not implemented yet. {failed_count} failed tools selected.")
|
187
|
+
|
188
|
+
@action(description="Clear errors", icon="clear", variant=ActionVariant.INFO)
|
189
|
+
def clear_errors(self, request, queryset):
|
190
|
+
"""Clear error messages."""
|
191
|
+
error_count = queryset.exclude(error_message__isnull=True).exclude(error_message='').count()
|
192
|
+
messages.info(request, f"Error clearing not implemented yet. {error_count} tools with errors selected.")
|
193
|
+
|
194
|
+
def get_queryset(self, request):
|
195
|
+
"""Optimize queryset."""
|
196
|
+
return super().get_queryset(request).select_related('user', 'agent_execution')
|
197
|
+
|
198
|
+
|
199
|
+
@admin.register(ApprovalLog)
|
200
|
+
class ApprovalLogAdmin(ModelAdmin):
|
201
|
+
"""Admin interface for ApprovalLog with Unfold styling."""
|
202
|
+
|
203
|
+
list_display = [
|
204
|
+
'approval_id_display', 'tool_name_display', 'status_badge', 'user',
|
205
|
+
'decision_info', 'time_metrics', 'expiry_status', 'requested_at'
|
206
|
+
]
|
207
|
+
ordering = ['-requested_at']
|
208
|
+
list_filter = [
|
209
|
+
'status', 'tool_name', 'requested_at',
|
210
|
+
('user', AutocompleteSelectFilter),
|
211
|
+
('approved_by', AutocompleteSelectFilter),
|
212
|
+
('rejected_by', AutocompleteSelectFilter)
|
213
|
+
]
|
214
|
+
search_fields = ['approval_id', 'tool_name', 'user__username', 'justification']
|
215
|
+
autocomplete_fields = ['user', 'approved_by', 'rejected_by']
|
216
|
+
readonly_fields = [
|
217
|
+
'approval_id', 'requested_at', 'decided_at', 'time_to_decision',
|
218
|
+
'is_expired', 'tool_args_preview', 'justification_preview'
|
219
|
+
]
|
220
|
+
|
221
|
+
# Unfold form field overrides
|
222
|
+
formfield_overrides = {
|
223
|
+
models.TextField: {"widget": WysiwygWidget},
|
224
|
+
JSONField: {"widget": JSONEditorWidget},
|
225
|
+
}
|
226
|
+
|
227
|
+
fieldsets = (
|
228
|
+
("✅ Approval Info", {
|
229
|
+
'fields': ('approval_id', 'tool_name', 'status', 'user'),
|
230
|
+
'classes': ('tab',)
|
231
|
+
}),
|
232
|
+
("📝 Request Details", {
|
233
|
+
'fields': ('tool_args_preview', 'tool_args', 'justification_preview', 'justification'),
|
234
|
+
'classes': ('tab',)
|
235
|
+
}),
|
236
|
+
("🎯 Decision", {
|
237
|
+
'fields': ('approved_by', 'rejected_by', 'rejection_reason'),
|
238
|
+
'classes': ('tab',)
|
239
|
+
}),
|
240
|
+
("⏰ Timestamps", {
|
241
|
+
'fields': ('requested_at', 'decided_at', 'expires_at', 'time_to_decision', 'is_expired'),
|
242
|
+
'classes': ('tab', 'collapse')
|
243
|
+
}),
|
244
|
+
)
|
245
|
+
|
246
|
+
actions = ['approve_selected', 'reject_selected', 'extend_expiry']
|
247
|
+
|
248
|
+
@display(description="Approval ID")
|
249
|
+
def approval_id_display(self, obj):
|
250
|
+
"""Enhanced approval ID display."""
|
251
|
+
return format_html(
|
252
|
+
'<span class="font-mono text-sm text-blue-600">#{}</span>',
|
253
|
+
obj.approval_id[:8] if obj.approval_id else "N/A"
|
254
|
+
)
|
255
|
+
|
256
|
+
@display(description="Tool")
|
257
|
+
def tool_name_display(self, obj):
|
258
|
+
"""Enhanced tool name display."""
|
259
|
+
return format_html(
|
260
|
+
'<div class="flex items-center space-x-2">'
|
261
|
+
'<span class="text-purple-600 font-medium">{}</span>'
|
262
|
+
'</div>',
|
263
|
+
obj.tool_name
|
264
|
+
)
|
265
|
+
|
266
|
+
@display(description="Status")
|
267
|
+
def status_badge(self, obj):
|
268
|
+
"""Status badge with color coding."""
|
269
|
+
colors = {
|
270
|
+
'pending': 'bg-yellow-100 text-yellow-800',
|
271
|
+
'approved': 'bg-green-100 text-green-800',
|
272
|
+
'rejected': 'bg-red-100 text-red-800',
|
273
|
+
'expired': 'bg-gray-100 text-gray-800'
|
274
|
+
}
|
275
|
+
color_class = colors.get(obj.status, 'bg-gray-100 text-gray-800')
|
276
|
+
return format_html(
|
277
|
+
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium {}">{}</span>',
|
278
|
+
color_class, obj.get_status_display()
|
279
|
+
)
|
280
|
+
|
281
|
+
@display(description="Decision")
|
282
|
+
def decision_info(self, obj):
|
283
|
+
"""Decision information."""
|
284
|
+
if obj.approved_by:
|
285
|
+
return format_html(
|
286
|
+
'<div class="text-sm">'
|
287
|
+
'<div class="text-green-600 font-medium">✓ Approved by</div>'
|
288
|
+
'<div class="text-gray-600">{}</div>'
|
289
|
+
'</div>',
|
290
|
+
obj.approved_by.username
|
291
|
+
)
|
292
|
+
elif obj.rejected_by:
|
293
|
+
return format_html(
|
294
|
+
'<div class="text-sm">'
|
295
|
+
'<div class="text-red-600 font-medium">✗ Rejected by</div>'
|
296
|
+
'<div class="text-gray-600">{}</div>'
|
297
|
+
'</div>',
|
298
|
+
obj.rejected_by.username
|
299
|
+
)
|
300
|
+
return format_html(
|
301
|
+
'<div class="text-sm text-yellow-600">⏳ Pending</div>'
|
302
|
+
)
|
303
|
+
|
304
|
+
@display(description="Time Metrics")
|
305
|
+
def time_metrics(self, obj):
|
306
|
+
"""Time-related metrics."""
|
307
|
+
decision_time = f"{obj.time_to_decision:.1f}s" if obj.time_to_decision else "N/A"
|
308
|
+
return format_html(
|
309
|
+
'<div class="text-sm space-y-1">'
|
310
|
+
'<div><span class="font-medium">Decision:</span> {}</div>'
|
311
|
+
'</div>',
|
312
|
+
decision_time
|
313
|
+
)
|
314
|
+
|
315
|
+
@display(description="Expiry", boolean=True)
|
316
|
+
def expiry_status(self, obj):
|
317
|
+
"""Expiry status."""
|
318
|
+
return not obj.is_expired
|
319
|
+
|
320
|
+
@display(description="Tool Args Preview")
|
321
|
+
def tool_args_preview(self, obj):
|
322
|
+
"""Preview of tool arguments."""
|
323
|
+
if not obj.tool_args:
|
324
|
+
return "-"
|
325
|
+
preview = str(obj.tool_args)[:200] + "..." if len(str(obj.tool_args)) > 200 else str(obj.tool_args)
|
326
|
+
return format_html(
|
327
|
+
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
328
|
+
preview
|
329
|
+
)
|
330
|
+
|
331
|
+
@display(description="Justification Preview")
|
332
|
+
def justification_preview(self, obj):
|
333
|
+
"""Preview of justification."""
|
334
|
+
if not obj.justification:
|
335
|
+
return "-"
|
336
|
+
preview = obj.justification[:200] + "..." if len(obj.justification) > 200 else obj.justification
|
337
|
+
return format_html(
|
338
|
+
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
339
|
+
preview
|
340
|
+
)
|
341
|
+
|
342
|
+
@action(description="Approve selected requests", icon="check", variant=ActionVariant.SUCCESS)
|
343
|
+
def approve_selected(self, request, queryset):
|
344
|
+
"""Approve selected requests."""
|
345
|
+
count = 0
|
346
|
+
for approval in queryset.filter(status='pending'):
|
347
|
+
approval.approve(request.user)
|
348
|
+
count += 1
|
349
|
+
|
350
|
+
messages.success(request, f"Approved {count} requests.")
|
351
|
+
|
352
|
+
@action(description="Reject selected requests", icon="close", variant=ActionVariant.DANGER)
|
353
|
+
def reject_selected(self, request, queryset):
|
354
|
+
"""Reject selected requests."""
|
355
|
+
count = 0
|
356
|
+
for approval in queryset.filter(status='pending'):
|
357
|
+
approval.reject(request.user, "Bulk rejection by admin")
|
358
|
+
count += 1
|
359
|
+
|
360
|
+
messages.warning(request, f"Rejected {count} requests.")
|
361
|
+
|
362
|
+
@action(description="Extend expiry", icon="schedule", variant=ActionVariant.INFO)
|
363
|
+
def extend_expiry(self, request, queryset):
|
364
|
+
"""Extend expiry time for selected requests."""
|
365
|
+
pending_count = queryset.filter(status='pending').count()
|
366
|
+
messages.info(request, f"Expiry extension not implemented yet. {pending_count} pending requests selected.")
|
367
|
+
|
368
|
+
def get_queryset(self, request):
|
369
|
+
"""Optimize queryset."""
|
370
|
+
return super().get_queryset(request).select_related('user', 'approved_by', 'rejected_by')
|
371
|
+
|
372
|
+
|
373
|
+
@admin.register(ToolsetConfiguration)
|
374
|
+
class ToolsetConfigurationAdmin(ModelAdmin):
|
375
|
+
"""Admin interface for ToolsetConfiguration with Unfold styling."""
|
376
|
+
|
377
|
+
list_display = [
|
378
|
+
'name_display', 'toolset_class_badge', 'status_badge', 'usage_info', 'created_by', 'created_at'
|
379
|
+
]
|
380
|
+
ordering = ['-created_at']
|
381
|
+
list_filter = [
|
382
|
+
'is_active', 'created_at',
|
383
|
+
('created_by', AutocompleteSelectFilter)
|
384
|
+
]
|
385
|
+
search_fields = ['name', 'description', 'toolset_class']
|
386
|
+
autocomplete_fields = ['created_by', 'allowed_users', 'allowed_groups']
|
387
|
+
readonly_fields = ['created_at', 'updated_at', 'config_preview']
|
388
|
+
|
389
|
+
# Unfold form field overrides
|
390
|
+
formfield_overrides = {
|
391
|
+
models.TextField: {"widget": WysiwygWidget},
|
392
|
+
JSONField: {"widget": JSONEditorWidget},
|
393
|
+
}
|
394
|
+
|
395
|
+
fieldsets = (
|
396
|
+
("🔧 Basic Information", {
|
397
|
+
'fields': ('name', 'description', 'toolset_class'),
|
398
|
+
'classes': ('tab',)
|
399
|
+
}),
|
400
|
+
("⚙️ Configuration", {
|
401
|
+
'fields': ('config_preview', 'config'),
|
402
|
+
'classes': ('tab',)
|
403
|
+
}),
|
404
|
+
("🔐 Access Control", {
|
405
|
+
'fields': ('is_active', 'allowed_users', 'allowed_groups'),
|
406
|
+
'classes': ('tab',)
|
407
|
+
}),
|
408
|
+
("📝 Metadata", {
|
409
|
+
'fields': ('created_by', 'created_at', 'updated_at'),
|
410
|
+
'classes': ('tab', 'collapse')
|
411
|
+
}),
|
412
|
+
)
|
413
|
+
|
414
|
+
actions = ['activate_toolsets', 'deactivate_toolsets']
|
415
|
+
|
416
|
+
@display(description="Toolset Name")
|
417
|
+
def name_display(self, obj):
|
418
|
+
"""Enhanced name display."""
|
419
|
+
return format_html(
|
420
|
+
'<div class="flex items-center space-x-2">'
|
421
|
+
'<span class="text-teal-600 font-medium">{}</span>'
|
422
|
+
'</div>',
|
423
|
+
obj.name
|
424
|
+
)
|
425
|
+
|
426
|
+
@display(description="Class")
|
427
|
+
def toolset_class_badge(self, obj):
|
428
|
+
"""Toolset class badge."""
|
429
|
+
if not obj.toolset_class:
|
430
|
+
return "-"
|
431
|
+
class_name = obj.toolset_class.split('.')[-1] if '.' in obj.toolset_class else obj.toolset_class
|
432
|
+
return format_html(
|
433
|
+
'<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-cyan-100 text-cyan-800">{}</span>',
|
434
|
+
class_name
|
435
|
+
)
|
436
|
+
|
437
|
+
@display(description="Status", boolean=True)
|
438
|
+
def status_badge(self, obj):
|
439
|
+
"""Status badge."""
|
440
|
+
return obj.is_active
|
441
|
+
|
442
|
+
@display(description="Usage")
|
443
|
+
def usage_info(self, obj):
|
444
|
+
"""Usage information."""
|
445
|
+
users_count = obj.allowed_users.count() if obj.allowed_users.exists() else 0
|
446
|
+
groups_count = obj.allowed_groups.count() if obj.allowed_groups.exists() else 0
|
447
|
+
|
448
|
+
return format_html(
|
449
|
+
'<div class="text-sm space-y-1">'
|
450
|
+
'<div><span class="font-medium">Users:</span> {}</div>'
|
451
|
+
'<div><span class="font-medium">Groups:</span> {}</div>'
|
452
|
+
'</div>',
|
453
|
+
users_count,
|
454
|
+
groups_count
|
455
|
+
)
|
456
|
+
|
457
|
+
@display(description="Config Preview")
|
458
|
+
def config_preview(self, obj):
|
459
|
+
"""Preview of configuration."""
|
460
|
+
if not obj.config:
|
461
|
+
return "-"
|
462
|
+
preview = str(obj.config)[:200] + "..." if len(str(obj.config)) > 200 else str(obj.config)
|
463
|
+
return format_html(
|
464
|
+
'<div class="text-sm text-gray-600 max-w-md">{}</div>',
|
465
|
+
preview
|
466
|
+
)
|
467
|
+
|
468
|
+
@action(description="Activate toolsets", icon="play_arrow", variant=ActionVariant.SUCCESS)
|
469
|
+
def activate_toolsets(self, request, queryset):
|
470
|
+
"""Activate selected toolsets."""
|
471
|
+
updated = queryset.update(is_active=True)
|
472
|
+
messages.success(request, f"Activated {updated} toolsets.")
|
473
|
+
|
474
|
+
@action(description="Deactivate toolsets", icon="pause", variant=ActionVariant.WARNING)
|
475
|
+
def deactivate_toolsets(self, request, queryset):
|
476
|
+
"""Deactivate selected toolsets."""
|
477
|
+
updated = queryset.update(is_active=False)
|
478
|
+
messages.warning(request, f"Deactivated {updated} toolsets.")
|
479
|
+
|
480
|
+
def get_queryset(self, request):
|
481
|
+
"""Optimize queryset."""
|
482
|
+
return super().get_queryset(request).select_related('created_by').prefetch_related('allowed_users', 'allowed_groups')
|
@@ -0,0 +1,29 @@
|
|
1
|
+
"""
|
2
|
+
Django app configuration for Django Agents.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from django.apps import AppConfig
|
6
|
+
|
7
|
+
|
8
|
+
class AgentsConfig(AppConfig):
|
9
|
+
"""Django app configuration for Django Agents."""
|
10
|
+
|
11
|
+
default_auto_field = 'django.db.models.BigAutoField'
|
12
|
+
name = 'django_cfg.apps.agents'
|
13
|
+
label = 'django_cfg_agents'
|
14
|
+
verbose_name = 'Django Agents'
|
15
|
+
|
16
|
+
def ready(self):
|
17
|
+
"""Initialize app when Django starts."""
|
18
|
+
# Import signal handlers
|
19
|
+
try:
|
20
|
+
from . import signals # noqa
|
21
|
+
except ImportError:
|
22
|
+
pass
|
23
|
+
|
24
|
+
# Initialize orchestrator registry
|
25
|
+
try:
|
26
|
+
from .integration.registry import initialize_registry
|
27
|
+
initialize_registry()
|
28
|
+
except ImportError:
|
29
|
+
pass
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
Core components for Django Orchestrator.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .agent import DjangoAgent
|
6
|
+
from .orchestrator import SimpleOrchestrator
|
7
|
+
from .dependencies import DjangoDeps
|
8
|
+
from .models import ExecutionResult, WorkflowConfig
|
9
|
+
from .exceptions import OrchestratorError, AgentNotFoundError, ExecutionError
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"DjangoAgent",
|
13
|
+
"SimpleOrchestrator",
|
14
|
+
"DjangoDeps",
|
15
|
+
"ExecutionResult",
|
16
|
+
"WorkflowConfig",
|
17
|
+
"OrchestratorError",
|
18
|
+
"AgentNotFoundError",
|
19
|
+
"ExecutionError",
|
20
|
+
]
|