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.
- django_cfg/__init__.py +20 -448
- django_cfg/apps/accounts/README.md +3 -3
- django_cfg/apps/accounts/admin/__init__.py +0 -2
- django_cfg/apps/accounts/admin/activity.py +2 -9
- django_cfg/apps/accounts/admin/filters.py +0 -42
- django_cfg/apps/accounts/admin/inlines.py +8 -8
- django_cfg/apps/accounts/admin/otp.py +5 -5
- django_cfg/apps/accounts/admin/registration_source.py +1 -8
- django_cfg/apps/accounts/admin/user.py +12 -20
- django_cfg/apps/accounts/managers/user_manager.py +2 -129
- django_cfg/apps/accounts/migrations/0006_remove_twilioresponse_otp_secret_and_more.py +46 -0
- django_cfg/apps/accounts/models.py +3 -123
- django_cfg/apps/accounts/serializers/otp.py +40 -44
- django_cfg/apps/accounts/serializers/profile.py +0 -2
- django_cfg/apps/accounts/services/otp_service.py +98 -186
- django_cfg/apps/accounts/signals.py +25 -15
- django_cfg/apps/accounts/utils/auth_email_service.py +84 -0
- django_cfg/apps/accounts/views/otp.py +35 -36
- django_cfg/apps/agents/README.md +129 -0
- django_cfg/apps/agents/__init__.py +68 -0
- django_cfg/apps/agents/admin/__init__.py +17 -0
- django_cfg/apps/agents/admin/execution_admin.py +460 -0
- django_cfg/apps/agents/admin/registry_admin.py +360 -0
- django_cfg/apps/agents/admin/toolsets_admin.py +482 -0
- django_cfg/apps/agents/apps.py +29 -0
- django_cfg/apps/agents/core/__init__.py +20 -0
- django_cfg/apps/agents/core/agent.py +281 -0
- django_cfg/apps/agents/core/dependencies.py +154 -0
- django_cfg/apps/agents/core/exceptions.py +66 -0
- django_cfg/apps/agents/core/models.py +106 -0
- django_cfg/apps/agents/core/orchestrator.py +391 -0
- django_cfg/apps/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- django_cfg/apps/agents/integration/__init__.py +14 -0
- django_cfg/apps/agents/integration/middleware.py +80 -0
- django_cfg/apps/agents/integration/registry.py +345 -0
- django_cfg/apps/agents/integration/signals.py +50 -0
- django_cfg/apps/agents/management/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/__init__.py +3 -0
- django_cfg/apps/agents/management/commands/create_agent.py +365 -0
- django_cfg/apps/agents/management/commands/orchestrator_status.py +191 -0
- django_cfg/apps/agents/managers/__init__.py +23 -0
- django_cfg/apps/agents/managers/execution.py +236 -0
- django_cfg/apps/agents/managers/registry.py +254 -0
- django_cfg/apps/agents/managers/toolsets.py +496 -0
- django_cfg/apps/agents/migrations/0001_initial.py +286 -0
- django_cfg/apps/agents/migrations/__init__.py +5 -0
- django_cfg/apps/agents/models/__init__.py +15 -0
- django_cfg/apps/agents/models/execution.py +215 -0
- django_cfg/apps/agents/models/registry.py +220 -0
- django_cfg/apps/agents/models/toolsets.py +305 -0
- django_cfg/apps/agents/patterns/__init__.py +24 -0
- django_cfg/apps/agents/patterns/content_agents.py +234 -0
- django_cfg/apps/agents/toolsets/__init__.py +15 -0
- django_cfg/apps/agents/toolsets/cache_toolset.py +285 -0
- django_cfg/apps/agents/toolsets/django_toolset.py +220 -0
- django_cfg/apps/agents/toolsets/file_toolset.py +324 -0
- django_cfg/apps/agents/toolsets/orm_toolset.py +319 -0
- django_cfg/apps/agents/urls.py +46 -0
- django_cfg/apps/knowbase/README.md +150 -0
- django_cfg/apps/knowbase/__init__.py +27 -0
- django_cfg/apps/knowbase/admin/__init__.py +23 -0
- django_cfg/apps/knowbase/admin/archive_admin.py +857 -0
- django_cfg/apps/knowbase/admin/chat_admin.py +386 -0
- django_cfg/apps/knowbase/admin/document_admin.py +650 -0
- django_cfg/apps/knowbase/admin/external_data_admin.py +685 -0
- django_cfg/apps/knowbase/apps.py +81 -0
- django_cfg/apps/knowbase/config/README.md +176 -0
- django_cfg/apps/knowbase/config/__init__.py +51 -0
- django_cfg/apps/knowbase/config/constance_fields.py +186 -0
- django_cfg/apps/knowbase/config/constance_settings.py +200 -0
- django_cfg/apps/knowbase/config/settings.py +444 -0
- django_cfg/apps/knowbase/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- django_cfg/apps/knowbase/management/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/__init__.py +0 -0
- django_cfg/apps/knowbase/management/commands/knowbase_stats.py +158 -0
- django_cfg/apps/knowbase/management/commands/setup_knowbase.py +59 -0
- django_cfg/apps/knowbase/managers/__init__.py +22 -0
- django_cfg/apps/knowbase/managers/archive.py +426 -0
- django_cfg/apps/knowbase/managers/base.py +32 -0
- django_cfg/apps/knowbase/managers/chat.py +141 -0
- django_cfg/apps/knowbase/managers/document.py +203 -0
- django_cfg/apps/knowbase/managers/external_data.py +471 -0
- django_cfg/apps/knowbase/migrations/0001_initial.py +427 -0
- django_cfg/apps/knowbase/migrations/0002_archiveitem_archiveitemchunk_documentarchive_and_more.py +434 -0
- django_cfg/apps/knowbase/migrations/__init__.py +5 -0
- django_cfg/apps/knowbase/mixins/__init__.py +15 -0
- django_cfg/apps/knowbase/mixins/config.py +108 -0
- django_cfg/apps/knowbase/mixins/creator.py +81 -0
- django_cfg/apps/knowbase/mixins/examples/vehicle_model_example.py +199 -0
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +813 -0
- django_cfg/apps/knowbase/mixins/service.py +362 -0
- django_cfg/apps/knowbase/models/__init__.py +41 -0
- django_cfg/apps/knowbase/models/archive.py +599 -0
- django_cfg/apps/knowbase/models/base.py +58 -0
- django_cfg/apps/knowbase/models/chat.py +157 -0
- django_cfg/apps/knowbase/models/document.py +267 -0
- django_cfg/apps/knowbase/models/external_data.py +376 -0
- django_cfg/apps/knowbase/serializers/__init__.py +68 -0
- django_cfg/apps/knowbase/serializers/archive_serializers.py +386 -0
- django_cfg/apps/knowbase/serializers/chat_serializers.py +137 -0
- django_cfg/apps/knowbase/serializers/document_serializers.py +94 -0
- django_cfg/apps/knowbase/serializers/external_data_serializers.py +256 -0
- django_cfg/apps/knowbase/serializers/public_serializers.py +74 -0
- django_cfg/apps/knowbase/services/__init__.py +40 -0
- django_cfg/apps/knowbase/services/archive/__init__.py +42 -0
- django_cfg/apps/knowbase/services/archive/archive_service.py +541 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +791 -0
- django_cfg/apps/knowbase/services/archive/exceptions.py +52 -0
- django_cfg/apps/knowbase/services/archive/extraction_service.py +508 -0
- django_cfg/apps/knowbase/services/archive/vectorization_service.py +362 -0
- django_cfg/apps/knowbase/services/base.py +53 -0
- django_cfg/apps/knowbase/services/chat_service.py +239 -0
- django_cfg/apps/knowbase/services/document_service.py +144 -0
- django_cfg/apps/knowbase/services/embedding/__init__.py +43 -0
- django_cfg/apps/knowbase/services/embedding/async_processor.py +244 -0
- django_cfg/apps/knowbase/services/embedding/batch_processor.py +250 -0
- django_cfg/apps/knowbase/services/embedding/batch_result.py +61 -0
- django_cfg/apps/knowbase/services/embedding/models.py +229 -0
- django_cfg/apps/knowbase/services/embedding/processors.py +148 -0
- django_cfg/apps/knowbase/services/embedding/utils.py +176 -0
- django_cfg/apps/knowbase/services/prompt_builder.py +191 -0
- django_cfg/apps/knowbase/services/search_service.py +293 -0
- django_cfg/apps/knowbase/signals/__init__.py +21 -0
- django_cfg/apps/knowbase/signals/archive_signals.py +211 -0
- django_cfg/apps/knowbase/signals/chat_signals.py +37 -0
- django_cfg/apps/knowbase/signals/document_signals.py +143 -0
- django_cfg/apps/knowbase/signals/external_data_signals.py +157 -0
- django_cfg/apps/knowbase/tasks/__init__.py +39 -0
- django_cfg/apps/knowbase/tasks/archive_tasks.py +316 -0
- django_cfg/apps/knowbase/tasks/document_processing.py +341 -0
- django_cfg/apps/knowbase/tasks/external_data_tasks.py +341 -0
- django_cfg/apps/knowbase/tasks/maintenance.py +195 -0
- django_cfg/apps/knowbase/urls.py +43 -0
- django_cfg/apps/knowbase/utils/__init__.py +12 -0
- django_cfg/apps/knowbase/utils/chunk_settings.py +261 -0
- django_cfg/apps/knowbase/utils/text_processing.py +375 -0
- django_cfg/apps/knowbase/utils/validation.py +99 -0
- django_cfg/apps/knowbase/views/__init__.py +28 -0
- django_cfg/apps/knowbase/views/archive_views.py +469 -0
- django_cfg/apps/knowbase/views/base.py +49 -0
- django_cfg/apps/knowbase/views/chat_views.py +181 -0
- django_cfg/apps/knowbase/views/document_views.py +183 -0
- django_cfg/apps/knowbase/views/public_views.py +129 -0
- django_cfg/apps/leads/admin.py +70 -0
- django_cfg/apps/newsletter/admin.py +234 -0
- django_cfg/apps/newsletter/admin_filters.py +124 -0
- django_cfg/apps/support/admin.py +196 -0
- django_cfg/apps/support/admin_filters.py +71 -0
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +1 -1
- django_cfg/apps/urls.py +5 -4
- django_cfg/cli/README.md +1 -1
- django_cfg/cli/commands/create_project.py +2 -2
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/config.py +44 -0
- django_cfg/core/config.py +29 -82
- django_cfg/core/environment.py +1 -1
- django_cfg/core/generation.py +19 -107
- django_cfg/{integration.py → core/integration.py} +18 -16
- django_cfg/core/validation.py +1 -1
- django_cfg/management/__init__.py +1 -1
- django_cfg/management/commands/__init__.py +1 -1
- django_cfg/management/commands/auto_generate.py +482 -0
- django_cfg/management/commands/migrator.py +19 -101
- django_cfg/management/commands/test_email.py +1 -1
- django_cfg/middleware/README.md +0 -158
- django_cfg/middleware/__init__.py +0 -2
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/api.py +145 -0
- django_cfg/models/base.py +287 -0
- django_cfg/models/cache.py +4 -4
- django_cfg/models/constance.py +25 -88
- django_cfg/models/database.py +9 -9
- django_cfg/models/drf.py +3 -36
- django_cfg/models/email.py +163 -0
- django_cfg/models/environment.py +276 -0
- django_cfg/models/limits.py +1 -1
- django_cfg/models/logging.py +366 -0
- django_cfg/models/revolution.py +41 -2
- django_cfg/models/security.py +125 -0
- django_cfg/models/services.py +1 -1
- django_cfg/modules/__init__.py +2 -56
- django_cfg/modules/base.py +78 -52
- django_cfg/modules/django_currency/service.py +2 -2
- django_cfg/modules/django_email.py +2 -2
- django_cfg/modules/django_health.py +267 -0
- django_cfg/modules/django_llm/llm/client.py +79 -17
- django_cfg/modules/django_llm/translator/translator.py +2 -2
- django_cfg/modules/django_logger.py +2 -2
- django_cfg/modules/django_ngrok.py +2 -2
- django_cfg/modules/django_tasks.py +68 -3
- django_cfg/modules/django_telegram.py +3 -3
- django_cfg/modules/django_twilio/sendgrid_service.py +2 -2
- django_cfg/modules/django_twilio/service.py +2 -2
- django_cfg/modules/django_twilio/simple_service.py +2 -2
- django_cfg/modules/django_twilio/twilio_service.py +2 -2
- django_cfg/modules/django_unfold/__init__.py +69 -0
- django_cfg/modules/{unfold → django_unfold}/callbacks.py +23 -22
- django_cfg/modules/django_unfold/dashboard.py +278 -0
- django_cfg/modules/django_unfold/icons/README.md +145 -0
- django_cfg/modules/django_unfold/icons/__init__.py +12 -0
- django_cfg/modules/django_unfold/icons/constants.py +2851 -0
- django_cfg/modules/django_unfold/icons/generate_icons.py +486 -0
- django_cfg/modules/django_unfold/models/__init__.py +42 -0
- django_cfg/modules/django_unfold/models/config.py +601 -0
- django_cfg/modules/django_unfold/models/dashboard.py +206 -0
- django_cfg/modules/django_unfold/models/dropdown.py +40 -0
- django_cfg/modules/django_unfold/models/navigation.py +73 -0
- django_cfg/modules/django_unfold/models/tabs.py +25 -0
- django_cfg/modules/{unfold → django_unfold}/system_monitor.py +2 -2
- django_cfg/modules/django_unfold/utils.py +140 -0
- django_cfg/registry/__init__.py +23 -0
- django_cfg/registry/core.py +61 -0
- django_cfg/registry/exceptions.py +11 -0
- django_cfg/registry/modules.py +12 -0
- django_cfg/registry/services.py +26 -0
- django_cfg/registry/third_party.py +52 -0
- django_cfg/routing/__init__.py +19 -0
- django_cfg/routing/callbacks.py +198 -0
- django_cfg/routing/routers.py +48 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +8 -9
- django_cfg/templatetags/__init__.py +0 -0
- django_cfg/templatetags/django_cfg.py +33 -0
- django_cfg/urls.py +33 -0
- django_cfg/utils/path_resolution.py +1 -1
- django_cfg/utils/smart_defaults.py +7 -61
- django_cfg/utils/toolkit.py +663 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/METADATA +83 -86
- django_cfg-1.2.0.dist-info/RECORD +441 -0
- django_cfg/archive/django_sample.zip +0 -0
- django_cfg/models/unfold.py +0 -271
- django_cfg/modules/unfold/__init__.py +0 -29
- django_cfg/modules/unfold/dashboard.py +0 -318
- django_cfg/pyproject.toml +0 -370
- django_cfg/routers.py +0 -83
- django_cfg-1.1.82.dist-info/RECORD +0 -278
- /django_cfg/{exceptions.py → core/exceptions.py} +0 -0
- /django_cfg/modules/{unfold → django_unfold}/models.py +0 -0
- /django_cfg/modules/{unfold → django_unfold}/tailwind.py +0 -0
- /django_cfg/{version_check.py → utils/version_check.py} +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,234 @@
|
|
1
|
+
"""
|
2
|
+
Pre-built content processing agents.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from pydantic import BaseModel, Field
|
6
|
+
from typing import List, Dict, Any
|
7
|
+
|
8
|
+
from ..core.agent import DjangoAgent
|
9
|
+
from ..core.dependencies import ContentDeps, RunContext
|
10
|
+
from ..core.models import AnalysisResult, ProcessResult, ValidationResult
|
11
|
+
|
12
|
+
|
13
|
+
class ContentAnalysisResult(BaseModel):
|
14
|
+
"""Result from content analysis."""
|
15
|
+
sentiment: str = Field(..., description="Sentiment: positive, negative, neutral")
|
16
|
+
confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence score")
|
17
|
+
topics: List[str] = Field(default_factory=list, description="Extracted topics")
|
18
|
+
keywords: List[str] = Field(default_factory=list, description="Key terms")
|
19
|
+
summary: str = Field(default="", description="Content summary")
|
20
|
+
readability_score: float = Field(default=0.0, ge=0.0, le=100.0)
|
21
|
+
word_count: int = Field(default=0, ge=0)
|
22
|
+
language: str = Field(default="unknown", description="Detected language")
|
23
|
+
|
24
|
+
|
25
|
+
class ContentGenerationResult(BaseModel):
|
26
|
+
"""Result from content generation."""
|
27
|
+
generated_content: str = Field(..., description="Generated content")
|
28
|
+
content_type: str = Field(..., description="Type of generated content")
|
29
|
+
word_count: int = Field(..., ge=0, description="Word count")
|
30
|
+
quality_score: float = Field(default=0.0, ge=0.0, le=1.0, description="Quality assessment")
|
31
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
32
|
+
|
33
|
+
|
34
|
+
def ContentAnalyzerAgent() -> DjangoAgent[ContentDeps, ContentAnalysisResult]:
|
35
|
+
"""
|
36
|
+
Create content analyzer agent.
|
37
|
+
|
38
|
+
Analyzes content for:
|
39
|
+
- Sentiment analysis
|
40
|
+
- Topic extraction
|
41
|
+
- Keyword identification
|
42
|
+
- Readability assessment
|
43
|
+
- Language detection
|
44
|
+
"""
|
45
|
+
agent = DjangoAgent[ContentDeps, ContentAnalysisResult](
|
46
|
+
name="content_analyzer",
|
47
|
+
deps_type=ContentDeps,
|
48
|
+
output_type=ContentAnalysisResult,
|
49
|
+
instructions="""
|
50
|
+
You are a content analysis expert. Analyze the provided content and extract:
|
51
|
+
|
52
|
+
1. Sentiment (positive, negative, neutral) with confidence score
|
53
|
+
2. Main topics and themes
|
54
|
+
3. Important keywords and phrases
|
55
|
+
4. Content summary (2-3 sentences)
|
56
|
+
5. Readability assessment (0-100 scale)
|
57
|
+
6. Language detection
|
58
|
+
|
59
|
+
Be thorough but concise in your analysis.
|
60
|
+
"""
|
61
|
+
)
|
62
|
+
|
63
|
+
@agent.tool
|
64
|
+
async def analyze_text_structure(ctx: RunContext[ContentDeps]) -> Dict[str, Any]:
|
65
|
+
"""Analyze text structure and formatting."""
|
66
|
+
# This would integrate with actual text analysis libraries
|
67
|
+
return {
|
68
|
+
"paragraphs": 0,
|
69
|
+
"sentences": 0,
|
70
|
+
"avg_sentence_length": 0.0,
|
71
|
+
"complexity_score": 0.0
|
72
|
+
}
|
73
|
+
|
74
|
+
@agent.tool
|
75
|
+
async def extract_entities(ctx: RunContext[ContentDeps]) -> List[Dict[str, str]]:
|
76
|
+
"""Extract named entities from content."""
|
77
|
+
# This would integrate with NER libraries
|
78
|
+
return [
|
79
|
+
{"text": "example", "label": "PERSON", "confidence": 0.95}
|
80
|
+
]
|
81
|
+
|
82
|
+
@agent.tool
|
83
|
+
async def get_content_metadata(ctx: RunContext[ContentDeps]) -> Dict[str, Any]:
|
84
|
+
"""Get content metadata from dependencies."""
|
85
|
+
return {
|
86
|
+
"content_id": ctx.deps.content_id,
|
87
|
+
"content_type": ctx.deps.content_type,
|
88
|
+
"target_audience": ctx.deps.target_audience,
|
89
|
+
"user_id": ctx.deps.user.id
|
90
|
+
}
|
91
|
+
|
92
|
+
return agent
|
93
|
+
|
94
|
+
|
95
|
+
def ContentGeneratorAgent() -> DjangoAgent[ContentDeps, ContentGenerationResult]:
|
96
|
+
"""
|
97
|
+
Create content generator agent.
|
98
|
+
|
99
|
+
Generates content based on:
|
100
|
+
- Content type requirements
|
101
|
+
- Target audience
|
102
|
+
- Style guidelines
|
103
|
+
- Length specifications
|
104
|
+
"""
|
105
|
+
agent = DjangoAgent[ContentDeps, ContentGenerationResult](
|
106
|
+
name="content_generator",
|
107
|
+
deps_type=ContentDeps,
|
108
|
+
output_type=ContentGenerationResult,
|
109
|
+
instructions="""
|
110
|
+
You are a professional content writer. Generate high-quality content based on:
|
111
|
+
|
112
|
+
1. Content type (article, blog post, social media, etc.)
|
113
|
+
2. Target audience preferences
|
114
|
+
3. Specified tone and style
|
115
|
+
4. Length requirements
|
116
|
+
5. SEO considerations if applicable
|
117
|
+
|
118
|
+
Ensure the content is:
|
119
|
+
- Engaging and well-structured
|
120
|
+
- Appropriate for the target audience
|
121
|
+
- Original and creative
|
122
|
+
- Grammatically correct
|
123
|
+
- Optimized for readability
|
124
|
+
"""
|
125
|
+
)
|
126
|
+
|
127
|
+
@agent.tool
|
128
|
+
async def get_style_guidelines(ctx: RunContext[ContentDeps]) -> Dict[str, Any]:
|
129
|
+
"""Get style guidelines for content generation."""
|
130
|
+
return {
|
131
|
+
"tone": "professional",
|
132
|
+
"style": "informative",
|
133
|
+
"max_words": 1000,
|
134
|
+
"include_headers": True,
|
135
|
+
"target_audience": ctx.deps.target_audience
|
136
|
+
}
|
137
|
+
|
138
|
+
@agent.tool
|
139
|
+
async def check_content_requirements(ctx: RunContext[ContentDeps]) -> Dict[str, Any]:
|
140
|
+
"""Check content requirements and constraints."""
|
141
|
+
return {
|
142
|
+
"content_type": ctx.deps.content_type,
|
143
|
+
"min_length": 300,
|
144
|
+
"max_length": 2000,
|
145
|
+
"required_keywords": [],
|
146
|
+
"forbidden_topics": []
|
147
|
+
}
|
148
|
+
|
149
|
+
@agent.tool
|
150
|
+
async def validate_generated_content(ctx: RunContext[ContentDeps], content: str) -> Dict[str, Any]:
|
151
|
+
"""Validate generated content quality."""
|
152
|
+
word_count = len(content.split())
|
153
|
+
|
154
|
+
return {
|
155
|
+
"word_count": word_count,
|
156
|
+
"quality_score": 0.85, # This would be calculated by quality metrics
|
157
|
+
"passes_validation": word_count >= 100,
|
158
|
+
"suggestions": []
|
159
|
+
}
|
160
|
+
|
161
|
+
return agent
|
162
|
+
|
163
|
+
|
164
|
+
def ContentValidatorAgent() -> DjangoAgent[ContentDeps, ValidationResult]:
|
165
|
+
"""
|
166
|
+
Create content validator agent.
|
167
|
+
|
168
|
+
Validates content for:
|
169
|
+
- Grammar and spelling
|
170
|
+
- Style consistency
|
171
|
+
- Factual accuracy
|
172
|
+
- Compliance with guidelines
|
173
|
+
- Plagiarism detection
|
174
|
+
"""
|
175
|
+
agent = DjangoAgent[ContentDeps, ValidationResult](
|
176
|
+
name="content_validator",
|
177
|
+
deps_type=ContentDeps,
|
178
|
+
output_type=ValidationResult,
|
179
|
+
instructions="""
|
180
|
+
You are a content quality assurance expert. Validate content for:
|
181
|
+
|
182
|
+
1. Grammar, spelling, and punctuation errors
|
183
|
+
2. Style consistency and readability
|
184
|
+
3. Factual accuracy and logical flow
|
185
|
+
4. Compliance with content guidelines
|
186
|
+
5. Potential plagiarism or copyright issues
|
187
|
+
|
188
|
+
Provide specific feedback and suggestions for improvement.
|
189
|
+
Mark content as valid only if it meets all quality standards.
|
190
|
+
"""
|
191
|
+
)
|
192
|
+
|
193
|
+
@agent.tool
|
194
|
+
async def check_grammar_spelling(ctx: RunContext[ContentDeps], content: str) -> Dict[str, Any]:
|
195
|
+
"""Check grammar and spelling."""
|
196
|
+
# This would integrate with grammar checking libraries
|
197
|
+
return {
|
198
|
+
"grammar_errors": [],
|
199
|
+
"spelling_errors": [],
|
200
|
+
"grammar_score": 0.95,
|
201
|
+
"suggestions": []
|
202
|
+
}
|
203
|
+
|
204
|
+
@agent.tool
|
205
|
+
async def check_style_consistency(ctx: RunContext[ContentDeps], content: str) -> Dict[str, Any]:
|
206
|
+
"""Check style consistency."""
|
207
|
+
return {
|
208
|
+
"style_score": 0.90,
|
209
|
+
"inconsistencies": [],
|
210
|
+
"tone_analysis": "consistent",
|
211
|
+
"readability_grade": 8.5
|
212
|
+
}
|
213
|
+
|
214
|
+
@agent.tool
|
215
|
+
async def check_content_guidelines(ctx: RunContext[ContentDeps], content: str) -> Dict[str, Any]:
|
216
|
+
"""Check compliance with content guidelines."""
|
217
|
+
return {
|
218
|
+
"guideline_compliance": True,
|
219
|
+
"violations": [],
|
220
|
+
"compliance_score": 0.98,
|
221
|
+
"recommendations": []
|
222
|
+
}
|
223
|
+
|
224
|
+
@agent.tool
|
225
|
+
async def check_factual_accuracy(ctx: RunContext[ContentDeps], content: str) -> Dict[str, Any]:
|
226
|
+
"""Check factual accuracy of content."""
|
227
|
+
return {
|
228
|
+
"accuracy_score": 0.92,
|
229
|
+
"questionable_claims": [],
|
230
|
+
"fact_check_results": [],
|
231
|
+
"verification_needed": []
|
232
|
+
}
|
233
|
+
|
234
|
+
return agent
|
@@ -0,0 +1,15 @@
|
|
1
|
+
"""
|
2
|
+
Django-specific toolsets for agent orchestration.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .django_toolset import DjangoToolset
|
6
|
+
from .orm_toolset import ORMToolset
|
7
|
+
from .cache_toolset import CacheToolset
|
8
|
+
from .file_toolset import FileToolset
|
9
|
+
|
10
|
+
__all__ = [
|
11
|
+
"DjangoToolset",
|
12
|
+
"ORMToolset",
|
13
|
+
"CacheToolset",
|
14
|
+
"FileToolset",
|
15
|
+
]
|
@@ -0,0 +1,285 @@
|
|
1
|
+
"""
|
2
|
+
Django cache toolset for caching operations.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
from typing import Any, Optional, Dict, List
|
7
|
+
from pydantic_ai.toolsets import AbstractToolset
|
8
|
+
from pydantic_ai import RunContext
|
9
|
+
from django.core.cache import cache, caches
|
10
|
+
|
11
|
+
from ..core.dependencies import DjangoDeps
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class CacheToolset(AbstractToolset[DjangoDeps]):
|
17
|
+
"""
|
18
|
+
Django cache toolset for caching operations.
|
19
|
+
|
20
|
+
Provides tools for:
|
21
|
+
- Cache get/set operations
|
22
|
+
- Cache key management
|
23
|
+
- Cache statistics
|
24
|
+
- Multi-cache support
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(self, cache_alias: str = 'default', key_prefix: str = 'orchestrator'):
|
28
|
+
"""
|
29
|
+
Initialize cache toolset.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
cache_alias: Django cache alias to use
|
33
|
+
key_prefix: Prefix for all cache keys
|
34
|
+
"""
|
35
|
+
self.cache_alias = cache_alias
|
36
|
+
self.key_prefix = key_prefix
|
37
|
+
|
38
|
+
@property
|
39
|
+
def id(self) -> str:
|
40
|
+
return f"django_cache_{self.cache_alias}"
|
41
|
+
|
42
|
+
def _get_cache(self):
|
43
|
+
"""Get cache instance."""
|
44
|
+
if self.cache_alias == 'default':
|
45
|
+
return cache
|
46
|
+
else:
|
47
|
+
return caches[self.cache_alias]
|
48
|
+
|
49
|
+
def _make_key(self, key: str, user_id: Optional[int] = None) -> str:
|
50
|
+
"""Create cache key with prefix and optional user scope."""
|
51
|
+
parts = [self.key_prefix]
|
52
|
+
|
53
|
+
if user_id:
|
54
|
+
parts.append(f"user_{user_id}")
|
55
|
+
|
56
|
+
parts.append(key)
|
57
|
+
|
58
|
+
return ':'.join(parts)
|
59
|
+
|
60
|
+
async def get_cached_value(
|
61
|
+
self,
|
62
|
+
ctx: RunContext[DjangoDeps],
|
63
|
+
key: str,
|
64
|
+
user_scoped: bool = False
|
65
|
+
) -> Any:
|
66
|
+
"""Get value from cache."""
|
67
|
+
cache_instance = self._get_cache()
|
68
|
+
|
69
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
70
|
+
cache_key = self._make_key(key, user_id)
|
71
|
+
|
72
|
+
try:
|
73
|
+
value = cache_instance.get(cache_key)
|
74
|
+
logger.debug(f"Cache get: {cache_key} -> {'HIT' if value is not None else 'MISS'}")
|
75
|
+
return value
|
76
|
+
except Exception as e:
|
77
|
+
logger.error(f"Cache get failed for key '{cache_key}': {e}")
|
78
|
+
return None
|
79
|
+
|
80
|
+
async def set_cached_value(
|
81
|
+
self,
|
82
|
+
ctx: RunContext[DjangoDeps],
|
83
|
+
key: str,
|
84
|
+
value: Any,
|
85
|
+
timeout: Optional[int] = None,
|
86
|
+
user_scoped: bool = False
|
87
|
+
) -> bool:
|
88
|
+
"""Set value in cache."""
|
89
|
+
cache_instance = self._get_cache()
|
90
|
+
|
91
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
92
|
+
cache_key = self._make_key(key, user_id)
|
93
|
+
|
94
|
+
try:
|
95
|
+
cache_instance.set(cache_key, value, timeout)
|
96
|
+
logger.debug(f"Cache set: {cache_key} (timeout: {timeout})")
|
97
|
+
return True
|
98
|
+
except Exception as e:
|
99
|
+
logger.error(f"Cache set failed for key '{cache_key}': {e}")
|
100
|
+
return False
|
101
|
+
|
102
|
+
async def delete_cached_value(
|
103
|
+
self,
|
104
|
+
ctx: RunContext[DjangoDeps],
|
105
|
+
key: str,
|
106
|
+
user_scoped: bool = False
|
107
|
+
) -> bool:
|
108
|
+
"""Delete value from cache."""
|
109
|
+
cache_instance = self._get_cache()
|
110
|
+
|
111
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
112
|
+
cache_key = self._make_key(key, user_id)
|
113
|
+
|
114
|
+
try:
|
115
|
+
result = cache_instance.delete(cache_key)
|
116
|
+
logger.debug(f"Cache delete: {cache_key} -> {result}")
|
117
|
+
return bool(result)
|
118
|
+
except Exception as e:
|
119
|
+
logger.error(f"Cache delete failed for key '{cache_key}': {e}")
|
120
|
+
return False
|
121
|
+
|
122
|
+
async def get_or_set_cached_value(
|
123
|
+
self,
|
124
|
+
ctx: RunContext[DjangoDeps],
|
125
|
+
key: str,
|
126
|
+
default_value: Any,
|
127
|
+
timeout: Optional[int] = None,
|
128
|
+
user_scoped: bool = False
|
129
|
+
) -> Any:
|
130
|
+
"""Get value from cache or set default if not exists."""
|
131
|
+
cache_instance = self._get_cache()
|
132
|
+
|
133
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
134
|
+
cache_key = self._make_key(key, user_id)
|
135
|
+
|
136
|
+
try:
|
137
|
+
value = cache_instance.get_or_set(cache_key, default_value, timeout)
|
138
|
+
logger.debug(f"Cache get_or_set: {cache_key}")
|
139
|
+
return value
|
140
|
+
except Exception as e:
|
141
|
+
logger.error(f"Cache get_or_set failed for key '{cache_key}': {e}")
|
142
|
+
return default_value
|
143
|
+
|
144
|
+
async def increment_cached_value(
|
145
|
+
self,
|
146
|
+
ctx: RunContext[DjangoDeps],
|
147
|
+
key: str,
|
148
|
+
delta: int = 1,
|
149
|
+
user_scoped: bool = False
|
150
|
+
) -> Optional[int]:
|
151
|
+
"""Increment numeric value in cache."""
|
152
|
+
cache_instance = self._get_cache()
|
153
|
+
|
154
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
155
|
+
cache_key = self._make_key(key, user_id)
|
156
|
+
|
157
|
+
try:
|
158
|
+
# Check if cache backend supports increment
|
159
|
+
if hasattr(cache_instance, 'incr'):
|
160
|
+
try:
|
161
|
+
value = cache_instance.incr(cache_key, delta)
|
162
|
+
logger.debug(f"Cache increment: {cache_key} += {delta} -> {value}")
|
163
|
+
return value
|
164
|
+
except ValueError:
|
165
|
+
# Key doesn't exist, set initial value
|
166
|
+
cache_instance.set(cache_key, delta)
|
167
|
+
return delta
|
168
|
+
else:
|
169
|
+
# Fallback for backends without increment support
|
170
|
+
current = cache_instance.get(cache_key, 0)
|
171
|
+
new_value = current + delta
|
172
|
+
cache_instance.set(cache_key, new_value)
|
173
|
+
return new_value
|
174
|
+
except Exception as e:
|
175
|
+
logger.error(f"Cache increment failed for key '{cache_key}': {e}")
|
176
|
+
return None
|
177
|
+
|
178
|
+
async def get_cache_keys(
|
179
|
+
self,
|
180
|
+
ctx: RunContext[DjangoDeps],
|
181
|
+
pattern: Optional[str] = None,
|
182
|
+
user_scoped: bool = False
|
183
|
+
) -> List[str]:
|
184
|
+
"""Get cache keys matching pattern."""
|
185
|
+
cache_instance = self._get_cache()
|
186
|
+
|
187
|
+
# This is backend-dependent and may not work with all cache backends
|
188
|
+
try:
|
189
|
+
if hasattr(cache_instance, 'keys'):
|
190
|
+
if pattern:
|
191
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
192
|
+
search_pattern = self._make_key(pattern, user_id)
|
193
|
+
keys = cache_instance.keys(search_pattern)
|
194
|
+
else:
|
195
|
+
keys = cache_instance.keys(f"{self.key_prefix}:*")
|
196
|
+
|
197
|
+
return list(keys)
|
198
|
+
else:
|
199
|
+
logger.warning("Cache backend does not support key listing")
|
200
|
+
return []
|
201
|
+
except Exception as e:
|
202
|
+
logger.error(f"Failed to get cache keys: {e}")
|
203
|
+
return []
|
204
|
+
|
205
|
+
async def clear_user_cache(self, ctx: RunContext[DjangoDeps]) -> bool:
|
206
|
+
"""Clear all cache entries for current user."""
|
207
|
+
user_id = ctx.deps.user.id
|
208
|
+
pattern = f"{self.key_prefix}:user_{user_id}:*"
|
209
|
+
|
210
|
+
try:
|
211
|
+
keys = await self.get_cache_keys(ctx, pattern="*", user_scoped=True)
|
212
|
+
|
213
|
+
if keys:
|
214
|
+
cache_instance = self._get_cache()
|
215
|
+
cache_instance.delete_many(keys)
|
216
|
+
logger.info(f"Cleared {len(keys)} cache entries for user {user_id}")
|
217
|
+
|
218
|
+
return True
|
219
|
+
except Exception as e:
|
220
|
+
logger.error(f"Failed to clear user cache for user {user_id}: {e}")
|
221
|
+
return False
|
222
|
+
|
223
|
+
async def get_cache_stats(self, ctx: RunContext[DjangoDeps]) -> Dict[str, Any]:
|
224
|
+
"""Get cache statistics (if supported by backend)."""
|
225
|
+
cache_instance = self._get_cache()
|
226
|
+
|
227
|
+
stats = {
|
228
|
+
'cache_alias': self.cache_alias,
|
229
|
+
'key_prefix': self.key_prefix,
|
230
|
+
'backend': cache_instance.__class__.__name__,
|
231
|
+
}
|
232
|
+
|
233
|
+
try:
|
234
|
+
# Try to get backend-specific stats
|
235
|
+
if hasattr(cache_instance, '_cache') and hasattr(cache_instance._cache, 'get_stats'):
|
236
|
+
# Memcached backend
|
237
|
+
backend_stats = cache_instance._cache.get_stats()
|
238
|
+
stats['backend_stats'] = backend_stats
|
239
|
+
elif hasattr(cache_instance, 'info'):
|
240
|
+
# Redis backend
|
241
|
+
info = cache_instance.info()
|
242
|
+
stats['backend_stats'] = {
|
243
|
+
'used_memory': info.get('used_memory_human'),
|
244
|
+
'connected_clients': info.get('connected_clients'),
|
245
|
+
'total_commands_processed': info.get('total_commands_processed'),
|
246
|
+
}
|
247
|
+
|
248
|
+
# Get key count if possible
|
249
|
+
keys = await self.get_cache_keys(ctx)
|
250
|
+
stats['total_keys'] = len(keys)
|
251
|
+
|
252
|
+
except Exception as e:
|
253
|
+
logger.warning(f"Could not get cache stats: {e}")
|
254
|
+
stats['error'] = str(e)
|
255
|
+
|
256
|
+
return stats
|
257
|
+
|
258
|
+
async def touch_cached_value(
|
259
|
+
self,
|
260
|
+
ctx: RunContext[DjangoDeps],
|
261
|
+
key: str,
|
262
|
+
timeout: Optional[int] = None,
|
263
|
+
user_scoped: bool = False
|
264
|
+
) -> bool:
|
265
|
+
"""Update cache key timeout without changing value."""
|
266
|
+
cache_instance = self._get_cache()
|
267
|
+
|
268
|
+
user_id = ctx.deps.user.id if user_scoped else None
|
269
|
+
cache_key = self._make_key(key, user_id)
|
270
|
+
|
271
|
+
try:
|
272
|
+
if hasattr(cache_instance, 'touch'):
|
273
|
+
result = cache_instance.touch(cache_key, timeout)
|
274
|
+
logger.debug(f"Cache touch: {cache_key} -> {result}")
|
275
|
+
return result
|
276
|
+
else:
|
277
|
+
# Fallback: get and set with new timeout
|
278
|
+
value = cache_instance.get(cache_key)
|
279
|
+
if value is not None:
|
280
|
+
cache_instance.set(cache_key, value, timeout)
|
281
|
+
return True
|
282
|
+
return False
|
283
|
+
except Exception as e:
|
284
|
+
logger.error(f"Cache touch failed for key '{cache_key}': {e}")
|
285
|
+
return False
|