isa-model 0.3.4__py3-none-any.whl → 0.3.6__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 (100) hide show
  1. isa_model/__init__.py +30 -1
  2. isa_model/client.py +770 -0
  3. isa_model/core/config/__init__.py +16 -0
  4. isa_model/core/config/config_manager.py +514 -0
  5. isa_model/core/config.py +426 -0
  6. isa_model/core/models/model_billing_tracker.py +476 -0
  7. isa_model/core/models/model_manager.py +399 -0
  8. isa_model/core/models/model_repo.py +343 -0
  9. isa_model/core/pricing_manager.py +426 -0
  10. isa_model/core/services/__init__.py +19 -0
  11. isa_model/core/services/intelligent_model_selector.py +547 -0
  12. isa_model/core/types.py +291 -0
  13. isa_model/deployment/__init__.py +2 -0
  14. isa_model/deployment/cloud/__init__.py +9 -0
  15. isa_model/deployment/cloud/modal/__init__.py +10 -0
  16. isa_model/deployment/cloud/modal/isa_vision_doc_service.py +766 -0
  17. isa_model/deployment/cloud/modal/isa_vision_table_service.py +532 -0
  18. isa_model/deployment/cloud/modal/isa_vision_ui_service.py +406 -0
  19. isa_model/deployment/cloud/modal/register_models.py +321 -0
  20. isa_model/deployment/runtime/deployed_service.py +338 -0
  21. isa_model/deployment/services/__init__.py +9 -0
  22. isa_model/deployment/services/auto_deploy_vision_service.py +537 -0
  23. isa_model/deployment/services/model_service.py +332 -0
  24. isa_model/deployment/services/service_monitor.py +356 -0
  25. isa_model/deployment/services/service_registry.py +527 -0
  26. isa_model/eval/__init__.py +80 -44
  27. isa_model/eval/config/__init__.py +10 -0
  28. isa_model/eval/config/evaluation_config.py +108 -0
  29. isa_model/eval/evaluators/__init__.py +18 -0
  30. isa_model/eval/evaluators/base_evaluator.py +503 -0
  31. isa_model/eval/evaluators/llm_evaluator.py +472 -0
  32. isa_model/eval/factory.py +417 -709
  33. isa_model/eval/infrastructure/__init__.py +24 -0
  34. isa_model/eval/infrastructure/experiment_tracker.py +466 -0
  35. isa_model/eval/metrics.py +191 -21
  36. isa_model/inference/ai_factory.py +187 -387
  37. isa_model/inference/providers/modal_provider.py +109 -0
  38. isa_model/inference/providers/yyds_provider.py +108 -0
  39. isa_model/inference/services/__init__.py +2 -1
  40. isa_model/inference/services/audio/base_stt_service.py +65 -1
  41. isa_model/inference/services/audio/base_tts_service.py +75 -1
  42. isa_model/inference/services/audio/openai_stt_service.py +189 -151
  43. isa_model/inference/services/audio/openai_tts_service.py +12 -10
  44. isa_model/inference/services/audio/replicate_tts_service.py +61 -56
  45. isa_model/inference/services/base_service.py +55 -55
  46. isa_model/inference/services/embedding/base_embed_service.py +65 -1
  47. isa_model/inference/services/embedding/ollama_embed_service.py +103 -43
  48. isa_model/inference/services/embedding/openai_embed_service.py +8 -10
  49. isa_model/inference/services/helpers/stacked_config.py +148 -0
  50. isa_model/inference/services/img/__init__.py +18 -0
  51. isa_model/inference/services/{vision → img}/base_image_gen_service.py +80 -35
  52. isa_model/inference/services/img/flux_professional_service.py +603 -0
  53. isa_model/inference/services/img/helpers/base_stacked_service.py +274 -0
  54. isa_model/inference/services/{vision → img}/replicate_image_gen_service.py +210 -69
  55. isa_model/inference/services/llm/__init__.py +3 -3
  56. isa_model/inference/services/llm/base_llm_service.py +519 -35
  57. isa_model/inference/services/llm/{llm_adapter.py → helpers/llm_adapter.py} +40 -0
  58. isa_model/inference/services/llm/helpers/llm_prompts.py +258 -0
  59. isa_model/inference/services/llm/helpers/llm_utils.py +280 -0
  60. isa_model/inference/services/llm/ollama_llm_service.py +150 -15
  61. isa_model/inference/services/llm/openai_llm_service.py +134 -31
  62. isa_model/inference/services/llm/yyds_llm_service.py +255 -0
  63. isa_model/inference/services/vision/__init__.py +38 -4
  64. isa_model/inference/services/vision/base_vision_service.py +241 -96
  65. isa_model/inference/services/vision/disabled/isA_vision_service.py +500 -0
  66. isa_model/inference/services/vision/doc_analysis_service.py +640 -0
  67. isa_model/inference/services/vision/helpers/base_stacked_service.py +274 -0
  68. isa_model/inference/services/vision/helpers/image_utils.py +272 -3
  69. isa_model/inference/services/vision/helpers/vision_prompts.py +297 -0
  70. isa_model/inference/services/vision/openai_vision_service.py +109 -170
  71. isa_model/inference/services/vision/replicate_vision_service.py +508 -0
  72. isa_model/inference/services/vision/ui_analysis_service.py +823 -0
  73. isa_model/scripts/register_models.py +370 -0
  74. isa_model/scripts/register_models_with_embeddings.py +510 -0
  75. isa_model/serving/__init__.py +19 -0
  76. isa_model/serving/api/__init__.py +10 -0
  77. isa_model/serving/api/fastapi_server.py +89 -0
  78. isa_model/serving/api/middleware/__init__.py +9 -0
  79. isa_model/serving/api/middleware/request_logger.py +88 -0
  80. isa_model/serving/api/routes/__init__.py +5 -0
  81. isa_model/serving/api/routes/health.py +82 -0
  82. isa_model/serving/api/routes/llm.py +19 -0
  83. isa_model/serving/api/routes/ui_analysis.py +223 -0
  84. isa_model/serving/api/routes/unified.py +202 -0
  85. isa_model/serving/api/routes/vision.py +19 -0
  86. isa_model/serving/api/schemas/__init__.py +17 -0
  87. isa_model/serving/api/schemas/common.py +33 -0
  88. isa_model/serving/api/schemas/ui_analysis.py +78 -0
  89. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/METADATA +4 -1
  90. isa_model-0.3.6.dist-info/RECORD +147 -0
  91. isa_model/core/model_manager.py +0 -208
  92. isa_model/core/model_registry.py +0 -342
  93. isa_model/inference/billing_tracker.py +0 -406
  94. isa_model/inference/services/llm/triton_llm_service.py +0 -481
  95. isa_model/inference/services/vision/ollama_vision_service.py +0 -194
  96. isa_model-0.3.4.dist-info/RECORD +0 -91
  97. /isa_model/core/{model_storage.py → models/model_storage.py} +0 -0
  98. /isa_model/inference/services/{vision → embedding}/helpers/text_splitter.py +0 -0
  99. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/WHEEL +0 -0
  100. {isa_model-0.3.4.dist-info → isa_model-0.3.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,88 @@
1
+ """
2
+ Request Logger Middleware
3
+
4
+ Logs all incoming requests and responses for monitoring
5
+ """
6
+
7
+ from fastapi import Request, Response
8
+ from starlette.middleware.base import BaseHTTPMiddleware
9
+ import time
10
+ import logging
11
+ import json
12
+ from typing import Callable
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class RequestLoggerMiddleware(BaseHTTPMiddleware):
17
+ """
18
+ Middleware to log HTTP requests and responses
19
+ """
20
+
21
+ def __init__(self, app, log_body: bool = False):
22
+ super().__init__(app)
23
+ self.log_body = log_body
24
+
25
+ async def dispatch(self, request: Request, call_next: Callable) -> Response:
26
+ """
27
+ Process request and log details
28
+ """
29
+ start_time = time.time()
30
+
31
+ # Log request
32
+ request_info = {
33
+ "method": request.method,
34
+ "url": str(request.url),
35
+ "headers": dict(request.headers),
36
+ "client": request.client.host if request.client else None,
37
+ "timestamp": start_time
38
+ }
39
+
40
+ # Optionally log request body (be careful with large images)
41
+ if self.log_body and request.method in ["POST", "PUT", "PATCH"]:
42
+ try:
43
+ body = await request.body()
44
+ if len(body) < 1024: # Only log small bodies
45
+ request_info["body_size"] = len(body)
46
+ else:
47
+ request_info["body_size"] = len(body)
48
+ request_info["body_preview"] = "Large body truncated"
49
+ except Exception as e:
50
+ request_info["body_error"] = str(e)
51
+
52
+ logger.info(f"Request: {json.dumps(request_info, default=str)}")
53
+
54
+ # Process request
55
+ try:
56
+ response = await call_next(request)
57
+
58
+ # Calculate processing time
59
+ process_time = time.time() - start_time
60
+
61
+ # Log response
62
+ response_info = {
63
+ "status_code": response.status_code,
64
+ "processing_time": process_time,
65
+ "url": str(request.url),
66
+ "method": request.method
67
+ }
68
+
69
+ # Add processing time header
70
+ response.headers["X-Process-Time"] = str(process_time)
71
+
72
+ if response.status_code >= 400:
73
+ logger.warning(f"Response: {json.dumps(response_info, default=str)}")
74
+ else:
75
+ logger.info(f"Response: {json.dumps(response_info, default=str)}")
76
+
77
+ return response
78
+
79
+ except Exception as e:
80
+ process_time = time.time() - start_time
81
+ error_info = {
82
+ "error": str(e),
83
+ "processing_time": process_time,
84
+ "url": str(request.url),
85
+ "method": request.method
86
+ }
87
+ logger.error(f"Request error: {json.dumps(error_info, default=str)}")
88
+ raise
@@ -0,0 +1,5 @@
1
+ """
2
+ API Routes Module
3
+
4
+ Contains all API route definitions for different services
5
+ """
@@ -0,0 +1,82 @@
1
+ """
2
+ Health Check Routes
3
+
4
+ System health and status endpoints
5
+ """
6
+
7
+ from fastapi import APIRouter, HTTPException
8
+ from pydantic import BaseModel
9
+ import time
10
+ import psutil
11
+ import torch
12
+ from typing import Dict, Any
13
+
14
+ router = APIRouter()
15
+
16
+ class HealthResponse(BaseModel):
17
+ status: str
18
+ timestamp: float
19
+ version: str
20
+ uptime: float
21
+ system: Dict[str, Any]
22
+
23
+ @router.get("/", response_model=HealthResponse)
24
+ async def health_check():
25
+ """
26
+ Basic health check endpoint
27
+ """
28
+ return HealthResponse(
29
+ status="healthy",
30
+ timestamp=time.time(),
31
+ version="1.0.0",
32
+ uptime=time.time(), # Simplified uptime
33
+ system={
34
+ "cpu_percent": psutil.cpu_percent(),
35
+ "memory_percent": psutil.virtual_memory().percent,
36
+ "gpu_available": torch.cuda.is_available(),
37
+ "gpu_count": torch.cuda.device_count() if torch.cuda.is_available() else 0
38
+ }
39
+ )
40
+
41
+ @router.get("/detailed")
42
+ async def detailed_health():
43
+ """
44
+ Detailed health check with system information
45
+ """
46
+ gpu_info = []
47
+ if torch.cuda.is_available():
48
+ for i in range(torch.cuda.device_count()):
49
+ gpu_info.append({
50
+ "device": i,
51
+ "name": torch.cuda.get_device_name(i),
52
+ "memory_allocated": torch.cuda.memory_allocated(i),
53
+ "memory_cached": torch.cuda.memory_reserved(i)
54
+ })
55
+
56
+ return {
57
+ "status": "healthy",
58
+ "timestamp": time.time(),
59
+ "system": {
60
+ "cpu": {
61
+ "percent": psutil.cpu_percent(),
62
+ "count": psutil.cpu_count()
63
+ },
64
+ "memory": {
65
+ "percent": psutil.virtual_memory().percent,
66
+ "available": psutil.virtual_memory().available,
67
+ "total": psutil.virtual_memory().total
68
+ },
69
+ "gpu": {
70
+ "available": torch.cuda.is_available(),
71
+ "devices": gpu_info
72
+ }
73
+ }
74
+ }
75
+
76
+ @router.get("/ready")
77
+ async def readiness_probe():
78
+ """
79
+ Kubernetes readiness probe endpoint
80
+ """
81
+ # Add model loading checks here
82
+ return {"status": "ready", "timestamp": time.time()}
@@ -0,0 +1,19 @@
1
+ """
2
+ LLM API Routes
3
+
4
+ Endpoints for language model tasks (placeholder)
5
+ """
6
+
7
+ from fastapi import APIRouter, HTTPException
8
+ from pydantic import BaseModel
9
+
10
+ router = APIRouter()
11
+
12
+ @router.get("/")
13
+ async def llm_info():
14
+ """LLM service information"""
15
+ return {
16
+ "service": "llm",
17
+ "status": "placeholder",
18
+ "description": "Language model processing endpoints"
19
+ }
@@ -0,0 +1,223 @@
1
+ """
2
+ UI Analysis API Routes
3
+
4
+ Endpoints for UI element detection and analysis
5
+ """
6
+
7
+ from fastapi import APIRouter, HTTPException, UploadFile, File, Form
8
+ from pydantic import BaseModel
9
+ from typing import List, Dict, Any, Optional
10
+ import base64
11
+ import time
12
+ import logging
13
+
14
+ from ..schemas.ui_analysis import (
15
+ UIAnalysisRequest,
16
+ UIAnalysisResponse,
17
+ UIElement,
18
+ ActionPlan
19
+ )
20
+
21
+ router = APIRouter()
22
+ logger = logging.getLogger(__name__)
23
+
24
+ class UIAnalysisService:
25
+ """
26
+ Placeholder for UI Analysis Service
27
+ Will be replaced with actual Modal deployment integration
28
+ """
29
+
30
+ @staticmethod
31
+ async def analyze_ui(image_b64: str, task_type: str = "search") -> Dict[str, Any]:
32
+ """
33
+ Placeholder method for UI analysis
34
+ """
35
+ # TODO: Replace with actual Modal service call
36
+ return {
37
+ "success": True,
38
+ "service": "ui_analysis",
39
+ "total_execution_time": 2.5,
40
+ "final_output": {
41
+ "ui_elements": {
42
+ "interactive_elements": [
43
+ {
44
+ "id": "ui_0",
45
+ "type": "textbox",
46
+ "content": "Search",
47
+ "center": [400, 200],
48
+ "bbox": [300, 180, 500, 220],
49
+ "confidence": 0.95,
50
+ "interactable": True
51
+ }
52
+ ],
53
+ "summary": {
54
+ "interactive_count": 1,
55
+ "detection_confidence": 0.95
56
+ }
57
+ },
58
+ "action_plan": {
59
+ "action_plan": [
60
+ {
61
+ "step": 1,
62
+ "action": "click",
63
+ "target_coordinates": [400, 200],
64
+ "actual_coordinates": [400, 200],
65
+ "description": "Click search box",
66
+ "confidence": 0.95
67
+ }
68
+ ]
69
+ },
70
+ "automation_ready": {
71
+ "ready": True,
72
+ "confidence": 0.95,
73
+ "page_type": task_type,
74
+ "steps_count": 1
75
+ }
76
+ }
77
+ }
78
+
79
+ @router.post("/analyze", response_model=UIAnalysisResponse)
80
+ async def analyze_ui_elements(request: UIAnalysisRequest):
81
+ """
82
+ Analyze UI elements in an image
83
+
84
+ Args:
85
+ request: UI analysis request with image and task type
86
+
87
+ Returns:
88
+ UI analysis results with detected elements and action plan
89
+ """
90
+ try:
91
+ start_time = time.time()
92
+
93
+ # Validate task type
94
+ valid_task_types = ["login", "search", "content", "navigation"]
95
+ if request.task_type not in valid_task_types:
96
+ raise HTTPException(
97
+ status_code=400,
98
+ detail=f"Invalid task_type. Must be one of: {valid_task_types}"
99
+ )
100
+
101
+ # Call UI analysis service
102
+ result = await UIAnalysisService.analyze_ui(
103
+ request.image_b64,
104
+ request.task_type
105
+ )
106
+
107
+ if not result.get("success"):
108
+ raise HTTPException(
109
+ status_code=500,
110
+ detail=f"UI analysis failed: {result.get('error', 'Unknown error')}"
111
+ )
112
+
113
+ # Convert to response model
114
+ final_output = result["final_output"]
115
+
116
+ return UIAnalysisResponse(
117
+ success=True,
118
+ service="ui_analysis",
119
+ total_execution_time=result["total_execution_time"],
120
+ ui_elements=[
121
+ UIElement(**elem)
122
+ for elem in final_output["ui_elements"]["interactive_elements"]
123
+ ],
124
+ action_plan=ActionPlan(
125
+ steps=final_output["action_plan"]["action_plan"]
126
+ ),
127
+ automation_ready=final_output["automation_ready"],
128
+ metadata={
129
+ "detection_method": "modal_omniparser",
130
+ "request_time": start_time,
131
+ "task_type": request.task_type
132
+ }
133
+ )
134
+
135
+ except HTTPException:
136
+ raise
137
+ except Exception as e:
138
+ logger.error(f"UI analysis error: {e}", exc_info=True)
139
+ raise HTTPException(status_code=500, detail=str(e))
140
+
141
+ @router.post("/upload")
142
+ async def upload_and_analyze(
143
+ file: UploadFile = File(...),
144
+ task_type: str = Form("search")
145
+ ):
146
+ """
147
+ Upload image file and analyze UI elements
148
+
149
+ Args:
150
+ file: Image file upload
151
+ task_type: Type of UI analysis task
152
+
153
+ Returns:
154
+ UI analysis results
155
+ """
156
+ try:
157
+ # Validate file type
158
+ if not file.content_type.startswith('image/'):
159
+ raise HTTPException(
160
+ status_code=400,
161
+ detail="File must be an image"
162
+ )
163
+
164
+ # Read and encode image
165
+ image_data = await file.read()
166
+ image_b64 = base64.b64encode(image_data).decode()
167
+
168
+ # Create request
169
+ request = UIAnalysisRequest(
170
+ image_b64=image_b64,
171
+ task_type=task_type
172
+ )
173
+
174
+ # Analyze
175
+ return await analyze_ui_elements(request)
176
+
177
+ except HTTPException:
178
+ raise
179
+ except Exception as e:
180
+ logger.error(f"Upload and analyze error: {e}", exc_info=True)
181
+ raise HTTPException(status_code=500, detail=str(e))
182
+
183
+ @router.post("/detect")
184
+ async def detect_elements_only(request: UIAnalysisRequest):
185
+ """
186
+ Detect UI elements only (without action planning)
187
+
188
+ Args:
189
+ request: UI analysis request
190
+
191
+ Returns:
192
+ UI elements detection results
193
+ """
194
+ try:
195
+ # Call UI analysis service for detection only
196
+ result = await UIAnalysisService.analyze_ui(
197
+ request.image_b64,
198
+ request.task_type
199
+ )
200
+
201
+ if not result.get("success"):
202
+ raise HTTPException(
203
+ status_code=500,
204
+ detail=f"UI detection failed: {result.get('error', 'Unknown error')}"
205
+ )
206
+
207
+ # Return only UI elements
208
+ final_output = result["final_output"]
209
+ ui_elements = final_output["ui_elements"]["interactive_elements"]
210
+
211
+ return {
212
+ "success": True,
213
+ "processing_time": result["total_execution_time"],
214
+ "ui_elements": ui_elements,
215
+ "element_count": len(ui_elements),
216
+ "task_type": request.task_type
217
+ }
218
+
219
+ except HTTPException:
220
+ raise
221
+ except Exception as e:
222
+ logger.error(f"UI detection error: {e}", exc_info=True)
223
+ raise HTTPException(status_code=500, detail=str(e))
@@ -0,0 +1,202 @@
1
+ """
2
+ Unified API Route - Single endpoint for all AI services
3
+
4
+ This is the main API that handles all types of AI requests:
5
+ - Vision tasks (image analysis, OCR, UI detection)
6
+ - Text tasks (chat, generation, translation)
7
+ - Audio tasks (TTS, STT)
8
+ - Image generation tasks
9
+ - Embedding tasks
10
+ """
11
+
12
+ from fastapi import APIRouter, HTTPException, UploadFile, File, Form
13
+ from pydantic import BaseModel, Field
14
+ from typing import Optional, Dict, Any, Union, List
15
+ import logging
16
+ import asyncio
17
+ from pathlib import Path
18
+
19
+ from isa_model.client import ISAModelClient
20
+
21
+ logger = logging.getLogger(__name__)
22
+ router = APIRouter()
23
+
24
+ class UnifiedRequest(BaseModel):
25
+ """Unified request model for all AI services"""
26
+ input_data: Union[str, Dict[str, Any]] = Field(..., description="Input data (text, image URL, etc.)")
27
+ task: str = Field(..., description="Task to perform (chat, analyze_image, generate_speech, etc.)")
28
+ service_type: str = Field(..., description="Service type (text, vision, audio, image, embedding)")
29
+ model_hint: Optional[str] = Field(None, description="Optional model preference")
30
+ provider_hint: Optional[str] = Field(None, description="Optional provider preference")
31
+ parameters: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Additional task parameters")
32
+
33
+ class UnifiedResponse(BaseModel):
34
+ """Unified response model for all AI services"""
35
+ success: bool
36
+ result: Optional[Any] = None
37
+ error: Optional[str] = None
38
+ metadata: Dict[str, Any]
39
+
40
+ # Global ISA client instance for server-side processing
41
+ _isa_client = None
42
+
43
+ def get_isa_client():
44
+ """Get or create ISA client for local processing"""
45
+ global _isa_client
46
+ if _isa_client is None:
47
+ _isa_client = ISAModelClient(mode="local") # Use local mode
48
+ return _isa_client
49
+
50
+ @router.get("/")
51
+ async def unified_info():
52
+ """API information"""
53
+ return {
54
+ "service": "unified_api",
55
+ "status": "active",
56
+ "description": "Single endpoint for all AI services",
57
+ "supported_service_types": ["vision", "text", "audio", "image", "embedding"],
58
+ "version": "1.0.0"
59
+ }
60
+
61
+ @router.post("/invoke", response_model=UnifiedResponse)
62
+ async def unified_invoke(request: UnifiedRequest) -> UnifiedResponse:
63
+ """
64
+ **Unified API endpoint for all AI services**
65
+
66
+ This single endpoint handles:
67
+ - Vision: image analysis, OCR, UI detection
68
+ - Text: chat, generation, translation
69
+ - Audio: TTS, STT, transcription
70
+ - Image: generation, img2img
71
+ - Embedding: text embedding, similarity
72
+
73
+ **Uses ISAModelClient in local mode - all the complex logic is in client.py**
74
+ """
75
+ try:
76
+ # Get ISA client instance (local mode)
77
+ client = get_isa_client()
78
+
79
+ # Use client's local invoke method directly
80
+ # This handles all the complexity: model selection, service routing, execution
81
+ result = await client._invoke_local(
82
+ input_data=request.input_data,
83
+ task=request.task,
84
+ service_type=request.service_type,
85
+ model_hint=request.model_hint,
86
+ provider_hint=request.provider_hint,
87
+ **request.parameters
88
+ )
89
+
90
+ # Return the result in our API format
91
+ return UnifiedResponse(
92
+ success=result["success"],
93
+ result=result.get("result"),
94
+ error=result.get("error"),
95
+ metadata=result["metadata"]
96
+ )
97
+
98
+ except Exception as e:
99
+ logger.error(f"Unified invoke failed: {e}")
100
+ return UnifiedResponse(
101
+ success=False,
102
+ error=str(e),
103
+ metadata={
104
+ "task": request.task,
105
+ "service_type": request.service_type,
106
+ "model_hint": request.model_hint,
107
+ "provider_hint": request.provider_hint
108
+ }
109
+ )
110
+
111
+ @router.post("/invoke-file", response_model=UnifiedResponse)
112
+ async def unified_invoke_file(
113
+ task: str = Form(...),
114
+ service_type: str = Form(...),
115
+ model_hint: Optional[str] = Form(None),
116
+ provider_hint: Optional[str] = Form(None),
117
+ file: UploadFile = File(...)
118
+ ) -> UnifiedResponse:
119
+ """
120
+ Unified file upload endpoint
121
+
122
+ For tasks that require file input (images, audio, documents)
123
+ """
124
+ try:
125
+ # Read file data
126
+ file_data = await file.read()
127
+
128
+ # Get ISA client instance (local mode)
129
+ client = get_isa_client()
130
+
131
+ # Use client's local invoke method with binary data
132
+ result = await client._invoke_local(
133
+ input_data=file_data, # Binary data
134
+ task=task,
135
+ service_type=service_type,
136
+ model_hint=model_hint,
137
+ provider_hint=provider_hint,
138
+ filename=file.filename,
139
+ content_type=file.content_type,
140
+ file_size=len(file_data)
141
+ )
142
+
143
+ # Return the result in our API format
144
+ return UnifiedResponse(
145
+ success=result["success"],
146
+ result=result.get("result"),
147
+ error=result.get("error"),
148
+ metadata={
149
+ **result["metadata"],
150
+ "filename": file.filename,
151
+ "content_type": file.content_type,
152
+ "file_size": len(file_data)
153
+ }
154
+ )
155
+
156
+ except Exception as e:
157
+ logger.error(f"File invoke failed: {e}")
158
+ return UnifiedResponse(
159
+ success=False,
160
+ error=str(e),
161
+ metadata={
162
+ "task": task,
163
+ "service_type": service_type,
164
+ "filename": file.filename if file else None
165
+ }
166
+ )
167
+
168
+ @router.get("/models")
169
+ async def get_available_models(service_type: Optional[str] = None):
170
+ """Get available models (optional filter by service type)"""
171
+ try:
172
+ client = get_isa_client()
173
+ return await client.get_available_models(service_type)
174
+ except Exception as e:
175
+ logger.error(f"Failed to get available models: {e}")
176
+ # Fallback static model list
177
+ return {
178
+ "models": [
179
+ {"service_type": "vision", "provider": "openai", "model_id": "gpt-4.1-mini"},
180
+ {"service_type": "text", "provider": "openai", "model_id": "gpt-4.1-mini"},
181
+ {"service_type": "audio", "provider": "openai", "model_id": "whisper-1"},
182
+ {"service_type": "audio", "provider": "openai", "model_id": "tts-1"},
183
+ {"service_type": "embedding", "provider": "openai", "model_id": "text-embedding-3-small"},
184
+ {"service_type": "image", "provider": "replicate", "model_id": "black-forest-labs/flux-schnell"}
185
+ ]
186
+ }
187
+
188
+ @router.get("/health")
189
+ async def health_check():
190
+ """Health check for unified API"""
191
+ try:
192
+ client = get_isa_client()
193
+ health_result = await client.health_check()
194
+ return {
195
+ "api": "healthy",
196
+ "client_health": health_result
197
+ }
198
+ except Exception as e:
199
+ return {
200
+ "api": "error",
201
+ "error": str(e)
202
+ }
@@ -0,0 +1,19 @@
1
+ """
2
+ Vision API Routes
3
+
4
+ Endpoints for general vision tasks (placeholder)
5
+ """
6
+
7
+ from fastapi import APIRouter, HTTPException
8
+ from pydantic import BaseModel
9
+
10
+ router = APIRouter()
11
+
12
+ @router.get("/")
13
+ async def vision_info():
14
+ """Vision service information"""
15
+ return {
16
+ "service": "vision",
17
+ "status": "placeholder",
18
+ "description": "General vision processing endpoints"
19
+ }
@@ -0,0 +1,17 @@
1
+ """
2
+ API Schemas Module
3
+
4
+ Pydantic models for API request and response validation
5
+ """
6
+
7
+ from .ui_analysis import *
8
+ from .common import *
9
+
10
+ __all__ = [
11
+ "UIAnalysisRequest",
12
+ "UIAnalysisResponse",
13
+ "UIElement",
14
+ "ActionPlan",
15
+ "BaseResponse",
16
+ "ErrorResponse"
17
+ ]
@@ -0,0 +1,33 @@
1
+ """
2
+ Common API Schemas
3
+
4
+ Base schemas used across different endpoints
5
+ """
6
+
7
+ from pydantic import BaseModel
8
+ from typing import Dict, Any, Optional
9
+ import time
10
+
11
+ class BaseResponse(BaseModel):
12
+ """Base response model"""
13
+ success: bool
14
+ timestamp: float = time.time()
15
+
16
+ class ErrorResponse(BaseResponse):
17
+ """Error response model"""
18
+ success: bool = False
19
+ error: str
20
+ detail: Optional[str] = None
21
+
22
+ class HealthStatus(BaseModel):
23
+ """Health status model"""
24
+ status: str
25
+ timestamp: float
26
+ version: str
27
+
28
+ class SystemInfo(BaseModel):
29
+ """System information model"""
30
+ cpu_percent: float
31
+ memory_percent: float
32
+ gpu_available: bool
33
+ gpu_count: int