isa-model 0.3.8__tar.gz → 0.3.91__tar.gz

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 (149) hide show
  1. {isa_model-0.3.8 → isa_model-0.3.91}/PKG-INFO +1 -1
  2. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/__init__.py +1 -1
  3. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/client.py +18 -1
  4. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/pricing_manager.py +248 -1
  5. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/ollama_llm_service.py +43 -0
  6. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/openai_llm_service.py +7 -5
  7. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/yyds_llm_service.py +43 -1
  8. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model.egg-info/PKG-INFO +1 -1
  9. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model.egg-info/SOURCES.txt +1 -0
  10. {isa_model-0.3.8 → isa_model-0.3.91}/pyproject.toml +1 -1
  11. isa_model-0.3.91/tests/test_client_tools.py +359 -0
  12. {isa_model-0.3.8 → isa_model-0.3.91}/MANIFEST.in +0 -0
  13. {isa_model-0.3.8 → isa_model-0.3.91}/README.md +0 -0
  14. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/config/__init__.py +0 -0
  15. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/config/config_manager.py +0 -0
  16. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/config.py +0 -0
  17. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/models/model_billing_tracker.py +0 -0
  18. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/models/model_manager.py +0 -0
  19. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/models/model_repo.py +0 -0
  20. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/models/model_storage.py +0 -0
  21. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/services/__init__.py +0 -0
  22. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/services/intelligent_model_selector.py +0 -0
  23. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/storage/hf_storage.py +0 -0
  24. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/storage/local_storage.py +0 -0
  25. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/storage/minio_storage.py +0 -0
  26. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/core/types.py +0 -0
  27. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/__init__.py +0 -0
  28. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/cloud/__init__.py +0 -0
  29. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/cloud/modal/__init__.py +0 -0
  30. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/cloud/modal/isa_vision_doc_service.py +0 -0
  31. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/cloud/modal/isa_vision_table_service.py +0 -0
  32. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/cloud/modal/isa_vision_ui_service.py +0 -0
  33. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/cloud/modal/register_models.py +0 -0
  34. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/core/__init__.py +0 -0
  35. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/core/deployment_config.py +0 -0
  36. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/core/deployment_manager.py +0 -0
  37. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/core/isa_deployment_service.py +0 -0
  38. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/gpu_int8_ds8/app/server.py +0 -0
  39. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -0
  40. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -0
  41. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/runtime/deployed_service.py +0 -0
  42. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/services/__init__.py +0 -0
  43. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/services/auto_deploy_vision_service.py +0 -0
  44. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/services/model_service.py +0 -0
  45. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/services/service_monitor.py +0 -0
  46. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/services/service_registry.py +0 -0
  47. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/deployment/services/simple_auto_deploy_vision_service.py +0 -0
  48. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/__init__.py +0 -0
  49. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/benchmarks.py +0 -0
  50. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/config/__init__.py +0 -0
  51. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/config/evaluation_config.py +0 -0
  52. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/evaluators/__init__.py +0 -0
  53. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/evaluators/base_evaluator.py +0 -0
  54. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/evaluators/llm_evaluator.py +0 -0
  55. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/factory.py +0 -0
  56. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/infrastructure/__init__.py +0 -0
  57. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/infrastructure/experiment_tracker.py +0 -0
  58. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/eval/metrics.py +0 -0
  59. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/__init__.py +0 -0
  60. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/adapter/unified_api.py +0 -0
  61. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/ai_factory.py +0 -0
  62. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/base.py +0 -0
  63. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/__init__.py +0 -0
  64. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/audio/base_stt_service.py +0 -0
  65. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/audio/base_tts_service.py +0 -0
  66. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/audio/openai_realtime_service.py +0 -0
  67. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/audio/openai_stt_service.py +0 -0
  68. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/audio/openai_tts_service.py +0 -0
  69. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/audio/replicate_tts_service.py +0 -0
  70. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/base_service.py +0 -0
  71. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/embedding/base_embed_service.py +0 -0
  72. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/embedding/helpers/text_splitter.py +0 -0
  73. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/embedding/ollama_embed_service.py +0 -0
  74. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/embedding/openai_embed_service.py +0 -0
  75. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/helpers/stacked_config.py +0 -0
  76. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/img/__init__.py +0 -0
  77. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/img/base_image_gen_service.py +0 -0
  78. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/img/flux_professional_service.py +0 -0
  79. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/img/helpers/base_stacked_service.py +0 -0
  80. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/img/replicate_image_gen_service.py +0 -0
  81. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/__init__.py +0 -0
  82. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/base_llm_service.py +0 -0
  83. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/helpers/llm_adapter.py +0 -0
  84. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/helpers/llm_prompts.py +0 -0
  85. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/llm/helpers/llm_utils.py +0 -0
  86. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/ml/base_ml_service.py +0 -0
  87. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/ml/sklearn_ml_service.py +0 -0
  88. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/others/table_transformer_service.py +0 -0
  89. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/__init__.py +0 -0
  90. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/base_vision_service.py +0 -0
  91. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/disabled/isA_vision_service.py +0 -0
  92. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/doc_analysis_service.py +0 -0
  93. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/helpers/base_stacked_service.py +0 -0
  94. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/helpers/image_utils.py +0 -0
  95. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/helpers/vision_prompts.py +0 -0
  96. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/openai_vision_service.py +0 -0
  97. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/replicate_vision_service.py +0 -0
  98. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/services/vision/ui_analysis_service.py +0 -0
  99. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/utils/conversion/bge_rerank_convert.py +0 -0
  100. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/utils/conversion/onnx_converter.py +0 -0
  101. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/inference/utils/conversion/torch_converter.py +0 -0
  102. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/inference_tracker.py +0 -0
  103. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/mlflow_manager.py +0 -0
  104. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/model_registry.py +0 -0
  105. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/register_models.py +0 -0
  106. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/register_models_with_embeddings.py +0 -0
  107. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/start_mlflow.py +0 -0
  108. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/scripts/training_tracker.py +0 -0
  109. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/__init__.py +0 -0
  110. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/__init__.py +0 -0
  111. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/fastapi_server.py +0 -0
  112. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/middleware/__init__.py +0 -0
  113. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/middleware/request_logger.py +0 -0
  114. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/routes/__init__.py +0 -0
  115. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/routes/health.py +0 -0
  116. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/routes/llm.py +0 -0
  117. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/routes/ui_analysis.py +0 -0
  118. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/routes/unified.py +0 -0
  119. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/routes/vision.py +0 -0
  120. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/schemas/__init__.py +0 -0
  121. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/schemas/common.py +0 -0
  122. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/serving/api/schemas/ui_analysis.py +0 -0
  123. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/__init__.py +0 -0
  124. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/annotation_schema.py +0 -0
  125. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/processors/annotation_processor.py +0 -0
  126. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/storage/dataset_manager.py +0 -0
  127. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/storage/dataset_schema.py +0 -0
  128. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/tests/test_annotation_flow.py +0 -0
  129. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/tests/test_minio copy.py +0 -0
  130. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/tests/test_minio_upload.py +0 -0
  131. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/annotation/views/annotation_controller.py +0 -0
  132. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/cloud/__init__.py +0 -0
  133. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/cloud/job_orchestrator.py +0 -0
  134. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/cloud/runpod_trainer.py +0 -0
  135. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/cloud/storage_manager.py +0 -0
  136. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/core/__init__.py +0 -0
  137. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/core/config.py +0 -0
  138. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/core/dataset.py +0 -0
  139. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/core/trainer.py +0 -0
  140. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/core/utils.py +0 -0
  141. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model/training/factory.py +0 -0
  142. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model.egg-info/dependency_links.txt +0 -0
  143. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model.egg-info/requires.txt +0 -0
  144. {isa_model-0.3.8 → isa_model-0.3.91}/isa_model.egg-info/top_level.txt +0 -0
  145. {isa_model-0.3.8 → isa_model-0.3.91}/setup.cfg +0 -0
  146. {isa_model-0.3.8 → isa_model-0.3.91}/setup.py +0 -0
  147. {isa_model-0.3.8 → isa_model-0.3.91}/tests/test_cleaned_ai_factory.py +0 -0
  148. {isa_model-0.3.8 → isa_model-0.3.91}/tests/test_isa_model_client.py +0 -0
  149. {isa_model-0.3.8 → isa_model-0.3.91}/tests/test_isa_model_client_http.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isa_model
