django-cfg 1.1.82__py3-none-any.whl → 1.2.1__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 +450 -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 +91 -19
- 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.1.dist-info}/METADATA +83 -86
- django_cfg-1.2.1.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.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.82.dist-info → django_cfg-1.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,450 @@
|
|
1
|
+
"""
|
2
|
+
Pydantic-based configuration for Knowledge Base app.
|
3
|
+
|
4
|
+
This module provides a clean, type-safe configuration system that replaces
|
5
|
+
the removed Constance settings with sensible defaults and validation.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, Any, Optional
|
9
|
+
from pydantic import BaseModel, Field, field_validator
|
10
|
+
from pathlib import Path
|
11
|
+
import os
|
12
|
+
|
13
|
+
# Import environment configuration for API keys
|
14
|
+
try:
|
15
|
+
from django.conf import settings
|
16
|
+
except ImportError:
|
17
|
+
# Fallback for tests or when environment is not available
|
18
|
+
env = None
|
19
|
+
|
20
|
+
|
21
|
+
class CacheSettings(BaseModel):
|
22
|
+
"""Cache settings for LLMClient."""
|
23
|
+
|
24
|
+
cache_dir: Path = Field(..., description="Cache directory path")
|
25
|
+
cache_ttl: int = Field(..., description="Cache TTL in seconds")
|
26
|
+
max_cache_size: int = Field(..., description="Maximum cache size")
|
27
|
+
|
28
|
+
class Config:
|
29
|
+
arbitrary_types_allowed = True # Allow Path type
|
30
|
+
|
31
|
+
|
32
|
+
class EmbeddingConfig(BaseModel):
|
33
|
+
"""Configuration for embedding processing."""
|
34
|
+
|
35
|
+
model: str = Field(
|
36
|
+
default="text-embedding-ada-002",
|
37
|
+
description="OpenAI embedding model to use"
|
38
|
+
)
|
39
|
+
batch_size: int = Field(
|
40
|
+
default=50,
|
41
|
+
ge=1,
|
42
|
+
le=100,
|
43
|
+
description="Number of chunks to process in one batch"
|
44
|
+
)
|
45
|
+
max_retries: int = Field(
|
46
|
+
default=3,
|
47
|
+
ge=0,
|
48
|
+
le=10,
|
49
|
+
description="Maximum number of retries for failed embeddings"
|
50
|
+
)
|
51
|
+
timeout_seconds: int = Field(
|
52
|
+
default=30,
|
53
|
+
ge=5,
|
54
|
+
le=300,
|
55
|
+
description="Timeout for embedding API calls"
|
56
|
+
)
|
57
|
+
cache_dir: str = Field(
|
58
|
+
default=".cache/django_cfg_knowbase_llm",
|
59
|
+
description="Directory for LLM cache files (relative to project root)"
|
60
|
+
)
|
61
|
+
cache_ttl: int = Field(
|
62
|
+
default=3600,
|
63
|
+
ge=60,
|
64
|
+
le=86400,
|
65
|
+
description="Cache TTL in seconds (1 hour to 24 hours)"
|
66
|
+
)
|
67
|
+
cache_max_size: int = Field(
|
68
|
+
default=1000,
|
69
|
+
ge=10,
|
70
|
+
le=10000,
|
71
|
+
description="Maximum number of items in memory cache"
|
72
|
+
)
|
73
|
+
|
74
|
+
# API Keys from environment
|
75
|
+
@property
|
76
|
+
def openai_api_key(self) -> Optional[str]:
|
77
|
+
"""Get OpenAI API key from environment configuration."""
|
78
|
+
try:
|
79
|
+
return settings.api_keys.openai
|
80
|
+
except AttributeError:
|
81
|
+
return os.getenv("OPENAI_API_KEY")
|
82
|
+
|
83
|
+
@property
|
84
|
+
def openrouter_api_key(self) -> Optional[str]:
|
85
|
+
"""Get OpenRouter API key from environment configuration."""
|
86
|
+
try:
|
87
|
+
return settings.api_keys.openrouter
|
88
|
+
except AttributeError:
|
89
|
+
return os.getenv("OPENROUTER_API_KEY")
|
90
|
+
|
91
|
+
|
92
|
+
class ChunkingConfig(BaseModel):
|
93
|
+
"""Configuration for text chunking."""
|
94
|
+
|
95
|
+
document_chunk_size: int = Field(
|
96
|
+
default=1000,
|
97
|
+
ge=100,
|
98
|
+
le=8000,
|
99
|
+
description="Chunk size for document processing (characters)"
|
100
|
+
)
|
101
|
+
document_chunk_overlap: int = Field(
|
102
|
+
default=200,
|
103
|
+
ge=0,
|
104
|
+
le=1000,
|
105
|
+
description="Overlap between document chunks (characters)"
|
106
|
+
)
|
107
|
+
archive_chunk_size: int = Field(
|
108
|
+
default=800,
|
109
|
+
ge=100,
|
110
|
+
le=8000,
|
111
|
+
description="Chunk size for archive processing (characters)"
|
112
|
+
)
|
113
|
+
archive_chunk_overlap: int = Field(
|
114
|
+
default=160,
|
115
|
+
ge=0,
|
116
|
+
le=1000,
|
117
|
+
description="Overlap between archive chunks (characters)"
|
118
|
+
)
|
119
|
+
|
120
|
+
@field_validator('document_chunk_overlap')
|
121
|
+
@classmethod
|
122
|
+
def validate_document_overlap(cls, v, info):
|
123
|
+
if info.data and 'document_chunk_size' in info.data and v >= info.data['document_chunk_size']:
|
124
|
+
raise ValueError('Document chunk overlap must be less than chunk size')
|
125
|
+
return v
|
126
|
+
|
127
|
+
@field_validator('archive_chunk_overlap')
|
128
|
+
@classmethod
|
129
|
+
def validate_archive_overlap(cls, v, info):
|
130
|
+
if info.data and 'archive_chunk_size' in info.data and v >= info.data['archive_chunk_size']:
|
131
|
+
raise ValueError('Archive chunk overlap must be less than chunk size')
|
132
|
+
return v
|
133
|
+
|
134
|
+
|
135
|
+
class SearchConfig(BaseModel):
|
136
|
+
"""Configuration for search functionality."""
|
137
|
+
|
138
|
+
results_limit: int = Field(
|
139
|
+
default=10,
|
140
|
+
ge=1,
|
141
|
+
le=100,
|
142
|
+
description="Maximum number of search results to return"
|
143
|
+
)
|
144
|
+
|
145
|
+
# Type-specific similarity thresholds for better multilingual and content-type support
|
146
|
+
document_threshold: float = Field(
|
147
|
+
default=0.7,
|
148
|
+
ge=0.0,
|
149
|
+
le=1.0,
|
150
|
+
description="Similarity threshold for document chunks (high precision)"
|
151
|
+
)
|
152
|
+
archive_threshold: float = Field(
|
153
|
+
default=0.6,
|
154
|
+
ge=0.0,
|
155
|
+
le=1.0,
|
156
|
+
description="Similarity threshold for archive chunks (medium precision for code)"
|
157
|
+
)
|
158
|
+
# Note: external_data_threshold removed - now configured per-object in ExternalData.similarity_threshold
|
159
|
+
|
160
|
+
# Legacy field for backward compatibility
|
161
|
+
similarity_threshold: float = Field(
|
162
|
+
default=0.7,
|
163
|
+
ge=0.0,
|
164
|
+
le=1.0,
|
165
|
+
description="Default similarity threshold (legacy, use type-specific thresholds)"
|
166
|
+
)
|
167
|
+
|
168
|
+
|
169
|
+
class ChatConfig(BaseModel):
|
170
|
+
"""Configuration for chat functionality."""
|
171
|
+
|
172
|
+
context_chunks: int = Field(
|
173
|
+
default=5,
|
174
|
+
ge=1,
|
175
|
+
le=20,
|
176
|
+
description="Number of chunks to include in chat context"
|
177
|
+
)
|
178
|
+
max_tokens: int = Field(
|
179
|
+
default=4000,
|
180
|
+
ge=100,
|
181
|
+
le=32000,
|
182
|
+
description="Maximum tokens for chat completion"
|
183
|
+
)
|
184
|
+
temperature: float = Field(
|
185
|
+
default=0.7,
|
186
|
+
ge=0.0,
|
187
|
+
le=2.0,
|
188
|
+
description="Temperature for chat completion (creativity level)"
|
189
|
+
)
|
190
|
+
|
191
|
+
|
192
|
+
class ProcessingConfig(BaseModel):
|
193
|
+
"""Configuration for processing limits and timeouts."""
|
194
|
+
|
195
|
+
max_document_size_mb: int = Field(
|
196
|
+
default=10,
|
197
|
+
ge=1,
|
198
|
+
le=100,
|
199
|
+
description="Maximum document size in MB"
|
200
|
+
)
|
201
|
+
max_archive_size_mb: int = Field(
|
202
|
+
default=50,
|
203
|
+
ge=1,
|
204
|
+
le=500,
|
205
|
+
description="Maximum archive size in MB"
|
206
|
+
)
|
207
|
+
timeout_minutes: int = Field(
|
208
|
+
default=30,
|
209
|
+
ge=1,
|
210
|
+
le=180,
|
211
|
+
description="Processing timeout in minutes"
|
212
|
+
)
|
213
|
+
|
214
|
+
|
215
|
+
class KnowledgeBaseConfig(BaseModel):
|
216
|
+
"""Main configuration for the Knowledge Base app."""
|
217
|
+
|
218
|
+
embedding: EmbeddingConfig = Field(default_factory=EmbeddingConfig)
|
219
|
+
chunking: ChunkingConfig = Field(default_factory=ChunkingConfig)
|
220
|
+
search: SearchConfig = Field(default_factory=SearchConfig)
|
221
|
+
chat: ChatConfig = Field(default_factory=ChatConfig)
|
222
|
+
processing: ProcessingConfig = Field(default_factory=ProcessingConfig)
|
223
|
+
|
224
|
+
class Config:
|
225
|
+
env_prefix = "KNOWBASE_"
|
226
|
+
case_sensitive = False
|
227
|
+
|
228
|
+
def get_chunking_params_for_type(self, content_type: str) -> Dict[str, Any]:
|
229
|
+
"""
|
230
|
+
Get chunking parameters for SemanticChunker.
|
231
|
+
|
232
|
+
Args:
|
233
|
+
content_type: Either 'document' or 'archive'
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
Dictionary with chunk_size and overlap parameters
|
237
|
+
"""
|
238
|
+
if content_type == 'archive':
|
239
|
+
return {
|
240
|
+
'chunk_size': self.chunking.archive_chunk_size,
|
241
|
+
'overlap': self.chunking.archive_chunk_overlap
|
242
|
+
}
|
243
|
+
else: # default to document
|
244
|
+
return {
|
245
|
+
'chunk_size': self.chunking.document_chunk_size,
|
246
|
+
'overlap': self.chunking.document_chunk_overlap
|
247
|
+
}
|
248
|
+
|
249
|
+
def get_embedding_model(self) -> str:
|
250
|
+
"""Get the configured embedding model."""
|
251
|
+
return self.embedding.model
|
252
|
+
|
253
|
+
def get_embedding_batch_size(self) -> int:
|
254
|
+
"""Get the configured embedding batch size."""
|
255
|
+
return self.embedding.batch_size
|
256
|
+
|
257
|
+
def get_openai_api_key(self) -> Optional[str]:
|
258
|
+
"""Get OpenAI API key from environment."""
|
259
|
+
return self.embedding.openai_api_key
|
260
|
+
|
261
|
+
def get_openrouter_api_key(self) -> Optional[str]:
|
262
|
+
"""Get OpenRouter API key from environment."""
|
263
|
+
return self.embedding.openrouter_api_key
|
264
|
+
|
265
|
+
def get_cache_dir(self) -> Path:
|
266
|
+
"""Get cache directory path and ensure it exists."""
|
267
|
+
from pathlib import Path
|
268
|
+
cache_path = Path(self.embedding.cache_dir)
|
269
|
+
if not cache_path.is_absolute():
|
270
|
+
cache_path = Path.cwd() / cache_path
|
271
|
+
|
272
|
+
# Ensure cache directory exists
|
273
|
+
cache_path.mkdir(parents=True, exist_ok=True)
|
274
|
+
return cache_path
|
275
|
+
|
276
|
+
def get_cache_settings(self) -> CacheSettings:
|
277
|
+
"""Get cache settings for LLMClient."""
|
278
|
+
return CacheSettings(
|
279
|
+
cache_dir=self.get_cache_dir(),
|
280
|
+
cache_ttl=self.embedding.cache_ttl,
|
281
|
+
max_cache_size=self.embedding.cache_max_size
|
282
|
+
)
|
283
|
+
|
284
|
+
def to_dict(self) -> Dict[str, Any]:
|
285
|
+
"""Convert config to dictionary for easy access."""
|
286
|
+
return self.dict()
|
287
|
+
|
288
|
+
|
289
|
+
# Global configuration instance
|
290
|
+
_config: Optional[KnowledgeBaseConfig] = None
|
291
|
+
|
292
|
+
|
293
|
+
def get_config() -> KnowledgeBaseConfig:
|
294
|
+
"""
|
295
|
+
Get the global configuration instance.
|
296
|
+
|
297
|
+
This function implements a singleton pattern to ensure we only
|
298
|
+
create one configuration instance per application run.
|
299
|
+
"""
|
300
|
+
global _config
|
301
|
+
if _config is None:
|
302
|
+
_config = KnowledgeBaseConfig()
|
303
|
+
return _config
|
304
|
+
|
305
|
+
|
306
|
+
def reload_config() -> KnowledgeBaseConfig:
|
307
|
+
"""
|
308
|
+
Reload configuration from environment variables.
|
309
|
+
|
310
|
+
Useful for testing or when configuration needs to be updated.
|
311
|
+
"""
|
312
|
+
global _config
|
313
|
+
_config = KnowledgeBaseConfig()
|
314
|
+
return _config
|
315
|
+
|
316
|
+
|
317
|
+
# Convenience functions for easy access (backward compatibility)
|
318
|
+
def get_document_chunk_size() -> int:
|
319
|
+
"""Get document chunk size."""
|
320
|
+
return get_config().chunking.document_chunk_size
|
321
|
+
|
322
|
+
|
323
|
+
def get_document_chunk_overlap() -> int:
|
324
|
+
"""Get document chunk overlap."""
|
325
|
+
return get_config().chunking.document_chunk_overlap
|
326
|
+
|
327
|
+
|
328
|
+
def get_archive_chunk_size() -> int:
|
329
|
+
"""Get archive chunk size."""
|
330
|
+
return get_config().chunking.archive_chunk_size
|
331
|
+
|
332
|
+
|
333
|
+
def get_archive_chunk_overlap() -> int:
|
334
|
+
"""Get archive chunk overlap."""
|
335
|
+
return get_config().chunking.archive_chunk_overlap
|
336
|
+
|
337
|
+
|
338
|
+
def get_embedding_batch_size() -> int:
|
339
|
+
"""Get embedding batch size."""
|
340
|
+
return get_config().embedding.batch_size
|
341
|
+
|
342
|
+
|
343
|
+
def get_embedding_model() -> str:
|
344
|
+
"""Get embedding model."""
|
345
|
+
return get_config().embedding.model
|
346
|
+
|
347
|
+
|
348
|
+
def get_search_results_limit() -> int:
|
349
|
+
"""Get search results limit."""
|
350
|
+
return get_config().search.results_limit
|
351
|
+
|
352
|
+
|
353
|
+
def get_search_similarity_threshold() -> float:
|
354
|
+
"""Get search similarity threshold (legacy)."""
|
355
|
+
return get_config().search.similarity_threshold
|
356
|
+
|
357
|
+
|
358
|
+
def get_document_threshold() -> float:
|
359
|
+
"""Get similarity threshold for document chunks."""
|
360
|
+
return get_config().search.document_threshold
|
361
|
+
|
362
|
+
|
363
|
+
def get_archive_threshold() -> float:
|
364
|
+
"""Get similarity threshold for archive chunks."""
|
365
|
+
return get_config().search.archive_threshold
|
366
|
+
|
367
|
+
|
368
|
+
# Note: get_external_data_threshold removed - now configured per-object in ExternalData.similarity_threshold
|
369
|
+
|
370
|
+
|
371
|
+
def get_threshold_for_type(content_type: str) -> float:
|
372
|
+
"""
|
373
|
+
Get appropriate similarity threshold for content type.
|
374
|
+
|
375
|
+
Args:
|
376
|
+
content_type: 'document', 'archive', or 'external_data'
|
377
|
+
|
378
|
+
Returns:
|
379
|
+
Appropriate similarity threshold for the content type
|
380
|
+
"""
|
381
|
+
config = get_config()
|
382
|
+
thresholds = {
|
383
|
+
'document': config.search.document_threshold,
|
384
|
+
'archive': config.search.archive_threshold,
|
385
|
+
# Note: external_data now uses per-object thresholds from ExternalData.similarity_threshold
|
386
|
+
}
|
387
|
+
return thresholds.get(content_type, config.search.similarity_threshold)
|
388
|
+
|
389
|
+
|
390
|
+
def get_chat_context_chunks() -> int:
|
391
|
+
"""Get number of chunks for chat context."""
|
392
|
+
return get_config().chat.context_chunks
|
393
|
+
|
394
|
+
|
395
|
+
def get_chat_max_tokens() -> int:
|
396
|
+
"""Get maximum tokens for chat completion."""
|
397
|
+
return get_config().chat.max_tokens
|
398
|
+
|
399
|
+
|
400
|
+
def get_chat_temperature() -> float:
|
401
|
+
"""Get chat completion temperature."""
|
402
|
+
return get_config().chat.temperature
|
403
|
+
|
404
|
+
|
405
|
+
def get_max_archive_size_mb() -> int:
|
406
|
+
"""Get maximum archive size in MB."""
|
407
|
+
return get_config().processing.max_archive_size_mb
|
408
|
+
|
409
|
+
|
410
|
+
def get_max_document_size_mb() -> int:
|
411
|
+
"""Get maximum document size in MB."""
|
412
|
+
return get_config().processing.max_document_size_mb
|
413
|
+
|
414
|
+
|
415
|
+
def get_processing_timeout_minutes() -> int:
|
416
|
+
"""Get processing timeout in minutes."""
|
417
|
+
return get_config().processing.timeout_minutes
|
418
|
+
|
419
|
+
|
420
|
+
def get_chunking_params_for_type(content_type: str) -> Dict[str, Any]:
|
421
|
+
"""
|
422
|
+
Get chunking parameters for SemanticChunker.
|
423
|
+
|
424
|
+
Args:
|
425
|
+
content_type: Either 'document' or 'archive'
|
426
|
+
|
427
|
+
Returns:
|
428
|
+
Dictionary with chunk_size and overlap parameters
|
429
|
+
"""
|
430
|
+
return get_config().get_chunking_params_for_type(content_type)
|
431
|
+
|
432
|
+
|
433
|
+
def get_openai_api_key() -> Optional[str]:
|
434
|
+
"""Get OpenAI API key from environment."""
|
435
|
+
return get_config().get_openai_api_key()
|
436
|
+
|
437
|
+
|
438
|
+
def get_openrouter_api_key() -> Optional[str]:
|
439
|
+
"""Get OpenRouter API key from environment."""
|
440
|
+
return get_config().get_openrouter_api_key()
|
441
|
+
|
442
|
+
|
443
|
+
def get_cache_dir():
|
444
|
+
"""Get cache directory path."""
|
445
|
+
return get_config().get_cache_dir()
|
446
|
+
|
447
|
+
|
448
|
+
def get_cache_settings() -> CacheSettings:
|
449
|
+
"""Get cache settings for LLMClient."""
|
450
|
+
return get_config().get_cache_settings()
|
@@ -0,0 +1,191 @@
|
|
1
|
+
"""
|
2
|
+
Примеры использования нового ExternalDataManager.
|
3
|
+
|
4
|
+
Этот файл показывает, как легко интегрировать внешние данные в knowbase
|
5
|
+
после рефакторинга.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from django.contrib.auth import get_user_model
|
9
|
+
from django_cfg.apps.knowbase.utils.external_data_manager import ExternalDataManager, quick_add_model, quick_search
|
10
|
+
from django_cfg.apps.knowbase.models.external_data import ExternalDataType
|
11
|
+
|
12
|
+
User = get_user_model()
|
13
|
+
|
14
|
+
|
15
|
+
def example_add_django_model():
|
16
|
+
"""Пример добавления Django модели как внешнего источника данных."""
|
17
|
+
|
18
|
+
# Получаем пользователя
|
19
|
+
user = User.objects.first()
|
20
|
+
if not user:
|
21
|
+
print("❌ No users found")
|
22
|
+
return
|
23
|
+
|
24
|
+
# Создаем менеджер
|
25
|
+
manager = ExternalDataManager(user)
|
26
|
+
|
27
|
+
# Добавляем модель Vehicle (если она существует)
|
28
|
+
try:
|
29
|
+
from apps.vehicles_data.models import Vehicle
|
30
|
+
|
31
|
+
external_data = manager.add_django_model(
|
32
|
+
model_class=Vehicle,
|
33
|
+
title="Vehicle Database",
|
34
|
+
fields=['brand__name', 'model', 'year', 'description'],
|
35
|
+
description="All vehicles from the database",
|
36
|
+
search_fields=['brand__name', 'model'],
|
37
|
+
chunk_size=800,
|
38
|
+
overlap_size=150,
|
39
|
+
auto_vectorize=True
|
40
|
+
)
|
41
|
+
|
42
|
+
print(f"✅ Added Vehicle model as external data: {external_data.id}")
|
43
|
+
print(f" Status: {external_data.status}")
|
44
|
+
print(f" Total chunks: {external_data.total_chunks}")
|
45
|
+
|
46
|
+
except ImportError:
|
47
|
+
print("⚠️ Vehicle model not found, using example data instead")
|
48
|
+
|
49
|
+
# Добавляем произвольные данные
|
50
|
+
external_data = manager.add_custom_data(
|
51
|
+
title="Sample Car Data",
|
52
|
+
identifier="sample_cars",
|
53
|
+
content="""
|
54
|
+
Toyota Camry 2023: Reliable sedan with excellent fuel economy
|
55
|
+
Honda Civic 2023: Compact car perfect for city driving
|
56
|
+
BMW X5 2023: Luxury SUV with advanced features
|
57
|
+
Tesla Model 3 2023: Electric vehicle with autopilot
|
58
|
+
""",
|
59
|
+
description="Sample car data for testing",
|
60
|
+
tags=['cars', 'vehicles', 'sample']
|
61
|
+
)
|
62
|
+
|
63
|
+
print(f"✅ Added sample car data: {external_data.id}")
|
64
|
+
|
65
|
+
|
66
|
+
def example_search_external_data():
|
67
|
+
"""Пример поиска по внешним данным."""
|
68
|
+
|
69
|
+
user = User.objects.first()
|
70
|
+
if not user:
|
71
|
+
print("❌ No users found")
|
72
|
+
return
|
73
|
+
|
74
|
+
manager = ExternalDataManager(user)
|
75
|
+
|
76
|
+
# Поиск по запросу
|
77
|
+
results = manager.search(
|
78
|
+
query="reliable car with good fuel economy",
|
79
|
+
limit=3,
|
80
|
+
threshold=0.6
|
81
|
+
)
|
82
|
+
|
83
|
+
print(f"🔍 Search results ({len(results)} found):")
|
84
|
+
for i, result in enumerate(results, 1):
|
85
|
+
print(f" {i}. {result['source_title']} (similarity: {result['similarity']:.3f})")
|
86
|
+
print(f" Content: {result['content'][:100]}...")
|
87
|
+
print()
|
88
|
+
|
89
|
+
|
90
|
+
def example_get_statistics():
|
91
|
+
"""Пример получения статистики."""
|
92
|
+
|
93
|
+
user = User.objects.first()
|
94
|
+
if not user:
|
95
|
+
print("❌ No users found")
|
96
|
+
return
|
97
|
+
|
98
|
+
manager = ExternalDataManager(user)
|
99
|
+
stats = manager.get_statistics()
|
100
|
+
|
101
|
+
print("📊 External Data Statistics:")
|
102
|
+
print(f" Total sources: {stats.total_sources}")
|
103
|
+
print(f" Active sources: {stats.active_sources}")
|
104
|
+
print(f" Processed sources: {stats.processed_sources}")
|
105
|
+
print(f" Failed sources: {stats.failed_sources}")
|
106
|
+
print(f" Total chunks: {stats.total_chunks}")
|
107
|
+
print(f" Total tokens: {stats.total_tokens}")
|
108
|
+
print(f" Total cost: ${stats.total_cost:.4f}")
|
109
|
+
print(f" Source types: {stats.source_type_counts}")
|
110
|
+
|
111
|
+
|
112
|
+
def example_health_check():
|
113
|
+
"""Пример проверки здоровья системы."""
|
114
|
+
|
115
|
+
user = User.objects.first()
|
116
|
+
if not user:
|
117
|
+
print("❌ No users found")
|
118
|
+
return
|
119
|
+
|
120
|
+
manager = ExternalDataManager(user)
|
121
|
+
health = manager.health_check()
|
122
|
+
|
123
|
+
print("🏥 System Health Check:")
|
124
|
+
print(f" Status: {health.status}")
|
125
|
+
print(f" Healthy: {'✅' if health.healthy else '❌'}")
|
126
|
+
print(f" Database: {'✅' if health.database_healthy else '❌'}")
|
127
|
+
print(f" Embedding Service: {'✅' if health.embedding_service_healthy else '❌'}")
|
128
|
+
print(f" Processing: {'✅' if health.processing_healthy else '❌'}")
|
129
|
+
print(f" Response time: {health.response_time_ms:.2f}ms")
|
130
|
+
print(f" Active sources: {health.active_sources}")
|
131
|
+
print(f" Pending processing: {health.pending_processing}")
|
132
|
+
print(f" Failed processing: {health.failed_processing}")
|
133
|
+
|
134
|
+
if health.issues:
|
135
|
+
print(f" Issues: {health.issues}")
|
136
|
+
if health.warnings:
|
137
|
+
print(f" Warnings: {health.warnings}")
|
138
|
+
|
139
|
+
|
140
|
+
def example_quick_functions():
|
141
|
+
"""Пример использования быстрых функций."""
|
142
|
+
|
143
|
+
user = User.objects.first()
|
144
|
+
if not user:
|
145
|
+
print("❌ No users found")
|
146
|
+
return
|
147
|
+
|
148
|
+
# Быстрый поиск
|
149
|
+
results = quick_search(
|
150
|
+
user=user,
|
151
|
+
query="electric vehicle",
|
152
|
+
limit=2
|
153
|
+
)
|
154
|
+
|
155
|
+
print(f"⚡ Quick search results: {len(results)} found")
|
156
|
+
for result in results:
|
157
|
+
print(f" - {result['source_title']}: {result['similarity']:.3f}")
|
158
|
+
|
159
|
+
|
160
|
+
def run_all_examples():
|
161
|
+
"""Запустить все примеры."""
|
162
|
+
|
163
|
+
print("🚀 Running External Data Manager Examples")
|
164
|
+
print("=" * 50)
|
165
|
+
|
166
|
+
try:
|
167
|
+
print("\n1. Adding Django Model:")
|
168
|
+
example_add_django_model()
|
169
|
+
|
170
|
+
print("\n2. Searching External Data:")
|
171
|
+
example_search_external_data()
|
172
|
+
|
173
|
+
print("\n3. Getting Statistics:")
|
174
|
+
example_get_statistics()
|
175
|
+
|
176
|
+
print("\n4. Health Check:")
|
177
|
+
example_health_check()
|
178
|
+
|
179
|
+
print("\n5. Quick Functions:")
|
180
|
+
example_quick_functions()
|
181
|
+
|
182
|
+
print("\n✅ All examples completed successfully!")
|
183
|
+
|
184
|
+
except Exception as e:
|
185
|
+
print(f"\n❌ Error running examples: {e}")
|
186
|
+
import traceback
|
187
|
+
traceback.print_exc()
|
188
|
+
|
189
|
+
|
190
|
+
if __name__ == "__main__":
|
191
|
+
run_all_examples()
|
File without changes
|
File without changes
|