isa-model 0.3.91__py3-none-any.whl → 0.4.3__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 (228) hide show
  1. isa_model/client.py +1166 -584
  2. isa_model/core/cache/redis_cache.py +410 -0
  3. isa_model/core/config/config_manager.py +282 -12
  4. isa_model/core/config.py +91 -1
  5. isa_model/core/database/__init__.py +1 -0
  6. isa_model/core/database/direct_db_client.py +114 -0
  7. isa_model/core/database/migration_manager.py +563 -0
  8. isa_model/core/database/migrations.py +297 -0
  9. isa_model/core/database/supabase_client.py +258 -0
  10. isa_model/core/dependencies.py +316 -0
  11. isa_model/core/discovery/__init__.py +19 -0
  12. isa_model/core/discovery/consul_discovery.py +190 -0
  13. isa_model/core/logging/__init__.py +54 -0
  14. isa_model/core/logging/influx_logger.py +523 -0
  15. isa_model/core/logging/loki_logger.py +160 -0
  16. isa_model/core/models/__init__.py +46 -0
  17. isa_model/core/models/config_models.py +625 -0
  18. isa_model/core/models/deployment_billing_tracker.py +430 -0
  19. isa_model/core/models/model_billing_tracker.py +60 -88
  20. isa_model/core/models/model_manager.py +66 -25
  21. isa_model/core/models/model_metadata.py +690 -0
  22. isa_model/core/models/model_repo.py +217 -55
  23. isa_model/core/models/model_statistics_tracker.py +234 -0
  24. isa_model/core/models/model_storage.py +0 -1
  25. isa_model/core/models/model_version_manager.py +959 -0
  26. isa_model/core/models/system_models.py +857 -0
  27. isa_model/core/pricing_manager.py +2 -249
  28. isa_model/core/repositories/__init__.py +9 -0
  29. isa_model/core/repositories/config_repository.py +912 -0
  30. isa_model/core/resilience/circuit_breaker.py +366 -0
  31. isa_model/core/security/secrets.py +358 -0
  32. isa_model/core/services/__init__.py +2 -4
  33. isa_model/core/services/intelligent_model_selector.py +479 -370
  34. isa_model/core/storage/hf_storage.py +2 -2
  35. isa_model/core/types.py +8 -0
  36. isa_model/deployment/__init__.py +5 -48
  37. isa_model/deployment/core/__init__.py +2 -31
  38. isa_model/deployment/core/deployment_manager.py +1278 -368
  39. isa_model/deployment/local/__init__.py +31 -0
  40. isa_model/deployment/local/config.py +248 -0
  41. isa_model/deployment/local/gpu_gateway.py +607 -0
  42. isa_model/deployment/local/health_checker.py +428 -0
  43. isa_model/deployment/local/provider.py +586 -0
  44. isa_model/deployment/local/tensorrt_service.py +621 -0
  45. isa_model/deployment/local/transformers_service.py +644 -0
  46. isa_model/deployment/local/vllm_service.py +527 -0
  47. isa_model/deployment/modal/__init__.py +8 -0
  48. isa_model/deployment/modal/config.py +136 -0
  49. isa_model/deployment/modal/deployer.py +894 -0
  50. isa_model/deployment/modal/services/__init__.py +3 -0
  51. isa_model/deployment/modal/services/audio/__init__.py +1 -0
  52. isa_model/deployment/modal/services/audio/isa_audio_chatTTS_service.py +520 -0
  53. isa_model/deployment/modal/services/audio/isa_audio_openvoice_service.py +758 -0
  54. isa_model/deployment/modal/services/audio/isa_audio_service_v2.py +1044 -0
  55. isa_model/deployment/modal/services/embedding/__init__.py +1 -0
  56. isa_model/deployment/modal/services/embedding/isa_embed_rerank_service.py +296 -0
  57. isa_model/deployment/modal/services/llm/__init__.py +1 -0
  58. isa_model/deployment/modal/services/llm/isa_llm_service.py +424 -0
  59. isa_model/deployment/modal/services/video/__init__.py +1 -0
  60. isa_model/deployment/modal/services/video/isa_video_hunyuan_service.py +423 -0
  61. isa_model/deployment/modal/services/vision/__init__.py +1 -0
  62. isa_model/deployment/modal/services/vision/isa_vision_ocr_service.py +519 -0
  63. isa_model/deployment/modal/services/vision/isa_vision_qwen25_service.py +709 -0
  64. isa_model/deployment/modal/services/vision/isa_vision_table_service.py +676 -0
  65. isa_model/deployment/modal/services/vision/isa_vision_ui_service.py +833 -0
  66. isa_model/deployment/modal/services/vision/isa_vision_ui_service_optimized.py +660 -0
  67. isa_model/deployment/models/org-org-acme-corp-tenant-a-service-llm-20250825-225822/tenant-a-service_modal_service.py +48 -0
  68. isa_model/deployment/models/org-test-org-123-prefix-test-service-llm-20250825-225822/prefix-test-service_modal_service.py +48 -0
  69. isa_model/deployment/models/test-llm-service-llm-20250825-204442/test-llm-service_modal_service.py +48 -0
  70. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-212906/test-monitoring-gpt2_modal_service.py +48 -0
  71. isa_model/deployment/models/test-monitoring-gpt2-llm-20250825-213009/test-monitoring-gpt2_modal_service.py +48 -0
  72. isa_model/deployment/storage/__init__.py +5 -0
  73. isa_model/deployment/storage/deployment_repository.py +824 -0
  74. isa_model/deployment/triton/__init__.py +10 -0
  75. isa_model/deployment/triton/config.py +196 -0
  76. isa_model/deployment/triton/configs/__init__.py +1 -0
  77. isa_model/deployment/triton/provider.py +512 -0
  78. isa_model/deployment/triton/scripts/__init__.py +1 -0
  79. isa_model/deployment/triton/templates/__init__.py +1 -0
  80. isa_model/inference/__init__.py +47 -1
  81. isa_model/inference/ai_factory.py +179 -16
  82. isa_model/inference/legacy_services/__init__.py +21 -0
  83. isa_model/inference/legacy_services/model_evaluation.py +637 -0
  84. isa_model/inference/legacy_services/model_service.py +573 -0
  85. isa_model/inference/legacy_services/model_serving.py +717 -0
  86. isa_model/inference/legacy_services/model_training.py +561 -0
  87. isa_model/inference/models/__init__.py +21 -0
  88. isa_model/inference/models/inference_config.py +551 -0
  89. isa_model/inference/models/inference_record.py +675 -0
  90. isa_model/inference/models/performance_models.py +714 -0
  91. isa_model/inference/repositories/__init__.py +9 -0
  92. isa_model/inference/repositories/inference_repository.py +828 -0
  93. isa_model/inference/services/audio/__init__.py +21 -0
  94. isa_model/inference/services/audio/base_realtime_service.py +225 -0
  95. isa_model/inference/services/audio/base_stt_service.py +184 -11
  96. isa_model/inference/services/audio/isa_tts_service.py +0 -0
  97. isa_model/inference/services/audio/openai_realtime_service.py +320 -124
  98. isa_model/inference/services/audio/openai_stt_service.py +53 -11
  99. isa_model/inference/services/base_service.py +17 -1
  100. isa_model/inference/services/custom_model_manager.py +277 -0
  101. isa_model/inference/services/embedding/__init__.py +13 -0
  102. isa_model/inference/services/embedding/base_embed_service.py +111 -8
  103. isa_model/inference/services/embedding/isa_embed_service.py +305 -0
  104. isa_model/inference/services/embedding/ollama_embed_service.py +15 -3
  105. isa_model/inference/services/embedding/openai_embed_service.py +2 -4
  106. isa_model/inference/services/embedding/resilient_embed_service.py +285 -0
  107. isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
  108. isa_model/inference/services/img/__init__.py +2 -2
  109. isa_model/inference/services/img/base_image_gen_service.py +24 -7
  110. isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
  111. isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
  112. isa_model/inference/services/img/services/replicate_flux.py +226 -0
  113. isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
  114. isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
  115. isa_model/inference/services/img/tests/test_img_client.py +297 -0
  116. isa_model/inference/services/llm/__init__.py +10 -2
  117. isa_model/inference/services/llm/base_llm_service.py +361 -26
  118. isa_model/inference/services/llm/cerebras_llm_service.py +628 -0
  119. isa_model/inference/services/llm/helpers/llm_adapter.py +71 -12
  120. isa_model/inference/services/llm/helpers/llm_prompts.py +342 -0
  121. isa_model/inference/services/llm/helpers/llm_utils.py +321 -23
  122. isa_model/inference/services/llm/huggingface_llm_service.py +581 -0
  123. isa_model/inference/services/llm/local_llm_service.py +747 -0
  124. isa_model/inference/services/llm/ollama_llm_service.py +11 -3
  125. isa_model/inference/services/llm/openai_llm_service.py +670 -56
  126. isa_model/inference/services/llm/yyds_llm_service.py +10 -3
  127. isa_model/inference/services/vision/__init__.py +27 -6
  128. isa_model/inference/services/vision/base_vision_service.py +118 -185
  129. isa_model/inference/services/vision/blip_vision_service.py +359 -0
  130. isa_model/inference/services/vision/helpers/image_utils.py +19 -10
  131. isa_model/inference/services/vision/isa_vision_service.py +634 -0
  132. isa_model/inference/services/vision/openai_vision_service.py +19 -10
  133. isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
  134. isa_model/inference/services/vision/vgg16_vision_service.py +257 -0
  135. isa_model/serving/api/cache_manager.py +245 -0
  136. isa_model/serving/api/dependencies/__init__.py +1 -0
  137. isa_model/serving/api/dependencies/auth.py +194 -0
  138. isa_model/serving/api/dependencies/database.py +139 -0
  139. isa_model/serving/api/error_handlers.py +284 -0
  140. isa_model/serving/api/fastapi_server.py +240 -18
  141. isa_model/serving/api/middleware/auth.py +317 -0
  142. isa_model/serving/api/middleware/security.py +268 -0
  143. isa_model/serving/api/middleware/tenant_context.py +414 -0
  144. isa_model/serving/api/routes/analytics.py +489 -0
  145. isa_model/serving/api/routes/config.py +645 -0
  146. isa_model/serving/api/routes/deployment_billing.py +315 -0
  147. isa_model/serving/api/routes/deployments.py +475 -0
  148. isa_model/serving/api/routes/gpu_gateway.py +440 -0
  149. isa_model/serving/api/routes/health.py +32 -12
  150. isa_model/serving/api/routes/inference_monitoring.py +486 -0
  151. isa_model/serving/api/routes/local_deployments.py +448 -0
  152. isa_model/serving/api/routes/logs.py +430 -0
  153. isa_model/serving/api/routes/settings.py +582 -0
  154. isa_model/serving/api/routes/tenants.py +575 -0
  155. isa_model/serving/api/routes/unified.py +992 -171
  156. isa_model/serving/api/routes/webhooks.py +479 -0
  157. isa_model/serving/api/startup.py +318 -0
  158. isa_model/serving/modal_proxy_server.py +249 -0
  159. isa_model/utils/gpu_utils.py +311 -0
  160. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/METADATA +76 -22
  161. isa_model-0.4.3.dist-info/RECORD +193 -0
  162. isa_model/deployment/cloud/__init__.py +0 -9
  163. isa_model/deployment/cloud/modal/__init__.py +0 -10
  164. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
  165. isa_model/deployment/cloud/modal/isa_vision_table_service.py +0 -532
  166. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +0 -406
  167. isa_model/deployment/cloud/modal/register_models.py +0 -321
  168. isa_model/deployment/core/deployment_config.py +0 -356
  169. isa_model/deployment/core/isa_deployment_service.py +0 -401
  170. isa_model/deployment/gpu_int8_ds8/app/server.py +0 -66
  171. isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -43
  172. isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -35
  173. isa_model/deployment/runtime/deployed_service.py +0 -338
  174. isa_model/deployment/services/__init__.py +0 -9
  175. isa_model/deployment/services/auto_deploy_vision_service.py +0 -538
  176. isa_model/deployment/services/model_service.py +0 -332
  177. isa_model/deployment/services/service_monitor.py +0 -356
  178. isa_model/deployment/services/service_registry.py +0 -527
  179. isa_model/eval/__init__.py +0 -92
  180. isa_model/eval/benchmarks.py +0 -469
  181. isa_model/eval/config/__init__.py +0 -10
  182. isa_model/eval/config/evaluation_config.py +0 -108
  183. isa_model/eval/evaluators/__init__.py +0 -18
  184. isa_model/eval/evaluators/base_evaluator.py +0 -503
  185. isa_model/eval/evaluators/llm_evaluator.py +0 -472
  186. isa_model/eval/factory.py +0 -531
  187. isa_model/eval/infrastructure/__init__.py +0 -24
  188. isa_model/eval/infrastructure/experiment_tracker.py +0 -466
  189. isa_model/eval/metrics.py +0 -798
  190. isa_model/inference/adapter/unified_api.py +0 -248
  191. isa_model/inference/services/helpers/stacked_config.py +0 -148
  192. isa_model/inference/services/img/flux_professional_service.py +0 -603
  193. isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
  194. isa_model/inference/services/others/table_transformer_service.py +0 -61
  195. isa_model/inference/services/vision/doc_analysis_service.py +0 -640
  196. isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
  197. isa_model/inference/services/vision/ui_analysis_service.py +0 -823
  198. isa_model/scripts/inference_tracker.py +0 -283
  199. isa_model/scripts/mlflow_manager.py +0 -379
  200. isa_model/scripts/model_registry.py +0 -465
  201. isa_model/scripts/register_models.py +0 -370
  202. isa_model/scripts/register_models_with_embeddings.py +0 -510
  203. isa_model/scripts/start_mlflow.py +0 -95
  204. isa_model/scripts/training_tracker.py +0 -257
  205. isa_model/training/__init__.py +0 -74
  206. isa_model/training/annotation/annotation_schema.py +0 -47
  207. isa_model/training/annotation/processors/annotation_processor.py +0 -126
  208. isa_model/training/annotation/storage/dataset_manager.py +0 -131
  209. isa_model/training/annotation/storage/dataset_schema.py +0 -44
  210. isa_model/training/annotation/tests/test_annotation_flow.py +0 -109
  211. isa_model/training/annotation/tests/test_minio copy.py +0 -113
  212. isa_model/training/annotation/tests/test_minio_upload.py +0 -43
  213. isa_model/training/annotation/views/annotation_controller.py +0 -158
  214. isa_model/training/cloud/__init__.py +0 -22
  215. isa_model/training/cloud/job_orchestrator.py +0 -402
  216. isa_model/training/cloud/runpod_trainer.py +0 -454
  217. isa_model/training/cloud/storage_manager.py +0 -482
  218. isa_model/training/core/__init__.py +0 -23
  219. isa_model/training/core/config.py +0 -181
  220. isa_model/training/core/dataset.py +0 -222
  221. isa_model/training/core/trainer.py +0 -720
  222. isa_model/training/core/utils.py +0 -213
  223. isa_model/training/factory.py +0 -424
  224. isa_model-0.3.91.dist-info/RECORD +0 -138
  225. /isa_model/{core/storage/minio_storage.py → deployment/modal/services/audio/isa_audio_fish_service.py} +0 -0
  226. /isa_model/deployment/{services → modal/services/vision}/simple_auto_deploy_vision_service.py +0 -0
  227. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/WHEEL +0 -0
  228. {isa_model-0.3.91.dist-info → isa_model-0.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,489 @@
1
+ """
2
+ Analytics API Routes
3
+
4
+ Provides comprehensive analytics data for the ISA Model Platform including
5
+ usage statistics, cost analysis, model performance, and user activity metrics.
6
+ """
7
+
8
+ from fastapi import APIRouter, Query, HTTPException, Depends
9
+ from pydantic import BaseModel
10
+ from typing import Optional, List, Dict, Any
11
+ import logging
12
+ from datetime import datetime, timedelta, date
13
+ import asyncpg
14
+ import os
15
+ from collections import defaultdict
16
+
17
+ from ....core.config.config_manager import ConfigManager
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ router = APIRouter()
22
+
23
+ # Database connection configuration
24
+ config_manager = ConfigManager()
25
+ DATABASE_URL = os.getenv("DATABASE_URL", config_manager.get_global_config().database.default_database_url)
26
+
27
+ class AnalyticsDateRange(BaseModel):
28
+ start_date: Optional[str] = None
29
+ end_date: Optional[str] = None
30
+ period: Optional[str] = "7d" # 1d, 7d, 30d, 90d
31
+
32
+ class UsageStats(BaseModel):
33
+ total_requests: int
34
+ total_cost_usd: float
35
+ total_tokens: int
36
+ unique_models: int
37
+ period_label: str
38
+
39
+ class ModelUsageStats(BaseModel):
40
+ model_id: str
41
+ provider: str
42
+ service_type: str
43
+ total_requests: int
44
+ total_cost_usd: float
45
+ total_tokens: int
46
+ avg_daily_requests: float
47
+
48
+ class ServiceTypeStats(BaseModel):
49
+ service_type: str
50
+ total_requests: int
51
+ total_cost_usd: float
52
+ total_tokens: int
53
+ model_count: int
54
+
55
+ async def get_db_connection():
56
+ """Get database connection"""
57
+ try:
58
+ conn = await asyncpg.connect(DATABASE_URL)
59
+ return conn
60
+ except Exception as e:
61
+ logger.error(f"Failed to connect to database: {e}")
62
+ raise HTTPException(status_code=500, detail="Database connection failed")
63
+
64
+ def parse_date_range(period: str) -> tuple:
65
+ """Parse period string into start and end dates"""
66
+ end_date = date.today()
67
+
68
+ if period == "1d":
69
+ start_date = end_date - timedelta(days=1)
70
+ elif period == "7d":
71
+ start_date = end_date - timedelta(days=7)
72
+ elif period == "30d":
73
+ start_date = end_date - timedelta(days=30)
74
+ elif period == "90d":
75
+ start_date = end_date - timedelta(days=90)
76
+ else:
77
+ start_date = end_date - timedelta(days=7)
78
+
79
+ return start_date, end_date
80
+
81
+ @router.get("/overview")
82
+ async def get_analytics_overview(
83
+ period: str = Query("7d", description="Time period (1d, 7d, 30d, 90d)")
84
+ ):
85
+ """Get high-level analytics overview"""
86
+ try:
87
+ start_date, end_date = parse_date_range(period)
88
+
89
+ conn = await get_db_connection()
90
+ try:
91
+ # Get overall statistics
92
+ stats_query = """
93
+ SELECT
94
+ SUM(total_requests) as total_requests,
95
+ SUM(total_cost_usd) as total_cost_usd,
96
+ SUM(total_tokens) as total_tokens,
97
+ COUNT(DISTINCT model_id) as unique_models,
98
+ COUNT(DISTINCT provider) as unique_providers,
99
+ COUNT(DISTINCT service_type) as unique_services
100
+ FROM dev.model_statistics
101
+ WHERE date >= $1 AND date <= $2
102
+ """
103
+
104
+ stats_result = await conn.fetchrow(stats_query, start_date, end_date)
105
+
106
+ # Get daily trend data
107
+ trend_query = """
108
+ SELECT
109
+ date,
110
+ SUM(total_requests) as daily_requests,
111
+ SUM(total_cost_usd) as daily_cost,
112
+ SUM(total_tokens) as daily_tokens
113
+ FROM dev.model_statistics
114
+ WHERE date >= $1 AND date <= $2
115
+ GROUP BY date
116
+ ORDER BY date
117
+ """
118
+
119
+ trend_results = await conn.fetch(trend_query, start_date, end_date)
120
+
121
+ # Format response
122
+ overview = {
123
+ "summary": {
124
+ "total_requests": int(stats_result["total_requests"] or 0),
125
+ "total_cost_usd": float(stats_result["total_cost_usd"] or 0),
126
+ "total_tokens": int(stats_result["total_tokens"] or 0),
127
+ "unique_models": int(stats_result["unique_models"] or 0),
128
+ "unique_providers": int(stats_result["unique_providers"] or 0),
129
+ "unique_services": int(stats_result["unique_services"] or 0),
130
+ "period": period,
131
+ "start_date": start_date.isoformat(),
132
+ "end_date": end_date.isoformat()
133
+ },
134
+ "daily_trends": [
135
+ {
136
+ "date": row["date"].isoformat(),
137
+ "requests": int(row["daily_requests"] or 0),
138
+ "cost_usd": float(row["daily_cost"] or 0),
139
+ "tokens": int(row["daily_tokens"] or 0)
140
+ }
141
+ for row in trend_results
142
+ ]
143
+ }
144
+
145
+ return overview
146
+
147
+ finally:
148
+ await conn.close()
149
+
150
+ except Exception as e:
151
+ logger.error(f"Error getting analytics overview: {e}")
152
+ raise HTTPException(status_code=500, detail=f"Failed to get analytics overview: {str(e)}")
153
+
154
+ @router.get("/models")
155
+ async def get_model_analytics(
156
+ period: str = Query("7d", description="Time period"),
157
+ service_type: Optional[str] = Query(None, description="Filter by service type"),
158
+ limit: int = Query(20, description="Limit number of results")
159
+ ):
160
+ """Get model usage analytics"""
161
+ try:
162
+ start_date, end_date = parse_date_range(period)
163
+
164
+ conn = await get_db_connection()
165
+ try:
166
+ # Build query with optional service type filter
167
+ where_clause = "WHERE date >= $1 AND date <= $2"
168
+ params = [start_date, end_date]
169
+
170
+ if service_type:
171
+ where_clause += " AND service_type = $3"
172
+ params.append(service_type)
173
+
174
+ # Calculate days for average calculation
175
+ days_in_period = (end_date - start_date).days + 1
176
+
177
+ if service_type:
178
+ query = """
179
+ SELECT
180
+ model_id,
181
+ provider,
182
+ service_type,
183
+ SUM(total_requests) as total_requests,
184
+ SUM(total_cost_usd) as total_cost_usd,
185
+ SUM(total_tokens) as total_tokens,
186
+ ROUND(SUM(total_requests)::numeric / $4, 2) as avg_daily_requests
187
+ FROM dev.model_statistics
188
+ WHERE date >= $1 AND date <= $2 AND service_type = $3
189
+ GROUP BY model_id, provider, service_type
190
+ ORDER BY total_requests DESC
191
+ LIMIT $5
192
+ """
193
+ params = [start_date, end_date, service_type, days_in_period, limit]
194
+ else:
195
+ query = """
196
+ SELECT
197
+ model_id,
198
+ provider,
199
+ service_type,
200
+ SUM(total_requests) as total_requests,
201
+ SUM(total_cost_usd) as total_cost_usd,
202
+ SUM(total_tokens) as total_tokens,
203
+ ROUND(SUM(total_requests)::numeric / $3, 2) as avg_daily_requests
204
+ FROM dev.model_statistics
205
+ WHERE date >= $1 AND date <= $2
206
+ GROUP BY model_id, provider, service_type
207
+ ORDER BY total_requests DESC
208
+ LIMIT $4
209
+ """
210
+ params = [start_date, end_date, days_in_period, limit]
211
+
212
+ results = await conn.fetch(query, *params)
213
+
214
+ models = [
215
+ {
216
+ "model_id": row["model_id"],
217
+ "provider": row["provider"],
218
+ "service_type": row["service_type"],
219
+ "total_requests": int(row["total_requests"] or 0),
220
+ "total_cost_usd": float(row["total_cost_usd"] or 0),
221
+ "total_tokens": int(row["total_tokens"] or 0),
222
+ "avg_daily_requests": float(row["avg_daily_requests"] or 0)
223
+ }
224
+ for row in results
225
+ ]
226
+
227
+ return {
228
+ "models": models,
229
+ "period": period,
230
+ "service_type_filter": service_type,
231
+ "total_models": len(models)
232
+ }
233
+
234
+ finally:
235
+ await conn.close()
236
+
237
+ except Exception as e:
238
+ logger.error(f"Error getting model analytics: {e}")
239
+ raise HTTPException(status_code=500, detail=f"Failed to get model analytics: {str(e)}")
240
+
241
+ @router.get("/services")
242
+ async def get_service_analytics(
243
+ period: str = Query("7d", description="Time period")
244
+ ):
245
+ """Get service type analytics"""
246
+ try:
247
+ start_date, end_date = parse_date_range(period)
248
+
249
+ conn = await get_db_connection()
250
+ try:
251
+ query = """
252
+ SELECT
253
+ service_type,
254
+ SUM(total_requests) as total_requests,
255
+ SUM(total_cost_usd) as total_cost_usd,
256
+ SUM(total_tokens) as total_tokens,
257
+ COUNT(DISTINCT model_id) as model_count,
258
+ COUNT(DISTINCT provider) as provider_count
259
+ FROM dev.model_statistics
260
+ WHERE date >= $1 AND date <= $2
261
+ GROUP BY service_type
262
+ ORDER BY total_requests DESC
263
+ """
264
+
265
+ results = await conn.fetch(query, start_date, end_date)
266
+
267
+ services = [
268
+ {
269
+ "service_type": row["service_type"],
270
+ "total_requests": int(row["total_requests"] or 0),
271
+ "total_cost_usd": float(row["total_cost_usd"] or 0),
272
+ "total_tokens": int(row["total_tokens"] or 0),
273
+ "model_count": int(row["model_count"] or 0),
274
+ "provider_count": int(row["provider_count"] or 0)
275
+ }
276
+ for row in results
277
+ ]
278
+
279
+ return {
280
+ "services": services,
281
+ "period": period,
282
+ "total_services": len(services)
283
+ }
284
+
285
+ finally:
286
+ await conn.close()
287
+
288
+ except Exception as e:
289
+ logger.error(f"Error getting service analytics: {e}")
290
+ raise HTTPException(status_code=500, detail=f"Failed to get service analytics: {str(e)}")
291
+
292
+ @router.get("/costs")
293
+ async def get_cost_analytics(
294
+ period: str = Query("7d", description="Time period"),
295
+ breakdown: str = Query("daily", description="Breakdown type (daily, service, model, provider)")
296
+ ):
297
+ """Get detailed cost analytics"""
298
+ try:
299
+ start_date, end_date = parse_date_range(period)
300
+
301
+ conn = await get_db_connection()
302
+ try:
303
+ if breakdown == "daily":
304
+ query = """
305
+ SELECT
306
+ date,
307
+ SUM(total_cost_usd) as total_cost,
308
+ SUM(total_requests) as total_requests
309
+ FROM dev.model_statistics
310
+ WHERE date >= $1 AND date <= $2
311
+ GROUP BY date
312
+ ORDER BY date
313
+ """
314
+
315
+ elif breakdown == "service":
316
+ query = """
317
+ SELECT
318
+ service_type as category,
319
+ SUM(total_cost_usd) as total_cost,
320
+ SUM(total_requests) as total_requests
321
+ FROM dev.model_statistics
322
+ WHERE date >= $1 AND date <= $2
323
+ GROUP BY service_type
324
+ ORDER BY total_cost DESC
325
+ """
326
+
327
+ elif breakdown == "model":
328
+ query = """
329
+ SELECT
330
+ model_id as category,
331
+ SUM(total_cost_usd) as total_cost,
332
+ SUM(total_requests) as total_requests
333
+ FROM dev.model_statistics
334
+ WHERE date >= $1 AND date <= $2
335
+ GROUP BY model_id
336
+ ORDER BY total_cost DESC
337
+ LIMIT 10
338
+ """
339
+
340
+ elif breakdown == "provider":
341
+ query = """
342
+ SELECT
343
+ provider as category,
344
+ SUM(total_cost_usd) as total_cost,
345
+ SUM(total_requests) as total_requests
346
+ FROM dev.model_statistics
347
+ WHERE date >= $1 AND date <= $2
348
+ GROUP BY provider
349
+ ORDER BY total_cost DESC
350
+ """
351
+
352
+ else:
353
+ raise HTTPException(status_code=400, detail="Invalid breakdown type")
354
+
355
+ results = await conn.fetch(query, start_date, end_date)
356
+
357
+ cost_data = []
358
+ for row in results:
359
+ if breakdown == "daily":
360
+ cost_data.append({
361
+ "date": row["date"].isoformat(),
362
+ "total_cost": float(row["total_cost"] or 0),
363
+ "total_requests": int(row["total_requests"] or 0)
364
+ })
365
+ else:
366
+ cost_data.append({
367
+ "category": row["category"],
368
+ "total_cost": float(row["total_cost"] or 0),
369
+ "total_requests": int(row["total_requests"] or 0)
370
+ })
371
+
372
+ return {
373
+ "cost_data": cost_data,
374
+ "breakdown": breakdown,
375
+ "period": period,
376
+ "total_entries": len(cost_data)
377
+ }
378
+
379
+ finally:
380
+ await conn.close()
381
+
382
+ except Exception as e:
383
+ logger.error(f"Error getting cost analytics: {e}")
384
+ raise HTTPException(status_code=500, detail=f"Failed to get cost analytics: {str(e)}")
385
+
386
+ @router.get("/users")
387
+ async def get_user_analytics(
388
+ period: str = Query("7d", description="Time period"),
389
+ limit: int = Query(10, description="Limit number of results")
390
+ ):
391
+ """Get user activity analytics"""
392
+ try:
393
+ start_date, end_date = parse_date_range(period)
394
+ start_datetime = datetime.combine(start_date, datetime.min.time())
395
+ end_datetime = datetime.combine(end_date, datetime.max.time())
396
+
397
+ conn = await get_db_connection()
398
+ try:
399
+ # Get user usage statistics
400
+ user_query = """
401
+ SELECT
402
+ user_id,
403
+ COUNT(*) as total_requests,
404
+ SUM(cost_usd) as total_cost,
405
+ SUM(tokens_used) as total_tokens,
406
+ COUNT(DISTINCT DATE(created_at)) as active_days
407
+ FROM dev.user_usage_records
408
+ WHERE created_at >= $1 AND created_at <= $2
409
+ GROUP BY user_id
410
+ ORDER BY total_requests DESC
411
+ LIMIT $3
412
+ """
413
+
414
+ user_results = await conn.fetch(user_query, start_datetime, end_datetime, limit)
415
+
416
+ # Get daily user activity
417
+ daily_query = """
418
+ SELECT
419
+ DATE(created_at) as date,
420
+ COUNT(DISTINCT user_id) as active_users,
421
+ COUNT(*) as total_requests
422
+ FROM dev.user_usage_records
423
+ WHERE created_at >= $1 AND created_at <= $2
424
+ GROUP BY DATE(created_at)
425
+ ORDER BY date
426
+ """
427
+
428
+ daily_results = await conn.fetch(daily_query, start_datetime, end_datetime)
429
+
430
+ users = [
431
+ {
432
+ "user_id": row["user_id"],
433
+ "total_requests": int(row["total_requests"] or 0),
434
+ "total_cost": float(row["total_cost"] or 0),
435
+ "total_tokens": int(row["total_tokens"] or 0),
436
+ "active_days": int(row["active_days"] or 0)
437
+ }
438
+ for row in user_results
439
+ ]
440
+
441
+ daily_activity = [
442
+ {
443
+ "date": row["date"].isoformat(),
444
+ "active_users": int(row["active_users"] or 0),
445
+ "total_requests": int(row["total_requests"] or 0)
446
+ }
447
+ for row in daily_results
448
+ ]
449
+
450
+ return {
451
+ "top_users": users,
452
+ "daily_activity": daily_activity,
453
+ "period": period,
454
+ "total_users_shown": len(users)
455
+ }
456
+
457
+ finally:
458
+ await conn.close()
459
+
460
+ except Exception as e:
461
+ logger.error(f"Error getting user analytics: {e}")
462
+ raise HTTPException(status_code=500, detail=f"Failed to get user analytics: {str(e)}")
463
+
464
+ @router.get("/health")
465
+ async def analytics_health():
466
+ """Health check for analytics service"""
467
+ try:
468
+ conn = await get_db_connection()
469
+ try:
470
+ # Test database connectivity
471
+ result = await conn.fetchval("SELECT COUNT(*) FROM dev.model_statistics")
472
+
473
+ return {
474
+ "status": "healthy",
475
+ "service": "analytics",
476
+ "total_model_records": result,
477
+ "database": "connected"
478
+ }
479
+
480
+ finally:
481
+ await conn.close()
482
+
483
+ except Exception as e:
484
+ logger.error(f"Analytics health check failed: {e}")
485
+ return {
486
+ "status": "unhealthy",
487
+ "service": "analytics",
488
+ "error": str(e)
489
+ }