isa-model 0.3.1__tar.gz → 0.3.2__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 (100) hide show
  1. {isa_model-0.3.1 → isa_model-0.3.2}/PKG-INFO +1 -1
  2. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/llm/base_llm_service.py +2 -68
  3. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/llm/llm_adapter.py +51 -6
  4. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/llm/ollama_llm_service.py +22 -77
  5. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/llm/openai_llm_service.py +4 -81
  6. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model.egg-info/PKG-INFO +1 -1
  7. {isa_model-0.3.1 → isa_model-0.3.2}/pyproject.toml +1 -1
  8. {isa_model-0.3.1 → isa_model-0.3.2}/MANIFEST.in +0 -0
  9. {isa_model-0.3.1 → isa_model-0.3.2}/README.md +0 -0
  10. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/__init__.py +0 -0
  11. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/core/model_manager.py +0 -0
  12. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/core/model_registry.py +0 -0
  13. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/core/model_storage.py +0 -0
  14. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/core/storage/hf_storage.py +0 -0
  15. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/core/storage/local_storage.py +0 -0
  16. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/core/storage/minio_storage.py +0 -0
  17. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/__init__.py +0 -0
  18. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/core/__init__.py +0 -0
  19. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/core/deployment_config.py +0 -0
  20. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/core/deployment_manager.py +0 -0
  21. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/core/isa_deployment_service.py +0 -0
  22. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/gpu_int8_ds8/app/server.py +0 -0
  23. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/gpu_int8_ds8/scripts/test_client.py +0 -0
  24. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/deployment/gpu_int8_ds8/scripts/test_client_os.py +0 -0
  25. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/eval/__init__.py +0 -0
  26. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/eval/benchmarks.py +0 -0
  27. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/eval/factory.py +0 -0
  28. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/eval/metrics.py +0 -0
  29. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/__init__.py +0 -0
  30. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/adapter/unified_api.py +0 -0
  31. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/ai_factory.py +0 -0
  32. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/base.py +0 -0
  33. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/billing_tracker.py +0 -0
  34. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/__init__.py +0 -0
  35. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/base_provider.py +0 -0
  36. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/ml_provider.py +0 -0
  37. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/model_cache_manager.py +0 -0
  38. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/ollama_provider.py +0 -0
  39. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/openai_provider.py +0 -0
  40. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/replicate_provider.py +0 -0
  41. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/providers/triton_provider.py +0 -0
  42. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/__init__.py +0 -0
  43. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/audio/base_stt_service.py +0 -0
  44. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/audio/base_tts_service.py +0 -0
  45. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/audio/openai_realtime_service.py +0 -0
  46. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/audio/openai_stt_service.py +0 -0
  47. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/audio/openai_tts_service.py +0 -0
  48. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/audio/replicate_tts_service.py +0 -0
  49. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/base_service.py +0 -0
  50. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/embedding/base_embed_service.py +0 -0
  51. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/embedding/ollama_embed_service.py +0 -0
  52. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/embedding/openai_embed_service.py +0 -0
  53. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/llm/__init__.py +0 -0
  54. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/llm/triton_llm_service.py +0 -0
  55. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/ml/base_ml_service.py +0 -0
  56. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/ml/sklearn_ml_service.py +0 -0
  57. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/others/table_transformer_service.py +0 -0
  58. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/__init__.py +0 -0
  59. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/base_image_gen_service.py +0 -0
  60. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/base_vision_service.py +0 -0
  61. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/helpers/image_utils.py +0 -0
  62. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/helpers/text_splitter.py +0 -0
  63. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/ollama_vision_service.py +0 -0
  64. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/openai_vision_service.py +0 -0
  65. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/services/vision/replicate_image_gen_service.py +0 -0
  66. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/utils/conversion/bge_rerank_convert.py +0 -0
  67. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/utils/conversion/onnx_converter.py +0 -0
  68. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/inference/utils/conversion/torch_converter.py +0 -0
  69. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/scripts/inference_tracker.py +0 -0
  70. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/scripts/mlflow_manager.py +0 -0
  71. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/scripts/model_registry.py +0 -0
  72. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/scripts/start_mlflow.py +0 -0
  73. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/scripts/training_tracker.py +0 -0
  74. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/__init__.py +0 -0
  75. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/annotation_schema.py +0 -0
  76. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/processors/annotation_processor.py +0 -0
  77. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/storage/dataset_manager.py +0 -0
  78. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/storage/dataset_schema.py +0 -0
  79. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/tests/test_annotation_flow.py +0 -0
  80. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/tests/test_minio copy.py +0 -0
  81. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/tests/test_minio_upload.py +0 -0
  82. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/annotation/views/annotation_controller.py +0 -0
  83. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/cloud/__init__.py +0 -0
  84. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/cloud/job_orchestrator.py +0 -0
  85. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/cloud/runpod_trainer.py +0 -0
  86. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/cloud/storage_manager.py +0 -0
  87. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/core/__init__.py +0 -0
  88. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/core/config.py +0 -0
  89. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/core/dataset.py +0 -0
  90. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/core/trainer.py +0 -0
  91. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/core/utils.py +0 -0
  92. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model/training/factory.py +0 -0
  93. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model.egg-info/SOURCES.txt +0 -0
  94. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model.egg-info/dependency_links.txt +0 -0
  95. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model.egg-info/requires.txt +0 -0
  96. {isa_model-0.3.1 → isa_model-0.3.2}/isa_model.egg-info/top_level.txt +0 -0
  97. {isa_model-0.3.1 → isa_model-0.3.2}/setup.cfg +0 -0
  98. {isa_model-0.3.1 → isa_model-0.3.2}/setup.py +0 -0
  99. {isa_model-0.3.1 → isa_model-0.3.2}/tests/test_all_services.py +0 -0
  100. {isa_model-0.3.1 → isa_model-0.3.2}/tests/test_training_setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isa_model
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Unified AI model serving framework
5
5
  Author: isA_Model Contributors
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -90,80 +90,14 @@ class BaseLLMService(BaseService):
90
90
  # No event loop running, create a new one