3
- Version: 0.3.8
3
+ Version: 0.3.91
4
4
  Summary: Unified AI model serving framework
5
5
  Author: isA_Model Contributors
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -6,7 +6,7 @@ Main Components:
6
6
  - AIFactory: Legacy factory pattern (still supported)
7
7
  """
8
8
 
9
- __version__ = "0.3.5"
9
+ __version__ = "0.3.91"
10
10
 
11
11
  # Main unified client interface
12
12
  from isa_model.client import ISAModelClient, create_client
@@ -522,11 +522,28 @@ class ISAModelClient:
522
522
  unified_task = task_mapping.get(task, task)
523
523
 
524
524
  # Use unified invoke method
525
- return await service.invoke(
525
+ result = await service.invoke(
526
526
  input_data=input_data,
527
527
  task=unified_task,
528
528
  **kwargs
529
529
  )
530
+
531
+ # Handle the new response format from LLM services
532
+ # LLM services now return {"message": ..., "success": ..., "metadata": ...}
533
+ if isinstance(result, dict) and "message" in result:
534
+ # Extract the message content (convert AIMessage to string)
535
+ message = result["message"]
536
+ if hasattr(message, 'content'):
537
+ # Handle langchain AIMessage objects
538
+ return message.content
539
+ elif isinstance(message, str):
540
+ return message
541
+ else:
542
+ # Fallback: convert to string
543
+ return str(message)
544
+
545
+ # Fallback for other service types or legacy format
546
+ return result
530
547
 
531
548
  async def _execute_image_task(self, service, input_data, task, **kwargs):
532
549
  """Execute image generation tasks using unified invoke method"""
@@ -15,6 +15,7 @@ from datetime import datetime, timedelta
15
15
  from dataclasses import dataclass, field
16
16
 
17
17
  from .types import Provider
18
+ from .config import config_manager
18
19
 
19
20
  logger = logging.getLogger(__name__)
20
21
 
@@ -77,7 +78,17 @@ class PricingManager:
77
78
 
78
79
  def _load_pricing_data(self):
79
80
  """Load pricing data from configuration files"""
80
- # Try to load from specified config path first
81
+ # Try to load from Supabase first
82
+ if self._load_from_supabase():
83
+ logger.info("Loaded pricing data from Supabase")
84
+ return
85
+
86
+ # Try to load from provider configurations
87
+ if self._load_from_provider_configs():
88
+ logger.info("Loaded pricing data from provider configurations")
89
+ return
90
+
91
+ # Try to load from specified config path
81
92
  if self.config_path and self.config_path.exists():
82
93
  self._load_from_file(self.config_path)
83
94
  return
@@ -187,6 +198,242 @@ class PricingManager:
187
198
  self._parse_pricing_data({"providers": default_pricing})
188
199
  logger.info("Loaded default pricing data")
189
200
 
201
+ def _load_from_supabase(self) -> bool:
202
+ """Try to load pricing data from Supabase models table"""
203
+ try:
204
+ global_config = config_manager.get_global_config()
205
+ if not global_config.use_supabase:
206
+ return False
207
+
208
+ # Import Supabase client
209
+ try:
210
+ from supabase import create_client, Client
211
+ except ImportError:
212
+ logger.debug("Supabase library not available")
213
+ return False
214
+
215
+ # Get Supabase credentials
216
+ supabase_url = global_config.supabase_url or os.getenv('SUPABASE_URL')
217
+ supabase_key = global_config.supabase_key or os.getenv('SUPABASE_ANON_KEY')
218
+
219
+ if not supabase_url or not supabase_key:
220
+ logger.debug("Supabase credentials not configured")
221
+ return False
222
+
223
+ # Create Supabase client
224
+ supabase: Client = create_client(supabase_url, supabase_key)
225
+
226
+ # Query models table for pricing information
227
+ result = supabase.table('models').select('model_id, provider, metadata').execute()
228
+
229
+ if not result.data:
230
+ logger.debug("No models found in Supabase")
231
+ return False
232
+
233
+ self.pricing_data = {}
234
+ loaded_count = 0
235
+
236
+ for model_record in result.data:
237
+ model_id = model_record.get('model_id')
238
+ provider = model_record.get('provider')
239
+ metadata = model_record.get('metadata', {})
240
+
241
+ if not model_id or not provider:
242
+ continue
243
+
244
+ # Extract pricing from metadata
245
+ pricing = self._extract_pricing_from_supabase_metadata(metadata, provider, model_id)
246
+ if pricing:
247
+ if provider not in self.pricing_data:
248
+ self.pricing_data[provider] = {}
249
+ self.pricing_data[provider][model_id] = pricing
250
+ loaded_count += 1
251
+
252
+ if loaded_count > 0:
253
+ logger.info(f"Loaded pricing for {loaded_count} models from Supabase")
254
+ return True
255
+ else:
256
+ logger.debug("No pricing data found in Supabase models")
257
+ return False
258
+
259
+ except Exception as e:
260
+ logger.debug(f"Failed to load pricing from Supabase: {e}")
261
+ return False
262
+
263
+ def _load_from_provider_configs(self) -> bool:
264
+ """Load pricing data from provider configuration files"""
265
+ try:
266
+ providers_dir = self._find_project_root() / "isa_model" / "core" / "config" / "providers"
267
+ if not providers_dir.exists():
268
+ return False
269
+
270
+ self.pricing_data = {}
271
+ loaded_any = False
272
+
273
+ # Load all provider config files
274
+ for config_file in providers_dir.glob("*.yaml"):
275
+ if self._load_provider_config_file(config_file):
276
+ loaded_any = True
277
+
278
+ return loaded_any
279
+
280
+ except Exception as e:
281
+ logger.error(f"Failed to load pricing from provider configs: {e}")
282
+ return False
283
+
284
+ def _load_provider_config_file(self, config_file: Path) -> bool:
285
+ """Load pricing data from a single provider config file"""
286
+ try:
287
+ with open(config_file, 'r') as f:
288
+ provider_data = yaml.safe_load(f)
289
+
290
+ provider_name = provider_data.get("provider")
291
+ if not provider_name:
292
+ return False
293
+
294
+ models = provider_data.get("models", [])
295
+ if not models:
296
+ return False
297
+
298
+ self.pricing_data[provider_name] = {}
299
+
300
+ for model in models:
301
+ model_id = model.get("model_id")
302
+ metadata = model.get("metadata", {})
303
+
304
+ if not model_id:
305
+ continue
306
+
307
+ # Extract pricing information from metadata
308
+ pricing = self._extract_pricing_from_metadata(metadata, provider_name, model_id)
309
+ if pricing:
310
+ self.pricing_data[provider_name][model_id] = pricing
311
+
312
+ logger.debug(f"Loaded pricing for {len(self.pricing_data[provider_name])} models from {provider_name}")
313
+ return True
314
+
315
+ except Exception as e:
316
+ logger.error(f"Failed to load provider config {config_file}: {e}")
317
+ return False
318
+
319
+ def _extract_pricing_from_metadata(self, metadata: Dict[str, Any], provider: str, model_name: str) -> Optional[ModelPricing]:
320
+ """Extract pricing information from model metadata"""
321
+ try:
322
+ # Map different pricing field formats to our standard format
323
+ pricing_fields = {
324
+ "cost_per_1000_chars": ("character", 1000),
325
+ "cost_per_1000_tokens": ("token", 1000000), # Convert to cost per 1M tokens
326
+ "cost_per_minute": ("minute", 1),
327
+ "cost_per_image": ("image", 1),
328
+ "cost_per_request": ("request", 1),
329
+ }
330
+
331
+ input_cost = 0.0
332
+ output_cost = 0.0
333
+ unit_type = "token"
334
+ base_cost = 0.0
335
+
336
+ for field, (unit, multiplier) in pricing_fields.items():
337
+ if field in metadata:
338
+ cost = float(metadata[field])
339
+ if unit == "character":
340
+ # Convert cost per 1K chars to cost per 1K chars
341
+ input_cost = cost
342
+ unit_type = "character"
343
+ elif unit == "token":
344
+ # Cost per 1M tokens
345
+ input_cost = cost
346
+ unit_type = "token"
347
+ elif unit == "minute":
348
+ input_cost = cost
349
+ unit_type = "minute"
350
+ elif unit == "image":
351
+ input_cost = cost
352
+ unit_type = "image"
353
+ elif unit == "request":
354
+ base_cost = cost
355
+ break
356
+
357
+ # If no pricing found, skip this model
358
+ if input_cost == 0.0 and base_cost == 0.0:
359
+ return None
360
+
361
+ return ModelPricing(
362
+ provider=provider,
363
+ model_name=model_name,
364
+ input_cost=input_cost,
365
+ output_cost=output_cost,
366
+ unit_type=unit_type,
367
+ base_cost=base_cost,
368
+ last_updated=datetime.now()
369
+ )
370
+
371
+ except Exception as e:
372
+ logger.warning(f"Failed to extract pricing for {provider}/{model_name}: {e}")
373
+ return None
374
+
375
+ def _extract_pricing_from_supabase_metadata(self, metadata: Dict[str, Any], provider: str, model_name: str) -> Optional[ModelPricing]:
376
+ """Extract pricing information from Supabase model metadata"""
377
+ try:
378
+ # Check for pricing information in metadata
379
+ pricing_info = metadata.get('pricing', {})
380
+
381
+ # If no pricing object, look for direct pricing fields
382
+ if not pricing_info:
383
+ # Look for various pricing field formats in metadata
384
+ pricing_fields = [
385
+ 'cost_per_1000_chars', 'cost_per_1000_tokens', 'cost_per_minute',
386
+ 'cost_per_image', 'cost_per_request', 'input_cost', 'output_cost',
387
+ 'cost_per_1k_tokens', 'cost_per_1k_chars'
388
+ ]
389
+
390
+ for field in pricing_fields:
391
+ if field in metadata:
392
+ # Create a pricing object from the field
393
+ if 'char' in field:
394
+ pricing_info = {'input': metadata[field], 'unit_type': 'character'}
395
+ elif 'token' in field:
396
+ pricing_info = {'input': metadata[field], 'unit_type': 'token'}
397
+ elif 'minute' in field:
398
+ pricing_info = {'input': metadata[field], 'unit_type': 'minute'}
399
+ elif 'image' in field:
400
+ pricing_info = {'input': metadata[field], 'unit_type': 'image'}
401
+ elif 'request' in field:
402
+ pricing_info = {'base_cost': metadata[field], 'unit_type': 'request'}
403
+ break
404
+
405
+ if not pricing_info:
406
+ return None
407
+
408
+ # Extract standard pricing fields
409
+ input_cost = float(pricing_info.get('input', pricing_info.get('input_cost', 0.0)))
410
+ output_cost = float(pricing_info.get('output', pricing_info.get('output_cost', 0.0)))
411
+ unit_type = pricing_info.get('unit_type', 'token')
412
+ base_cost = float(pricing_info.get('base_cost', 0.0))
413
+ infrastructure_cost_per_hour = float(pricing_info.get('infrastructure_cost_per_hour', 0.0))
414
+ currency = pricing_info.get('currency', 'USD')
415
+
416
+ # If no pricing found, skip this model
417
+ if input_cost == 0.0 and output_cost == 0.0 and base_cost == 0.0:
418
+ return None
419
+
420
+ return ModelPricing(
421
+ provider=provider,
422
+ model_name=model_name,
423
+ input_cost=input_cost,
424
+ output_cost=output_cost,
425
+ unit_type=unit_type,
426
+ base_cost=base_cost,
427
+ infrastructure_cost_per_hour=infrastructure_cost_per_hour,
428
+ currency=currency,
429
+ last_updated=datetime.now(),
430
+ metadata=pricing_info
431
+ )
432
+
433
+ except Exception as e:
434
+ logger.warning(f"Failed to extract pricing from Supabase metadata for {provider}/{model_name}: {e}")
435
+ return None
436
+
190
437
  def get_model_pricing(self, provider: str, model_name: str) -> Optional[ModelPricing]:
191
438
  """Get pricing information for a specific model"""
192
439
  self._refresh_if_needed()
@@ -140,6 +140,49 @@ class OllamaLLMService(BaseLLMService):
140
140
  """Use adapter manager to format response (consistent with OpenAI service)"""
141
141
  return self.adapter_manager.format_response(response, original_input)
142
142
 
143
+ async def chat(
144
+ self,
145
+ input_data: Union[str, List[Dict[str, str]], Any],
146
+ max_tokens: Optional[int] = None
147
+ ) -> Dict[str, Any]:
148
+ """
149
+ Chat method that wraps ainvoke for compatibility with base class
150
+
151
+ Args:
152
+ input_data: Input messages
153
+ max_tokens: Maximum tokens to generate
154
+
155
+ Returns:
156
+ Dict containing chat response with properly formatted message object
157
+ """
158
+ try:
159
+ # Call ainvoke and get the response (already processed by adapter)
160
+ response = await self.ainvoke(input_data)
161
+
162
+ # Return the response as-is (adapter already formatted it correctly)
163
+ # For LangChain inputs, this will be an AIMessage object
164
+ # For standard inputs, this will be a string
165
+ return {
166
+ "message": response, # Use "message" to preserve object type
167
+ "success": True,
168
+ "metadata": {
169
+ "model": self.model_name,
170
+ "provider": self.provider_name,
171
+ "max_tokens": max_tokens or self.max_tokens
172
+ }
173
+ }
174
+ except Exception as e:
175
+ logger.error(f"Chat method failed: {e}")
176
+ return {
177
+ "message": None,
178
+ "success": False,
179
+ "error": str(e),
180
+ "metadata": {
181
+ "model": self.model_name,
182
+ "provider": self.provider_name
183
+ }
184
+ }
185
+
143
186
 
144
187
  async def _stream_response(self, payload: Dict[str, Any]) -> AsyncGenerator[str, None]:
145
188
  """Handle streaming responses"""
@@ -269,15 +269,17 @@ class OpenAILLMService(BaseLLMService):
269
269
  max_tokens: Maximum tokens to generate
270
270
 
271
271
  Returns:
272
- Dict containing chat response
272
+ Dict containing chat response with properly formatted message object
273
273
  """
