mcli-framework 7.0.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.

Potentially problematic release.


This version of mcli-framework might be problematic. Click here for more details.

Files changed (186) hide show
  1. mcli/app/chat_cmd.py +42 -0
  2. mcli/app/commands_cmd.py +226 -0
  3. mcli/app/completion_cmd.py +216 -0
  4. mcli/app/completion_helpers.py +288 -0
  5. mcli/app/cron_test_cmd.py +697 -0
  6. mcli/app/logs_cmd.py +419 -0
  7. mcli/app/main.py +492 -0
  8. mcli/app/model/model.py +1060 -0
  9. mcli/app/model_cmd.py +227 -0
  10. mcli/app/redis_cmd.py +269 -0
  11. mcli/app/video/video.py +1114 -0
  12. mcli/app/visual_cmd.py +303 -0
  13. mcli/chat/chat.py +2409 -0
  14. mcli/chat/command_rag.py +514 -0
  15. mcli/chat/enhanced_chat.py +652 -0
  16. mcli/chat/system_controller.py +1010 -0
  17. mcli/chat/system_integration.py +1016 -0
  18. mcli/cli.py +25 -0
  19. mcli/config.toml +20 -0
  20. mcli/lib/api/api.py +586 -0
  21. mcli/lib/api/daemon_client.py +203 -0
  22. mcli/lib/api/daemon_client_local.py +44 -0
  23. mcli/lib/api/daemon_decorator.py +217 -0
  24. mcli/lib/api/mcli_decorators.py +1032 -0
  25. mcli/lib/auth/auth.py +85 -0
  26. mcli/lib/auth/aws_manager.py +85 -0
  27. mcli/lib/auth/azure_manager.py +91 -0
  28. mcli/lib/auth/credential_manager.py +192 -0
  29. mcli/lib/auth/gcp_manager.py +93 -0
  30. mcli/lib/auth/key_manager.py +117 -0
  31. mcli/lib/auth/mcli_manager.py +93 -0
  32. mcli/lib/auth/token_manager.py +75 -0
  33. mcli/lib/auth/token_util.py +1011 -0
  34. mcli/lib/config/config.py +47 -0
  35. mcli/lib/discovery/__init__.py +1 -0
  36. mcli/lib/discovery/command_discovery.py +274 -0
  37. mcli/lib/erd/erd.py +1345 -0
  38. mcli/lib/erd/generate_graph.py +453 -0
  39. mcli/lib/files/files.py +76 -0
  40. mcli/lib/fs/fs.py +109 -0
  41. mcli/lib/lib.py +29 -0
  42. mcli/lib/logger/logger.py +611 -0
  43. mcli/lib/performance/optimizer.py +409 -0
  44. mcli/lib/performance/rust_bridge.py +502 -0
  45. mcli/lib/performance/uvloop_config.py +154 -0
  46. mcli/lib/pickles/pickles.py +50 -0
  47. mcli/lib/search/cached_vectorizer.py +479 -0
  48. mcli/lib/services/data_pipeline.py +460 -0
  49. mcli/lib/services/lsh_client.py +441 -0
  50. mcli/lib/services/redis_service.py +387 -0
  51. mcli/lib/shell/shell.py +137 -0
  52. mcli/lib/toml/toml.py +33 -0
  53. mcli/lib/ui/styling.py +47 -0
  54. mcli/lib/ui/visual_effects.py +634 -0
  55. mcli/lib/watcher/watcher.py +185 -0
  56. mcli/ml/api/app.py +215 -0
  57. mcli/ml/api/middleware.py +224 -0
  58. mcli/ml/api/routers/admin_router.py +12 -0
  59. mcli/ml/api/routers/auth_router.py +244 -0
  60. mcli/ml/api/routers/backtest_router.py +12 -0
  61. mcli/ml/api/routers/data_router.py +12 -0
  62. mcli/ml/api/routers/model_router.py +302 -0
  63. mcli/ml/api/routers/monitoring_router.py +12 -0
  64. mcli/ml/api/routers/portfolio_router.py +12 -0
  65. mcli/ml/api/routers/prediction_router.py +267 -0
  66. mcli/ml/api/routers/trade_router.py +12 -0
  67. mcli/ml/api/routers/websocket_router.py +76 -0
  68. mcli/ml/api/schemas.py +64 -0
  69. mcli/ml/auth/auth_manager.py +425 -0
  70. mcli/ml/auth/models.py +154 -0
  71. mcli/ml/auth/permissions.py +302 -0
  72. mcli/ml/backtesting/backtest_engine.py +502 -0
  73. mcli/ml/backtesting/performance_metrics.py +393 -0
  74. mcli/ml/cache.py +400 -0
  75. mcli/ml/cli/main.py +398 -0
  76. mcli/ml/config/settings.py +394 -0
  77. mcli/ml/configs/dvc_config.py +230 -0
  78. mcli/ml/configs/mlflow_config.py +131 -0
  79. mcli/ml/configs/mlops_manager.py +293 -0
  80. mcli/ml/dashboard/app.py +532 -0
  81. mcli/ml/dashboard/app_integrated.py +738 -0
  82. mcli/ml/dashboard/app_supabase.py +560 -0
  83. mcli/ml/dashboard/app_training.py +615 -0
  84. mcli/ml/dashboard/cli.py +51 -0
  85. mcli/ml/data_ingestion/api_connectors.py +501 -0
  86. mcli/ml/data_ingestion/data_pipeline.py +567 -0
  87. mcli/ml/data_ingestion/stream_processor.py +512 -0
  88. mcli/ml/database/migrations/env.py +94 -0
  89. mcli/ml/database/models.py +667 -0
  90. mcli/ml/database/session.py +200 -0
  91. mcli/ml/experimentation/ab_testing.py +845 -0
  92. mcli/ml/features/ensemble_features.py +607 -0
  93. mcli/ml/features/political_features.py +676 -0
  94. mcli/ml/features/recommendation_engine.py +809 -0
  95. mcli/ml/features/stock_features.py +573 -0
  96. mcli/ml/features/test_feature_engineering.py +346 -0
  97. mcli/ml/logging.py +85 -0
  98. mcli/ml/mlops/data_versioning.py +518 -0
  99. mcli/ml/mlops/experiment_tracker.py +377 -0
  100. mcli/ml/mlops/model_serving.py +481 -0
  101. mcli/ml/mlops/pipeline_orchestrator.py +614 -0
  102. mcli/ml/models/base_models.py +324 -0
  103. mcli/ml/models/ensemble_models.py +675 -0
  104. mcli/ml/models/recommendation_models.py +474 -0
  105. mcli/ml/models/test_models.py +487 -0
  106. mcli/ml/monitoring/drift_detection.py +676 -0
  107. mcli/ml/monitoring/metrics.py +45 -0
  108. mcli/ml/optimization/portfolio_optimizer.py +834 -0
  109. mcli/ml/preprocessing/data_cleaners.py +451 -0
  110. mcli/ml/preprocessing/feature_extractors.py +491 -0
  111. mcli/ml/preprocessing/ml_pipeline.py +382 -0
  112. mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
  113. mcli/ml/preprocessing/test_preprocessing.py +294 -0
  114. mcli/ml/scripts/populate_sample_data.py +200 -0
  115. mcli/ml/tasks.py +400 -0
  116. mcli/ml/tests/test_integration.py +429 -0
  117. mcli/ml/tests/test_training_dashboard.py +387 -0
  118. mcli/public/oi/oi.py +15 -0
  119. mcli/public/public.py +4 -0
  120. mcli/self/self_cmd.py +1246 -0
  121. mcli/workflow/daemon/api_daemon.py +800 -0
  122. mcli/workflow/daemon/async_command_database.py +681 -0
  123. mcli/workflow/daemon/async_process_manager.py +591 -0
  124. mcli/workflow/daemon/client.py +530 -0
  125. mcli/workflow/daemon/commands.py +1196 -0
  126. mcli/workflow/daemon/daemon.py +905 -0
  127. mcli/workflow/daemon/daemon_api.py +59 -0
  128. mcli/workflow/daemon/enhanced_daemon.py +571 -0
  129. mcli/workflow/daemon/process_cli.py +244 -0
  130. mcli/workflow/daemon/process_manager.py +439 -0
  131. mcli/workflow/daemon/test_daemon.py +275 -0
  132. mcli/workflow/dashboard/dashboard_cmd.py +113 -0
  133. mcli/workflow/docker/docker.py +0 -0
  134. mcli/workflow/file/file.py +100 -0
  135. mcli/workflow/gcloud/config.toml +21 -0
  136. mcli/workflow/gcloud/gcloud.py +58 -0
  137. mcli/workflow/git_commit/ai_service.py +328 -0
  138. mcli/workflow/git_commit/commands.py +430 -0
  139. mcli/workflow/lsh_integration.py +355 -0
  140. mcli/workflow/model_service/client.py +594 -0
  141. mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
  142. mcli/workflow/model_service/lightweight_embedder.py +397 -0
  143. mcli/workflow/model_service/lightweight_model_server.py +714 -0
  144. mcli/workflow/model_service/lightweight_test.py +241 -0
  145. mcli/workflow/model_service/model_service.py +1955 -0
  146. mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
  147. mcli/workflow/model_service/pdf_processor.py +386 -0
  148. mcli/workflow/model_service/test_efficient_runner.py +234 -0
  149. mcli/workflow/model_service/test_example.py +315 -0
  150. mcli/workflow/model_service/test_integration.py +131 -0
  151. mcli/workflow/model_service/test_new_features.py +149 -0
  152. mcli/workflow/openai/openai.py +99 -0
  153. mcli/workflow/politician_trading/commands.py +1790 -0
  154. mcli/workflow/politician_trading/config.py +134 -0
  155. mcli/workflow/politician_trading/connectivity.py +490 -0
  156. mcli/workflow/politician_trading/data_sources.py +395 -0
  157. mcli/workflow/politician_trading/database.py +410 -0
  158. mcli/workflow/politician_trading/demo.py +248 -0
  159. mcli/workflow/politician_trading/models.py +165 -0
  160. mcli/workflow/politician_trading/monitoring.py +413 -0
  161. mcli/workflow/politician_trading/scrapers.py +966 -0
  162. mcli/workflow/politician_trading/scrapers_california.py +412 -0
  163. mcli/workflow/politician_trading/scrapers_eu.py +377 -0
  164. mcli/workflow/politician_trading/scrapers_uk.py +350 -0
  165. mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
  166. mcli/workflow/politician_trading/supabase_functions.py +354 -0
  167. mcli/workflow/politician_trading/workflow.py +852 -0
  168. mcli/workflow/registry/registry.py +180 -0
  169. mcli/workflow/repo/repo.py +223 -0
  170. mcli/workflow/scheduler/commands.py +493 -0
  171. mcli/workflow/scheduler/cron_parser.py +238 -0
  172. mcli/workflow/scheduler/job.py +182 -0
  173. mcli/workflow/scheduler/monitor.py +139 -0
  174. mcli/workflow/scheduler/persistence.py +324 -0
  175. mcli/workflow/scheduler/scheduler.py +679 -0
  176. mcli/workflow/sync/sync_cmd.py +437 -0
  177. mcli/workflow/sync/test_cmd.py +314 -0
  178. mcli/workflow/videos/videos.py +242 -0
  179. mcli/workflow/wakatime/wakatime.py +11 -0
  180. mcli/workflow/workflow.py +37 -0
  181. mcli_framework-7.0.0.dist-info/METADATA +479 -0
  182. mcli_framework-7.0.0.dist-info/RECORD +186 -0
  183. mcli_framework-7.0.0.dist-info/WHEEL +5 -0
  184. mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
  185. mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
  186. mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,328 @@