91
91
  return asyncio.run(self.ainvoke(input_data))
92
92
 
93
- # 保留旧的方法以保持向后兼容
94
- def _convert_tools_to_schema(self, tools: List[Union[Dict[str, Any], Callable]]) -> List[Dict[str, Any]]:
95
- """Convert tools to OpenAI function calling schema (deprecated, use adapter manager)"""
96
- import asyncio
97
- try:
98
- loop = asyncio.get_event_loop()
99
- schemas, _ = loop.run_until_complete(self.adapter_manager.convert_tools_to_schemas(tools))
100
- return schemas
101
- except RuntimeError:
102
- schemas, _ = asyncio.run(self.adapter_manager.convert_tools_to_schemas(tools))
103
- return schemas
104
-
105
93
  def _has_bound_tools(self) -> bool:
106
94
  """Check if this service has bound tools"""
107
95
  return bool(self._bound_tools)
108
96
 
109
- def _get_bound_tools(self) -> List[Dict[str, Any]]:
110
- """Get the bound tools schema"""
97
+ def _get_bound_tools(self) -> List[Any]:
98
+ """Get the bound tools"""
111
99
  return self._bound_tools
112
100
 
113
- def _convert_langchain_to_openai(self, messages: List[Any]) -> List[Dict[str, str]]:
114
- """Convert LangChain message objects to OpenAI format"""
115
- converted_messages = []
116
-
117
- for msg in messages:
118
- # Handle different LangChain message types
119
- if hasattr(msg, 'type') and hasattr(msg, 'content'):
120
- # LangChain message object
121
- msg_dict = {"content": str(msg.content)}
122
-
123
- # Map LangChain types to OpenAI roles
124
- if msg.type == "system":
125
- msg_dict["role"] = "system"
126
- elif msg.type == "human":
127
- msg_dict["role"] = "user"
128
- elif msg.type == "ai":
129
- msg_dict["role"] = "assistant"
130
- # Handle tool calls if present
131
- if hasattr(msg, 'tool_calls') and msg.tool_calls:
132
- # Handle tool calls - need to store as separate key since it's not a string
133
- tool_calls = [
134
- {
135
- "id": tc.get("id", f"call_{i}"),
136
- "type": "function",
137
- "function": {
138
- "name": tc["name"],
139
- "arguments": tc.get("args", {}) if isinstance(tc.get("args"), dict) else tc.get("args", "{}")
140
- }
141
- } for i, tc in enumerate(msg.tool_calls)
142
- ]
143
- # Store tool_calls separately to avoid type issues
144
- msg_dict["tool_calls"] = tool_calls # type: ignore
145
- elif msg.type == "tool":
146
- msg_dict["role"] = "tool"
147
- if hasattr(msg, 'tool_call_id'):
148
- msg_dict["tool_call_id"] = msg.tool_call_id
149
- elif msg.type == "function": # Legacy function message
150
- msg_dict["role"] = "function"
151
- if hasattr(msg, 'name'):
152
- msg_dict["name"] = msg.name
153
- else:
154
- msg_dict["role"] = "user" # Default fallback
155
-
156
- converted_messages.append(msg_dict)
157
-
158
- elif isinstance(msg, dict):
159
- # Already in OpenAI format
160
- converted_messages.append(msg)
161
- else:
162
- # Fallback: treat as user message
163
- converted_messages.append({"role": "user", "content": str(msg)})
164
-
165
- return converted_messages
166
-
167
101
  @abstractmethod