274
274
  try:
275
- # Call ainvoke and get the response
275
+ # Call ainvoke and get the response (already processed by adapter)
276
276
  response = await self.ainvoke(input_data)
277
277
 
278
- # Return in expected format
278
+ # Return the response as-is (adapter already formatted it correctly)
279
+ # For LangChain inputs, this will be an AIMessage object
280
+ # For standard inputs, this will be a string
279
281
  return {
280
- "text": response if isinstance(response, str) else str(response),
282
+ "message": response, # Changed from "text" to "message" to preserve object
281
283
  "success": True,
282
284
  "metadata": {
283
285
  "model": self.model_name,
@@ -288,7 +290,7 @@ class OpenAILLMService(BaseLLMService):
288
290
  except Exception as e:
289
291
  logger.error(f"Chat method failed: {e}")
290
292
  return {
291
- "text": "",
293
+ "message": None,
292
294
  "success": False,
293
295
  "error": str(e),
294
296
  "metadata": {
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Dict, Any, List, Union, AsyncGenerator
2
+ from typing import Dict, Any, List, Union, AsyncGenerator, Optional
3
3
 
4
4
  # (�� OpenAI �
5
5
  from openai import AsyncOpenAI
@@ -249,6 +249,48 @@ class YydsLLMService(BaseLLMService):
249
249
  }
250
250
  }
251
251
 
252
+ async def chat(
253
+ self,
254
+ input_data: Union[str, List[Dict[str, str]], Any],
255
+ max_tokens: Optional[int] = None
256
+ ) -> Dict[str, Any]:
257
+ """
258
+ Chat method that wraps ainvoke for compatibility with base class
259
+
260
+ Args:
261
+ input_data: Input messages
262
+ max_tokens: Maximum tokens to generate
263
+
264
+ Returns:
265
+ Dict containing chat response with properly formatted message object
266
+ """
267
+ try:
268
+ # Call ainvoke and get the response (already processed by adapter)
269
+ response = await self.ainvoke(input_data)
270
+
271
+ # Return the response as-is (adapter already formatted it correctly)
272
+ # For LangChain inputs, this will be an AIMessage object
273
+ # For standard inputs, this will be a string
274
+ return {
275
+ "message": response, # Use "message" to preserve object type
276
+ "success": True,
277
+ "metadata": {
278
+ "model": self.model_name,
279
+ "provider": self.provider_name,
280
+ "max_tokens": max_tokens or self.max_tokens
281
+ }
282
+ }
283
+ except Exception as e:
284
+ logger.error(f"Chat method failed: {e}")
285
+ return {
286
+ "message": None,
287
+ "success": False,
288
+ "error": str(e),
289
+ "metadata": {
290
+ "model": self.model_name,
291
+ "provider": self.provider_name
292
+ }
293
+ }
252
294
 
253
295
  async def close(self):
254
296
  """Close the backend client"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isa_model
3
- Version: 0.3.8
3
+ Version: 0.3.91
4
4
  Summary: Unified AI model serving framework
5
5
  Author: isA_Model Contributors
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -142,5 +142,6 @@ isa_model/training/core/dataset.py
142
142
  isa_model/training/core/trainer.py
143
143
  isa_model/training/core/utils.py
144
144
  tests/test_cleaned_ai_factory.py
145
+ tests/test_client_tools.py
145
146
  tests/test_isa_model_client.py
146
147
  tests/test_isa_model_client_http.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "isa_model"
7
- version = "0.3.8"
7
+ version = "0.3.91"
8
8
  description = "Unified AI model serving framework"
9
9
  authors = [{name = "isA_Model Contributors"}]
10
10
  readme = "README.md"