1
+ import json
2
+ import logging
3
+ from typing import Any, Dict, Optional
4
+
5
+ import ollama
6
+
7
+ from mcli.lib.logger.logger import get_logger
8
+ from mcli.lib.toml.toml import read_from_toml
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ class GitCommitAIService:
14
+ """AI service for generating intelligent git commit messages"""
15
+
16
+ def __init__(self):
17
+ self.config = self._load_config()
18
+ self.model_name = self.config.get("model", "phi3-mini")
19
+ self.temperature = float(
20
+ self.config.get("temperature", 0.3)
21
+ ) # Lower temp for more consistent commits
22
+ self.ollama_base_url = self.config.get("ollama_base_url", "http://localhost:11434")
23
+
24
+ def _load_config(self) -> Dict[str, Any]:
25
+ """Load LLM configuration from config.toml"""
26
+ try:
27
+ config = read_from_toml("config.toml", "llm") or {}
28
+
29
+ if not config:
30
+ # Default configuration for git commits
31
+ config = {
32
+ "provider": "local",
33
+ "model": "phi3-mini",
34
+ "temperature": 0.3,
35
+ "ollama_base_url": "http://localhost:11434",
36
+ }
37
+
38
+ return config
39
+ except Exception as e:
40
+ logger.warning(f"Could not load config, using defaults: {e}")
41
+ return {
42
+ "provider": "local",
43
+ "model": "phi3-mini",
44
+ "temperature": 0.3,
45
+ "ollama_base_url": "http://localhost:11434",
46
+ }
47
+
48
+ def _analyze_file_patterns(self, changes: Dict[str, Any]) -> Dict[str, Any]:
49
+ """Analyze file patterns to understand the scope of changes"""
50
+ analysis = {"languages": set(), "categories": set(), "scope": "unknown", "confidence": 0.0}
51
+
52
+ all_files = (
53
+ changes["changes"]["added"]
54
+ + changes["changes"]["modified"]
55
+ + changes["changes"]["deleted"]
56
+ + changes["changes"]["renamed"]
57
+ )
58
+
59
+ # Language detection
60
+ language_map = {
61
+ ".py": "python",
62
+ ".js": "javascript",
63
+ ".ts": "typescript",
64
+ ".jsx": "react",
65
+ ".tsx": "react",
66
+ ".go": "go",
67
+ ".rs": "rust",
68
+ ".java": "java",
69
+ ".cpp": "cpp",
70
+ ".c": "c",
71
+ ".html": "html",
72
+ ".css": "css",
73
+ ".scss": "sass",
74
+ ".md": "markdown",
75
+ ".yml": "yaml",
76
+ ".yaml": "yaml",
77
+ ".json": "json",
78
+ ".toml": "toml",
79
+ ".dockerfile": "docker",
80
+ ".sql": "sql",
81
+ }
82
+
83
+ # Category detection
84
+ category_patterns = {
85
+ "tests": ["test_", "_test", "tests/", "/test/", ".test.", "spec_", "_spec"],
86
+ "docs": [".md", "README", "CHANGELOG", "docs/", "/doc/"],
87
+ "config": [".toml", ".yml", ".yaml", ".json", "config", ".env", "settings"],
88
+ "build": [
89
+ "Dockerfile",
90
+ "requirements",
91
+ "package.json",
92
+ "cargo.toml",
93
+ "pom.xml",
94
+ "build",
95
+ "makefile",
96
+ ],
97
+ "frontend": [
98
+ ".html",
99
+ ".css",
100
+ ".scss",
101
+ ".js",
102
+ ".jsx",
103
+ ".ts",
104
+ ".tsx",
105
+ "assets/",
106
+ "static/",
107
+ ],
108
+ "backend": [".py", ".go", ".rs", ".java", ".cpp", "api/", "server/", "backend/"],
109
+ "database": [".sql", "migration", "schema", "models/", "db/"],
110
+ "infra": ["docker", "deploy", "infrastructure", ".tf", "kubernetes", "helm"],
111
+ }
112
+
113
+ for file in all_files:
114
+ file_lower = file.lower()
115
+
116
+ # Detect languages
117
+ for ext, lang in language_map.items():
118
+ if file_lower.endswith(ext):
119
+ analysis["languages"].add(lang)
120
+ break
121
+
122
+ # Detect categories
123
+ for category, patterns in category_patterns.items():
124
+ if any(pattern in file_lower for pattern in patterns):
125
+ analysis["categories"].add(category)
126
+
127
+ # Determine scope
128
+ if len(all_files) == 1:
129
+ analysis["scope"] = "single"
130
+ analysis["confidence"] = 0.9
131
+ elif len(all_files) <= 5:
132
+ analysis["scope"] = "focused"
133
+ analysis["confidence"] = 0.8
134
+ elif len(all_files) <= 15:
135
+ analysis["scope"] = "moderate"
136
+ analysis["confidence"] = 0.7
137
+ else:
138
+ analysis["scope"] = "broad"
139
+ analysis["confidence"] = 0.6
140
+
141
+ # Convert sets to lists for JSON serialization
142
+ analysis["languages"] = list(analysis["languages"])
143
+ analysis["categories"] = list(analysis["categories"])
144
+
145
+ return analysis
146
+
147
+ def _create_commit_prompt(
148
+ self, changes: Dict[str, Any], diff_content: str, analysis: Dict[str, Any]
149
+ ) -> str:
150
+ """Create a detailed prompt for AI commit message generation"""
151
+
152
+ # Truncate diff if too long (keep first 2000 chars)
153
+ truncated_diff = diff_content[:2000] + "..." if len(diff_content) > 2000 else diff_content
154
+
155
+ prompt = f"""You are an expert software developer writing git commit messages following conventional commit standards.
156
+
157
+ CHANGE ANALYSIS:
158
+ - Files changed: {changes['total_files']}
159
+ - New files: {len(changes['changes']['added'])}
160
+ - Modified files: {len(changes['changes']['modified'])}
161
+ - Deleted files: {len(changes['changes']['deleted'])}
162
+ - Languages: {', '.join(analysis['languages']) if analysis['languages'] else 'unknown'}
163
+ - Categories: {', '.join(analysis['categories']) if analysis['categories'] else 'general'}
164
+ - Scope: {analysis['scope']}
165
+
166
+ FILE DETAILS:
167
+ Added: {', '.join(changes['changes']['added'][:10])}{'...' if len(changes['changes']['added']) > 10 else ''}
168
+ Modified: {', '.join(changes['changes']['modified'][:10])}{'...' if len(changes['changes']['modified']) > 10 else ''}
169
+ Deleted: {', '.join(changes['changes']['deleted'][:10])}{'...' if len(changes['changes']['deleted']) > 10 else ''}
170
+
171
+ CODE DIFF SAMPLE:
172
+ {truncated_diff}
173
+
174
+ Generate a conventional commit message following this format:
175
+ <type>[optional scope]: <description>
176
+
177
+ [optional body]
178
+
179
+ Types: feat, fix, docs, style, refactor, test, chore, build, ci, perf
180
+ Scope: component/module affected (optional)
181
+ Description: imperative, lowercase, no period, max 50 chars
182
+ Body: explain what and why, not how (optional, max 72 chars per line)
183
+
184
+ Guidelines:
185
+ 1. Use imperative mood ("add" not "added" or "adds")
186
+ 2. Be specific but concise
187
+ 3. Focus on WHAT changed and WHY
188
+ 4. Don't describe HOW (that's in the diff)
189
+ 5. Use conventional commit types appropriately
190
+ 6. Add scope when changes are focused on specific area
191
+ 7. Include body only if necessary for context
192
+
193
+ Examples:
194
+ - "feat(auth): add JWT token validation"
195
+ - "fix: resolve memory leak in user session cleanup"
196
+ - "docs: update API documentation for new endpoints"
197
+ - "refactor(database): optimize query performance"
198
+ - "test: add unit tests for payment processing"
199
+
200
+ Generate ONLY the commit message, nothing else:"""
201
+
202
+ return prompt
203
+
204
+ def generate_commit_message(self, changes: Dict[str, Any], diff_content: str) -> str:
205
+ """Generate an AI-powered commit message"""
206
+ try:
207
+ # Analyze the changes first
208
+ analysis = self._analyze_file_patterns(changes)
209
+
210
+ # Create the prompt
211
+ prompt = self._create_commit_prompt(changes, diff_content, analysis)
212
+
213
+ # Generate using Ollama
214
+ response = ollama.generate(
215
+ model=self.model_name,
216
+ prompt=prompt,
217
+ options={
218
+ "temperature": self.temperature,
219
+ "top_p": 0.9,
220
+ "max_tokens": 200, # Keep commit messages concise
221
+ },
222
+ )
223
+
224
+ commit_message = response.get("response", "").strip()
225
+
226
+ # Clean up the response
227
+ commit_message = self._clean_commit_message(commit_message)
228
+
229
+ # Validate and provide fallback
230
+ if not commit_message or len(commit_message.split("\n")[0]) > 100:
231
+ logger.warning("AI generated invalid commit message, using fallback")
232
+ return self._generate_fallback_message(changes, analysis)
233
+
234
+ logger.info(f"Generated AI commit message: {commit_message}")
235
+ return commit_message
236
+
237
+ except Exception as e:
238
+ logger.error(f"AI commit message generation failed: {e}")
239
+ # Fallback to rule-based generation
240
+ return self._generate_fallback_message(
241
+ changes,
242
+ analysis if "analysis" in locals() else self._analyze_file_patterns(changes),
243
+ )
244
+
245
+ def _clean_commit_message(self, message: str) -> str:
246
+ """Clean up AI generated commit message"""
247
+ lines = message.strip().split("\n")
248
+
249
+ # Remove any introductory text
250
+ cleaned_lines = []
251
+ for line in lines:
252
+ line = line.strip()
253
+ if not line:
254
+ continue
255
+ if line.lower().startswith(("here", "commit message:", "the commit", "generated")):
256
+ continue
257
+ if line.startswith('"') and line.endswith('"'):
258
+ line = line[1:-1]
259
+ if line.startswith("'") and line.endswith("'"):
260
+ line = line[1:-1]
261
+ cleaned_lines.append(line)
262
+
263
+ return "\n".join(cleaned_lines) if cleaned_lines else ""
264
+
265
+ def _generate_fallback_message(self, changes: Dict[str, Any], analysis: Dict[str, Any]) -> str:
266
+ """Generate fallback commit message using rules"""
267
+ summary_parts = []
268
+
269
+ # Determine commit type based on analysis
270
+ if "tests" in analysis["categories"]:
271
+ commit_type = "test"
272
+ elif "docs" in analysis["categories"]:
273
+ commit_type = "docs"
274
+ elif "config" in analysis["categories"]:
275
+ commit_type = "chore"
276
+ elif "build" in analysis["categories"]:
277
+ commit_type = "build"
278
+ elif changes["changes"]["added"] and not changes["changes"]["modified"]:
279
+ commit_type = "feat"
280
+ elif changes["changes"]["deleted"] and not changes["changes"]["added"]:
281
+ commit_type = "refactor"
282
+ else:
283
+ commit_type = "chore"
284
+
285
+ # Create scope if languages are focused
286
+ scope = ""
287
+ if len(analysis["languages"]) == 1:
288
+ scope = f"({analysis['languages'][0]})"
289
+ elif len(analysis["categories"]) == 1 and analysis["categories"][0] not in [
290
+ "general",
291
+ "config",
292
+ ]:
293
+ scope = f"({analysis['categories'][0]})"
294
+
295
+ # Generate description
296
+ if changes["changes"]["added"] and len(changes["changes"]["added"]) == 1:
297
+ description = f"add {changes['changes']['added'][0].split('/')[-1]}"
298
+ elif changes["changes"]["modified"] and len(changes["changes"]["modified"]) == 1:
299
+ description = f"update {changes['changes']['modified'][0].split('/')[-1]}"
300
+ elif changes["changes"]["deleted"] and len(changes["changes"]["deleted"]) == 1:
301
+ description = f"remove {changes['changes']['deleted'][0].split('/')[-1]}"
302
+ else:
303
+ total = changes["total_files"]
304
+ description = f"update {total} files"
305
+
306
+ return f"{commit_type}{scope}: {description}"
307
+
308
+ def test_ai_service(self) -> bool:
309
+ """Test if the AI service is working properly"""
310
+ try:
311
+ # Test with minimal changes
312
+ test_changes = {
313
+ "total_files": 1,
314
+ "changes": {"added": ["test.py"], "modified": [], "deleted": [], "renamed": []},
315
+ }
316
+
317
+ test_diff = """
318
+ +def hello_world():
319
+ + print("Hello, World!")
320
+ """
321
+
322
+ message = self.generate_commit_message(test_changes, test_diff)
323
+ logger.info(f"AI service test successful: {message}")
324
+ return True
325
+
326
+ except Exception as e:
327
+ logger.error(f"AI service test failed: {e}")
328
+ return False