168
102
  def get_token_usage(self) -> Dict[str, Any]:
169
103
  """Get cumulative token usage statistics"""
@@ -100,18 +100,55 @@ class LangChainMessageAdapter:
100
100
 
101
101
  return converted_messages
102
102
 
103
- def from_openai_format(self, response: str, original_input: Any) -> Any:
103
+ def from_openai_format(self, response: Union[str, Any], original_input: Any) -> Any:
104
104
  """从 OpenAI 格式转换回 LangChain 格式"""
105
105
  try:
106
106
  from langchain_core.messages import AIMessage
107
- return AIMessage(content=response)
107
+
108
+ # Handle response objects with tool_calls (OpenAI message objects)
109
+ if hasattr(response, 'tool_calls') and response.tool_calls:
110
+ # Convert OpenAI tool_calls to LangChain format
111
+ tool_calls = []
112
+ for tc in response.tool_calls:
113
+ tool_call = {
114
+ "name": tc.function.name,
115
+ "args": json.loads(tc.function.arguments),
116
+ "id": tc.id
117
+ }
118
+ tool_calls.append(tool_call)
119
+
120
+ return AIMessage(
121
+ content=response.content or "",
122
+ tool_calls=tool_calls
123
+ )
124
+ else:
125
+ # Handle simple string response
126
+ content = response if isinstance(response, str) else getattr(response, 'content', str(response))
127
+ return AIMessage(content=content)
128
+
108
129
  except ImportError:
109
130
  # 回退实现
110
131
  class SimpleAIMessage:
111
- def __init__(self, content):
132
+ def __init__(self, content, tool_calls=None):
112
133
  self.content = content
113
134
  self.type = "ai"
114
- return SimpleAIMessage(response)
135
+ self.tool_calls = tool_calls or []
136
+
137
+ # Handle response objects with tool_calls
138
+ if hasattr(response, 'tool_calls') and response.tool_calls:
139
+ tool_calls = []
140
+ for tc in response.tool_calls:
141
+ tool_call = {
142
+ "name": tc.function.name,
143
+ "args": json.loads(tc.function.arguments),
144
+ "id": tc.id
145
+ }
146
+ tool_calls.append(tool_call)
147
+
148
+ return SimpleAIMessage(response.content or "", tool_calls)
149
+ else:
150
+ content = response if isinstance(response, str) else getattr(response, 'content', str(response))
151
+ return SimpleAIMessage(content)
115
152
 
116
153
 
117
154
  class LangChainToolAdapter:
@@ -293,9 +330,17 @@ class StandardMessageAdapter:
293
330
  else:
294
331
  return [{"role": "user", "content": str(input_data)}]
295
332
 
296
- def from_openai_format(self, response: str, original_input: Any) -> Any:
333
+ def from_openai_format(self, response: Union[str, Any], original_input: Any) -> Any:
297
334
  """从 OpenAI 格式转换回原始格式"""
298
- return response # 默认返回字符串
335
+ # Handle response objects with tool_calls (preserve message object for compatibility)
336
+ if hasattr(response, 'tool_calls') and response.tool_calls:
337
+ # For standard format, we preserve the message object for tool_calls access
338
+ # This handles both OpenAI and Ollama message objects
339
+ return response
340
+ else:
341
+ # Handle simple string response
342
+ content = response if isinstance(response, str) else getattr(response, 'content', str(response))
343
+ return content
299
344
 
300
345
 
301
346
  # ============= 适配器管理器 =============
@@ -8,7 +8,7 @@ from isa_model.inference.providers.base_provider import BaseProvider
8
8
  logger = logging.getLogger(__name__)
9
9
 
10
10
  class OllamaLLMService(BaseLLMService):
11
- """Ollama LLM service with unified invoke interface"""
11
+ """Ollama LLM service with unified invoke interface and proper adapter support"""
12
12
 
