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,391 @@
1
+ """
2
+ Simple Orchestrator - Main coordination class for multi-agent workflows.
3
+ """
4
+
5
+ import asyncio
6
+ import time
7
+ import logging
8
+ from typing import Dict, List, Any, Optional, Callable
9
+ from dataclasses import dataclass
10
+
11
+ from .agent import DjangoAgent
12
+ from .models import ExecutionResult, WorkflowConfig
13
+ from .exceptions import AgentNotFoundError, ExecutionError, ConfigurationError
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ @dataclass
19
+ class ExecutionPattern:
20
+ """Base class for execution patterns."""
21
+
22
+ name: str
23
+ description: str
24
+
25
+ async def execute(
26
+ self,
27
+ agents: List[DjangoAgent],
28
+ prompt: str,
29
+ deps: Any,
30
+ **kwargs
31
+ ) -> List[ExecutionResult]:
32
+ """Execute agents according to pattern."""
33
+ raise NotImplementedError
34
+
35
+
36
+ class SequentialPattern(ExecutionPattern):
37
+ """Sequential execution pattern - agents run one after another."""
38
+
39
+ def __init__(self):
40
+ super().__init__(
41
+ name="sequential",
42
+ description="Execute agents one after another, passing results forward"
43
+ )
44
+
45
+ async def execute(
46
+ self,
47
+ agents: List[DjangoAgent],
48
+ prompt: str,
49
+ deps: Any,
50
+ **kwargs
51
+ ) -> List[ExecutionResult]:
52
+ """Execute agents sequentially."""
53
+ results = []
54
+ current_prompt = prompt
55
+
56
+ for i, agent in enumerate(agents):
57
+ logger.debug(f"Executing agent {i+1}/{len(agents)}: {agent.name}")
58
+
59
+ try:
60
+ result = await agent.run(current_prompt, deps)
61
+ results.append(result)
62
+
63
+ # Pass result to next agent (if not the last one)
64
+ if i < len(agents) - 1:
65
+ current_prompt = f"Previous result: {result.output}\nNew task: {prompt}"
66
+
67
+ except Exception as e:
68
+ logger.error(f"Sequential execution failed at agent '{agent.name}': {e}")
69
+ # Add failed result
70
+ results.append(ExecutionResult(
71
+ agent_name=agent.name,
72
+ output=None,
73
+ execution_time=0.0,
74
+ error=str(e)
75
+ ))
76
+ break # Stop execution on error
77
+
78
+ return results
79
+
80
+
81
+ class ParallelPattern(ExecutionPattern):
82
+ """Parallel execution pattern - agents run concurrently."""
83
+
84
+ def __init__(self):
85
+ super().__init__(
86
+ name="parallel",
87
+ description="Execute agents concurrently with optional concurrency limits"
88
+ )
89
+
90
+ async def execute(
91
+ self,
92
+ agents: List[DjangoAgent],
93
+ prompt: str,
94
+ deps: Any,
95
+ **kwargs
96
+ ) -> List[ExecutionResult]:
97
+ """Execute agents in parallel."""
98
+ max_concurrent = kwargs.get('max_concurrent', 5)
99
+ semaphore = asyncio.Semaphore(max_concurrent)
100
+
101
+ async def run_agent_with_semaphore(agent: DjangoAgent) -> ExecutionResult:
102
+ async with semaphore:
103
+ try:
104
+ return await agent.run(prompt, deps)
105
+ except Exception as e:
106
+ logger.error(f"Parallel execution failed for agent '{agent.name}': {e}")
107
+ return ExecutionResult(
108
+ agent_name=agent.name,
109
+ output=None,
110
+ execution_time=0.0,
111
+ error=str(e)
112
+ )
113
+
114
+ logger.debug(f"Executing {len(agents)} agents in parallel (max_concurrent={max_concurrent})")
115
+
116
+ # Execute all agents concurrently
117
+ tasks = [run_agent_with_semaphore(agent) for agent in agents]
118
+ results = await asyncio.gather(*tasks, return_exceptions=True)
119
+
120
+ # Handle any exceptions that weren't caught
121
+ final_results = []
122
+ for i, result in enumerate(results):
123
+ if isinstance(result, Exception):
124
+ final_results.append(ExecutionResult(
125
+ agent_name=agents[i].name,
126
+ output=None,
127
+ execution_time=0.0,
128
+ error=str(result)
129
+ ))
130
+ else:
131
+ final_results.append(result)
132
+
133
+ return final_results
134
+
135
+
136
+ class ConditionalPattern(ExecutionPattern):
137
+ """Conditional execution pattern - execute agents based on conditions."""
138
+
139
+ def __init__(self):
140
+ super().__init__(
141
+ name="conditional",
142
+ description="Execute agents based on condition evaluation"
143
+ )
144
+
145
+ async def execute(
146
+ self,
147
+ agents: List[DjangoAgent],
148
+ prompt: str,
149
+ deps: Any,
150
+ **kwargs
151
+ ) -> List[ExecutionResult]:
152
+ """Execute agents conditionally."""
153
+ if len(agents) < 2:
154
+ raise ConfigurationError("Conditional pattern requires at least 2 agents")
155
+
156
+ # First agent determines condition
157
+ condition_agent = agents[0]
158
+ execution_agents = agents[1:]
159
+
160
+ logger.debug(f"Using '{condition_agent.name}' as condition agent")
161
+
162
+ # Get condition
163
+ condition_result = await condition_agent.run(prompt, deps)
164
+ results = [condition_result]
165
+
166
+ # Check if we should proceed
167
+ should_proceed = self._evaluate_condition(condition_result.output)
168
+
169
+ if should_proceed:
170
+ logger.debug(f"Condition met, executing {len(execution_agents)} agents")
171
+
172
+ # Execute remaining agents sequentially
173
+ sequential = SequentialPattern()
174
+ execution_results = await sequential.execute(execution_agents, prompt, deps, **kwargs)
175
+ results.extend(execution_results)
176
+ else:
177
+ logger.debug("Condition not met, skipping execution agents")
178
+
179
+ return results
180
+
181
+ def _evaluate_condition(self, output: Any) -> bool:
182
+ """Evaluate condition from agent output."""
183
+ # Simple condition evaluation
184
+ if isinstance(output, dict):
185
+ return output.get('proceed', False) or output.get('success', False)
186
+ elif isinstance(output, bool):
187
+ return output
188
+ elif hasattr(output, 'success'):
189
+ return output.success
190
+ else:
191
+ # Default to True if we can't determine condition
192
+ return True
193
+
194
+
195
+ class SimpleOrchestrator:
196
+ """
197
+ Main orchestrator for agent coordination.
198
+
199
+ Provides simple, clean interface for multi-agent workflows
200
+ following KISS principles.
201
+ """
202
+
203
+ def __init__(self):
204
+ """Initialize orchestrator."""
205
+ self.agents: Dict[str, DjangoAgent] = {}
206
+ self.patterns: Dict[str, ExecutionPattern] = {
207
+ 'sequential': SequentialPattern(),
208
+ 'parallel': ParallelPattern(),
209
+ 'conditional': ConditionalPattern()
210
+ }
211
+
212
+ # Metrics
213
+ self._total_executions = 0
214
+ self._total_execution_time = 0.0
215
+ self._pattern_usage = {}
216
+
217
+ logger.info("Initialized SimpleOrchestrator")
218
+
219
+ def register_agent(self, agent: DjangoAgent):
220
+ """
221
+ Register agent with orchestrator.
222
+
223
+ Args:
224
+ agent: DjangoAgent instance to register
225
+
226
+ Raises:
227
+ ConfigurationError: If agent name already exists
228
+ """
229
+ if agent.name in self.agents:
230
+ raise ConfigurationError(f"Agent '{agent.name}' already registered")
231
+
232
+ self.agents[agent.name] = agent
233
+ logger.info(f"Registered agent '{agent.name}'")
234
+
235
+ def unregister_agent(self, agent_name: str):
236
+ """
237
+ Unregister agent from orchestrator.
238
+
239
+ Args:
240
+ agent_name: Name of agent to unregister
241
+ """
242
+ if agent_name in self.agents:
243
+ del self.agents[agent_name]
244
+ logger.info(f"Unregistered agent '{agent_name}'")
245
+
246
+ def get_agent(self, name: str) -> DjangoAgent:
247
+ """
248
+ Get registered agent by name.
249
+
250
+ Args:
251
+ name: Agent name
252
+
253
+ Returns:
254
+ DjangoAgent instance
255
+
256
+ Raises:
257
+ AgentNotFoundError: If agent not found
258
+ """
259
+ if name not in self.agents:
260
+ raise AgentNotFoundError(name)
261
+
262
+ return self.agents[name]
263
+
264
+ def list_agents(self) -> List[str]:
265
+ """Get list of registered agent names."""
266
+ return list(self.agents.keys())
267
+
268
+ def add_pattern(self, pattern: ExecutionPattern):
269
+ """
270
+ Add custom execution pattern.
271
+
272
+ Args:
273
+ pattern: ExecutionPattern instance
274
+ """
275
+ self.patterns[pattern.name] = pattern
276
+ logger.info(f"Added execution pattern '{pattern.name}'")
277
+
278
+ async def execute(
279
+ self,
280
+ pattern: str,
281
+ agents: List[str],
282
+ prompt: str,
283
+ deps: Any,
284
+ config: Optional[WorkflowConfig] = None,
285
+ **kwargs
286
+ ) -> List[ExecutionResult]:
287
+ """
288
+ Execute agents using specified pattern.
289
+
290
+ Args:
291
+ pattern: Execution pattern name
292
+ agents: List of agent names to execute
293
+ prompt: Input prompt for agents
294
+ deps: Dependencies for execution
295
+ config: Optional workflow configuration
296
+ **kwargs: Pattern-specific options
297
+
298
+ Returns:
299
+ List of execution results
300
+
301
+ Raises:
302
+ AgentNotFoundError: If any agent not found
303
+ ConfigurationError: If pattern not found
304
+ ExecutionError: If execution fails
305
+ """
306
+ # Validate pattern
307
+ if pattern not in self.patterns:
308
+ available_patterns = list(self.patterns.keys())
309
+ raise ConfigurationError(
310
+ f"Unknown pattern '{pattern}'. Available patterns: {available_patterns}"
311
+ )
312
+
313
+ # Validate agents
314
+ agent_instances = []
315
+ for agent_name in agents:
316
+ if agent_name not in self.agents:
317
+ raise AgentNotFoundError(agent_name)
318
+ agent_instances.append(self.agents[agent_name])
319
+
320
+ # Apply configuration
321
+ if config:
322
+ kwargs.setdefault('max_concurrent', config.max_concurrent)
323
+ kwargs.setdefault('timeout', config.timeout)
324
+
325
+ # Execute pattern
326
+ start_time = time.time()
327
+ execution_id = f"exec_{int(time.time() * 1000)}"
328
+
329
+ logger.info(
330
+ f"Starting execution {execution_id}: pattern='{pattern}', "
331
+ f"agents={agents}, prompt='{prompt[:50]}...'"
332
+ )
333
+
334
+ try:
335
+ pattern_executor = self.patterns[pattern]
336
+ results = await pattern_executor.execute(agent_instances, prompt, deps, **kwargs)
337
+
338
+ execution_time = time.time() - start_time
339
+
340
+ # Update metrics
341
+ self._total_executions += 1
342
+ self._total_execution_time += execution_time
343
+ self._pattern_usage[pattern] = self._pattern_usage.get(pattern, 0) + 1
344
+
345
+ # Log results
346
+ successful_agents = sum(1 for r in results if r.success)
347
+ failed_agents = len(results) - successful_agents
348
+
349
+ logger.info(
350
+ f"Completed execution {execution_id} in {execution_time:.2f}s: "
351
+ f"{successful_agents} successful, {failed_agents} failed"
352
+ )
353
+
354
+ return results
355
+
356
+ except Exception as e:
357
+ execution_time = time.time() - start_time
358
+ logger.error(f"Execution {execution_id} failed after {execution_time:.2f}s: {e}")
359
+
360
+ raise ExecutionError(
361
+ f"Execution failed: {str(e)}",
362
+ original_error=e
363
+ )
364
+
365
+ def get_metrics(self) -> Dict[str, Any]:
366
+ """Get orchestrator metrics."""
367
+ return {
368
+ 'total_executions': self._total_executions,
369
+ 'total_execution_time': self._total_execution_time,
370
+ 'average_execution_time': (
371
+ self._total_execution_time / self._total_executions
372
+ if self._total_executions > 0 else 0
373
+ ),
374
+ 'pattern_usage': self._pattern_usage.copy(),
375
+ 'registered_agents': len(self.agents),
376
+ 'available_patterns': list(self.patterns.keys())
377
+ }
378
+
379
+ def reset_metrics(self):
380
+ """Reset orchestrator metrics."""
381
+ self._total_executions = 0
382
+ self._total_execution_time = 0.0
383
+ self._pattern_usage = {}
384
+
385
+ @property
386
+ def registered_agents(self) -> Dict[str, DjangoAgent]:
387
+ """Get copy of registered agents."""
388
+ return self.agents.copy()
389
+
390
+ def __repr__(self) -> str:
391
+ return f"SimpleOrchestrator(agents={len(self.agents)}, patterns={len(self.patterns)})"
@@ -0,0 +1,3 @@
1
+ """
2
+ Examples for Django Orchestrator.
3
+ """
@@ -0,0 +1,161 @@
1
+ """
2
+ Simple example demonstrating Django Orchestrator usage.
3
+ """
4
+
5
+ import asyncio
6
+ from dataclasses import dataclass
7
+ from pydantic import BaseModel
8
+ from django.contrib.auth.models import User
9
+
10
+ from django_cfg.modules.django_orchestrator import DjangoAgent, SimpleOrchestrator, DjangoDeps, RunContext
11
+
12
+
13
+ # Define output model
14
+ class GreetingResult(BaseModel):
15
+ """Result from greeting agent."""
16
+ greeting: str
17
+ personalized: bool
18
+ user_info: str
19
+
20
+
21
+ # Create agent
22
+ greeting_agent = DjangoAgent[DjangoDeps, GreetingResult](
23
+ name="greeting_agent",
24
+ deps_type=DjangoDeps,
25
+ output_type=GreetingResult,
26
+ instructions="Generate personalized greetings for users"
27
+ )
28
+
29
+
30
+ # Add tools to agent
31
+ @greeting_agent.tool
32
+ async def get_user_info(ctx: RunContext[DjangoDeps]) -> str:
33
+ """Get user information for personalization."""
34
+ user = ctx.deps.user
35
+ return f"User: {user.username}, Email: {user.email}, Joined: {user.date_joined}"
36
+
37
+
38
+ @greeting_agent.tool
39
+ async def get_time_of_day(ctx: RunContext[DjangoDeps]) -> str:
40
+ """Get current time of day for greeting."""
41
+ from datetime import datetime
42
+ hour = datetime.now().hour
43
+
44
+ if hour < 12:
45
+ return "morning"
46
+ elif hour < 17:
47
+ return "afternoon"
48
+ else:
49
+ return "evening"
50
+
51
+
52
+ async def simple_example():
53
+ """Run simple orchestrator example."""
54
+ print("🤖 Django Orchestrator Simple Example")
55
+ print("=" * 50)
56
+
57
+ # Create test user (in real app, get from request)
58
+ try:
59
+ user = await User.objects.aget(username='testuser')
60
+ except User.DoesNotExist:
61
+ user = await User.objects.acreate_user(
62
+ username='testuser',
63
+ email='test@example.com',
64
+ first_name='Test',
65
+ last_name='User'
66
+ )
67
+
68
+ # Create dependencies
69
+ deps = DjangoDeps(user=user)
70
+
71
+ # Create orchestrator
72
+ orchestrator = SimpleOrchestrator()
73
+ orchestrator.register_agent(greeting_agent)
74
+
75
+ # Execute single agent
76
+ print("\n1. Single Agent Execution:")
77
+ results = await orchestrator.execute(
78
+ pattern="sequential",
79
+ agents=["greeting_agent"],
80
+ prompt="Create a personalized greeting",
81
+ deps=deps
82
+ )
83
+
84
+ result = results[0]
85
+ print(f"✅ Agent: {result.agent_name}")
86
+ print(f"✅ Output: {result.output}")
87
+ print(f"✅ Execution Time: {result.execution_time:.2f}s")
88
+ print(f"✅ Tokens Used: {result.tokens_used}")
89
+
90
+ # Get metrics
91
+ print("\n2. Agent Metrics:")
92
+ metrics = greeting_agent.get_metrics()
93
+ for key, value in metrics.items():
94
+ print(f"📊 {key}: {value}")
95
+
96
+ # Get orchestrator metrics
97
+ print("\n3. Orchestrator Metrics:")
98
+ orch_metrics = orchestrator.get_metrics()
99
+ for key, value in orch_metrics.items():
100
+ print(f"📈 {key}: {value}")
101
+
102
+
103
+ async def multi_agent_example():
104
+ """Run multi-agent orchestrator example."""
105
+ print("\n🤖 Multi-Agent Example")
106
+ print("=" * 30)
107
+
108
+ # Create additional agents
109
+ analyzer_agent = DjangoAgent[DjangoDeps, BaseModel](
110
+ name="content_analyzer",
111
+ deps_type=DjangoDeps,
112
+ output_type=BaseModel,
113
+ instructions="Analyze content sentiment and topics"
114
+ )
115
+
116
+ formatter_agent = DjangoAgent[DjangoDeps, BaseModel](
117
+ name="content_formatter",
118
+ deps_type=DjangoDeps,
119
+ output_type=BaseModel,
120
+ instructions="Format content for display"
121
+ )
122
+
123
+ # Create orchestrator
124
+ orchestrator = SimpleOrchestrator()
125
+ orchestrator.register_agent(analyzer_agent)
126
+ orchestrator.register_agent(formatter_agent)
127
+
128
+ # Get user
129
+ user = await User.objects.aget(username='testuser')
130
+ deps = DjangoDeps(user=user)
131
+
132
+ # Execute sequential workflow
133
+ print("\n1. Sequential Workflow:")
134
+ results = await orchestrator.execute(
135
+ pattern="sequential",
136
+ agents=["content_analyzer", "content_formatter"],
137
+ prompt="Analyze and format this content: Hello world!",
138
+ deps=deps
139
+ )
140
+
141
+ for i, result in enumerate(results, 1):
142
+ print(f"Step {i} - {result.agent_name}: {result.success}")
143
+
144
+ # Execute parallel workflow
145
+ print("\n2. Parallel Workflow:")
146
+ results = await orchestrator.execute(
147
+ pattern="parallel",
148
+ agents=["content_analyzer", "content_formatter"],
149
+ prompt="Process content in parallel",
150
+ deps=deps,
151
+ max_concurrent=2
152
+ )
153
+
154
+ successful = sum(1 for r in results if r.success)
155
+ print(f"✅ {successful}/{len(results)} agents completed successfully")
156
+
157
+
158
+ if __name__ == "__main__":
159
+ # Run examples
160
+ asyncio.run(simple_example())
161
+ asyncio.run(multi_agent_example())
@@ -0,0 +1,14 @@
1
+ """
2
+ Django integration components for orchestrator.
3
+ """
4
+
5
+ from .registry import AgentRegistry, initialize_registry
6
+ from .signals import setup_signals
7
+ from .middleware import OrchestratorMiddleware
8
+
9
+ __all__ = [
10
+ "AgentRegistry",
11
+ "initialize_registry",
12
+ "setup_signals",
13
+ "OrchestratorMiddleware",
14
+ ]
@@ -0,0 +1,80 @@
1
+ """
2
+ Django middleware for orchestrator integration.
3
+ """
4
+
5
+ import time
6
+ import logging
7
+ from typing import Callable
8
+ from django.http import HttpRequest, HttpResponse
9
+ from django.utils.deprecation import MiddlewareMixin
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class OrchestratorMiddleware(MiddlewareMixin):
15
+ """
16
+ Middleware for Django Orchestrator integration.
17
+
18
+ Provides:
19
+ - Request context for agents
20
+ - Performance monitoring
21
+ - Error tracking
22
+ - User session integration
23
+ """
24
+
25
+ def __init__(self, get_response: Callable):
26
+ """Initialize middleware."""
27
+ self.get_response = get_response
28
+ super().__init__(get_response)
29
+
30
+ def process_request(self, request: HttpRequest):
31
+ """Process incoming request."""
32
+ # Add orchestrator context to request
33
+ request.orchestrator_start_time = time.time()
34
+ request.orchestrator_context = {
35
+ 'user_agent': request.META.get('HTTP_USER_AGENT', ''),
36
+ 'ip_address': self._get_client_ip(request),
37
+ 'session_key': request.session.session_key if hasattr(request, 'session') else None,
38
+ }
39
+
40
+ logger.debug(f"Orchestrator middleware: Processing request {request.path}")
41
+
42
+ def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse:
43
+ """Process outgoing response."""
44
+ if hasattr(request, 'orchestrator_start_time'):
45
+ duration = time.time() - request.orchestrator_start_time
46
+
47
+ # Add performance header
48
+ response['X-Orchestrator-Duration'] = f"{duration:.3f}s"
49
+
50
+ # Log slow requests
51
+ if duration > 5.0: # 5 seconds threshold
52
+ logger.warning(
53
+ f"Slow request detected: {request.path} took {duration:.3f}s"
54
+ )
55
+
56
+ return response
57
+
58
+ def process_exception(self, request: HttpRequest, exception: Exception):
59
+ """Process exceptions."""
60
+ if hasattr(request, 'orchestrator_context'):
61
+ logger.error(
62
+ f"Exception in orchestrator context: {exception}",
63
+ extra={
64
+ 'request_path': request.path,
65
+ 'user_id': request.user.id if hasattr(request, 'user') and request.user.is_authenticated else None,
66
+ 'orchestrator_context': request.orchestrator_context,
67
+ }
68
+ )
69
+
70
+ # Don't handle the exception, just log it
71
+ return None
72
+
73
+ def _get_client_ip(self, request: HttpRequest) -> str:
74
+ """Get client IP address."""
75
+ x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
76
+ if x_forwarded_for:
77
+ ip = x_forwarded_for.split(',')[0]
78
+ else:
79
+ ip = request.META.get('REMOTE_ADDR')
80
+ return ip or 'unknown'