django-cfg 1.5.8__py3-none-any.whl → 1.5.14__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.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (119) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/commands/serializers.py +152 -0
  3. django_cfg/apps/api/commands/views.py +32 -0
  4. django_cfg/apps/business/accounts/management/commands/otp_test.py +5 -2
  5. django_cfg/apps/business/agents/management/commands/create_agent.py +5 -194
  6. django_cfg/apps/business/agents/management/commands/load_agent_templates.py +205 -0
  7. django_cfg/apps/business/agents/management/commands/orchestrator_status.py +4 -2
  8. django_cfg/apps/business/knowbase/management/commands/knowbase_stats.py +4 -2
  9. django_cfg/apps/business/knowbase/management/commands/setup_knowbase.py +4 -2
  10. django_cfg/apps/business/newsletter/management/commands/test_newsletter.py +5 -2
  11. django_cfg/apps/business/payments/management/commands/check_payment_status.py +4 -2
  12. django_cfg/apps/business/payments/management/commands/create_payment.py +4 -2
  13. django_cfg/apps/business/payments/management/commands/sync_currencies.py +4 -2
  14. django_cfg/apps/integrations/centrifugo/management/commands/generate_centrifugo_clients.py +5 -5
  15. django_cfg/apps/integrations/centrifugo/serializers/__init__.py +2 -1
  16. django_cfg/apps/integrations/centrifugo/serializers/publishes.py +22 -2
  17. django_cfg/apps/integrations/centrifugo/views/monitoring.py +25 -40
  18. django_cfg/apps/integrations/grpc/admin/__init__.py +7 -1
  19. django_cfg/apps/integrations/grpc/admin/config.py +113 -9
  20. django_cfg/apps/integrations/grpc/admin/grpc_api_key.py +129 -0
  21. django_cfg/apps/integrations/grpc/admin/grpc_request_log.py +72 -63
  22. django_cfg/apps/integrations/grpc/admin/grpc_server_status.py +236 -0
  23. django_cfg/apps/integrations/grpc/auth/__init__.py +11 -3
  24. django_cfg/apps/integrations/grpc/auth/api_key_auth.py +320 -0
  25. django_cfg/apps/integrations/grpc/interceptors/logging.py +17 -20
  26. django_cfg/apps/integrations/grpc/interceptors/metrics.py +15 -14
  27. django_cfg/apps/integrations/grpc/interceptors/request_logger.py +79 -59
  28. django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +130 -0
  29. django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +171 -96
  30. django_cfg/apps/integrations/grpc/management/commands/test_grpc_integration.py +75 -0
  31. django_cfg/apps/integrations/grpc/managers/__init__.py +2 -0
  32. django_cfg/apps/integrations/grpc/managers/grpc_api_key.py +192 -0
  33. django_cfg/apps/integrations/grpc/managers/grpc_server_status.py +19 -11
  34. django_cfg/apps/integrations/grpc/migrations/0005_grpcapikey.py +143 -0
  35. django_cfg/apps/integrations/grpc/migrations/0006_grpcrequestlog_api_key_and_more.py +34 -0
  36. django_cfg/apps/integrations/grpc/models/__init__.py +2 -0
  37. django_cfg/apps/integrations/grpc/models/grpc_api_key.py +198 -0
  38. django_cfg/apps/integrations/grpc/models/grpc_request_log.py +11 -0
  39. django_cfg/apps/integrations/grpc/models/grpc_server_status.py +39 -4
  40. django_cfg/apps/integrations/grpc/serializers/__init__.py +22 -6
  41. django_cfg/apps/integrations/grpc/serializers/api_keys.py +63 -0
  42. django_cfg/apps/integrations/grpc/serializers/charts.py +118 -120
  43. django_cfg/apps/integrations/grpc/serializers/config.py +65 -51
  44. django_cfg/apps/integrations/grpc/serializers/health.py +7 -7
  45. django_cfg/apps/integrations/grpc/serializers/proto_files.py +74 -0
  46. django_cfg/apps/integrations/grpc/serializers/requests.py +13 -7
  47. django_cfg/apps/integrations/grpc/serializers/service_registry.py +181 -112
  48. django_cfg/apps/integrations/grpc/serializers/services.py +14 -32
  49. django_cfg/apps/integrations/grpc/serializers/stats.py +50 -12
  50. django_cfg/apps/integrations/grpc/serializers/testing.py +66 -58
  51. django_cfg/apps/integrations/grpc/services/__init__.py +2 -0
  52. django_cfg/apps/integrations/grpc/services/monitoring_service.py +149 -43
  53. django_cfg/apps/integrations/grpc/services/proto_files_manager.py +268 -0
  54. django_cfg/apps/integrations/grpc/services/service_registry.py +48 -46
  55. django_cfg/apps/integrations/grpc/services/testing_service.py +10 -15
  56. django_cfg/apps/integrations/grpc/urls.py +8 -0
  57. django_cfg/apps/integrations/grpc/utils/__init__.py +4 -13
  58. django_cfg/apps/integrations/grpc/utils/integration_test.py +334 -0
  59. django_cfg/apps/integrations/grpc/utils/proto_gen.py +48 -8
  60. django_cfg/apps/integrations/grpc/utils/streaming_logger.py +177 -0
  61. django_cfg/apps/integrations/grpc/views/__init__.py +4 -0
  62. django_cfg/apps/integrations/grpc/views/api_keys.py +255 -0
  63. django_cfg/apps/integrations/grpc/views/charts.py +21 -14
  64. django_cfg/apps/integrations/grpc/views/config.py +8 -6
  65. django_cfg/apps/integrations/grpc/views/monitoring.py +51 -79
  66. django_cfg/apps/integrations/grpc/views/proto_files.py +214 -0
  67. django_cfg/apps/integrations/grpc/views/services.py +30 -21
  68. django_cfg/apps/integrations/grpc/views/testing.py +45 -43
  69. django_cfg/apps/integrations/rq/views/jobs.py +19 -9
  70. django_cfg/apps/integrations/rq/views/schedule.py +7 -3
  71. django_cfg/apps/system/dashboard/serializers/commands.py +25 -1
  72. django_cfg/apps/system/dashboard/services/commands_service.py +12 -1
  73. django_cfg/apps/system/maintenance/management/commands/maintenance.py +5 -2
  74. django_cfg/apps/system/maintenance/management/commands/process_scheduled_maintenance.py +4 -2
  75. django_cfg/apps/system/maintenance/management/commands/sync_cloudflare.py +5 -2
  76. django_cfg/config.py +33 -0
  77. django_cfg/core/generation/integration_generators/grpc_generator.py +30 -32
  78. django_cfg/management/commands/check_endpoints.py +2 -2
  79. django_cfg/management/commands/check_settings.py +3 -10
  80. django_cfg/management/commands/clear_constance.py +3 -10
  81. django_cfg/management/commands/create_token.py +4 -11
  82. django_cfg/management/commands/list_urls.py +4 -10
  83. django_cfg/management/commands/migrate_all.py +18 -12
  84. django_cfg/management/commands/migrator.py +4 -11
  85. django_cfg/management/commands/script.py +4 -10
  86. django_cfg/management/commands/show_config.py +8 -16
  87. django_cfg/management/commands/show_urls.py +5 -11
  88. django_cfg/management/commands/superuser.py +4 -11
  89. django_cfg/management/commands/tree.py +5 -10
  90. django_cfg/management/utils/README.md +402 -0
  91. django_cfg/management/utils/__init__.py +29 -0
  92. django_cfg/management/utils/mixins.py +176 -0
  93. django_cfg/middleware/pagination.py +53 -54
  94. django_cfg/models/api/grpc/__init__.py +15 -21
  95. django_cfg/models/api/grpc/config.py +155 -73
  96. django_cfg/models/ngrok/config.py +7 -6
  97. django_cfg/modules/django_client/core/generator/python/files_generator.py +5 -13
  98. django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +16 -4
  99. django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +2 -3
  100. django_cfg/modules/django_client/core/generator/typescript/files_generator.py +6 -5
  101. django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +12 -8
  102. django_cfg/modules/django_client/core/parser/base.py +114 -30
  103. django_cfg/modules/django_client/management/commands/generate_client.py +5 -2
  104. django_cfg/modules/django_client/management/commands/validate_openapi.py +5 -2
  105. django_cfg/modules/django_email/management/commands/test_email.py +4 -10
  106. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +16 -13
  107. django_cfg/modules/django_telegram/management/commands/test_telegram.py +4 -11
  108. django_cfg/modules/django_twilio/management/commands/test_twilio.py +4 -11
  109. django_cfg/modules/django_unfold/navigation.py +6 -18
  110. django_cfg/pyproject.toml +1 -1
  111. django_cfg/registry/modules.py +1 -4
  112. django_cfg/requirements.txt +52 -0
  113. django_cfg/static/frontend/admin.zip +0 -0
  114. {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/METADATA +1 -1
  115. {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/RECORD +118 -97
  116. django_cfg/apps/integrations/grpc/auth/jwt_auth.py +0 -295
  117. {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/WHEEL +0 -0
  118. {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/entry_points.txt +0 -0
  119. {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/licenses/LICENSE +0 -0
django_cfg/__init__.py CHANGED
@@ -32,7 +32,7 @@ Example:
32
32
  default_app_config = "django_cfg.apps.DjangoCfgConfig"
33
33
 
34
34
  # Version information
35
- __version__ = "1.5.8"
35
+ __version__ = "1.5.14"
36
36
  __license__ = "MIT"
37
37
 
38
38
  # Setup warnings debug early (checks env var only at this point)
@@ -0,0 +1,152 @@
1
+ """
2
+ Django CFG Commands API Serializers.
3
+ """
4
+
5
+ from rest_framework import serializers
6
+
7
+
8
+ class CommandMetadataSerializer(serializers.Serializer):
9
+ """
10
+ Serializer for command metadata.
11
+
12
+ Includes security attributes from our base classes:
13
+ - web_executable: Can be executed via web interface
14
+ - requires_input: Requires interactive user input
15
+ - is_destructive: Modifies or deletes data
16
+ - is_allowed: Passes security checks
17
+ - risk_level: Security risk assessment
18
+ """
19
+
20
+ web_executable = serializers.BooleanField(
21
+ allow_null=True,
22
+ help_text="Whether command can be executed via web interface"
23
+ )
24
+ requires_input = serializers.BooleanField(
25
+ allow_null=True,
26
+ help_text="Whether command requires interactive user input"
27
+ )
28
+ is_destructive = serializers.BooleanField(
29
+ allow_null=True,
30
+ help_text="Whether command modifies or deletes data"
31
+ )
32
+ is_allowed = serializers.BooleanField(
33
+ help_text="Whether command passes security checks for web execution"
34
+ )
35
+ risk_level = serializers.ChoiceField(
36
+ choices=['safe', 'caution', 'dangerous', 'unknown'],
37
+ help_text="Security risk level assessment"
38
+ )
39
+
40
+
41
+ class CommandSerializer(serializers.Serializer):
42
+ """
43
+ Serializer for Django management commands.
44
+
45
+ Returns command information including:
46
+ - Basic info (name, app, description)
47
+ - Security metadata (web_executable, requires_input, is_destructive)
48
+ - Execution status (is_allowed, risk_level)
49
+ """
50
+
51
+ name = serializers.CharField(
52
+ help_text="Command name"
53
+ )
54
+ app = serializers.CharField(
55
+ help_text="Django app that provides this command"
56
+ )
57
+ description = serializers.CharField(
58
+ help_text="Short description of what the command does"
59
+ )
60
+
61
+ # Security metadata - flattened for easier API consumption
62
+ web_executable = serializers.BooleanField(
63
+ allow_null=True,
64
+ help_text="Can be executed via web interface"
65
+ )
66
+ requires_input = serializers.BooleanField(
67
+ allow_null=True,
68
+ help_text="Requires interactive user input"
69
+ )
70
+ is_destructive = serializers.BooleanField(
71
+ allow_null=True,
72
+ help_text="Modifies or deletes data"
73
+ )
74
+ is_allowed = serializers.BooleanField(
75
+ help_text="Passes security checks"
76
+ )
77
+ risk_level = serializers.ChoiceField(
78
+ choices=['safe', 'caution', 'dangerous', 'unknown'],
79
+ help_text="Security risk level"
80
+ )
81
+
82
+
83
+ class CommandListResponseSerializer(serializers.Serializer):
84
+ """
85
+ Serializer for list commands API response.
86
+
87
+ Returns categorized commands:
88
+ - django_cfg: Custom django-cfg commands
89
+ - django_core: Django built-in commands
90
+ - third_party: Third-party app commands
91
+ - project: Project-specific commands
92
+ """
93
+
94
+ status = serializers.ChoiceField(
95
+ choices=['success', 'error'],
96
+ help_text="Response status"
97
+ )
98
+ commands = serializers.DictField(
99
+ child=serializers.ListField(child=CommandSerializer()),
100
+ help_text="Categorized command lists"
101
+ )
102
+ total_commands = serializers.IntegerField(
103
+ help_text="Total number of commands available"
104
+ )
105
+ timestamp = serializers.DateTimeField(
106
+ help_text="Response timestamp"
107
+ )
108
+
109
+
110
+ class CommandExecutionRequestSerializer(serializers.Serializer):
111
+ """
112
+ Serializer for command execution requests.
113
+ """
114
+
115
+ command = serializers.CharField(
116
+ required=True,
117
+ help_text="Name of the command to execute"
118
+ )
119
+ args = serializers.ListField(
120
+ child=serializers.CharField(),
121
+ required=False,
122
+ default=list,
123
+ help_text="Positional arguments for the command"
124
+ )
125
+ options = serializers.DictField(
126
+ required=False,
127
+ default=dict,
128
+ help_text="Named options for the command (e.g., {'--verbose': True})"
129
+ )
130
+
131
+
132
+ class CommandHelpResponseSerializer(serializers.Serializer):
133
+ """
134
+ Serializer for command help API response.
135
+ """
136
+
137
+ status = serializers.ChoiceField(
138
+ choices=['success', 'error'],
139
+ help_text="Response status"
140
+ )
141
+ command = serializers.CharField(
142
+ help_text="Command name"
143
+ )
144
+ app = serializers.CharField(
145
+ help_text="Django app"
146
+ )
147
+ help = serializers.CharField(
148
+ help_text="Full help text from command --help"
149
+ )
150
+ timestamp = serializers.DateTimeField(
151
+ help_text="Response timestamp"
152
+ )
@@ -49,6 +49,7 @@ def list_commands_view(request):
49
49
  "name": command_name,
50
50
  "app": app_name,
51
51
  "description": _get_command_description(command_name),
52
+ **_get_command_metadata(command_name, app_name),
52
53
  }
53
54
 
54
55
  if app_name == "django_cfg":
@@ -270,6 +271,37 @@ def _get_command_description(command_name: str) -> str:
270
271
  return f'Django management command: {command_name}'
271
272
 
272
273
 
274
+ def _get_command_metadata(command_name: str, app_name: str) -> dict:
275
+ """
276
+ Get security metadata for a command.
277
+
278
+ Returns:
279
+ Dict with web_executable, requires_input, is_destructive, is_allowed
280
+ """
281
+ try:
282
+ from django.core.management import load_command_class
283
+ from django_cfg.apps.system.dashboard.services.commands_security import is_command_allowed, get_command_risk_level
284
+
285
+ command_instance = load_command_class(app_name, command_name)
286
+
287
+ return {
288
+ 'web_executable': getattr(command_instance, 'web_executable', None),
289
+ 'requires_input': getattr(command_instance, 'requires_input', None),
290
+ 'is_destructive': getattr(command_instance, 'is_destructive', None),
291
+ 'is_allowed': is_command_allowed(command_name, app_name),
292
+ 'risk_level': get_command_risk_level(command_name, app_name),
293
+ }
294
+ except Exception as e:
295
+ logger.debug(f"Could not load metadata for {command_name}: {e}")
296
+ return {
297
+ 'web_executable': None,
298
+ 'requires_input': None,
299
+ 'is_destructive': None,
300
+ 'is_allowed': False,
301
+ 'risk_level': 'unknown',
302
+ }
303
+
304
+
273
305
  def _get_command_help(command_name: str) -> str:
274
306
  """Get full help text for a command."""
275
307
  try:
@@ -2,17 +2,20 @@
2
2
  Management command to test OTP functionality.
3
3
  """
4
4
 
5
- from django.core.management.base import BaseCommand, CommandError
5
+ from django.core.management.base import CommandError
6
6
  from django.utils import timezone
7
7
  from rich.console import Console
8
8
  from rich.panel import Panel
9
9
  from rich.table import Table
10
10
 
11
+ from django_cfg.management.utils import SafeCommand
12
+
11
13
  from ...models import OTPSecret
12
14
  from ...services.otp_service import OTPService
13
15
 
14
16
 
15
- class Command(BaseCommand):
17
+ class Command(SafeCommand):
18
+ command_name = 'otp_test'
16
19
  help = "Test OTP functionality with email and telegram delivery"
17
20
 
18
21
  def __init__(self, *args, **kwargs):
@@ -5,14 +5,17 @@ Management command to create agent definitions.
5
5
  import asyncio
6
6
 
7
7
  from django.contrib.auth.models import User
8
- from django.core.management.base import BaseCommand, CommandError
8
+ from django.core.management.base import CommandError
9
+
10
+ from django_cfg.management.utils import AdminCommand
9
11
 
10
12
  from django_cfg.apps.business.agents.models.registry import AgentDefinition
11
13
 
12
14
 
13
- class Command(BaseCommand):
15
+ class Command(AdminCommand):
14
16
  """Create agent definition from command line."""
15
17
 
18
+ command_name = 'create_agent'
16
19
  help = 'Create a new agent definition'
17
20
 
18
21
  def add_arguments(self, parser):
@@ -172,195 +175,3 @@ class Command(BaseCommand):
172
175
  return await User.objects.filter(is_superuser=True).afirst()
173
176
  except User.DoesNotExist:
174
177
  raise CommandError("No superuser found. Please create a superuser first or specify --creator")
175
-
176
-
177
- class Command(BaseCommand):
178
- """Load agent definitions from templates."""
179
-
180
- help = 'Load pre-built agent templates'
181
-
182
- def add_arguments(self, parser):
183
- """Add command arguments."""
184
- parser.add_argument(
185
- '--list',
186
- action='store_true',
187
- help='List available templates'
188
- )
189
- parser.add_argument(
190
- '--load',
191
- type=str,
192
- nargs='*',
193
- help='Load specific templates (space-separated names)'
194
- )
195
- parser.add_argument(
196
- '--load-all',
197
- action='store_true',
198
- help='Load all available templates'
199
- )
200
- parser.add_argument(
201
- '--creator',
202
- type=str,
203
- help='Username of agent creator (defaults to first superuser)'
204
- )
205
-
206
- def handle(self, *args, **options):
207
- """Handle command execution."""
208
- if options['list']:
209
- self._list_templates()
210
- elif options['load'] or options['load_all']:
211
- asyncio.run(self._load_templates(options))
212
- else:
213
- self.stdout.write(
214
- self.style.ERROR('Please specify --list, --load, or --load-all')
215
- )
216
-
217
- def _list_templates(self):
218
- """List available templates."""
219
- templates = self._get_available_templates()
220
-
221
- self.stdout.write(self.style.SUCCESS('šŸ“‹ Available Agent Templates:'))
222
- self.stdout.write('=' * 40)
223
-
224
- for category, agents in templates.items():
225
- self.stdout.write(f"\n{category.upper()}:")
226
- for agent_name, agent_info in agents.items():
227
- self.stdout.write(f" • {agent_name}: {agent_info['description']}")
228
-
229
- async def _load_templates(self, options):
230
- """Load templates."""
231
- creator = await self._get_creator_user(options.get('creator'))
232
- templates = self._get_available_templates()
233
-
234
- if options['load_all']:
235
- # Load all templates
236
- to_load = []
237
- for category_templates in templates.values():
238
- to_load.extend(category_templates.keys())
239
- else:
240
- to_load = options['load']
241
-
242
- loaded_count = 0
243
-
244
- for template_name in to_load:
245
- # Find template
246
- template_info = None
247
- for category_templates in templates.values():
248
- if template_name in category_templates:
249
- template_info = category_templates[template_name]
250
- break
251
-
252
- if not template_info:
253
- self.stdout.write(
254
- self.style.WARNING(f"Template '{template_name}' not found")
255
- )
256
- continue
257
-
258
- # Check if agent already exists
259
- if await AgentDefinition.objects.filter(name=template_name).aexists():
260
- self.stdout.write(
261
- self.style.WARNING(f"Agent '{template_name}' already exists, skipping")
262
- )
263
- continue
264
-
265
- # Create agent
266
- try:
267
- agent_data = template_info.copy()
268
- agent_data['name'] = template_name
269
- agent_data['created_by'] = creator
270
-
271
- await AgentDefinition.objects.acreate(**agent_data)
272
-
273
- self.stdout.write(
274
- self.style.SUCCESS(f"āœ… Loaded template: {template_name}")
275
- )
276
- loaded_count += 1
277
-
278
- except Exception as e:
279
- self.stdout.write(
280
- self.style.ERROR(f"Failed to load template '{template_name}': {e}")
281
- )
282
-
283
- self.stdout.write(
284
- self.style.SUCCESS(f"\nšŸŽ‰ Loaded {loaded_count} agent templates")
285
- )
286
-
287
- def _get_available_templates(self):
288
- """Get available agent templates."""
289
- return {
290
- 'content': {
291
- 'content_analyzer': {
292
- 'description': 'Analyze content sentiment, topics, and quality',
293
- 'instructions': 'Analyze content for sentiment, topics, keywords, and quality metrics.',
294
- 'deps_type': 'ContentDeps',
295
- 'output_type': 'AnalysisResult',
296
- 'category': 'content',
297
- 'model': 'openai:gpt-4o-mini',
298
- },
299
- 'content_generator': {
300
- 'description': 'Generate high-quality content based on requirements',
301
- 'instructions': 'Generate engaging, well-structured content based on type, audience, and style requirements.',
302
- 'deps_type': 'ContentDeps',
303
- 'output_type': 'ProcessResult',
304
- 'category': 'content',
305
- 'model': 'openai:gpt-4o-mini',
306
- },
307
- 'content_validator': {
308
- 'description': 'Validate content quality and compliance',
309
- 'instructions': 'Validate content for grammar, style, accuracy, and guideline compliance.',
310
- 'deps_type': 'ContentDeps',
311
- 'output_type': 'ValidationResult',
312
- 'category': 'content',
313
- 'model': 'openai:gpt-4o-mini',
314
- },
315
- },
316
- 'data': {
317
- 'data_processor': {
318
- 'description': 'Process and transform data',
319
- 'instructions': 'Process, clean, and transform data according to specifications.',
320
- 'deps_type': 'DataProcessingDeps',
321
- 'output_type': 'ProcessResult',
322
- 'category': 'data',
323
- 'model': 'openai:gpt-4o-mini',
324
- },
325
- 'data_validator': {
326
- 'description': 'Validate data quality and integrity',
327
- 'instructions': 'Validate data quality, check for errors, and ensure integrity.',
328
- 'deps_type': 'DataProcessingDeps',
329
- 'output_type': 'ValidationResult',
330
- 'category': 'data',
331
- 'model': 'openai:gpt-4o-mini',
332
- },
333
- },
334
- 'business': {
335
- 'business_rules': {
336
- 'description': 'Apply business rules and logic',
337
- 'instructions': 'Apply business rules, validate decisions, and ensure compliance.',
338
- 'deps_type': 'BusinessLogicDeps',
339
- 'output_type': 'ProcessResult',
340
- 'category': 'business',
341
- 'model': 'openai:gpt-4o-mini',
342
- },
343
- 'decision_maker': {
344
- 'description': 'Make decisions based on criteria',
345
- 'instructions': 'Analyze options and make informed decisions based on criteria and context.',
346
- 'deps_type': 'BusinessLogicDeps',
347
- 'output_type': 'ProcessResult',
348
- 'category': 'business',
349
- 'model': 'openai:gpt-4o-mini',
350
- },
351
- }
352
- }
353
-
354
- async def _get_creator_user(self, username):
355
- """Get creator user."""
356
- if username:
357
- try:
358
- return await User.objects.aget(username=username)
359
- except User.DoesNotExist:
360
- raise CommandError(f"User '{username}' not found")
361
- else:
362
- # Use first superuser
363
- try:
364
- return await User.objects.filter(is_superuser=True).afirst()
365
- except User.DoesNotExist:
366
- raise CommandError("No superuser found. Please create a superuser first or specify --creator")
@@ -0,0 +1,205 @@
1
+ """
2
+ Management command to load pre-built agent templates.
3
+ """
4
+
5
+ import asyncio
6
+
7
+ from django.contrib.auth.models import User
8
+ from django.core.management.base import CommandError
9
+
10
+ from django_cfg.management.utils import AdminCommand
11
+
12
+ from django_cfg.apps.business.agents.models.registry import AgentDefinition
13
+
14
+
15
+ class Command(AdminCommand):
16
+ """Load agent definitions from templates."""
17
+
18
+ command_name = 'load_agent_templates'
19
+ help = 'Load pre-built agent templates'
20
+
21
+ def add_arguments(self, parser):
22
+ """Add command arguments."""
23
+ parser.add_argument(
24
+ '--list',
25
+ action='store_true',
26
+ help='List available templates'
27
+ )
28
+ parser.add_argument(
29
+ '--load',
30
+ type=str,
31
+ nargs='*',
32
+ help='Load specific templates (space-separated names)'
33
+ )
34
+ parser.add_argument(
35
+ '--load-all',
36
+ action='store_true',
37
+ help='Load all available templates'
38
+ )
39
+ parser.add_argument(
40
+ '--creator',
41
+ type=str,
42
+ help='Username of agent creator (defaults to first superuser)'
43
+ )
44
+
45
+ def handle(self, *args, **options):
46
+ """Handle command execution."""
47
+ if options['list']:
48
+ self._list_templates()
49
+ elif options['load'] or options['load_all']:
50
+ asyncio.run(self._load_templates(options))
51
+ else:
52
+ self.stdout.write(
53
+ self.style.ERROR('Please specify --list, --load, or --load-all')
54
+ )
55
+
56
+ def _list_templates(self):
57
+ """List available templates."""
58
+ templates = self._get_available_templates()
59
+
60
+ self.stdout.write(self.style.SUCCESS('šŸ“‹ Available Agent Templates:'))
61
+ self.stdout.write('=' * 40)
62
+
63
+ for category, agents in templates.items():
64
+ self.stdout.write(f"\n{category.upper()}:")
65
+ for agent_name, agent_info in agents.items():
66
+ self.stdout.write(f" • {agent_name}: {agent_info['description']}")
67
+
68
+ async def _load_templates(self, options):
69
+ """Load templates."""
70
+ creator = await self._get_creator_user(options.get('creator'))
71
+ templates = self._get_available_templates()
72
+
73
+ if options['load_all']:
74
+ # Load all templates
75
+ to_load = []
76
+ for category_templates in templates.values():
77
+ to_load.extend(category_templates.keys())
78
+ else:
79
+ to_load = options['load']
80
+
81
+ loaded_count = 0
82
+
83
+ for template_name in to_load:
84
+ # Find template
85
+ template_info = None
86
+ for category_templates in templates.values():
87
+ if template_name in category_templates:
88
+ template_info = category_templates[template_name]
89
+ break
90
+
91
+ if not template_info:
92
+ self.stdout.write(
93
+ self.style.WARNING(f"Template '{template_name}' not found")
94
+ )
95
+ continue
96
+
97
+ # Check if agent already exists
98
+ if await AgentDefinition.objects.filter(name=template_name).aexists():
99
+ self.stdout.write(
100
+ self.style.WARNING(f"Agent '{template_name}' already exists, skipping")
101
+ )
102
+ continue
103
+
104
+ # Create agent
105
+ try:
106
+ agent_data = template_info.copy()
107
+ agent_data['name'] = template_name
108
+ agent_data['created_by'] = creator
109
+
110
+ await AgentDefinition.objects.acreate(**agent_data)
111
+
112
+ self.stdout.write(
113
+ self.style.SUCCESS(f"āœ… Loaded template: {template_name}")
114
+ )
115
+ loaded_count += 1
116
+
117
+ except Exception as e:
118
+ self.stdout.write(
119
+ self.style.ERROR(f"Failed to load template '{template_name}': {e}")
120
+ )
121
+
122
+ self.stdout.write(
123
+ self.style.SUCCESS(f"\nšŸŽ‰ Loaded {loaded_count} agent templates")
124
+ )
125
+
126
+ def _get_available_templates(self):
127
+ """Get available agent templates."""
128
+ return {
129
+ 'content': {
130
+ 'content_analyzer': {
131
+ 'description': 'Analyze content sentiment, topics, and quality',
132
+ 'instructions': 'Analyze content for sentiment, topics, keywords, and quality metrics.',
133
+ 'deps_type': 'ContentDeps',
134
+ 'output_type': 'AnalysisResult',
135
+ 'category': 'content',
136
+ 'model': 'openai:gpt-4o-mini',
137
+ },
138
+ 'content_generator': {
139
+ 'description': 'Generate high-quality content based on requirements',
140
+ 'instructions': 'Generate engaging, well-structured content based on type, audience, and style requirements.',
141
+ 'deps_type': 'ContentDeps',
142
+ 'output_type': 'ProcessResult',
143
+ 'category': 'content',
144
+ 'model': 'openai:gpt-4o-mini',
145
+ },
146
+ 'content_validator': {
147
+ 'description': 'Validate content quality and compliance',
148
+ 'instructions': 'Validate content for grammar, style, accuracy, and guideline compliance.',
149
+ 'deps_type': 'ContentDeps',
150
+ 'output_type': 'ValidationResult',
151
+ 'category': 'content',
152
+ 'model': 'openai:gpt-4o-mini',
153
+ },
154
+ },
155
+ 'data': {
156
+ 'data_processor': {
157
+ 'description': 'Process and transform data',
158
+ 'instructions': 'Process, clean, and transform data according to specifications.',
159
+ 'deps_type': 'DataProcessingDeps',
160
+ 'output_type': 'ProcessResult',
161
+ 'category': 'data',
162
+ 'model': 'openai:gpt-4o-mini',
163
+ },
164
+ 'data_validator': {
165
+ 'description': 'Validate data quality and integrity',
166
+ 'instructions': 'Validate data quality, check for errors, and ensure integrity.',
167
+ 'deps_type': 'DataProcessingDeps',
168
+ 'output_type': 'ValidationResult',
169
+ 'category': 'data',
170
+ 'model': 'openai:gpt-4o-mini',
171
+ },
172
+ },
173
+ 'business': {
174
+ 'business_rules': {
175
+ 'description': 'Apply business rules and logic',
176
+ 'instructions': 'Apply business rules, validate decisions, and ensure compliance.',
177
+ 'deps_type': 'BusinessLogicDeps',
178
+ 'output_type': 'ProcessResult',
179
+ 'category': 'business',
180
+ 'model': 'openai:gpt-4o-mini',
181
+ },
182
+ 'decision_maker': {
183
+ 'description': 'Make decisions based on criteria',
184
+ 'instructions': 'Analyze options and make informed decisions based on criteria and context.',
185
+ 'deps_type': 'BusinessLogicDeps',
186
+ 'output_type': 'ProcessResult',
187
+ 'category': 'business',
188
+ 'model': 'openai:gpt-4o-mini',
189
+ },
190
+ }
191
+ }
192
+
193
+ async def _get_creator_user(self, username):
194
+ """Get creator user."""
195
+ if username:
196
+ try:
197
+ return await User.objects.aget(username=username)
198
+ except User.DoesNotExist:
199
+ raise CommandError(f"User '{username}' not found")
200
+ else:
201
+ # Use first superuser
202
+ try:
203
+ return await User.objects.filter(is_superuser=True).afirst()
204
+ except User.DoesNotExist:
205
+ raise CommandError("No superuser found. Please create a superuser first or specify --creator")
@@ -5,17 +5,19 @@ Management command to show orchestrator status.
5
5
  import asyncio
6
6
  from datetime import timedelta
7
7
 
8
- from django.core.management.base import BaseCommand
9
8
  from django.utils import timezone
10
9
 
10
+ from django_cfg.management.utils import SafeCommand
11
+
11
12
  from django_cfg.apps.business.agents.integration.registry import get_registry
12
13
  from django_cfg.apps.business.agents.models.execution import AgentExecution, WorkflowExecution
13
14
  from django_cfg.apps.business.agents.models.registry import AgentDefinition
14
15
 
15
16
 
16
- class Command(BaseCommand):
17
+ class Command(SafeCommand):
17
18
  """Show Django Orchestrator status and statistics."""
18
19
 
20
+ command_name = 'orchestrator_status'
19
21
  help = 'Display Django Orchestrator status and statistics'
20
22
 
21
23
  def add_arguments(self, parser):