13
13
  def __init__(self, provider: 'BaseProvider', model_name: str = "llama3.2:3b-instruct-fp16"):
14
14
  super().__init__(provider, model_name)
@@ -25,10 +25,6 @@ class OllamaLLMService(BaseLLMService):
25
25
  self.last_token_usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
26
26
  self.total_token_usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "requests_count": 0}
27
27
 
28
- # Tool binding attributes
29
- self._bound_tools: List[Any] = []
30
- self._tool_binding_kwargs: Dict[str, Any] = {}
31
- self._tool_functions: Dict[str, Callable] = {}
32
28
 
33
29
  logger.info(f"Initialized OllamaLLMService with model {model_name} at {base_url}")
34
30
 
@@ -43,16 +39,13 @@ class OllamaLLMService(BaseLLMService):
43
39
  """Create a copy of this service for tool binding"""
44
40
  bound_service = OllamaLLMService(self.provider, self.model_name)
45
41
  bound_service._bound_tools = self._bound_tools.copy()
46
- bound_service._tool_binding_kwargs = self._tool_binding_kwargs.copy()
47
- bound_service._tool_functions = self._tool_functions.copy()
48
42
  return bound_service
49
43
 
50
- def bind_tools(self, tools: List[Union[Dict[str, Any], Callable]], **kwargs) -> 'OllamaLLMService':
44
+ def bind_tools(self, tools: List[Any], **kwargs) -> 'OllamaLLMService':
51
45
  """Bind tools to this LLM service for function calling"""
52
46
  bound_service = self._create_bound_copy()
53
- # 使用基类的适配器管理器方法
47
+ # Use base class method to bind tools
54
48
  bound_service._bound_tools = tools
55
- bound_service._tool_binding_kwargs = kwargs
56
49
 
57
50
  return bound_service
58
51
 
@@ -73,7 +66,7 @@ class OllamaLLMService(BaseLLMService):
73
66
  # Ensure client is available
74
67
  self._ensure_client()
75
68
 
76
- # Convert input to messages format
69
+ # Use adapter manager to prepare messages (consistent with OpenAI service)
77
70
  messages = self._prepare_messages(input_data)
78
71
 
79
72
  # Prepare request parameters
@@ -106,77 +99,36 @@ class OllamaLLMService(BaseLLMService):
106
99
  if "eval_count" in result:
107
100
  self._update_token_usage(result)
108
101
 
109
- # Handle tool calls if present
102
+ # Handle tool calls if present - let adapter process the complete message
110
103
  message = result["message"]
111
104
  if "tool_calls" in message and message["tool_calls"]:
112
- return await self._handle_tool_calls(message, messages)
105
+ # Create message object similar to OpenAI format for adapter processing
106
+ message_obj = type('OllamaMessage', (), {
107
+ 'content': message.get("content", ""),
108
+ 'tool_calls': message["tool_calls"]
109
+ })()
110
+ # Pass the complete message object to adapter for proper tool_calls handling
111
+ return self._format_response(message_obj, input_data)
113
112
 
114
113
  # Return appropriate format based on input type
115
- return self._format_response(message["content"], input_data)
114
+ return self._format_response(message.get("content", ""), input_data)
116
115
 
117
116
  except httpx.RequestError as e:
118
117
  logger.error(f"HTTP request error in ainvoke: {e}")
119
118
  raise
120
119
  except Exception as e:
121
- logger.error(f"Error in ainvoke: {e}")
120
+ logger.error(f"Error in chat completion: {e}")
122
121
  raise
123
122
 
124
123
  def _prepare_messages(self, input_data: Union[str, List[Dict[str, str]], Any]) -> List[Dict[str, str]]:
