isa-model 0.3.9__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.
- isa_model/__init__.py +1 -1
- isa_model/client.py +732 -565
- isa_model/core/cache/redis_cache.py +401 -0
- isa_model/core/config/config_manager.py +53 -10
- isa_model/core/config.py +1 -1
- isa_model/core/database/__init__.py +1 -0
- isa_model/core/database/migrations.py +277 -0
- isa_model/core/database/supabase_client.py +123 -0
- isa_model/core/models/__init__.py +37 -0
- isa_model/core/models/model_billing_tracker.py +60 -88
- isa_model/core/models/model_manager.py +36 -18
- isa_model/core/models/model_repo.py +44 -38
- isa_model/core/models/model_statistics_tracker.py +234 -0
- isa_model/core/models/model_storage.py +0 -1
- isa_model/core/models/model_version_manager.py +959 -0
- isa_model/core/pricing_manager.py +2 -249
- isa_model/core/resilience/circuit_breaker.py +366 -0
- isa_model/core/security/secrets.py +358 -0
- isa_model/core/services/__init__.py +2 -4
- isa_model/core/services/intelligent_model_selector.py +101 -370
- isa_model/core/storage/hf_storage.py +1 -1
- isa_model/core/types.py +7 -0
- isa_model/deployment/cloud/modal/isa_audio_chatTTS_service.py +520 -0
- isa_model/deployment/cloud/modal/isa_audio_fish_service.py +0 -0
- isa_model/deployment/cloud/modal/isa_audio_openvoice_service.py +758 -0
- isa_model/deployment/cloud/modal/isa_audio_service_v2.py +1044 -0
- isa_model/deployment/cloud/modal/isa_embed_rerank_service.py +296 -0
- isa_model/deployment/cloud/modal/isa_video_hunyuan_service.py +423 -0
- isa_model/deployment/cloud/modal/isa_vision_ocr_service.py +519 -0
- isa_model/deployment/cloud/modal/isa_vision_qwen25_service.py +709 -0
- isa_model/deployment/cloud/modal/isa_vision_table_service.py +467 -323
- isa_model/deployment/cloud/modal/isa_vision_ui_service.py +607 -180
- isa_model/deployment/cloud/modal/isa_vision_ui_service_optimized.py +660 -0
- isa_model/deployment/core/deployment_manager.py +6 -4
- isa_model/deployment/services/auto_hf_modal_deployer.py +894 -0
- isa_model/eval/benchmarks/__init__.py +27 -0
- isa_model/eval/benchmarks/multimodal_datasets.py +460 -0
- isa_model/eval/benchmarks.py +244 -12
- isa_model/eval/evaluators/__init__.py +8 -2
- isa_model/eval/evaluators/audio_evaluator.py +727 -0
- isa_model/eval/evaluators/embedding_evaluator.py +742 -0
- isa_model/eval/evaluators/vision_evaluator.py +564 -0
- isa_model/eval/example_evaluation.py +395 -0
- isa_model/eval/factory.py +272 -5
- isa_model/eval/isa_benchmarks.py +700 -0
- isa_model/eval/isa_integration.py +582 -0
- isa_model/eval/metrics.py +159 -6
- isa_model/eval/tests/unit/test_basic.py +396 -0
- isa_model/inference/ai_factory.py +44 -8
- isa_model/inference/services/audio/__init__.py +21 -0
- isa_model/inference/services/audio/base_realtime_service.py +225 -0
- isa_model/inference/services/audio/isa_tts_service.py +0 -0
- isa_model/inference/services/audio/openai_realtime_service.py +320 -124
- isa_model/inference/services/audio/openai_stt_service.py +32 -6
- isa_model/inference/services/base_service.py +17 -1
- isa_model/inference/services/embedding/__init__.py +13 -0
- isa_model/inference/services/embedding/base_embed_service.py +111 -8
- isa_model/inference/services/embedding/isa_embed_service.py +305 -0
- isa_model/inference/services/embedding/openai_embed_service.py +2 -4
- isa_model/inference/services/embedding/tests/test_embedding.py +222 -0
- isa_model/inference/services/img/__init__.py +2 -2
- isa_model/inference/services/img/base_image_gen_service.py +24 -7
- isa_model/inference/services/img/replicate_image_gen_service.py +84 -422
- isa_model/inference/services/img/services/replicate_face_swap.py +193 -0
- isa_model/inference/services/img/services/replicate_flux.py +226 -0
- isa_model/inference/services/img/services/replicate_flux_kontext.py +219 -0
- isa_model/inference/services/img/services/replicate_sticker_maker.py +249 -0
- isa_model/inference/services/img/tests/test_img_client.py +297 -0
- isa_model/inference/services/llm/base_llm_service.py +30 -6
- isa_model/inference/services/llm/helpers/llm_adapter.py +63 -9
- isa_model/inference/services/llm/ollama_llm_service.py +2 -1
- isa_model/inference/services/llm/openai_llm_service.py +652 -55
- isa_model/inference/services/llm/yyds_llm_service.py +2 -1
- isa_model/inference/services/vision/__init__.py +5 -5
- isa_model/inference/services/vision/base_vision_service.py +118 -185
- isa_model/inference/services/vision/helpers/image_utils.py +11 -5
- isa_model/inference/services/vision/isa_vision_service.py +573 -0
- isa_model/inference/services/vision/tests/test_ocr_client.py +284 -0
- isa_model/serving/api/fastapi_server.py +88 -16
- isa_model/serving/api/middleware/auth.py +311 -0
- isa_model/serving/api/middleware/security.py +278 -0
- isa_model/serving/api/routes/analytics.py +486 -0
- isa_model/serving/api/routes/deployments.py +339 -0
- isa_model/serving/api/routes/evaluations.py +579 -0
- isa_model/serving/api/routes/logs.py +430 -0
- isa_model/serving/api/routes/settings.py +582 -0
- isa_model/serving/api/routes/unified.py +324 -165
- isa_model/serving/api/startup.py +304 -0
- isa_model/serving/modal_proxy_server.py +249 -0
- isa_model/training/__init__.py +100 -6
- isa_model/training/core/__init__.py +4 -1
- isa_model/training/examples/intelligent_training_example.py +281 -0
- isa_model/training/intelligent/__init__.py +25 -0
- isa_model/training/intelligent/decision_engine.py +643 -0
- isa_model/training/intelligent/intelligent_factory.py +888 -0
- isa_model/training/intelligent/knowledge_base.py +751 -0
- isa_model/training/intelligent/resource_optimizer.py +839 -0
- isa_model/training/intelligent/task_classifier.py +576 -0
- isa_model/training/storage/__init__.py +24 -0
- isa_model/training/storage/core_integration.py +439 -0
- isa_model/training/storage/training_repository.py +552 -0
- isa_model/training/storage/training_storage.py +628 -0
- {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/METADATA +13 -1
- isa_model-0.4.0.dist-info/RECORD +182 -0
- isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -766
- isa_model/deployment/cloud/modal/register_models.py +0 -321
- isa_model/inference/adapter/unified_api.py +0 -248
- isa_model/inference/services/helpers/stacked_config.py +0 -148
- isa_model/inference/services/img/flux_professional_service.py +0 -603
- isa_model/inference/services/img/helpers/base_stacked_service.py +0 -274
- isa_model/inference/services/others/table_transformer_service.py +0 -61
- isa_model/inference/services/vision/doc_analysis_service.py +0 -640
- isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -274
- isa_model/inference/services/vision/ui_analysis_service.py +0 -823
- isa_model/scripts/inference_tracker.py +0 -283
- isa_model/scripts/mlflow_manager.py +0 -379
- isa_model/scripts/model_registry.py +0 -465
- isa_model/scripts/register_models.py +0 -370
- isa_model/scripts/register_models_with_embeddings.py +0 -510
- isa_model/scripts/start_mlflow.py +0 -95
- isa_model/scripts/training_tracker.py +0 -257
- isa_model-0.3.9.dist-info/RECORD +0 -138
- {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/WHEEL +0 -0
- {isa_model-0.3.9.dist-info → isa_model-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
Test OCR Client using ISA Model Client
|
6
|
+
Tests the SuryaOCR service for text extraction through the unified client.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import asyncio
|
10
|
+
import logging
|
11
|
+
from typing import Dict, Any
|
12
|
+
|
13
|
+
from isa_model.client import ISAModelClient
|
14
|
+
|
15
|
+
# Set up logging
|
16
|
+
logging.basicConfig(level=logging.INFO)
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
class OCRTester:
|
20
|
+
"""Test client for SuryaOCR service using ISA Model Client"""
|
21
|
+
|
22
|
+
def __init__(self):
|
23
|
+
self.client = ISAModelClient()
|
24
|
+
|
25
|
+
# Test configuration for OCR service
|
26
|
+
self.test_config = {
|
27
|
+
"model": "isa-suryaocr",
|
28
|
+
"provider": "isa",
|
29
|
+
"task": "extract",
|
30
|
+
"input_image": "isa_model/inference/services/vision/tests/contract.png"
|
31
|
+
}
|
32
|
+
|
33
|
+
async def test_ocr_extraction(self) -> Dict[str, Any]:
|
34
|
+
"""Test OCR text extraction from contract image using unified client"""
|
35
|
+
logger.info("Testing OCR text extraction via unified client...")
|
36
|
+
|
37
|
+
try:
|
38
|
+
config = self.test_config
|
39
|
+
|
40
|
+
result = await self.client.invoke(
|
41
|
+
input_data=config["input_image"],
|
42
|
+
task=config["task"],
|
43
|
+
service_type="vision",
|
44
|
+
model=config["model"],
|
45
|
+
provider=config["provider"],
|
46
|
+
languages=["en", "zh"]
|
47
|
+
)
|
48
|
+
|
49
|
+
if result.get("success"):
|
50
|
+
response = result["result"]
|
51
|
+
logger.info(f"OCR extraction successful")
|
52
|
+
|
53
|
+
# Get extracted text
|
54
|
+
extracted_text = response.get('text', '')
|
55
|
+
text_length = len(extracted_text)
|
56
|
+
logger.info(f"Text extracted: {text_length} characters")
|
57
|
+
|
58
|
+
# Get cost information
|
59
|
+
cost = response.get('metadata', {}).get('billing', {}).get('estimated_cost_usd', 0)
|
60
|
+
logger.info(f"Cost: ${cost:.6f}")
|
61
|
+
|
62
|
+
# Log first 200 characters of extracted text for verification
|
63
|
+
if extracted_text:
|
64
|
+
preview_text = extracted_text[:200] + "..." if text_length > 200 else extracted_text
|
65
|
+
logger.info(f"Text preview: {preview_text}")
|
66
|
+
|
67
|
+
return {
|
68
|
+
"status": "success",
|
69
|
+
"result": response,
|
70
|
+
"metadata": result.get("metadata", {}),
|
71
|
+
"text_length": text_length,
|
72
|
+
"cost": cost
|
73
|
+
}
|
74
|
+
else:
|
75
|
+
error_msg = result.get("error", "Unknown error")
|
76
|
+
logger.error(f"OCR extraction failed: {error_msg}")
|
77
|
+
return {"status": "error", "error": error_msg}
|
78
|
+
|
79
|
+
except Exception as e:
|
80
|
+
logger.error(f"OCR extraction failed with exception: {e}")
|
81
|
+
return {"status": "error", "error": str(e)}
|
82
|
+
|
83
|
+
async def test_direct_vision_service(self) -> Dict[str, Any]:
|
84
|
+
"""Test OCR using direct ISA vision service call"""
|
85
|
+
logger.info("Testing direct ISA vision service OCR...")
|
86
|
+
|
87
|
+
try:
|
88
|
+
from isa_model.inference import AIFactory
|
89
|
+
|
90
|
+
# Get ISA vision service directly
|
91
|
+
vision = AIFactory().get_vision(provider="isa")
|
92
|
+
|
93
|
+
# Extract text using SuryaOCR
|
94
|
+
result = await vision.extract_text(
|
95
|
+
self.test_config["input_image"],
|
96
|
+
languages=["en", "zh"]
|
97
|
+
)
|
98
|
+
|
99
|
+
if result.get('success'):
|
100
|
+
logger.info(f"Direct SuryaOCR successful")
|
101
|
+
|
102
|
+
# Get extracted text from text_results array
|
103
|
+
text_results = result.get('text_results', [])
|
104
|
+
extracted_text = ' '.join([item.get('text', '') for item in text_results])
|
105
|
+
text_length = len(extracted_text)
|
106
|
+
logger.info(f"Text extracted: {text_length} characters from {len(text_results)} detected regions")
|
107
|
+
|
108
|
+
# Get cost information
|
109
|
+
cost = result.get('billing', {}).get('estimated_cost_usd', 0)
|
110
|
+
logger.info(f"Cost: ${cost:.6f}")
|
111
|
+
|
112
|
+
# Count Chinese and English characters
|
113
|
+
chinese_chars = sum(1 for char in extracted_text if '\u4e00' <= char <= '\u9fff')
|
114
|
+
english_chars = sum(1 for char in extracted_text if char.isalpha() and ord(char) < 256)
|
115
|
+
logger.info(f"Chinese characters: {chinese_chars}, English characters: {english_chars}")
|
116
|
+
|
117
|
+
# Log text preview (first few items)
|
118
|
+
if text_results:
|
119
|
+
sample_texts = [item.get('text', '') for item in text_results[:5]]
|
120
|
+
logger.info(f"Sample extracted text: {sample_texts}")
|
121
|
+
|
122
|
+
return {
|
123
|
+
"status": "success",
|
124
|
+
"result": result,
|
125
|
+
"text_length": text_length,
|
126
|
+
"cost": cost
|
127
|
+
}
|
128
|
+
else:
|
129
|
+
error_msg = result.get("error", "Unknown error")
|
130
|
+
logger.error(f"Direct SuryaOCR failed: {error_msg}")
|
131
|
+
return {"status": "error", "error": error_msg}
|
132
|
+
|
133
|
+
except Exception as e:
|
134
|
+
logger.error(f"Direct SuryaOCR failed with exception: {e}")
|
135
|
+
return {"status": "error", "error": str(e)}
|
136
|
+
|
137
|
+
async def test_chinese_and_english_ocr(self) -> Dict[str, Any]:
|
138
|
+
"""Test OCR with Chinese and English language support"""
|
139
|
+
logger.info("Testing Chinese and English OCR support...")
|
140
|
+
|
141
|
+
try:
|
142
|
+
from isa_model.inference import AIFactory
|
143
|
+
|
144
|
+
vision = AIFactory().get_vision(provider="isa")
|
145
|
+
|
146
|
+
# Test with both Chinese and English languages
|
147
|
+
result = await vision.extract_text(
|
148
|
+
self.test_config["input_image"],
|
149
|
+
languages=["zh", "en"] # Chinese first, then English
|
150
|
+
)
|
151
|
+
|
152
|
+
if result.get('success'):
|
153
|
+
logger.info(f"Multi-language OCR successful")
|
154
|
+
|
155
|
+
extracted_text = result.get('text', '')
|
156
|
+
text_length = len(extracted_text)
|
157
|
+
logger.info(f"Text length: {text_length}")
|
158
|
+
|
159
|
+
# Check for Chinese characters
|
160
|
+
chinese_chars = sum(1 for char in extracted_text if '\u4e00' <= char <= '\u9fff')
|
161
|
+
english_chars = sum(1 for char in extracted_text if char.isalpha() and ord(char) < 256)
|
162
|
+
|
163
|
+
logger.info(f"Chinese characters detected: {chinese_chars}")
|
164
|
+
logger.info(f"English characters detected: {english_chars}")
|
165
|
+
|
166
|
+
# Get cost
|
167
|
+
cost = result.get('billing', {}).get('estimated_cost_usd', 0)
|
168
|
+
logger.info(f"Cost: ${cost:.6f}")
|
169
|
+
|
170
|
+
return {
|
171
|
+
"status": "success",
|
172
|
+
"result": result,
|
173
|
+
"text_length": text_length,
|
174
|
+
"chinese_chars": chinese_chars,
|
175
|
+
"english_chars": english_chars,
|
176
|
+
"cost": cost
|
177
|
+
}
|
178
|
+
else:
|
179
|
+
error_msg = result.get("error", "Unknown error")
|
180
|
+
logger.error(f"Multi-language OCR failed: {error_msg}")
|
181
|
+
return {"status": "error", "error": error_msg}
|
182
|
+
|
183
|
+
except Exception as e:
|
184
|
+
logger.error(f"Multi-language OCR failed with exception: {e}")
|
185
|
+
return {"status": "error", "error": str(e)}
|
186
|
+
|
187
|
+
async def test_all_ocr_methods(self) -> Dict[str, Dict[str, Any]]:
|
188
|
+
"""Test OCR functionality"""
|
189
|
+
logger.info("Starting SuryaOCR test using ISA Model Client...")
|
190
|
+
|
191
|
+
results = {}
|
192
|
+
|
193
|
+
# Test only the direct vision service (most comprehensive)
|
194
|
+
tests = [
|
195
|
+
("suryaocr_extraction", self.test_direct_vision_service)
|
196
|
+
]
|
197
|
+
|
198
|
+
for test_name, test_func in tests:
|
199
|
+
logger.info(f"\n{'='*50}")
|
200
|
+
logger.info(f"Running test: {test_name}")
|
201
|
+
logger.info(f"{'='*50}")
|
202
|
+
|
203
|
+
try:
|
204
|
+
result = await test_func()
|
205
|
+
results[test_name] = result
|
206
|
+
|
207
|
+
if result.get("status") == "success":
|
208
|
+
logger.info(f" {test_name} PASSED")
|
209
|
+
else:
|
210
|
+
logger.error(f"L {test_name} FAILED: {result.get('error', 'Unknown error')}")
|
211
|
+
|
212
|
+
except Exception as e:
|
213
|
+
logger.error(f"L {test_name} FAILED with exception: {e}")
|
214
|
+
results[test_name] = {"status": "error", "error": str(e)}
|
215
|
+
|
216
|
+
# Summary
|
217
|
+
logger.info(f"\n{'='*50}")
|
218
|
+
logger.info("TEST SUMMARY")
|
219
|
+
logger.info(f"{'='*50}")
|
220
|
+
|
221
|
+
passed = sum(1 for r in results.values() if r.get("status") == "success")
|
222
|
+
total = len(results)
|
223
|
+
|
224
|
+
logger.info(f"Passed: {passed}/{total}")
|
225
|
+
|
226
|
+
for test_name, result in results.items():
|
227
|
+
status = " PASS" if result.get("status") == "success" else "L FAIL"
|
228
|
+
logger.info(f"{test_name}: {status}")
|
229
|
+
|
230
|
+
return results
|
231
|
+
|
232
|
+
async def get_service_health(self) -> Dict[str, Any]:
|
233
|
+
"""Get health status of the client and services"""
|
234
|
+
logger.info("Checking service health...")
|
235
|
+
|
236
|
+
try:
|
237
|
+
health = await self.client.health_check()
|
238
|
+
return health
|
239
|
+
except Exception as e:
|
240
|
+
logger.error(f"Health check failed: {e}")
|
241
|
+
return {"status": "error", "error": str(e)}
|
242
|
+
|
243
|
+
async def main():
|
244
|
+
"""Main test function"""
|
245
|
+
tester = OCRTester()
|
246
|
+
|
247
|
+
# Get service health
|
248
|
+
logger.info("Checking service health...")
|
249
|
+
health = await tester.get_service_health()
|
250
|
+
logger.info(f"Service health: {health}")
|
251
|
+
|
252
|
+
# Run all tests
|
253
|
+
results = await tester.test_all_ocr_methods()
|
254
|
+
|
255
|
+
# Calculate total cost
|
256
|
+
total_cost = 0.0
|
257
|
+
for test_name, result in results.items():
|
258
|
+
if result.get("status") == "success":
|
259
|
+
cost = result.get("cost", 0.0)
|
260
|
+
total_cost += cost
|
261
|
+
|
262
|
+
logger.info(f"\nTotal cost for all OCR tests: ${total_cost:.6f}")
|
263
|
+
|
264
|
+
# Summary of text extraction results
|
265
|
+
logger.info(f"\n{'='*50}")
|
266
|
+
logger.info("TEXT EXTRACTION SUMMARY")
|
267
|
+
logger.info(f"{'='*50}")
|
268
|
+
|
269
|
+
for test_name, result in results.items():
|
270
|
+
if result.get("status") == "success":
|
271
|
+
text_length = result.get("text_length", 0)
|
272
|
+
cost = result.get("cost", 0)
|
273
|
+
logger.info(f"{test_name}: {text_length} chars extracted, ${cost:.6f}")
|
274
|
+
|
275
|
+
# Show language breakdown if available
|
276
|
+
if "chinese_chars" in result and "english_chars" in result:
|
277
|
+
logger.info(f" - Chinese: {result['chinese_chars']} chars")
|
278
|
+
logger.info(f" - English: {result['english_chars']} chars")
|
279
|
+
|
280
|
+
return results
|
281
|
+
|
282
|
+
if __name__ == "__main__":
|
283
|
+
# Run the tests
|
284
|
+
results = asyncio.run(main())
|
@@ -4,18 +4,56 @@ FastAPI Server for ISA Model Serving
|
|
4
4
|
Main FastAPI application that serves model inference endpoints
|
5
5
|
"""
|
6
6
|
|
7
|
-
from fastapi import FastAPI, Request
|
7
|
+
from fastapi import FastAPI, Request, HTTPException, Depends
|
8
8
|
from fastapi.middleware.cors import CORSMiddleware
|
9
|
+
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
9
10
|
from fastapi.responses import JSONResponse
|
11
|
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
12
|
+
from fastapi.staticfiles import StaticFiles
|
10
13
|
import time
|
11
14
|
import logging
|
12
|
-
|
15
|
+
import os
|
16
|
+
from typing import Dict, Any, Optional
|
13
17
|
|
14
|
-
from .routes import
|
18
|
+
from .routes import health, unified, deployments, logs, analytics, settings, evaluations
|
15
19
|
from .middleware.request_logger import RequestLoggerMiddleware
|
20
|
+
from .middleware.security import setup_security_middleware, check_redis_health
|
21
|
+
from .startup import run_startup_initialization
|
16
22
|
|
17
23
|
logger = logging.getLogger(__name__)
|
18
24
|
|
25
|
+
def configure_logging():
|
26
|
+
"""Configure logging based on environment variables"""
|
27
|
+
log_level = os.getenv('LOG_LEVEL', 'INFO').upper()
|
28
|
+
verbose_logging = os.getenv('VERBOSE_LOGGING', 'false').lower() == 'true'
|
29
|
+
|
30
|
+
# Set log level
|
31
|
+
level = getattr(logging, log_level, logging.INFO)
|
32
|
+
|
33
|
+
# Configure format
|
34
|
+
if verbose_logging:
|
35
|
+
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
|
36
|
+
else:
|
37
|
+
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
38
|
+
|
39
|
+
# Configure root logger
|
40
|
+
logging.basicConfig(
|
41
|
+
level=level,
|
42
|
+
format=log_format,
|
43
|
+
datefmt='%Y-%m-%d %H:%M:%S',
|
44
|
+
force=True # Override existing configuration
|
45
|
+
)
|
46
|
+
|
47
|
+
# Set uvicorn logger level to match
|
48
|
+
uvicorn_logger = logging.getLogger("uvicorn")
|
49
|
+
uvicorn_logger.setLevel(level)
|
50
|
+
|
51
|
+
# Set app logger level
|
52
|
+
app_logger = logging.getLogger("isa_model")
|
53
|
+
app_logger.setLevel(level)
|
54
|
+
|
55
|
+
logger.info(f"Logging configured - Level: {log_level}, Verbose: {verbose_logging}")
|
56
|
+
|
19
57
|
def create_app(config: Dict[str, Any] = None) -> FastAPI:
|
20
58
|
"""
|
21
59
|
Create and configure FastAPI application
|
@@ -26,6 +64,9 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
|
|
26
64
|
Returns:
|
27
65
|
Configured FastAPI application
|
28
66
|
"""
|
67
|
+
# Configure logging first
|
68
|
+
configure_logging()
|
69
|
+
|
29
70
|
app = FastAPI(
|
30
71
|
title="ISA Model Serving API",
|
31
72
|
description="High-performance model inference API",
|
@@ -34,14 +75,9 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
|
|
34
75
|
redoc_url="/redoc"
|
35
76
|
)
|
36
77
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
allow_origins=["*"], # Configure appropriately for production
|
41
|
-
allow_credentials=True,
|
42
|
-
allow_methods=["*"],
|
43
|
-
allow_headers=["*"],
|
44
|
-
)
|
78
|
+
# Setup comprehensive security middleware
|
79
|
+
# This includes CORS, rate limiting, security headers, request validation
|
80
|
+
setup_security_middleware(app)
|
45
81
|
|
46
82
|
# Add custom middleware
|
47
83
|
app.add_middleware(RequestLoggerMiddleware)
|
@@ -64,10 +100,34 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
|
|
64
100
|
# MAIN UNIFIED API - Single endpoint for all AI services
|
65
101
|
app.include_router(unified.router, prefix="/api/v1", tags=["unified-api"])
|
66
102
|
|
67
|
-
#
|
68
|
-
app.include_router(
|
69
|
-
|
70
|
-
|
103
|
+
# DEPLOYMENTS API - Model deployment management
|
104
|
+
app.include_router(deployments.router, prefix="/api/v1/deployments", tags=["deployments"])
|
105
|
+
|
106
|
+
# LOGS API - Log management and streaming
|
107
|
+
app.include_router(logs.router, prefix="/api/v1/logs", tags=["logs"])
|
108
|
+
|
109
|
+
# ANALYTICS API - Usage analytics and reporting
|
110
|
+
app.include_router(analytics.router, prefix="/api/v1/analytics", tags=["analytics"])
|
111
|
+
|
112
|
+
# SETTINGS API - Configuration and API key management
|
113
|
+
app.include_router(settings.router, prefix="/api/v1/settings", tags=["settings"])
|
114
|
+
|
115
|
+
# EVALUATIONS API - Model evaluation and benchmarking
|
116
|
+
app.include_router(evaluations.router, prefix="/api/v1/evaluations", tags=["evaluations"])
|
117
|
+
|
118
|
+
# Mount static files
|
119
|
+
static_path = os.path.join(os.path.dirname(__file__), "../static")
|
120
|
+
if os.path.exists(static_path):
|
121
|
+
app.mount("/static", StaticFiles(directory=static_path), name="static")
|
122
|
+
|
123
|
+
# Serve management dashboard at /admin
|
124
|
+
@app.get("/admin")
|
125
|
+
async def admin_dashboard():
|
126
|
+
from fastapi.responses import FileResponse
|
127
|
+
index_path = os.path.join(static_path, "index.html")
|
128
|
+
if os.path.exists(index_path):
|
129
|
+
return FileResponse(index_path)
|
130
|
+
return {"error": "Management dashboard not found"}
|
71
131
|
|
72
132
|
# Root endpoint
|
73
133
|
@app.get("/")
|
@@ -76,9 +136,21 @@ def create_app(config: Dict[str, Any] = None) -> FastAPI:
|
|
76
136
|
"service": "isa-model-serving",
|
77
137
|
"version": "1.0.0",
|
78
138
|
"status": "running",
|
79
|
-
"timestamp": time.time()
|
139
|
+
"timestamp": time.time(),
|
140
|
+
"admin_url": "/admin"
|
80
141
|
}
|
81
142
|
|
143
|
+
# Add startup event handler
|
144
|
+
@app.on_event("startup")
|
145
|
+
async def startup_event():
|
146
|
+
logger.info("🚀 Starting application startup initialization...")
|
147
|
+
try:
|
148
|
+
await run_startup_initialization()
|
149
|
+
logger.info("✅ Application startup completed successfully")
|
150
|
+
except Exception as e:
|
151
|
+
logger.error(f"❌ Application startup failed: {e}")
|
152
|
+
# Don't raise - let the app start anyway
|
153
|
+
|
82
154
|
return app
|
83
155
|
|
84
156
|
# Create default app instance
|