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.
Files changed (246) 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/templates/guide.md +266 -0
  198. django_cfg/modules/django_twilio/twilio_service.py +2 -2
  199. django_cfg/modules/django_unfold/__init__.py +69 -0
  200. django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
  201. django_cfg/modules/django_unfold/dashboard.py +278 -0
  202. django_cfg/modules/django_unfold/icons/README.md +145 -0
  203. django_cfg/modules/django_unfold/icons/__init__.py +12 -0
  204. django_cfg/modules/django_unfold/icons/constants.py +2851 -0
  205. django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
  206. django_cfg/modules/django_unfold/models/__init__.py +42 -0
  207. django_cfg/modules/django_unfold/models/config.py +601 -0
  208. django_cfg/modules/django_unfold/models/dashboard.py +206 -0
  209. django_cfg/modules/django_unfold/models/dropdown.py +40 -0
  210. django_cfg/modules/django_unfold/models/navigation.py +73 -0
  211. django_cfg/modules/django_unfold/models/tabs.py +25 -0
  212. django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
  213. django_cfg/modules/django_unfold/utils.py +140 -0
  214. django_cfg/registry/__init__.py +23 -0
  215. django_cfg/registry/core.py +61 -0
  216. django_cfg/registry/exceptions.py +11 -0
  217. django_cfg/registry/modules.py +12 -0
  218. django_cfg/registry/services.py +26 -0
  219. django_cfg/registry/third_party.py +52 -0
  220. django_cfg/routing/__init__.py +19 -0
  221. django_cfg/routing/callbacks.py +198 -0
  222. django_cfg/routing/routers.py +48 -0
  223. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
  224. django_cfg/templatetags/__init__.py +0 -0
  225. django_cfg/templatetags/django_cfg.py +33 -0
  226. django_cfg/urls.py +33 -0
  227. django_cfg/utils/path_resolution.py +1 -1
  228. django_cfg/utils/smart_defaults.py +7 -61
  229. django_cfg/utils/toolkit.py +663 -0
  230. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
  231. django_cfg-1.2.0.dist-info/RECORD +441 -0
  232. django_cfg/apps/tasks/@docs/README.md +0 -195
  233. django_cfg/archive/django_sample.zip +0 -0
  234. django_cfg/models/unfold.py +0 -271
  235. django_cfg/modules/unfold/__init__.py +0 -29
  236. django_cfg/modules/unfold/dashboard.py +0 -318
  237. django_cfg/pyproject.toml +0 -370
  238. django_cfg/routers.py +0 -83
  239. django_cfg-1.1.81.dist-info/RECORD +0 -278
  240. /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
  241. /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
  242. /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
  243. /django_cfg/{version_check.py → utils/version_check.py} +0 -0
  244. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
  245. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
  246. {django_cfg-1.1.81.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,281 @@
1
+ """
2
+ Django Agent - Wrapper around Pydantic AI Agent with Django integration.
3
+ """
4
+
5
+ import time
6
+ import logging
7
+ from typing import TypeVar, Generic, Type, Any, Optional, Dict, Callable
8
+ from dataclasses import dataclass
9
+
10
+ from pydantic_ai import Agent
11
+ from pydantic_ai.models import Model
12
+
13
+ from .exceptions import ExecutionError, ConfigurationError
14
+ from .models import ExecutionResult
15
+
16
+ # Type variables for generic typing
17
+ DepsT = TypeVar('DepsT')
18
+ OutputT = TypeVar('OutputT')
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class DjangoAgent(Generic[DepsT, OutputT]):
24
+ """
25
+ Django-integrated agent wrapper around Pydantic AI Agent.
26
+
27
+ Provides Django-specific functionality like:
28
+ - Integration with django_llm module
29
+ - Caching support
30
+ - Metrics collection
31
+ - Error handling
32
+ - Type safety
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ name: str,
38
+ deps_type: Type[DepsT],
39
+ output_type: Type[OutputT],
40
+ instructions: str,
41
+ model: Optional[str] = None,
42
+ llm_client: Optional[Any] = None,
43
+ timeout: int = 300,
44
+ max_retries: int = 3,
45
+ enable_caching: bool = True
46
+ ):
47
+ """
48
+ Initialize Django Agent.
49
+
50
+ Args:
51
+ name: Unique agent identifier
52
+ deps_type: Type for dependency injection (must be dataclass)
53
+ output_type: Pydantic model for output validation
54
+ instructions: System prompt for the agent
55
+ model: Override model (uses client default if None)
56
+ llm_client: Optional LLM client (uses default if None)
57
+ timeout: Execution timeout in seconds
58
+ max_retries: Maximum retry attempts
59
+ enable_caching: Whether to enable result caching
60
+ """
61
+ self.name = name
62
+ self.deps_type = deps_type
63
+ self.output_type = output_type
64
+ self.instructions = instructions
65
+ self.timeout = timeout
66
+ self.max_retries = max_retries
67
+ self.enable_caching = enable_caching
68
+
69
+ # Get LLM client
70
+ self.llm_client = llm_client or self._get_default_llm_client()
71
+
72
+ # Determine model to use
73
+ model_name = model or self._get_default_model()
74
+
75
+ # Create Pydantic AI agent
76
+ self.agent = Agent[DepsT, OutputT](
77
+ model=model_name,
78
+ deps_type=deps_type,
79
+ output_type=output_type,
80
+ instructions=instructions,
81
+ retries=max_retries
82
+ )
83
+
84
+ # Initialize metrics
85
+ self._execution_count = 0
86
+ self._total_execution_time = 0.0
87
+ self._error_count = 0
88
+ self._cache_hits = 0
89
+
90
+ logger.info(f"Initialized DjangoAgent '{name}' with model '{model_name}'")
91
+
92
+ async def run(self, prompt: str, deps: DepsT, **kwargs) -> ExecutionResult:
93
+ """
94
+ Run agent with Django-specific enhancements.
95
+
96
+ Args:
97
+ prompt: User prompt/instruction
98
+ deps: Dependencies instance
99
+ **kwargs: Additional Pydantic AI parameters
100
+
101
+ Returns:
102
+ ExecutionResult with output, usage, and metadata
103
+ """
104
+ start_time = time.time()
105
+ self._execution_count += 1
106
+
107
+ # Check cache if enabled
108
+ if self.enable_caching:
109
+ cached_result = await self._check_cache(prompt, deps)
110
+ if cached_result:
111
+ self._cache_hits += 1
112
+ logger.debug(f"Cache hit for agent '{self.name}'")
113
+ return ExecutionResult(
114
+ agent_name=self.name,
115
+ output=cached_result,
116
+ execution_time=0.0,
117
+ cached=True
118
+ )
119
+
120
+ try:
121
+ # Execute agent
122
+ logger.debug(f"Executing agent '{self.name}' with prompt: {prompt[:100]}...")
123
+
124
+ result = await self.agent.run(prompt, deps=deps, **kwargs)
125
+
126
+ execution_time = time.time() - start_time
127
+ self._total_execution_time += execution_time
128
+
129
+ # Extract metrics
130
+ tokens_used = 0
131
+ cost = 0.0
132
+ if hasattr(result, 'usage') and result.usage:
133
+ tokens_used = getattr(result.usage, 'total_tokens', 0)
134
+ # Calculate cost based on model and tokens (implement based on your pricing)
135
+ cost = self._calculate_cost(tokens_used)
136
+
137
+ execution_result = ExecutionResult(
138
+ agent_name=self.name,
139
+ output=result.output,
140
+ execution_time=execution_time,
141
+ tokens_used=tokens_used,
142
+ cost=cost
143
+ )
144
+
145
+ # Cache result if enabled
146
+ if self.enable_caching:
147
+ await self._cache_result(prompt, deps, result.output)
148
+
149
+ logger.info(
150
+ f"Agent '{self.name}' executed successfully in {execution_time:.2f}s "
151
+ f"(tokens: {tokens_used}, cost: ${cost:.4f})"
152
+ )
153
+
154
+ return execution_result
155
+
156
+ except Exception as e:
157
+ self._error_count += 1
158
+ execution_time = time.time() - start_time
159
+
160
+ logger.error(f"Agent '{self.name}' execution failed: {e}")
161
+
162
+ raise ExecutionError(
163
+ f"Agent '{self.name}' execution failed: {str(e)}",
164
+ agent_name=self.name,
165
+ original_error=e
166
+ )
167
+
168
+ def tool(self, func: Callable) -> Callable:
169
+ """
170
+ Register tool with agent.
171
+
172
+ Args:
173
+ func: Function to register as agent tool
174
+
175
+ Returns:
176
+ Decorated function
177
+ """
178
+ return self.agent.tool(func)
179
+
180
+ def get_metrics(self) -> Dict[str, Any]:
181
+ """Get agent execution metrics."""
182
+ return {
183
+ 'name': self.name,
184
+ 'execution_count': self._execution_count,
185
+ 'total_execution_time': self._total_execution_time,
186
+ 'average_execution_time': (
187
+ self._total_execution_time / self._execution_count
188
+ if self._execution_count > 0 else 0
189
+ ),
190
+ 'error_count': self._error_count,
191
+ 'success_rate': (
192
+ (self._execution_count - self._error_count) / self._execution_count
193
+ if self._execution_count > 0 else 0
194
+ ),
195
+ 'cache_hits': self._cache_hits,
196
+ 'cache_hit_rate': (
197
+ self._cache_hits / self._execution_count
198
+ if self._execution_count > 0 else 0
199
+ )
200
+ }
201
+
202
+ def reset_metrics(self):
203
+ """Reset agent metrics."""
204
+ self._execution_count = 0
205
+ self._total_execution_time = 0.0
206
+ self._error_count = 0
207
+ self._cache_hits = 0
208
+
209
+ def _get_default_llm_client(self):
210
+ """Get default LLM client from django_llm module."""
211
+ try:
212
+ from django_cfg.modules.django_llm import DjangoLLM
213
+ llm_service = DjangoLLM()
214
+ if llm_service.is_configured:
215
+ return llm_service.client
216
+ except ImportError:
217
+ logger.warning("django_llm module not available, using default client")
218
+
219
+ return None
220
+
221
+ def _get_default_model(self) -> str:
222
+ """Get default model name."""
223
+ if self.llm_client and hasattr(self.llm_client, 'primary_provider'):
224
+ if self.llm_client.primary_provider == 'openrouter':
225
+ return 'openai:gpt-4o-mini'
226
+ elif self.llm_client.primary_provider == 'openai':
227
+ return 'openai:gpt-4o-mini'
228
+
229
+ return 'openai:gpt-4o-mini' # Default fallback
230
+
231
+ async def _check_cache(self, prompt: str, deps: DepsT) -> Optional[Any]:
232
+ """Check cache for existing result."""
233
+ if not self.llm_client or not hasattr(self.llm_client, 'cache'):
234
+ return None
235
+
236
+ try:
237
+ cache_key = self._generate_cache_key(prompt, deps)
238
+ return self.llm_client.cache.get(cache_key)
239
+ except Exception as e:
240
+ logger.warning(f"Cache check failed: {e}")
241
+ return None
242
+
243
+ async def _cache_result(self, prompt: str, deps: DepsT, result: Any):
244
+ """Cache execution result."""
245
+ if not self.llm_client or not hasattr(self.llm_client, 'cache'):
246
+ return
247
+
248
+ try:
249
+ cache_key = self._generate_cache_key(prompt, deps)
250
+ self.llm_client.cache.set(cache_key, result)
251
+ except Exception as e:
252
+ logger.warning(f"Cache storage failed: {e}")
253
+
254
+ def _generate_cache_key(self, prompt: str, deps: DepsT) -> str:
255
+ """Generate cache key for prompt and dependencies."""
256
+ # Create a simple hash-based key
257
+ import hashlib
258
+
259
+ # Include agent name, prompt, and relevant deps data
260
+ key_data = f"{self.name}:{prompt}"
261
+
262
+ # Add user info if available
263
+ if hasattr(deps, 'user') and deps.user:
264
+ key_data += f":user_{deps.user.id}"
265
+
266
+ # Add other relevant dependency data
267
+ if hasattr(deps, 'to_dict'):
268
+ deps_str = str(sorted(deps.to_dict().items()))
269
+ key_data += f":deps_{deps_str}"
270
+
271
+ return hashlib.md5(key_data.encode()).hexdigest()
272
+
273
+ def _calculate_cost(self, tokens_used: int) -> float:
274
+ """Calculate cost based on tokens used."""
275
+ # Implement based on your pricing model
276
+ # This is a simple example
277
+ cost_per_1k_tokens = 0.002 # Example: $0.002 per 1K tokens
278
+ return (tokens_used / 1000) * cost_per_1k_tokens
279
+
280
+ def __repr__(self) -> str:
281
+ return f"DjangoAgent(name='{self.name}', deps_type={self.deps_type.__name__}, output_type={self.output_type.__name__})"
@@ -0,0 +1,154 @@
1
+ """
2
+ Dependency injection classes for Django Orchestrator.
3
+ """
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Dict, Any, Optional, TYPE_CHECKING
7
+ from django.http import HttpRequest
8
+
9
+ # Re-export RunContext from pydantic_ai for convenience
10
+ from pydantic_ai import RunContext
11
+
12
+ if TYPE_CHECKING:
13
+ from django.contrib.auth.models import AbstractUser
14
+
15
+ __all__ = ["DjangoDeps", "ContentDeps", "RunContext"]
16
+
17
+
18
+ @dataclass
19
+ class DjangoDeps:
20
+ """Standard Django dependencies for agent execution."""
21
+
22
+ user: "AbstractUser"
23
+ request: Optional[HttpRequest] = None
24
+ session_data: Dict[str, Any] = field(default_factory=dict)
25
+
26
+ @classmethod
27
+ async def from_request(cls, request: HttpRequest) -> 'DjangoDeps':
28
+ """Create dependencies from Django request."""
29
+ return cls(
30
+ user=request.user,
31
+ request=request,
32
+ session_data=dict(request.session) if request.session else {}
33
+ )
34
+
35
+ @classmethod
36
+ async def from_user_id(cls, user_id: int) -> 'DjangoDeps':
37
+ """Create dependencies from user ID."""
38
+ from django.contrib.auth import get_user_model
39
+ User = get_user_model()
40
+ user = await User.objects.aget(id=user_id)
41
+ return cls(user=user)
42
+
43
+ @classmethod
44
+ async def from_user(cls, user: "AbstractUser") -> 'DjangoDeps':
45
+ """Create dependencies from user instance."""
46
+ return cls(user=user)
47
+
48
+ def to_dict(self) -> Dict[str, Any]:
49
+ """Convert to dictionary for serialization."""
50
+ return {
51
+ 'user_id': self.user.id,
52
+ 'username': self.user.username,
53
+ 'session_data': self.session_data,
54
+ 'has_request': self.request is not None
55
+ }
56
+
57
+
58
+ @dataclass
59
+ class ContentDeps(DjangoDeps):
60
+ """Content-specific dependencies."""
61
+
62
+ content_id: Optional[int] = None
63
+ content_type: str = "article"
64
+ target_audience: str = "general"
65
+
66
+ async def get_content(self):
67
+ """Get content object if content_id is provided."""
68
+ if not self.content_id:
69
+ return None
70
+
71
+ # Import here to avoid circular imports
72
+ from django.apps import apps
73
+
74
+ try:
75
+ Content = apps.get_model('your_app', 'Content') # Adjust app name
76
+ return await Content.objects.aget(id=self.content_id)
77
+ except Exception:
78
+ return None
79
+
80
+
81
+ @dataclass
82
+ class DataProcessingDeps(DjangoDeps):
83
+ """Dependencies for data processing agents."""
84
+
85
+ dataset_id: Optional[int] = None
86
+ processing_options: Dict[str, Any] = field(default_factory=dict)
87
+ batch_size: int = 100
88
+
89
+ async def get_dataset(self):
90
+ """Get dataset object if dataset_id is provided."""
91
+ if not self.dataset_id:
92
+ return None
93
+
94
+ # Import here to avoid circular imports
95
+ from django.apps import apps
96
+
97
+ try:
98
+ Dataset = apps.get_model('your_app', 'Dataset') # Adjust app name
99
+ return await Dataset.objects.aget(id=self.dataset_id)
100
+ except Exception:
101
+ return None
102
+
103
+
104
+ @dataclass
105
+ class BusinessLogicDeps(DjangoDeps):
106
+ """Dependencies for business logic agents."""
107
+
108
+ business_context: Dict[str, Any] = field(default_factory=dict)
109
+ rules_version: str = "latest"
110
+ organization_id: Optional[int] = None
111
+
112
+ async def get_organization(self):
113
+ """Get organization object if organization_id is provided."""
114
+ if not self.organization_id:
115
+ return None
116
+
117
+ # Import here to avoid circular imports
118
+ from django.apps import apps
119
+
120
+ try:
121
+ Organization = apps.get_model('your_app', 'Organization') # Adjust app name
122
+ return await Organization.objects.aget(id=self.organization_id)
123
+ except Exception:
124
+ return None
125
+
126
+
127
+ @dataclass
128
+ class IntegrationDeps(DjangoDeps):
129
+ """Dependencies for external integration agents."""
130
+
131
+ api_credentials: Dict[str, str] = field(default_factory=dict)
132
+ service_config: Dict[str, Any] = field(default_factory=dict)
133
+ rate_limit_key: Optional[str] = None
134
+
135
+ def get_api_key(self, service_name: str) -> Optional[str]:
136
+ """Get API key for specific service."""
137
+ return self.api_credentials.get(f"{service_name}_api_key")
138
+
139
+ def get_service_config(self, service_name: str) -> Dict[str, Any]:
140
+ """Get configuration for specific service."""
141
+ return self.service_config.get(service_name, {})
142
+
143
+
144
+ @dataclass
145
+ class TestDeps:
146
+ """Simple dependencies for testing."""
147
+
148
+ user_id: int
149
+ test_data: Dict[str, Any] = field(default_factory=dict)
150
+ mock_responses: Dict[str, Any] = field(default_factory=dict)
151
+
152
+ def get_mock_response(self, key: str) -> Any:
153
+ """Get mock response for testing."""
154
+ return self.mock_responses.get(key)
@@ -0,0 +1,66 @@
1
+ """
2
+ Exception classes for Django Agents.
3
+ """
4
+
5
+ from typing import Optional, Any
6
+
7
+
8
+ class AgentError(Exception):
9
+ """Base exception for Django Agents."""
10
+
11
+ def __init__(self, message: str, details: Optional[dict] = None):
12
+ super().__init__(message)
13
+ self.message = message
14
+ self.details = details or {}
15
+
16
+
17
+ class AgentNotFoundError(AgentError):
18
+ """Raised when requested agent is not found in registry."""
19
+
20
+ def __init__(self, agent_name: str):
21
+ message = f"Agent '{agent_name}' not found in registry"
22
+ super().__init__(message)
23
+ self.agent_name = agent_name
24
+
25
+
26
+ class ExecutionError(AgentError):
27
+ """Raised when agent execution fails."""
28
+
29
+ def __init__(
30
+ self,
31
+ message: str,
32
+ agent_name: Optional[str] = None,
33
+ original_error: Optional[Exception] = None
34
+ ):
35
+ super().__init__(message)
36
+ self.agent_name = agent_name
37
+ self.original_error = original_error
38
+
39
+
40
+ class ValidationError(AgentError):
41
+ """Raised when input/output validation fails."""
42
+
43
+ def __init__(self, message: str, validation_errors: Optional[list] = None):
44
+ super().__init__(message)
45
+ self.validation_errors = validation_errors or []
46
+
47
+
48
+ class ConfigurationError(AgentError):
49
+ """Raised when agent configuration is invalid."""
50
+ pass
51
+
52
+
53
+ class PermissionError(AgentError):
54
+ """Raised when user lacks required permissions."""
55
+
56
+ def __init__(self, message: str, required_permission: Optional[str] = None):
57
+ super().__init__(message)
58
+ self.required_permission = required_permission
59
+
60
+
61
+ class TimeoutError(AgentError):
62
+ """Raised when agent execution times out."""
63
+
64
+ def __init__(self, message: str, timeout_seconds: Optional[float] = None):
65
+ super().__init__(message)
66
+ self.timeout_seconds = timeout_seconds
@@ -0,0 +1,106 @@
1
+ """
2
+ Data models for Django Orchestrator.
3
+ """
4
+
5
+ from dataclasses import dataclass, field
6
+ from datetime import datetime
7
+ from typing import Any, Dict, List, Optional, Literal
8
+ from pydantic import BaseModel, Field, ConfigDict
9
+
10
+
11
+ @dataclass
12
+ class ExecutionResult:
13
+ """Result from agent execution with metadata."""
14
+
15
+ agent_name: str
16
+ output: Any
17
+ execution_time: float
18
+ tokens_used: int = 0
19
+ cost: float = 0.0
20
+ cached: bool = False
21
+ error: Optional[str] = None
22
+ timestamp: datetime = field(default_factory=datetime.now)
23
+
24
+ @property
25
+ def success(self) -> bool:
26
+ """Check if execution was successful."""
27
+ return self.error is None
28
+
29
+
30
+ class WorkflowConfig(BaseModel):
31
+ """Configuration for workflow execution."""
32
+
33
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
34
+
35
+ name: str = Field(..., description="Workflow identifier")
36
+ timeout: int = Field(default=300, ge=1, le=3600, description="Timeout in seconds")
37
+ max_retries: int = Field(default=3, ge=0, le=10, description="Maximum retry attempts")
38
+ execution_strategy: Literal["sequential", "parallel", "conditional"] = Field(
39
+ default="sequential", description="Execution strategy"
40
+ )
41
+ priority: int = Field(default=1, ge=1, le=10, description="Execution priority")
42
+ max_concurrent: int = Field(default=5, ge=1, le=20, description="Max concurrent executions")
43
+
44
+
45
+ class AgentDefinition(BaseModel):
46
+ """Agent definition for registry storage."""
47
+
48
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
49
+
50
+ name: str = Field(..., description="Agent identifier")
51
+ instructions: str = Field(..., description="Agent instructions")
52
+ deps_type: str = Field(..., description="Dependencies type name")
53
+ output_type: str = Field(..., description="Output type name")
54
+ model: str = Field(default="openai:gpt-4o-mini", description="LLM model to use")
55
+ timeout: int = Field(default=300, ge=1, le=3600, description="Execution timeout")
56
+ max_retries: int = Field(default=3, ge=0, le=10, description="Maximum retries")
57
+ is_active: bool = Field(default=True, description="Whether agent is active")
58
+ created_at: datetime = Field(default_factory=datetime.now)
59
+ updated_at: datetime = Field(default_factory=datetime.now)
60
+
61
+
62
+ class ProcessResult(BaseModel):
63
+ """Standard processing result model."""
64
+
65
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
66
+
67
+ success: bool = Field(..., description="Whether processing was successful")
68
+ message: str = Field(..., description="Result message")
69
+ data: Dict[str, Any] = Field(default_factory=dict, description="Additional data")
70
+ timestamp: datetime = Field(default_factory=datetime.now)
71
+
72
+
73
+ class AnalysisResult(BaseModel):
74
+ """Standard analysis result model."""
75
+
76
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
77
+
78
+ sentiment: str = Field(..., description="Sentiment analysis result")
79
+ confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score")
80
+ topics: List[str] = Field(default_factory=list, description="Extracted topics")
81
+ keywords: List[str] = Field(default_factory=list, description="Key terms")
82
+ summary: str = Field(default="", description="Content summary")
83
+
84
+
85
+ class ValidationResult(BaseModel):
86
+ """Standard validation result model."""
87
+
88
+ model_config = ConfigDict(validate_assignment=True, extra="forbid")
89
+
90
+ is_valid: bool = Field(..., description="Whether input is valid")
91
+ errors: List[str] = Field(default_factory=list, description="Validation errors")
92
+ warnings: List[str] = Field(default_factory=list, description="Validation warnings")
93
+ suggestions: List[str] = Field(default_factory=list, description="Improvement suggestions")
94
+
95
+
96
+ @dataclass
97
+ class ErrorContext:
98
+ """Error context information."""
99
+
100
+ agent_name: str
101
+ prompt: str
102
+ error_type: str
103
+ error_message: str
104
+ timestamp: datetime
105
+ retry_count: int = 0
106
+ stack_trace: Optional[str] = None