125
- """Convert various input formats to Ollama messages format (same as OpenAI)"""
126
- if isinstance(input_data, str):
127
- # Simple string prompt
128
- return [{"role": "user", "content": input_data}]
129
-
130
- elif isinstance(input_data, list):
131
- if not input_data:
132
- raise ValueError("Empty message list provided")
133
-
134
- # Check if it's LangChain messages or standard messages
135
- first_msg = input_data[0]
136
- if hasattr(first_msg, 'content') and hasattr(first_msg, 'type'):
137
- # LangChain message objects - use base class method
138
- return self._convert_langchain_to_openai(input_data)
139
- elif isinstance(first_msg, dict):
140
- # Standard message dictionaries
141
- return input_data
142
- else:
143
- # List of strings or other formats
144
- messages = []
145
- for i, msg in enumerate(input_data):
146
- if isinstance(msg, str):
147
- role = "user" if i % 2 == 0 else "assistant"
148
- messages.append({"role": role, "content": msg})
149
- elif isinstance(msg, dict):
150
- messages.append(msg)
151
- else:
152
- messages.append({"role": "user", "content": str(msg)})
153
- return messages
154
-
155
- else:
156
- # Handle single LangChain message objects or other objects
157
- if hasattr(input_data, 'content') and hasattr(input_data, 'type'):
158
- return self._convert_langchain_to_openai([input_data])
159
- else:
160
- return [{"role": "user", "content": str(input_data)}]
124
+ """Use adapter manager to convert messages (consistent with OpenAI service)"""
125
+ return self.adapter_manager.convert_messages(input_data)
126
+
127
+
128
+ def _format_response(self, response: Union[str, Any], original_input: Any) -> Union[str, Any]:
129
+ """Use adapter manager to format response (consistent with OpenAI service)"""
130
+ return self.adapter_manager.format_response(response, original_input)
161
131
 
162
- def _format_response(self, content: str, original_input: Any) -> Union[str, Any]:
163
- """Format response based on original input type"""
164
- # For LangGraph compatibility, return AIMessage object if needed
165
- if hasattr(original_input, 'type') or (isinstance(original_input, list) and
166
- original_input and hasattr(original_input[0], 'type')):
167
- try:
168
- from langchain_core.messages import AIMessage
169
- return AIMessage(content=content)
170
- except ImportError:
171
- # Fallback to simple object
172
- class SimpleAIMessage:
173
- def __init__(self, content):
174
- self.content = content
175
- self.type = "ai"
176
- return SimpleAIMessage(content)
177
-
178
- # Default to string
179
- return content
180
132
 
181
133
  async def _stream_response(self, payload: Dict[str, Any]) -> AsyncGenerator[str, None]:
182
134
  """Handle streaming responses"""
@@ -270,13 +222,6 @@ class OllamaLLMService(BaseLLMService):
270
222
  "provider": "ollama"
271
223
  }
272
224
 
273
- def _has_bound_tools(self) -> bool:
274
- """Check if this service has bound tools"""
275
- return bool(self._bound_tools)
276
-
277
- def _get_bound_tools(self) -> List[Any]:
278
- """Get the bound tools schema"""
279
- return self._bound_tools
280
225
 
281
226
  async def close(self):
282
227
  """Close the HTTP client"""
@@ -285,4 +230,4 @@ class OllamaLLMService(BaseLLMService):
285
230
  if not self.client.is_closed:
286
231
  await self.client.aclose()
287
232
  except Exception as e:
288
- logger.warning(f"Error closing Ollama client: {e}")
233
+ logger.warning(f"Error closing Ollama client: {e}")
@@ -41,17 +41,11 @@ class OpenAILLMService(BaseLLMService):
41
41
  self.last_token_usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
42
42
  self.total_token_usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0, "requests_count": 0}
43
43
 
44
- # Tool binding attributes
45
- self._bound_tools: List[Dict[str, Any]] = []
46
- self._tool_binding_kwargs: Dict[str, Any] = {}
47
- self._tool_functions: Dict[str, Callable] = {}
48
44
 
49
45
  def _create_bound_copy(self) -> 'OpenAILLMService':
50
46
  """Create a copy of this service for tool binding"""
51
47
  bound_service = OpenAILLMService(self.provider, self.model_name)
52
48
  bound_service._bound_tools = self._bound_tools.copy()
53
- bound_service._tool_binding_kwargs = self._tool_binding_kwargs.copy()
54
- bound_service._tool_functions = self._tool_functions.copy()
55
49
  return bound_service
56
50
 
57
51
  def bind_tools(self, tools: List[Any], **kwargs) -> 'OpenAILLMService':
@@ -68,7 +62,7 @@ class OpenAILLMService(BaseLLMService):
68
62
  # Create a copy of this service
69
63
  bound_service = self._create_bound_copy()
70
64
 
71
- # Use the adapter manager to handle tools
65
+ # Use base class method to bind tools
72
66
  bound_service._bound_tools = tools
73
67
 
74
68
  return bound_service
@@ -123,10 +117,10 @@ class OpenAILLMService(BaseLLMService):
123
117
  self._update_token_usage(response.usage)
124
118
  self._track_billing(response.usage)
