isa-model 0.3.91__py3-none-any.whl → 0.4.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 (123) hide show
  1. isa_model/client.py +732 -573
  2. isa_model/core/cache/redis_cache.py +401 -0
  3. isa_model/core/config/config_manager.py +53 -10
  4. isa_model/core/config.py +1 -1
  5. isa_model/core/database/__init__.py +1 -0
  6. isa_model/core/database/migrations.py +277 -0
  7. isa_model/core/database/supabase_client.py +123 -0
  8. isa_model/core/models/__init__.py +37 -0
  9. isa_model/core/models/model_billing_tracker.py +60 -88
  10. isa_model/core/models/model_manager.py +36 -18
  11. isa_model/core/models/model_repo.py +44 -38
  12. isa_model/core/models/model_statistics_tracker.py +234 -0
  13. isa_model/core/models/model_storage.py +0 -1
  14. isa_model/core/models/model_version_manager.py +959 -0
  15. isa_model/core/pricing_manager.py +2 -249
  16. isa_model/core/resilience/circuit_breaker.py +366 -0
  17. isa_model/core/security/secrets.py +358 -0
  18. isa_model/core/services/__init__.py +2 -4
  19. isa_model/core/services/intelligent_model_selector.py +101 -370
  20. isa_model/core/storage/hf_storage.py +1 -1
  21. isa_model/core/types.py +7 -0
  22. isa_model/deployment/cloud/modal/isa_audio_chatTTS_service.py +520 -0
  23. isa_model/deployment/cloud/modal/isa_audio_fish_service.py +0 -0
  24. isa_model/deployment/cloud/modal/isa_audio_openvoice_service.py +758 -0
  25. isa_model/deployment/cloud/modal/isa_audio_service_v2.py +1044 -0
  26. isa_model/deployment/cloud/modal/isa_embed_rerank_service.py +296 -0
  27. isa_model/deployment/cloud/modal/isa_video_hunyuan_service.py +423 -0
  28. isa_model/deployment/cloud/modal/isa_vision_ocr_service.py +519 -0
  29. isa_model/deployment/cloud/modal/isa_vision_qwen25_service.py +709 -0
  30. isa_model/deployment/cloud/modal/isa_vision_table_service.py +467 -323
  31. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +607 -180
  32. isa_model/deployment/cloud/modal/isa_vision_ui_service_optimized.py +660 -0
  33. isa_model/deployment/core/deployment_manager.py +6 -4
  34. isa_model/deployment/services/auto_hf_modal_deployer.py +894 -0
  35. isa_model/eval/benchmarks/__init__.py +27 -0
  36. isa_model/eval/benchmarks/multimodal_datasets.py +460 -0
  37. isa_model/eval/benchmarks.py +244 -12
  38. isa_model/eval/evaluators/__init__.py +8 -2
  39. isa_model/eval/evaluators/audio_evaluator.py +727 -0
  40. isa_model/eval/evaluators/embedding_evaluator.py +742 -0
  41. isa_model/eval/evaluators/vision_evaluator.py +564 -0
  42. isa_model/eval/example_evaluation.py +395 -0
  43. isa_model/eval/factory.py +272 -5
  44. isa_model/eval/isa_benchmarks.py +700 -0
  45. isa_model/eval/isa_integration.py +582 -0
  46. isa_model/eval/metrics.py +159 -6
  47. isa_model/eval/tests/unit/test_basic.py +396 -0
  48. isa_model/inference/ai_factory.py +44 -8
  49. isa_model/inference/services/audio/__init__.py +21 -0
  50. isa_model/inference/services/audio/base_realtime_service.py +225 -0
  51. isa_model/inference/services/audio/isa_tts_service.py +0 -0
  52. isa_model/inference/services/audio/openai_realtime_service.py +320 -124
  53. isa_model/inference/services/audio/openai_stt_service.py +32 -6
  54. isa_model/inference/services/base_service.py +17 -1
  55. isa_model/inference/services/embedding/__init__.py +13 -0
  56. isa_model/inference/services/embedding/base_embed_service.py +111 -8
  57. isa_model/inference/services/embedding/isa_embed_service.py +305 -0
  58. isa_model/inference/services/embedding/openai_embed_service.py +2 -4
  59. isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
  60. isa_model/inference/services/img/__init__.py +2 -2
  61. isa_model/inference/services/img/base_image_gen_service.py +24 -7
  62. isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
  63. isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
  64. isa_model/inference/services/img/services/replicate_flux.py +226 -0
  65. isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
  66. isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
  67. isa_model/inference/services/img/tests/test_img_client.py +297 -0
  68. isa_model/inference/services/llm/base_llm_service.py +30 -6
  69. isa_model/inference/services/llm/helpers/llm_adapter.py +63 -9
  70. isa_model/inference/services/llm/ollama_llm_service.py +2 -1
  71. isa_model/inference/services/llm/openai_llm_service.py +652 -55
  72. isa_model/inference/services/llm/yyds_llm_service.py +2 -1
  73. isa_model/inference/services/vision/__init__.py +5 -5
  74. isa_model/inference/services/vision/base_vision_service.py +118 -185
  75. isa_model/inference/services/vision/helpers/image_utils.py +11 -5
  76. isa_model/inference/services/vision/isa_vision_service.py +573 -0
  77. isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
  78. isa_model/serving/api/fastapi_server.py +88 -16
  79. isa_model/serving/api/middleware/auth.py +311 -0
  80. isa_model/serving/api/middleware/security.py +278 -0
  81. isa_model/serving/api/routes/analytics.py +486 -0
  82. isa_model/serving/api/routes/deployments.py +339 -0
  83. isa_model/serving/api/routes/evaluations.py +579 -0
  84. isa_model/serving/api/routes/logs.py +430 -0
  85. isa_model/serving/api/routes/settings.py +582 -0
  86. isa_model/serving/api/routes/unified.py +324 -165
  87. isa_model/serving/api/startup.py +304 -0
  88. isa_model/serving/modal_proxy_server.py +249 -0
  89. isa_model/training/__init__.py +100 -6
  90. isa_model/training/core/__init__.py +4 -1
  91. isa_model/training/examples/intelligent_training_example.py +281 -0
  92. isa_model/training/intelligent/__init__.py +25 -0
  93. isa_model/training/intelligent/decision_engine.py +643 -0
  94. isa_model/training/intelligent/intelligent_factory.py +888 -0
  95. isa_model/training/intelligent/knowledge_base.py +751 -0
  96. isa_model/training/intelligent/resource_optimizer.py +839 -0
  97. isa_model/training/intelligent/task_classifier.py +576 -0
  98. isa_model/training/storage/__init__.py +24 -0
  99. isa_model/training/storage/core_integration.py +439 -0
  100. isa_model/training/storage/training_repository.py +552 -0
  101. isa_model/training/storage/training_storage.py +628 -0
  102. {isa_model-0.3.91.dist-info → isa_model-0.4.0.dist-info}/METADATA +13 -1
  103. isa_model-0.4.0.dist-info/RECORD +182 -0
  104. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
  105. isa_model/deployment/cloud/modal/register_models.py +0 -321
  106. isa_model/inference/adapter/unified_api.py +0 -248
  107. isa_model/inference/services/helpers/stacked_config.py +0 -148
  108. isa_model/inference/services/img/flux_professional_service.py +0 -603
  109. isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
  110. isa_model/inference/services/others/table_transformer_service.py +0 -61
  111. isa_model/inference/services/vision/doc_analysis_service.py +0 -640
  112. isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
  113. isa_model/inference/services/vision/ui_analysis_service.py +0 -823
  114. isa_model/scripts/inference_tracker.py +0 -283
  115. isa_model/scripts/mlflow_manager.py +0 -379
  116. isa_model/scripts/model_registry.py +0 -465
  117. isa_model/scripts/register_models.py +0 -370
  118. isa_model/scripts/register_models_with_embeddings.py +0 -510
  119. isa_model/scripts/start_mlflow.py +0 -95
  120. isa_model/scripts/training_tracker.py +0 -257
  121. isa_model-0.3.91.dist-info/RECORD +0 -138
  122. {isa_model-0.3.91.dist-info → isa_model-0.4.0.dist-info}/WHEEL +0 -0
  123. {isa_model-0.3.91.dist-info → isa_model-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,439 @@
1
+ """
2
+ Training-Core Integration Layer
3
+
4
+ This module provides seamless integration between the training module
5
+ and the core model management system, ensuring:
6
+ - Trained models are automatically registered in Core ModelManager
7
+ - Training costs are tracked through Core billing system
8
+ - Model lifecycle is managed consistently
9
+ - No duplication of model metadata
10
+ """
11
+
12
+ import logging
13
+ from typing import Dict, Optional, Any, List
14
+ from datetime import datetime
15
+ from pathlib import Path
16
+
17
+ try:
18
+ from ...core.models.model_manager import ModelManager
19
+ from ...core.models.model_repo import ModelType, ModelCapability
20
+ from ...core.models.model_billing_tracker import ModelBillingTracker, ModelOperationType
21
+ from ...core.models.model_statistics_tracker import ModelStatisticsTracker
22
+ CORE_AVAILABLE = True
23
+ except ImportError:
24
+ CORE_AVAILABLE = False
25
+
26
+ from .training_storage import TrainingStorage, TrainingJobRecord
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ class CoreModelIntegration:
32
+ """
33
+ Integration layer between training and core model management.
34
+
35
+ This class ensures that:
36
+ 1. Trained models are registered in the core ModelManager
37
+ 2. Training costs are tracked through the core billing system
38
+ 3. Model metadata is kept consistent between training and core
39
+ 4. Training metrics are available in the core statistics system
40
+
41
+ Example:
42
+ ```python
43
+ integration = CoreModelIntegration()
44
+
45
+ # After training completion
46
+ integration.register_trained_model(
47
+ job_record=training_job,
48
+ model_path="/path/to/trained/model",
49
+ performance_metrics={"accuracy": 0.95}
50
+ )
51
+
52
+ # This will:
53
+ # 1. Register model in CoreModelManager
54
+ # 2. Record training costs in billing tracker
55
+ # 3. Update model statistics
56
+ # 4. Link training job to core model record
57
+ ```
58
+ """
59
+
60
+ def __init__(self,
61
+ model_manager: Optional[ModelManager] = None,
62
+ training_storage: Optional[TrainingStorage] = None):
63
+ """
64
+ Initialize core integration.
65
+
66
+ Args:
67
+ model_manager: Core model manager instance
68
+ training_storage: Training storage instance
69
+ """
70
+ self.core_available = CORE_AVAILABLE
71
+
72
+ if self.core_available:
73
+ self.model_manager = model_manager or ModelManager()
74
+ self.billing_tracker = self.model_manager.billing_tracker
75
+ self.statistics_tracker = self.model_manager.statistics_tracker
76
+ self.model_registry = self.model_manager.registry
77
+ else:
78
+ logger.warning("Core model management not available")
79
+ self.model_manager = None
80
+ self.billing_tracker = None
81
+ self.statistics_tracker = None
82
+ self.model_registry = None
83
+
84
+ self.training_storage = training_storage or TrainingStorage()
85
+
86
+ logger.info(f"Core integration initialized (Core available: {self.core_available})")
87
+
88
+ def register_trained_model(
89
+ self,
90
+ job_record: TrainingJobRecord,
91
+ model_path: str,
92
+ performance_metrics: Optional[Dict[str, float]] = None,
93
+ model_size_mb: Optional[float] = None
94
+ ) -> Optional[str]:
95
+ """
96
+ Register a trained model in the core system.
97
+
98
+ Args:
99
+ job_record: Training job record
100
+ model_path: Path to the trained model
101
+ performance_metrics: Model performance metrics
102
+ model_size_mb: Model size in MB
103
+
104
+ Returns:
105
+ Core model ID if successful, None otherwise
106
+ """
107
+ if not self.core_available:
108
+ logger.warning("Core integration not available, skipping model registration")
109
+ return None
110
+
111
+ try:
112
+ # Determine model type based on task
113
+ model_type = self._get_model_type_from_task(job_record.task_type)
114
+
115
+ # Determine capabilities
116
+ capabilities = self._get_capabilities_from_task(job_record.task_type, job_record.domain)
117
+
118
+ # Create model metadata
119
+ model_metadata = {
120
+ "base_model": job_record.base_model,
121
+ "training_job_id": job_record.job_id,
122
+ "task_type": job_record.task_type,
123
+ "domain": job_record.domain,
124
+ "dataset_source": job_record.dataset_source,
125
+ "training_config": job_record.training_config,
126
+ "performance_metrics": performance_metrics or {},
127
+ "trained_at": job_record.completed_at.isoformat() if job_record.completed_at else None,
128
+ "training_cost": sum(job_record.cost_breakdown.values()) if job_record.cost_breakdown else None,
129
+ "model_size_mb": model_size_mb
130
+ }
131
+
132
+ # Generate model name
133
+ model_name = self._generate_model_name(job_record)
134
+
135
+ # Register model in core registry
136
+ success = self.model_registry.register_model(
137
+ model_id=model_name,
138
+ model_type=model_type,
139
+ capabilities=capabilities,
140
+ metadata=model_metadata,
141
+ local_path=model_path
142
+ )
143
+
144
+ if success:
145
+ logger.info(f"Successfully registered trained model: {model_name}")
146
+
147
+ # Update training job with core model ID
148
+ self.training_storage.update_training_job(
149
+ job_record.job_id,
150
+ {"core_model_id": model_name}
151
+ )
152
+
153
+ # Record training costs
154
+ self._record_training_costs(job_record, model_name)
155
+
156
+ # Update statistics
157
+ self._update_model_statistics(job_record, model_name, performance_metrics)
158
+
159
+ return model_name
160
+ else:
161
+ logger.error("Failed to register model in core registry")
162
+ return None
163
+
164
+ except Exception as e:
165
+ logger.error(f"Failed to register trained model: {e}")
166
+ return None
167
+
168
+ def sync_training_knowledge_to_core(self) -> bool:
169
+ """
170
+ Sync training knowledge base data to core model registry.
171
+
172
+ This helps keep the core system updated with training capabilities
173
+ and model information discovered through intelligent training.
174
+
175
+ Returns:
176
+ True if successful
177
+ """
178
+ if not self.core_available:
179
+ return False
180
+
181
+ try:
182
+ # Import training knowledge base
183
+ from ..intelligent.knowledge_base import KnowledgeBase
184
+
185
+ kb = KnowledgeBase()
186
+
187
+ # Sync model specifications
188
+ synced_count = 0
189
+ for model_name, model_spec in kb.models.items():
190
+ try:
191
+ # Convert training model spec to core format
192
+ model_type = self._convert_model_type(model_spec.model_type)
193
+ capabilities = self._convert_capabilities(model_spec.supported_tasks)
194
+
195
+ metadata = {
196
+ "source": "training_knowledge_base",
197
+ "parameters": model_spec.parameters,
198
+ "context_length": model_spec.context_length,
199
+ "supported_tasks": model_spec.supported_tasks,
200
+ "supported_domains": model_spec.supported_domains,
201
+ "quality_score": model_spec.quality_score,
202
+ "efficiency_score": model_spec.efficiency_score,
203
+ "is_popular": model_spec.is_popular,
204
+ "description": model_spec.description,
205
+ "synced_at": datetime.now().isoformat()
206
+ }
207
+
208
+ # Only register if not already exists
209
+ existing = self.model_registry.get_model_by_id(model_name)
210
+ if not existing:
211
+ success = self.model_registry.register_model(
212
+ model_id=model_name,
213
+ model_type=model_type,
214
+ capabilities=capabilities,
215
+ metadata=metadata
216
+ )
217
+ if success:
218
+ synced_count += 1
219
+
220
+ except Exception as e:
221
+ logger.warning(f"Failed to sync model {model_name}: {e}")
222
+ continue
223
+
224
+ logger.info(f"Synced {synced_count} models from training knowledge base to core")
225
+ return True
226
+
227
+ except Exception as e:
228
+ logger.error(f"Failed to sync training knowledge to core: {e}")
229
+ return False
230
+
231
+ def get_core_model_for_training(self, base_model: str) -> Optional[Dict[str, Any]]:
232
+ """
233
+ Get core model information for a base model used in training.
234
+
235
+ Args:
236
+ base_model: Base model identifier
237
+
238
+ Returns:
239
+ Core model information if available
240
+ """
241
+ if not self.core_available:
242
+ return None
243
+
244
+ try:
245
+ return self.model_registry.get_model_by_id(base_model)
246
+ except Exception as e:
247
+ logger.error(f"Failed to get core model info for {base_model}: {e}")
248
+ return None
249
+
250
+ def get_training_history_for_model(self, model_id: str) -> List[TrainingJobRecord]:
251
+ """
252
+ Get training history for a specific model.
253
+
254
+ Args:
255
+ model_id: Model identifier
256
+
257
+ Returns:
258
+ List of training job records
259
+ """
260
+ try:
261
+ # Search for training jobs that produced this model
262
+ all_jobs = self.training_storage.list_training_jobs(limit=1000)
263
+
264
+ model_training_jobs = []
265
+ for job in all_jobs:
266
+ # Check if this job produced the model
267
+ if (hasattr(job, 'core_model_id') and job.core_model_id == model_id) or \
268
+ job.base_model == model_id:
269
+ model_training_jobs.append(job)
270
+
271
+ return model_training_jobs
272
+
273
+ except Exception as e:
274
+ logger.error(f"Failed to get training history for model {model_id}: {e}")
275
+ return []
276
+
277
+ def calculate_model_training_cost(self, model_id: str) -> Optional[float]:
278
+ """
279
+ Calculate total training cost for a model.
280
+
281
+ Args:
282
+ model_id: Model identifier
283
+
284
+ Returns:
285
+ Total training cost in USD
286
+ """
287
+ try:
288
+ training_jobs = self.get_training_history_for_model(model_id)
289
+
290
+ total_cost = 0.0
291
+ for job in training_jobs:
292
+ if job.cost_breakdown:
293
+ total_cost += sum(job.cost_breakdown.values())
294
+
295
+ return total_cost if total_cost > 0 else None
296
+
297
+ except Exception as e:
298
+ logger.error(f"Failed to calculate training cost for model {model_id}: {e}")
299
+ return None
300
+
301
+ def _get_model_type_from_task(self, task_type: str) -> ModelType:
302
+ """Convert training task type to core model type."""
303
+ task_to_type_map = {
304
+ "chat": ModelType.CHAT,
305
+ "classification": ModelType.CLASSIFICATION,
306
+ "generation": ModelType.GENERATION,
307
+ "summarization": ModelType.SUMMARIZATION,
308
+ "translation": ModelType.TRANSLATION,
309
+ "code": ModelType.CODE_GENERATION,
310
+ "image_generation": ModelType.IMAGE_GENERATION
311
+ }
312
+
313
+ return task_to_type_map.get(task_type, ModelType.CHAT)
314
+
315
+ def _get_capabilities_from_task(self, task_type: str, domain: str) -> List[ModelCapability]:
316
+ """Determine model capabilities from task and domain."""
317
+ capabilities = []
318
+
319
+ # Task-based capabilities
320
+ if task_type in ["chat", "generation"]:
321
+ capabilities.append(ModelCapability.TEXT_GENERATION)
322
+ if task_type == "classification":
323
+ capabilities.append(ModelCapability.TEXT_CLASSIFICATION)
324
+ if task_type == "summarization":
325
+ capabilities.append(ModelCapability.SUMMARIZATION)
326
+ if task_type == "translation":
327
+ capabilities.append(ModelCapability.TRANSLATION)
328
+ if task_type == "code":
329
+ capabilities.append(ModelCapability.CODE_GENERATION)
330
+
331
+ # Domain-based capabilities
332
+ if domain == "medical":
333
+ capabilities.append(ModelCapability.DOMAIN_SPECIFIC)
334
+ if domain in ["legal", "financial", "technical"]:
335
+ capabilities.append(ModelCapability.DOMAIN_SPECIFIC)
336
+
337
+ return capabilities
338
+
339
+ def _generate_model_name(self, job_record: TrainingJobRecord) -> str:
340
+ """Generate a unique model name for core registration."""
341
+ base_name = job_record.base_model.split("/")[-1] if "/" in job_record.base_model else job_record.base_model
342
+ timestamp = job_record.created_at.strftime("%Y%m%d_%H%M%S")
343
+ task = job_record.task_type
344
+ domain = job_record.domain
345
+
346
+ return f"{base_name}_{task}_{domain}_{timestamp}"
347
+
348
+ def _record_training_costs(self, job_record: TrainingJobRecord, model_name: str) -> None:
349
+ """Record training costs in the core billing system."""
350
+ if not self.billing_tracker or not job_record.cost_breakdown:
351
+ return
352
+
353
+ try:
354
+ total_cost = sum(job_record.cost_breakdown.values())
355
+
356
+ # Record training operation cost
357
+ self.billing_tracker.record_operation(
358
+ model_id=model_name,
359
+ operation_type=ModelOperationType.TRAINING,
360
+ cost=total_cost,
361
+ metadata={
362
+ "training_job_id": job_record.job_id,
363
+ "base_model": job_record.base_model,
364
+ "task_type": job_record.task_type,
365
+ "cost_breakdown": job_record.cost_breakdown,
366
+ "training_duration": str(job_record.completed_at - job_record.started_at) if job_record.completed_at and job_record.started_at else None
367
+ }
368
+ )
369
+
370
+ logger.info(f"Recorded training cost ${total_cost:.2f} for model {model_name}")
371
+
372
+ except Exception as e:
373
+ logger.error(f"Failed to record training costs: {e}")
374
+
375
+ def _update_model_statistics(
376
+ self,
377
+ job_record: TrainingJobRecord,
378
+ model_name: str,
379
+ performance_metrics: Optional[Dict[str, float]]
380
+ ) -> None:
381
+ """Update model statistics in the core system."""
382
+ if not self.statistics_tracker:
383
+ return
384
+
385
+ try:
386
+ # Record training completion
387
+ self.statistics_tracker.record_usage(
388
+ model_id=model_name,
389
+ operation_type="training",
390
+ metadata={
391
+ "training_job_id": job_record.job_id,
392
+ "task_type": job_record.task_type,
393
+ "domain": job_record.domain,
394
+ "performance_metrics": performance_metrics or {}
395
+ }
396
+ )
397
+
398
+ logger.info(f"Updated statistics for model {model_name}")
399
+
400
+ except Exception as e:
401
+ logger.error(f"Failed to update model statistics: {e}")
402
+
403
+ def _convert_model_type(self, training_model_type: str) -> ModelType:
404
+ """Convert training model type to core model type."""
405
+ type_map = {
406
+ "llm": ModelType.CHAT,
407
+ "sd": ModelType.IMAGE_GENERATION,
408
+ "ml": ModelType.CLASSIFICATION
409
+ }
410
+
411
+ return type_map.get(training_model_type, ModelType.CHAT)
412
+
413
+ def _convert_capabilities(self, supported_tasks: List[str]) -> List[ModelCapability]:
414
+ """Convert training supported tasks to core capabilities."""
415
+ capabilities = []
416
+
417
+ for task in supported_tasks:
418
+ if task in ["chat", "generation"]:
419
+ capabilities.append(ModelCapability.TEXT_GENERATION)
420
+ elif task == "classification":
421
+ capabilities.append(ModelCapability.TEXT_CLASSIFICATION)
422
+ elif task == "summarization":
423
+ capabilities.append(ModelCapability.SUMMARIZATION)
424
+ elif task == "translation":
425
+ capabilities.append(ModelCapability.TRANSLATION)
426
+ elif task == "code":
427
+ capabilities.append(ModelCapability.CODE_GENERATION)
428
+
429
+ return list(set(capabilities)) # Remove duplicates
430
+
431
+ def get_integration_status(self) -> Dict[str, Any]:
432
+ """Get status of core integration."""
433
+ return {
434
+ "core_available": self.core_available,
435
+ "model_manager_connected": self.model_manager is not None,
436
+ "billing_tracker_available": self.billing_tracker is not None,
437
+ "statistics_tracker_available": self.statistics_tracker is not None,
438
+ "training_storage_available": self.training_storage is not None
439
+ }