125
119
 
126
- # Handle tool calls if present
120
+ # Handle tool calls if present - let adapter process the complete message
127
121
  if message.tool_calls:
128
- final_content = await self._handle_tool_calls(message, messages)
129
- return self._format_response(final_content, input_data)
122
+ # Pass the complete message object to adapter for proper tool_calls handling
123
+ return self._format_response(message, input_data)
130
124
 
131
125
  # Return appropriate format based on input type
132
126
  return self._format_response(message.content or "", input_data)
@@ -135,13 +129,6 @@ class OpenAILLMService(BaseLLMService):
135
129
  logger.error(f"Error in ainvoke: {e}")
136
130
  raise
137
131
 
138
- def _prepare_messages(self, input_data: Union[str, List[Dict[str, str]], Any]) -> List[Dict[str, str]]:
139
- """使用适配器管理器转换消息格式(覆盖基类方法以保持兼容性)"""
140
- return self.adapter_manager.convert_messages(input_data)
141
-
142
- def _format_response(self, content: str, original_input: Any) -> Union[str, Any]:
143
- """使用适配器管理器格式化响应(覆盖基类方法以保持兼容性)"""
144
- return self.adapter_manager.format_response(content, original_input)
145
132
 
146
133
  async def _stream_response(self, kwargs: Dict[str, Any]) -> AsyncGenerator[str, None]:
147
134
  """Handle streaming responses"""
@@ -160,63 +147,6 @@ class OpenAILLMService(BaseLLMService):
160
147
 
161
148
  return stream_generator()
162
149
 
163
- async def _handle_tool_calls(self, assistant_message, original_messages: List[Dict[str, str]]) -> str:
164
- """Handle tool calls from the assistant using adapter manager"""
165
- # Add assistant message with tool calls to conversation
166
- messages = original_messages + [{
167
- "role": "assistant",
168
- "content": assistant_message.content or "",
169
- "tool_calls": [
170
- {
171
- "id": tc.id,
172
- "type": tc.type,
173
- "function": {
174
- "name": tc.function.name,
175
- "arguments": tc.function.arguments
176
- }
177
- } for tc in assistant_message.tool_calls
178
- ]
179
- }]
180
-
181
- # Execute each tool call using adapter manager
182
- for tool_call in assistant_message.tool_calls:
183
- function_name = tool_call.function.name
184
- arguments = json.loads(tool_call.function.arguments)
185
-
186
- try:
187
- # Use adapter manager to execute tool
188
- result = await self._execute_tool_call(function_name, arguments)
189
-
190
- # Add tool result to messages
191
- messages.append({
192
- "role": "tool",
193
- "content": str(result),
194
- "tool_call_id": tool_call.id
195
- })
196
-
197
- except Exception as e:
198
- logger.error(f"Error executing tool {function_name}: {e}")
199
- messages.append({
200
- "role": "tool",
201
- "content": f"Error executing {function_name}: {str(e)}",
202
- "tool_call_id": tool_call.id
203
- })
204
-
205
- # Get final response from the model
206
- try:
207
- kwargs = {
208
- "model": self.model_name,
209
- "messages": messages,
210
- "temperature": self.config.get("temperature", 0.7),
211
- "max_tokens": self.config.get("max_tokens", 1024)
212
- }
213
-
214
- response = await self.client.chat.completions.create(**kwargs)
215
- return response.choices[0].message.content or ""
216
-
217
- except Exception as e:
218
- logger.error(f"Error getting final response after tool calls: {e}")
219
- raise
220
150
 
221
151
  def _update_token_usage(self, usage):
222
152
  """Update token usage statistics"""
@@ -263,13 +193,6 @@ class OpenAILLMService(BaseLLMService):
263
193
  "provider": "openai"
264
194
  }
265
195
 
266
- def _has_bound_tools(self) -> bool:
267
- """Check if this service has bound tools"""
268
- return bool(self._bound_tools)
269
-
270
- def _get_bound_tools(self) -> List[Dict[str, Any]]:
271
- """Get the bound tools schema"""
272
- return self._bound_tools
273
196
 
274
197
  async def close(self):
275
198
  """Close the backend client"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isa_model
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: Unified AI model serving framework
5
5
  Author: isA_Model Contributors
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "isa_model"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Unified AI model serving framework"
9
9
  authors = [{name = "isA_Model Contributors"}]
10
10
  readme = "README.md"
File without changes
File without changes
File without changes
File without changes